mirror of
https://github.com/cloudflare/cloudflared.git
synced 2026-08-07 15:24:46 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a2b45c2a5 | |||
| 78865b19bc | |||
| 12e11208ae | |||
| 2206516c3b | |||
| 8679787525 | |||
| 45c0f22e97 | |||
| e8956c10ac | |||
| 541a608dde | |||
| 6fd720a4a4 | |||
| 60a8c3da17 | |||
| 2601f87b57 | |||
| f70adda11c | |||
| ecb88678f1 | |||
| 43bfec0bcd |
@@ -3,23 +3,12 @@
|
||||
runOnBranches: "^master$"
|
||||
COMPONENT: "common"
|
||||
|
||||
.register_inputs_stable_bookworm: ®ister_inputs_stable_bookworm
|
||||
<<: *register_inputs
|
||||
runOnChangesTo: ['RELEASE_NOTES']
|
||||
FLAVOR: "bookworm"
|
||||
SERIES: "stable"
|
||||
|
||||
.register_inputs_stable_trixie: ®ister_inputs_stable_trixie
|
||||
<<: *register_inputs
|
||||
runOnChangesTo: ['RELEASE_NOTES']
|
||||
FLAVOR: "trixie"
|
||||
SERIES: "stable"
|
||||
|
||||
.register_inputs_next_bookworm: ®ister_inputs_next_bookworm
|
||||
<<: *register_inputs
|
||||
FLAVOR: "bookworm"
|
||||
SERIES: next
|
||||
|
||||
.register_inputs_next_trixie: ®ister_inputs_next_trixie
|
||||
<<: *register_inputs
|
||||
FLAVOR: "trixie"
|
||||
@@ -66,6 +55,10 @@ cloudflared-arm64-stable:
|
||||
ORIGINAL_NAME: true
|
||||
CGO_ENABLED: 1
|
||||
|
||||
# Jobs names
|
||||
.amd64-stable: &amd64-stable ["cloudflared-amd64-stable"]
|
||||
.arm64-stable: &arm64-stable ["cloudflared-arm64-stable"]
|
||||
|
||||
############
|
||||
### Next ###
|
||||
############
|
||||
@@ -85,25 +78,16 @@ cloudflared-arm64-next:
|
||||
<<: *arm64-stable-vars
|
||||
NIGHTLY: true
|
||||
|
||||
# Jobs names
|
||||
.amd64-next: &amd64-next ["cloudflared-amd64-next"]
|
||||
.arm64-next: &arm64-next ["cloudflared-arm64-next"]
|
||||
|
||||
include:
|
||||
- local: .ci/commons.gitlab-ci.yml
|
||||
|
||||
##########################################
|
||||
### Publish Packages to Internal Repos ###
|
||||
##########################################
|
||||
# Bookworm AMD64
|
||||
- component: $CI_SERVER_FQDN/cloudflare/ci/apt-register/register@~latest
|
||||
inputs:
|
||||
<<: *register_inputs_stable_bookworm
|
||||
jobPrefix: cloudflared-bookworm-amd64
|
||||
needs: &amd64-stable ["cloudflared-amd64-stable"]
|
||||
|
||||
# Bookworm ARM64
|
||||
- component: $CI_SERVER_FQDN/cloudflare/ci/apt-register/register@~latest
|
||||
inputs:
|
||||
<<: *register_inputs_stable_bookworm
|
||||
jobPrefix: cloudflared-bookworm-arm64
|
||||
needs: &arm64-stable ["cloudflared-arm64-stable"]
|
||||
|
||||
# Trixie AMD64
|
||||
- component: $CI_SERVER_FQDN/cloudflare/ci/apt-register/register@~latest
|
||||
@@ -122,19 +106,6 @@ include:
|
||||
##################################################
|
||||
### Publish Nightly Packages to Internal Repos ###
|
||||
##################################################
|
||||
# Bookworm AMD64
|
||||
- component: $CI_SERVER_FQDN/cloudflare/ci/apt-register/register@~latest
|
||||
inputs:
|
||||
<<: *register_inputs_next_bookworm
|
||||
jobPrefix: cloudflared-nightly-bookworm-amd64
|
||||
needs: &amd64-next ['cloudflared-amd64-next']
|
||||
|
||||
# Bookworm ARM64
|
||||
- component: $CI_SERVER_FQDN/cloudflare/ci/apt-register/register@~latest
|
||||
inputs:
|
||||
<<: *register_inputs_next_bookworm
|
||||
jobPrefix: cloudflared-nightly-bookworm-arm64
|
||||
needs: &arm64-next ['cloudflared-arm64-next']
|
||||
|
||||
# Trixie AMD64
|
||||
- component: $CI_SERVER_FQDN/cloudflare/ci/apt-register/register@~latest
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
images:
|
||||
- name: cloudflared-daemon
|
||||
- name: cloudflared
|
||||
dockerfile: Dockerfile.$ARCH
|
||||
context: .
|
||||
version_file: versions-internal
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ COPY . .
|
||||
RUN make cloudflared
|
||||
|
||||
# use a distroless base image with glibc
|
||||
FROM gcr.io/distroless/base-debian13:nonroot@sha256:ab7554b6d07ad354fad31957f8a1a813e65dfb93a8ad160568c79c3f2be6884f
|
||||
FROM gcr.io/distroless/base-debian13:nonroot@sha256:b78832f41c8128046807c24840ebee4f1c18ba7870eed423d8750c272c15e147
|
||||
|
||||
LABEL org.opencontainers.image.source="https://github.com/cloudflare/cloudflared"
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ COPY . .
|
||||
RUN GOOS=linux GOARCH=amd64 make cloudflared
|
||||
|
||||
# use a distroless base image with glibc
|
||||
FROM gcr.io/distroless/base-debian13:nonroot-amd64@sha256:ced0a2b1936b14d5bddc2ee02a807b1586ca6576a967f5b043f4a3301c8a8f6b
|
||||
FROM gcr.io/distroless/base-debian13:nonroot-amd64@sha256:ce2a20e0e277b7d913aa8bcfa098fc2a543dc08028f7393434963fa24b39ea81
|
||||
|
||||
LABEL org.opencontainers.image.source="https://github.com/cloudflare/cloudflared"
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ COPY . .
|
||||
RUN GOOS=linux GOARCH=arm64 make cloudflared
|
||||
|
||||
# use a distroless base image with glibc
|
||||
FROM gcr.io/distroless/base-debian13:nonroot-arm64@sha256:9c1ab6a3dbf9e22827b0be4a314d7cfbe008f922b7ca833ed0e5a63318c6169e
|
||||
FROM gcr.io/distroless/base-debian13:nonroot-arm64@sha256:7a4876f88e7fe3190972c274b679b3473f61e8d990a4dce3627961b3e22a0eaf
|
||||
|
||||
LABEL org.opencontainers.image.source="https://github.com/cloudflare/cloudflared"
|
||||
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
2026.7.3
|
||||
- 2026-07-22 VULN-143514: Windows svc: use --token-file instead of --token
|
||||
- 2026-07-22 fix: Bump golang.org/x/text and its dependencies to fix CVE
|
||||
- 2026-07-20 TUN-10701: Use curves for prechecks
|
||||
|
||||
2026.7.2
|
||||
- 2026-07-15 VULN-118896: MacOS service: use --token-file instead of --token
|
||||
- 2026-07-14 Update gcr.io/distroless/base-debian13:nonroot Docker digest to b78832f
|
||||
- 2026-07-14 chore: Change internal image name
|
||||
- 2026-07-14 Update gcr.io/distroless/base-debian13:nonroot-arm Docker digest to 7a4876f88
|
||||
- 2026-07-13 VULN-118896: Linux service: Use --token-file instead of --token
|
||||
- 2026-07-13 chore: Remove bookworm internal register publish
|
||||
- 2026-07-13 Update gcr.io/distroless/base-debian13:nonroot-amd64 Docker digest to ce2a20e
|
||||
|
||||
2026.7.1
|
||||
- 2026-07-09 Revert "TUN-10621: Propagate max wait timeout"
|
||||
|
||||
2026.7.0
|
||||
- 2026-07-08 chore: Bump go-chi to version 5.3.1
|
||||
- 2026-07-08 Revert "TUN-10557: Bump quic-go v0.59.1"
|
||||
|
||||
@@ -37,7 +37,7 @@ func (ws *Websocket) ServeStream(options *StartOptions, conn io.ReadWriter) erro
|
||||
}
|
||||
defer func() { _ = wsConn.Close() }()
|
||||
|
||||
stream.Pipe(wsConn, conn, stream.DefaultTimeoutAfterFirstClose, ws.log)
|
||||
stream.Pipe(wsConn, conn, ws.log)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,8 @@ import (
|
||||
crand "crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -21,7 +17,6 @@ import (
|
||||
"golang.org/x/net/websocket"
|
||||
|
||||
"github.com/cloudflare/cloudflared/hello"
|
||||
"github.com/cloudflare/cloudflared/stream"
|
||||
"github.com/cloudflare/cloudflared/tlsconfig"
|
||||
cfwebsocket "github.com/cloudflare/cloudflared/websocket"
|
||||
)
|
||||
@@ -132,137 +127,3 @@ func TestWebsocketWrapper(t *testing.T) {
|
||||
require.Equal(t, 2, n)
|
||||
require.Equal(t, "bc", string(buf[:n]))
|
||||
}
|
||||
|
||||
// halfClosePipeEnd is a bidirectional pipe built from two independent io.Pipe
|
||||
// pairs. Unlike net.Pipe, closing one direction does not affect the other, which
|
||||
// lets us simulate a half-close: the local client can signal EOF on its write
|
||||
// side while still reading a delayed response from the remote side.
|
||||
type halfClosePipeEnd struct {
|
||||
r *io.PipeReader // data flowing into this end
|
||||
w *io.PipeWriter // data flowing out of this end
|
||||
}
|
||||
|
||||
func newHalfClosePipe() (local, remote *halfClosePipeEnd) {
|
||||
// Pipe A carries data from local → remote.
|
||||
ar, aw := io.Pipe()
|
||||
// Pipe B carries data from remote → local.
|
||||
br, bw := io.Pipe()
|
||||
|
||||
local = &halfClosePipeEnd{r: br, w: aw}
|
||||
remote = &halfClosePipeEnd{r: ar, w: bw}
|
||||
return local, remote
|
||||
}
|
||||
|
||||
func (p *halfClosePipeEnd) Read(b []byte) (int, error) { return p.r.Read(b) }
|
||||
func (p *halfClosePipeEnd) Write(b []byte) (int, error) { return p.w.Write(b) }
|
||||
|
||||
// CloseWrite signals EOF to the remote reader without closing the read side.
|
||||
func (p *halfClosePipeEnd) CloseWrite() error { return p.w.Close() }
|
||||
|
||||
// Close shuts down both directions.
|
||||
func (p *halfClosePipeEnd) Close() error {
|
||||
_ = p.r.Close()
|
||||
return p.w.Close()
|
||||
}
|
||||
|
||||
// TestServeStreamWaitsForResponseAfterLocalClose exercises the websocket path
|
||||
// It verifies that the pipe does not tear down immediately after the client closes
|
||||
// its write side.
|
||||
//
|
||||
// The setup mirrors the cloudflared access tcp path:
|
||||
//
|
||||
// local app -> halfClosePipe -> ServeStream -> mock WS echo server
|
||||
//
|
||||
// Sequence:
|
||||
// 1. Write payload to the mock server through ServeStream.
|
||||
// 2. Half-close the write side (CloseWrite) — signals EOF upstream.
|
||||
// 3. Sleep for halfCloseWait to make the race window explicit: a buggy
|
||||
// (timeout=0) pipe would already have torn down the connection here.
|
||||
// 4. Read the echo — must succeed because the patch keeps the pipe alive.
|
||||
func TestServeStreamWaitsForResponseAfterLocalClose(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
payload = "half-close-test"
|
||||
// halfCloseWait makes the race window visible: if ServeStream tears
|
||||
// down the connection on CloseWrite the read that follows will fail
|
||||
// immediately, mirroring the 3-second sleep in the cftunnel reference
|
||||
// test. It must be shorter than DefaultTimeoutAfterFirstClose (10s).
|
||||
halfCloseWait = 3 * time.Second
|
||||
// testTimeout is an upper bound for the whole test.
|
||||
testTimeout = stream.DefaultTimeoutAfterFirstClose + 5*time.Second
|
||||
)
|
||||
|
||||
server := websocketServer()
|
||||
defer server.Close()
|
||||
|
||||
localEnd, remoteEnd := newHalfClosePipe()
|
||||
|
||||
log := zerolog.Nop()
|
||||
wsConn := NewWSConnection(&log)
|
||||
options := &StartOptions{
|
||||
OriginURL: "ws://" + server.Listener.Addr().String(),
|
||||
}
|
||||
|
||||
serveErrCh := make(chan error, 1)
|
||||
go func() {
|
||||
serveErrCh <- wsConn.ServeStream(options, remoteEnd)
|
||||
}()
|
||||
|
||||
ctx, cancel := context.WithTimeout(t.Context(), testTimeout)
|
||||
defer cancel()
|
||||
|
||||
// 1. Write the payload. ServeStream forwards it as a WS binary frame.
|
||||
_, err := localEnd.Write([]byte(payload))
|
||||
require.NoError(t, err)
|
||||
|
||||
// 2. Half-close the write side
|
||||
require.NoError(t, localEnd.CloseWrite())
|
||||
|
||||
// 3. Wait to make the race window explicit.
|
||||
time.Sleep(halfCloseWait)
|
||||
|
||||
// 4. Read the echo.
|
||||
got := make([]byte, len(payload))
|
||||
_, err = io.ReadFull(localEnd, got)
|
||||
require.NoError(t, err, "read after half-close failed: pipe was torn down too early")
|
||||
require.Equal(t, payload, string(got))
|
||||
|
||||
// Drain ServeStream.
|
||||
_ = localEnd.Close()
|
||||
_ = remoteEnd.Close()
|
||||
|
||||
select {
|
||||
case err := <-serveErrCh:
|
||||
if err != nil && err != io.EOF && !errors.Is(err, io.ErrClosedPipe) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
t.Fatal("ServeStream did not return in time")
|
||||
}
|
||||
}
|
||||
|
||||
func websocketServer() *httptest.Server {
|
||||
upgrader := gws.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
CheckOrigin: func(*http.Request) bool { return true },
|
||||
}
|
||||
|
||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
_, msg, err := conn.ReadMessage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := conn.WriteMessage(gws.BinaryMessage, msg); err != nil {
|
||||
return
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
@@ -8,23 +13,85 @@ import (
|
||||
"github.com/cloudflare/cloudflared/cmd/cloudflared/tunnel"
|
||||
)
|
||||
|
||||
func buildArgsForToken(c *cli.Context, log *zerolog.Logger) ([]string, error) {
|
||||
token := c.Args().First()
|
||||
const (
|
||||
defaultTokenFile = "token"
|
||||
)
|
||||
|
||||
func ensureConfigDirExists(configDir string) error {
|
||||
if err := os.Mkdir(configDir, 0o755); err != nil { //nolint:gosec // config dir must be traversable by non-root user
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("create config dir at %s: %w", configDir, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createTokenFileUnix(path string) error {
|
||||
const tokenPerms os.FileMode = 0o600
|
||||
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, tokenPerms) //nolint:gosec // All callers of this function construct path from constant strings or well-known env vars (e.g., $HOME)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create token file at %s: %w", path, err)
|
||||
}
|
||||
defer func() { _ = f.Close() }()
|
||||
|
||||
// If the file already existed with unrestrictive permissions, os.OpenFile
|
||||
// will not update its permissions, so perform an extra os.Chmod
|
||||
if err := os.Chmod(path, tokenPerms); err != nil {
|
||||
return fmt.Errorf("chmod token file at %s: %w", path, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write out the token file to the configuration directory with the correct
|
||||
// permissions. Since the method used to restrict the permissions is platform
|
||||
// dependent, make the function used to restrict the permissions an injectable
|
||||
// dependency
|
||||
func writeTokenToFile(path string, token string) error {
|
||||
if _, err := tunnel.ParseToken(token); err != nil {
|
||||
return nil, cliutil.UsageError("Provided tunnel token is not valid (%s).", err)
|
||||
return cliutil.UsageError("Provided tunnel token is not valid (%s).", err)
|
||||
}
|
||||
|
||||
if err := createTokenFile(path); err != nil {
|
||||
return fmt.Errorf("create token file at %s: %w", path, err)
|
||||
}
|
||||
|
||||
// Won't update permissions as file already exists
|
||||
if err := os.WriteFile(path, []byte(token), 0o600); err != nil {
|
||||
return fmt.Errorf("write token to %s: %w", path, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeTokenFile(configDir string, log *zerolog.Logger) {
|
||||
tp := tokenPath(configDir)
|
||||
err := os.Remove(tp)
|
||||
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
log.Warn().Msgf("Could not remove service token file at %s: %v", tp, err)
|
||||
}
|
||||
}
|
||||
|
||||
func buildArgsForTokenFile(configDir string) []string {
|
||||
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
|
||||
"tunnel", "run", "--token-file", tokenPath(configDir),
|
||||
}
|
||||
}
|
||||
|
||||
func tokenPath(configDir string) string {
|
||||
return filepath.Join(configDir, defaultTokenFile)
|
||||
}
|
||||
|
||||
func writeTokenToConfigDir(c *cli.Context, configDir string) error {
|
||||
if err := ensureConfigDirExists(configDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := writeTokenToFile(tokenPath(configDir), c.Args().First()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -24,8 +24,21 @@ func runApp(app *cli.App, _ chan struct{}) {
|
||||
Usage: "Manages the cloudflared system service",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "install",
|
||||
Usage: "Install cloudflared as a system service",
|
||||
Name: "install",
|
||||
Usage: "Install cloudflared as a system service",
|
||||
ArgsUsage: "[TOKEN]",
|
||||
Description: `
|
||||
Installs cloudflared as a service using the detected init system (e.g., sysv,
|
||||
systemd, openrc).
|
||||
|
||||
A token may optionally be provided. If a token is provided, it will be written
|
||||
to disk in the service configuration directory and the cloudflared service
|
||||
configured to use it via the --token-file argument.
|
||||
|
||||
If no token is provided, cloudflared will attempt to find a configuration file
|
||||
with tunnel credentials from a predetermined list of configuration directory
|
||||
paths. If found, it will use that configuration file and credentials (or error
|
||||
out if no configuration file with credentials was found).`,
|
||||
Action: cliutil.ConfiguredAction(installLinuxService),
|
||||
Flags: []cli.Flag{
|
||||
noUpdateServiceFlag,
|
||||
@@ -54,6 +67,9 @@ const (
|
||||
cloudflaredOpenRCService = "cloudflared"
|
||||
)
|
||||
|
||||
// OS-specific function for token file creation
|
||||
var createTokenFile = createTokenFileUnix
|
||||
|
||||
var systemdAllTemplates = map[string]ServiceTemplate{
|
||||
cloudflaredService: {
|
||||
Path: fmt.Sprintf("/etc/systemd/system/%s", cloudflaredService),
|
||||
@@ -246,23 +262,39 @@ func installLinuxService(c *cli.Context) error {
|
||||
Path: etPath,
|
||||
}
|
||||
|
||||
// Check if the "no update flag" is set
|
||||
autoUpdate := !c.IsSet(noUpdateServiceFlag.Name)
|
||||
|
||||
var extraArgsFunc func(c *cli.Context, log *zerolog.Logger) ([]string, error)
|
||||
var extraArgs []string
|
||||
if c.NArg() == 0 {
|
||||
extraArgsFunc = buildArgsForConfig
|
||||
// If passed no arguments e.g., "$ cloudflared service install",
|
||||
// install the service using the detected config file (or error-out if
|
||||
// no config exists).
|
||||
if extraArgs, err = buildArgsForConfig(c, log); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
extraArgsFunc = buildArgsForToken
|
||||
}
|
||||
// If passed one argument e.g., "$ cloudflared service install <token>"
|
||||
// write the token to the config directory and install the service
|
||||
// using --token-file pointing to that file. This is the quick setup
|
||||
// the tunnel UI suggests.
|
||||
|
||||
extraArgs, err := extraArgsFunc(c, log)
|
||||
if err != nil {
|
||||
return err
|
||||
// Ensure token file is removed if install fails
|
||||
defer func() {
|
||||
if err != nil {
|
||||
removeTokenFile(serviceConfigDir, log)
|
||||
}
|
||||
}()
|
||||
|
||||
if err = writeTokenToConfigDir(c, serviceConfigDir); err != nil {
|
||||
return fmt.Errorf("could not write token to configuration directory: %w", err)
|
||||
}
|
||||
|
||||
extraArgs = buildArgsForTokenFile(serviceConfigDir)
|
||||
}
|
||||
|
||||
templateArgs.ExtraArgs = extraArgs
|
||||
|
||||
// Check if the "no update flag" is set
|
||||
autoUpdate := !c.IsSet(noUpdateServiceFlag.Name)
|
||||
|
||||
switch {
|
||||
case inits.IsSystemd():
|
||||
log.Info().Msgf("Using Systemd")
|
||||
@@ -423,9 +455,12 @@ func uninstallLinuxService(c *cli.Context) error {
|
||||
err = uninstallSysv(log)
|
||||
}
|
||||
|
||||
removeTokenFile(serviceConfigDir, log)
|
||||
|
||||
if err == nil {
|
||||
log.Info().Msg("Linux service for cloudflared uninstalled successfully")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -516,14 +551,6 @@ func uninstallOpenRC(log *zerolog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ensureConfigDirExists(configDir string) error {
|
||||
ok, err := config.FileExists(configDir)
|
||||
if !ok && err == nil {
|
||||
err = os.Mkdir(configDir, 0o755) //nolint:gosec // config dir must be traversable by a non-root service user
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func copyFile(src, dest string) error {
|
||||
srcFile, err := os.Open(src) //nolint:gosec // operator-provided service config path
|
||||
if err != nil {
|
||||
|
||||
@@ -5,6 +5,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/pkg/errors"
|
||||
@@ -18,14 +19,28 @@ const (
|
||||
launchdIdentifier = "com.cloudflare.cloudflared"
|
||||
)
|
||||
|
||||
// OS-specific function for token file creation
|
||||
var createTokenFile = createTokenFileUnix
|
||||
|
||||
func runApp(app *cli.App, _ chan struct{}) {
|
||||
app.Commands = append(app.Commands, &cli.Command{
|
||||
Name: "service",
|
||||
Usage: "Manages the cloudflared launch agent",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "install",
|
||||
Usage: "Install cloudflared as an user launch agent",
|
||||
Name: "install",
|
||||
Usage: "Install cloudflared as an user launch agent",
|
||||
ArgsUsage: "[TOKEN]",
|
||||
Description: `
|
||||
Installs cloudflared as a launchd-managed service.
|
||||
|
||||
A token may optionally be provided. If a token is provided, it will be written
|
||||
to disk in the service configuration directory and the cloudflared service
|
||||
configured to use it via the --token-file argument.
|
||||
|
||||
If no token is provided, cloudflared will run without the --token-file argument,
|
||||
causing it to look for credentials in a configuration file upon startup.`,
|
||||
|
||||
Action: cliutil.ConfiguredAction(installLaunchd),
|
||||
},
|
||||
{
|
||||
@@ -76,38 +91,45 @@ func isRootUser() bool {
|
||||
return os.Geteuid() == 0
|
||||
}
|
||||
|
||||
func installPath() (string, error) {
|
||||
// User is root, use /Library/LaunchDaemons instead of home directory
|
||||
func resolveLibraryPath(subPath, fileName string) (string, error) {
|
||||
const libraryDirName = "Library"
|
||||
|
||||
// We use the system-wide /Library/... instead of ~/Library/... if the user is root
|
||||
if isRootUser() {
|
||||
return fmt.Sprintf("/Library/LaunchDaemons/%s.plist", launchdIdentifier), nil
|
||||
return filepath.Join("/", libraryDirName, subPath, fileName), nil
|
||||
}
|
||||
userHomeDir, err := userHomeDir()
|
||||
|
||||
// This returns the home dir of the executing user using OS-specific method
|
||||
// for discovering the home dir. It's not recommended to call this when the
|
||||
// user has root permission as $HOME depends on what options the user uses
|
||||
// with sudo.
|
||||
userHomeDir, err := homedir.Dir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", errors.Wrap(err, "Cannot determine home directory for the user")
|
||||
}
|
||||
return fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", userHomeDir, launchdIdentifier), nil
|
||||
return filepath.Join(userHomeDir, libraryDirName, subPath, fileName), nil
|
||||
}
|
||||
|
||||
// For docs on these subdirectories, see:
|
||||
// https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html
|
||||
func installPath() (string, error) {
|
||||
subpath := "LaunchAgents"
|
||||
if isRootUser() {
|
||||
subpath = "LaunchDaemons"
|
||||
}
|
||||
return resolveLibraryPath(subpath, launchdIdentifier+".plist")
|
||||
}
|
||||
|
||||
func stdoutPath() (string, error) {
|
||||
if isRootUser() {
|
||||
return fmt.Sprintf("/Library/Logs/%s.out.log", launchdIdentifier), nil
|
||||
}
|
||||
userHomeDir, err := userHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%s/Library/Logs/%s.out.log", userHomeDir, launchdIdentifier), nil
|
||||
return resolveLibraryPath("Logs", launchdIdentifier+".out.log")
|
||||
}
|
||||
|
||||
func stderrPath() (string, error) {
|
||||
if isRootUser() {
|
||||
return fmt.Sprintf("/Library/Logs/%s.err.log", launchdIdentifier), nil
|
||||
}
|
||||
userHomeDir, err := userHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%s/Library/Logs/%s.err.log", userHomeDir, launchdIdentifier), nil
|
||||
return resolveLibraryPath("Logs", launchdIdentifier+".err.log")
|
||||
}
|
||||
|
||||
func configPath() (string, error) {
|
||||
return resolveLibraryPath("Application Support", launchdIdentifier)
|
||||
}
|
||||
|
||||
func installLaunchd(c *cli.Context) error {
|
||||
@@ -125,18 +147,45 @@ func installLaunchd(c *cli.Context) error {
|
||||
etPath, err := os.Executable()
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Error determining executable path")
|
||||
return fmt.Errorf("Error determining executable path: %v", err)
|
||||
return fmt.Errorf("error determining executable path: %w", err)
|
||||
}
|
||||
installPath, err := installPath()
|
||||
if err != nil {
|
||||
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)
|
||||
|
||||
var extraArgs []string
|
||||
if c.NArg() > 0 {
|
||||
// The service has been installed using a token e.g.,
|
||||
// $ cloudflared service install <token>
|
||||
//
|
||||
// Write the token file to a config directory so we can start the
|
||||
// daemon with --token-file
|
||||
|
||||
// Don't use :=, if we did so we would create a new err variable and
|
||||
// shadow the outer one, causing the defer below to not have access to
|
||||
// the outer err
|
||||
var cp string
|
||||
cp, err = configPath()
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Error determining path to config directory")
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure token file is removed if install fails at any point from now
|
||||
// on
|
||||
defer func() {
|
||||
if err != nil {
|
||||
removeTokenFile(cp, log)
|
||||
}
|
||||
}()
|
||||
|
||||
if err = writeTokenToConfigDir(c, cp); err != nil {
|
||||
return fmt.Errorf("could not write token to configuration directory: %w", err)
|
||||
}
|
||||
|
||||
extraArgs = buildArgsForTokenFile(cp)
|
||||
}
|
||||
|
||||
stdoutPath, err := stdoutPath()
|
||||
@@ -206,17 +255,13 @@ func uninstallLaunchd(c *cli.Context) error {
|
||||
if err == nil {
|
||||
log.Info().Msg("Launchd for cloudflared was uninstalled successfully")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func userHomeDir() (string, error) {
|
||||
// This returns the home dir of the executing user using OS-specific method
|
||||
// for discovering the home dir. It's not recommended to call this function
|
||||
// when the user has root permission as $HOME depends on what options the user
|
||||
// use with sudo.
|
||||
homeDir, err := homedir.Dir()
|
||||
cp, err := configPath()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "Cannot determine home directory for the user")
|
||||
log.Err(err).Msg("error determining path to config directory, not removing token file")
|
||||
return err
|
||||
}
|
||||
return homeDir, nil
|
||||
removeTokenFile(cp, log)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import (
|
||||
"github.com/cloudflare/cloudflared/diagnostic"
|
||||
"github.com/cloudflare/cloudflared/edgediscovery"
|
||||
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
|
||||
"github.com/cloudflare/cloudflared/features"
|
||||
"github.com/cloudflare/cloudflared/ingress"
|
||||
"github.com/cloudflare/cloudflared/logger"
|
||||
"github.com/cloudflare/cloudflared/management"
|
||||
@@ -421,7 +422,7 @@ func StartServer(
|
||||
// goroutine, as we want to keep initializing cloudflared while prechecks
|
||||
// are running. Prechecks are controlled via DNS flag for remote kill-switch capability.
|
||||
if !tunnelConfig.ClientConfig.ConnectionFeaturesSnapshot().SkipPrechecks && !c.Bool(cfdflags.NoPrechecks) {
|
||||
go runPrechecks(c, log, tunnelConfig.Region)
|
||||
go runPrechecks(c, log, tunnelConfig.Region, tunnelConfig.ClientConfig.ConnectionFeaturesSnapshot().PostQuantum)
|
||||
}
|
||||
|
||||
// Disable ICMP packet routing for quick tunnels
|
||||
@@ -525,7 +526,7 @@ func StartServer(
|
||||
|
||||
// runPrechecks executes connectivity pre-checks and logs the results.
|
||||
// Pre-checks are diagnostic only and do not gate tunnel startup.
|
||||
func runPrechecks(c *cli.Context, log *zerolog.Logger, region string) {
|
||||
func runPrechecks(c *cli.Context, log *zerolog.Logger, region string, pqMode features.PostQuantumMode) {
|
||||
ipVersion := allregions.Auto
|
||||
if ipVersionStr := c.String(cfdflags.EdgeIpVersion); ipVersionStr != "" {
|
||||
parsedVersion, err := parseConfigIPVersion(ipVersionStr)
|
||||
@@ -550,7 +551,7 @@ func runPrechecks(c *cli.Context, log *zerolog.Logger, region string) {
|
||||
ManagementDialer: &prechecks.NetManagementDialer{Dialer: net.Dialer{}},
|
||||
}
|
||||
|
||||
report := prechecks.Run(c.Context, c.String(cfdflags.CACert), cfg, log, dialers)
|
||||
report := prechecks.Run(c.Context, c.String(cfdflags.CACert), cfg, pqMode, log, dialers)
|
||||
|
||||
// Output the human-readable table
|
||||
cliutil.LogTable(log, report.String(), "CONNECTIVITY PRE-CHECKS")
|
||||
|
||||
@@ -299,5 +299,5 @@ func wasInstalledFromPackageManager() bool {
|
||||
}
|
||||
|
||||
func isRunningFromTerminal() bool {
|
||||
return term.IsTerminal(int(os.Stdout.Fd())) //nolint:gosec
|
||||
return term.IsTerminal(int(os.Stdout.Fd())) // nolint:gosec
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
@@ -28,6 +29,12 @@ const (
|
||||
windowsServiceDescription = "Cloudflared agent"
|
||||
windowsServiceUrl = "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configure-tunnels/local-management/as-a-service/windows/"
|
||||
|
||||
// Env var that points to a directory for storing application-specific
|
||||
// configuration and data (analogous to /etc/). Normally this points to
|
||||
// C:\ProgramData.
|
||||
programDataEnvVar = "PROGRAMDATA"
|
||||
configDirName = "cloudflared"
|
||||
|
||||
recoverActionDelay = time.Second * 20
|
||||
failureCountResetPeriod = time.Hour * 24
|
||||
|
||||
@@ -48,8 +55,18 @@ func runApp(app *cli.App, graceShutdownC chan struct{}) {
|
||||
Usage: "Manages the cloudflared Windows service",
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "install",
|
||||
Usage: "Install cloudflared as a Windows service",
|
||||
Name: "install",
|
||||
Usage: "Install cloudflared as a Windows service",
|
||||
ArgsUsage: "[TOKEN]",
|
||||
Description: `
|
||||
Installs cloudflared as a Windows service
|
||||
|
||||
A token may optionally be provided. If a token is provided, it will be written
|
||||
to disk in the service configuration directory and the cloudflared service
|
||||
configured to use it via the --token-file argument.
|
||||
|
||||
If no token is provided, cloudflared will run without the --token-file argument,
|
||||
causing it to look for credentials in a configuration file upon startup.`,
|
||||
Action: cliutil.ConfiguredAction(installWindowsService),
|
||||
},
|
||||
{
|
||||
@@ -96,6 +113,105 @@ func runApp(app *cli.App, graceShutdownC chan struct{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// Creates the token file at the given path, restricting its permissions by
|
||||
// modifying its Windows ACLs. We change the ACLs on the token file such that
|
||||
// the Administrators group and SYSTEM account (which is what cloudflared runs
|
||||
// as) have full access and all others are denied, with the Administrator group
|
||||
// owning the file.
|
||||
func createTokenFile(path string) error {
|
||||
// This is a Windows Security Descriptor string describing the permissions
|
||||
// we apply to the token file. This is the domain-specific language Windows
|
||||
// uses for representing access rights.
|
||||
//
|
||||
// - O:BA -> Set the owner to the builtin administrators group (BA)
|
||||
// - D: -> Start of discretionary access control list describing access rights
|
||||
// - P -> Set the SE_DACL_PROTECTED flag, which prevents the file from
|
||||
// inheriting the (usually permissive) ACEs from its parent directory
|
||||
// - (A;;FA;;;BA) -> ACE #1: Allow (A) Full access (FA) to the Builtin Administrators group (BA)
|
||||
// - (A;;FA;;;SY) -> ACE #2: Ditto but for the Local System user (SY)
|
||||
//
|
||||
// Relevant Docs:
|
||||
//
|
||||
// - SecurityDescriptor string as a whole:
|
||||
// https://learn.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-string-format
|
||||
// - SID Strings such as BA/SY
|
||||
// https://learn.microsoft.com/en-us/windows/win32/secauthz/sid-strings
|
||||
// - ACE Strings such as (A;;FA;;BA)
|
||||
// https://learn.microsoft.com/en-us/windows/win32/secauthz/ace-strings
|
||||
const sdString = "O:BAD:P(A;;FA;;;BA)(A;;FA;;;SY)"
|
||||
sd, err := windows.SecurityDescriptorFromString(sdString)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create token security descriptor: %w", err)
|
||||
}
|
||||
|
||||
pathRaw, err := windows.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("convert path to UTF-16: %w", err)
|
||||
}
|
||||
|
||||
f, err := windows.CreateFile(
|
||||
pathRaw,
|
||||
windows.GENERIC_WRITE,
|
||||
0,
|
||||
&windows.SecurityAttributes{
|
||||
Length: uint32(unsafe.Sizeof(windows.SecurityAttributes{})),
|
||||
SecurityDescriptor: sd,
|
||||
InheritHandle: 0,
|
||||
},
|
||||
windows.CREATE_ALWAYS, // Will truncate the file if it exists
|
||||
windows.FILE_ATTRIBUTE_NORMAL,
|
||||
0,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("create token file: %w", err)
|
||||
}
|
||||
|
||||
if err := windows.CloseHandle(f); err != nil {
|
||||
return fmt.Errorf("close token file: %w", err)
|
||||
}
|
||||
|
||||
// As with os.CreateFile / os.OpenFile on Unix, if the file already exists
|
||||
// windows.CreateFile will not update the permission information, so we do
|
||||
// that explicitly after creating the file.
|
||||
|
||||
owner, _, err := sd.Owner()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get token file owner: %w", err)
|
||||
}
|
||||
|
||||
dacl, _, err := sd.DACL()
|
||||
if err != nil {
|
||||
return fmt.Errorf("get token file DACL: %w", err)
|
||||
}
|
||||
|
||||
// Bitmask indicating which security info we want to set on the file:
|
||||
//
|
||||
// OWNER_SECURITY_INFORMATION
|
||||
// -> Set file owner
|
||||
// DACL_SECURITY_INFORMATION
|
||||
// -> Set ACEs
|
||||
// PROTECTED_DACL_SECURITY_INFORMATION
|
||||
// -> Update DACL to be "protected' such that it cannot inherit entries from its parent
|
||||
const securityInfo = windows.OWNER_SECURITY_INFORMATION |
|
||||
windows.DACL_SECURITY_INFORMATION |
|
||||
windows.PROTECTED_DACL_SECURITY_INFORMATION
|
||||
|
||||
if err := windows.SetNamedSecurityInfo(
|
||||
path,
|
||||
windows.SE_FILE_OBJECT,
|
||||
securityInfo,
|
||||
owner,
|
||||
nil,
|
||||
dacl,
|
||||
nil,
|
||||
); err != nil {
|
||||
return fmt.Errorf("set token file security info: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type windowsService struct {
|
||||
app *cli.App
|
||||
graceShutdownC chan struct{}
|
||||
@@ -173,6 +289,15 @@ func (s *windowsService) Execute(serviceArgs []string, r <-chan svc.ChangeReques
|
||||
}
|
||||
}
|
||||
|
||||
func getConfigDir() (string, error) {
|
||||
progDat, progDatSet := os.LookupEnv(programDataEnvVar)
|
||||
if !progDatSet {
|
||||
return "", fmt.Errorf("could not find program data directory, %s env var must be set", programDataEnvVar)
|
||||
}
|
||||
|
||||
return filepath.Join(progDat, configDirName), nil
|
||||
}
|
||||
|
||||
func installWindowsService(c *cli.Context) error {
|
||||
zeroLogger := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
|
||||
|
||||
@@ -192,11 +317,35 @@ func installWindowsService(c *cli.Context) error {
|
||||
s.Close()
|
||||
return errors.New(serviceAlreadyExistsWarn(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)
|
||||
var extraArgs []string
|
||||
if c.NArg() > 0 {
|
||||
// The service has been installed using a token e.g.,
|
||||
// $ cloudflared service install <token>
|
||||
//
|
||||
// Write the token file to a config directory so we can start the
|
||||
// service with --token-file.
|
||||
|
||||
// Don't use :=, if we did so we would create a new err variable and
|
||||
// shadow the outer one, causing the defer below to not have access to
|
||||
// the outer err
|
||||
var configDir string
|
||||
configDir, err = getConfigDir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("locate config dir: %w", err)
|
||||
}
|
||||
|
||||
// Remove token file if service install fails any point onwards from here
|
||||
defer func() {
|
||||
if err != nil {
|
||||
removeTokenFile(configDir, zeroLogger)
|
||||
}
|
||||
}()
|
||||
|
||||
if err = writeTokenToConfigDir(c, configDir); err != nil {
|
||||
return fmt.Errorf("write token to configuration directory at %s: %w", configDir, err)
|
||||
}
|
||||
|
||||
extraArgs = buildArgsForTokenFile(configDir)
|
||||
}
|
||||
|
||||
config := mgr.Config{StartType: mgr.StartAutomatic, DisplayName: windowsServiceDescription}
|
||||
@@ -219,10 +368,13 @@ func installWindowsService(c *cli.Context) error {
|
||||
}
|
||||
|
||||
err = s.Start()
|
||||
if err == nil {
|
||||
log.Info().Msg("Agent service for cloudflared installed successfully")
|
||||
if err != nil {
|
||||
s.Delete()
|
||||
return errors.Wrap(err, "Cannot start service")
|
||||
}
|
||||
return err
|
||||
|
||||
log.Info().Msg("Agent service for cloudflared installed successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
func uninstallWindowsService(c *cli.Context) error {
|
||||
@@ -258,6 +410,15 @@ func uninstallWindowsService(c *cli.Context) error {
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Cannot remove event logger")
|
||||
}
|
||||
|
||||
configDir, err := getConfigDir()
|
||||
if err != nil {
|
||||
// We don't need to hard-error out here, this isn't critical, but we should log it
|
||||
log.Warn().Err(err).Msgf("Failed to find configuration directory, not removing secret token file")
|
||||
} else {
|
||||
removeTokenFile(configDir, &log)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ func wsEchoEndpoint(w ResponseWriter, r *http.Request) error {
|
||||
}()
|
||||
|
||||
originConn := &echoPipe{reader: readPipe, writer: writePipe}
|
||||
stream.Pipe(wsConn, originConn, 0, &log)
|
||||
stream.Pipe(wsConn, originConn, &log)
|
||||
cancel()
|
||||
wsConn.Close()
|
||||
return nil
|
||||
@@ -190,7 +190,7 @@ func wsFlakyEndpoint(w ResponseWriter, r *http.Request) error {
|
||||
rInt, _ := rand.Int(rand.Reader, big.NewInt(50))
|
||||
closedAfter := time.Millisecond * time.Duration(rInt.Int64())
|
||||
originConn := &flakyConn{closeAt: time.Now().Add(closedAfter)}
|
||||
stream.Pipe(wsConn, originConn, 0, &log)
|
||||
stream.Pipe(wsConn, originConn, &log)
|
||||
cancel()
|
||||
wsConn.Close()
|
||||
return nil
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
network "github.com/cloudflare/cloudflared/diagnostic/network"
|
||||
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
|
||||
"github.com/cloudflare/cloudflared/features"
|
||||
"github.com/cloudflare/cloudflared/prechecks"
|
||||
)
|
||||
|
||||
@@ -470,7 +471,7 @@ func collectPrechecks(region string) collectFunc {
|
||||
}
|
||||
|
||||
emptyCert := ""
|
||||
report := prechecks.Run(ctx, emptyCert, cfg, &log, dialers)
|
||||
report := prechecks.Run(ctx, emptyCert, cfg, features.PostQuantumPrefer, &log, dialers)
|
||||
|
||||
// Write the report to a JSON file
|
||||
// nolint: gosec
|
||||
|
||||
@@ -36,11 +36,11 @@ require (
|
||||
go.opentelemetry.io/proto/otlp v1.10.0
|
||||
go.uber.org/automaxprocs v1.6.0
|
||||
go.uber.org/mock v0.5.1
|
||||
golang.org/x/crypto v0.52.0
|
||||
golang.org/x/net v0.55.0
|
||||
golang.org/x/sync v0.20.0
|
||||
golang.org/x/sys v0.45.0
|
||||
golang.org/x/term v0.43.0
|
||||
golang.org/x/crypto v0.53.0
|
||||
golang.org/x/net v0.56.0
|
||||
golang.org/x/sync v0.22.0
|
||||
golang.org/x/sys v0.46.0
|
||||
golang.org/x/term v0.44.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
@@ -91,10 +91,10 @@ require (
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
golang.org/x/arch v0.4.0 // indirect
|
||||
golang.org/x/mod v0.35.0 // indirect
|
||||
golang.org/x/mod v0.37.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/text v0.37.0 // indirect
|
||||
golang.org/x/tools v0.44.0 // indirect
|
||||
golang.org/x/text v0.40.0 // indirect
|
||||
golang.org/x/tools v0.47.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||
google.golang.org/grpc v1.81.1 // indirect
|
||||
|
||||
@@ -245,21 +245,21 @@ golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc=
|
||||
golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
||||
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
||||
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
|
||||
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
|
||||
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -269,20 +269,20 @@ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
|
||||
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
|
||||
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
|
||||
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
|
||||
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=
|
||||
|
||||
@@ -25,9 +25,9 @@ type OriginConnection interface {
|
||||
type streamHandlerFunc func(originConn io.ReadWriter, remoteConn net.Conn, log *zerolog.Logger)
|
||||
|
||||
// DefaultStreamHandler is an implementation of streamHandlerFunc that
|
||||
// performs a two-way io.Copy between originConn and remoteConn.
|
||||
// performs a two way io.Copy between originConn and remoteConn.
|
||||
func DefaultStreamHandler(originConn io.ReadWriter, remoteConn net.Conn, log *zerolog.Logger) {
|
||||
stream.Pipe(originConn, remoteConn, stream.DefaultTimeoutAfterFirstClose, log)
|
||||
stream.Pipe(originConn, remoteConn, log)
|
||||
}
|
||||
|
||||
// tcpConnection is an OriginConnection that directly streams to raw TCP.
|
||||
@@ -38,7 +38,7 @@ type tcpConnection struct {
|
||||
}
|
||||
|
||||
func (tc *tcpConnection) Stream(_ context.Context, tunnelConn io.ReadWriter, _ *zerolog.Logger) {
|
||||
stream.Pipe(tunnelConn, tc, stream.DefaultTimeoutAfterFirstClose, tc.logger)
|
||||
stream.Pipe(tunnelConn, tc, tc.logger)
|
||||
}
|
||||
|
||||
func (tc *tcpConnection) Write(b []byte) (int, error) {
|
||||
|
||||
@@ -38,7 +38,6 @@ func TestStreamTCPConnection(t *testing.T) {
|
||||
tcpConn := tcpConnection{
|
||||
Conn: cfdConn,
|
||||
writeTimeout: 30 * time.Second,
|
||||
logger: TestLogger,
|
||||
}
|
||||
|
||||
eyeballConn, edgeConn := net.Pipe()
|
||||
@@ -158,7 +157,7 @@ func TestSocksStreamWSOverTCPConnection(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
defer func() { _ = wsForwarderInConn.Close() }()
|
||||
|
||||
stream.Pipe(wsForwarderInConn, &wsEyeball{wsForwarderOutConn}, stream.DefaultTimeoutAfterFirstClose, TestLogger)
|
||||
stream.Pipe(wsForwarderInConn, &wsEyeball{wsForwarderOutConn}, TestLogger)
|
||||
return nil
|
||||
})
|
||||
|
||||
|
||||
@@ -99,6 +99,7 @@ type rawTCPService struct {
|
||||
name string
|
||||
dialer net.Dialer
|
||||
writeTimeout time.Duration
|
||||
logger *zerolog.Logger
|
||||
}
|
||||
|
||||
func (o *rawTCPService) String() string {
|
||||
@@ -232,12 +233,10 @@ func (o *helloWorld) start(
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Cannot start Hello World Server")
|
||||
}
|
||||
go func() {
|
||||
_ = hello.StartHelloWorldServer(log, helloListener, shutdownC)
|
||||
}()
|
||||
go hello.StartHelloWorldServer(log, helloListener, shutdownC)
|
||||
o.server = helloListener
|
||||
|
||||
o.url = &url.URL{
|
||||
o.httpService.url = &url.URL{
|
||||
Scheme: "https",
|
||||
Host: o.server.Addr().String(),
|
||||
}
|
||||
@@ -357,7 +356,7 @@ func newHTTPTransport(service OriginService, cfg OriginRequestConfig, log *zerol
|
||||
IdleConnTimeout: cfg.KeepAliveTimeout.Duration,
|
||||
TLSHandshakeTimeout: cfg.TLSTimeout.Duration,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: &tls.Config{RootCAs: originCertPool, InsecureSkipVerify: cfg.NoTLSVerify}, //nolint: gosec
|
||||
TLSClientConfig: &tls.Config{RootCAs: originCertPool, InsecureSkipVerify: cfg.NoTLSVerify},
|
||||
ForceAttemptHTTP2: cfg.Http2Origin,
|
||||
}
|
||||
if _, isHelloWorld := service.(*helloWorld); !isHelloWorld && cfg.OriginServerName != "" {
|
||||
@@ -375,6 +374,7 @@ func newHTTPTransport(service OriginService, cfg OriginRequestConfig, log *zerol
|
||||
// DialContext depends on which kind of origin is being used.
|
||||
dialContext := dialer.DialContext
|
||||
switch service := service.(type) {
|
||||
|
||||
// If this origin is a unix socket, enforce network type "unix".
|
||||
case *unixSocketPath:
|
||||
httpTransport.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/cloudflare/cloudflared/connection"
|
||||
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
|
||||
"github.com/cloudflare/cloudflared/features"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -59,7 +60,10 @@ func (tr TransportResults) Collect() []CheckResult {
|
||||
//
|
||||
// Each failed probe is retried up to maxRetries times with exponential backoff.
|
||||
// The suite is bounded by cfg.Timeout (defaultTimeout if zero).
|
||||
func Run(ctx context.Context, caCert string, cfg Config, log *zerolog.Logger, runDialers RunDialers) Report {
|
||||
//
|
||||
// pqMode controls the TLS curve preferences advertised during probe handshakes,
|
||||
// matching the key-exchange algorithms used by the real tunnel connections.
|
||||
func Run(ctx context.Context, caCert string, cfg Config, pqMode features.PostQuantumMode, log *zerolog.Logger, runDialers RunDialers) Report {
|
||||
runID := uuid.New()
|
||||
|
||||
if cfg.Timeout <= 0 {
|
||||
@@ -68,9 +72,10 @@ func Run(ctx context.Context, caCert string, cfg Config, log *zerolog.Logger, ru
|
||||
ctx, cancel := context.WithTimeout(ctx, cfg.Timeout)
|
||||
defer cancel()
|
||||
|
||||
// Build TLS configs once per protocol.
|
||||
quicTLSConfig, quicTLSErr := probeTLSConfig(caCert, connection.QUIC)
|
||||
http2TLSConfig, http2TLSErr := probeTLSConfig(caCert, connection.HTTP2)
|
||||
// Build TLS configs once per protocol, applying the same curve preferences
|
||||
// (including post-quantum curves) used by production tunnel connections.
|
||||
quicTLSConfig, quicTLSErr := probeTLSConfig(caCert, connection.QUIC, pqMode)
|
||||
http2TLSConfig, http2TLSErr := probeTLSConfig(caCert, connection.HTTP2, pqMode)
|
||||
|
||||
// 1) Resolve edge addresses. Each ResolvedTarget bundles its addr group
|
||||
// with the DNS CheckResult that labels it, keeping the two in sync.
|
||||
|
||||
+19
-18
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"github.com/cloudflare/cloudflared/connection"
|
||||
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
|
||||
"github.com/cloudflare/cloudflared/features"
|
||||
"github.com/cloudflare/cloudflared/mocks"
|
||||
)
|
||||
|
||||
@@ -119,7 +120,7 @@ func TestRun_AllPass(t *testing.T) {
|
||||
Return(nopConn{}, nil)
|
||||
|
||||
report := Run(t.Context(), emptyCert, Config{Timeout: 2 * time.Second, IPVersion: allregions.Auto},
|
||||
nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
features.PostQuantumPrefer, nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
// 2 DNS + 2 QUIC + 2 HTTP2 + 1 API = 7 results.
|
||||
requireStatuses(t, report, Pass, Pass, Pass, Pass, Pass, Pass, Pass)
|
||||
@@ -150,7 +151,7 @@ func TestRun_QUICBlocked(t *testing.T) {
|
||||
Return(nopConn{}, nil)
|
||||
|
||||
report := Run(t.Context(), emptyCert, Config{Timeout: 2 * time.Second, IPVersion: allregions.Auto},
|
||||
nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
features.PostQuantumPrefer, nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
// 2 DNS Pass + 2 QUIC Fail + 2 HTTP2 Pass + 1 API Pass.
|
||||
requireStatuses(t, report, Pass, Pass, Fail, Fail, Pass, Pass, Pass)
|
||||
@@ -180,7 +181,7 @@ func TestRun_HTTP2Blocked(t *testing.T) {
|
||||
Return(nopConn{}, nil)
|
||||
|
||||
report := Run(t.Context(), emptyCert, Config{Timeout: 2 * time.Second, IPVersion: allregions.Auto},
|
||||
nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
features.PostQuantumPrefer, nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
// 2 DNS Pass + 2 QUIC Pass + 2 HTTP2 Fail + 1 API Pass.
|
||||
requireStatuses(t, report, Pass, Pass, Pass, Pass, Fail, Fail, Pass)
|
||||
@@ -210,7 +211,7 @@ func TestRun_BothTransportsBlocked(t *testing.T) {
|
||||
Return(nopConn{}, nil)
|
||||
|
||||
report := Run(t.Context(), emptyCert, Config{Timeout: 2 * time.Second, IPVersion: allregions.Auto},
|
||||
nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
features.PostQuantumPrefer, nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
// 2 DNS Pass + 2 QUIC Fail + 2 HTTP2 Fail + 1 API Pass.
|
||||
requireStatuses(t, report, Pass, Pass, Fail, Fail, Fail, Fail, Pass)
|
||||
@@ -249,7 +250,7 @@ func TestRun_PartialRegionQUICFail(t *testing.T) {
|
||||
Return(nopConn{}, nil)
|
||||
|
||||
report := Run(t.Context(), emptyCert, Config{Timeout: 2 * time.Second, IPVersion: allregions.Auto},
|
||||
nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
features.PostQuantumPrefer, nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
// 2 DNS Pass + QUIC-region1 Pass + QUIC-region2 Fail + 2 HTTP2 Pass + 1 API Pass.
|
||||
requireStatuses(t, report, Pass, Pass, Pass, Fail, Pass, Pass, Pass)
|
||||
@@ -282,7 +283,7 @@ func TestRun_DNSFail_SkipsTransports(t *testing.T) {
|
||||
Return(nopConn{}, nil)
|
||||
|
||||
report := Run(t.Context(), emptyCert, Config{Timeout: 2 * time.Second, IPVersion: allregions.Auto},
|
||||
nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
features.PostQuantumPrefer, nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
// DNS failure emits 2 Fail rows (one per default region).
|
||||
// Transport rows: one skip per DNS region for QUIC and HTTP/2 = 2 QUIC skips + 2 HTTP2 skips.
|
||||
@@ -319,7 +320,7 @@ func TestRun_ManagementAPIFail(t *testing.T) {
|
||||
Return(nil, errors.New("connection refused")).AnyTimes()
|
||||
|
||||
report := Run(t.Context(), emptyCert, Config{Timeout: 2 * time.Second, IPVersion: allregions.Auto},
|
||||
nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
features.PostQuantumPrefer, nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
// 2 DNS Pass + 2 QUIC Pass + 2 HTTP2 Pass + 1 API Fail.
|
||||
requireStatuses(t, report, Pass, Pass, Pass, Pass, Pass, Pass, Fail)
|
||||
@@ -350,7 +351,7 @@ func TestRun_RegionFlagForwardedToDNS(t *testing.T) {
|
||||
Return(nopConn{}, nil)
|
||||
|
||||
report := Run(t.Context(), emptyCert, Config{Region: "us", Timeout: 2 * time.Second, IPVersion: allregions.Auto},
|
||||
nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
features.PostQuantumPrefer, nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
// DNS rows carry regional hostnames (indices 0 and 1).
|
||||
assert.Equal(t, "us-region1.v2.argotunnel.com", report.Results[0].Target, "DNS region1")
|
||||
@@ -388,7 +389,7 @@ func TestRun_QUICUsesProbeConnIndex(t *testing.T) {
|
||||
Return(nopConn{}, nil)
|
||||
|
||||
Run(t.Context(), emptyCert, Config{Timeout: 2 * time.Second, IPVersion: allregions.Auto},
|
||||
nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
features.PostQuantumPrefer, nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
}
|
||||
|
||||
// TestRun_BothFamiliesProbed verifies that when both V4 and V6 addresses are
|
||||
@@ -412,7 +413,7 @@ func TestRun_BothFamiliesProbed(t *testing.T) {
|
||||
Return(nopConn{}, nil)
|
||||
|
||||
report := Run(t.Context(), emptyCert, Config{Timeout: 2 * time.Second, IPVersion: allregions.Auto},
|
||||
nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
features.PostQuantumPrefer, nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
// 2 DNS + 2 QUIC + 2 HTTP2 + 1 API = 7 results, all passing.
|
||||
requireStatuses(t, report, Pass, Pass, Pass, Pass, Pass, Pass, Pass)
|
||||
@@ -454,7 +455,7 @@ func TestRun_IPVersionRestriction(t *testing.T) {
|
||||
Return(nopConn{}, nil)
|
||||
|
||||
report := Run(t.Context(), emptyCert, Config{Timeout: 2 * time.Second, IPVersion: tt.ipVersion},
|
||||
nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
features.PostQuantumPrefer, nopLogger(), RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
requireStatuses(t, report, Pass, Pass, Pass, Pass, Pass, Pass, Pass)
|
||||
})
|
||||
@@ -489,7 +490,7 @@ func TestRun_EdgeAddrs_SingleAddr(t *testing.T) {
|
||||
Timeout: 2 * time.Second,
|
||||
IPVersion: allregions.Auto,
|
||||
}
|
||||
report := Run(t.Context(), emptyCert, cfg, nopLogger(),
|
||||
report := Run(t.Context(), emptyCert, cfg, features.PostQuantumPrefer, nopLogger(),
|
||||
RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
// 1 DNS Skip + 1 QUIC + 1 HTTP2 + 1 API = 4 results.
|
||||
@@ -527,7 +528,7 @@ func TestRun_EdgeAddrs_MultipleAddrs(t *testing.T) {
|
||||
Timeout: 2 * time.Second,
|
||||
IPVersion: allregions.Auto,
|
||||
}
|
||||
report := Run(t.Context(), emptyCert, cfg, nopLogger(),
|
||||
report := Run(t.Context(), emptyCert, cfg, features.PostQuantumPrefer, nopLogger(),
|
||||
RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
// 2 DNS Pass (one per addr) + 2 QUIC + 2 HTTP2 + 1 API = 7 results.
|
||||
@@ -567,7 +568,7 @@ func TestRun_EdgeAddrs_UnresolvableAddr(t *testing.T) {
|
||||
Timeout: 2 * time.Second,
|
||||
IPVersion: allregions.Auto,
|
||||
}
|
||||
report := Run(t.Context(), emptyCert, cfg, nopLogger(),
|
||||
report := Run(t.Context(), emptyCert, cfg, features.PostQuantumPrefer, nopLogger(),
|
||||
RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
// 1 DNS Fail + 1 QUIC Skip + 1 HTTP2 Skip + 1 API = 4 results.
|
||||
@@ -609,7 +610,7 @@ func TestRun_ProtocolOverride_HTTP2_BothPass(t *testing.T) {
|
||||
IPVersion: allregions.Auto,
|
||||
ProtocolOverride: "http2",
|
||||
}
|
||||
report := Run(t.Context(), emptyCert, cfg, nopLogger(),
|
||||
report := Run(t.Context(), emptyCert, cfg, features.PostQuantumPrefer, nopLogger(),
|
||||
RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
// Both transports pass, but the override must win — HTTP/2 is reported.
|
||||
@@ -644,7 +645,7 @@ func TestRun_ProtocolOverride_QUIC_BothPass(t *testing.T) {
|
||||
IPVersion: allregions.Auto,
|
||||
ProtocolOverride: "quic",
|
||||
}
|
||||
report := Run(t.Context(), emptyCert, cfg, nopLogger(),
|
||||
report := Run(t.Context(), emptyCert, cfg, features.PostQuantumPrefer, nopLogger(),
|
||||
RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
require.NotNil(t, report.SuggestedProtocol)
|
||||
@@ -677,7 +678,7 @@ func TestRun_ProtocolOverride_HTTP2_QUICBlocked(t *testing.T) {
|
||||
IPVersion: allregions.Auto,
|
||||
ProtocolOverride: "http2",
|
||||
}
|
||||
report := Run(t.Context(), emptyCert, cfg, nopLogger(),
|
||||
report := Run(t.Context(), emptyCert, cfg, features.PostQuantumPrefer, nopLogger(),
|
||||
RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
require.NotNil(t, report.SuggestedProtocol)
|
||||
@@ -710,7 +711,7 @@ func TestRun_ProtocolOverride_HTTP2_BothBlocked(t *testing.T) {
|
||||
IPVersion: allregions.Auto,
|
||||
ProtocolOverride: "http2",
|
||||
}
|
||||
report := Run(t.Context(), emptyCert, cfg, nopLogger(),
|
||||
report := Run(t.Context(), emptyCert, cfg, features.PostQuantumPrefer, nopLogger(),
|
||||
RunDialers{DNSResolver: dns, TCPDialer: tcp, QUICDialer: quicD, ManagementDialer: mgmt})
|
||||
|
||||
// The overridden transport (HTTP/2) is blocked, so the override cannot be
|
||||
|
||||
+13
-4
@@ -15,8 +15,10 @@ import (
|
||||
"github.com/cloudflare/cloudflared/connection/dialopts"
|
||||
|
||||
"github.com/cloudflare/cloudflared/connection"
|
||||
cfdcrypto "github.com/cloudflare/cloudflared/crypto"
|
||||
edgedial "github.com/cloudflare/cloudflared/edgediscovery"
|
||||
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
|
||||
"github.com/cloudflare/cloudflared/features"
|
||||
cfdquic "github.com/cloudflare/cloudflared/quic"
|
||||
"github.com/cloudflare/cloudflared/tlsconfig"
|
||||
)
|
||||
@@ -110,10 +112,13 @@ func (d *NetManagementDialer) DialContext(ctx context.Context, network, addr str
|
||||
}
|
||||
|
||||
// probeTLSConfig builds a *tls.Config for a pre-check probe using the same
|
||||
// certificate pool as the production tunnel. The SNI and NextProtos are taken from
|
||||
// p.ProbeTLSSettings() so that the probe SNI is used instead of the production SNI,
|
||||
// which avoids noisy logs in origintunneld.
|
||||
func probeTLSConfig(caCert string, p connection.Protocol) (*tls.Config, error) {
|
||||
// certificate pool and curve preferences as the production tunnel. The SNI and
|
||||
// NextProtos are taken from p.ProbeTLSSettings() so that the probe SNI is used
|
||||
// instead of the production SNI, which avoids noisy logs in origintunneld.
|
||||
// Curve preferences are set via cfdcrypto.TLSConfigWithCurvePreferences so that
|
||||
// prechecks advertise the same key-exchange algorithms (including post-quantum
|
||||
// curves) as the real QUIC/H2 connections.
|
||||
func probeTLSConfig(caCert string, p connection.Protocol, pqMode features.PostQuantumMode) (*tls.Config, error) {
|
||||
settings := p.ProbeTLSSettings()
|
||||
if settings == nil {
|
||||
return nil, fmt.Errorf("no probe TLS settings for protocol %s", p)
|
||||
@@ -125,6 +130,10 @@ func probeTLSConfig(caCert string, p connection.Protocol) (*tls.Config, error) {
|
||||
if len(settings.NextProtos) > 0 {
|
||||
cfg.NextProtos = settings.NextProtos
|
||||
}
|
||||
cfg, err = cfdcrypto.TLSConfigWithCurvePreferences(cfg, pqMode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("apply curve preferences: %w", err)
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
|
||||
+3
-4
@@ -256,7 +256,7 @@ func (p *Proxy) proxyHTTPRequest(
|
||||
reader: tr.Body,
|
||||
}
|
||||
|
||||
stream.Pipe(eyeballStream, rwc, time.Second*0, logger)
|
||||
stream.Pipe(eyeballStream, rwc, logger)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ func (p *Proxy) proxyStream(
|
||||
|
||||
// proxyTCPStream proxies private network type TCP connections as a stream towards an available origin.
|
||||
//
|
||||
// This is different from proxyStream because it's not leveraged ingress rule services and uses the
|
||||
// This is different than proxyStream because it's not leveraged ingress rule services and uses the
|
||||
// originDialer from OriginDialerService.
|
||||
func (p *Proxy) proxyTCPStream(
|
||||
tr *tracing.TracedContext,
|
||||
@@ -344,8 +344,7 @@ func (p *Proxy) proxyTCPStream(
|
||||
connectLatency.Observe(float64(time.Since(start).Milliseconds()))
|
||||
logger.Debug().Msg("proxy stream acknowledged")
|
||||
|
||||
stream.Pipe(tunnelConn, originConn, stream.DefaultTimeoutAfterFirstClose, logger)
|
||||
|
||||
stream.Pipe(tunnelConn, originConn, logger)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+8
-10
@@ -223,9 +223,9 @@ func testProxyWebsocket(proxy connection.OriginProxy) func(t *testing.T) {
|
||||
}
|
||||
if ctx.Err() == context.DeadlineExceeded {
|
||||
t.Errorf("Test timed out")
|
||||
_ = readPipe.Close()
|
||||
_ = writePipe.Close()
|
||||
_ = responseWriter.Close()
|
||||
readPipe.Close()
|
||||
writePipe.Close()
|
||||
responseWriter.Close()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -647,7 +647,7 @@ func TestConnections(t *testing.T) {
|
||||
ingressServiceScheme: "tcp://",
|
||||
originService: func(t *testing.T, ln net.Listener) {
|
||||
// closing the listener created by the test.
|
||||
_ = ln.Close()
|
||||
ln.Close()
|
||||
},
|
||||
eyeballResponseWriter: newTCPRespWriter(replayer),
|
||||
eyeballRequestBody: newTCPRequestBody([]byte("test2")),
|
||||
@@ -756,8 +756,6 @@ func newTCPRequestBody(data []byte) *requestBody {
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
_, _ = pw.Write(data)
|
||||
// Close the write side once the payload has been sent.
|
||||
_ = pw.Close()
|
||||
}()
|
||||
return &requestBody{
|
||||
pr: pr,
|
||||
@@ -803,8 +801,8 @@ func (p *pipedRequestBody) roundtrip(addr string) []byte {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
defer conn.Close()
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusSwitchingProtocols {
|
||||
panic(fmt.Errorf("resp returned status code: %d", resp.StatusCode))
|
||||
@@ -951,7 +949,7 @@ func runEchoTCPService(t *testing.T, l net.Listener) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
defer conn.Close()
|
||||
|
||||
for {
|
||||
buf := make([]byte, 1024)
|
||||
@@ -989,7 +987,7 @@ func runEchoWSService(t *testing.T, l net.Listener) {
|
||||
t.Log(err)
|
||||
return
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
defer conn.Close()
|
||||
|
||||
for {
|
||||
messageType, p, err := conn.ReadMessage()
|
||||
|
||||
+11
-21
@@ -2,7 +2,6 @@ package stream
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime/debug"
|
||||
@@ -10,17 +9,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/cloudflare/cloudflared/cfio"
|
||||
)
|
||||
|
||||
// DefaultTimeoutAfterFirstClose controls the upper bound of how long we wait for the second stream to finish.
|
||||
// We use bidirectional streams for our communication. Since the read and write sides can be closed independently,
|
||||
// we must have a way to close the second stream once the first one finishes. We don't want to wait indefinitely,
|
||||
// since we want to prevent misbehaving clients from blocking cloudflared.
|
||||
const DefaultTimeoutAfterFirstClose = time.Second * 10
|
||||
|
||||
type Stream interface {
|
||||
Reader
|
||||
WriterCloser
|
||||
@@ -43,7 +37,7 @@ type nopCloseWriterAdapter struct {
|
||||
io.ReadWriter
|
||||
}
|
||||
|
||||
func noopCloseWriter(stream io.ReadWriter) *nopCloseWriterAdapter {
|
||||
func NopCloseWriterAdapter(stream io.ReadWriter) *nopCloseWriterAdapter {
|
||||
return &nopCloseWriterAdapter{stream}
|
||||
}
|
||||
|
||||
@@ -78,7 +72,7 @@ func (s *bidirectionalStreamStatus) wait(maxWaitForSecondStream time.Duration) e
|
||||
|
||||
select {
|
||||
case <-timer.C:
|
||||
return fmt.Errorf("timeout waiting for second stream to finish %s", maxWaitForSecondStream)
|
||||
return fmt.Errorf("timeout waiting for second stream to finish")
|
||||
case <-s.doneChan:
|
||||
return nil
|
||||
}
|
||||
@@ -91,19 +85,15 @@ func (s *bidirectionalStreamStatus) isAnyDone() bool {
|
||||
}
|
||||
|
||||
// Pipe copies copy data to & from provided io.ReadWriters.
|
||||
func Pipe(tunnelConn, originConn io.ReadWriter, timeoutAfterFirstClose time.Duration, log *zerolog.Logger) {
|
||||
if err := PipeBidirectional(noopCloseWriter(tunnelConn), noopCloseWriter(originConn), timeoutAfterFirstClose, log); err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to pipe bidirectional stream")
|
||||
}
|
||||
func Pipe(tunnelConn, originConn io.ReadWriter, log *zerolog.Logger) {
|
||||
_ = PipeBidirectional(NopCloseWriterAdapter(tunnelConn), NopCloseWriterAdapter(originConn), 0, log)
|
||||
}
|
||||
|
||||
// PipeBidirectional copies data between two unidirectional streams. It is a special case of Pipe that accepts streams
|
||||
// whose read and write sides can be closed independently. The main difference is that when piping data from a reader
|
||||
// to a writer, if EOF is read, this implementation propagates the EOF signal to the destination by closing the write
|
||||
// side of the bidirectional stream.
|
||||
// Finally, once EOF is received from one of the provided streams, the other direction has a configured grace period to
|
||||
// finish; otherwise, the method returns a timeout error. It is, however, the responsibility of the caller to close
|
||||
// the associated streams at both ends in order to free all resources and goroutines.
|
||||
// PipeBidirectional copies data to two unidirectional streams. It is a special case of Pipe where it receives a concept that allows for Read and Write side to be closed independently.
|
||||
// The main difference is that when piping data from a reader to a writer, if EOF is read, then this implementation propagates the EOF signal to the destination/writer by closing the write side of the
|
||||
// Bidirectional Stream.
|
||||
// Finally, depending on once EOF is ready from one of the provided streams, the other direction of streaming data will have a configured time period to also finish, otherwise,
|
||||
// the method will return immediately with a timeout error. It is however, the responsibility of the caller to close the associated streams in both ends in order to free all the resources/go-routines.
|
||||
func PipeBidirectional(downstream, upstream Stream, maxWaitForSecondStream time.Duration, log *zerolog.Logger) error {
|
||||
status := newBiStreamStatus()
|
||||
|
||||
@@ -111,7 +101,7 @@ func PipeBidirectional(downstream, upstream Stream, maxWaitForSecondStream time.
|
||||
go unidirectionalStream(upstream, downstream, "downstream->upstream", status, log)
|
||||
|
||||
if err := status.wait(maxWaitForSecondStream); err != nil {
|
||||
return fmt.Errorf("unable to wait for both streams while proxying: %w", err)
|
||||
return errors.Wrap(err, "unable to wait for both streams while proxying")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+5
-45
@@ -30,8 +30,8 @@ func TestPipeBidirectionalFinishOneSideTimeout(t *testing.T) {
|
||||
|
||||
func TestPipeBidirectionalClosingWriteBothSidesAlsoExists(t *testing.T) {
|
||||
fun := func(upstream, downstream *mockedStream) {
|
||||
_ = downstream.CloseWrite()
|
||||
_ = upstream.CloseWrite()
|
||||
downstream.CloseWrite()
|
||||
upstream.CloseWrite()
|
||||
|
||||
downstream.writeToReader("abc")
|
||||
upstream.writeToReader("abc")
|
||||
@@ -42,7 +42,7 @@ func TestPipeBidirectionalClosingWriteBothSidesAlsoExists(t *testing.T) {
|
||||
|
||||
func TestPipeBidirectionalClosingWriteSingleSideAlsoExists(t *testing.T) {
|
||||
fun := func(upstream, downstream *mockedStream) {
|
||||
_ = downstream.CloseWrite()
|
||||
downstream.CloseWrite()
|
||||
|
||||
downstream.writeToReader("abc")
|
||||
upstream.writeToReader("abc")
|
||||
@@ -51,46 +51,6 @@ func TestPipeBidirectionalClosingWriteSingleSideAlsoExists(t *testing.T) {
|
||||
testPipeBidirectionalUnblocking(t, fun, time.Millisecond*200, true)
|
||||
}
|
||||
|
||||
// TestPipeBidirectionalReturnsWhenBothSidesFinish verifies that
|
||||
// PipeBidirectional returns as soon as both stream directions finish, without
|
||||
// waiting for the full timeout grace period to expire. This guards against a
|
||||
// regression where the second-stream wait would block for the whole timeout
|
||||
// even when the result is already available.
|
||||
func TestPipeBidirectionalReturnsWhenBothSidesFinish(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const (
|
||||
timeout = time.Second * 5
|
||||
maxWallTime = time.Millisecond * 500
|
||||
)
|
||||
|
||||
logger := zerolog.Nop()
|
||||
downstream := newMockedStream()
|
||||
upstream := newMockedStream()
|
||||
|
||||
resultCh := make(chan error, 1)
|
||||
go func() {
|
||||
resultCh <- PipeBidirectional(downstream, upstream, timeout, &logger)
|
||||
}()
|
||||
|
||||
// Close both reader sides so both stream directions reach EOF promptly.
|
||||
downstream.closeReader()
|
||||
upstream.closeReader()
|
||||
|
||||
start := time.Now()
|
||||
select {
|
||||
case err := <-resultCh:
|
||||
elapsed := time.Since(start)
|
||||
require.NoError(t, err)
|
||||
require.Less(t, elapsed, maxWallTime,
|
||||
"PipeBidirectional should return as soon as both streams finish, not after the full %s timeout (took %s)",
|
||||
timeout, elapsed,
|
||||
)
|
||||
case <-time.After(timeout):
|
||||
require.Fail(t, "PipeBidirectional did not return before the timeout expired")
|
||||
}
|
||||
}
|
||||
|
||||
func testPipeBidirectionalUnblocking(t *testing.T, afterFun func(*mockedStream, *mockedStream), timeout time.Duration, expectTimeout bool) {
|
||||
logger := zerolog.Nop()
|
||||
|
||||
@@ -107,9 +67,9 @@ func testPipeBidirectionalUnblocking(t *testing.T, afterFun func(*mockedStream,
|
||||
select {
|
||||
case err := <-resultCh:
|
||||
if expectTimeout {
|
||||
require.Error(t, err)
|
||||
require.NotNil(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
case <-time.After(timeout * 2):
|
||||
|
||||
+825
@@ -0,0 +1,825 @@
|
||||
// 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 cryptobyte
|
||||
|
||||
import (
|
||||
encoding_asn1 "encoding/asn1"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/cryptobyte/asn1"
|
||||
)
|
||||
|
||||
// This file contains ASN.1-related methods for String and Builder.
|
||||
|
||||
// Builder
|
||||
|
||||
// AddASN1Int64 appends a DER-encoded ASN.1 INTEGER.
|
||||
func (b *Builder) AddASN1Int64(v int64) {
|
||||
b.addASN1Signed(asn1.INTEGER, v)
|
||||
}
|
||||
|
||||
// AddASN1Int64WithTag appends a DER-encoded ASN.1 INTEGER with the
|
||||
// given tag.
|
||||
func (b *Builder) AddASN1Int64WithTag(v int64, tag asn1.Tag) {
|
||||
b.addASN1Signed(tag, v)
|
||||
}
|
||||
|
||||
// AddASN1Enum appends a DER-encoded ASN.1 ENUMERATION.
|
||||
func (b *Builder) AddASN1Enum(v int64) {
|
||||
b.addASN1Signed(asn1.ENUM, v)
|
||||
}
|
||||
|
||||
func (b *Builder) addASN1Signed(tag asn1.Tag, v int64) {
|
||||
b.AddASN1(tag, func(c *Builder) {
|
||||
length := 1
|
||||
for i := v; i >= 0x80 || i < -0x80; i >>= 8 {
|
||||
length++
|
||||
}
|
||||
|
||||
for ; length > 0; length-- {
|
||||
i := v >> uint((length-1)*8) & 0xff
|
||||
c.AddUint8(uint8(i))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// AddASN1Uint64 appends a DER-encoded ASN.1 INTEGER.
|
||||
func (b *Builder) AddASN1Uint64(v uint64) {
|
||||
b.AddASN1(asn1.INTEGER, func(c *Builder) {
|
||||
length := 1
|
||||
for i := v; i >= 0x80; i >>= 8 {
|
||||
length++
|
||||
}
|
||||
|
||||
for ; length > 0; length-- {
|
||||
i := v >> uint((length-1)*8) & 0xff
|
||||
c.AddUint8(uint8(i))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// AddASN1BigInt appends a DER-encoded ASN.1 INTEGER.
|
||||
func (b *Builder) AddASN1BigInt(n *big.Int) {
|
||||
if b.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b.AddASN1(asn1.INTEGER, func(c *Builder) {
|
||||
if n.Sign() < 0 {
|
||||
// A negative number has to be converted to two's-complement form. So we
|
||||
// invert and subtract 1. If the most-significant-bit isn't set then
|
||||
// we'll need to pad the beginning with 0xff in order to keep the number
|
||||
// negative.
|
||||
nMinus1 := new(big.Int).Neg(n)
|
||||
nMinus1.Sub(nMinus1, bigOne)
|
||||
bytes := nMinus1.Bytes()
|
||||
for i := range bytes {
|
||||
bytes[i] ^= 0xff
|
||||
}
|
||||
if len(bytes) == 0 || bytes[0]&0x80 == 0 {
|
||||
c.add(0xff)
|
||||
}
|
||||
c.add(bytes...)
|
||||
} else if n.Sign() == 0 {
|
||||
c.add(0)
|
||||
} else {
|
||||
bytes := n.Bytes()
|
||||
if bytes[0]&0x80 != 0 {
|
||||
c.add(0)
|
||||
}
|
||||
c.add(bytes...)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// AddASN1OctetString appends a DER-encoded ASN.1 OCTET STRING.
|
||||
func (b *Builder) AddASN1OctetString(bytes []byte) {
|
||||
b.AddASN1(asn1.OCTET_STRING, func(c *Builder) {
|
||||
c.AddBytes(bytes)
|
||||
})
|
||||
}
|
||||
|
||||
const generalizedTimeFormatStr = "20060102150405Z0700"
|
||||
|
||||
// AddASN1GeneralizedTime appends a DER-encoded ASN.1 GENERALIZEDTIME.
|
||||
func (b *Builder) AddASN1GeneralizedTime(t time.Time) {
|
||||
if t.Year() < 0 || t.Year() > 9999 {
|
||||
b.err = fmt.Errorf("cryptobyte: cannot represent %v as a GeneralizedTime", t)
|
||||
return
|
||||
}
|
||||
b.AddASN1(asn1.GeneralizedTime, func(c *Builder) {
|
||||
c.AddBytes([]byte(t.Format(generalizedTimeFormatStr)))
|
||||
})
|
||||
}
|
||||
|
||||
// AddASN1UTCTime appends a DER-encoded ASN.1 UTCTime.
|
||||
func (b *Builder) AddASN1UTCTime(t time.Time) {
|
||||
b.AddASN1(asn1.UTCTime, func(c *Builder) {
|
||||
// As utilized by the X.509 profile, UTCTime can only
|
||||
// represent the years 1950 through 2049.
|
||||
if t.Year() < 1950 || t.Year() >= 2050 {
|
||||
b.err = fmt.Errorf("cryptobyte: cannot represent %v as a UTCTime", t)
|
||||
return
|
||||
}
|
||||
c.AddBytes([]byte(t.Format(defaultUTCTimeFormatStr)))
|
||||
})
|
||||
}
|
||||
|
||||
// AddASN1BitString appends a DER-encoded ASN.1 BIT STRING. This does not
|
||||
// support BIT STRINGs that are not a whole number of bytes.
|
||||
func (b *Builder) AddASN1BitString(data []byte) {
|
||||
b.AddASN1(asn1.BIT_STRING, func(b *Builder) {
|
||||
b.AddUint8(0)
|
||||
b.AddBytes(data)
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Builder) addBase128Int(n int64) {
|
||||
var length int
|
||||
if n == 0 {
|
||||
length = 1
|
||||
} else {
|
||||
for i := n; i > 0; i >>= 7 {
|
||||
length++
|
||||
}
|
||||
}
|
||||
|
||||
for i := length - 1; i >= 0; i-- {
|
||||
o := byte(n >> uint(i*7))
|
||||
o &= 0x7f
|
||||
if i != 0 {
|
||||
o |= 0x80
|
||||
}
|
||||
|
||||
b.add(o)
|
||||
}
|
||||
}
|
||||
|
||||
func isValidOID(oid encoding_asn1.ObjectIdentifier) bool {
|
||||
if len(oid) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
if oid[0] > 2 || (oid[0] <= 1 && oid[1] >= 40) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, v := range oid {
|
||||
if v < 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *Builder) AddASN1ObjectIdentifier(oid encoding_asn1.ObjectIdentifier) {
|
||||
b.AddASN1(asn1.OBJECT_IDENTIFIER, func(b *Builder) {
|
||||
if !isValidOID(oid) {
|
||||
b.err = fmt.Errorf("cryptobyte: invalid OID: %v", oid)
|
||||
return
|
||||
}
|
||||
|
||||
b.addBase128Int(int64(oid[0])*40 + int64(oid[1]))
|
||||
for _, v := range oid[2:] {
|
||||
b.addBase128Int(int64(v))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Builder) AddASN1Boolean(v bool) {
|
||||
b.AddASN1(asn1.BOOLEAN, func(b *Builder) {
|
||||
if v {
|
||||
b.AddUint8(0xff)
|
||||
} else {
|
||||
b.AddUint8(0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Builder) AddASN1NULL() {
|
||||
b.add(uint8(asn1.NULL), 0)
|
||||
}
|
||||
|
||||
// MarshalASN1 calls encoding_asn1.Marshal on its input and appends the result if
|
||||
// successful or records an error if one occurred.
|
||||
func (b *Builder) MarshalASN1(v interface{}) {
|
||||
// NOTE(martinkr): This is somewhat of a hack to allow propagation of
|
||||
// encoding_asn1.Marshal errors into Builder.err. N.B. if you call MarshalASN1 with a
|
||||
// value embedded into a struct, its tag information is lost.
|
||||
if b.err != nil {
|
||||
return
|
||||
}
|
||||
bytes, err := encoding_asn1.Marshal(v)
|
||||
if err != nil {
|
||||
b.err = err
|
||||
return
|
||||
}
|
||||
b.AddBytes(bytes)
|
||||
}
|
||||
|
||||
// AddASN1 appends an ASN.1 object. The object is prefixed with the given tag.
|
||||
// Tags greater than 30 are not supported and result in an error (i.e.
|
||||
// low-tag-number form only). The child builder passed to the
|
||||
// BuilderContinuation can be used to build the content of the ASN.1 object.
|
||||
func (b *Builder) AddASN1(tag asn1.Tag, f BuilderContinuation) {
|
||||
if b.err != nil {
|
||||
return
|
||||
}
|
||||
// Identifiers with the low five bits set indicate high-tag-number format
|
||||
// (two or more octets), which we don't support.
|
||||
if tag&0x1f == 0x1f {
|
||||
b.err = fmt.Errorf("cryptobyte: high-tag number identifier octets not supported: 0x%x", tag)
|
||||
return
|
||||
}
|
||||
b.AddUint8(uint8(tag))
|
||||
b.addLengthPrefixed(1, true, f)
|
||||
}
|
||||
|
||||
// String
|
||||
|
||||
// ReadASN1Boolean decodes an ASN.1 BOOLEAN and converts it to a boolean
|
||||
// representation into out and advances. It reports whether the read
|
||||
// was successful.
|
||||
func (s *String) ReadASN1Boolean(out *bool) bool {
|
||||
var bytes String
|
||||
if !s.ReadASN1(&bytes, asn1.BOOLEAN) || len(bytes) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch bytes[0] {
|
||||
case 0:
|
||||
*out = false
|
||||
case 0xff:
|
||||
*out = true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadASN1Integer decodes an ASN.1 INTEGER into out and advances. If out does
|
||||
// not point to an integer, to a big.Int, or to a []byte it panics. Only
|
||||
// positive and zero values can be decoded into []byte, and they are returned as
|
||||
// big-endian binary values that share memory with s. Positive values will have
|
||||
// no leading zeroes, and zero will be returned as a single zero byte.
|
||||
// ReadASN1Integer reports whether the read was successful.
|
||||
func (s *String) ReadASN1Integer(out interface{}) bool {
|
||||
switch out := out.(type) {
|
||||
case *int, *int8, *int16, *int32, *int64:
|
||||
var i int64
|
||||
if !s.readASN1Int64(&i) || reflect.ValueOf(out).Elem().OverflowInt(i) {
|
||||
return false
|
||||
}
|
||||
reflect.ValueOf(out).Elem().SetInt(i)
|
||||
return true
|
||||
case *uint, *uint8, *uint16, *uint32, *uint64:
|
||||
var u uint64
|
||||
if !s.readASN1Uint64(&u) || reflect.ValueOf(out).Elem().OverflowUint(u) {
|
||||
return false
|
||||
}
|
||||
reflect.ValueOf(out).Elem().SetUint(u)
|
||||
return true
|
||||
case *big.Int:
|
||||
return s.readASN1BigInt(out)
|
||||
case *[]byte:
|
||||
return s.readASN1Bytes(out)
|
||||
default:
|
||||
panic("out does not point to an integer type")
|
||||
}
|
||||
}
|
||||
|
||||
func checkASN1Integer(bytes []byte) bool {
|
||||
if len(bytes) == 0 {
|
||||
// An INTEGER is encoded with at least one octet.
|
||||
return false
|
||||
}
|
||||
if len(bytes) == 1 {
|
||||
return true
|
||||
}
|
||||
if bytes[0] == 0 && bytes[1]&0x80 == 0 || bytes[0] == 0xff && bytes[1]&0x80 == 0x80 {
|
||||
// Value is not minimally encoded.
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var bigOne = big.NewInt(1)
|
||||
|
||||
func (s *String) readASN1BigInt(out *big.Int) bool {
|
||||
var bytes String
|
||||
if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) {
|
||||
return false
|
||||
}
|
||||
if bytes[0]&0x80 == 0x80 {
|
||||
// Negative number.
|
||||
neg := make([]byte, len(bytes))
|
||||
for i, b := range bytes {
|
||||
neg[i] = ^b
|
||||
}
|
||||
out.SetBytes(neg)
|
||||
out.Add(out, bigOne)
|
||||
out.Neg(out)
|
||||
} else {
|
||||
out.SetBytes(bytes)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *String) readASN1Bytes(out *[]byte) bool {
|
||||
var bytes String
|
||||
if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) {
|
||||
return false
|
||||
}
|
||||
if bytes[0]&0x80 == 0x80 {
|
||||
return false
|
||||
}
|
||||
for len(bytes) > 1 && bytes[0] == 0 {
|
||||
bytes = bytes[1:]
|
||||
}
|
||||
*out = bytes
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *String) readASN1Int64(out *int64) bool {
|
||||
var bytes String
|
||||
if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Signed(out, bytes) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func asn1Signed(out *int64, n []byte) bool {
|
||||
length := len(n)
|
||||
if length > 8 {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < length; i++ {
|
||||
*out <<= 8
|
||||
*out |= int64(n[i])
|
||||
}
|
||||
// Shift up and down in order to sign extend the result.
|
||||
*out <<= 64 - uint8(length)*8
|
||||
*out >>= 64 - uint8(length)*8
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *String) readASN1Uint64(out *uint64) bool {
|
||||
var bytes String
|
||||
if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Unsigned(out, bytes) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func asn1Unsigned(out *uint64, n []byte) bool {
|
||||
length := len(n)
|
||||
if length > 9 || length == 9 && n[0] != 0 {
|
||||
// Too large for uint64.
|
||||
return false
|
||||
}
|
||||
if n[0]&0x80 != 0 {
|
||||
// Negative number.
|
||||
return false
|
||||
}
|
||||
for i := 0; i < length; i++ {
|
||||
*out <<= 8
|
||||
*out |= uint64(n[i])
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadASN1Int64WithTag decodes an ASN.1 INTEGER with the given tag into out
|
||||
// and advances. It reports whether the read was successful and resulted in a
|
||||
// value that can be represented in an int64.
|
||||
func (s *String) ReadASN1Int64WithTag(out *int64, tag asn1.Tag) bool {
|
||||
var bytes String
|
||||
return s.ReadASN1(&bytes, tag) && checkASN1Integer(bytes) && asn1Signed(out, bytes)
|
||||
}
|
||||
|
||||
// ReadASN1Enum decodes an ASN.1 ENUMERATION into out and advances. It reports
|
||||
// whether the read was successful.
|
||||
func (s *String) ReadASN1Enum(out *int) bool {
|
||||
var bytes String
|
||||
var i int64
|
||||
if !s.ReadASN1(&bytes, asn1.ENUM) || !checkASN1Integer(bytes) || !asn1Signed(&i, bytes) {
|
||||
return false
|
||||
}
|
||||
if int64(int(i)) != i {
|
||||
return false
|
||||
}
|
||||
*out = int(i)
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *String) readBase128Int(out *int) bool {
|
||||
ret := 0
|
||||
for i := 0; len(*s) > 0; i++ {
|
||||
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
|
||||
b := s.read(1)[0]
|
||||
|
||||
// ITU-T X.690, section 8.19.2:
|
||||
// The subidentifier shall be encoded in the fewest possible octets,
|
||||
// that is, the leading octet of the subidentifier shall not have the value 0x80.
|
||||
if i == 0 && b == 0x80 {
|
||||
return false
|
||||
}
|
||||
|
||||
ret |= int(b & 0x7f)
|
||||
if b&0x80 == 0 {
|
||||
*out = ret
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false // truncated
|
||||
}
|
||||
|
||||
// ReadASN1ObjectIdentifier decodes an ASN.1 OBJECT IDENTIFIER into out and
|
||||
// advances. It reports whether the read was successful.
|
||||
func (s *String) ReadASN1ObjectIdentifier(out *encoding_asn1.ObjectIdentifier) bool {
|
||||
var bytes String
|
||||
if !s.ReadASN1(&bytes, asn1.OBJECT_IDENTIFIER) || len(bytes) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// In the worst case, we get two elements from the first byte (which is
|
||||
// encoded differently) and then every varint is a single byte long.
|
||||
components := make([]int, len(bytes)+1)
|
||||
|
||||
// The first varint is 40*value1 + value2:
|
||||
// According to this packing, value1 can take the values 0, 1 and 2 only.
|
||||
// When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2,
|
||||
// then there are no restrictions on value2.
|
||||
var v int
|
||||
if !bytes.readBase128Int(&v) {
|
||||
return false
|
||||
}
|
||||
if v < 80 {
|
||||
components[0] = v / 40
|
||||
components[1] = v % 40
|
||||
} else {
|
||||
components[0] = 2
|
||||
components[1] = v - 80
|
||||
}
|
||||
|
||||
i := 2
|
||||
for ; len(bytes) > 0; i++ {
|
||||
if !bytes.readBase128Int(&v) {
|
||||
return false
|
||||
}
|
||||
components[i] = v
|
||||
}
|
||||
*out = components[:i]
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadASN1GeneralizedTime decodes an ASN.1 GENERALIZEDTIME into out and
|
||||
// advances. It reports whether the read was successful.
|
||||
func (s *String) ReadASN1GeneralizedTime(out *time.Time) bool {
|
||||
var bytes String
|
||||
if !s.ReadASN1(&bytes, asn1.GeneralizedTime) {
|
||||
return false
|
||||
}
|
||||
t := string(bytes)
|
||||
res, err := time.Parse(generalizedTimeFormatStr, t)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if serialized := res.Format(generalizedTimeFormatStr); serialized != t {
|
||||
return false
|
||||
}
|
||||
*out = res
|
||||
return true
|
||||
}
|
||||
|
||||
const defaultUTCTimeFormatStr = "060102150405Z0700"
|
||||
|
||||
// ReadASN1UTCTime decodes an ASN.1 UTCTime into out and advances.
|
||||
// It reports whether the read was successful.
|
||||
func (s *String) ReadASN1UTCTime(out *time.Time) bool {
|
||||
var bytes String
|
||||
if !s.ReadASN1(&bytes, asn1.UTCTime) {
|
||||
return false
|
||||
}
|
||||
t := string(bytes)
|
||||
|
||||
formatStr := defaultUTCTimeFormatStr
|
||||
var err error
|
||||
res, err := time.Parse(formatStr, t)
|
||||
if err != nil {
|
||||
// Fallback to minute precision if we can't parse second
|
||||
// precision. If we are following X.509 or X.690 we shouldn't
|
||||
// support this, but we do.
|
||||
formatStr = "0601021504Z0700"
|
||||
res, err = time.Parse(formatStr, t)
|
||||
}
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if serialized := res.Format(formatStr); serialized != t {
|
||||
return false
|
||||
}
|
||||
|
||||
if res.Year() >= 2050 {
|
||||
// UTCTime interprets the low order digits 50-99 as 1950-99.
|
||||
// This only applies to its use in the X.509 profile.
|
||||
// See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
|
||||
res = res.AddDate(-100, 0, 0)
|
||||
}
|
||||
*out = res
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadASN1BitString decodes an ASN.1 BIT STRING into out and advances.
|
||||
// It reports whether the read was successful.
|
||||
func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool {
|
||||
var bytes String
|
||||
if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 ||
|
||||
len(bytes)*8/8 != len(bytes) {
|
||||
return false
|
||||
}
|
||||
|
||||
paddingBits := bytes[0]
|
||||
bytes = bytes[1:]
|
||||
if paddingBits > 7 ||
|
||||
len(bytes) == 0 && paddingBits != 0 ||
|
||||
len(bytes) > 0 && bytes[len(bytes)-1]&(1<<paddingBits-1) != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
out.BitLength = len(bytes)*8 - int(paddingBits)
|
||||
out.Bytes = bytes
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadASN1BitStringAsBytes decodes an ASN.1 BIT STRING into out and advances. It is
|
||||
// an error if the BIT STRING is not a whole number of bytes. It reports
|
||||
// whether the read was successful.
|
||||
func (s *String) ReadASN1BitStringAsBytes(out *[]byte) bool {
|
||||
var bytes String
|
||||
if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
paddingBits := bytes[0]
|
||||
if paddingBits != 0 {
|
||||
return false
|
||||
}
|
||||
*out = bytes[1:]
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadASN1Bytes reads the contents of a DER-encoded ASN.1 element (not including
|
||||
// tag and length bytes) into out, and advances. The element must match the
|
||||
// given tag. It reports whether the read was successful.
|
||||
func (s *String) ReadASN1Bytes(out *[]byte, tag asn1.Tag) bool {
|
||||
return s.ReadASN1((*String)(out), tag)
|
||||
}
|
||||
|
||||
// ReadASN1 reads the contents of a DER-encoded ASN.1 element (not including
|
||||
// tag and length bytes) into out, and advances. The element must match the
|
||||
// given tag. It reports whether the read was successful.
|
||||
//
|
||||
// Tags greater than 30 are not supported (i.e. low-tag-number format only).
|
||||
func (s *String) ReadASN1(out *String, tag asn1.Tag) bool {
|
||||
var t asn1.Tag
|
||||
if !s.ReadAnyASN1(out, &t) || t != tag {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadASN1Element reads the contents of a DER-encoded ASN.1 element (including
|
||||
// tag and length bytes) into out, and advances. The element must match the
|
||||
// given tag. It reports whether the read was successful.
|
||||
//
|
||||
// Tags greater than 30 are not supported (i.e. low-tag-number format only).
|
||||
func (s *String) ReadASN1Element(out *String, tag asn1.Tag) bool {
|
||||
var t asn1.Tag
|
||||
if !s.ReadAnyASN1Element(out, &t) || t != tag {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadAnyASN1 reads the contents of a DER-encoded ASN.1 element (not including
|
||||
// tag and length bytes) into out, sets outTag to its tag, and advances.
|
||||
// It reports whether the read was successful.
|
||||
//
|
||||
// Tags greater than 30 are not supported (i.e. low-tag-number format only).
|
||||
func (s *String) ReadAnyASN1(out *String, outTag *asn1.Tag) bool {
|
||||
return s.readASN1(out, outTag, true /* skip header */)
|
||||
}
|
||||
|
||||
// ReadAnyASN1Element reads the contents of a DER-encoded ASN.1 element
|
||||
// (including tag and length bytes) into out, sets outTag to is tag, and
|
||||
// advances. It reports whether the read was successful.
|
||||
//
|
||||
// Tags greater than 30 are not supported (i.e. low-tag-number format only).
|
||||
func (s *String) ReadAnyASN1Element(out *String, outTag *asn1.Tag) bool {
|
||||
return s.readASN1(out, outTag, false /* include header */)
|
||||
}
|
||||
|
||||
// PeekASN1Tag reports whether the next ASN.1 value on the string starts with
|
||||
// the given tag.
|
||||
func (s String) PeekASN1Tag(tag asn1.Tag) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
return asn1.Tag(s[0]) == tag
|
||||
}
|
||||
|
||||
// SkipASN1 reads and discards an ASN.1 element with the given tag. It
|
||||
// reports whether the operation was successful.
|
||||
func (s *String) SkipASN1(tag asn1.Tag) bool {
|
||||
var unused String
|
||||
return s.ReadASN1(&unused, tag)
|
||||
}
|
||||
|
||||
// ReadOptionalASN1 attempts to read the contents of a DER-encoded ASN.1
|
||||
// element (not including tag and length bytes) tagged with the given tag into
|
||||
// out. It stores whether an element with the tag was found in outPresent,
|
||||
// unless outPresent is nil. It reports whether the read was successful.
|
||||
func (s *String) ReadOptionalASN1(out *String, outPresent *bool, tag asn1.Tag) bool {
|
||||
present := s.PeekASN1Tag(tag)
|
||||
if outPresent != nil {
|
||||
*outPresent = present
|
||||
}
|
||||
if present && !s.ReadASN1(out, tag) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// SkipOptionalASN1 advances s over an ASN.1 element with the given tag, or
|
||||
// else leaves s unchanged. It reports whether the operation was successful.
|
||||
func (s *String) SkipOptionalASN1(tag asn1.Tag) bool {
|
||||
if !s.PeekASN1Tag(tag) {
|
||||
return true
|
||||
}
|
||||
var unused String
|
||||
return s.ReadASN1(&unused, tag)
|
||||
}
|
||||
|
||||
// ReadOptionalASN1Integer attempts to read an optional ASN.1 INTEGER explicitly
|
||||
// tagged with tag into out and advances. If no element with a matching tag is
|
||||
// present, it writes defaultValue into out instead. Otherwise, it behaves like
|
||||
// ReadASN1Integer.
|
||||
func (s *String) ReadOptionalASN1Integer(out interface{}, tag asn1.Tag, defaultValue interface{}) bool {
|
||||
var present bool
|
||||
var i String
|
||||
if !s.ReadOptionalASN1(&i, &present, tag) {
|
||||
return false
|
||||
}
|
||||
if !present {
|
||||
switch out.(type) {
|
||||
case *int, *int8, *int16, *int32, *int64,
|
||||
*uint, *uint8, *uint16, *uint32, *uint64, *[]byte:
|
||||
reflect.ValueOf(out).Elem().Set(reflect.ValueOf(defaultValue))
|
||||
case *big.Int:
|
||||
if defaultValue, ok := defaultValue.(*big.Int); ok {
|
||||
out.(*big.Int).Set(defaultValue)
|
||||
} else {
|
||||
panic("out points to big.Int, but defaultValue does not")
|
||||
}
|
||||
default:
|
||||
panic("invalid integer type")
|
||||
}
|
||||
return true
|
||||
}
|
||||
if !i.ReadASN1Integer(out) || !i.Empty() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadOptionalASN1OctetString attempts to read an optional ASN.1 OCTET STRING
|
||||
// explicitly tagged with tag into out and advances. If no element with a
|
||||
// matching tag is present, it sets "out" to nil instead. It reports
|
||||
// whether the read was successful.
|
||||
func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag asn1.Tag) bool {
|
||||
var present bool
|
||||
var child String
|
||||
if !s.ReadOptionalASN1(&child, &present, tag) {
|
||||
return false
|
||||
}
|
||||
if outPresent != nil {
|
||||
*outPresent = present
|
||||
}
|
||||
if present {
|
||||
var oct String
|
||||
if !child.ReadASN1(&oct, asn1.OCTET_STRING) || !child.Empty() {
|
||||
return false
|
||||
}
|
||||
*out = oct
|
||||
} else {
|
||||
*out = nil
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadOptionalASN1Boolean attempts to read an optional ASN.1 BOOLEAN
|
||||
// explicitly tagged with tag into out and advances. If no element with a
|
||||
// matching tag is present, it sets "out" to defaultValue instead. It reports
|
||||
// whether the read was successful.
|
||||
func (s *String) ReadOptionalASN1Boolean(out *bool, tag asn1.Tag, defaultValue bool) bool {
|
||||
var present bool
|
||||
var child String
|
||||
if !s.ReadOptionalASN1(&child, &present, tag) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !present {
|
||||
*out = defaultValue
|
||||
return true
|
||||
}
|
||||
|
||||
return child.ReadASN1Boolean(out)
|
||||
}
|
||||
|
||||
func (s *String) readASN1(out *String, outTag *asn1.Tag, skipHeader bool) bool {
|
||||
if len(*s) < 2 {
|
||||
return false
|
||||
}
|
||||
tag, lenByte := (*s)[0], (*s)[1]
|
||||
|
||||
if tag&0x1f == 0x1f {
|
||||
// ITU-T X.690 section 8.1.2
|
||||
//
|
||||
// An identifier octet with a tag part of 0x1f indicates a high-tag-number
|
||||
// form identifier with two or more octets. We only support tags less than
|
||||
// 31 (i.e. low-tag-number form, single octet identifier).
|
||||
return false
|
||||
}
|
||||
|
||||
if outTag != nil {
|
||||
*outTag = asn1.Tag(tag)
|
||||
}
|
||||
|
||||
// ITU-T X.690 section 8.1.3
|
||||
//
|
||||
// Bit 8 of the first length byte indicates whether the length is short- or
|
||||
// long-form.
|
||||
var length, headerLen uint32 // length includes headerLen
|
||||
if lenByte&0x80 == 0 {
|
||||
// Short-form length (section 8.1.3.4), encoded in bits 1-7.
|
||||
length = uint32(lenByte) + 2
|
||||
headerLen = 2
|
||||
} else {
|
||||
// Long-form length (section 8.1.3.5). Bits 1-7 encode the number of octets
|
||||
// used to encode the length.
|
||||
lenLen := lenByte & 0x7f
|
||||
var len32 uint32
|
||||
|
||||
if lenLen == 0 || lenLen > 4 || len(*s) < int(2+lenLen) {
|
||||
return false
|
||||
}
|
||||
|
||||
lenBytes := String((*s)[2 : 2+lenLen])
|
||||
if !lenBytes.readUnsigned(&len32, int(lenLen)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// ITU-T X.690 section 10.1 (DER length forms) requires encoding the length
|
||||
// with the minimum number of octets.
|
||||
if len32 < 128 {
|
||||
// Length should have used short-form encoding.
|
||||
return false
|
||||
}
|
||||
if len32>>((lenLen-1)*8) == 0 {
|
||||
// Leading octet is 0. Length should have been at least one byte shorter.
|
||||
return false
|
||||
}
|
||||
|
||||
headerLen = 2 + uint32(lenLen)
|
||||
if headerLen+len32 < len32 {
|
||||
// Overflow.
|
||||
return false
|
||||
}
|
||||
length = headerLen + len32
|
||||
}
|
||||
|
||||
if int(length) < 0 || !s.ReadBytes((*[]byte)(out), int(length)) {
|
||||
return false
|
||||
}
|
||||
if skipHeader && !out.Skip(int(headerLen)) {
|
||||
panic("cryptobyte: internal error")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
// 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 asn1 contains supporting types for parsing and building ASN.1
|
||||
// messages with the cryptobyte package.
|
||||
package asn1
|
||||
|
||||
// Tag represents an ASN.1 identifier octet, consisting of a tag number
|
||||
// (indicating a type) and class (such as context-specific or constructed).
|
||||
//
|
||||
// Methods in the cryptobyte package only support the low-tag-number form, i.e.
|
||||
// a single identifier octet with bits 7-8 encoding the class and bits 1-6
|
||||
// encoding the tag number.
|
||||
type Tag uint8
|
||||
|
||||
const (
|
||||
classConstructed = 0x20
|
||||
classContextSpecific = 0x80
|
||||
)
|
||||
|
||||
// Constructed returns t with the constructed class bit set.
|
||||
func (t Tag) Constructed() Tag { return t | classConstructed }
|
||||
|
||||
// ContextSpecific returns t with the context-specific class bit set.
|
||||
func (t Tag) ContextSpecific() Tag { return t | classContextSpecific }
|
||||
|
||||
// The following is a list of standard tag and class combinations.
|
||||
const (
|
||||
BOOLEAN = Tag(1)
|
||||
INTEGER = Tag(2)
|
||||
BIT_STRING = Tag(3)
|
||||
OCTET_STRING = Tag(4)
|
||||
NULL = Tag(5)
|
||||
OBJECT_IDENTIFIER = Tag(6)
|
||||
ENUM = Tag(10)
|
||||
UTF8String = Tag(12)
|
||||
SEQUENCE = Tag(16 | classConstructed)
|
||||
SET = Tag(17 | classConstructed)
|
||||
PrintableString = Tag(19)
|
||||
T61String = Tag(20)
|
||||
IA5String = Tag(22)
|
||||
UTCTime = Tag(23)
|
||||
GeneralizedTime = Tag(24)
|
||||
GeneralString = Tag(27)
|
||||
)
|
||||
+350
@@ -0,0 +1,350 @@
|
||||
// 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 cryptobyte
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// A Builder builds byte strings from fixed-length and length-prefixed values.
|
||||
// Builders either allocate space as needed, or are ‘fixed’, which means that
|
||||
// they write into a given buffer and produce an error if it's exhausted.
|
||||
//
|
||||
// The zero value is a usable Builder that allocates space as needed.
|
||||
//
|
||||
// Simple values are marshaled and appended to a Builder using methods on the
|
||||
// Builder. Length-prefixed values are marshaled by providing a
|
||||
// BuilderContinuation, which is a function that writes the inner contents of
|
||||
// the value to a given Builder. See the documentation for BuilderContinuation
|
||||
// for details.
|
||||
type Builder struct {
|
||||
err error
|
||||
result []byte
|
||||
fixedSize bool
|
||||
child *Builder
|
||||
offset int
|
||||
pendingLenLen int
|
||||
pendingIsASN1 bool
|
||||
inContinuation *bool
|
||||
}
|
||||
|
||||
// NewBuilder creates a Builder that appends its output to the given buffer.
|
||||
// Like append(), the slice will be reallocated if its capacity is exceeded.
|
||||
// Use Bytes to get the final buffer.
|
||||
func NewBuilder(buffer []byte) *Builder {
|
||||
return &Builder{
|
||||
result: buffer,
|
||||
}
|
||||
}
|
||||
|
||||
// NewFixedBuilder creates a Builder that appends its output into the given
|
||||
// buffer. This builder does not reallocate the output buffer. Writes that
|
||||
// would exceed the buffer's capacity are treated as an error.
|
||||
func NewFixedBuilder(buffer []byte) *Builder {
|
||||
return &Builder{
|
||||
result: buffer,
|
||||
fixedSize: true,
|
||||
}
|
||||
}
|
||||
|
||||
// SetError sets the value to be returned as the error from Bytes. Writes
|
||||
// performed after calling SetError are ignored.
|
||||
func (b *Builder) SetError(err error) {
|
||||
b.err = err
|
||||
}
|
||||
|
||||
// Bytes returns the bytes written by the builder or an error if one has
|
||||
// occurred during building.
|
||||
func (b *Builder) Bytes() ([]byte, error) {
|
||||
if b.err != nil {
|
||||
return nil, b.err
|
||||
}
|
||||
return b.result[b.offset:], nil
|
||||
}
|
||||
|
||||
// BytesOrPanic returns the bytes written by the builder or panics if an error
|
||||
// has occurred during building.
|
||||
func (b *Builder) BytesOrPanic() []byte {
|
||||
if b.err != nil {
|
||||
panic(b.err)
|
||||
}
|
||||
return b.result[b.offset:]
|
||||
}
|
||||
|
||||
// AddUint8 appends an 8-bit value to the byte string.
|
||||
func (b *Builder) AddUint8(v uint8) {
|
||||
b.add(byte(v))
|
||||
}
|
||||
|
||||
// AddUint16 appends a big-endian, 16-bit value to the byte string.
|
||||
func (b *Builder) AddUint16(v uint16) {
|
||||
b.add(byte(v>>8), byte(v))
|
||||
}
|
||||
|
||||
// AddUint24 appends a big-endian, 24-bit value to the byte string. The highest
|
||||
// byte of the 32-bit input value is silently truncated.
|
||||
func (b *Builder) AddUint24(v uint32) {
|
||||
b.add(byte(v>>16), byte(v>>8), byte(v))
|
||||
}
|
||||
|
||||
// AddUint32 appends a big-endian, 32-bit value to the byte string.
|
||||
func (b *Builder) AddUint32(v uint32) {
|
||||
b.add(byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
||||
}
|
||||
|
||||
// AddUint48 appends a big-endian, 48-bit value to the byte string.
|
||||
func (b *Builder) AddUint48(v uint64) {
|
||||
b.add(byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
||||
}
|
||||
|
||||
// AddUint64 appends a big-endian, 64-bit value to the byte string.
|
||||
func (b *Builder) AddUint64(v uint64) {
|
||||
b.add(byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
|
||||
}
|
||||
|
||||
// AddBytes appends a sequence of bytes to the byte string.
|
||||
func (b *Builder) AddBytes(v []byte) {
|
||||
b.add(v...)
|
||||
}
|
||||
|
||||
// BuilderContinuation is a continuation-passing interface for building
|
||||
// length-prefixed byte sequences. Builder methods for length-prefixed
|
||||
// sequences (AddUint8LengthPrefixed etc) will invoke the BuilderContinuation
|
||||
// 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)
|
||||
// })
|
||||
// })
|
||||
//
|
||||
// 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
|
||||
// invalid, i.e. users must not store any copies or references of the child
|
||||
// that outlive the continuation.
|
||||
//
|
||||
// If the continuation panics with a value of type BuildError then the inner
|
||||
// error will be returned as the error from Bytes. If the child panics
|
||||
// otherwise then Bytes will repanic with the same value.
|
||||
type BuilderContinuation func(child *Builder)
|
||||
|
||||
// BuildError wraps an error. If a BuilderContinuation panics with this value,
|
||||
// the panic will be recovered and the inner error will be returned from
|
||||
// Builder.Bytes.
|
||||
type BuildError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// AddUint8LengthPrefixed adds a 8-bit length-prefixed byte sequence.
|
||||
func (b *Builder) AddUint8LengthPrefixed(f BuilderContinuation) {
|
||||
b.addLengthPrefixed(1, false, f)
|
||||
}
|
||||
|
||||
// AddUint16LengthPrefixed adds a big-endian, 16-bit length-prefixed byte sequence.
|
||||
func (b *Builder) AddUint16LengthPrefixed(f BuilderContinuation) {
|
||||
b.addLengthPrefixed(2, false, f)
|
||||
}
|
||||
|
||||
// AddUint24LengthPrefixed adds a big-endian, 24-bit length-prefixed byte sequence.
|
||||
func (b *Builder) AddUint24LengthPrefixed(f BuilderContinuation) {
|
||||
b.addLengthPrefixed(3, false, f)
|
||||
}
|
||||
|
||||
// AddUint32LengthPrefixed adds a big-endian, 32-bit length-prefixed byte sequence.
|
||||
func (b *Builder) AddUint32LengthPrefixed(f BuilderContinuation) {
|
||||
b.addLengthPrefixed(4, false, f)
|
||||
}
|
||||
|
||||
func (b *Builder) callContinuation(f BuilderContinuation, arg *Builder) {
|
||||
if !*b.inContinuation {
|
||||
*b.inContinuation = true
|
||||
|
||||
defer func() {
|
||||
*b.inContinuation = false
|
||||
|
||||
r := recover()
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if buildError, ok := r.(BuildError); ok {
|
||||
b.err = buildError.Err
|
||||
} else {
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
f(arg)
|
||||
}
|
||||
|
||||
func (b *Builder) addLengthPrefixed(lenLen int, isASN1 bool, f BuilderContinuation) {
|
||||
// Subsequent writes can be ignored if the builder has encountered an error.
|
||||
if b.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
offset := len(b.result)
|
||||
b.add(make([]byte, lenLen)...)
|
||||
|
||||
if b.inContinuation == nil {
|
||||
b.inContinuation = new(bool)
|
||||
}
|
||||
|
||||
b.child = &Builder{
|
||||
result: b.result,
|
||||
fixedSize: b.fixedSize,
|
||||
offset: offset,
|
||||
pendingLenLen: lenLen,
|
||||
pendingIsASN1: isASN1,
|
||||
inContinuation: b.inContinuation,
|
||||
}
|
||||
|
||||
b.callContinuation(f, b.child)
|
||||
b.flushChild()
|
||||
if b.child != nil {
|
||||
panic("cryptobyte: internal error")
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) flushChild() {
|
||||
if b.child == nil {
|
||||
return
|
||||
}
|
||||
b.child.flushChild()
|
||||
child := b.child
|
||||
b.child = nil
|
||||
|
||||
if child.err != nil {
|
||||
b.err = child.err
|
||||
return
|
||||
}
|
||||
|
||||
length := len(child.result) - child.pendingLenLen - child.offset
|
||||
|
||||
if length < 0 {
|
||||
panic("cryptobyte: internal error") // result unexpectedly shrunk
|
||||
}
|
||||
|
||||
if child.pendingIsASN1 {
|
||||
// For ASN.1, we reserved a single byte for the length. If that turned out
|
||||
// to be incorrect, we have to move the contents along in order to make
|
||||
// space.
|
||||
if child.pendingLenLen != 1 {
|
||||
panic("cryptobyte: internal error")
|
||||
}
|
||||
var lenLen, lenByte uint8
|
||||
if int64(length) > 0xfffffffe {
|
||||
b.err = errors.New("pending ASN.1 child too long")
|
||||
return
|
||||
} else if length > 0xffffff {
|
||||
lenLen = 5
|
||||
lenByte = 0x80 | 4
|
||||
} else if length > 0xffff {
|
||||
lenLen = 4
|
||||
lenByte = 0x80 | 3
|
||||
} else if length > 0xff {
|
||||
lenLen = 3
|
||||
lenByte = 0x80 | 2
|
||||
} else if length > 0x7f {
|
||||
lenLen = 2
|
||||
lenByte = 0x80 | 1
|
||||
} else {
|
||||
lenLen = 1
|
||||
lenByte = uint8(length)
|
||||
length = 0
|
||||
}
|
||||
|
||||
// Insert the initial length byte, make space for successive length bytes,
|
||||
// and adjust the offset.
|
||||
child.result[child.offset] = lenByte
|
||||
extraBytes := int(lenLen - 1)
|
||||
if extraBytes != 0 {
|
||||
child.add(make([]byte, extraBytes)...)
|
||||
childStart := child.offset + child.pendingLenLen
|
||||
copy(child.result[childStart+extraBytes:], child.result[childStart:])
|
||||
}
|
||||
child.offset++
|
||||
child.pendingLenLen = extraBytes
|
||||
}
|
||||
|
||||
l := length
|
||||
for i := child.pendingLenLen - 1; i >= 0; i-- {
|
||||
child.result[child.offset+i] = uint8(l)
|
||||
l >>= 8
|
||||
}
|
||||
if l != 0 {
|
||||
b.err = fmt.Errorf("cryptobyte: pending child length %d exceeds %d-byte length prefix", length, child.pendingLenLen)
|
||||
return
|
||||
}
|
||||
|
||||
if b.fixedSize && &b.result[0] != &child.result[0] {
|
||||
panic("cryptobyte: BuilderContinuation reallocated a fixed-size buffer")
|
||||
}
|
||||
|
||||
b.result = child.result
|
||||
}
|
||||
|
||||
func (b *Builder) add(bytes ...byte) {
|
||||
if b.err != nil {
|
||||
return
|
||||
}
|
||||
if b.child != nil {
|
||||
panic("cryptobyte: attempted write while child is pending")
|
||||
}
|
||||
if len(b.result)+len(bytes) < len(bytes) {
|
||||
b.err = errors.New("cryptobyte: length overflow")
|
||||
}
|
||||
if b.fixedSize && len(b.result)+len(bytes) > cap(b.result) {
|
||||
b.err = errors.New("cryptobyte: Builder is exceeding its fixed-size buffer")
|
||||
return
|
||||
}
|
||||
b.result = append(b.result, bytes...)
|
||||
}
|
||||
|
||||
// Unwrite rolls back non-negative n bytes written directly to the Builder.
|
||||
// An attempt by a child builder passed to a continuation to unwrite bytes
|
||||
// from its parent will panic.
|
||||
func (b *Builder) Unwrite(n int) {
|
||||
if b.err != nil {
|
||||
return
|
||||
}
|
||||
if b.child != nil {
|
||||
panic("cryptobyte: attempted unwrite while child is pending")
|
||||
}
|
||||
length := len(b.result) - b.pendingLenLen - b.offset
|
||||
if length < 0 {
|
||||
panic("cryptobyte: internal error")
|
||||
}
|
||||
if n < 0 {
|
||||
panic("cryptobyte: attempted to unwrite negative number of bytes")
|
||||
}
|
||||
if n > length {
|
||||
panic("cryptobyte: attempted to unwrite more than was written")
|
||||
}
|
||||
b.result = b.result[:len(b.result)-n]
|
||||
}
|
||||
|
||||
// A MarshalingValue marshals itself into a Builder.
|
||||
type MarshalingValue interface {
|
||||
// Marshal is called by Builder.AddValue. It receives a pointer to a builder
|
||||
// to marshal itself into. It may return an error that occurred during
|
||||
// marshaling, such as unset or invalid values.
|
||||
Marshal(b *Builder) error
|
||||
}
|
||||
|
||||
// AddValue calls Marshal on v, passing a pointer to the builder to append to.
|
||||
// If Marshal returns an error, it is set on the Builder so that subsequent
|
||||
// appends don't have an effect.
|
||||
func (b *Builder) AddValue(v MarshalingValue) {
|
||||
err := v.Marshal(b)
|
||||
if err != nil {
|
||||
b.err = err
|
||||
}
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
// 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 cryptobyte contains types that help with parsing and constructing
|
||||
// length-prefixed, binary messages, including ASN.1 DER. (The asn1 subpackage
|
||||
// contains useful ASN.1 constants.)
|
||||
//
|
||||
// The String type is for parsing. It wraps a []byte slice and provides helper
|
||||
// functions for consuming structures, value by value.
|
||||
//
|
||||
// The Builder type is for constructing messages. It providers helper functions
|
||||
// for appending values and also for appending length-prefixed submessages –
|
||||
// without having to worry about calculating the length prefix ahead of time.
|
||||
//
|
||||
// See the documentation and examples for the Builder and String types to get
|
||||
// started.
|
||||
package cryptobyte
|
||||
|
||||
// String represents a string of bytes. It provides methods for parsing
|
||||
// fixed-length and length-prefixed values from it.
|
||||
type String []byte
|
||||
|
||||
// read advances a String by n bytes and returns them. If less than n bytes
|
||||
// remain, it returns nil.
|
||||
func (s *String) read(n int) []byte {
|
||||
if len(*s) < n || n < 0 {
|
||||
return nil
|
||||
}
|
||||
v := (*s)[:n]
|
||||
*s = (*s)[n:]
|
||||
return v
|
||||
}
|
||||
|
||||
// Skip advances the String by n byte and reports whether it was successful.
|
||||
func (s *String) Skip(n int) bool {
|
||||
return s.read(n) != nil
|
||||
}
|
||||
|
||||
// ReadUint8 decodes an 8-bit value into out and advances over it.
|
||||
// It reports whether the read was successful.
|
||||
func (s *String) ReadUint8(out *uint8) bool {
|
||||
v := s.read(1)
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
*out = uint8(v[0])
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadUint16 decodes a big-endian, 16-bit value into out and advances over it.
|
||||
// It reports whether the read was successful.
|
||||
func (s *String) ReadUint16(out *uint16) bool {
|
||||
v := s.read(2)
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
*out = uint16(v[0])<<8 | uint16(v[1])
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadUint24 decodes a big-endian, 24-bit value into out and advances over it.
|
||||
// It reports whether the read was successful.
|
||||
func (s *String) ReadUint24(out *uint32) bool {
|
||||
v := s.read(3)
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
*out = uint32(v[0])<<16 | uint32(v[1])<<8 | uint32(v[2])
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadUint32 decodes a big-endian, 32-bit value into out and advances over it.
|
||||
// It reports whether the read was successful.
|
||||
func (s *String) ReadUint32(out *uint32) bool {
|
||||
v := s.read(4)
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
*out = uint32(v[0])<<24 | uint32(v[1])<<16 | uint32(v[2])<<8 | uint32(v[3])
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadUint48 decodes a big-endian, 48-bit value into out and advances over it.
|
||||
// It reports whether the read was successful.
|
||||
func (s *String) ReadUint48(out *uint64) bool {
|
||||
v := s.read(6)
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
*out = uint64(v[0])<<40 | uint64(v[1])<<32 | uint64(v[2])<<24 | uint64(v[3])<<16 | uint64(v[4])<<8 | uint64(v[5])
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadUint64 decodes a big-endian, 64-bit value into out and advances over it.
|
||||
// It reports whether the read was successful.
|
||||
func (s *String) ReadUint64(out *uint64) bool {
|
||||
v := s.read(8)
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
*out = uint64(v[0])<<56 | uint64(v[1])<<48 | uint64(v[2])<<40 | uint64(v[3])<<32 | uint64(v[4])<<24 | uint64(v[5])<<16 | uint64(v[6])<<8 | uint64(v[7])
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *String) readUnsigned(out *uint32, length int) bool {
|
||||
v := s.read(length)
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
var result uint32
|
||||
for i := 0; i < length; i++ {
|
||||
result <<= 8
|
||||
result |= uint32(v[i])
|
||||
}
|
||||
*out = result
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *String) readLengthPrefixed(lenLen int, outChild *String) bool {
|
||||
lenBytes := s.read(lenLen)
|
||||
if lenBytes == nil {
|
||||
return false
|
||||
}
|
||||
var length uint32
|
||||
for _, b := range lenBytes {
|
||||
length = length << 8
|
||||
length = length | uint32(b)
|
||||
}
|
||||
v := s.read(int(length))
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
*outChild = v
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadUint8LengthPrefixed reads the content of an 8-bit length-prefixed value
|
||||
// into out and advances over it. It reports whether the read was successful.
|
||||
func (s *String) ReadUint8LengthPrefixed(out *String) bool {
|
||||
return s.readLengthPrefixed(1, out)
|
||||
}
|
||||
|
||||
// ReadUint16LengthPrefixed reads the content of a big-endian, 16-bit
|
||||
// length-prefixed value into out and advances over it. It reports whether the
|
||||
// read was successful.
|
||||
func (s *String) ReadUint16LengthPrefixed(out *String) bool {
|
||||
return s.readLengthPrefixed(2, out)
|
||||
}
|
||||
|
||||
// ReadUint24LengthPrefixed reads the content of a big-endian, 24-bit
|
||||
// length-prefixed value into out and advances over it. It reports whether
|
||||
// the read was successful.
|
||||
func (s *String) ReadUint24LengthPrefixed(out *String) bool {
|
||||
return s.readLengthPrefixed(3, out)
|
||||
}
|
||||
|
||||
// ReadBytes reads n bytes into out and advances over them. It reports
|
||||
// whether the read was successful.
|
||||
func (s *String) ReadBytes(out *[]byte, n int) bool {
|
||||
v := s.read(n)
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
*out = v
|
||||
return true
|
||||
}
|
||||
|
||||
// CopyBytes copies len(out) bytes into out and advances over them. It reports
|
||||
// whether the copy operation was successful
|
||||
func (s *String) CopyBytes(out []byte) bool {
|
||||
n := len(out)
|
||||
v := s.read(n)
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
return copy(out, v) == n
|
||||
}
|
||||
|
||||
// Empty reports whether the string does not contain any bytes.
|
||||
func (s String) Empty() bool {
|
||||
return len(s) == 0
|
||||
}
|
||||
+4
-1
@@ -634,7 +634,10 @@ func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (boo
|
||||
drain:
|
||||
for {
|
||||
select {
|
||||
case <-ch.msg:
|
||||
case _, ok := <-ch.msg:
|
||||
if !ok {
|
||||
break drain
|
||||
}
|
||||
default:
|
||||
break drain
|
||||
}
|
||||
|
||||
+85
@@ -88,6 +88,32 @@ func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan
|
||||
return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil
|
||||
}
|
||||
|
||||
// NewControlClientConn establishes an SSH connection over an OpenSSH
|
||||
// ControlMaster socket c in proxy mode.
|
||||
//
|
||||
// Note that this package only implements the client side of the multiplexing
|
||||
// protocol. The provided net.Conn must be a local, secure connection (such as a
|
||||
// Unix domain socket) connected to an already-running OpenSSH process acting as
|
||||
// the ControlMaster.
|
||||
//
|
||||
// WARNING: Because proxy mode bypasses the standard cryptographic handshake
|
||||
// passing a standard network connection (e.g., TCP) will result in plaintext
|
||||
// data leakage.
|
||||
//
|
||||
// The Request and NewChannel channels must be serviced or the connection
|
||||
// will hang.
|
||||
func NewControlClientConn(c net.Conn) (Conn, <-chan NewChannel, <-chan *Request, error) {
|
||||
conn := &connection{
|
||||
sshConn: sshConn{conn: c},
|
||||
}
|
||||
var err error
|
||||
if conn.transport, err = handshakeControlProxy(c); err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("ssh: control proxy handshake failed: %w", err)
|
||||
}
|
||||
conn.mux = newMux(conn.transport)
|
||||
return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil
|
||||
}
|
||||
|
||||
// clientHandshake performs the client side key exchange. See RFC 4253 Section
|
||||
// 7.
|
||||
func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) error {
|
||||
@@ -197,6 +223,59 @@ type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error
|
||||
// the server. A BannerCallback receives the message sent by the remote server.
|
||||
type BannerCallback func(message string) error
|
||||
|
||||
// ClientAuthContext contains information about the current state of the
|
||||
// authentication process, passed to [ClientAuthCallback].
|
||||
type ClientAuthContext struct {
|
||||
// Metadata contains the connection metadata.
|
||||
Metadata ConnMetadata
|
||||
|
||||
// Algorithms contains the negotiated algorithms.
|
||||
Algorithms NegotiatedAlgorithms
|
||||
|
||||
// AllowedMethods lists the authentication methods currently accepted
|
||||
// by the server. These are the protocol-level names defined in RFC 4252
|
||||
// such as "publickey", "password".
|
||||
AllowedMethods []string
|
||||
|
||||
// PartialSuccessMethods lists the authentication methods that have already
|
||||
// succeeded, indicating a multi-step authentication flow. This list
|
||||
// represents the exact sequence of partial successes and may contain
|
||||
// duplicates if the same method succeeded multiple times.
|
||||
PartialSuccessMethods []string
|
||||
|
||||
// TriedMethods lists the methods that have already been attempted and
|
||||
// failed during this session. This list represents the exact sequence of
|
||||
// failures and may contain duplicates. This allows the callback to also
|
||||
// track the number of failed attempts for a specific method.
|
||||
TriedMethods []string
|
||||
}
|
||||
|
||||
// ClientAuthCallback is a hook invoked before each authentication attempt. It
|
||||
// allows the client to dynamically select an authentication method based on the
|
||||
// current context, server capabilities, or previous failures.
|
||||
//
|
||||
// The callback is invoked after the initial "none" authentication method, once
|
||||
// the server's supported authentication methods are known.
|
||||
//
|
||||
// Return values:
|
||||
// - (AuthMethod, nil): The client will attempt this specific method next.
|
||||
// The returned method does NOT need to be present in [ClientConfig.Auth].
|
||||
// This allows for dynamic authentication strategies (e.g., prompting
|
||||
// for a password only if public key auth fails). Callers should inspect
|
||||
// [ClientAuthContext.TriedMethods] to avoid repeatedly returning the
|
||||
// same failing method.
|
||||
// - (nil, nil): The client selects from [ClientConfig.Auth] the first
|
||||
// instance of a method that has not been tried yet, or aborts if none
|
||||
// are left. If authentication is not successful, the callback is invoked
|
||||
// again before the following attempt.
|
||||
// - (nil, error): The authentication process is aborted immediately,
|
||||
// causing the ongoing SSH handshake to fail with the provided error.
|
||||
//
|
||||
// To bound resource use, the client caps the total number of authentication
|
||||
// attempts (failures and partial successes combined) at 64. If the cap is
|
||||
// exceeded the handshake aborts with an error.
|
||||
type ClientAuthCallback func(ctx *ClientAuthContext) (AuthMethod, error)
|
||||
|
||||
// A ClientConfig structure is used to configure a Client. It must not be
|
||||
// modified after having been passed to an SSH function.
|
||||
type ClientConfig struct {
|
||||
@@ -210,6 +289,9 @@ type ClientConfig struct {
|
||||
// Auth contains possible authentication methods to use with the
|
||||
// server. Only the first instance of a particular RFC 4252 method will
|
||||
// be used during authentication.
|
||||
//
|
||||
// If AuthCallback is set, these AuthMethod are only used if the
|
||||
// callback returns nil.
|
||||
Auth []AuthMethod
|
||||
|
||||
// HostKeyCallback is called during the cryptographic
|
||||
@@ -240,6 +322,9 @@ type ClientConfig struct {
|
||||
//
|
||||
// A Timeout of zero means no timeout.
|
||||
Timeout time.Duration
|
||||
|
||||
// AuthCallback, if non-nil, is invoked before each authentication attempt.
|
||||
AuthCallback ClientAuthCallback
|
||||
}
|
||||
|
||||
// InsecureIgnoreHostKey returns a function that can be used for
|
||||
|
||||
+50
-14
@@ -21,6 +21,12 @@ const (
|
||||
authSuccess
|
||||
)
|
||||
|
||||
// maxAuthClientTried bounds the total number of authentication attempts
|
||||
// (failures and partial successes combined) the client makes before
|
||||
// aborting the loop, to prevent unbounded growth when an AuthCallback
|
||||
// keeps supplying methods.
|
||||
const maxAuthClientTried = 64
|
||||
|
||||
// clientAuthenticate authenticates with the remote server. See RFC 4252.
|
||||
func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
||||
// initiate user auth session
|
||||
@@ -67,32 +73,62 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
||||
// then any untried methods suggested by the server.
|
||||
var tried []string
|
||||
var lastMethods []string
|
||||
var partialSuccess []string
|
||||
|
||||
sessionID := c.transport.getSessionID()
|
||||
for auth := AuthMethod(new(noneAuth)); auth != nil; {
|
||||
ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand, extensions)
|
||||
if err != nil {
|
||||
// On disconnect, return error immediately
|
||||
if _, ok := err.(*disconnectMsg); ok {
|
||||
if _, isDisconnect := err.(*disconnectMsg); isDisconnect {
|
||||
return err
|
||||
}
|
||||
// We return the error later if there is no other method left to
|
||||
// try.
|
||||
// We return the error later if there is no other method
|
||||
// left to try.
|
||||
ok = authFailure
|
||||
}
|
||||
if ok == authSuccess {
|
||||
// success
|
||||
|
||||
switch ok {
|
||||
case authSuccess:
|
||||
return nil
|
||||
} else if ok == authFailure {
|
||||
if m := auth.method(); !slices.Contains(tried, m) {
|
||||
tried = append(tried, m)
|
||||
}
|
||||
case authPartialSuccess:
|
||||
partialSuccess = append(partialSuccess, auth.method())
|
||||
case authFailure:
|
||||
tried = append(tried, auth.method())
|
||||
}
|
||||
if len(partialSuccess)+len(tried) > maxAuthClientTried {
|
||||
return fmt.Errorf("ssh: too many authentication attempts (%d), aborting",
|
||||
len(partialSuccess)+len(tried))
|
||||
}
|
||||
|
||||
if methods == nil {
|
||||
methods = lastMethods
|
||||
}
|
||||
lastMethods = methods
|
||||
|
||||
// If AuthCallback is set it takes precedence: it picks the next
|
||||
// AuthMethod dynamically. The returned method need not be in
|
||||
// config.Auth. If the callback returns (nil, nil) we fall back to
|
||||
// selecting the next untried method from config.Auth below; on
|
||||
// (nil, error) the handshake aborts.
|
||||
if config.AuthCallback != nil {
|
||||
ctx := &ClientAuthContext{
|
||||
Metadata: c,
|
||||
Algorithms: c.Algorithms(),
|
||||
AllowedMethods: slices.Clone(methods),
|
||||
PartialSuccessMethods: slices.Clone(partialSuccess),
|
||||
TriedMethods: slices.Clone(tried),
|
||||
}
|
||||
altAuth, cbErr := config.AuthCallback(ctx)
|
||||
if cbErr != nil {
|
||||
return cbErr
|
||||
}
|
||||
if altAuth != nil {
|
||||
auth = altAuth
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
auth = nil
|
||||
|
||||
findNext:
|
||||
@@ -377,11 +413,11 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
||||
return authFailure, nil, err
|
||||
}
|
||||
|
||||
// If authentication succeeds or the list of available methods does not
|
||||
// contain the "publickey" method, do not attempt to authenticate with any
|
||||
// other keys. According to RFC 4252 Section 7, the latter can occur when
|
||||
// additional authentication methods are required.
|
||||
if success == authSuccess || !slices.Contains(methods, cb.method()) {
|
||||
// If authentication succeeds or partially succeeds, return immediately
|
||||
// so the caller can select the next auth method. According to RFC 4252
|
||||
// Section 7, if the server no longer lists "publickey" among its
|
||||
// allowed methods, do not attempt to authenticate with any other keys.
|
||||
if success == authSuccess || success == authPartialSuccess || !slices.Contains(methods, cb.method()) {
|
||||
return success, methods, err
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -91,9 +91,17 @@ func DiscardRequests(in <-chan *Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// A connTransport represents the transport for a connection.
|
||||
type connTransport interface {
|
||||
packetConn
|
||||
getAlgorithms() NegotiatedAlgorithms
|
||||
getSessionID() []byte
|
||||
waitSession() error
|
||||
}
|
||||
|
||||
// A connection represents an incoming connection.
|
||||
type connection struct {
|
||||
transport *handshakeTransport
|
||||
transport connTransport
|
||||
sshConn
|
||||
|
||||
// The connection protocol.
|
||||
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
// Copyright 2026 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 ssh
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
)
|
||||
|
||||
const (
|
||||
muxProtocolVersion = 4
|
||||
|
||||
muxMsgHello = 0x00000001
|
||||
muxCProxy = 0x1000000f
|
||||
muxSProxy = 0x8000000f
|
||||
)
|
||||
|
||||
const controlProxyRequestID = 0
|
||||
|
||||
// handshakeControlProxy attempts to establish a transport connection with an
|
||||
// OpenSSH ControlMaster socket in proxy mode. For details see:
|
||||
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.mux
|
||||
func handshakeControlProxy(rw io.ReadWriteCloser) (connTransport, error) {
|
||||
if err := controlProxyWritePacket(rw, func(b *cryptobyte.Builder) {
|
||||
b.AddUint32(muxMsgHello)
|
||||
b.AddUint32(muxProtocolVersion)
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("mux hello write failed: %w", err)
|
||||
}
|
||||
if err := controlProxyWritePacket(rw, func(b *cryptobyte.Builder) {
|
||||
b.AddUint32(muxCProxy)
|
||||
b.AddUint32(controlProxyRequestID)
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("mux client proxy write failed: %w", err)
|
||||
}
|
||||
|
||||
messageType, body, err := controlProxyReadMessage(rw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("mux hello read failed: %w", err)
|
||||
}
|
||||
if messageType != muxMsgHello {
|
||||
return nil, fmt.Errorf("expected hello response, got %v", messageType)
|
||||
}
|
||||
var v uint32
|
||||
if !body.ReadUint32(&v) {
|
||||
return nil, errors.New("EOF reading mux protocol version")
|
||||
}
|
||||
if v != muxProtocolVersion {
|
||||
return nil, fmt.Errorf("mux server has unsupported version %v", v)
|
||||
}
|
||||
messageType, body, err = controlProxyReadMessage(rw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("mux server proxy read failed: %w", err)
|
||||
}
|
||||
if messageType != muxSProxy {
|
||||
return nil, fmt.Errorf("expected server proxy response, got %v", messageType)
|
||||
}
|
||||
var reqID uint32
|
||||
if !body.ReadUint32(&reqID) {
|
||||
return nil, errors.New("EOF reading request id")
|
||||
}
|
||||
if reqID != controlProxyRequestID {
|
||||
return nil, fmt.Errorf("expected request id %v, got %v", controlProxyRequestID, reqID)
|
||||
}
|
||||
return &controlProxyTransport{rw}, nil
|
||||
}
|
||||
|
||||
// controlProxyTransport implements the connTransport interface for
|
||||
// ControlMaster connections. Each controlMessage has zero length padding and
|
||||
// no MAC.
|
||||
type controlProxyTransport struct {
|
||||
rw io.ReadWriteCloser
|
||||
}
|
||||
|
||||
func (p *controlProxyTransport) Close() error {
|
||||
return p.rw.Close()
|
||||
}
|
||||
|
||||
func (p *controlProxyTransport) writePacket(controlMessage []byte) error {
|
||||
return controlProxyWritePacket(p.rw, func(b *cryptobyte.Builder) {
|
||||
b.AddUint8(0) // Padding length.
|
||||
b.AddBytes(controlMessage)
|
||||
})
|
||||
}
|
||||
|
||||
func (p *controlProxyTransport) readPacket() ([]byte, error) {
|
||||
buf, err := controlProxyReadPacket(p.rw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ssh: error reading control message: %w", err)
|
||||
}
|
||||
// Discard the padding length.
|
||||
if len(buf) < 1 {
|
||||
return nil, errors.New("ssh: EOF reading padding length")
|
||||
}
|
||||
if buf[0] != 0 {
|
||||
return nil, errors.New("ssh: unexpected non-zero padding in control message")
|
||||
}
|
||||
return buf[1:], nil
|
||||
}
|
||||
|
||||
func (p *controlProxyTransport) getAlgorithms() NegotiatedAlgorithms {
|
||||
return NegotiatedAlgorithms{}
|
||||
}
|
||||
|
||||
func (p *controlProxyTransport) getSessionID() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *controlProxyTransport) waitSession() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func controlProxyWritePacket(w io.Writer, f cryptobyte.BuilderContinuation) error {
|
||||
var buf []byte
|
||||
b := cryptobyte.NewBuilder(buf)
|
||||
b.AddUint32LengthPrefixed(f)
|
||||
out, err := b.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(out)
|
||||
return err
|
||||
}
|
||||
|
||||
func controlProxyReadPacket(r io.Reader) (cryptobyte.String, error) {
|
||||
var l uint32
|
||||
if err := binary.Read(r, binary.BigEndian, &l); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if l > maxPacket {
|
||||
return nil, fmt.Errorf("message length %v exceeds maximum %v", l, maxPacket)
|
||||
}
|
||||
buf := make([]byte, l)
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func controlProxyReadMessage(r io.Reader) (messageType uint32, body cryptobyte.String, err error) {
|
||||
body, err = controlProxyReadPacket(r)
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("error reading message body: %w", err)
|
||||
}
|
||||
if !body.ReadUint32(&messageType) {
|
||||
return 0, nil, errors.New("EOF reading message type")
|
||||
}
|
||||
return messageType, body, nil
|
||||
}
|
||||
+66
-9
@@ -16,6 +16,7 @@ import (
|
||||
"io"
|
||||
"math/big"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
@@ -718,15 +719,9 @@ func (gex *dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshak
|
||||
kexDHGexRequest.MaxBits, kexDHGexRequest.PreferredBits)
|
||||
}
|
||||
|
||||
var p *big.Int
|
||||
// We hardcode sending Oakley Group 14 (2048 bits), Oakley Group 15 (3072
|
||||
// bits) or Oakley Group 16 (4096 bits), based on the requested max size.
|
||||
if kexDHGexRequest.MaxBits < 3072 {
|
||||
p, _ = new(big.Int).SetString(oakleyGroup14, 16)
|
||||
} else if kexDHGexRequest.MaxBits < 4096 {
|
||||
p, _ = new(big.Int).SetString(oakleyGroup15, 16)
|
||||
} else {
|
||||
p, _ = new(big.Int).SetString(oakleyGroup16, 16)
|
||||
p, err := chooseDH(kexDHGexRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g := big.NewInt(2)
|
||||
@@ -805,3 +800,65 @@ func (gex *dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshak
|
||||
Hash: gex.hashFunc,
|
||||
}, err
|
||||
}
|
||||
|
||||
type dhKEXGroup struct {
|
||||
size int
|
||||
p *big.Int
|
||||
}
|
||||
|
||||
// supportedDHKEXGroups returns the DH groups the server is willing to offer
|
||||
// for diffie-hellman-group-exchange-* key exchanges. The list is built lazily
|
||||
// on first use to keep the hex-to-big.Int parse out of package initialization.
|
||||
var supportedDHKEXGroups = sync.OnceValue(func() []dhKEXGroup {
|
||||
specs := []struct {
|
||||
size int
|
||||
hex string
|
||||
}{
|
||||
{2048, oakleyGroup14},
|
||||
{3072, oakleyGroup15},
|
||||
{4096, oakleyGroup16},
|
||||
}
|
||||
out := make([]dhKEXGroup, 0, len(specs))
|
||||
for _, s := range specs {
|
||||
p, _ := new(big.Int).SetString(s.hex, 16)
|
||||
out = append(out, dhKEXGroup{size: s.size, p: p})
|
||||
}
|
||||
return out
|
||||
})
|
||||
|
||||
// chooseDH picks a DH group for the given client request, mirroring the
|
||||
// algorithm used by OpenSSH's choose_dh in dh.c: prefer the smallest known
|
||||
// group larger than or equal to the client's PreferredBits, and otherwise pick
|
||||
// the largest group within the accepted [MinBits, MaxBits] range.
|
||||
func chooseDH(req kexDHGexRequestMsg) (*big.Int, error) {
|
||||
var best *big.Int
|
||||
bestSize := 0
|
||||
wantBits := int(req.PreferredBits)
|
||||
|
||||
for _, group := range supportedDHKEXGroups() {
|
||||
if uint32(group.size) < req.MinBits || uint32(group.size) > req.MaxBits {
|
||||
continue
|
||||
}
|
||||
|
||||
if bestSize == 0 {
|
||||
best = group.p
|
||||
bestSize = group.size
|
||||
continue
|
||||
}
|
||||
|
||||
closerFromAbove := group.size >= wantBits && group.size < bestSize
|
||||
closerFromBelow := group.size > bestSize && bestSize < wantBits
|
||||
|
||||
if closerFromAbove || closerFromBelow {
|
||||
best = group.p
|
||||
bestSize = group.size
|
||||
}
|
||||
}
|
||||
|
||||
if bestSize == 0 {
|
||||
return nil, fmt.Errorf("ssh: no suitable DH group found for request min: %d, preferred: %d, max: %d",
|
||||
req.MinBits, req.PreferredBits, req.MaxBits)
|
||||
}
|
||||
|
||||
return best, nil
|
||||
}
|
||||
|
||||
+38
-3
@@ -76,7 +76,7 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err
|
||||
case InsecureKeyAlgoDSA:
|
||||
return parseDSA(in)
|
||||
case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
|
||||
return parseECDSA(in)
|
||||
return parseECDSA(in, algo)
|
||||
case KeyAlgoSKECDSA256:
|
||||
return parseSKECDSA(in)
|
||||
case KeyAlgoED25519:
|
||||
@@ -806,7 +806,7 @@ func supportedEllipticCurve(curve elliptic.Curve) bool {
|
||||
}
|
||||
|
||||
// parseECDSA parses an ECDSA key according to RFC 5656, section 3.1.
|
||||
func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) {
|
||||
func parseECDSA(in []byte, expectedType string) (out PublicKey, rest []byte, err error) {
|
||||
var w struct {
|
||||
Curve string
|
||||
KeyBytes []byte
|
||||
@@ -817,6 +817,12 @@ func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
actualType := "ecdsa-sha2-" + w.Curve
|
||||
if expectedType != actualType {
|
||||
return nil, nil, fmt.Errorf("ssh: algorithm type mismatch: expected %q, found curve %q (type %q)",
|
||||
expectedType, w.Curve, actualType)
|
||||
}
|
||||
|
||||
key := new(ecdsa.PublicKey)
|
||||
|
||||
switch w.Curve {
|
||||
@@ -1466,6 +1472,17 @@ func passphraseProtectedOpenSSHKey(passphrase []byte) openSSHDecryptFunc {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// OpenSSH does not impose an upper bound on the bcrypt round count
|
||||
// stored in the key file, but bcrypt_pbkdf cost is linear in rounds:
|
||||
// the default is 16, ssh-keygen lets users pick anything up to
|
||||
// INT_MAX. Cap at 2048 (128x the default, a few seconds of CPU) so
|
||||
// that an oversized value in the file cannot tie up the caller for
|
||||
// months.
|
||||
const maxRounds = 1 << 11
|
||||
if opts.Rounds > maxRounds {
|
||||
return nil, fmt.Errorf("ssh: bcrypt KDF rounds %d exceed maximum %d", opts.Rounds, maxRounds)
|
||||
}
|
||||
|
||||
k, err := bcrypt_pbkdf.Key(passphrase, []byte(opts.Salt), int(opts.Rounds), 32+16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1635,10 +1652,28 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Mirror the validation done in parseRSA for public keys: cap the
|
||||
// modulus at the same limit enforced by crypto/tls, reject oversized
|
||||
// or invalid exponents, and additionally bound the prime factors to
|
||||
// avoid the expensive CRT coefficient recomputation in pk.Precompute.
|
||||
if key.N.BitLen() > 8192 {
|
||||
return nil, errors.New("ssh: rsa modulus too large")
|
||||
}
|
||||
if key.P.BitLen() > 4096 || key.Q.BitLen() > 4096 {
|
||||
return nil, errors.New("ssh: rsa prime too large")
|
||||
}
|
||||
if key.E.BitLen() > 24 {
|
||||
return nil, errors.New("ssh: exponent too large")
|
||||
}
|
||||
e := key.E.Int64()
|
||||
if e < 3 || e&1 == 0 {
|
||||
return nil, errors.New("ssh: incorrect exponent")
|
||||
}
|
||||
|
||||
pk := &rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{
|
||||
N: key.N,
|
||||
E: int(key.E.Int64()),
|
||||
E: int(e),
|
||||
},
|
||||
D: key.D,
|
||||
Primes: []*big.Int{key.P, key.Q},
|
||||
|
||||
+4
-1
@@ -155,7 +155,10 @@ func (m *mux) SendRequest(name string, wantReply bool, payload []byte) (bool, []
|
||||
drain:
|
||||
for {
|
||||
select {
|
||||
case <-m.globalResponses:
|
||||
case _, ok := <-m.globalResponses:
|
||||
if !ok {
|
||||
break drain
|
||||
}
|
||||
default:
|
||||
break drain
|
||||
}
|
||||
|
||||
+33
-5
@@ -54,6 +54,9 @@ type Permissions struct {
|
||||
ExtraData map[any]any
|
||||
}
|
||||
|
||||
// GSSAPIWithMICConfig includes the server callbacks for gssapi-with-mic
|
||||
// authentication. If either field is nil, gssapi-with-mic is considered not
|
||||
// configured.
|
||||
type GSSAPIWithMICConfig struct {
|
||||
// AllowLogin, must be set, is called when gssapi-with-mic
|
||||
// authentication is selected (RFC 4462 section 3). The srcName is from the
|
||||
@@ -68,6 +71,10 @@ type GSSAPIWithMICConfig struct {
|
||||
Server GSSAPIServer
|
||||
}
|
||||
|
||||
func gssapiWithMICConfigured(config *GSSAPIWithMICConfig) bool {
|
||||
return config != nil && config.AllowLogin != nil && config.Server != nil
|
||||
}
|
||||
|
||||
// SendAuthBanner implements [ServerPreAuthConn].
|
||||
func (s *connection) SendAuthBanner(msg string) error {
|
||||
return s.transport.writePacket(Marshal(&userAuthBannerMsg{
|
||||
@@ -382,8 +389,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
|
||||
}
|
||||
|
||||
if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil &&
|
||||
config.KeyboardInteractiveCallback == nil && (config.GSSAPIWithMICConfig == nil ||
|
||||
config.GSSAPIWithMICConfig.AllowLogin == nil || config.GSSAPIWithMICConfig.Server == nil) {
|
||||
config.KeyboardInteractiveCallback == nil && !gssapiWithMICConfigured(config.GSSAPIWithMICConfig) {
|
||||
return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
|
||||
}
|
||||
|
||||
@@ -607,6 +613,15 @@ func (b *BannerError) Error() string {
|
||||
return b.Err.Error()
|
||||
}
|
||||
|
||||
// maxAuthServerAttempts caps the total number of SSH_MSG_USERAUTH_REQUEST
|
||||
// messages the server will process on a single connection, regardless of
|
||||
// outcome (failure, partial success, public key query, or none). It is a
|
||||
// backstop against clients that drive the authentication loop indefinitely
|
||||
// without ever incurring a real failure — for example by repeatedly
|
||||
// triggering PartialSuccessError or by spamming public key offer queries —
|
||||
// neither of which increment the MaxAuthTries failure counter.
|
||||
const maxAuthServerAttempts = 128
|
||||
|
||||
func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
|
||||
if config.PreAuthConnCallback != nil {
|
||||
config.PreAuthConnCallback(s)
|
||||
@@ -617,6 +632,7 @@ func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, err
|
||||
var perms *Permissions
|
||||
|
||||
authFailures := 0
|
||||
authAttempts := 0
|
||||
noneAuthCount := 0
|
||||
var authErrs []error
|
||||
var calledBannerCallback bool
|
||||
@@ -645,6 +661,19 @@ userAuthLoop:
|
||||
return nil, &ServerAuthError{Errors: authErrs}
|
||||
}
|
||||
|
||||
if authAttempts >= maxAuthServerAttempts {
|
||||
discMsg := &disconnectMsg{
|
||||
Reason: 2,
|
||||
Message: "too many authentication attempts",
|
||||
}
|
||||
if err := s.transport.writePacket(Marshal(discMsg)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authErrs = append(authErrs, discMsg)
|
||||
return nil, &ServerAuthError{Errors: authErrs}
|
||||
}
|
||||
authAttempts++
|
||||
|
||||
var userAuthReq userAuthRequestMsg
|
||||
if packet, err := s.transport.readPacket(); err != nil {
|
||||
if err == io.EOF {
|
||||
@@ -846,7 +875,7 @@ userAuthLoop:
|
||||
}
|
||||
}
|
||||
case "gssapi-with-mic":
|
||||
if authConfig.GSSAPIWithMICConfig == nil {
|
||||
if !gssapiWithMICConfigured(authConfig.GSSAPIWithMICConfig) {
|
||||
authErr = errors.New("ssh: gssapi-with-mic auth not configured")
|
||||
break
|
||||
}
|
||||
@@ -979,8 +1008,7 @@ userAuthLoop:
|
||||
if authConfig.KeyboardInteractiveCallback != nil {
|
||||
failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive")
|
||||
}
|
||||
if authConfig.GSSAPIWithMICConfig != nil && authConfig.GSSAPIWithMICConfig.Server != nil &&
|
||||
authConfig.GSSAPIWithMICConfig.AllowLogin != nil {
|
||||
if gssapiWithMICConfigured(authConfig.GSSAPIWithMICConfig) {
|
||||
failureMsg.Methods = append(failureMsg.Methods, "gssapi-with-mic")
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -423,6 +423,9 @@ func (s *Session) wait(reqs <-chan *Request) error {
|
||||
for msg := range reqs {
|
||||
switch msg.Type {
|
||||
case "exit-status":
|
||||
if len(msg.Payload) < 4 {
|
||||
return errors.New("ssh: malformed exit-status request")
|
||||
}
|
||||
wm.status = int(binary.BigEndian.Uint32(msg.Payload))
|
||||
case "exit-signal":
|
||||
var sigval struct {
|
||||
|
||||
+3
-5
@@ -9,6 +9,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
@@ -105,8 +106,7 @@ func (x *FileSyntax) addLine(hint Expr, tokens ...string) *Line {
|
||||
if hint == nil {
|
||||
// If no hint given, add to the last statement of the given type.
|
||||
Loop:
|
||||
for i := len(x.Stmt) - 1; i >= 0; i-- {
|
||||
stmt := x.Stmt[i]
|
||||
for _, stmt := range slices.Backward(x.Stmt) {
|
||||
switch stmt := stmt.(type) {
|
||||
case *Line:
|
||||
if stmt.Token != nil && stmt.Token[0] == tokens[0] {
|
||||
@@ -718,9 +718,7 @@ func (in *input) assignComments() {
|
||||
}
|
||||
|
||||
// Assign suffix comments to syntax immediately before.
|
||||
for i := len(in.post) - 1; i >= 0; i-- {
|
||||
x := in.post[i]
|
||||
|
||||
for _, x := range slices.Backward(in.post) {
|
||||
start, end := x.Span()
|
||||
if debug {
|
||||
fmt.Fprintf(os.Stderr, "post %T :%d:%d #%d :%d:%d #%d\n", x, start.Line, start.LineRune, start.Byte, end.Line, end.LineRune, end.Byte)
|
||||
|
||||
+56
-9
@@ -327,6 +327,7 @@ func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (parse
|
||||
}
|
||||
|
||||
var GoVersionRE = lazyregexp.New(`^([1-9][0-9]*)\.(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))?([a-z]+[0-9]+)?$`)
|
||||
|
||||
var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9].*)$`)
|
||||
|
||||
// Toolchains must be named beginning with `go1`,
|
||||
@@ -1272,6 +1273,17 @@ func (f *File) SetRequire(req []*Require) {
|
||||
// SetRequireSeparateIndirect will split it into a direct-only and indirect-only
|
||||
// block. This aids in the transition to separate blocks.
|
||||
func (f *File) SetRequireSeparateIndirect(req []*Require) {
|
||||
f.setRequireSeparateIndirect(req, false)
|
||||
}
|
||||
|
||||
// SetRequireAtMostTwo is like SetRequireSeparateIndirect but it aggressively
|
||||
// consolidates all requirements into at most two blocks (one direct, one indirect).
|
||||
// It ignores existing blocks and comments when deciding where to place requirements.
|
||||
func (f *File) SetRequireAtMostTwo(req []*Require) {
|
||||
f.setRequireSeparateIndirect(req, true)
|
||||
}
|
||||
|
||||
func (f *File) setRequireSeparateIndirect(req []*Require, simplify bool) {
|
||||
// hasComments returns whether a line or block has comments
|
||||
// other than "indirect".
|
||||
hasComments := func(c Comments) bool {
|
||||
@@ -1304,6 +1316,17 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
|
||||
}
|
||||
|
||||
// Examine existing require lines and blocks.
|
||||
need := make(map[string]*Require)
|
||||
for _, r := range req {
|
||||
need[r.Mod.Path] = r
|
||||
}
|
||||
lineIndirect := make(map[*Line]bool)
|
||||
for _, r := range f.Require {
|
||||
if n := need[r.Mod.Path]; n != nil {
|
||||
lineIndirect[r.Syntax] = n.Indirect
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// We may insert new requirements into the last uncommented
|
||||
// direct-only and indirect-only blocks. We may also move requirements
|
||||
@@ -1321,7 +1344,9 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
|
||||
|
||||
// Track the block each requirement belongs to (if any) so we can
|
||||
// move them later.
|
||||
lineToBlock = make(map[*Line]*LineBlock)
|
||||
lineToBlock = make(map[*Line]*LineBlock)
|
||||
directBlockComments []Comment
|
||||
indirectBlockComments []Comment
|
||||
)
|
||||
for i, stmt := range f.Syntax.Stmt {
|
||||
switch stmt := stmt.(type) {
|
||||
@@ -1364,6 +1389,24 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
|
||||
if allIndirect {
|
||||
lastIndirectIndex = i
|
||||
}
|
||||
if simplify {
|
||||
anyDirect := false
|
||||
for _, line := range stmt.Line {
|
||||
if ind, ok := lineIndirect[line]; ok && !ind {
|
||||
anyDirect = true
|
||||
break
|
||||
}
|
||||
}
|
||||
target := &directBlockComments
|
||||
if !anyDirect && len(stmt.Line) > 0 {
|
||||
target = &indirectBlockComments
|
||||
}
|
||||
if len(*target) > 0 && len(stmt.Comments.Before) > 0 {
|
||||
*target = append(*target, Comment{Token: "//"})
|
||||
}
|
||||
*target = append(*target, stmt.Comments.Before...)
|
||||
stmt.Comments.Before = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1422,6 +1465,15 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
|
||||
lastIndirectBlock = ensureBlock(lastIndirectIndex)
|
||||
}
|
||||
|
||||
if simplify {
|
||||
if len(directBlockComments) > 0 {
|
||||
lastDirectBlock.Comments.Before = append(lastDirectBlock.Comments.Before, directBlockComments...)
|
||||
}
|
||||
if len(indirectBlockComments) > 0 {
|
||||
lastIndirectBlock.Comments.Before = append(lastIndirectBlock.Comments.Before, indirectBlockComments...)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete requirements we don't want anymore.
|
||||
// Update versions and indirect comments on requirements we want to keep.
|
||||
// If a requirement is in last{Direct,Indirect}Block with the wrong
|
||||
@@ -1430,10 +1482,6 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
|
||||
// correct block.
|
||||
//
|
||||
// Some blocks may be empty after this. Cleanup will remove them.
|
||||
need := make(map[string]*Require)
|
||||
for _, r := range req {
|
||||
need[r.Mod.Path] = r
|
||||
}
|
||||
have := make(map[string]*Require)
|
||||
for _, r := range f.Require {
|
||||
path := r.Mod.Path
|
||||
@@ -1446,10 +1494,10 @@ func (f *File) SetRequireSeparateIndirect(req []*Require) {
|
||||
r.setVersion(need[path].Mod.Version)
|
||||
r.setIndirect(need[path].Indirect)
|
||||
if need[path].Indirect &&
|
||||
(oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastDirectBlock) {
|
||||
(simplify || oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastDirectBlock) {
|
||||
moveReq(r, lastIndirectBlock)
|
||||
} else if !need[path].Indirect &&
|
||||
(oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastIndirectBlock) {
|
||||
(simplify || oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastIndirectBlock) {
|
||||
moveReq(r, lastDirectBlock)
|
||||
}
|
||||
}
|
||||
@@ -1736,8 +1784,7 @@ func removeDups(syntax *FileSyntax, exclude *[]*Exclude, replace *[]*Replace, to
|
||||
// Remove duplicate replacements.
|
||||
// Later replacements take priority over earlier ones.
|
||||
haveReplace := make(map[module.Version]bool)
|
||||
for i := len(*replace) - 1; i >= 0; i-- {
|
||||
x := (*replace)[i]
|
||||
for _, x := range slices.Backward(*replace) {
|
||||
if haveReplace[x.Old] {
|
||||
kill[x.Syntax] = true
|
||||
continue
|
||||
|
||||
+16
@@ -10,9 +10,11 @@ package http2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -44,6 +46,20 @@ func configureServer(s *http.Server, conf *Server) error {
|
||||
h2.IdleTimeout = h1.ReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Register h2 and http/1.1 ALPN protocols on s.TLSConfig, matching
|
||||
// the pre-wrapping implementation in server.go, so that TLS listeners
|
||||
// built from s.TLSConfig still negotiate HTTP/2.
|
||||
if s.TLSConfig == nil {
|
||||
s.TLSConfig = new(tls.Config)
|
||||
}
|
||||
if !slices.Contains(s.TLSConfig.NextProtos, NextProtoTLS) {
|
||||
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, NextProtoTLS)
|
||||
}
|
||||
if !slices.Contains(s.TLSConfig.NextProtos, "http/1.1") {
|
||||
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "http/1.1")
|
||||
}
|
||||
|
||||
conf.state = &serverInternalState{
|
||||
s1: s,
|
||||
}
|
||||
|
||||
+13
-2
@@ -22,8 +22,8 @@ import (
|
||||
)
|
||||
|
||||
func configureTransport(t1 *http.Transport) error {
|
||||
// ConfigureTransport is a no-op: The http.Transport already supports HTTP/2.
|
||||
return nil
|
||||
_, err := configureTransports(t1)
|
||||
return err
|
||||
}
|
||||
|
||||
func configureTransports(t1 *http.Transport) (*Transport, error) {
|
||||
@@ -31,6 +31,17 @@ func configureTransports(t1 *http.Transport) (*Transport, error) {
|
||||
// linked to the http.Transport's.
|
||||
tr2 := &Transport{}
|
||||
tr2.configure(t1)
|
||||
// Enable HTTP/2 on the transport, as the pre-wrapping implementation did:
|
||||
// net/http does not auto-enable it for a transport with a custom
|
||||
// TLSClientConfig or dialer.
|
||||
if t1.TLSClientConfig == nil {
|
||||
t1.TLSClientConfig = &tls.Config{}
|
||||
}
|
||||
if t1.Protocols == nil {
|
||||
t1.Protocols = new(http.Protocols)
|
||||
t1.Protocols.SetHTTP1(true)
|
||||
}
|
||||
t1.Protocols.SetHTTP2(true)
|
||||
return tr2, nil
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ 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.
|
||||
// Note: this allows barging if and only if channels in general allow barging.
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
+76
@@ -6397,3 +6397,79 @@ const (
|
||||
MPOL_PREFERRED_MANY = 0x5
|
||||
MPOL_WEIGHTED_INTERLEAVE = 0x6
|
||||
)
|
||||
|
||||
const (
|
||||
GPIO_V2_GET_LINEINFO_IOCTL = 0xc100b405
|
||||
GPIO_V2_GET_LINE_IOCTL = 0xc250b407
|
||||
GPIO_V2_LINE_GET_VALUES_IOCTL = 0xc010b40e
|
||||
GPIO_V2_LINE_SET_VALUES_IOCTL = 0xc010b40f
|
||||
GPIO_V2_GET_LINEINFO_WATCH_IOCTL = 0xc100b406
|
||||
GPIO_GET_LINEINFO_UNWATCH_IOCTL = 0xc004b40c
|
||||
)
|
||||
const (
|
||||
GPIO_V2_LINE_ATTR_ID_FLAGS = 0x1
|
||||
GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES = 0x2
|
||||
GPIO_V2_LINE_ATTR_ID_DEBOUNCE = 0x3
|
||||
GPIO_V2_LINE_CHANGED_REQUESTED = 0x1
|
||||
GPIO_V2_LINE_CHANGED_RELEASED = 0x2
|
||||
GPIO_V2_LINE_CHANGED_CONFIG = 0x3
|
||||
GPIO_V2_LINE_EVENT_RISING_EDGE = 0x1
|
||||
GPIO_V2_LINE_EVENT_FALLING_EDGE = 0x2
|
||||
)
|
||||
|
||||
type GPIOChipInfo struct {
|
||||
Name [32]byte
|
||||
Label [32]byte
|
||||
Lines uint32
|
||||
}
|
||||
type GPIOV2LineValues struct {
|
||||
Bits uint64
|
||||
Mask uint64
|
||||
}
|
||||
type GPIOV2LineAttribute struct {
|
||||
Id uint32
|
||||
_ uint32
|
||||
Flags uint64
|
||||
}
|
||||
type GPIOV2LineConfigAttribute struct {
|
||||
Attr GPIOV2LineAttribute
|
||||
Mask uint64
|
||||
}
|
||||
type GPIOV2LineConfig struct {
|
||||
Flags uint64
|
||||
Num_attrs uint32
|
||||
_ [5]uint32
|
||||
Attrs [10]GPIOV2LineConfigAttribute
|
||||
}
|
||||
type GPIOV2LineRequest struct {
|
||||
Offsets [64]uint32
|
||||
Consumer [32]byte
|
||||
Config GPIOV2LineConfig
|
||||
Num_lines uint32
|
||||
Event_buffer_size uint32
|
||||
_ [5]uint32
|
||||
Fd int32
|
||||
}
|
||||
type GPIOV2LineInfo struct {
|
||||
Name [32]byte
|
||||
Consumer [32]byte
|
||||
Offset uint32
|
||||
Num_attrs uint32
|
||||
Flags uint64
|
||||
Attrs [10]GPIOV2LineAttribute
|
||||
_ [4]uint32
|
||||
}
|
||||
type GPIOV2LineInfoChanged struct {
|
||||
Info GPIOV2LineInfo
|
||||
Timestamp_ns uint64
|
||||
Event_type uint32
|
||||
_ [5]uint32
|
||||
}
|
||||
type GPIOV2LineEvent struct {
|
||||
Timestamp_ns uint64
|
||||
Id uint32
|
||||
Offset uint32
|
||||
Seqno uint32
|
||||
Line_seqno uint32
|
||||
_ [6]uint32
|
||||
}
|
||||
|
||||
+4
@@ -711,3 +711,7 @@ type SysvShmDesc struct {
|
||||
_ uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x8044b401
|
||||
)
|
||||
|
||||
+4
@@ -725,3 +725,7 @@ type SysvShmDesc struct {
|
||||
_ uint64
|
||||
_ uint64
|
||||
}
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x8044b401
|
||||
)
|
||||
|
||||
+4
@@ -705,3 +705,7 @@ type SysvShmDesc struct {
|
||||
_ uint32
|
||||
_ uint32
|
||||
}
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x8044b401
|
||||
)
|
||||
|
||||
+4
@@ -704,3 +704,7 @@ type SysvShmDesc struct {
|
||||
_ uint64
|
||||
_ uint64
|
||||
}
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x8044b401
|
||||
)
|
||||
|
||||
+4
@@ -705,3 +705,7 @@ type SysvShmDesc struct {
|
||||
_ uint64
|
||||
_ uint64
|
||||
}
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x8044b401
|
||||
)
|
||||
|
||||
+4
@@ -710,3 +710,7 @@ type SysvShmDesc struct {
|
||||
Ctime_high uint16
|
||||
_ uint16
|
||||
}
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x4044b401
|
||||
)
|
||||
|
||||
+4
@@ -707,3 +707,7 @@ type SysvShmDesc struct {
|
||||
_ uint64
|
||||
_ uint64
|
||||
}
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x4044b401
|
||||
)
|
||||
|
||||
+4
@@ -707,3 +707,7 @@ type SysvShmDesc struct {
|
||||
_ uint64
|
||||
_ uint64
|
||||
}
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x4044b401
|
||||
)
|
||||
|
||||
+4
@@ -710,3 +710,7 @@ type SysvShmDesc struct {
|
||||
Ctime_high uint16
|
||||
_ uint16
|
||||
}
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x4044b401
|
||||
)
|
||||
|
||||
+4
@@ -718,3 +718,7 @@ type SysvShmDesc struct {
|
||||
_ uint32
|
||||
_ [4]byte
|
||||
}
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x4044b401
|
||||
)
|
||||
|
||||
+4
@@ -713,3 +713,7 @@ type SysvShmDesc struct {
|
||||
_ uint64
|
||||
_ uint64
|
||||
}
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x4044b401
|
||||
)
|
||||
|
||||
+4
@@ -713,3 +713,7 @@ type SysvShmDesc struct {
|
||||
_ uint64
|
||||
_ uint64
|
||||
}
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x4044b401
|
||||
)
|
||||
|
||||
+4
@@ -792,3 +792,7 @@ const (
|
||||
RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE = 0x6
|
||||
RISCV_HWPROBE_WHICH_CPUS = 0x1
|
||||
)
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x8044b401
|
||||
)
|
||||
|
||||
+4
@@ -727,3 +727,7 @@ type SysvShmDesc struct {
|
||||
_ uint64
|
||||
_ uint64
|
||||
}
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x8044b401
|
||||
)
|
||||
|
||||
+4
@@ -708,3 +708,7 @@ type SysvShmDesc struct {
|
||||
_ uint64
|
||||
_ uint64
|
||||
}
|
||||
|
||||
const (
|
||||
GPIO_GET_CHIPINFO_IOCTL = 0x4044b401
|
||||
)
|
||||
|
||||
+1
-1
@@ -249,7 +249,7 @@ func upper(c *context) bool {
|
||||
return c.copy()
|
||||
}
|
||||
|
||||
// isUpper writes the isUppercase version of the current rune to dst.
|
||||
// isUpper reports whether the current rune is in upper case.
|
||||
func isUpper(c *context) bool {
|
||||
ct := c.caseType()
|
||||
if c.info&hasMappingMask == 0 || ct == cUpper {
|
||||
|
||||
+2
-2
@@ -774,7 +774,7 @@ func nlTitle(c *context) bool {
|
||||
// From CLDR:
|
||||
// # Special titlecasing for Dutch initial "ij".
|
||||
// ::Any-Title();
|
||||
// # Fix up Ij at the beginning of a "word" (per Any-Title, notUAX #29)
|
||||
// # Fix up Ij at the beginning of a "word" (per Any-Title, not UAX #29)
|
||||
// [:^WB=ALetter:] [:WB=Extend:]* [[:WB=MidLetter:][:WB=MidNumLet:]]? { Ij } → IJ ;
|
||||
if c.src[c.pSrc] != 'I' && c.src[c.pSrc] != 'i' {
|
||||
return title(c)
|
||||
@@ -794,7 +794,7 @@ func nlTitleSpan(c *context) bool {
|
||||
// From CLDR:
|
||||
// # Special titlecasing for Dutch initial "ij".
|
||||
// ::Any-Title();
|
||||
// # Fix up Ij at the beginning of a "word" (per Any-Title, notUAX #29)
|
||||
// # Fix up Ij at the beginning of a "word" (per Any-Title, not UAX #29)
|
||||
// [:^WB=ALetter:] [:WB=Extend:]* [[:WB=MidLetter:][:WB=MidNumLet:]]? { Ij } → IJ ;
|
||||
if c.src[c.pSrc] != 'I' {
|
||||
return isTitle(c)
|
||||
|
||||
+8
-1
@@ -121,8 +121,12 @@ func (p Properties) BoundaryAfter() bool {
|
||||
//
|
||||
// When all 6 bits are zero, the character is inert, meaning it is never
|
||||
// influenced by normalization.
|
||||
//
|
||||
// We set flags to 0x80 (high bit 7 unused in quick check data) to indicate an invalid rune.
|
||||
type qcInfo uint8
|
||||
|
||||
func (p Properties) isInvalid() bool { return p.flags == 0x80 }
|
||||
|
||||
func (p Properties) isYesC() bool { return p.flags&0x10 == 0 }
|
||||
func (p Properties) isYesD() bool { return p.flags&0x4 == 0 }
|
||||
|
||||
@@ -247,6 +251,9 @@ func (f Form) PropertiesString(s string) Properties {
|
||||
// to a Properties. See the comment at the top of the file
|
||||
// for more information on the format.
|
||||
func compInfo(v uint16, sz int) Properties {
|
||||
if sz == 0 {
|
||||
return Properties{flags: 0x80, size: 1}
|
||||
}
|
||||
if v == 0 {
|
||||
return Properties{size: uint8(sz)}
|
||||
} else if v >= 0x8000 {
|
||||
@@ -254,7 +261,7 @@ func compInfo(v uint16, sz int) Properties {
|
||||
size: uint8(sz),
|
||||
ccc: uint8(v),
|
||||
tccc: uint8(v),
|
||||
flags: qcInfo(v >> 8),
|
||||
flags: qcInfo(v>>8) & 0x3f,
|
||||
}
|
||||
if p.ccc > 0 || p.combinesBackward() {
|
||||
p.nLead = uint8(p.flags & 0x3)
|
||||
|
||||
+2
-6
@@ -376,16 +376,12 @@ func nextComposed(i *Iter) []byte {
|
||||
goto doNorm
|
||||
}
|
||||
prevCC = i.info.tccc
|
||||
sz := int(i.info.size)
|
||||
if sz == 0 {
|
||||
sz = 1 // illegal rune: copy byte-by-byte
|
||||
}
|
||||
p := outp + sz
|
||||
p := outp + int(i.info.size)
|
||||
if p > len(i.buf) {
|
||||
break
|
||||
}
|
||||
outp = p
|
||||
i.p += sz
|
||||
i.p += int(i.info.size)
|
||||
if i.p >= i.rb.nsrc {
|
||||
i.setDone()
|
||||
break
|
||||
|
||||
+10
-10
@@ -148,7 +148,7 @@ func (f Form) IsNormalString(s string) bool {
|
||||
// patched buffer and whether the decomposition is still in progress.
|
||||
func patchTail(rb *reorderBuffer) bool {
|
||||
info, p := lastRuneStart(&rb.f, rb.out)
|
||||
if p == -1 || info.size == 0 {
|
||||
if p == -1 || info.isInvalid() {
|
||||
return true
|
||||
}
|
||||
end := p + int(info.size)
|
||||
@@ -225,7 +225,7 @@ func doAppend(rb *reorderBuffer, out []byte, p int) []byte {
|
||||
}
|
||||
fd := &rb.f
|
||||
if doMerge {
|
||||
var info Properties
|
||||
info := Properties{flags: 0x80, size: 1} // invalid rune
|
||||
if p < n {
|
||||
info = fd.info(src, p)
|
||||
if !info.BoundaryBefore() || info.nLeadingNonStarters() > 0 {
|
||||
@@ -235,7 +235,7 @@ func doAppend(rb *reorderBuffer, out []byte, p int) []byte {
|
||||
p = decomposeSegment(rb, p, true)
|
||||
}
|
||||
}
|
||||
if info.size == 0 {
|
||||
if info.isInvalid() {
|
||||
rb.doFlush()
|
||||
// Append incomplete UTF-8 encoding.
|
||||
return src.appendSlice(rb.out, p, n)
|
||||
@@ -314,7 +314,7 @@ func (f *formInfo) quickSpan(src input, i, end int, atEOF bool) (n int, ok bool)
|
||||
continue
|
||||
}
|
||||
info := f.info(src, i)
|
||||
if info.size == 0 {
|
||||
if info.isInvalid() {
|
||||
if atEOF {
|
||||
// include incomplete runes
|
||||
return n, true
|
||||
@@ -379,7 +379,7 @@ func (f Form) firstBoundary(src input, nsrc int) int {
|
||||
// CGJ insertion points correctly. Luckily it doesn't have to.
|
||||
for {
|
||||
info := fd.info(src, i)
|
||||
if info.size == 0 {
|
||||
if info.isInvalid() {
|
||||
return -1
|
||||
}
|
||||
if s := ss.next(info); s != ssSuccess {
|
||||
@@ -424,7 +424,7 @@ func (f Form) nextBoundary(src input, nsrc int, atEOF bool) int {
|
||||
}
|
||||
fd := formTable[f]
|
||||
info := fd.info(src, 0)
|
||||
if info.size == 0 {
|
||||
if info.isInvalid() {
|
||||
if atEOF {
|
||||
return 1
|
||||
}
|
||||
@@ -435,7 +435,7 @@ func (f Form) nextBoundary(src input, nsrc int, atEOF bool) int {
|
||||
|
||||
for i := int(info.size); i < nsrc; i += int(info.size) {
|
||||
info = fd.info(src, i)
|
||||
if info.size == 0 {
|
||||
if info.isInvalid() {
|
||||
if atEOF {
|
||||
return i
|
||||
}
|
||||
@@ -465,7 +465,7 @@ func lastBoundary(fd *formInfo, b []byte) int {
|
||||
if p == -1 {
|
||||
return -1
|
||||
}
|
||||
if info.size == 0 { // ends with incomplete rune
|
||||
if info.isInvalid() { // ends with incomplete rune
|
||||
if p == 0 { // starts with incomplete rune
|
||||
return -1
|
||||
}
|
||||
@@ -504,7 +504,7 @@ func lastBoundary(fd *formInfo, b []byte) int {
|
||||
func decomposeSegment(rb *reorderBuffer, sp int, atEOF bool) int {
|
||||
// Force one character to be consumed.
|
||||
info := rb.f.info(rb.src, sp)
|
||||
if info.size == 0 {
|
||||
if info.isInvalid() {
|
||||
return 0
|
||||
}
|
||||
if s := rb.ss.next(info); s == ssStarter {
|
||||
@@ -528,7 +528,7 @@ func decomposeSegment(rb *reorderBuffer, sp int, atEOF bool) int {
|
||||
break
|
||||
}
|
||||
info = rb.f.info(rb.src, sp)
|
||||
if info.size == 0 {
|
||||
if info.isInvalid() {
|
||||
if !atEOF {
|
||||
return int(iShortSrc)
|
||||
}
|
||||
|
||||
+14
-10
@@ -12,7 +12,7 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// A Kind describes a field of an ast.Node struct.
|
||||
// A Kind describes a field of an [ast.Node] struct.
|
||||
type Kind uint8
|
||||
|
||||
// String returns a description of the edge kind.
|
||||
@@ -41,21 +41,25 @@ func (k Kind) Get(n ast.Node, idx int) ast.Node {
|
||||
panic(fmt.Sprintf("%v.Get(%T): invalid node type", k, n))
|
||||
}
|
||||
v := reflect.ValueOf(n).Elem().Field(fieldInfos[k].index)
|
||||
if idx != -1 {
|
||||
v = v.Index(idx) // asserts valid index
|
||||
} else {
|
||||
// (The type assertion below asserts that v is not a slice.)
|
||||
|
||||
if v.Kind() == reflect.Slice {
|
||||
v = v.Index(idx) // asserts valid idx
|
||||
} else if idx != -1 {
|
||||
panic(fmt.Sprintf("%v, Get(%T, %d): cannot index non-slice", v, n, idx))
|
||||
}
|
||||
return v.Interface().(ast.Node) // may be nil
|
||||
|
||||
out, _ := v.Interface().(ast.Node) // may be nil
|
||||
return out
|
||||
}
|
||||
|
||||
// Each [Kind] is named Type_Field, where Type is the
|
||||
// [ast.Node] struct type and Field is the name of the field
|
||||
const (
|
||||
Invalid Kind = iota // for nodes at the root of the traversal
|
||||
|
||||
// Kinds are sorted alphabetically.
|
||||
// Numbering is not stable.
|
||||
// Each is named Type_Field, where Type is the
|
||||
// ast.Node struct type and Field is the name of the field
|
||||
// As of Go1.26 these kinds are sorted alphabetically, but
|
||||
// numbering must be stable, so any new addition of const should
|
||||
// use a new value (be added at the end of the list).
|
||||
|
||||
ArrayType_Elt
|
||||
ArrayType_Len
|
||||
|
||||
+13
-4
@@ -207,11 +207,10 @@ func goListDriver(cfg *Config, runner *gocommand.Runner, overlay string, pattern
|
||||
// doesn't exist.
|
||||
extractQueries:
|
||||
for _, pattern := range patterns {
|
||||
eqidx := strings.Index(pattern, "=")
|
||||
if eqidx < 0 {
|
||||
query, value, ok := strings.Cut(pattern, "=")
|
||||
if !ok {
|
||||
restPatterns = append(restPatterns, pattern)
|
||||
} else {
|
||||
query, value := pattern[:eqidx], pattern[eqidx+len("="):]
|
||||
switch query {
|
||||
case "file":
|
||||
containFiles = append(containFiles, value)
|
||||
@@ -563,8 +562,18 @@ func (state *golistState) createDriverResponse(words ...string) (*DriverResponse
|
||||
} else {
|
||||
// golang/go#38990: go list silently fails to do cgo processing
|
||||
pkg.CompiledGoFiles = nil
|
||||
|
||||
var msg strings.Builder
|
||||
fmt.Fprintf(&msg, "go list failed to return CompiledGoFiles for %q.\n", p.Name)
|
||||
|
||||
for _, err := range p.DepsErrors {
|
||||
msg.WriteString(strings.TrimSpace(err.Err))
|
||||
msg.WriteByte('\n')
|
||||
}
|
||||
|
||||
msg.WriteString("This may indicate failure to perform cgo processing; try building at the command line. See https://golang.org/issue/38990.")
|
||||
pkg.Errors = append(pkg.Errors, Error{
|
||||
Msg: "go list failed to return CompiledGoFiles. This may indicate failure to perform cgo processing; try building at the command line. See https://golang.org/issue/38990.",
|
||||
Msg: msg.String(),
|
||||
Kind: ListError,
|
||||
})
|
||||
}
|
||||
|
||||
+28
-2
@@ -539,6 +539,11 @@ type Package struct {
|
||||
|
||||
// depsErrors is the DepsErrors field from the go list response, if any.
|
||||
depsErrors []*packagesinternal.PackageError
|
||||
|
||||
// exportDataError is the error encountered reading export data, if any.
|
||||
// Decoding export data should ordinarily be infallible, so this typically
|
||||
// indicates a producer/consumer version skew.
|
||||
exportDataError error
|
||||
}
|
||||
|
||||
// Module provides module information for a package.
|
||||
@@ -810,6 +815,12 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) {
|
||||
needsrc: needsrc,
|
||||
goVersion: response.GoVersion,
|
||||
}
|
||||
// Don't trust the driver to respond with duplicate-free
|
||||
// package names (go.dev/issue/63822).
|
||||
if _, ok := ld.pkgs[lpkg.ID]; ok {
|
||||
return nil, fmt.Errorf("%s response contained duplicate packages for ID %q",
|
||||
cond(ld.externalDriver, "go/packages driver", "go list"), lpkg.ID)
|
||||
}
|
||||
ld.pkgs[lpkg.ID] = lpkg
|
||||
if rootIndex >= 0 {
|
||||
initial[rootIndex] = lpkg
|
||||
@@ -1073,10 +1084,11 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
||||
}
|
||||
|
||||
// TODO(adonovan): this condition looks wrong:
|
||||
// I think it should be lpkg.needtypes && !lpg.needsrc,
|
||||
// I think it should be lpkg.needtypes && !lpkg.needsrc,
|
||||
// so that NeedSyntax without NeedTypes can be satisfied by export data.
|
||||
if !lpkg.needsrc {
|
||||
if err := ld.loadFromExportData(lpkg); err != nil {
|
||||
lpkg.exportDataError = err
|
||||
lpkg.Errors = append(lpkg.Errors, Error{
|
||||
Pos: "-",
|
||||
Msg: err.Error(),
|
||||
@@ -1215,7 +1227,13 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
|
||||
if ipkg.Types != nil && ipkg.Types.Complete() {
|
||||
return ipkg.Types, nil
|
||||
}
|
||||
log.Fatalf("internal error: package %q without types was imported from %q", path, lpkg)
|
||||
|
||||
// If types are unavailable, there must be an export data error.
|
||||
if ipkg.exportDataError != nil {
|
||||
return nil, ipkg.exportDataError
|
||||
}
|
||||
|
||||
log.Fatalf("internal error: expected complete types for package %q", path)
|
||||
panic("unreachable")
|
||||
})
|
||||
|
||||
@@ -1577,3 +1595,11 @@ func usesExportData(cfg *Config) bool {
|
||||
}
|
||||
|
||||
type unit struct{}
|
||||
|
||||
func cond[T any](cond bool, t, f T) T {
|
||||
if cond {
|
||||
return t
|
||||
} else {
|
||||
return f
|
||||
}
|
||||
}
|
||||
|
||||
+366
-237
@@ -24,8 +24,10 @@
|
||||
package objectpath
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -124,7 +126,66 @@ func For(obj types.Object) (Path, error) {
|
||||
// An Encoder amortizes the cost of encoding the paths of multiple objects.
|
||||
// The zero value of an Encoder is ready to use.
|
||||
type Encoder struct {
|
||||
scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects
|
||||
pkgIndex map[*types.Package]*pkgIndex
|
||||
}
|
||||
|
||||
// A traversal encapsulates the state of a single traversal of the object/type graph.
|
||||
type traversal struct {
|
||||
pkg *types.Package
|
||||
ix *pkgIndex // non-nil if we are building the index
|
||||
|
||||
target types.Object // the sought symbol (if ix == nil)
|
||||
found Path // the found path (if ix == nil)
|
||||
|
||||
// These maps are used to short circuit cycles through
|
||||
// interface methods, such as occur in the following example:
|
||||
//
|
||||
// type I interface { f() interface{I} }
|
||||
//
|
||||
// See golang/go#68046 for details.
|
||||
seenTParamNames map[*types.TypeName]bool // global cycle breaking through type parameters
|
||||
seenMethods map[*types.Func]bool // global cycle breaking through recursive interfaces
|
||||
}
|
||||
|
||||
// A pkgIndex holds a compressed index of objectpaths of all symbols
|
||||
// (fields, methods, params) requiring search for an entire package.
|
||||
//
|
||||
// The first time a search for a given package is requested, we simply
|
||||
// traverse the type graph for the target object, maintaining the
|
||||
// current object path as a stack. If we find the target object, we
|
||||
// save the path and terminate the main loop (but it's not worth
|
||||
// breaking out of the current recursion).
|
||||
//
|
||||
// On the second search (a pkgIndex exists but its data is nil), we
|
||||
// build an index of the traversal, which we use for all subsequent
|
||||
// searches.
|
||||
//
|
||||
// The traversal index is encoded in the data field as a list of records,
|
||||
// one per node, in preorder. Records are of two types:
|
||||
//
|
||||
// - A record for a package-level object consists of a pair
|
||||
// (parent, nameIndex uvarint), where parent is zero and
|
||||
// nameIndex is the index of the object's name in the sorted
|
||||
// pkg.Scope().Names() slice.
|
||||
//
|
||||
// - A record for a nested node (a segment of an object path)
|
||||
// consists of (parent uvarint, op byte, index uvarint), where
|
||||
// parent is the index of the record for the parent node,
|
||||
// op is the destructuring operator, and index (if op = [AFMTr])
|
||||
// is its integer operand.
|
||||
//
|
||||
// Since data[0] = 0 all nodes have positive offsets. In effect the
|
||||
// encoding is a trie in which each node stores one path segment
|
||||
// and points to the node for its prefix.
|
||||
//
|
||||
// TODO(adonovan): opt: evaluate an only 2-level tree with nodes for
|
||||
// package-level objects and the-rest-of-the-path. One calculation
|
||||
// suggested that it might be similar speed but 30% more compact.
|
||||
type pkgIndex struct {
|
||||
pkg *types.Package
|
||||
data []byte // encoding of traversal; nil if not yet constructed
|
||||
scopeNames []string // memo of pkg.Scope().Names() to avoid O(n) alloc/sort at lookup
|
||||
offsets map[types.Object]uint32 // each object's node offset within encoded traversal data
|
||||
}
|
||||
|
||||
// For returns the path to an object relative to its package,
|
||||
@@ -211,10 +272,9 @@ func (enc *Encoder) For(obj types.Object) (Path, error) {
|
||||
if pkg == nil {
|
||||
return "", fmt.Errorf("predeclared %s has no path", obj)
|
||||
}
|
||||
scope := pkg.Scope()
|
||||
|
||||
// 2. package-level object?
|
||||
if scope.Lookup(obj.Name()) == obj {
|
||||
if pkg.Scope().Lookup(obj.Name()) == obj {
|
||||
// Only exported objects (and non-exported types) have a path.
|
||||
// Non-exported types may be referenced by other objects.
|
||||
if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() {
|
||||
@@ -232,19 +292,18 @@ func (enc *Encoder) For(obj types.Object) (Path, error) {
|
||||
// have a path.
|
||||
return "", fmt.Errorf("no path for %v", obj)
|
||||
}
|
||||
|
||||
case *types.Const, // Only package-level constants have a path.
|
||||
*types.Label, // Labels are function-local.
|
||||
*types.PkgName: // PkgNames are file-local.
|
||||
return "", fmt.Errorf("no path for %v", obj)
|
||||
|
||||
case *types.Var:
|
||||
// Could be:
|
||||
// - a field (obj.IsField())
|
||||
// - a func parameter or result
|
||||
// - a local var.
|
||||
// Sadly there is no way to distinguish
|
||||
// a param/result from a local
|
||||
// so we must proceed to the find.
|
||||
// A var, if not package-level, must be a
|
||||
// parameter (incl. receiver) or result, or a struct field.
|
||||
if obj.Kind() == types.LocalVar {
|
||||
return "", fmt.Errorf("no path for local %v", obj)
|
||||
}
|
||||
|
||||
case *types.Func:
|
||||
// A func, if not package-level, must be a method.
|
||||
@@ -261,89 +320,311 @@ func (enc *Encoder) For(obj types.Object) (Path, error) {
|
||||
panic(obj)
|
||||
}
|
||||
|
||||
// 4. Search the API for the path to the var (field/param/result) or method.
|
||||
// 4. Search the object/type graph for the path to
|
||||
// the var (field/param/result) or method.
|
||||
ix, ok := enc.pkgIndex[pkg]
|
||||
if !ok {
|
||||
// First search: don't build an index, just traverse.
|
||||
// This avoids allocation in [For], whose Encoder
|
||||
// lives for a single call.
|
||||
ix = &pkgIndex{pkg: pkg}
|
||||
|
||||
// First inspect package-level named types.
|
||||
// In the presence of path aliases, these give
|
||||
// the best paths because non-types may
|
||||
// refer to types, but not the reverse.
|
||||
empty := make([]byte, 0, 48) // initial space
|
||||
objs := enc.scopeObjects(scope)
|
||||
for _, o := range objs {
|
||||
tname, ok := o.(*types.TypeName)
|
||||
if !ok {
|
||||
continue // handle non-types in second pass
|
||||
if enc.pkgIndex == nil {
|
||||
enc.pkgIndex = make(map[*types.Package]*pkgIndex)
|
||||
}
|
||||
enc.pkgIndex[pkg] = ix // build the index next time
|
||||
|
||||
f := traversal{pkg: pkg, target: obj}
|
||||
f.traverse()
|
||||
|
||||
if f.found != "" {
|
||||
return f.found, nil
|
||||
}
|
||||
} else {
|
||||
// Second search: build an index while traversing.
|
||||
if ix.data == nil {
|
||||
ix.offsets = make(map[types.Object]uint32)
|
||||
ix.data = []byte{0} // offset 0 is sentinel
|
||||
(&traversal{pkg: pkg, ix: ix}).traverse()
|
||||
}
|
||||
|
||||
path := append(empty, o.Name()...)
|
||||
path = append(path, opType)
|
||||
|
||||
T := o.Type()
|
||||
if alias, ok := T.(*types.Alias); ok {
|
||||
if r := findTypeParam(obj, alias.TypeParams(), path, opTypeParam); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
if r := find(obj, alias.Rhs(), append(path, opRhs)); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
|
||||
} else if tname.IsAlias() {
|
||||
// legacy alias
|
||||
if r := find(obj, T, path); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
|
||||
} else if named, ok := T.(*types.Named); ok {
|
||||
// defined (named) type
|
||||
if r := findTypeParam(obj, named.TypeParams(), path, opTypeParam); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
if r := find(obj, named.Underlying(), append(path, opUnderlying)); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then inspect everything else:
|
||||
// non-types, and declared methods of defined types.
|
||||
for _, o := range objs {
|
||||
path := append(empty, o.Name()...)
|
||||
if _, ok := o.(*types.TypeName); !ok {
|
||||
if o.Exported() {
|
||||
// exported non-type (const, var, func)
|
||||
if r := find(obj, o.Type(), append(path, opType)); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Inspect declared methods of defined types.
|
||||
if T, ok := types.Unalias(o.Type()).(*types.Named); ok {
|
||||
path = append(path, opType)
|
||||
// The method index here is always with respect
|
||||
// to the underlying go/types data structures,
|
||||
// which ultimately derives from source order
|
||||
// and must be preserved by export data.
|
||||
for i := 0; i < T.NumMethods(); i++ {
|
||||
m := T.Method(i)
|
||||
path2 := appendOpArg(path, opMethod, i)
|
||||
if m == obj {
|
||||
return Path(path2), nil // found declared method
|
||||
}
|
||||
if r := find(obj, m.Type(), append(path2, opType)); r != nil {
|
||||
return Path(r), nil
|
||||
}
|
||||
}
|
||||
// Second and later searches: consult the index.
|
||||
if offset, ok := ix.offsets[obj]; ok {
|
||||
return ix.path(offset), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path())
|
||||
}
|
||||
|
||||
func appendOpArg(path []byte, op byte, arg int) []byte {
|
||||
// traverse performs a complete traversal of all symbols reachable from the package.
|
||||
func (tr *traversal) traverse() {
|
||||
scope := tr.pkg.Scope()
|
||||
names := scope.Names()
|
||||
if tr.ix != nil {
|
||||
tr.ix.scopeNames = names
|
||||
}
|
||||
|
||||
empty := make([]byte, 0, 48) // initial space for stack (ix == nil)
|
||||
|
||||
// First inspect package-level type names.
|
||||
// In the presence of path aliases, these give
|
||||
// the best paths because non-types may
|
||||
// refer to types, but not the reverse.
|
||||
for i, name := range names {
|
||||
if tr.found != "" {
|
||||
return // found (ix == nil)
|
||||
}
|
||||
|
||||
obj := scope.Lookup(name)
|
||||
if _, ok := obj.(*types.TypeName); !ok {
|
||||
continue // handle non-types in second pass
|
||||
}
|
||||
|
||||
// emit (name, opType)
|
||||
var path []byte
|
||||
var offset uint32
|
||||
if tr.ix == nil {
|
||||
path = append(empty, name...)
|
||||
path = append(path, opType)
|
||||
} else {
|
||||
offset = tr.ix.emitPackageLevel(i)
|
||||
tr.ix.offsets[obj] = offset
|
||||
offset = tr.ix.emitPathSegment(offset, opType, -1)
|
||||
}
|
||||
|
||||
// A TypeName (for Named or Alias) may have type parameters.
|
||||
switch t := obj.Type().(type) {
|
||||
case *types.Alias:
|
||||
tr.tparams(t.TypeParams(), path, offset, opTypeParam)
|
||||
tr.typ(path, offset, opRhs, -1, t.Rhs())
|
||||
case *types.Named:
|
||||
tr.tparams(t.TypeParams(), path, offset, opTypeParam)
|
||||
tr.typ(path, offset, opUnderlying, -1, t.Underlying())
|
||||
}
|
||||
}
|
||||
|
||||
// Then inspect everything else:
|
||||
// exported non-types, and declared methods of defined types.
|
||||
for i, name := range names {
|
||||
if tr.found != "" {
|
||||
return // found (ix == nil)
|
||||
}
|
||||
|
||||
obj := scope.Lookup(name)
|
||||
|
||||
if tname, ok := obj.(*types.TypeName); !ok {
|
||||
if obj.Exported() {
|
||||
// exported non-type (const, var, func)
|
||||
var path []byte
|
||||
var offset uint32
|
||||
if tr.ix == nil {
|
||||
path = append(empty, name...)
|
||||
} else {
|
||||
offset = tr.ix.emitPackageLevel(i)
|
||||
tr.ix.offsets[obj] = offset
|
||||
}
|
||||
tr.typ(path, offset, opType, -1, obj.Type())
|
||||
}
|
||||
|
||||
} else if T, ok := types.Unalias(tname.Type()).(*types.Named); ok {
|
||||
// defined type
|
||||
var path []byte
|
||||
var offset uint32
|
||||
if tr.ix == nil {
|
||||
path = append(empty, name...)
|
||||
path = append(path, opType)
|
||||
} else {
|
||||
// Inv: map entry for obj was populated in first pass.
|
||||
offset = tr.ix.emitPathSegment(tr.ix.offsets[obj], opType, -1)
|
||||
}
|
||||
|
||||
// Inspect declared methods of defined types.
|
||||
//
|
||||
// The method index here is always with respect
|
||||
// to the underlying go/types data structures,
|
||||
// which ultimately derives from source order
|
||||
// and must be preserved by export data.
|
||||
for i := 0; i < T.NumMethods(); i++ {
|
||||
m := T.Method(i)
|
||||
tr.object(path, offset, opMethod, i, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tr *traversal) visitType(path []byte, offset uint32, T types.Type) {
|
||||
switch T := T.(type) {
|
||||
case *types.Alias:
|
||||
tr.typ(path, offset, opRhs, -1, T.Rhs())
|
||||
|
||||
case *types.Basic, *types.Named:
|
||||
// Named types belonging to pkg were handled already,
|
||||
// so T must belong to another package. No path.
|
||||
return
|
||||
|
||||
case *types.Pointer, *types.Slice, *types.Array, *types.Chan:
|
||||
type hasElem interface{ Elem() types.Type } // note: includes Map
|
||||
tr.typ(path, offset, opElem, -1, T.(hasElem).Elem())
|
||||
|
||||
case *types.Map:
|
||||
tr.typ(path, offset, opKey, -1, T.Key())
|
||||
tr.typ(path, offset, opElem, -1, T.Elem())
|
||||
|
||||
case *types.Signature:
|
||||
tr.tparams(T.RecvTypeParams(), path, offset, opRecvTypeParam)
|
||||
tr.tparams(T.TypeParams(), path, offset, opTypeParam)
|
||||
tr.typ(path, offset, opParams, -1, T.Params())
|
||||
tr.typ(path, offset, opResults, -1, T.Results())
|
||||
|
||||
case *types.Struct:
|
||||
for i := 0; i < T.NumFields(); i++ {
|
||||
tr.object(path, offset, opField, i, T.Field(i))
|
||||
}
|
||||
|
||||
case *types.Tuple:
|
||||
for i := 0; i < T.Len(); i++ {
|
||||
tr.object(path, offset, opAt, i, T.At(i))
|
||||
}
|
||||
|
||||
case *types.Interface:
|
||||
for i := 0; i < T.NumMethods(); i++ {
|
||||
m := T.Method(i)
|
||||
if m.Pkg() != nil && m.Pkg() != tr.pkg {
|
||||
continue // embedded method from another package
|
||||
}
|
||||
if !tr.seenMethods[m] {
|
||||
if tr.seenMethods == nil {
|
||||
tr.seenMethods = make(map[*types.Func]bool)
|
||||
}
|
||||
tr.seenMethods[m] = true
|
||||
tr.object(path, offset, opMethod, i, m)
|
||||
}
|
||||
}
|
||||
|
||||
case *types.TypeParam:
|
||||
tname := T.Obj()
|
||||
if tname.Pkg() != nil && tname.Pkg() != tr.pkg {
|
||||
return // type parameter from another package
|
||||
}
|
||||
if !tr.seenTParamNames[tname] {
|
||||
if tr.seenTParamNames == nil {
|
||||
tr.seenTParamNames = make(map[*types.TypeName]bool)
|
||||
}
|
||||
tr.seenTParamNames[tname] = true
|
||||
tr.object(path, offset, opObj, -1, tname)
|
||||
tr.typ(path, offset, opConstraint, -1, T.Constraint())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tr *traversal) tparams(list *types.TypeParamList, path []byte, offset uint32, op byte) {
|
||||
for i := 0; i < list.Len(); i++ {
|
||||
tr.typ(path, offset, op, i, list.At(i))
|
||||
}
|
||||
}
|
||||
|
||||
// typ descends the type graph edge (op, index), then proceeds to traverse type t.
|
||||
func (tr *traversal) typ(path []byte, offset uint32, op byte, index int, t types.Type) {
|
||||
if tr.ix == nil {
|
||||
path = appendOpArg(path, op, index)
|
||||
} else {
|
||||
offset = tr.ix.emitPathSegment(offset, op, index)
|
||||
}
|
||||
tr.visitType(path, offset, t)
|
||||
}
|
||||
|
||||
// object descends the type graph edge (op, index), records object
|
||||
// obj, then proceeds to traverse its type.
|
||||
func (tr *traversal) object(path []byte, offset uint32, op byte, index int, obj types.Object) {
|
||||
if tr.ix == nil {
|
||||
path = appendOpArg(path, op, index)
|
||||
if obj == tr.target && tr.found == "" {
|
||||
tr.found = Path(path)
|
||||
}
|
||||
path = append(path, opType)
|
||||
} else {
|
||||
offset = tr.ix.emitPathSegment(offset, op, index)
|
||||
if _, ok := tr.ix.offsets[obj]; !ok {
|
||||
tr.ix.offsets[obj] = offset
|
||||
}
|
||||
offset = tr.ix.emitPathSegment(offset, opType, -1)
|
||||
}
|
||||
tr.visitType(path, offset, obj.Type())
|
||||
}
|
||||
|
||||
// emitPackageLevel encodes a record for a package-level symbol,
|
||||
// identified by its index in ix.scopeNames.
|
||||
func (p *pkgIndex) emitPackageLevel(index int) uint32 {
|
||||
off := uint32(len(p.data))
|
||||
p.data = append(p.data, 0) // zero varint => no parent
|
||||
p.data = binary.AppendUvarint(p.data, uint64(index))
|
||||
return off
|
||||
}
|
||||
|
||||
// emitPathSegment emits a record for a non-initial object path segment.
|
||||
func (p *pkgIndex) emitPathSegment(parent uint32, op byte, index int) uint32 {
|
||||
off := uint32(len(p.data))
|
||||
p.data = binary.AppendUvarint(p.data, uint64(parent))
|
||||
p.data = append(p.data, op)
|
||||
switch op {
|
||||
case opAt, opField, opMethod, opTypeParam, opRecvTypeParam:
|
||||
p.data = binary.AppendUvarint(p.data, uint64(index))
|
||||
}
|
||||
return off
|
||||
}
|
||||
|
||||
// path returns the Path for the encoded node at the specified offset.
|
||||
func (p *pkgIndex) path(offset uint32) Path {
|
||||
var elems []string // path elements in reverse
|
||||
for {
|
||||
// Read parent index.
|
||||
parent, n := binary.Uvarint(p.data[offset:])
|
||||
offset += uint32(n)
|
||||
|
||||
if parent == 0 {
|
||||
break // root (end of path)
|
||||
}
|
||||
|
||||
op := p.data[offset]
|
||||
offset++
|
||||
|
||||
// The [AFMTr] operators have a numeric operand.
|
||||
switch op {
|
||||
case opAt, opField, opMethod, opTypeParam, opRecvTypeParam:
|
||||
val, n := binary.Uvarint(p.data[offset:])
|
||||
offset += uint32(n)
|
||||
elems = append(elems, strconv.Itoa(int(val)))
|
||||
}
|
||||
|
||||
elems = append(elems, string([]byte{op}))
|
||||
|
||||
offset = uint32(parent)
|
||||
}
|
||||
idx, _ := binary.Uvarint(p.data[offset:])
|
||||
|
||||
// Convert index to Path string.
|
||||
name := p.scopeNames[idx]
|
||||
sz := len(name)
|
||||
for _, elem := range elems {
|
||||
sz += len(elem)
|
||||
}
|
||||
var buf strings.Builder
|
||||
buf.Grow(sz)
|
||||
buf.WriteString(name)
|
||||
for _, elem := range slices.Backward(elems) {
|
||||
buf.WriteString(elem)
|
||||
}
|
||||
return Path(buf.String())
|
||||
}
|
||||
|
||||
// appendOpArg appends (op, index) to the object path.
|
||||
// A negative index is ignored.
|
||||
func appendOpArg(path []byte, op byte, index int) []byte {
|
||||
path = append(path, op)
|
||||
path = strconv.AppendInt(path, int64(arg), 10)
|
||||
if index >= 0 {
|
||||
path = strconv.AppendInt(path, int64(index), 10)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
@@ -442,138 +723,6 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
|
||||
// panic(fmt.Sprintf("couldn't find method %s on type %s; methods: %#v", meth, named, enc.namedMethods(named)))
|
||||
}
|
||||
|
||||
// find finds obj within type T, returning the path to it, or nil if not found.
|
||||
//
|
||||
// The seen map is used to short circuit cycles through type parameters. If
|
||||
// nil, it will be allocated as necessary.
|
||||
//
|
||||
// The seenMethods map is used internally to short circuit cycles through
|
||||
// interface methods, such as occur in the following example:
|
||||
//
|
||||
// type I interface { f() interface{I} }
|
||||
//
|
||||
// See golang/go#68046 for details.
|
||||
func find(obj types.Object, T types.Type, path []byte) []byte {
|
||||
return (&finder{obj: obj}).find(T, path)
|
||||
}
|
||||
|
||||
// finder closes over search state for a call to find.
|
||||
type finder struct {
|
||||
obj types.Object // the sought object
|
||||
seenTParamNames map[*types.TypeName]bool // for cycle breaking through type parameters
|
||||
seenMethods map[*types.Func]bool // for cycle breaking through recursive interfaces
|
||||
}
|
||||
|
||||
func (f *finder) find(T types.Type, path []byte) []byte {
|
||||
switch T := T.(type) {
|
||||
case *types.Alias:
|
||||
return f.find(types.Unalias(T), path)
|
||||
case *types.Basic, *types.Named:
|
||||
// Named types belonging to pkg were handled already,
|
||||
// so T must belong to another package. No path.
|
||||
return nil
|
||||
case *types.Pointer:
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Slice:
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Array:
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Chan:
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Map:
|
||||
if r := f.find(T.Key(), append(path, opKey)); r != nil {
|
||||
return r
|
||||
}
|
||||
return f.find(T.Elem(), append(path, opElem))
|
||||
case *types.Signature:
|
||||
if r := f.findTypeParam(T.RecvTypeParams(), path, opRecvTypeParam); r != nil {
|
||||
return r
|
||||
}
|
||||
if r := f.findTypeParam(T.TypeParams(), path, opTypeParam); r != nil {
|
||||
return r
|
||||
}
|
||||
if r := f.find(T.Params(), append(path, opParams)); r != nil {
|
||||
return r
|
||||
}
|
||||
return f.find(T.Results(), append(path, opResults))
|
||||
case *types.Struct:
|
||||
for i := 0; i < T.NumFields(); i++ {
|
||||
fld := T.Field(i)
|
||||
path2 := appendOpArg(path, opField, i)
|
||||
if fld == f.obj {
|
||||
return path2 // found field var
|
||||
}
|
||||
if r := f.find(fld.Type(), append(path2, opType)); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case *types.Tuple:
|
||||
for i := 0; i < T.Len(); i++ {
|
||||
v := T.At(i)
|
||||
path2 := appendOpArg(path, opAt, i)
|
||||
if v == f.obj {
|
||||
return path2 // found param/result var
|
||||
}
|
||||
if r := f.find(v.Type(), append(path2, opType)); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case *types.Interface:
|
||||
for i := 0; i < T.NumMethods(); i++ {
|
||||
m := T.Method(i)
|
||||
if f.seenMethods[m] {
|
||||
continue // break cycles (see TestIssue70418)
|
||||
}
|
||||
path2 := appendOpArg(path, opMethod, i)
|
||||
if m == f.obj {
|
||||
return path2 // found interface method
|
||||
}
|
||||
if f.seenMethods == nil {
|
||||
f.seenMethods = make(map[*types.Func]bool)
|
||||
}
|
||||
f.seenMethods[m] = true
|
||||
if r := f.find(m.Type(), append(path2, opType)); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case *types.TypeParam:
|
||||
name := T.Obj()
|
||||
if f.seenTParamNames[name] {
|
||||
return nil
|
||||
}
|
||||
if name == f.obj {
|
||||
return append(path, opObj)
|
||||
}
|
||||
if f.seenTParamNames == nil {
|
||||
f.seenTParamNames = make(map[*types.TypeName]bool)
|
||||
}
|
||||
f.seenTParamNames[name] = true
|
||||
if r := f.find(T.Constraint(), append(path, opConstraint)); r != nil {
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
panic(T)
|
||||
}
|
||||
|
||||
func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, op byte) []byte {
|
||||
return (&finder{obj: obj}).findTypeParam(list, path, op)
|
||||
}
|
||||
|
||||
func (f *finder) findTypeParam(list *types.TypeParamList, path []byte, op byte) []byte {
|
||||
for i := 0; i < list.Len(); i++ {
|
||||
tparam := list.At(i)
|
||||
path2 := appendOpArg(path, op, i)
|
||||
if r := f.find(tparam, path2); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Object returns the object denoted by path p within the package pkg.
|
||||
func Object(pkg *types.Package, p Path) (types.Object, error) {
|
||||
pathstr := string(p)
|
||||
@@ -708,7 +857,7 @@ func Object(pkg *types.Package, p Path) (types.Object, error) {
|
||||
}
|
||||
tparams := hasTypeParams.TypeParams()
|
||||
if n := tparams.Len(); index >= n {
|
||||
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
|
||||
return nil, fmt.Errorf("type parameter index %d out of range [0-%d)", index, n)
|
||||
}
|
||||
t = tparams.At(index)
|
||||
|
||||
@@ -719,7 +868,7 @@ func Object(pkg *types.Package, p Path) (types.Object, error) {
|
||||
}
|
||||
rtparams := sig.RecvTypeParams()
|
||||
if n := rtparams.Len(); index >= n {
|
||||
return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
|
||||
return nil, fmt.Errorf("receiver type parameter index %d out of range [0-%d)", index, n)
|
||||
}
|
||||
t = rtparams.At(index)
|
||||
|
||||
@@ -794,23 +943,3 @@ func Object(pkg *types.Package, p Path) (types.Object, error) {
|
||||
|
||||
return obj, nil // success
|
||||
}
|
||||
|
||||
// scopeObjects is a memoization of scope objects.
|
||||
// Callers must not modify the result.
|
||||
func (enc *Encoder) scopeObjects(scope *types.Scope) []types.Object {
|
||||
m := enc.scopeMemo
|
||||
if m == nil {
|
||||
m = make(map[*types.Scope][]types.Object)
|
||||
enc.scopeMemo = m
|
||||
}
|
||||
objs, ok := m[scope]
|
||||
if !ok {
|
||||
names := scope.Names() // allocates and sorts
|
||||
objs = make([]types.Object, len(names))
|
||||
for i, name := range names {
|
||||
objs[i] = scope.Lookup(name)
|
||||
}
|
||||
m[scope] = objs
|
||||
}
|
||||
return objs
|
||||
}
|
||||
|
||||
+3
@@ -823,6 +823,9 @@ func (p *iexporter) doDecl(obj types.Object) {
|
||||
w.pos(m.Pos())
|
||||
w.string(m.Name())
|
||||
sig, _ := m.Type().(*types.Signature)
|
||||
if w.p.version >= iexportVersionGenericMethods && w.bool(sig.TypeParams().Len() > 0) {
|
||||
w.tparamList(obj.Name()+"."+m.Name(), sig.TypeParams(), obj.Pkg())
|
||||
}
|
||||
|
||||
// Receiver type parameters are type arguments of the receiver type, so
|
||||
// their name must be qualified before exporting recv.
|
||||
|
||||
+14
-10
@@ -48,13 +48,14 @@ func (r *intReader) uint64() uint64 {
|
||||
|
||||
// Keep this in sync with constants in iexport.go.
|
||||
const (
|
||||
iexportVersionGo1_11 = 0
|
||||
iexportVersionPosCol = 1
|
||||
iexportVersionGo1_18 = 2
|
||||
iexportVersionGenerics = 2
|
||||
iexportVersion = iexportVersionGenerics
|
||||
iexportVersionGo1_11 = 0
|
||||
iexportVersionPosCol = 1
|
||||
iexportVersionGo1_18 = 2
|
||||
iexportVersionGenerics = 2
|
||||
iexportVersionGenericMethods = 3
|
||||
iexportVersion = iexportVersionGenericMethods
|
||||
|
||||
iexportVersionCurrent = 2
|
||||
iexportVersionCurrent = 3
|
||||
)
|
||||
|
||||
type ident struct {
|
||||
@@ -179,9 +180,9 @@ func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte
|
||||
|
||||
version = int64(r.uint64())
|
||||
switch version {
|
||||
case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
|
||||
case iexportVersionGenericMethods, iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
|
||||
default:
|
||||
if version > iexportVersionGo1_18 {
|
||||
if version > iexportVersionGenericMethods {
|
||||
errorf("unstable iexport format version %d, just rebuild compiler and std library", version)
|
||||
} else {
|
||||
errorf("unknown iexport format version %d", version)
|
||||
@@ -614,6 +615,10 @@ func (r *importReader) obj(pkg *types.Package, name string) {
|
||||
for n := r.uint64(); n > 0; n-- {
|
||||
mpos := r.pos()
|
||||
mname := r.ident()
|
||||
var tpars []*types.TypeParam
|
||||
if r.p.version >= iexportVersionGenericMethods && r.bool() {
|
||||
tpars = r.tparamList()
|
||||
}
|
||||
recv := r.param(pkg)
|
||||
|
||||
// If the receiver has any targs, set those as the
|
||||
@@ -628,8 +633,7 @@ func (r *importReader) obj(pkg *types.Package, name string) {
|
||||
rparams[i] = types.Unalias(targs.At(i)).(*types.TypeParam)
|
||||
}
|
||||
}
|
||||
msig := r.signature(pkg, recv, rparams, nil)
|
||||
|
||||
msig := r.signature(pkg, recv, rparams, tpars)
|
||||
named.AddMethod(types.NewFunc(mpos, pkg, mname, msig))
|
||||
}
|
||||
}
|
||||
|
||||
+34
-8
@@ -11,6 +11,7 @@ import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/internal/aliases"
|
||||
"golang.org/x/tools/internal/pkgbits"
|
||||
@@ -523,6 +524,12 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) {
|
||||
return objPkg, objName
|
||||
}
|
||||
|
||||
// TODO(mark): This, like the above splitVargenSuffix, is not ideal.
|
||||
// Ignore generic methods promoted to global scope.
|
||||
if strings.Contains(objName, ".") {
|
||||
return objPkg, objName
|
||||
}
|
||||
|
||||
if objPkg.Scope().Lookup(objName) == nil {
|
||||
dict := pr.objDictIdx(idx)
|
||||
|
||||
@@ -554,15 +561,11 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) {
|
||||
|
||||
case pkgbits.ObjFunc:
|
||||
pos := r.pos()
|
||||
var rtparams []*types.TypeParam
|
||||
var recv *types.Var
|
||||
if r.Version().Has(pkgbits.GenericMethods) && r.Bool() {
|
||||
r.selector()
|
||||
rtparams = r.typeParamNames(true)
|
||||
recv = r.param()
|
||||
if r.Version().Has(pkgbits.GenericMethods) {
|
||||
assert(!r.Bool()) // generic methods are read in their defining type
|
||||
}
|
||||
tparams := r.typeParamNames(false)
|
||||
sig := r.signature(recv, rtparams, tparams)
|
||||
sig := r.signature(nil, nil, tparams)
|
||||
declare(types.NewFunc(pos, objPkg, objName, sig))
|
||||
|
||||
case pkgbits.ObjType:
|
||||
@@ -630,6 +633,29 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) {
|
||||
named.AddMethod(r.method())
|
||||
}
|
||||
|
||||
if r.Version().Has(pkgbits.GenericMethods) {
|
||||
for range r.Len() {
|
||||
// Careful: objIdx is used to read in package-scoped declarations, which
|
||||
// methods are not. Instead, decode it here. This makes it easier to
|
||||
// associate it with the type and avoids the main objIdx loop.
|
||||
idx := r.Reloc(pkgbits.RelocObj)
|
||||
|
||||
r := pr.tempReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1)
|
||||
r.dict = pr.objDictIdx(idx)
|
||||
|
||||
pos := r.pos()
|
||||
assert(r.Bool()) // generic method
|
||||
pkg, name := r.selector()
|
||||
rtparams := r.typeParamNames(true)
|
||||
recv := r.param()
|
||||
tparams := r.typeParamNames(false)
|
||||
sig := r.signature(recv, rtparams, tparams)
|
||||
|
||||
pr.retireReader(r)
|
||||
named.AddMethod(types.NewFunc(pos, pkg, name, sig))
|
||||
}
|
||||
}
|
||||
|
||||
case pkgbits.ObjVar:
|
||||
pos := r.pos()
|
||||
typ := r.typ()
|
||||
@@ -653,7 +679,7 @@ func (pr *pkgReader) objDictIdx(idx pkgbits.Index) *readerDict {
|
||||
}
|
||||
|
||||
nreceivers := 0
|
||||
if r.Version().Has(pkgbits.GenericMethods) && r.Bool() {
|
||||
if r.Version().Has(pkgbits.GenericMethods) {
|
||||
nreceivers = r.Len()
|
||||
}
|
||||
nexplicits := r.Len()
|
||||
|
||||
+3
-2
@@ -8,6 +8,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -41,9 +42,9 @@ func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) {
|
||||
}
|
||||
// Split up "[go1.1 go1.15]" and return highest go1.X value.
|
||||
tags := strings.Fields(stdout[1 : len(stdout)-2])
|
||||
for i := len(tags) - 1; i >= 0; i-- {
|
||||
for _, tag := range slices.Backward(tags) {
|
||||
var version int
|
||||
if _, err := fmt.Sscanf(tags[i], "go1.%d", &version); err != nil {
|
||||
if _, err := fmt.Sscanf(tag, "go1.%d", &version); err != nil {
|
||||
continue
|
||||
}
|
||||
return version, nil
|
||||
|
||||
+2
-5
@@ -273,7 +273,6 @@ func (p *pass) loadPackageNames(ctx context.Context, imports []*ImportInfo) erro
|
||||
}
|
||||
unknown = append(unknown, imp.ImportPath)
|
||||
}
|
||||
|
||||
names, err := p.source.LoadPackageNames(ctx, p.srcDir, unknown)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -1650,9 +1649,7 @@ func (s *symbolSearcher) search(ctx context.Context, candidates []pkgDistance, p
|
||||
}()
|
||||
|
||||
// Start the search.
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
wg.Go(func() {
|
||||
for i, c := range candidates {
|
||||
select {
|
||||
case loadExportsSem <- struct{}{}:
|
||||
@@ -1681,7 +1678,7 @@ func (s *symbolSearcher) search(ctx context.Context, candidates []pkgDistance, p
|
||||
rescv[i] <- pkg // may be nil
|
||||
}()
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
// Await the first (best) result.
|
||||
for _, resc := range rescv {
|
||||
|
||||
+4
@@ -74,6 +74,10 @@ func Process(filename string, src []byte, opt *Options) (formatted []byte, err e
|
||||
// Note that filename's directory influences which imports can be chosen,
|
||||
// so it is important that filename be accurate.
|
||||
func FixImports(ctx context.Context, filename string, src []byte, goroot string, logf func(string, ...any), source Source) (fixes []*ImportFix, err error) {
|
||||
if source == nil {
|
||||
// In case someone adds a defective call from a new place
|
||||
panic("source is nil")
|
||||
}
|
||||
ctx, done := event.Start(ctx, "imports.FixImports")
|
||||
defer done()
|
||||
|
||||
|
||||
+3
-3
@@ -679,11 +679,11 @@ func modRelevance(mod *gocommand.ModuleJSON) float64 {
|
||||
|
||||
_, versionString, ok := module.SplitPathVersion(mod.Path)
|
||||
if ok {
|
||||
index := strings.Index(versionString, "v")
|
||||
if index == -1 {
|
||||
_, after, ok := strings.Cut(versionString, "v")
|
||||
if !ok {
|
||||
return relevance
|
||||
}
|
||||
if versionNumber, err := strconv.ParseFloat(versionString[index+1:], 64); err == nil {
|
||||
if versionNumber, err := strconv.ParseFloat(after, 64); err == nil {
|
||||
relevance += versionNumber / 1000
|
||||
}
|
||||
}
|
||||
|
||||
+338
-316
@@ -12,366 +12,386 @@ type pkginfo struct {
|
||||
}
|
||||
|
||||
var deps = [...]pkginfo{
|
||||
{"archive/tar", "\x03q\x03F=\x01\n\x01$\x01\x01\x02\x05\b\x02\x01\x02\x02\r"},
|
||||
{"archive/zip", "\x02\x04g\a\x03\x13\x021=\x01+\x05\x01\x0f\x03\x02\x0f\x04"},
|
||||
{"bufio", "\x03q\x86\x01D\x15"},
|
||||
{"bytes", "t+[\x03\fH\x02\x02"},
|
||||
{"archive/tar", "\x03{\x03F>\x01\n\x01&\x01\x01\x02\x05\b\x02\x01\x02\x02\r"},
|
||||
{"archive/zip", "\x02\x04j\x0e\x03\x12\x022>\x01-\x05\x01\x0f\x03\x02\x0f\x04"},
|
||||
{"bufio", "\x03{\x87\x01F\x15"},
|
||||
{"bytes", "~*]\x03\fJ\x02\x02"},
|
||||
{"cmp", ""},
|
||||
{"compress/bzip2", "\x02\x02\xf6\x01A"},
|
||||
{"compress/flate", "\x02r\x03\x83\x01\f\x033\x01\x03"},
|
||||
{"compress/gzip", "\x02\x04g\a\x03\x15nU"},
|
||||
{"compress/lzw", "\x02r\x03\x83\x01"},
|
||||
{"compress/zlib", "\x02\x04g\a\x03\x13\x01o"},
|
||||
{"container/heap", "\xbc\x02"},
|
||||
{"compress/bzip2", "\x02\x02\x81\x02C"},
|
||||
{"compress/flate", "\x02|\x03\x84\x01\f\x034\x02\x03"},
|
||||
{"compress/gzip", "\x02\x04j\x0e\x03\x14pW"},
|
||||
{"compress/lzw", "\x02|\x03\x84\x01"},
|
||||
{"compress/zlib", "\x02\x04j\x0e\x03\x12\x01q"},
|
||||
{"container/heap", "\xc9\x02"},
|
||||
{"container/list", ""},
|
||||
{"container/ring", ""},
|
||||
{"context", "t\\p\x01\x0e"},
|
||||
{"crypto", "\x8a\x01pC"},
|
||||
{"crypto/aes", "\x10\v\t\x99\x02"},
|
||||
{"crypto/cipher", "\x03!\x01\x01 \x12\x1c,Z"},
|
||||
{"crypto/des", "\x10\x16 .,\x9d\x01\x03"},
|
||||
{"crypto/dsa", "F\x03+\x86\x01\r"},
|
||||
{"crypto/ecdh", "\x03\v\r\x10\x04\x17\x03\x0f\x1c\x86\x01"},
|
||||
{"crypto/ecdsa", "\x0e\x05\x03\x05\x01\x10\b\v\x06\x01\x03\x0e\x01\x1c\x86\x01\r\x05L\x01"},
|
||||
{"crypto/ed25519", "\x0e\x1f\x12\a\x03\b\a\x1cI=C"},
|
||||
{"crypto/elliptic", "4@\x86\x01\r9"},
|
||||
{"crypto/fips140", "#\x05\x95\x01\x98\x01"},
|
||||
{"crypto/hkdf", "0\x15\x01.\x16"},
|
||||
{"crypto/hmac", "\x1b\x16\x14\x01\x122"},
|
||||
{"crypto/hpke", "\x03\v\x02\x03\x04\x01\f\x01\x05\x1f\x05\a\x01\x01\x1d\x03\x13\x16\x9b\x01\x1c"},
|
||||
{"crypto/internal/boring", "\x0e\x02\x0el"},
|
||||
{"crypto/internal/boring/bbig", "\x1b\xec\x01N"},
|
||||
{"crypto/internal/boring/bcache", "\xc1\x02\x14"},
|
||||
{"context", "~]r\x01\x0e"},
|
||||
{"crypto", "\x93\x01rE"},
|
||||
{"crypto/aes", "\x10\v\n\xa5\x02"},
|
||||
{"crypto/cipher", "\x03\"\x01\x01 \x13$+\\"},
|
||||
{"crypto/des", "\x10\x17 7+\xa1\x01\x03"},
|
||||
{"crypto/dsa", "G\x034\x87\x01\r"},
|
||||
{"crypto/ecdh", "\x03\v\r\x11\x04\x17\x03\x10$\x87\x01"},
|
||||
{"crypto/ecdsa", "\x0e\x05\x03\x05\x01\x11\b\v\x06\x01\x03\x0f\x01$\x87\x01\r\x05O\x01"},
|
||||
{"crypto/ed25519", "\x0e \x12\a\x03\t\a$I>E"},
|
||||
{"crypto/elliptic", "5I\x87\x01\r;"},
|
||||
{"crypto/fips140", "$\x05\x9e\x01\x9b\x01"},
|
||||
{"crypto/hkdf", "1\x15\x017\x15"},
|
||||
{"crypto/hmac", "\x1b\x17\x14\x01\x139"},
|
||||
{"crypto/hpke", "\x03\v\x02\x03\x04\x01\r\x01\x05\x1f\x06\a\x01\x01%\x03\x12\x16\x9f\x01\x1d"},
|
||||
{"crypto/internal/boring", "\x0e\x02\x0eu"},
|
||||
{"crypto/internal/boring/bbig", "\x1b\xf7\x01P"},
|
||||
{"crypto/internal/boring/bcache", "\xce\x02\x14"},
|
||||
{"crypto/internal/boring/sig", ""},
|
||||
{"crypto/internal/constanttime", ""},
|
||||
{"crypto/internal/cryptotest", "\x03\r\v\b%\x10\x19\x06\x13\x12 \x04\x06\t\x19\x01\x11\x11\x1b\x01\a\x05\b\x03\x05\f"},
|
||||
{"crypto/internal/entropy", "K"},
|
||||
{"crypto/internal/entropy/v1.0.0", "D0\x95\x018\x14"},
|
||||
{"crypto/internal/fips140", "C1\xbf\x01\v\x17"},
|
||||
{"crypto/internal/fips140/aes", "\x03 \x03\x02\x14\x05\x01\x01\x05,\x95\x014"},
|
||||
{"crypto/internal/fips140/aes/gcm", "#\x01\x02\x02\x02\x12\x05\x01\x06,\x92\x01"},
|
||||
{"crypto/internal/fips140/alias", "\xd5\x02"},
|
||||
{"crypto/internal/fips140/bigmod", "(\x19\x01\x06,\x95\x01"},
|
||||
{"crypto/internal/fips140/check", "#\x0e\a\t\x02\xb7\x01["},
|
||||
{"crypto/internal/fips140/check/checktest", "(\x8b\x02\""},
|
||||
{"crypto/internal/fips140/drbg", "\x03\x1f\x01\x01\x04\x14\x05\n)\x86\x01\x0f7\x01"},
|
||||
{"crypto/internal/fips140/ecdh", "\x03 \x05\x02\n\r3\x86\x01\x0f7"},
|
||||
{"crypto/internal/fips140/ecdsa", "\x03 \x04\x01\x02\a\x03\x06:\x16pF"},
|
||||
{"crypto/internal/fips140/ed25519", "\x03 \x05\x02\x04\f:\xc9\x01\x03"},
|
||||
{"crypto/internal/fips140/edwards25519", "\x1f\t\a\x123\x95\x017"},
|
||||
{"crypto/internal/fips140/edwards25519/field", "(\x14\x053\x95\x01"},
|
||||
{"crypto/internal/fips140/hkdf", "\x03 \x05\t\a<\x16"},
|
||||
{"crypto/internal/fips140/hmac", "\x03 \x15\x01\x01:\x16"},
|
||||
{"crypto/internal/fips140/mldsa", "\x03\x1c\x04\x05\x02\x0e\x01\x03\x053\x95\x017"},
|
||||
{"crypto/internal/fips140/mlkem", "\x03 \x05\x02\x0f\x03\x053\xcc\x01"},
|
||||
{"crypto/internal/fips140/nistec", "\x1f\t\r\f3\x95\x01*\r\x15"},
|
||||
{"crypto/internal/fips140/nistec/fiat", "(\x148\x95\x01"},
|
||||
{"crypto/internal/fips140/pbkdf2", "\x03 \x05\t\a<\x16"},
|
||||
{"crypto/internal/fips140/rsa", "\x03\x1c\x04\x04\x01\x02\x0e\x01\x01\x028\x16pF"},
|
||||
{"crypto/internal/fips140/sha256", "\x03 \x1e\x01\x06,\x16\x7f"},
|
||||
{"crypto/internal/fips140/sha3", "\x03 \x19\x05\x012\x95\x01L"},
|
||||
{"crypto/internal/fips140/sha512", "\x03 \x1e\x01\x06,\x16\x7f"},
|
||||
{"crypto/internal/fips140/ssh", "(b"},
|
||||
{"crypto/internal/fips140/subtle", "\x1f\a\x1b\xc8\x01"},
|
||||
{"crypto/internal/fips140/tls12", "\x03 \x05\t\a\x02:\x16"},
|
||||
{"crypto/internal/fips140/tls13", "\x03 \x05\b\b\t3\x16"},
|
||||
{"crypto/internal/fips140cache", "\xb3\x02\r'"},
|
||||
{"crypto/internal/cryptotest", "\x03\r\v\t%\x11\x1a\r\x12\x12!\x04\x06\n\x19\x01\x11\x11\x1d\x01\a\x03\x02\b\x02\x01\x05\f"},
|
||||
{"crypto/internal/cryptotest/wycheproof", "\x0e\x12S\x01\f\x01r@\x05\x03\x15\x10"},
|
||||
{"crypto/internal/entropy", "L"},
|
||||
{"crypto/internal/entropy/v1.0.0", "E9\x96\x01:\x14"},
|
||||
{"crypto/internal/fips140", "D:\xc2\x01\v\x17"},
|
||||
{"crypto/internal/fips140/aes", "\x03!\x03\x02\x14\x05\x01\x01\x055\x96\x016"},
|
||||
{"crypto/internal/fips140/aes/gcm", "$\x01\x02\x02\x02\x12\x05\x01\x065\x93\x01"},
|
||||
{"crypto/internal/fips140/alias", "\xe2\x02"},
|
||||
{"crypto/internal/fips140/bigmod", ")\x19\x01\x065\x96\x01"},
|
||||
{"crypto/internal/fips140/check", "$\x0e\a\t\x02\xc1\x01]"},
|
||||
{"crypto/internal/fips140/check/checktest", ")\x97\x02\""},
|
||||
{"crypto/internal/fips140/drbg", "\x03 \x01\x01\x04\x14\x05\n2\x87\x01\x0f9\x01"},
|
||||
{"crypto/internal/fips140/ecdh", "\x03!\x05\x02\n\r<\x87\x01\x0f9"},
|
||||
{"crypto/internal/fips140/ecdsa", "\x03!\x04\x01\x02\a\x03\x06C\x15r\x0f9"},
|
||||
{"crypto/internal/fips140/ed25519", "\x03!\x05\x02\x04\fC\xcc\x01\x03"},
|
||||
{"crypto/internal/fips140/edwards25519", "\x1f\n\a\x12<\x96\x019"},
|
||||
{"crypto/internal/fips140/edwards25519/field", ")\x14\x05<\x96\x01"},
|
||||
{"crypto/internal/fips140/hkdf", "\x03!\x05\t\aE\x15"},
|
||||
{"crypto/internal/fips140/hmac", "\x03!\x15\x01\x01C\x15"},
|
||||
{"crypto/internal/fips140/mldsa", "\x03\x1c\x05\x05\x02\x0e\x01\x03\x05<\x96\x019"},
|
||||
{"crypto/internal/fips140/mlkem", "\x03!\x05\x02\x0f\x03\x05<\xcf\x01"},
|
||||
{"crypto/internal/fips140/nistec", "\x1f\n\r\f<\x96\x01,\r\x15"},
|
||||
{"crypto/internal/fips140/nistec/fiat", ")\x14A\x96\x01"},
|
||||
{"crypto/internal/fips140/pbkdf2", "\x03!\x05\t\aE\x15"},
|
||||
{"crypto/internal/fips140/rsa", "\x03\x1c\x05\x04\x01\x02\x0e\x01\x01\x02A\x15rH"},
|
||||
{"crypto/internal/fips140/sha256", "\x03!\x1e\x01\x065\x15\x81\x01"},
|
||||
{"crypto/internal/fips140/sha3", "\x03!\x19\x05\x01;\x96\x01N"},
|
||||
{"crypto/internal/fips140/sha512", "\x03!\x1e\x01\x065\x15\x81\x01"},
|
||||
{"crypto/internal/fips140/ssh", ")j"},
|
||||
{"crypto/internal/fips140/subtle", "\x1f\b\x1b\xd2\x01"},
|
||||
{"crypto/internal/fips140/tls12", "\x03!\x05\t\a\x02C\x15"},
|
||||
{"crypto/internal/fips140/tls13", "\x03!\x05\b\b\t<\x15"},
|
||||
{"crypto/internal/fips140cache", "\xc0\x02\r."},
|
||||
{"crypto/internal/fips140deps", ""},
|
||||
{"crypto/internal/fips140deps/byteorder", "\xa0\x01"},
|
||||
{"crypto/internal/fips140deps/cpu", "\xb5\x01\a"},
|
||||
{"crypto/internal/fips140deps/godebug", "\xbd\x01"},
|
||||
{"crypto/internal/fips140deps/time", "\xcf\x02"},
|
||||
{"crypto/internal/fips140hash", "9\x1d4\xcb\x01"},
|
||||
{"crypto/internal/fips140only", "\x17\x13\x0e\x01\x01Pp"},
|
||||
{"crypto/internal/fips140deps/byteorder", "\xa9\x01"},
|
||||
{"crypto/internal/fips140deps/cpu", "\xbe\x01\b"},
|
||||
{"crypto/internal/fips140deps/godebug", "\xc7\x01"},
|
||||
{"crypto/internal/fips140deps/time", "\xe2\x02"},
|
||||
{"crypto/internal/fips140hash", ":\x1e;\xcf\x01"},
|
||||
{"crypto/internal/fips140only", "\x17\x14\x0e\x01\x01Xr"},
|
||||
{"crypto/internal/fips140test", ""},
|
||||
{"crypto/internal/impl", "\xbe\x02"},
|
||||
{"crypto/internal/rand", "\x1b\x0f s=["},
|
||||
{"crypto/internal/randutil", "\xfa\x01\x12"},
|
||||
{"crypto/internal/sysrand", "tq! \r\r\x01\x01\r\x06"},
|
||||
{"crypto/internal/sysrand/internal/seccomp", "t"},
|
||||
{"crypto/md5", "\x0e8.\x16\x16i"},
|
||||
{"crypto/mlkem", "\x0e%"},
|
||||
{"crypto/mlkem/mlkemtest", "3\x13\b&"},
|
||||
{"crypto/pbkdf2", "6\x0f\x01.\x16"},
|
||||
{"crypto/rand", "\x1b\x0f\x1c\x03+\x86\x01\rN"},
|
||||
{"crypto/rc4", "& .\xc9\x01"},
|
||||
{"crypto/rsa", "\x0e\r\x01\v\x10\x0e\x01\x03\b\a\x1c\x03\x133=\f\x01"},
|
||||
{"crypto/sha1", "\x0e\r+\x02,\x16\x16\x15T"},
|
||||
{"crypto/sha256", "\x0e\r\x1dR"},
|
||||
{"crypto/sha3", "\x0e+Q\xcb\x01"},
|
||||
{"crypto/sha512", "\x0e\r\x1fP"},
|
||||
{"crypto/subtle", "\x1f\x1d\x9f\x01z"},
|
||||
{"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x02\x01\x01\x01\t\x01\x18\x01\x0f\x01\x03\x01\x01\x01\x01\x02\x01\x02\x01\x17\x02\x03\x13\x16\x15\b=\x16\x16\r\b\x01\x01\x01\x02\x01\x0e\x06\x02\x01\x0f"},
|
||||
{"crypto/tls/internal/fips140tls", "\x17\xaa\x02"},
|
||||
{"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x017\x06\x01\x01\x02\x05\x0e\x06\x02\x02\x03F\x03:\x01\x02\b\x01\x01\x02\a\x10\x05\x01\x06\a\b\x02\x01\x02\x0f\x02\x01\x01\x02\x03\x01"},
|
||||
{"crypto/x509/pkix", "j\x06\a\x90\x01H"},
|
||||
{"database/sql", "\x03\nQ\x16\x03\x83\x01\v\a\"\x05\b\x02\x03\x01\x0e\x02\x02\x02"},
|
||||
{"database/sql/driver", "\rg\x03\xb7\x01\x0f\x12"},
|
||||
{"debug/buildinfo", "\x03^\x02\x01\x01\b\a\x03g\x1a\x02\x01+\x0f "},
|
||||
{"debug/dwarf", "\x03j\a\x03\x83\x011\x11\x01\x01"},
|
||||
{"debug/elf", "\x03\x06W\r\a\x03g\x1b\x01\f \x17\x01\x17"},
|
||||
{"debug/gosym", "\x03j\n$\xa1\x01\x01\x01\x02"},
|
||||
{"debug/macho", "\x03\x06W\r\ng\x1c,\x17\x01"},
|
||||
{"debug/pe", "\x03\x06W\r\a\x03g\x1c,\x17\x01\x17"},
|
||||
{"debug/plan9obj", "m\a\x03g\x1c,"},
|
||||
{"embed", "t+B\x19\x01T"},
|
||||
{"crypto/internal/impl", "\xcb\x02"},
|
||||
{"crypto/internal/rand", "\x1b\x10 |>]"},
|
||||
{"crypto/internal/randutil", "\x85\x02\x12"},
|
||||
{"crypto/internal/sysrand", "~r!\"\r\r\x01\x01\r\x06"},
|
||||
{"crypto/internal/sysrand/internal/seccomp", "~"},
|
||||
{"crypto/md5", "\x0e97\x15\x16k"},
|
||||
{"crypto/mldsa", "\x0e%K\x87\x01"},
|
||||
{"crypto/mlkem", "\x0e&"},
|
||||
{"crypto/mlkem/mlkemtest", "4\x13\t."},
|
||||
{"crypto/pbkdf2", "7\x0f\x017\x15"},
|
||||
{"crypto/rand", "\x1b\x10\x1c\x034\x87\x01\rP"},
|
||||
{"crypto/rc4", "' 7\xcc\x01"},
|
||||
{"crypto/rsa", "\x0e\r\x01\f\x10\x0e\x01\x03\t\a$\x03\x124>\f\x01"},
|
||||
{"crypto/sha1", "\x0e\r,\x025\x15\x16\x15V"},
|
||||
{"crypto/sha256", "\x0e\r\x1eZ"},
|
||||
{"crypto/sha3", "\x0e,Y\xcf\x01"},
|
||||
{"crypto/sha512", "\x0e\r X"},
|
||||
{"crypto/subtle", "\x1f\x1e\xa9\x01|"},
|
||||
{"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\n\x01\x18\x01\x0f\x01\x01\x03\x01\x01\x01\x01\x02\x01\x02\x01\x1f\x02\x03\x12\x16\x15\t>\x16\x18\r\b\x01\x01\x01\x02\x01\x0e\x06\x03\x01\x15"},
|
||||
{"crypto/tls/internal/fips140tls", "\x17\xb7\x02"},
|
||||
{"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x01\x017\x01\x06\x01\x01\x02\x05\x0f\x06\t\x02\x03F\x03;\x01\x02\b\x01\x01\x02\a\x12\x05\x01\x06\a\b\x02\x01\x02\x0f\x02\x01\x01\x02\x04\x01"},
|
||||
{"crypto/x509/pkix", "m\x06\x0e\x91\x019\x11"},
|
||||
{"database/sql", "\x03\nS\x01\x1d\x03\x84\x01\v\a$\x05\b\x02\x03\x01\x0e\x02\x02\x02\x01"},
|
||||
{"database/sql/driver", "\rT\x1d\x03\xba\x01\x0f\x12\a"},
|
||||
{"database/sql/internal", ""},
|
||||
{"debug/buildinfo", "\x03a\x02\x01\x01\b\x0e\x03h\x1a\x02\x01-\x0f "},
|
||||
{"debug/dwarf", "\x03m\x0e\x03\x84\x013\x11\x01\x01"},
|
||||
{"debug/elf", "\x03\x06Z\r\x0e\x03h\x1b\x01\f\"\x17\x01\x17"},
|
||||
{"debug/gosym", "\x03m\x11#\xa5\x01\x01\x01\x02"},
|
||||
{"debug/macho", "\x03\x06Z\r\x11h\x1c.\x17\x01"},
|
||||
{"debug/pe", "\x03\x06Z\r\x0e\x03h\x1c.\x17\x01\x17"},
|
||||
{"debug/plan9obj", "p\x0e\x03h\x1c."},
|
||||
{"embed", "~*D\x19\x01V"},
|
||||
{"embed/internal/embedtest", ""},
|
||||
{"encoding", ""},
|
||||
{"encoding/ascii85", "\xfa\x01C"},
|
||||
{"encoding/asn1", "\x03q\x03g(\x01'\r\x02\x01\x11\x03\x01"},
|
||||
{"encoding/base32", "\xfa\x01A\x02"},
|
||||
{"encoding/base64", "\xa0\x01ZA\x02"},
|
||||
{"encoding/binary", "t\x86\x01\f(\r\x05"},
|
||||
{"encoding/csv", "\x02\x01q\x03\x83\x01D\x13\x02"},
|
||||
{"encoding/gob", "\x02f\x05\a\x03g\x1c\v\x01\x03\x1d\b\x12\x01\x10\x02"},
|
||||
{"encoding/hex", "t\x03\x83\x01A\x03"},
|
||||
{"encoding/json", "\x03\x01d\x04\b\x03\x83\x01\f(\r\x02\x01\x02\x11\x01\x01\x02"},
|
||||
{"encoding/pem", "\x03i\b\x86\x01A\x03"},
|
||||
{"encoding/xml", "\x02\x01e\f\x03\x83\x014\x05\n\x01\x02\x11\x02"},
|
||||
{"errors", "\xd0\x01\x85\x01"},
|
||||
{"expvar", "qLA\b\v\x15\r\b\x02\x03\x01\x12"},
|
||||
{"flag", "h\f\x03\x83\x01,\b\x05\b\x02\x01\x11"},
|
||||
{"fmt", "tF'\x19\f \b\r\x02\x03\x13"},
|
||||
{"go/ast", "\x03\x01s\x0f\x01s\x03)\b\r\x02\x01\x13\x02"},
|
||||
{"go/build", "\x02\x01q\x03\x01\x02\x02\b\x02\x01\x17\x1f\x04\x02\b\x1c\x13\x01+\x01\x04\x01\a\b\x02\x01\x13\x02\x02"},
|
||||
{"go/build/constraint", "t\xc9\x01\x01\x13\x02"},
|
||||
{"go/constant", "w\x10\x7f\x01\x024\x01\x02\x13"},
|
||||
{"go/doc", "\x04s\x01\x05\n=61\x10\x02\x01\x13\x02"},
|
||||
{"go/doc/comment", "\x03t\xc4\x01\x01\x01\x01\x13\x02"},
|
||||
{"go/format", "\x03t\x01\f\x01\x02sD"},
|
||||
{"go/importer", "y\a\x01\x02\x04\x01r9"},
|
||||
{"go/internal/gccgoimporter", "\x02\x01^\x13\x03\x04\f\x01p\x02,\x01\x05\x11\x01\r\b"},
|
||||
{"go/internal/gcimporter", "\x02u\x10\x010\x05\r0,\x15\x03\x02"},
|
||||
{"go/internal/scannerhooks", "\x87\x01"},
|
||||
{"go/internal/srcimporter", "w\x01\x01\v\x03\x01r,\x01\x05\x12\x02\x15"},
|
||||
{"go/parser", "\x03q\x03\x01\x02\b\x04\x01s\x01+\x06\x12"},
|
||||
{"go/printer", "w\x01\x02\x03\ns\f \x15\x02\x01\x02\f\x05\x02"},
|
||||
{"go/scanner", "\x03t\v\x05s2\x10\x01\x14\x02"},
|
||||
{"go/token", "\x04s\x86\x01>\x02\x03\x01\x10\x02"},
|
||||
{"go/types", "\x03\x01\x06j\x03\x01\x03\t\x03\x024\x063\x04\x03\t \x06\a\b\x01\x01\x01\x02\x01\x10\x02\x02"},
|
||||
{"go/version", "\xc2\x01|"},
|
||||
{"hash", "\xfa\x01"},
|
||||
{"hash/adler32", "t\x16\x16"},
|
||||
{"hash/crc32", "t\x16\x16\x15\x8b\x01\x01\x14"},
|
||||
{"hash/crc64", "t\x16\x16\xa0\x01"},
|
||||
{"hash/fnv", "t\x16\x16i"},
|
||||
{"hash/maphash", "\x8a\x01\x11<~"},
|
||||
{"html", "\xbe\x02\x02\x13"},
|
||||
{"html/template", "\x03n\x06\x19-=\x01\n!\x05\x01\x02\x03\f\x01\x02\r\x01\x03\x02"},
|
||||
{"image", "\x02r\x1fg\x0f4\x03\x01"},
|
||||
{"encoding/ascii85", "\x85\x02E"},
|
||||
{"encoding/asn1", "\x03{\x03h(\x01)\r\x02\x01\x11\x03\x01"},
|
||||
{"encoding/base32", "\x85\x02C\x02"},
|
||||
{"encoding/base64", "\xa9\x01\\C\x02"},
|
||||
{"encoding/binary", "~\x87\x01\f*\r\x05"},
|
||||
{"encoding/csv", "\x02\x01{\x03\x84\x01F\x13\x02"},
|
||||
{"encoding/gob", "\x02i\x05\x0e\x03h\x1c\v\x01\x03\x1f\b\x12\x01\x10\x02"},
|
||||
{"encoding/hex", "~\x03\x84\x01C\x03"},
|
||||
{"encoding/json", "\x03\x01g\n\x01\x01\x02\x01\x01\x03\x03\x84\x016\x0f\x01"},
|
||||
{"encoding/json/internal", "~"},
|
||||
{"encoding/json/internal/jsonflags", "u"},
|
||||
{"encoding/json/internal/jsonopts", "u\x01"},
|
||||
{"encoding/json/internal/jsontest", "\x03f\x15\x03\x83\x01\x01\x012\b\b\x03\x02\x0f"},
|
||||
{"encoding/json/internal/jsonwire", "\x04r\b\x87\x01\f7\x02\x01\x13\x01\x01"},
|
||||
{"encoding/json/jsontext", "\x03r\x01\x01\x02\x05\x87\x01\x03\t\x034\x02\x01\x02\x13"},
|
||||
{"encoding/json/v2", "\x03\x01g\x03\x01\x01\x03\x02\x01\x01\x02\x01\x04\x03\x84\x01\f\x03'\r\x02\x01\x02\x0f\x02\x02"},
|
||||
{"encoding/pem", "\x03l\x0f\x87\x01C\x03"},
|
||||
{"encoding/xml", "\x02\x01h\x13\x03\x84\x016\x05\n\x01\x02\x11\x02"},
|
||||
{"errors", "\xdb\x01\x87\x01"},
|
||||
{"expvar", "tSB\b\v\x17\r\b\x02\x03\x01\x12"},
|
||||
{"flag", "k\x13\x03\x84\x01.\b\x05\b\x02\x01\x11"},
|
||||
{"fmt", "~E)\x19\f\"\b\r\x02\x03\x13"},
|
||||
{"go/ast", "\x03\x01}\x0e\x01u\x03+\b\r\x02\x01\x13\x02"},
|
||||
{"go/build", "\x02\x01{\x03\x01\x02\x02\a\x02\x01\x17 \x04\x02\t\x1c\x13\x01-\x01\x04\x01\a\b\x02\x01\x13\x02\x02"},
|
||||
{"go/build/constraint", "~\xcc\x01\x01\x13\x02"},
|
||||
{"go/constant", "\x81\x01\x0f\x81\x01\x01\x026\x01\x02\x13"},
|
||||
{"go/doc", "\x04}\x01\x05\t>73\x10\x02\x01\x13\x02"},
|
||||
{"go/doc/comment", "\x03~\xc7\x01\x01\x01\x01\x13\x02"},
|
||||
{"go/format", "\x03~\x01\v\x01\x02uF"},
|
||||
{"go/importer", "\x83\x01\a\x01\x01\x04\x01t;"},
|
||||
{"go/internal/gccgoimporter", "\x02\x01a\x1a\x03\x04\v\x01r\x02.\x01\x05\x11\x01\r\b"},
|
||||
{"go/internal/gcimporter", "\x02\x7f\x0f\x010\x140.\x15\x03\x02"},
|
||||
{"go/internal/srcimporter", "\x81\x01\x01\x01\n\x03\x01t.\x01\x05\x12\x02\x15"},
|
||||
{"go/parser", "\x03{\x03\x01\x02\v\x01u\x01-\x06\x12"},
|
||||
{"go/printer", "\x81\x01\x01\x02\x03\tu\f\"\x15\x02\x01\x02\f\x05\x02"},
|
||||
{"go/scanner", "\x03~\x0fu4\x10\x01\x14\x02"},
|
||||
{"go/token", "\x04}\x87\x01@\x02\x03\x01\x10\x02"},
|
||||
{"go/types", "\x03\x01\x06t\x03\x01\x03\b\x03\x02\x0654\x04\x03\t\"\x06\a\b\x01\x01\x01\x02\x01\x10\x02\x02"},
|
||||
{"go/version", "\xcc\x01\x7f"},
|
||||
{"hash", "\x85\x02"},
|
||||
{"hash/adler32", "~\x15\x16"},
|
||||
{"hash/crc32", "~\x15\x16\x15\x8f\x01\x01\x14"},
|
||||
{"hash/crc64", "~\x15\x16\xa4\x01"},
|
||||
{"hash/fnv", "~\x15\x16k"},
|
||||
{"hash/maphash", "\x93\x01\x11>\x80\x01"},
|
||||
{"html", "\xcb\x02\x02\x13"},
|
||||
{"html/template", "\x03q\r\x18.>\x01\n#\x05\x01\x02\x03\n\x02\x01\x02\r\x01\x03\x02"},
|
||||
{"image", "\x02|\x1ei\x0f6\x03\x01"},
|
||||
{"image/color", ""},
|
||||
{"image/color/palette", "\x93\x01"},
|
||||
{"image/draw", "\x92\x01\x01\x04"},
|
||||
{"image/gif", "\x02\x01\x05l\x03\x1b\x01\x01\x01\vZ\x0f"},
|
||||
{"image/internal/imageutil", "\x92\x01"},
|
||||
{"image/jpeg", "\x02r\x1e\x01\x04c"},
|
||||
{"image/png", "\x02\ad\n\x13\x02\x06\x01gC"},
|
||||
{"index/suffixarray", "\x03j\a\x86\x01\f+\n\x01"},
|
||||
{"internal/abi", "\xbc\x01\x99\x01"},
|
||||
{"internal/asan", "\xd5\x02"},
|
||||
{"internal/bisect", "\xb3\x02\r\x01"},
|
||||
{"internal/buildcfg", "wHg\x06\x02\x05\n\x01"},
|
||||
{"internal/bytealg", "\xb5\x01\xa0\x01"},
|
||||
{"image/color/palette", "\x9c\x01"},
|
||||
{"image/draw", "\x9b\x01\x01\x04"},
|
||||
{"image/gif", "\x02\x01\x05v\x03\x1a\x01\x01\x01\v\\\x0f"},
|
||||
{"image/internal/imageutil", "\x9b\x01"},
|
||||
{"image/jpeg", "\x02|\x1d\x01\x04e"},
|
||||
{"image/png", "\x02\ag\x11\x12\x02\x06\x01iE"},
|
||||
{"index/suffixarray", "\x03m\x0e\x87\x01\f-\n\x01"},
|
||||
{"internal/abi", "\xc6\x01\x9c\x01"},
|
||||
{"internal/asan", "\xe2\x02"},
|
||||
{"internal/bisect", "\xc0\x02\r\x01"},
|
||||
{"internal/buildcfg", "\x81\x01Hj\x06\x02\x05\n\x01"},
|
||||
{"internal/bytealg", "\xbe\x01\xa4\x01"},
|
||||
{"internal/byteorder", ""},
|
||||
{"internal/cfg", ""},
|
||||
{"internal/cgrouptest", "w[T\x06\x0f\x02\x01\x04\x01"},
|
||||
{"internal/chacha8rand", "\xa0\x01\x15\a\x99\x01"},
|
||||
{"internal/cgrouptest", "\x81\x01\\V\x06\x0f\x02\x01\x04\x01"},
|
||||
{"internal/chacha8rand", "\xa9\x01\x15\b\x9c\x01"},
|
||||
{"internal/copyright", ""},
|
||||
{"internal/coverage", ""},
|
||||
{"internal/coverage/calloc", ""},
|
||||
{"internal/coverage/cfile", "q\x06\x17\x17\x01\x02\x01\x01\x01\x01\x01\x01\x01\"\x02',\x06\a\n\x01\x03\x0e\x06"},
|
||||
{"internal/coverage/cformat", "\x04s.\x04Q\v6\x01\x02\x0e"},
|
||||
{"internal/coverage/cmerge", "w.a"},
|
||||
{"internal/coverage/decodecounter", "m\n.\v\x02H,\x17\x18"},
|
||||
{"internal/coverage/decodemeta", "\x02k\n\x17\x17\v\x02H,"},
|
||||
{"internal/coverage/encodecounter", "\x02k\n.\f\x01\x02F\v!\x15"},
|
||||
{"internal/coverage/encodemeta", "\x02\x01j\n\x13\x04\x17\r\x02F,/"},
|
||||
{"internal/coverage/pods", "\x04s.\x81\x01\x06\x05\n\x02\x01"},
|
||||
{"internal/coverage/rtcov", "\xd5\x02"},
|
||||
{"internal/coverage/slicereader", "m\n\x83\x01["},
|
||||
{"internal/coverage/slicewriter", "w\x83\x01"},
|
||||
{"internal/coverage/stringtab", "w9\x04F"},
|
||||
{"internal/coverage/cfile", "t\r\x16\x17\x01\x02\x01\x01\x01\x01\x01\x01\x01$\x02'.\x06\a\n\x01\x03\x0e\x06"},
|
||||
{"internal/coverage/cformat", "\x04}-\x04S\v8\x01\x02\x0e"},
|
||||
{"internal/coverage/cmerge", "\x81\x01-c"},
|
||||
{"internal/coverage/decodecounter", "p\x11-\v\x02J.\x17\x18"},
|
||||
{"internal/coverage/decodemeta", "\x02n\x11\x16\x17\v\x02J."},
|
||||
{"internal/coverage/encodecounter", "\x02n\x11-\f\x01\x02H\v#\x15"},
|
||||
{"internal/coverage/encodemeta", "\x02\x01m\x11\x12\x04\x17\r\x02H./"},
|
||||
{"internal/coverage/pods", "\x04}-\x85\x01\x06\x05\n\x02\x01"},
|
||||
{"internal/coverage/rtcov", "\xe2\x02"},
|
||||
{"internal/coverage/slicereader", "p\x11\x84\x01]"},
|
||||
{"internal/coverage/slicewriter", "\x81\x01\x84\x01"},
|
||||
{"internal/coverage/stringtab", "\x81\x018\x04H"},
|
||||
{"internal/coverage/test", ""},
|
||||
{"internal/coverage/uleb128", ""},
|
||||
{"internal/cpu", "\xd5\x02"},
|
||||
{"internal/dag", "\x04s\xc4\x01\x03"},
|
||||
{"internal/diff", "\x03t\xc5\x01\x02"},
|
||||
{"internal/exportdata", "\x02\x01q\x03\x02e\x1c,\x01\x05\x11\x01\x02"},
|
||||
{"internal/filepathlite", "t+B\x1a@"},
|
||||
{"internal/fmtsort", "\x04\xaa\x02\r"},
|
||||
{"internal/fuzz", "\x03\nH\x18\x04\x03\x03\x01\f\x036=\f\x03\x1d\x01\x05\x02\x05\n\x01\x02\x01\x01\r\x04\x02"},
|
||||
{"internal/cpu", "\xe2\x02"},
|
||||
{"internal/dag", "\x04}\xc7\x01\x03"},
|
||||
{"internal/diff", "\x03~\xc8\x01\x02"},
|
||||
{"internal/exportdata", "\x02\x01{\x03\x02f\x1c.\x01\x05\x11\x01\x02"},
|
||||
{"internal/filepathlite", "~*D\x1aB"},
|
||||
{"internal/fmtsort", "\x04\xb7\x02\r"},
|
||||
{"internal/fuzz", "\x03\nJ\x19\x04\n\x03\x01\v\x037>\f\x03\x1f\x01\x05\x02\x05\n\x01\x02\x01\x01\r\x04\x02"},
|
||||
{"internal/gate", "\r"},
|
||||
{"internal/goarch", ""},
|
||||
{"internal/godebug", "\x9d\x01!\x82\x01\x01\x14"},
|
||||
{"internal/godebug", "\xa6\x01\"\x85\x01\x01\x14"},
|
||||
{"internal/godebugs", ""},
|
||||
{"internal/goexperiment", ""},
|
||||
{"internal/goos", ""},
|
||||
{"internal/goroot", "\xa6\x02\x01\x05\x12\x02"},
|
||||
{"internal/goroot", "\xb3\x02\x01\x05\x12\x02"},
|
||||
{"internal/gover", "\x04"},
|
||||
{"internal/goversion", ""},
|
||||
{"internal/lazyregexp", "\xa6\x02\v\r\x02"},
|
||||
{"internal/lazytemplate", "\xfa\x01,\x18\x02\r"},
|
||||
{"internal/msan", "\xd5\x02"},
|
||||
{"internal/lazyregexp", "\xb3\x02\v\r\x02"},
|
||||
{"internal/lazytemplate", "\x85\x02.\x18\x02\r"},
|
||||
{"internal/msan", "\xe2\x02"},
|
||||
{"internal/nettest", "\x03\nqG@\f\n\x12\x06\x15\x05\x0f"},
|
||||
{"internal/nettrace", ""},
|
||||
{"internal/obscuretestdata", "l\x8e\x01,"},
|
||||
{"internal/oserror", "t"},
|
||||
{"internal/pkgbits", "\x03R\x18\a\x03\x04\fs\r\x1f\r\n\x01"},
|
||||
{"internal/obscuretestdata", "o\x96\x01."},
|
||||
{"internal/oserror", "~"},
|
||||
{"internal/pkgbits", "\x03T\x19\x0e\x03\x04\vu\r!\r\n\x01"},
|
||||
{"internal/platform", ""},
|
||||
{"internal/poll", "tl\x05\x159\r\x01\x01\r\x06"},
|
||||
{"internal/profile", "\x03\x04m\x03\x83\x017\n\x01\x01\x01\x11"},
|
||||
{"internal/poll", "~m\x05\x15;\r\x01\x01\r\x06"},
|
||||
{"internal/profile", "\x03\x04w\x03\x84\x019\n\x01\x01\x01\x11"},
|
||||
{"internal/profilerecord", ""},
|
||||
{"internal/race", "\x9b\x01\xba\x01"},
|
||||
{"internal/reflectlite", "\x9b\x01!;<\""},
|
||||
{"internal/runtime/atomic", "\xbc\x01\x99\x01"},
|
||||
{"internal/runtime/cgroup", "\x9f\x01=\x04u"},
|
||||
{"internal/runtime/exithook", "\xd1\x01\x84\x01"},
|
||||
{"internal/runtime/gc", "\xbc\x01"},
|
||||
{"internal/runtime/gc/internal/gen", "\nc\n\x18k\x04\v\x1d\b\x10\x02"},
|
||||
{"internal/runtime/gc/scan", "\xb5\x01\a\x18\az"},
|
||||
{"internal/runtime/maps", "\x9b\x01\x01 \n\t\t\x03z"},
|
||||
{"internal/runtime/math", "\xbc\x01"},
|
||||
{"internal/race", "\xa4\x01\xbe\x01"},
|
||||
{"internal/reflectlite", "\xa4\x01\"<>\""},
|
||||
{"internal/runtime/atomic", "\xc6\x01\x9c\x01"},
|
||||
{"internal/runtime/cgroup", "\xa8\x01?\x04w"},
|
||||
{"internal/runtime/exithook", "\xdc\x01\x86\x01"},
|
||||
{"internal/runtime/gc", "\xc6\x01"},
|
||||
{"internal/runtime/gc/internal/gen", "\nf\x11\x17m\x04\v\x1f\b\x10\x02"},
|
||||
{"internal/runtime/gc/scan", "\xbe\x01\b\x19\a|"},
|
||||
{"internal/runtime/maps", "\xa4\x01\x01\x04\x15\b\x03\a\n\t\x03.N"},
|
||||
{"internal/runtime/math", "\xc6\x01"},
|
||||
{"internal/runtime/pprof/label", ""},
|
||||
{"internal/runtime/startlinetest", ""},
|
||||
{"internal/runtime/sys", "\xbc\x01\x04"},
|
||||
{"internal/runtime/syscall/linux", "\xbc\x01\x99\x01"},
|
||||
{"internal/runtime/sys", "\xc6\x01\x04"},
|
||||
{"internal/runtime/syscall/linux", "\xc6\x01\x9c\x01"},
|
||||
{"internal/runtime/wasitest", ""},
|
||||
{"internal/saferio", "\xfa\x01["},
|
||||
{"internal/singleflight", "\xc0\x02"},
|
||||
{"internal/strconv", "\x89\x02L"},
|
||||
{"internal/stringslite", "\x9f\x01\xb6\x01"},
|
||||
{"internal/sync", "\x9b\x01!\x13r\x14"},
|
||||
{"internal/synctest", "\x9b\x01\xba\x01"},
|
||||
{"internal/syscall/execenv", "\xc2\x02"},
|
||||
{"internal/syscall/unix", "\xb3\x02\x0e\x01\x13"},
|
||||
{"internal/sysinfo", "\x02\x01\xb2\x01E,\x18\x02"},
|
||||
{"internal/saferio", "\x85\x02]"},
|
||||
{"internal/singleflight", "\xcd\x02"},
|
||||
{"internal/strconv", "\x94\x02N"},
|
||||
{"internal/stringslite", "\xa8\x01\xba\x01"},
|
||||
{"internal/sync", "\xa4\x01\"\x14t\x14"},
|
||||
{"internal/synctest", "\xa4\x01\xbe\x01"},
|
||||
{"internal/syscall/execenv", "\xcf\x02"},
|
||||
{"internal/syscall/unix", "\xeb\x01U\x0e\x01\x13"},
|
||||
{"internal/sysinfo", "\x02\x01\xbb\x01G.\x18\x02"},
|
||||
{"internal/syslist", ""},
|
||||
{"internal/testenv", "\x03\ng\x02\x01*\x1b\x0f0+\x01\x05\a\n\x01\x02\x02\x01\f"},
|
||||
{"internal/testhash", "\x03\x87\x01p\x118\f"},
|
||||
{"internal/testlog", "\xc0\x02\x01\x14"},
|
||||
{"internal/testpty", "t\x03\xaf\x01"},
|
||||
{"internal/trace", "\x02\x01\x01\x06c\a\x03w\x03\x03\x06\x03\t+\n\x01\x01\x01\x11\x06"},
|
||||
{"internal/trace/internal/testgen", "\x03j\nu\x03\x02\x03\x011\v\r\x11"},
|
||||
{"internal/trace/internal/tracev1", "\x03\x01i\a\x03}\x06\f5\x01"},
|
||||
{"internal/trace/raw", "\x02k\nz\x03\x06C\x01\x13"},
|
||||
{"internal/trace/testtrace", "\x02\x01q\x03q\x04\x03\x05\x01\x05,\v\x02\b\x02\x01\x05"},
|
||||
{"internal/testenv", "\x03\nq\x02\x01)\x1c\x100-\x01\x05\a\n\x01\x02\x02\x01\f"},
|
||||
{"internal/testhash", "\x03\x90\x01r\x11:\f"},
|
||||
{"internal/testlog", "\xcd\x02\x01\x14"},
|
||||
{"internal/testpty", "~\x03\xb2\x01"},
|
||||
{"internal/trace", "\x02\x01\x01\x06f\x0e\x03x\x03\x03\x06\x03\t-\n\x01\x01\x01\x11\x06"},
|
||||
{"internal/trace/internal/testgen", "\x03m\x11v\x03\x02\x03\x013\v\r\x11"},
|
||||
{"internal/trace/internal/tracev1", "\x03\x01l\x0e\x03~\x06\f7\x01"},
|
||||
{"internal/trace/raw", "\x02n\x11{\x03\x06E\x01\x13"},
|
||||
{"internal/trace/testtrace", "\x02\x01{\x03r\x04\x03\x05\x01\x05.\v\x02\b\x02\x01\x05"},
|
||||
{"internal/trace/tracev2", ""},
|
||||
{"internal/trace/traceviewer", "\x02d\v\x06\x1a<\x1f\a\a\x04\b\v\x15\x01\x05\a\n\x01\x02\x0f"},
|
||||
{"internal/trace/traceviewer", "\x02g\v\r\x19>\x1f\a\a\x04\b\v\x17\x01\x05\a\n\x01\x02\x0f"},
|
||||
{"internal/trace/traceviewer/format", ""},
|
||||
{"internal/trace/version", "wz\t"},
|
||||
{"internal/txtar", "\x03t\xaf\x01\x18"},
|
||||
{"internal/types/errors", "\xbd\x02"},
|
||||
{"internal/unsafeheader", "\xd5\x02"},
|
||||
{"internal/xcoff", "`\r\a\x03g\x1c,\x17\x01"},
|
||||
{"internal/zstd", "m\a\x03\x83\x01\x0f"},
|
||||
{"io", "t\xcc\x01"},
|
||||
{"io/fs", "t+*11\x10\x14\x04"},
|
||||
{"io/ioutil", "\xfa\x01\x01+\x15\x03"},
|
||||
{"iter", "\xcf\x01d\""},
|
||||
{"log", "w\x83\x01\x05'\r\r\x01\x0e"},
|
||||
{"internal/trace/version", "\x81\x01{\t"},
|
||||
{"internal/txtar", "\x03~\xb2\x01\x18"},
|
||||
{"internal/types/errors", "\xca\x02"},
|
||||
{"internal/unsafeheader", "\xe2\x02"},
|
||||
{"internal/xcoff", "c\r\x0e\x03h\x1c.\x17\x01"},
|
||||
{"internal/zstd", "p\x0e\x03\x84\x01\x0f"},
|
||||
{"io", "~\xcf\x01"},
|
||||
{"io/fs", "~*,13\x10\x14\x04"},
|
||||
{"io/ioutil", "\x85\x02\x01-\x15\x03"},
|
||||
{"iter", "\xda\x01f\""},
|
||||
{"log", "\x81\x01\x84\x01\x05)\r\r\x01\x0e"},
|
||||
{"log/internal", ""},
|
||||
{"log/slog", "\x03\n[\t\x03\x03\x83\x01\x04\x01\x02\x02\x03(\x05\b\x02\x01\x02\x01\x0e\x02\x02\x02"},
|
||||
{"log/slog", "\x03\n^\t\n\x03H<\x04\x01\x02\x02\x03*\x05\b\x02\x01\x02\x01\x0e\x02\x02\x02"},
|
||||
{"log/slog/internal", ""},
|
||||
{"log/slog/internal/benchmarks", "\rg\x03\x83\x01\x06\x03:\x12"},
|
||||
{"log/slog/internal/buffer", "\xc0\x02"},
|
||||
{"log/syslog", "t\x03\x87\x01\x12\x16\x18\x02\x0f"},
|
||||
{"maps", "\xfd\x01X"},
|
||||
{"math", "\xb5\x01TL"},
|
||||
{"math/big", "\x03q\x03)\x15E\f\x03\x020\x02\x01\x02\x15"},
|
||||
{"math/big/internal/asmgen", "\x03\x01s\x92\x012\x03"},
|
||||
{"math/bits", "\xd5\x02"},
|
||||
{"math/cmplx", "\x86\x02\x03"},
|
||||
{"math/rand", "\xbd\x01I:\x01\x14"},
|
||||
{"math/rand/v2", "t,\x03c\x03L"},
|
||||
{"mime", "\x02\x01i\b\x03\x83\x01\v!\x15\x03\x02\x11\x02"},
|
||||
{"mime/multipart", "\x02\x01N#\x03F=\v\x01\a\x02\x15\x02\x06\x0f\x02\x01\x17"},
|
||||
{"mime/quotedprintable", "\x02\x01t\x83\x01"},
|
||||
{"net", "\x04\tg+\x1e\n\x05\x13\x01\x01\x04\x15\x01%\x06\r\b\x05\x01\x01\r\x06\a"},
|
||||
{"net/http", "\x02\x01\x03\x01\x04\x02D\b\x13\x01\a\x03F=\x01\x03\a\x01\x03\x02\x02\x01\x02\x06\x02\x01\x01\n\x01\x01\x05\x01\x02\x05\b\x01\x01\x01\x02\x01\x0e\x02\x02\x02\b\x01\x01\x01"},
|
||||
{"net/http/cgi", "\x02W\x1b\x03\x83\x01\x04\a\v\x01\x13\x01\x01\x01\x04\x01\x05\x02\b\x02\x01\x11\x0e"},
|
||||
{"net/http/cookiejar", "\x04p\x03\x99\x01\x01\b\a\x05\x16\x03\x02\x0f\x04"},
|
||||
{"net/http/fcgi", "\x02\x01\n`\a\x03\x83\x01\x16\x01\x01\x14\x18\x02\x0f"},
|
||||
{"net/http/httptest", "\x02\x01\nL\x02\x1b\x01\x83\x01\x04\x12\x01\n\t\x02\x17\x01\x02\x0f\x0e"},
|
||||
{"net/http/httptrace", "\rLnI\x14\n!"},
|
||||
{"net/http/httputil", "\x02\x01\ng\x03\x83\x01\x04\x0f\x03\x01\x05\x02\x01\v\x01\x19\x02\x01\x0e\x0e"},
|
||||
{"net/http/internal", "\x02\x01q\x03\x83\x01"},
|
||||
{"net/http/internal/ascii", "\xbe\x02\x13"},
|
||||
{"net/http/internal/httpcommon", "\rg\x03\x9f\x01\x0e\x01\x17\x01\x01\x02\x1d\x02"},
|
||||
{"net/http/internal/testcert", "\xbe\x02"},
|
||||
{"net/http/pprof", "\x02\x01\nj\x19-\x02\x0e-\x04\x13\x14\x01\r\x04\x03\x01\x02\x01\x11"},
|
||||
{"log/slog/internal/benchmarks", "\rq\x03\x84\x01\x06\x03<\x12"},
|
||||
{"log/slog/internal/buffer", "\xcd\x02"},
|
||||
{"log/syslog", "~\x03\x88\x01\x12\x18\x18\x02\x0f"},
|
||||
{"maps", "\x88\x02Z"},
|
||||
{"math", "\xbe\x01VN"},
|
||||
{"math/big", "\x03{\x03(\x15G\f\x03\x022\x02\x01\x02\x15"},
|
||||
{"math/big/internal/asmgen", "\x03\x01}\x93\x014\x03"},
|
||||
{"math/bits", "\xe2\x02"},
|
||||
{"math/cmplx", "\x91\x02\x03"},
|
||||
{"math/rand", "\xc7\x01J<\x01\x14"},
|
||||
{"math/rand/v2", "~+\x03e\x03N"},
|
||||
{"mime", "\x02\x01l\x0f\x03\x84\x01\v#\x15\x03\x02\x11\x02"},
|
||||
{"mime/multipart", "\x02\x01P+\x03F>\v\x01\a\x02\x17\x02\x06\x0f\x02\x01\x17"},
|
||||
{"mime/quotedprintable", "\x02\x01~\x84\x01"},
|
||||
{"net", "\x04\tq*\x1f\v\x05\x13\x01\x01\x04\x15\x01'\x06\r\b\x05\x01\x01\r\x06\t"},
|
||||
{"net/http", "\x02\x01\x03\x01\x04\x02N\x14\x0f\x03F>\x01\x03\a\x01\x06\x01\x01\x02\x06\x02\x01\x01\f\x01\x01\x05\x01\x02\x05\b\x01\x01\x01\x02\x01\x0e\x02\x02\x02\n\x01\x03"},
|
||||
{"net/http/cgi", "\x02Y#\x03\x84\x01\x04\a\v\x01\x15\x01\x01\x01\x04\x01\x05\x02\b\x02\x01\x11\x10"},
|
||||
{"net/http/cookiejar", "\x04z\x03\x9a\x01\x01\b\t\x05\x16\x03\x02\x0f\x04"},
|
||||
{"net/http/fcgi", "\x02\x01\nc\x0e\x03\x84\x01\x16\x01\x01\x16\x18\x02\x0f"},
|
||||
{"net/http/httptest", "\x02\x01\nN\x02#\x01P4\x04\x12\x01\f\t\x02\r\n\x01\x02\x03\f\x06\n"},
|
||||
{"net/http/httptrace", "\rNwI\x16+"},
|
||||
{"net/http/httputil", "\x02\x01\nq\x03F>\x04\x0f\x03\x01\x05\x02\x01\r\x01\x19\x02\x01\x0e\x10"},
|
||||
{"net/http/internal", "\x02\x01m\x0e\x03\x84\x01"},
|
||||
{"net/http/internal/ascii", "\xcb\x02\x13"},
|
||||
{"net/http/internal/http2", "\x02\x01\x03\x01\x06F\b\x15\x0e\x03\x84\x01\x01\x03\b\x03\x02\x03\x02\x06\x02\x03\x01\n\x01\x01\b\x05\b\x02\x01\x02\x01\x0e\x10\x02\x02"},
|
||||
{"net/http/internal/httpcommon", "\rq\x03\xa0\x01\x10\x01\x17\x01\x01\x02\x1f\x02"},
|
||||
{"net/http/internal/httpsfv", "\xc8\x02\x02\x01\x11\x04"},
|
||||
{"net/http/internal/testcert", "\xcb\x02"},
|
||||
{"net/http/pprof", "\x02\x01\nt\x18.\x11-\x04\x13\x16\x01\r\x04\x03\x01\x02\x01\x11"},
|
||||
{"net/internal/cgotest", ""},
|
||||
{"net/internal/socktest", "w\xc9\x01\x02"},
|
||||
{"net/mail", "\x02r\x03\x83\x01\x04\x0f\x03\x14\x1a\x02\x0f\x04"},
|
||||
{"net/netip", "\x04p+\x01f\x034\x17"},
|
||||
{"net/rpc", "\x02m\x05\x03\x10\ni\x04\x12\x01\x1d\r\x03\x02"},
|
||||
{"net/rpc/jsonrpc", "q\x03\x03\x83\x01\x16\x11\x1f"},
|
||||
{"net/smtp", "\x194\f\x13\b\x03\x83\x01\x16\x14\x1a"},
|
||||
{"net/textproto", "\x02\x01q\x03\x83\x01\f\n-\x01\x02\x15"},
|
||||
{"net/url", "t\x03Fc\v\x10\x02\x01\x17"},
|
||||
{"os", "t+\x01\x19\x03\x10\x14\x01\x03\x01\x05\x10\x018\b\x05\x01\x01\r\x06"},
|
||||
{"os/exec", "\x03\ngI'\x01\x15\x01+\x06\a\n\x01\x03\x01\r"},
|
||||
{"os/exec/internal/fdtest", "\xc2\x02"},
|
||||
{"os/signal", "\r\x99\x02\x15\x05\x02"},
|
||||
{"os/user", "\x02\x01q\x03\x83\x01,\r\n\x01\x02"},
|
||||
{"path", "t+\xb4\x01"},
|
||||
{"path/filepath", "t+\x1aB+\r\b\x03\x04\x11"},
|
||||
{"plugin", "t"},
|
||||
{"reflect", "t'\x04\x1d\x13\b\x04\x05\x17\x06\t-\n\x03\x11\x02\x02"},
|
||||
{"net/internal/socktest", "\x81\x01\xcc\x01\x02"},
|
||||
{"net/mail", "\x02|\x03\x84\x01\x04\x0f\x03\x16\x1a\x02\x0f\x04"},
|
||||
{"net/netip", "\x04z*\x01h\x036\x17"},
|
||||
{"net/rpc", "\x02p\f\x03\x0f\nk\x04\x12\x01\x1f\r\x03\x02"},
|
||||
{"net/rpc/jsonrpc", "t\n\x03\x84\x01\x16\x13\x1f"},
|
||||
{"net/smtp", "\x195\r\x14\x0f\x03\x84\x01\x16\x16\x1a"},
|
||||
{"net/textproto", "\x02\x01{\x03\x84\x01\f\n/\x01\x02\x15"},
|
||||
{"net/url", "~\x03Ff\v\x10\x02\x01\x17"},
|
||||
{"os", "~*\x01\x19\x04\x11\x14\x01\x03\x01\x05\x10\x01:\b\x05\x01\x01\r\x06"},
|
||||
{"os/exec", "\x03\nqI(\x01\x15\x01-\x06\a\n\x01\x03\x01\r"},
|
||||
{"os/exec/internal/fdtest", "\xcf\x02"},
|
||||
{"os/signal", "\r\xa6\x02\x15\x05\x02"},
|
||||
{"os/user", "\x02\x01{\x03\x84\x01.\r\n\x01\x02"},
|
||||
{"path", "~*\xb8\x01"},
|
||||
{"path/filepath", "~*\x1aD-\r\b\x03\x04\x11"},
|
||||
{"plugin", "~"},
|
||||
{"reflect", "~&\x04\x1e\x03\x11\b\x04\x05\x17\x06\t/\n\x03\x11\x02\x02"},
|
||||
{"reflect/internal/example1", ""},
|
||||
{"reflect/internal/example2", ""},
|
||||
{"regexp", "\x03\xf7\x018\t\x02\x01\x02\x11\x02"},
|
||||
{"regexp/syntax", "\xbb\x02\x01\x01\x01\x02\x11\x02"},
|
||||
{"runtime", "\x9b\x01\x04\x01\x03\f\x06\a\x02\x01\x01\x0e\x03\x01\x01\x01\x02\x01\x01\x01\x02\x01\x04\x01\x10\x18L"},
|
||||
{"runtime/coverage", "\xa7\x01S"},
|
||||
{"runtime/debug", "wUZ\r\b\x02\x01\x11\x06"},
|
||||
{"runtime/metrics", "\xbe\x01H-\""},
|
||||
{"runtime/pprof", "\x02\x01\x01\x03\x06`\a\x03$$\x0f\v!\f \r\b\x01\x01\x01\x02\x02\n\x03\x06"},
|
||||
{"runtime/race", "\xb9\x02"},
|
||||
{"regexp", "\x03\x82\x02\x037\t\x02\x01\x02\x11\x02"},
|
||||
{"regexp/syntax", "\xc8\x02\x01\x01\x01\x02\x11\x02"},
|
||||
{"runtime", "\xa4\x01\x04\x01\x03\f\x06\b\x02\x01\x01\x0f\x03\x01\x01\x01\x02\x01\x01\x01\x02\x01\x04\x01\x10\x18N"},
|
||||
{"runtime/coverage", "\xb0\x01U"},
|
||||
{"runtime/debug", "\x81\x01V\\\r\b\x02\x01\x11\x06"},
|
||||
{"runtime/metrics", "\xc8\x01I/\""},
|
||||
{"runtime/pprof", "\x02\x01\x01\x03\x06c\x0e\x03#5\v!\f\"\r\b\x01\x01\x01\x02\x02\n\x03\x06"},
|
||||
{"runtime/race", "\xc6\x02"},
|
||||
{"runtime/race/internal/amd64v1", ""},
|
||||
{"runtime/trace", "\rg\x03z\t9\b\x05\x01\x0e\x06"},
|
||||
{"slices", "\x04\xf9\x01\fL"},
|
||||
{"sort", "\xd0\x0192"},
|
||||
{"strconv", "t+A\x01r"},
|
||||
{"strings", "t'\x04B\x19\x03\f7\x11\x02\x02"},
|
||||
{"runtime/trace", "\rq\x03{\t;\b\x05\x01\x0e\x06"},
|
||||
{"slices", "\x04\x84\x02\fN"},
|
||||
{"sort", "\xdb\x0194"},
|
||||
{"strconv", "~*C\x01t"},
|
||||
{"strings", "~&\x04D\x19\x03\f9\x11\x02\x02"},
|
||||
{"structs", ""},
|
||||
{"sync", "\xcf\x01\x13\x01P\x0e\x14"},
|
||||
{"sync/atomic", "\xd5\x02"},
|
||||
{"syscall", "t(\x03\x01\x1c\n\x03\x06\r\x04S\b\x05\x01\x14"},
|
||||
{"testing", "\x03\ng\x02\x01X\x17\x14\f\x05\x1b\x06\x02\x05\x02\x05\x01\x02\x01\x02\x01\x0e\x02\x04"},
|
||||
{"testing/cryptotest", "QOZ\x124\x03\x12"},
|
||||
{"testing/fstest", "t\x03\x83\x01\x01\n&\x10\x03\t\b"},
|
||||
{"testing/internal/testdeps", "\x02\v\xae\x01/\x10,\x03\x05\x03\x06\a\x02\x0f"},
|
||||
{"testing/iotest", "\x03q\x03\x83\x01\x04"},
|
||||
{"testing/quick", "v\x01\x8f\x01\x05#\x10\x11"},
|
||||
{"testing/slogtest", "\rg\x03\x89\x01.\x05\x10\f"},
|
||||
{"testing/synctest", "\xe3\x01`\x12"},
|
||||
{"text/scanner", "\x03t\x83\x01,+\x02"},
|
||||
{"text/tabwriter", "w\x83\x01Y"},
|
||||
{"text/template", "t\x03C@\x01\n \x01\x05\x01\x02\x05\v\x02\x0e\x03\x02"},
|
||||
{"text/template/parse", "\x03t\xbc\x01\n\x01\x13\x02"},
|
||||
{"time", "t+\x1e$(*\r\x02\x13"},
|
||||
{"time/tzdata", "t\xce\x01\x13"},
|
||||
{"sync", "\xda\x01\x02\x11\x01R\x0e\x14"},
|
||||
{"sync/atomic", "\xe2\x02"},
|
||||
{"syscall", "~'\x03\x01\x1d\n\x04\x06\r\x04U\b\x05\x01\x14"},
|
||||
{"testing", "\x03\nq\x02\x01Y\x17\x14\f\x05\x1d\x06\x02\x05\x02\x05\x01\x02\x01\x02\x01\x0e\x02\x04"},
|
||||
{"testing/cryptotest", "SV\\\x126\x03\x12"},
|
||||
{"testing/fstest", "~\x03\x84\x01\x01\n(\x10\x03\t\b"},
|
||||
{"testing/internal/testdeps", "\x02\v\xb7\x011\x10.\x03\x05\x03\x06\a\x02\x0f"},
|
||||
{"testing/iotest", "\x03{\x03\x84\x01\x04"},
|
||||
{"testing/quick", "\x80\x01\x01\x90\x01\x05%\x10\x11"},
|
||||
{"testing/slogtest", "\rq\x03\x8a\x010\x05\x10\f"},
|
||||
{"testing/synctest", "\xee\x01b\f\x06"},
|
||||
{"text/scanner", "\x03~\x84\x01.+\x02"},
|
||||
{"text/tabwriter", "\x81\x01\x84\x01["},
|
||||
{"text/template", "~\x03BB\x01\n\"\x01\x05\x01\x02\x05\v\x02\x0e\x03\x02"},
|
||||
{"text/template/parse", "\x03~\xbf\x01\n\x01\x13\x02"},
|
||||
{"time", "~*D(,\r\x02\x13"},
|
||||
{"time/tzdata", "~\xd1\x01\x13"},
|
||||
{"unicode", ""},
|
||||
{"unicode/utf16", ""},
|
||||
{"unicode/utf8", ""},
|
||||
{"unique", "\x9b\x01!%\x01Q\r\x01\x14\x12"},
|
||||
{"unique", "\xa4\x01\"&\x01S\r\x01\x14\x19"},
|
||||
{"unsafe", ""},
|
||||
{"vendor/golang.org/x/crypto/chacha20", "\x10]\a\x95\x01*'"},
|
||||
{"vendor/golang.org/x/crypto/chacha20poly1305", "\x10\aV\a\xe2\x01\x04\x01\a"},
|
||||
{"vendor/golang.org/x/crypto/cryptobyte", "j\n\x03\x90\x01'!\n"},
|
||||
{"uuid", "\x03\x01O\x1d\x03\v\xcf\x01\x0f"},
|
||||
{"vendor/golang.org/x/crypto/chacha20", "\x10`\x0e\x96\x01,)"},
|
||||
{"vendor/golang.org/x/crypto/chacha20poly1305", "\x10\aY\x0e\xe6\x01\x05\x01\f"},
|
||||
{"vendor/golang.org/x/crypto/cryptobyte", "m\x11\x03\x91\x01)!\v"},
|
||||
{"vendor/golang.org/x/crypto/cryptobyte/asn1", ""},
|
||||
{"vendor/golang.org/x/crypto/internal/alias", "\xd5\x02"},
|
||||
{"vendor/golang.org/x/crypto/internal/poly1305", "X\x15\x9c\x01"},
|
||||
{"vendor/golang.org/x/net/dns/dnsmessage", "t\xc7\x01"},
|
||||
{"vendor/golang.org/x/net/http/httpguts", "\x90\x02\x14\x1a\x15\r"},
|
||||
{"vendor/golang.org/x/net/http/httpproxy", "t\x03\x99\x01\x10\x05\x01\x18\x15\r"},
|
||||
{"vendor/golang.org/x/net/http2/hpack", "\x03q\x03\x83\x01F"},
|
||||
{"vendor/golang.org/x/net/idna", "w\x8f\x018\x15\x10\x02\x01"},
|
||||
{"vendor/golang.org/x/net/nettest", "\x03j\a\x03\x83\x01\x11\x05\x16\x01\f\n\x01\x02\x02\x01\f"},
|
||||
{"vendor/golang.org/x/sys/cpu", "\xa6\x02\r\n\x01\x17"},
|
||||
{"vendor/golang.org/x/text/secure/bidirule", "t\xdf\x01\x11\x01"},
|
||||
{"vendor/golang.org/x/text/transform", "\x03q\x86\x01Y"},
|
||||
{"vendor/golang.org/x/text/unicode/bidi", "\x03\bl\x87\x01>\x17"},
|
||||
{"vendor/golang.org/x/text/unicode/norm", "m\n\x83\x01F\x13\x11"},
|
||||
{"weak", "\x9b\x01\x98\x01\""},
|
||||
{"vendor/golang.org/x/crypto/hkdf", "\x18\x01e\x15r"},
|
||||
{"vendor/golang.org/x/crypto/internal/alias", "\xe2\x02"},
|
||||
{"vendor/golang.org/x/crypto/internal/poly1305", "Z\x16\xa4\x01"},
|
||||
{"vendor/golang.org/x/net/dns/dnsmessage", "~\xca\x01"},
|
||||
{"vendor/golang.org/x/net/http/httpguts", "\x9b\x02\x16\x1a\x15\x10"},
|
||||
{"vendor/golang.org/x/net/http/httpproxy", "~\x03\x9a\x01\x12\x05\x01\x18\x15\x10"},
|
||||
{"vendor/golang.org/x/net/http2/hpack", "\x03{\x03\x84\x01H"},
|
||||
{"vendor/golang.org/x/net/http3", "\x9c\x02@\x06\x0f\x04"},
|
||||
{"vendor/golang.org/x/net/idna", "\x81\x01\x90\x01:\x13\x02\x17\x02\x01"},
|
||||
{"vendor/golang.org/x/net/internal/http3", "\rN#\x03\x84\x01\v\x04\a\x01\x05\x10\x01\x16\x02\x01\x02\x0f\x10\x02\x04\x03"},
|
||||
{"vendor/golang.org/x/net/internal/httpcommon", "\rq\x03\xa0\x01\x10\x01\x17\x01\x01\x02\x1f\x02"},
|
||||
{"vendor/golang.org/x/net/internal/quic/quicwire", "p"},
|
||||
{"vendor/golang.org/x/net/nettest", "\x03m\x0e\x03\x84\x01\x11\x05\x18\x01\f\n\x01\x02\x02\x01\f"},
|
||||
{"vendor/golang.org/x/net/quic", "\x03\n\x01\x01\x01\t:\x04\x04\x15\x03\v\x03\x12r\x06\x06\x06\x04\x12\x06\x15\x02\x01\x02\x01\x01\r\x06\x02\x01\x01\x02\v"},
|
||||
{"vendor/golang.org/x/sys/cpu", "\xb3\x02\r\n\x01\x17"},
|
||||
{"vendor/golang.org/x/text/secure/bidirule", "~\xe2\x01\x18\x01"},
|
||||
{"vendor/golang.org/x/text/transform", "\x03{\x87\x01["},
|
||||
{"vendor/golang.org/x/text/unicode/bidi", "\x03\bv\x88\x01@\x17"},
|
||||
{"vendor/golang.org/x/text/unicode/norm", "p\x11\x84\x01H\x13\x18"},
|
||||
{"weak", "\xa4\x01\x9c\x01\""},
|
||||
}
|
||||
|
||||
// bootstrap is the list of bootstrap packages extracted from cmd/dist.
|
||||
@@ -408,6 +428,7 @@ var bootstrap = map[string]bool{
|
||||
"cmd/compile/internal/logopt": true,
|
||||
"cmd/compile/internal/loong64": true,
|
||||
"cmd/compile/internal/loopvar": true,
|
||||
"cmd/compile/internal/midway": true,
|
||||
"cmd/compile/internal/mips": true,
|
||||
"cmd/compile/internal/mips64": true,
|
||||
"cmd/compile/internal/noder": true,
|
||||
@@ -512,6 +533,7 @@ var bootstrap = map[string]bool{
|
||||
"internal/race": true,
|
||||
"internal/runtime/gc": true,
|
||||
"internal/saferio": true,
|
||||
"internal/strconv": true,
|
||||
"internal/syscall/unix": true,
|
||||
"internal/types/errors": true,
|
||||
"internal/unsafeheader": true,
|
||||
|
||||
+286
-1
@@ -270,6 +270,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"ContainsRune", Func, 7, "func(b []byte, r rune) bool"},
|
||||
{"Count", Func, 0, "func(s []byte, sep []byte) int"},
|
||||
{"Cut", Func, 18, "func(s []byte, sep []byte) (before []byte, after []byte, found bool)"},
|
||||
{"CutLast", Func, 27, "func(s []byte, sep []byte) (before []byte, after []byte, found bool)"},
|
||||
{"CutPrefix", Func, 20, "func(s []byte, prefix []byte) (after []byte, found bool)"},
|
||||
{"CutSuffix", Func, 20, "func(s []byte, suffix []byte) (before []byte, found bool)"},
|
||||
{"Equal", Func, 0, "func(a []byte, b []byte) bool"},
|
||||
@@ -538,6 +539,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"MD4", Const, 0, ""},
|
||||
{"MD5", Const, 0, ""},
|
||||
{"MD5SHA1", Const, 0, ""},
|
||||
{"MLDSAMu", Const, 27, ""},
|
||||
{"MessageSigner", Type, 25, ""},
|
||||
{"PrivateKey", Type, 0, ""},
|
||||
{"PublicKey", Type, 2, ""},
|
||||
@@ -812,6 +814,40 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Size", Const, 0, ""},
|
||||
{"Sum", Func, 2, "func(data []byte) [16]byte"},
|
||||
},
|
||||
"crypto/mldsa": {
|
||||
{"(*Options).HashFunc", Method, 27, ""},
|
||||
{"(*PrivateKey).Bytes", Method, 27, ""},
|
||||
{"(*PrivateKey).Equal", Method, 27, ""},
|
||||
{"(*PrivateKey).Public", Method, 27, ""},
|
||||
{"(*PrivateKey).PublicKey", Method, 27, ""},
|
||||
{"(*PrivateKey).Sign", Method, 27, ""},
|
||||
{"(*PrivateKey).SignDeterministic", Method, 27, ""},
|
||||
{"(*PublicKey).Bytes", Method, 27, ""},
|
||||
{"(*PublicKey).Equal", Method, 27, ""},
|
||||
{"(*PublicKey).Parameters", Method, 27, ""},
|
||||
{"(Parameters).PublicKeySize", Method, 27, ""},
|
||||
{"(Parameters).SignatureSize", Method, 27, ""},
|
||||
{"(Parameters).String", Method, 27, ""},
|
||||
{"GenerateKey", Func, 27, "func(params Parameters) (*PrivateKey, error)"},
|
||||
{"MLDSA44", Func, 27, "func() Parameters"},
|
||||
{"MLDSA44PublicKeySize", Const, 27, ""},
|
||||
{"MLDSA44SignatureSize", Const, 27, ""},
|
||||
{"MLDSA65", Func, 27, "func() Parameters"},
|
||||
{"MLDSA65PublicKeySize", Const, 27, ""},
|
||||
{"MLDSA65SignatureSize", Const, 27, ""},
|
||||
{"MLDSA87", Func, 27, "func() Parameters"},
|
||||
{"MLDSA87PublicKeySize", Const, 27, ""},
|
||||
{"MLDSA87SignatureSize", Const, 27, ""},
|
||||
{"NewPrivateKey", Func, 27, "func(params Parameters, seed []byte) (*PrivateKey, error)"},
|
||||
{"NewPublicKey", Func, 27, "func(params Parameters, encoding []byte) (*PublicKey, error)"},
|
||||
{"Options", Type, 27, ""},
|
||||
{"Options.Context", Field, 27, ""},
|
||||
{"Parameters", Type, 27, ""},
|
||||
{"PrivateKey", Type, 27, ""},
|
||||
{"PrivateKeySize", Const, 27, ""},
|
||||
{"PublicKey", Type, 27, ""},
|
||||
{"Verify", Func, 27, "func(pk *PublicKey, message []byte, signature []byte, opts *Options) error"},
|
||||
},
|
||||
"crypto/mlkem": {
|
||||
{"(*DecapsulationKey1024).Bytes", Method, 24, ""},
|
||||
{"(*DecapsulationKey1024).Decapsulate", Method, 24, ""},
|
||||
@@ -1120,6 +1156,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"ConnectionState.ECHAccepted", Field, 23, ""},
|
||||
{"ConnectionState.HandshakeComplete", Field, 0, ""},
|
||||
{"ConnectionState.HelloRetryRequest", Field, 26, ""},
|
||||
{"ConnectionState.LocalCertificate", Field, 27, ""},
|
||||
{"ConnectionState.NegotiatedProtocol", Field, 0, ""},
|
||||
{"ConnectionState.NegotiatedProtocolIsMutual", Field, 0, ""},
|
||||
{"ConnectionState.OCSPResponse", Field, 5, ""},
|
||||
@@ -1152,6 +1189,10 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"InsecureCipherSuites", Func, 14, "func() []*CipherSuite"},
|
||||
{"Listen", Func, 0, "func(network string, laddr string, config *Config) (net.Listener, error)"},
|
||||
{"LoadX509KeyPair", Func, 0, "func(certFile string, keyFile string) (Certificate, error)"},
|
||||
{"MLDSA44", Const, 27, ""},
|
||||
{"MLDSA65", Const, 27, ""},
|
||||
{"MLDSA87", Const, 27, ""},
|
||||
{"MLKEM1024", Const, 27, ""},
|
||||
{"NewLRUClientSessionCache", Func, 3, "func(capacity int) ClientSessionCache"},
|
||||
{"NewListener", Func, 0, "func(inner net.Listener, config *Config) net.Listener"},
|
||||
{"NewResumptionState", Func, 21, "func(ticket []byte, state *SessionState) (*ClientSessionState, error)"},
|
||||
@@ -1166,6 +1207,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"ParseSessionState", Func, 21, "func(data []byte) (*SessionState, error)"},
|
||||
{"QUICClient", Func, 21, "func(config *QUICConfig) *QUICConn"},
|
||||
{"QUICConfig", Type, 21, ""},
|
||||
{"QUICConfig.ClientHelloInfoConn", Field, 27, ""},
|
||||
{"QUICConfig.EnableSessionEvents", Field, 23, ""},
|
||||
{"QUICConfig.TLSConfig", Field, 21, ""},
|
||||
{"QUICConn", Type, 21, ""},
|
||||
@@ -1334,6 +1376,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Certificate.PublicKeyAlgorithm", Field, 0, ""},
|
||||
{"Certificate.Raw", Field, 0, ""},
|
||||
{"Certificate.RawIssuer", Field, 0, ""},
|
||||
{"Certificate.RawSignatureAlgorithm", Field, 27, ""},
|
||||
{"Certificate.RawSubject", Field, 0, ""},
|
||||
{"Certificate.RawSubjectPublicKeyInfo", Field, 0, ""},
|
||||
{"Certificate.RawTBSCertificate", Field, 0, ""},
|
||||
@@ -1362,6 +1405,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"CertificateRequest.PublicKey", Field, 3, ""},
|
||||
{"CertificateRequest.PublicKeyAlgorithm", Field, 3, ""},
|
||||
{"CertificateRequest.Raw", Field, 3, ""},
|
||||
{"CertificateRequest.RawSignatureAlgorithm", Field, 27, ""},
|
||||
{"CertificateRequest.RawSubject", Field, 3, ""},
|
||||
{"CertificateRequest.RawSubjectPublicKeyInfo", Field, 3, ""},
|
||||
{"CertificateRequest.RawTBSCertificateRequest", Field, 3, ""},
|
||||
@@ -1422,6 +1466,10 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"KeyUsageKeyEncipherment", Const, 0, ""},
|
||||
{"MD2WithRSA", Const, 0, ""},
|
||||
{"MD5WithRSA", Const, 0, ""},
|
||||
{"MLDSA", Const, 27, ""},
|
||||
{"MLDSA44", Const, 27, ""},
|
||||
{"MLDSA65", Const, 27, ""},
|
||||
{"MLDSA87", Const, 27, ""},
|
||||
{"MarshalECPrivateKey", Func, 2, "func(key *ecdsa.PrivateKey) ([]byte, error)"},
|
||||
{"MarshalPKCS1PrivateKey", Func, 0, "func(key *rsa.PrivateKey) []byte"},
|
||||
{"MarshalPKCS1PublicKey", Func, 10, "func(key *rsa.PublicKey) []byte"},
|
||||
@@ -1468,6 +1516,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"RevocationList.Number", Field, 15, ""},
|
||||
{"RevocationList.Raw", Field, 19, ""},
|
||||
{"RevocationList.RawIssuer", Field, 19, ""},
|
||||
{"RevocationList.RawSignatureAlgorithm", Field, 27, ""},
|
||||
{"RevocationList.RawTBSRevocationList", Field, 19, ""},
|
||||
{"RevocationList.RevokedCertificateEntries", Field, 21, ""},
|
||||
{"RevocationList.RevokedCertificates", Field, 15, ""},
|
||||
@@ -1648,6 +1697,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"(Scanner).Scan", Method, 0, ""},
|
||||
{"ColumnType", Type, 8, ""},
|
||||
{"Conn", Type, 9, ""},
|
||||
{"ConvertAssign", Func, 27, "func(scanCtx driver.ScanContext, dest any, src driver.Value) error"},
|
||||
{"DB", Type, 0, ""},
|
||||
{"DBStats", Type, 5, ""},
|
||||
{"DBStats.Idle", Field, 11, ""},
|
||||
@@ -1744,6 +1794,11 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"(Rows).Next", Method, 0, ""},
|
||||
{"(RowsAffected).LastInsertId", Method, 0, ""},
|
||||
{"(RowsAffected).RowsAffected", Method, 0, ""},
|
||||
{"(RowsColumnScanner).Close", Method, 27, ""},
|
||||
{"(RowsColumnScanner).Columns", Method, 27, ""},
|
||||
{"(RowsColumnScanner).Next", Method, 27, ""},
|
||||
{"(RowsColumnScanner).NextRow", Method, 27, ""},
|
||||
{"(RowsColumnScanner).ScanColumn", Method, 27, ""},
|
||||
{"(RowsColumnTypeDatabaseTypeName).Close", Method, 8, ""},
|
||||
{"(RowsColumnTypeDatabaseTypeName).ColumnTypeDatabaseTypeName", Method, 8, ""},
|
||||
{"(RowsColumnTypeDatabaseTypeName).Columns", Method, 8, ""},
|
||||
@@ -1815,12 +1870,14 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"ResultNoRows", Var, 0, ""},
|
||||
{"Rows", Type, 0, ""},
|
||||
{"RowsAffected", Type, 0, ""},
|
||||
{"RowsColumnScanner", Type, 27, ""},
|
||||
{"RowsColumnTypeDatabaseTypeName", Type, 8, ""},
|
||||
{"RowsColumnTypeLength", Type, 8, ""},
|
||||
{"RowsColumnTypeNullable", Type, 8, ""},
|
||||
{"RowsColumnTypePrecisionScale", Type, 8, ""},
|
||||
{"RowsColumnTypeScanType", Type, 8, ""},
|
||||
{"RowsNextResultSet", Type, 8, ""},
|
||||
{"ScanContext", Type, 27, ""},
|
||||
{"SessionResetter", Type, 10, ""},
|
||||
{"Stmt", Type, 0, ""},
|
||||
{"StmtExecContext", Type, 8, ""},
|
||||
@@ -5038,24 +5095,32 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"(*InvalidUnmarshalError).Error", Method, 0, ""},
|
||||
{"(*MarshalerError).Error", Method, 0, ""},
|
||||
{"(*MarshalerError).Unwrap", Method, 13, ""},
|
||||
{"(*Number).UnmarshalJSONFrom", Method, 27, ""},
|
||||
{"(*RawMessage).MarshalJSON", Method, 0, ""},
|
||||
{"(*RawMessage).UnmarshalJSON", Method, 0, ""},
|
||||
{"(*SyntaxError).Error", Method, 0, ""},
|
||||
{"(*UnmarshalFieldError).Error", Method, 0, ""},
|
||||
{"(*UnmarshalTypeError).Error", Method, 0, ""},
|
||||
{"(*UnmarshalTypeError).Unwrap", Method, 27, ""},
|
||||
{"(*UnsupportedTypeError).Error", Method, 0, ""},
|
||||
{"(*UnsupportedValueError).Error", Method, 0, ""},
|
||||
{"(Delim).String", Method, 5, ""},
|
||||
{"(Marshaler).MarshalJSON", Method, 0, ""},
|
||||
{"(Number).Float64", Method, 1, ""},
|
||||
{"(Number).Int64", Method, 1, ""},
|
||||
{"(Number).MarshalJSONTo", Method, 27, ""},
|
||||
{"(Number).String", Method, 1, ""},
|
||||
{"(RawMessage).MarshalJSON", Method, 8, ""},
|
||||
{"(Unmarshaler).UnmarshalJSON", Method, 0, ""},
|
||||
{"CallMethodsWithLegacySemantics", Func, 27, "func(v bool) Options"},
|
||||
{"Compact", Func, 0, "func(dst *bytes.Buffer, src []byte) error"},
|
||||
{"Decoder", Type, 0, ""},
|
||||
{"DefaultOptionsV1", Func, 27, "func() Options"},
|
||||
{"Delim", Type, 5, ""},
|
||||
{"Encoder", Type, 0, ""},
|
||||
{"FormatByteArrayAsArray", Func, 27, "func(v bool) Options"},
|
||||
{"FormatBytesWithLegacySemantics", Func, 27, "func(v bool) Options"},
|
||||
{"FormatDurationAsNano", Func, 27, "func(v bool) Options"},
|
||||
{"HTMLEscape", Func, 0, "func(dst *bytes.Buffer, src []byte)"},
|
||||
{"Indent", Func, 0, "func(dst *bytes.Buffer, src []byte, prefix string, indent string) error"},
|
||||
{"InvalidUTF8Error", Type, 0, ""},
|
||||
@@ -5068,19 +5133,29 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"MarshalerError", Type, 0, ""},
|
||||
{"MarshalerError.Err", Field, 0, ""},
|
||||
{"MarshalerError.Type", Field, 0, ""},
|
||||
{"MatchCaseSensitiveDelimiter", Func, 27, "func(v bool) Options"},
|
||||
{"MergeWithLegacySemantics", Func, 27, "func(v bool) Options"},
|
||||
{"NewDecoder", Func, 0, "func(r io.Reader) *Decoder"},
|
||||
{"NewEncoder", Func, 0, "func(w io.Writer) *Encoder"},
|
||||
{"Number", Type, 1, ""},
|
||||
{"OmitEmptyWithLegacySemantics", Func, 27, "func(v bool) Options"},
|
||||
{"Options", Type, 27, ""},
|
||||
{"ParseBytesWithLooseRFC4648", Func, 27, "func(v bool) Options"},
|
||||
{"ParseTimeWithLooseRFC3339", Func, 27, "func(v bool) Options"},
|
||||
{"RawMessage", Type, 0, ""},
|
||||
{"ReportErrorsWithLegacySemantics", Func, 27, "func(v bool) Options"},
|
||||
{"StringifyWithLegacySemantics", Func, 27, "func(v bool) Options"},
|
||||
{"SyntaxError", Type, 0, ""},
|
||||
{"SyntaxError.Offset", Field, 0, ""},
|
||||
{"Token", Type, 5, ""},
|
||||
{"Unmarshal", Func, 0, "func(data []byte, v any) error"},
|
||||
{"UnmarshalArrayFromAnyLength", Func, 27, "func(v bool) Options"},
|
||||
{"UnmarshalFieldError", Type, 0, ""},
|
||||
{"UnmarshalFieldError.Field", Field, 0, ""},
|
||||
{"UnmarshalFieldError.Key", Field, 0, ""},
|
||||
{"UnmarshalFieldError.Type", Field, 0, ""},
|
||||
{"UnmarshalTypeError", Type, 0, ""},
|
||||
{"UnmarshalTypeError.Err", Field, 27, ""},
|
||||
{"UnmarshalTypeError.Field", Field, 8, ""},
|
||||
{"UnmarshalTypeError.Offset", Field, 5, ""},
|
||||
{"UnmarshalTypeError.Struct", Field, 8, ""},
|
||||
@@ -5094,6 +5169,158 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"UnsupportedValueError.Value", Field, 0, ""},
|
||||
{"Valid", Func, 9, "func(data []byte) bool"},
|
||||
},
|
||||
"encoding/json/jsontext": {
|
||||
{"(*Decoder).InputOffset", Method, 27, ""},
|
||||
{"(*Decoder).Options", Method, 27, ""},
|
||||
{"(*Decoder).PeekKind", Method, 27, ""},
|
||||
{"(*Decoder).ReadToken", Method, 27, ""},
|
||||
{"(*Decoder).ReadValue", Method, 27, ""},
|
||||
{"(*Decoder).Reset", Method, 27, ""},
|
||||
{"(*Decoder).SkipValue", Method, 27, ""},
|
||||
{"(*Decoder).StackDepth", Method, 27, ""},
|
||||
{"(*Decoder).StackIndex", Method, 27, ""},
|
||||
{"(*Decoder).StackPointer", Method, 27, ""},
|
||||
{"(*Decoder).UnreadBuffer", Method, 27, ""},
|
||||
{"(*Encoder).AvailableBuffer", Method, 27, ""},
|
||||
{"(*Encoder).Options", Method, 27, ""},
|
||||
{"(*Encoder).OutputOffset", Method, 27, ""},
|
||||
{"(*Encoder).Reset", Method, 27, ""},
|
||||
{"(*Encoder).StackDepth", Method, 27, ""},
|
||||
{"(*Encoder).StackIndex", Method, 27, ""},
|
||||
{"(*Encoder).StackPointer", Method, 27, ""},
|
||||
{"(*Encoder).WriteToken", Method, 27, ""},
|
||||
{"(*Encoder).WriteValue", Method, 27, ""},
|
||||
{"(*SyntacticError).Error", Method, 27, ""},
|
||||
{"(*SyntacticError).Unwrap", Method, 27, ""},
|
||||
{"(*Value).Canonicalize", Method, 27, ""},
|
||||
{"(*Value).Compact", Method, 27, ""},
|
||||
{"(*Value).Format", Method, 27, ""},
|
||||
{"(*Value).Indent", Method, 27, ""},
|
||||
{"(*Value).UnmarshalJSON", Method, 27, ""},
|
||||
{"(Kind).String", Method, 27, ""},
|
||||
{"(Pointer).AppendToken", Method, 27, ""},
|
||||
{"(Pointer).Contains", Method, 27, ""},
|
||||
{"(Pointer).IsValid", Method, 27, ""},
|
||||
{"(Pointer).LastToken", Method, 27, ""},
|
||||
{"(Pointer).Parent", Method, 27, ""},
|
||||
{"(Pointer).Tokens", Method, 27, ""},
|
||||
{"(Token).Bool", Method, 27, ""},
|
||||
{"(Token).Clone", Method, 27, ""},
|
||||
{"(Token).Float", Method, 27, ""},
|
||||
{"(Token).Float32", Method, 27, ""},
|
||||
{"(Token).Int", Method, 27, ""},
|
||||
{"(Token).Kind", Method, 27, ""},
|
||||
{"(Token).String", Method, 27, ""},
|
||||
{"(Token).Uint", Method, 27, ""},
|
||||
{"(Value).Clone", Method, 27, ""},
|
||||
{"(Value).IsValid", Method, 27, ""},
|
||||
{"(Value).Kind", Method, 27, ""},
|
||||
{"(Value).MarshalJSON", Method, 27, ""},
|
||||
{"(Value).String", Method, 27, ""},
|
||||
{"AllowDuplicateNames", Func, 27, "func(v bool) Options"},
|
||||
{"AllowInvalidUTF8", Func, 27, "func(v bool) Options"},
|
||||
{"AppendFloat", Func, 27, "func(dst []byte, src float64, bits int) []byte"},
|
||||
{"AppendFormat", Func, 27, "func[Bytes ~[]byte | ~string](dst []byte, src Bytes, opts ...Options) ([]byte, error)"},
|
||||
{"AppendQuote", Func, 27, "func[Bytes ~[]byte | ~string](dst []byte, src Bytes) ([]byte, error)"},
|
||||
{"AppendUnquote", Func, 27, "func[Bytes ~[]byte | ~string](dst []byte, src Bytes) ([]byte, error)"},
|
||||
{"BeginArray", Var, 27, ""},
|
||||
{"BeginObject", Var, 27, ""},
|
||||
{"Bool", Func, 27, "func(b bool) Token"},
|
||||
{"CanonicalizeRawFloats", Func, 27, "func(v bool) Options"},
|
||||
{"CanonicalizeRawInts", Func, 27, "func(v bool) Options"},
|
||||
{"Decoder", Type, 27, ""},
|
||||
{"Encoder", Type, 27, ""},
|
||||
{"EndArray", Var, 27, ""},
|
||||
{"EndObject", Var, 27, ""},
|
||||
{"ErrDuplicateName", Var, 27, ""},
|
||||
{"ErrNonStringName", Var, 27, ""},
|
||||
{"EscapeForHTML", Func, 27, "func(v bool) Options"},
|
||||
{"EscapeForJS", Func, 27, "func(v bool) Options"},
|
||||
{"False", Var, 27, ""},
|
||||
{"Float", Func, 27, "func(n float64) Token"},
|
||||
{"Float32", Func, 27, "func(n float32) Token"},
|
||||
{"Int", Func, 27, "func(n int64) Token"},
|
||||
{"Internal", Var, 27, ""},
|
||||
{"Kind", Type, 27, ""},
|
||||
{"KindBeginArray", Const, 27, ""},
|
||||
{"KindBeginObject", Const, 27, ""},
|
||||
{"KindEndArray", Const, 27, ""},
|
||||
{"KindEndObject", Const, 27, ""},
|
||||
{"KindFalse", Const, 27, ""},
|
||||
{"KindInvalid", Const, 27, ""},
|
||||
{"KindNull", Const, 27, ""},
|
||||
{"KindNumber", Const, 27, ""},
|
||||
{"KindString", Const, 27, ""},
|
||||
{"KindTrue", Const, 27, ""},
|
||||
{"Multiline", Func, 27, "func(v bool) Options"},
|
||||
{"NewDecoder", Func, 27, "func(r io.Reader, opts ...Options) *Decoder"},
|
||||
{"NewEncoder", Func, 27, "func(w io.Writer, opts ...Options) *Encoder"},
|
||||
{"Null", Var, 27, ""},
|
||||
{"Options", Type, 27, ""},
|
||||
{"Pointer", Type, 27, ""},
|
||||
{"PreserveRawStrings", Func, 27, "func(v bool) Options"},
|
||||
{"ReorderRawObjects", Func, 27, "func(v bool) Options"},
|
||||
{"SpaceAfterColon", Func, 27, "func(v bool) Options"},
|
||||
{"SpaceAfterComma", Func, 27, "func(v bool) Options"},
|
||||
{"String", Func, 27, "func(s string) Token"},
|
||||
{"SyntacticError", Type, 27, ""},
|
||||
{"SyntacticError.ByteOffset", Field, 27, ""},
|
||||
{"SyntacticError.Err", Field, 27, ""},
|
||||
{"SyntacticError.JSONPointer", Field, 27, ""},
|
||||
{"Token", Type, 27, ""},
|
||||
{"True", Var, 27, ""},
|
||||
{"Uint", Func, 27, "func(n uint64) Token"},
|
||||
{"Value", Type, 27, ""},
|
||||
{"WithIndent", Func, 27, "func(indent string) Options"},
|
||||
{"WithIndentPrefix", Func, 27, "func(prefix string) Options"},
|
||||
},
|
||||
"encoding/json/v2": {
|
||||
{"(*SemanticError).Error", Method, 27, ""},
|
||||
{"(*SemanticError).Unwrap", Method, 27, ""},
|
||||
{"(Marshaler).MarshalJSON", Method, 27, ""},
|
||||
{"(MarshalerTo).MarshalJSONTo", Method, 27, ""},
|
||||
{"(Unmarshaler).UnmarshalJSON", Method, 27, ""},
|
||||
{"(UnmarshalerFrom).UnmarshalJSONFrom", Method, 27, ""},
|
||||
{"DefaultOptionsV2", Func, 27, "func() Options"},
|
||||
{"Deterministic", Func, 27, "func(v bool) Options"},
|
||||
{"ErrUnknownName", Var, 27, ""},
|
||||
{"FormatNilMapAsNull", Func, 27, "func(v bool) Options"},
|
||||
{"FormatNilSliceAsNull", Func, 27, "func(v bool) Options"},
|
||||
{"GetOption", Func, 27, "func[T any](opts Options, setter func(T) Options) (T, bool)"},
|
||||
{"JoinMarshalers", Func, 27, "func(ms ...*Marshalers) *Marshalers"},
|
||||
{"JoinOptions", Func, 27, "func(srcs ...Options) Options"},
|
||||
{"JoinUnmarshalers", Func, 27, "func(us ...*Unmarshalers) *Unmarshalers"},
|
||||
{"Marshal", Func, 27, "func(in any, opts ...Options) (out []byte, err error)"},
|
||||
{"MarshalEncode", Func, 27, "func(out *jsontext.Encoder, in any, opts ...Options) (err error)"},
|
||||
{"MarshalFunc", Func, 27, "func[T any](fn func(T) ([]byte, error)) *Marshalers"},
|
||||
{"MarshalToFunc", Func, 27, "func[T any](fn func(*jsontext.Encoder, T) error) *Marshalers"},
|
||||
{"MarshalWrite", Func, 27, "func(out io.Writer, in any, opts ...Options) (err error)"},
|
||||
{"Marshaler", Type, 27, ""},
|
||||
{"MarshalerTo", Type, 27, ""},
|
||||
{"Marshalers", Type, 27, ""},
|
||||
{"MatchCaseInsensitiveNames", Func, 27, "func(v bool) Options"},
|
||||
{"OmitZeroStructFields", Func, 27, "func(v bool) Options"},
|
||||
{"Options", Type, 27, ""},
|
||||
{"RejectUnknownMembers", Func, 27, "func(v bool) Options"},
|
||||
{"SemanticError", Type, 27, ""},
|
||||
{"SemanticError.ByteOffset", Field, 27, ""},
|
||||
{"SemanticError.Err", Field, 27, ""},
|
||||
{"SemanticError.GoType", Field, 27, ""},
|
||||
{"SemanticError.JSONKind", Field, 27, ""},
|
||||
{"SemanticError.JSONPointer", Field, 27, ""},
|
||||
{"SemanticError.JSONValue", Field, 27, ""},
|
||||
{"StringifyNumbers", Func, 27, "func(v bool) Options"},
|
||||
{"Unmarshal", Func, 27, "func(in []byte, out any, opts ...Options) (err error)"},
|
||||
{"UnmarshalDecode", Func, 27, "func(in *jsontext.Decoder, out any, opts ...Options) (err error)"},
|
||||
{"UnmarshalFromFunc", Func, 27, "func[T any](fn func(*jsontext.Decoder, T) error) *Unmarshalers"},
|
||||
{"UnmarshalFunc", Func, 27, "func[T any](fn func([]byte, T) error) *Unmarshalers"},
|
||||
{"UnmarshalRead", Func, 27, "func(in io.Reader, out any, opts ...Options) (err error)"},
|
||||
{"Unmarshaler", Type, 27, ""},
|
||||
{"UnmarshalerFrom", Type, 27, ""},
|
||||
{"Unmarshalers", Type, 27, ""},
|
||||
{"WithMarshalers", Func, 27, "func(v *Marshalers) Options"},
|
||||
{"WithUnmarshalers", Func, 27, "func(v *Unmarshalers) Options"},
|
||||
},
|
||||
"encoding/pem": {
|
||||
{"Block", Type, 0, ""},
|
||||
{"Block.Bytes", Field, 0, ""},
|
||||
@@ -6002,6 +6229,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Shift", Func, 5, "func(x Value, op token.Token, s uint) Value"},
|
||||
{"Sign", Func, 5, "func(x Value) int"},
|
||||
{"String", Const, 5, ""},
|
||||
{"StringLen", Func, 27, "func(x Value) int64"},
|
||||
{"StringVal", Func, 5, "func(x Value) string"},
|
||||
{"ToComplex", Func, 6, "func(x Value) Value"},
|
||||
{"ToFloat", Func, 6, "func(x Value) Value"},
|
||||
@@ -6183,6 +6411,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"(*ErrorList).Add", Method, 0, ""},
|
||||
{"(*ErrorList).RemoveMultiples", Method, 0, ""},
|
||||
{"(*ErrorList).Reset", Method, 0, ""},
|
||||
{"(*Scanner).End", Method, 27, ""},
|
||||
{"(*Scanner).Init", Method, 0, ""},
|
||||
{"(*Scanner).Scan", Method, 0, ""},
|
||||
{"(Error).Error", Method, 0, ""},
|
||||
@@ -6222,6 +6451,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"(*File).SetLines", Method, 0, ""},
|
||||
{"(*File).SetLinesForContent", Method, 0, ""},
|
||||
{"(*File).Size", Method, 0, ""},
|
||||
{"(*File).String", Method, 27, ""},
|
||||
{"(*FileSet).AddExistingFiles", Method, 25, ""},
|
||||
{"(*FileSet).AddFile", Method, 0, ""},
|
||||
{"(*FileSet).Base", Method, 0, ""},
|
||||
@@ -6529,6 +6759,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"(*Tuple).Variables", Method, 24, ""},
|
||||
{"(*TypeList).At", Method, 18, ""},
|
||||
{"(*TypeList).Len", Method, 18, ""},
|
||||
{"(*TypeList).String", Method, 27, ""},
|
||||
{"(*TypeList).Types", Method, 24, ""},
|
||||
{"(*TypeName).Exported", Method, 5, ""},
|
||||
{"(*TypeName).Id", Method, 5, ""},
|
||||
@@ -6547,6 +6778,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"(*TypeParam).Underlying", Method, 18, ""},
|
||||
{"(*TypeParamList).At", Method, 18, ""},
|
||||
{"(*TypeParamList).Len", Method, 18, ""},
|
||||
{"(*TypeParamList).String", Method, 27, ""},
|
||||
{"(*TypeParamList).TypeParams", Method, 24, ""},
|
||||
{"(*Union).Len", Method, 18, ""},
|
||||
{"(*Union).String", Method, 18, ""},
|
||||
@@ -6571,9 +6803,14 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"(Checker).PkgNameOf", Method, 22, ""},
|
||||
{"(Checker).TypeOf", Method, 5, ""},
|
||||
{"(Error).Error", Method, 5, ""},
|
||||
{"(Hasher).Equal", Method, 27, ""},
|
||||
{"(Hasher).Hash", Method, 27, ""},
|
||||
{"(HasherIgnoreTags).Equal", Method, 27, ""},
|
||||
{"(HasherIgnoreTags).Hash", Method, 27, ""},
|
||||
{"(Importer).Import", Method, 5, ""},
|
||||
{"(ImporterFrom).Import", Method, 6, ""},
|
||||
{"(ImporterFrom).ImportFrom", Method, 6, ""},
|
||||
{"(Instance).String", Method, 27, ""},
|
||||
{"(Object).Exported", Method, 5, ""},
|
||||
{"(Object).Id", Method, 5, ""},
|
||||
{"(Object).Name", Method, 5, ""},
|
||||
@@ -6643,6 +6880,8 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Float32", Const, 5, ""},
|
||||
{"Float64", Const, 5, ""},
|
||||
{"Func", Type, 5, ""},
|
||||
{"Hasher", Type, 27, ""},
|
||||
{"HasherIgnoreTags", Type, 27, ""},
|
||||
{"Id", Func, 5, "func(pkg *Package, name string) string"},
|
||||
{"Identical", Func, 5, "func(x Type, y Type) bool"},
|
||||
{"IdenticalIgnoreTags", Func, 8, "func(x Type, y Type) bool"},
|
||||
@@ -6877,9 +7116,13 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"(*Hash).Write", Method, 14, ""},
|
||||
{"(*Hash).WriteByte", Method, 14, ""},
|
||||
{"(*Hash).WriteString", Method, 14, ""},
|
||||
{"(ComparableHasher).Equal", Method, 27, ""},
|
||||
{"(ComparableHasher).Hash", Method, 27, ""},
|
||||
{"Bytes", Func, 19, "func(seed Seed, b []byte) uint64"},
|
||||
{"Comparable", Func, 24, "func[T comparable](seed Seed, v T) uint64"},
|
||||
{"ComparableHasher", Type, 27, ""},
|
||||
{"Hash", Type, 14, ""},
|
||||
{"Hasher", Type, 27, ""},
|
||||
{"MakeSeed", Func, 14, "func() Seed"},
|
||||
{"Seed", Type, 14, ""},
|
||||
{"String", Func, 19, "func(seed Seed, s string) uint64"},
|
||||
@@ -8035,6 +8278,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"(*Int).CmpAbs", Method, 10, ""},
|
||||
{"(*Int).Div", Method, 0, ""},
|
||||
{"(*Int).DivMod", Method, 0, ""},
|
||||
{"(*Int).Divide", Method, 27, ""},
|
||||
{"(*Int).Exp", Method, 0, ""},
|
||||
{"(*Int).FillBytes", Method, 15, ""},
|
||||
{"(*Int).Float64", Method, 21, ""},
|
||||
@@ -8119,9 +8363,11 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Accuracy", Type, 5, ""},
|
||||
{"AwayFromZero", Const, 5, ""},
|
||||
{"Below", Const, 5, ""},
|
||||
{"Ceil", Const, 27, ""},
|
||||
{"ErrNaN", Type, 5, ""},
|
||||
{"Exact", Const, 5, ""},
|
||||
{"Float", Type, 5, ""},
|
||||
{"Floor", Const, 27, ""},
|
||||
{"Int", Type, 0, ""},
|
||||
{"Jacobi", Func, 5, "func(x *Int, y *Int) int"},
|
||||
{"MaxBase", Const, 0, ""},
|
||||
@@ -8133,12 +8379,14 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"NewRat", Func, 0, "func(a int64, b int64) *Rat"},
|
||||
{"ParseFloat", Func, 5, "func(s string, base int, prec uint, mode RoundingMode) (f *Float, b int, err error)"},
|
||||
{"Rat", Type, 0, ""},
|
||||
{"Round", Const, 27, ""},
|
||||
{"RoundingMode", Type, 5, ""},
|
||||
{"ToNearestAway", Const, 5, ""},
|
||||
{"ToNearestEven", Const, 5, ""},
|
||||
{"ToNegativeInf", Const, 5, ""},
|
||||
{"ToPositiveInf", Const, 5, ""},
|
||||
{"ToZero", Const, 5, ""},
|
||||
{"Trunc", Const, 27, ""},
|
||||
{"Word", Type, 0, ""},
|
||||
},
|
||||
"math/bits": {
|
||||
@@ -8290,6 +8538,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"(*Rand).Int64", Method, 22, ""},
|
||||
{"(*Rand).Int64N", Method, 22, ""},
|
||||
{"(*Rand).IntN", Method, 22, ""},
|
||||
{"(*Rand).N", Method, 27, ""},
|
||||
{"(*Rand).NormFloat64", Method, 22, ""},
|
||||
{"(*Rand).Perm", Method, 22, ""},
|
||||
{"(*Rand).Shuffle", Method, 22, ""},
|
||||
@@ -8985,7 +9234,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"NoBody", Var, 8, ""},
|
||||
{"NotFound", Func, 0, "func(w ResponseWriter, r *Request)"},
|
||||
{"NotFoundHandler", Func, 0, "func() Handler"},
|
||||
{"ParseCookie", Func, 23, "func(line string) ([]*Cookie, error)"},
|
||||
{"ParseCookie", Func, 23, "func(line string) (#rv1 []*Cookie, #rv2 error)"},
|
||||
{"ParseHTTPVersion", Func, 0, "func(vers string) (major int, minor int, ok bool)"},
|
||||
{"ParseSetCookie", Func, 23, "func(line string) (*Cookie, error)"},
|
||||
{"ParseTime", Func, 1, "func(text string) (t time.Time, err error)"},
|
||||
@@ -9061,6 +9310,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Server.BaseContext", Field, 13, ""},
|
||||
{"Server.ConnContext", Field, 13, ""},
|
||||
{"Server.ConnState", Field, 3, ""},
|
||||
{"Server.DisableClientPriority", Field, 27, ""},
|
||||
{"Server.DisableGeneralOptionsHandler", Field, 20, ""},
|
||||
{"Server.ErrorLog", Field, 3, ""},
|
||||
{"Server.HTTP2", Field, 24, ""},
|
||||
@@ -9226,6 +9476,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"NewRequestWithContext", Func, 23, "func(ctx context.Context, method string, target string, body io.Reader) *http.Request"},
|
||||
{"NewServer", Func, 0, "func(handler http.Handler) *Server"},
|
||||
{"NewTLSServer", Func, 0, "func(handler http.Handler) *Server"},
|
||||
{"NewTestServer", Func, 27, "func(t testing.TB, handler http.Handler) *Server"},
|
||||
{"NewUnstartedServer", Func, 0, "func(handler http.Handler) *Server"},
|
||||
{"ResponseRecorder", Type, 0, ""},
|
||||
{"ResponseRecorder.Body", Field, 0, ""},
|
||||
@@ -9596,6 +9847,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"(*Error).Timeout", Method, 6, ""},
|
||||
{"(*Error).Unwrap", Method, 13, ""},
|
||||
{"(*URL).AppendBinary", Method, 24, ""},
|
||||
{"(*URL).Clone", Method, 27, ""},
|
||||
{"(*URL).EscapedFragment", Method, 15, ""},
|
||||
{"(*URL).EscapedPath", Method, 5, ""},
|
||||
{"(*URL).Hostname", Method, 8, ""},
|
||||
@@ -9616,6 +9868,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"(EscapeError).Error", Method, 0, ""},
|
||||
{"(InvalidHostError).Error", Method, 6, ""},
|
||||
{"(Values).Add", Method, 0, ""},
|
||||
{"(Values).Clone", Method, 27, ""},
|
||||
{"(Values).Del", Method, 0, ""},
|
||||
{"(Values).Encode", Method, 0, ""},
|
||||
{"(Values).Get", Method, 0, ""},
|
||||
@@ -10793,6 +11046,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"ContainsRune", Func, 0, "func(s string, r rune) bool"},
|
||||
{"Count", Func, 0, "func(s string, substr string) int"},
|
||||
{"Cut", Func, 18, "func(s string, sep string) (before string, after string, found bool)"},
|
||||
{"CutLast", Func, 27, "func(s string, sep string) (before string, after string, found bool)"},
|
||||
{"CutPrefix", Func, 20, "func(s string, prefix string) (after string, found bool)"},
|
||||
{"CutSuffix", Func, 20, "func(s string, suffix string) (before string, found bool)"},
|
||||
{"EqualFold", Func, 0, "func(s string, t string) bool"},
|
||||
@@ -17476,6 +17730,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"TestHandler", Func, 21, "func(h slog.Handler, results func() []map[string]any) error"},
|
||||
},
|
||||
"testing/synctest": {
|
||||
{"Sleep", Func, 27, "func(d time.Duration)"},
|
||||
{"Test", Func, 25, "func(t *testing.T, f func(*testing.T))"},
|
||||
{"Wait", Func, 25, "func()"},
|
||||
},
|
||||
@@ -17980,6 +18235,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Bassa_Vah", Var, 4, ""},
|
||||
{"Batak", Var, 0, ""},
|
||||
{"Bengali", Var, 0, ""},
|
||||
{"Beria_Erfe", Var, 27, ""},
|
||||
{"Bhaiksuki", Var, 7, ""},
|
||||
{"Bidi_Control", Var, 0, ""},
|
||||
{"Bopomofo", Var, 0, ""},
|
||||
@@ -18029,6 +18285,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Extender", Var, 0, ""},
|
||||
{"FoldCategory", Var, 0, ""},
|
||||
{"FoldScript", Var, 0, ""},
|
||||
{"Garay", Var, 27, ""},
|
||||
{"Georgian", Var, 0, ""},
|
||||
{"Glagolitic", Var, 0, ""},
|
||||
{"Gothic", Var, 0, ""},
|
||||
@@ -18038,6 +18295,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Gujarati", Var, 0, ""},
|
||||
{"Gunjala_Gondi", Var, 13, ""},
|
||||
{"Gurmukhi", Var, 0, ""},
|
||||
{"Gurung_Khema", Var, 27, ""},
|
||||
{"Han", Var, 0, ""},
|
||||
{"Hangul", Var, 0, ""},
|
||||
{"Hanifi_Rohingya", Var, 13, ""},
|
||||
@@ -18049,6 +18307,9 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Hyphen", Var, 0, ""},
|
||||
{"IDS_Binary_Operator", Var, 0, ""},
|
||||
{"IDS_Trinary_Operator", Var, 0, ""},
|
||||
{"IDS_Unary_Operator", Var, 27, ""},
|
||||
{"ID_Compat_Math_Continue", Var, 27, ""},
|
||||
{"ID_Compat_Math_Start", Var, 27, ""},
|
||||
{"Ideographic", Var, 0, ""},
|
||||
{"Imperial_Aramaic", Var, 0, ""},
|
||||
{"In", Func, 2, "func(r rune, ranges ...*RangeTable) bool"},
|
||||
@@ -18082,6 +18343,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Khmer", Var, 0, ""},
|
||||
{"Khojki", Var, 4, ""},
|
||||
{"Khudawadi", Var, 4, ""},
|
||||
{"Kirat_Rai", Var, 27, ""},
|
||||
{"L", Var, 0, ""},
|
||||
{"LC", Var, 25, ""},
|
||||
{"Lao", Var, 0, ""},
|
||||
@@ -18125,6 +18387,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Miao", Var, 1, ""},
|
||||
{"Mn", Var, 0, ""},
|
||||
{"Modi", Var, 4, ""},
|
||||
{"Modifier_Combining_Mark", Var, 27, ""},
|
||||
{"Mongolian", Var, 0, ""},
|
||||
{"Mro", Var, 4, ""},
|
||||
{"Multani", Var, 5, ""},
|
||||
@@ -18145,6 +18408,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Nyiakeng_Puachue_Hmong", Var, 14, ""},
|
||||
{"Ogham", Var, 0, ""},
|
||||
{"Ol_Chiki", Var, 0, ""},
|
||||
{"Ol_Onal", Var, 27, ""},
|
||||
{"Old_Hungarian", Var, 5, ""},
|
||||
{"Old_Italic", Var, 0, ""},
|
||||
{"Old_North_Arabian", Var, 4, ""},
|
||||
@@ -18214,6 +18478,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Sharada", Var, 1, ""},
|
||||
{"Shavian", Var, 0, ""},
|
||||
{"Siddham", Var, 4, ""},
|
||||
{"Sidetic", Var, 27, ""},
|
||||
{"SignWriting", Var, 5, ""},
|
||||
{"SimpleFold", Func, 0, "func(r rune) rune"},
|
||||
{"Sinhala", Var, 0, ""},
|
||||
@@ -18227,6 +18492,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Space", Var, 0, ""},
|
||||
{"SpecialCase", Type, 0, ""},
|
||||
{"Sundanese", Var, 0, ""},
|
||||
{"Sunuwar", Var, 27, ""},
|
||||
{"Syloti_Nagri", Var, 0, ""},
|
||||
{"Symbol", Var, 0, ""},
|
||||
{"Syriac", Var, 0, ""},
|
||||
@@ -18235,6 +18501,7 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"Tai_Le", Var, 0, ""},
|
||||
{"Tai_Tham", Var, 0, ""},
|
||||
{"Tai_Viet", Var, 0, ""},
|
||||
{"Tai_Yo", Var, 27, ""},
|
||||
{"Takri", Var, 1, ""},
|
||||
{"Tamil", Var, 0, ""},
|
||||
{"Tangsa", Var, 21, ""},
|
||||
@@ -18252,7 +18519,10 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"ToLower", Func, 0, "func(r rune) rune"},
|
||||
{"ToTitle", Func, 0, "func(r rune) rune"},
|
||||
{"ToUpper", Func, 0, "func(r rune) rune"},
|
||||
{"Todhri", Var, 27, ""},
|
||||
{"Tolong_Siki", Var, 27, ""},
|
||||
{"Toto", Var, 21, ""},
|
||||
{"Tulu_Tigalari", Var, 27, ""},
|
||||
{"TurkishCase", Var, 0, ""},
|
||||
{"Ugaritic", Var, 0, ""},
|
||||
{"Unified_Ideograph", Var, 0, ""},
|
||||
@@ -18320,6 +18590,21 @@ var PackageSymbols = map[string][]Symbol{
|
||||
{"String", Func, 0, ""},
|
||||
{"StringData", Func, 0, ""},
|
||||
},
|
||||
"uuid": {
|
||||
{"(*UUID).UnmarshalText", Method, 27, ""},
|
||||
{"(UUID).AppendText", Method, 27, ""},
|
||||
{"(UUID).Compare", Method, 27, ""},
|
||||
{"(UUID).MarshalText", Method, 27, ""},
|
||||
{"(UUID).String", Method, 27, ""},
|
||||
{"Max", Func, 27, "func() UUID"},
|
||||
{"MustParse", Func, 27, "func(s string) UUID"},
|
||||
{"New", Func, 27, "func() UUID"},
|
||||
{"NewV4", Func, 27, "func() UUID"},
|
||||
{"NewV7", Func, 27, "func() UUID"},
|
||||
{"Nil", Func, 27, "func() UUID"},
|
||||
{"Parse", Func, 27, "func(s string) (UUID, error)"},
|
||||
{"UUID", Type, 27, ""},
|
||||
},
|
||||
"weak": {
|
||||
{"(Pointer).Value", Method, 24, ""},
|
||||
{"Make", Func, 24, "func[T any](ptr *T) Pointer[T]"},
|
||||
|
||||
+6
-2
@@ -37,6 +37,10 @@ func ForEachElement(rtypes *typeutil.Map, msets *typeutil.MethodSetCache, T type
|
||||
tmset := msets.MethodSet(T)
|
||||
for method := range tmset.Methods() {
|
||||
sig := method.Type().(*types.Signature)
|
||||
if sig.TypeParams() != nil {
|
||||
continue // skip type-parameterized methods
|
||||
}
|
||||
|
||||
// It is tempting to call visit(sig, false)
|
||||
// but, as noted in golang.org/cl/65450043,
|
||||
// the Signature.Recv field is ignored by
|
||||
@@ -123,10 +127,10 @@ func ForEachElement(rtypes *typeutil.Map, msets *typeutil.MethodSetCache, T type
|
||||
|
||||
case *types.TypeParam, *types.Union:
|
||||
// forEachReachable must not be called on parameterized types.
|
||||
panic(T)
|
||||
panic(fmt.Sprintf("ForEachElement called on type containing %T", T))
|
||||
|
||||
default:
|
||||
panic(T)
|
||||
panic(fmt.Sprintf("ForEachElement called on unexpected type %T", T))
|
||||
}
|
||||
}
|
||||
visit(T, false)
|
||||
|
||||
+28
@@ -22,6 +22,7 @@ import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"iter"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
@@ -242,3 +243,30 @@ func ObjectKind(obj types.Object) string {
|
||||
}
|
||||
return "unknown symbol"
|
||||
}
|
||||
|
||||
// ImplicitFieldSelections returns the sequence of implicit embedded fields
|
||||
// traversed by the given selection. It skips the final leaf field or method.
|
||||
// The boolean component indicates whether the traversal traversed a pointer.
|
||||
func ImplicitFieldSelections(seln types.Selection) iter.Seq2[*types.Var, bool] {
|
||||
return func(yield func(*types.Var, bool) bool) {
|
||||
var (
|
||||
t = seln.Recv()
|
||||
indices = seln.Index()
|
||||
)
|
||||
for _, idx := range indices[:len(indices)-1] {
|
||||
ptr, isPtr := t.Underlying().(*types.Pointer)
|
||||
if isPtr {
|
||||
t = ptr.Elem()
|
||||
}
|
||||
structType, ok := t.Underlying().(*types.Struct)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
field := structType.Field(idx)
|
||||
if !yield(field, isPtr) {
|
||||
break
|
||||
}
|
||||
t = field.Type()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -259,13 +259,13 @@ func TypeExpr(t types.Type, qual types.Qualifier) ast.Expr {
|
||||
case *types.Signature:
|
||||
var params []*ast.Field
|
||||
for v := range t.Params().Variables() {
|
||||
var names []*ast.Ident
|
||||
if v.Name() != "" {
|
||||
names = []*ast.Ident{ast.NewIdent(v.Name())}
|
||||
}
|
||||
params = append(params, &ast.Field{
|
||||
Type: TypeExpr(v.Type(), qual),
|
||||
Names: []*ast.Ident{
|
||||
{
|
||||
Name: v.Name(),
|
||||
},
|
||||
},
|
||||
Type: TypeExpr(v.Type(), qual),
|
||||
Names: names,
|
||||
})
|
||||
}
|
||||
if t.Variadic() {
|
||||
@@ -328,10 +328,10 @@ func TypeExpr(t types.Type, qual types.Qualifier) ast.Expr {
|
||||
return expr
|
||||
|
||||
case *types.Struct:
|
||||
return ast.NewIdent(t.String())
|
||||
return ast.NewIdent(types.TypeString(t, qual))
|
||||
|
||||
case *types.Interface:
|
||||
return ast.NewIdent(t.String())
|
||||
return ast.NewIdent(types.TypeString(t, qual))
|
||||
|
||||
case *types.Union:
|
||||
if t.Len() == 0 {
|
||||
|
||||
Vendored
+10
-8
@@ -322,12 +322,14 @@ go.uber.org/mock/mockgen
|
||||
go.uber.org/mock/mockgen/model
|
||||
# golang.org/x/arch v0.4.0
|
||||
## explicit; go 1.17
|
||||
# golang.org/x/crypto v0.52.0
|
||||
# golang.org/x/crypto v0.53.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/crypto/blake2b
|
||||
golang.org/x/crypto/blowfish
|
||||
golang.org/x/crypto/chacha20
|
||||
golang.org/x/crypto/chacha20poly1305
|
||||
golang.org/x/crypto/cryptobyte
|
||||
golang.org/x/crypto/cryptobyte/asn1
|
||||
golang.org/x/crypto/curve25519
|
||||
golang.org/x/crypto/hkdf
|
||||
golang.org/x/crypto/internal/alias
|
||||
@@ -337,13 +339,13 @@ golang.org/x/crypto/nacl/secretbox
|
||||
golang.org/x/crypto/salsa20/salsa
|
||||
golang.org/x/crypto/ssh
|
||||
golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
|
||||
# golang.org/x/mod v0.35.0
|
||||
# golang.org/x/mod v0.37.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/mod/internal/lazyregexp
|
||||
golang.org/x/mod/modfile
|
||||
golang.org/x/mod/module
|
||||
golang.org/x/mod/semver
|
||||
# golang.org/x/net v0.55.0
|
||||
# golang.org/x/net v0.56.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/net/bpf
|
||||
golang.org/x/net/context
|
||||
@@ -368,10 +370,10 @@ golang.org/x/net/websocket
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/oauth2
|
||||
golang.org/x/oauth2/internal
|
||||
# golang.org/x/sync v0.20.0
|
||||
# golang.org/x/sync v0.22.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/sync/errgroup
|
||||
# golang.org/x/sys v0.45.0
|
||||
# golang.org/x/sys v0.46.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/sys/cpu
|
||||
golang.org/x/sys/execabs
|
||||
@@ -382,10 +384,10 @@ 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.43.0
|
||||
# golang.org/x/term v0.44.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/term
|
||||
# golang.org/x/text v0.37.0
|
||||
# golang.org/x/text v0.40.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/text/cases
|
||||
golang.org/x/text/internal
|
||||
@@ -397,7 +399,7 @@ golang.org/x/text/secure/bidirule
|
||||
golang.org/x/text/transform
|
||||
golang.org/x/text/unicode/bidi
|
||||
golang.org/x/text/unicode/norm
|
||||
# golang.org/x/tools v0.44.0
|
||||
# golang.org/x/tools v0.47.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/tools/cover
|
||||
golang.org/x/tools/go/ast/astutil
|
||||
|
||||
Reference in New Issue
Block a user