From f4766df9471b6603f6846ad9dc83712ba0283369 Mon Sep 17 00:00:00 2001 From: ucwong Date: Mon, 21 Jul 2025 18:14:29 +0800 Subject: [PATCH] rpc optimization --- ctxc/downloader/api.go | 2 - ctxc/filters/api.go | 6 - ctxc/filters/api_test.go | 2 +- ctxc/filters/filter_test.go | 10 +- ctxc/tracers/api.go | 4 +- go.mod | 42 +- go.sum | 83 +-- node/api.go | 2 - node/endpoints.go | 6 +- rpc/client.go | 262 ++++---- rpc/client_example_test.go | 10 +- rpc/client_opt.go | 144 +++++ rpc/client_opt_test.go | 41 ++ rpc/client_test.go | 280 ++++++++- rpc/constants_unix.go | 34 - rpc/constants_unix_nocgo.go | 26 - rpc/context_headers.go | 4 +- rpc/doc.go | 4 +- rpc/endpoints.go | 4 +- rpc/errors.go | 29 +- rpc/handler.go | 296 +++++---- rpc/http.go | 135 ++-- rpc/http_test.go | 111 +++- rpc/inproc.go | 7 +- rpc/ipc.go | 13 +- rpc/ipc_js.go | 4 +- rpc/ipc_unix.go | 4 +- rpc/ipc_windows.go | 19 +- rpc/json.go | 57 +- rpc/metrics.go | 31 +- rpc/server.go | 107 +++- rpc/server_test.go | 66 +- rpc/service.go | 11 +- rpc/stdio.go | 13 +- rpc/subscription.go | 14 +- rpc/subscription_test.go | 34 +- rpc/testservice_test.go | 49 +- rpc/types.go | 85 +-- rpc/types_test.go | 76 ++- rpc/websocket.go | 163 +++-- rpc/websocket_test.go | 268 +++++--- .../CortexFoundation/robot/monitor.go | 2 +- .../github.com/CortexFoundation/robot/rpc.go | 6 +- .../Microsoft/go-winio/.gitattributes | 1 + .../github.com/Microsoft/go-winio/.gitignore | 10 + .../Microsoft/go-winio/.golangci.yml | 147 +++++ .../github.com/Microsoft/go-winio/CODEOWNERS | 1 + vendor/github.com/Microsoft/go-winio/LICENSE | 22 + .../github.com/Microsoft/go-winio/README.md | 89 +++ .../github.com/Microsoft/go-winio/SECURITY.md | 41 ++ .../github.com/Microsoft/go-winio/backup.go | 287 +++++++++ vendor/github.com/Microsoft/go-winio/doc.go | 22 + vendor/github.com/Microsoft/go-winio/ea.go | 137 ++++ vendor/github.com/Microsoft/go-winio/file.go | 320 ++++++++++ .../github.com/Microsoft/go-winio/fileinfo.go | 106 ++++ .../github.com/Microsoft/go-winio/hvsock.go | 582 +++++++++++++++++ .../Microsoft/go-winio/internal/fs/doc.go | 2 + .../Microsoft/go-winio/internal/fs/fs.go | 262 ++++++++ .../go-winio/internal/fs/security.go | 12 + .../go-winio/internal/fs/zsyscall_windows.go | 61 ++ .../go-winio/internal/socket/rawaddr.go | 20 + .../go-winio/internal/socket/socket.go | 177 ++++++ .../internal/socket/zsyscall_windows.go | 69 +++ .../go-winio/internal/stringbuffer/wstring.go | 132 ++++ vendor/github.com/Microsoft/go-winio/pipe.go | 586 ++++++++++++++++++ .../Microsoft/go-winio/pkg/guid/guid.go | 232 +++++++ .../go-winio/pkg/guid/guid_nonwindows.go | 16 + .../go-winio/pkg/guid/guid_windows.go | 13 + .../go-winio/pkg/guid/variant_string.go | 27 + .../Microsoft/go-winio/privilege.go | 196 ++++++ .../github.com/Microsoft/go-winio/reparse.go | 131 ++++ vendor/github.com/Microsoft/go-winio/sd.go | 133 ++++ .../github.com/Microsoft/go-winio/syscall.go | 5 + .../Microsoft/go-winio/zsyscall_windows.go | 378 +++++++++++ .../aws-sdk-go-v2/aws/go_module_metadata.go | 2 +- .../aws/aws-sdk-go-v2/config/CHANGELOG.md | 4 + .../config/go_module_metadata.go | 2 +- .../aws-sdk-go-v2/credentials/CHANGELOG.md | 4 + .../credentials/go_module_metadata.go | 2 +- .../feature/ec2/imds/CHANGELOG.md | 4 + .../feature/ec2/imds/go_module_metadata.go | 2 +- .../internal/configsources/CHANGELOG.md | 4 + .../configsources/go_module_metadata.go | 2 +- .../endpoints/awsrulesfn/partitions.go | 82 ++- .../internal/endpoints/v2/CHANGELOG.md | 4 + .../endpoints/v2/go_module_metadata.go | 2 +- .../internal/presigned-url/CHANGELOG.md | 4 + .../presigned-url/go_module_metadata.go | 2 +- .../service/route53/CHANGELOG.md | 4 + .../service/route53/go_module_metadata.go | 2 +- .../aws-sdk-go-v2/service/sso/CHANGELOG.md | 4 + .../service/sso/go_module_metadata.go | 2 +- .../service/ssooidc/CHANGELOG.md | 4 + .../service/ssooidc/go_module_metadata.go | 2 +- .../aws-sdk-go-v2/service/sts/CHANGELOG.md | 4 + .../service/sts/go_module_metadata.go | 2 +- .../github.com/dgraph-io/badger/v4/stream.go | 37 +- .../github.com/klauspost/cpuid/v2/README.md | 6 +- vendor/github.com/klauspost/cpuid/v2/cpuid.go | 104 +++- .../klauspost/cpuid/v2/detect_x86.go | 4 + .../klauspost/cpuid/v2/featureid_string.go | 178 +++--- .../oapi-codegen/runtime/bindparam.go | 25 +- .../oapi-codegen/runtime/bindstring.go | 6 +- .../oapi-codegen/runtime/deepobject.go | 2 +- .../oapi-codegen/runtime/styleparam.go | 23 +- vendor/github.com/pion/rtp/packet.go | 18 +- vendor/github.com/pion/sdp/v3/.golangci.yml | 6 + vendor/github.com/pion/sdp/v3/base_lexer.go | 13 + vendor/github.com/pion/sdp/v3/unmarshal.go | 51 +- vendor/github.com/pion/sdp/v3/util.go | 1 + vendor/gopkg.in/natefinch/npipe.v2/.gitignore | 22 - .../gopkg.in/natefinch/npipe.v2/LICENSE.txt | 8 - vendor/gopkg.in/natefinch/npipe.v2/README.md | 308 --------- vendor/gopkg.in/natefinch/npipe.v2/doc.go | 50 -- .../natefinch/npipe.v2/npipe_windows.go | 531 ---------------- .../natefinch/npipe.v2/znpipe_windows_386.go | 124 ---- .../npipe.v2/znpipe_windows_amd64.go | 124 ---- vendor/modules.txt | 50 +- whisper/whisperv6/api.go | 3 - 119 files changed, 6499 insertions(+), 2122 deletions(-) create mode 100644 rpc/client_opt.go create mode 100644 rpc/client_opt_test.go delete mode 100644 rpc/constants_unix.go delete mode 100644 rpc/constants_unix_nocgo.go create mode 100644 vendor/github.com/Microsoft/go-winio/.gitattributes create mode 100644 vendor/github.com/Microsoft/go-winio/.gitignore create mode 100644 vendor/github.com/Microsoft/go-winio/.golangci.yml create mode 100644 vendor/github.com/Microsoft/go-winio/CODEOWNERS create mode 100644 vendor/github.com/Microsoft/go-winio/LICENSE create mode 100644 vendor/github.com/Microsoft/go-winio/README.md create mode 100644 vendor/github.com/Microsoft/go-winio/SECURITY.md create mode 100644 vendor/github.com/Microsoft/go-winio/backup.go create mode 100644 vendor/github.com/Microsoft/go-winio/doc.go create mode 100644 vendor/github.com/Microsoft/go-winio/ea.go create mode 100644 vendor/github.com/Microsoft/go-winio/file.go create mode 100644 vendor/github.com/Microsoft/go-winio/fileinfo.go create mode 100644 vendor/github.com/Microsoft/go-winio/hvsock.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/fs/doc.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/fs/fs.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/fs/security.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/socket/socket.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go create mode 100644 vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go create mode 100644 vendor/github.com/Microsoft/go-winio/pipe.go create mode 100644 vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go create mode 100644 vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go create mode 100644 vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go create mode 100644 vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go create mode 100644 vendor/github.com/Microsoft/go-winio/privilege.go create mode 100644 vendor/github.com/Microsoft/go-winio/reparse.go create mode 100644 vendor/github.com/Microsoft/go-winio/sd.go create mode 100644 vendor/github.com/Microsoft/go-winio/syscall.go create mode 100644 vendor/github.com/Microsoft/go-winio/zsyscall_windows.go delete mode 100644 vendor/gopkg.in/natefinch/npipe.v2/.gitignore delete mode 100644 vendor/gopkg.in/natefinch/npipe.v2/LICENSE.txt delete mode 100644 vendor/gopkg.in/natefinch/npipe.v2/README.md delete mode 100644 vendor/gopkg.in/natefinch/npipe.v2/doc.go delete mode 100644 vendor/gopkg.in/natefinch/npipe.v2/npipe_windows.go delete mode 100644 vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_386.go delete mode 100644 vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_amd64.go diff --git a/ctxc/downloader/api.go b/ctxc/downloader/api.go index 72b4a3915d..d5ee3be031 100644 --- a/ctxc/downloader/api.go +++ b/ctxc/downloader/api.go @@ -109,8 +109,6 @@ func (api *PublicDownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription, notifier.Notify(rpcSub.ID, status) case <-rpcSub.Err(): return - case <-notifier.Closed(): - return } } }() diff --git a/ctxc/filters/api.go b/ctxc/filters/api.go index fa4e1d8b28..253605b6ce 100644 --- a/ctxc/filters/api.go +++ b/ctxc/filters/api.go @@ -175,8 +175,6 @@ func (api *FilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Subscrip } case <-rpcSub.Err(): return - case <-notifier.Closed(): - return } } }() @@ -237,8 +235,6 @@ func (api *FilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, error) { notifier.Notify(rpcSub.ID, h) case <-rpcSub.Err(): return - case <-notifier.Closed(): - return } } }() @@ -274,8 +270,6 @@ func (api *FilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subsc } case <-rpcSub.Err(): // client send an unsubscribe request return - case <-notifier.Closed(): // connection dropped - return } } }() diff --git a/ctxc/filters/api_test.go b/ctxc/filters/api_test.go index 42e67f97ec..9f502c3e7b 100644 --- a/ctxc/filters/api_test.go +++ b/ctxc/filters/api_test.go @@ -56,7 +56,7 @@ func TestUnmarshalJSONNewFilterArgs(t *testing.T) { // from, to block number var test1 FilterCriteria - vector := fmt.Sprintf(`{"fromBlock":"%#x","toBlock":"%#x"}`, fromBlock, toBlock) + vector := fmt.Sprintf(`{"fromBlock":"%v","toBlock":"%v"}`, fromBlock, toBlock) if err := json.Unmarshal([]byte(vector), &test1); err != nil { t.Fatal(err) } diff --git a/ctxc/filters/filter_test.go b/ctxc/filters/filter_test.go index 55faf700d1..01b6ca4f1c 100644 --- a/ctxc/filters/filter_test.go +++ b/ctxc/filters/filter_test.go @@ -177,7 +177,7 @@ func TestFilters(t *testing.T) { rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), receipts[i]) } _, sys := newTestFilterSystem(t, db, Config{}) - filter := sys.NewRangeFilter(0, -1, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}}) + filter := sys.NewRangeFilter(0, -2, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}}) logs, _ := filter.Logs(context.Background()) if len(logs) != 4 { @@ -193,7 +193,7 @@ func TestFilters(t *testing.T) { t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) } - filter = sys.NewRangeFilter(990, -1, []common.Address{addr}, [][]common.Hash{{hash3}}) + filter = sys.NewRangeFilter(990, -2, []common.Address{addr}, [][]common.Hash{{hash3}}) logs, _ = filter.Logs(context.Background()) if len(logs) != 1 { t.Error("expected 1 log, got", len(logs)) @@ -210,7 +210,7 @@ func TestFilters(t *testing.T) { } failHash := common.BytesToHash([]byte("fail")) - filter = sys.NewRangeFilter(0, -1, nil, [][]common.Hash{{failHash}}) + filter = sys.NewRangeFilter(0, -2, nil, [][]common.Hash{{failHash}}) logs, _ = filter.Logs(context.Background()) if len(logs) != 0 { @@ -218,14 +218,14 @@ func TestFilters(t *testing.T) { } failAddr := common.BytesToAddress([]byte("failmenow")) - filter = sys.NewRangeFilter(0, -1, []common.Address{failAddr}, nil) + filter = sys.NewRangeFilter(0, -2, []common.Address{failAddr}, nil) logs, _ = filter.Logs(context.Background()) if len(logs) != 0 { t.Error("expected 0 log, got", len(logs)) } - filter = sys.NewRangeFilter(0, -1, nil, [][]common.Hash{{failHash}, {hash1}}) + filter = sys.NewRangeFilter(0, -2, nil, [][]common.Hash{{failHash}, {hash1}}) logs, _ = filter.Logs(context.Background()) if len(logs) != 0 { diff --git a/ctxc/tracers/api.go b/ctxc/tracers/api.go index 631f6c50e3..b0febb8ef1 100644 --- a/ctxc/tracers/api.go +++ b/ctxc/tracers/api.go @@ -246,7 +246,7 @@ func (api *API) TraceChain(ctx context.Context, start, end rpc.BlockNumber, conf } sub := notifier.CreateSubscription() - resCh := api.traceChain(from, to, config, notifier.Closed()) + resCh := api.traceChain(from, to, config, sub.Err()) go func() { for result := range resCh { notifier.Notify(sub.ID, result) @@ -284,7 +284,7 @@ func (r *releaser) call() { // the end block but excludes the start one. The return value will be one item per // transaction, dependent on the requested tracer. // The tracing procedure should be aborted in case the closed signal is received. -func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed <-chan any) chan *blockTraceResult { +func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed <-chan error) chan *blockTraceResult { reexec := defaultTraceReexec if config != nil && config.Reexec != nil { reexec = *config.Reexec diff --git a/go.mod b/go.mod index 1e1c1c7174..c10d74502a 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,14 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1 github.com/CortexFoundation/inference v1.0.2-0.20230307032835-9197d586a4e8 github.com/CortexFoundation/statik v0.0.0-20210315012922-8bb8a7b5dc66 - github.com/CortexFoundation/torrentfs v1.0.70-0.20250705184717-83e763fd9aef + github.com/CortexFoundation/torrentfs v1.0.70-0.20250720163701-a81b9d33f725 + github.com/Microsoft/go-winio v0.6.2 github.com/VictoriaMetrics/fastcache v1.12.5 github.com/arsham/figurine v1.3.0 - github.com/aws/aws-sdk-go-v2 v1.36.5 - github.com/aws/aws-sdk-go-v2/config v1.29.17 - github.com/aws/aws-sdk-go-v2/credentials v1.17.70 - github.com/aws/aws-sdk-go-v2/service/route53 v1.53.0 + github.com/aws/aws-sdk-go-v2 v1.36.6 + github.com/aws/aws-sdk-go-v2/config v1.29.18 + github.com/aws/aws-sdk-go-v2/credentials v1.17.71 + github.com/aws/aws-sdk-go-v2/service/route53 v1.53.1 github.com/cespare/cp v1.1.1 github.com/charmbracelet/bubbletea v1.3.6 github.com/cloudflare/cloudflare-go v0.115.0 @@ -71,7 +72,6 @@ require ( golang.org/x/tools v0.35.0 google.golang.org/protobuf v1.36.6 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c - gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce gopkg.in/urfave/cli.v1 v1.20.0 ) @@ -81,7 +81,7 @@ require ( github.com/CortexFoundation/compress v0.0.0-20240218153512-9074bdc2397c // indirect github.com/CortexFoundation/cvm-runtime v0.0.0-20221117094012-b5a251885572 // indirect github.com/CortexFoundation/merkletree v0.0.0-20240407093305-416581a62b5b // indirect - github.com/CortexFoundation/robot v1.0.7-0.20250226143617-b3819ddaddc6 // indirect + github.com/CortexFoundation/robot v1.0.7-0.20250720133840-49217aa96f7e // indirect github.com/CortexFoundation/wormhole v0.0.2-0.20241128010855-a23c88842cfa // indirect github.com/DataDog/zstd v1.5.7 // indirect github.com/RoaringBitmap/roaring v1.9.4 // indirect @@ -107,15 +107,15 @@ require ( github.com/antlabs/timer v0.1.4 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/arsham/rainbow v1.2.1 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.33 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.37 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.37 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.18 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.25.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.34.1 // indirect github.com/aws/smithy-go v1.22.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect @@ -139,7 +139,7 @@ require ( github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect - github.com/dgraph-io/badger/v4 v4.7.1-0.20250601174320-4f557a74bbf7 // indirect + github.com/dgraph-io/badger/v4 v4.8.0 // indirect github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect github.com/dustin/go-humanize v1.0.1 // indirect @@ -168,7 +168,7 @@ require ( github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // indirect github.com/jedib0t/go-pretty/v6 v6.6.7 // indirect github.com/klauspost/compress v1.18.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.11 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-localereader v0.0.1 // indirect @@ -185,7 +185,7 @@ require ( github.com/naoina/go-stringutil v0.1.0 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/nutsdb/nutsdb v1.0.4 // indirect - github.com/oapi-codegen/runtime v1.1.1 // indirect + github.com/oapi-codegen/runtime v1.1.2 // indirect github.com/olekukonko/errors v1.1.0 // indirect github.com/olekukonko/ll v0.0.9 // indirect github.com/otiai10/copy v1.14.1 // indirect @@ -199,9 +199,9 @@ require ( github.com/pion/mdns/v2 v2.0.7 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.15 // indirect - github.com/pion/rtp v1.8.20 // indirect + github.com/pion/rtp v1.8.21 // indirect github.com/pion/sctp v1.8.39 // indirect - github.com/pion/sdp/v3 v3.0.14 // indirect + github.com/pion/sdp/v3 v3.0.15 // indirect github.com/pion/srtp/v3 v3.0.6 // indirect github.com/pion/stun/v3 v3.0.0 // indirect github.com/pion/transport/v2 v2.2.10 // indirect @@ -230,7 +230,7 @@ require ( github.com/tklauser/numcpus v0.10.0 // indirect github.com/ucwong/filecache v1.0.6-0.20230405163841-810d53ced4bd // indirect github.com/ucwong/go-ttlmap v1.0.2-0.20221020173635-331e7ddde2bb // indirect - github.com/ucwong/golang-kv v1.0.24-0.20250606215932-9b6cb1d9814d // indirect + github.com/ucwong/golang-kv v1.0.24-0.20250720145037-4c149701f3b8 // indirect github.com/ucwong/shard v1.0.1-0.20250426172507-f1db2901f62c // indirect github.com/wlynxg/anet v0.0.5 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect @@ -244,7 +244,7 @@ require ( go.opentelemetry.io/otel v1.37.0 // indirect go.opentelemetry.io/otel/metric v1.37.0 // indirect go.opentelemetry.io/otel/trace v1.37.0 // indirect - golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect + golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect golang.org/x/mod v0.26.0 // indirect golang.org/x/net v0.42.0 // indirect golang.org/x/term v0.33.0 // indirect diff --git a/go.sum b/go.sum index 6d581e55db..5ca11d0b60 100644 --- a/go.sum +++ b/go.sum @@ -63,15 +63,15 @@ github.com/CortexFoundation/inference v1.0.2-0.20230307032835-9197d586a4e8 h1:W/ github.com/CortexFoundation/inference v1.0.2-0.20230307032835-9197d586a4e8/go.mod h1:Doj3mBNzdjCDvKVwysKaHEPbS20A7RRaQY0bHtEVz88= github.com/CortexFoundation/merkletree v0.0.0-20240407093305-416581a62b5b h1:PJvLUrORQ3nEiEYK+X06iRRi1Ayq93mqZ8z+YLSb12I= github.com/CortexFoundation/merkletree v0.0.0-20240407093305-416581a62b5b/go.mod h1:H/XQUOyyWqFweHqa39mLPewU3q9GTY4wvf8KQduuuPQ= -github.com/CortexFoundation/robot v1.0.7-0.20250226143617-b3819ddaddc6 h1:Piuiy3tH3FndM0E4j5RKhmb8IOY+utnE4l0nO52dZp8= -github.com/CortexFoundation/robot v1.0.7-0.20250226143617-b3819ddaddc6/go.mod h1:o0QGALGkHIwjrz3YlDegPjOtIF5hL3BF7vgvmJnd7ZQ= +github.com/CortexFoundation/robot v1.0.7-0.20250720133840-49217aa96f7e h1:ZmPrKHb76Z16hXMeFYKv7oTm0jY8Q1Y9PdeD5FkjQvY= +github.com/CortexFoundation/robot v1.0.7-0.20250720133840-49217aa96f7e/go.mod h1:o0QGALGkHIwjrz3YlDegPjOtIF5hL3BF7vgvmJnd7ZQ= github.com/CortexFoundation/statik v0.0.0-20210315012922-8bb8a7b5dc66 h1:yJbN4DFvpStCShXOVxNV64aawsPqizLuXZhrnhCr2fY= github.com/CortexFoundation/statik v0.0.0-20210315012922-8bb8a7b5dc66/go.mod h1:AkjV4OECAskB9m6w+2e84F0Zcx7oZWEmHB3EKoaDXYk= github.com/CortexFoundation/torrentfs v1.0.13-0.20200623060705-ce027f43f2f8/go.mod h1:Ma+tGhPPvz4CEZHaqEJQMOEGOfHeQBiAoNd1zyc/w3Q= github.com/CortexFoundation/torrentfs v1.0.14-0.20200703071639-3fcabcabf274/go.mod h1:qnb3YlIJmuetVBtC6Lsejr0Xru+1DNmDCdTqnwy7lhk= github.com/CortexFoundation/torrentfs v1.0.20-0.20200810031954-d36d26f82fcc/go.mod h1:N5BsicP5ynjXIi/Npl/SRzlJ630n1PJV2sRj0Z0t2HA= -github.com/CortexFoundation/torrentfs v1.0.70-0.20250705184717-83e763fd9aef h1:Omnrs5UisVld4p7CE3x3sP3h791FWfnc7W5jP8bK2b8= -github.com/CortexFoundation/torrentfs v1.0.70-0.20250705184717-83e763fd9aef/go.mod h1:2kPs5QtFTkHKQz/VKUN0qzRHKSGucudGcEIEvRhmH/g= +github.com/CortexFoundation/torrentfs v1.0.70-0.20250720163701-a81b9d33f725 h1:AVN1sMi2R980BcYgd0lBnxO27equtW4Z+kVsfobDkvA= +github.com/CortexFoundation/torrentfs v1.0.70-0.20250720163701-a81b9d33f725/go.mod h1:p9quWwJZFir1L2OU6rgGHWb/8yOAD/Jl5t6Kiv5vNtg= github.com/CortexFoundation/wormhole v0.0.2-0.20241128010855-a23c88842cfa h1:46VAGWxOwpoLlPNcR9etAhK0NtT215skO9Wl4i14r4o= github.com/CortexFoundation/wormhole v0.0.2-0.20241128010855-a23c88842cfa/go.mod h1:ipzmPabDgzYKUbXkGVe2gTkBEp+MsDx6pXGiuYzmP6s= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= @@ -81,6 +81,8 @@ github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= @@ -262,32 +264,32 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.30.24/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.31.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= -github.com/aws/aws-sdk-go-v2 v1.36.5 h1:0OF9RiEMEdDdZEMqF9MRjevyxAQcf6gY+E7vwBILFj0= -github.com/aws/aws-sdk-go-v2 v1.36.5/go.mod h1:EYrzvCCN9CMUTa5+6lf6MM4tq3Zjp8UhSGR/cBsjai0= -github.com/aws/aws-sdk-go-v2/config v1.29.17 h1:jSuiQ5jEe4SAMH6lLRMY9OVC+TqJLP5655pBGjmnjr0= -github.com/aws/aws-sdk-go-v2/config v1.29.17/go.mod h1:9P4wwACpbeXs9Pm9w1QTh6BwWwJjwYvJ1iCt5QbCXh8= -github.com/aws/aws-sdk-go-v2/credentials v1.17.70 h1:ONnH5CM16RTXRkS8Z1qg7/s2eDOhHhaXVd72mmyv4/0= -github.com/aws/aws-sdk-go-v2/credentials v1.17.70/go.mod h1:M+lWhhmomVGgtuPOhO85u4pEa3SmssPTdcYpP/5J/xc= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 h1:KAXP9JSHO1vKGCr5f4O6WmlVKLFFXgWYAGoJosorxzU= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32/go.mod h1:h4Sg6FQdexC1yYG9RDnOvLbW1a/P986++/Y/a+GyEM8= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36 h1:SsytQyTMHMDPspp+spo7XwXTP44aJZZAC7fBV2C5+5s= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36/go.mod h1:Q1lnJArKRXkenyog6+Y+zr7WDpk4e6XlR6gs20bbeNo= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36 h1:i2vNHQiXUvKhs3quBR6aqlgJaiaexz/aNvdCktW/kAM= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36/go.mod h1:UdyGa7Q91id/sdyHPwth+043HhmP6yP9MBHgbZM0xo8= +github.com/aws/aws-sdk-go-v2 v1.36.6 h1:zJqGjVbRdTPojeCGWn5IR5pbJwSQSBh5RWFTQcEQGdU= +github.com/aws/aws-sdk-go-v2 v1.36.6/go.mod h1:EYrzvCCN9CMUTa5+6lf6MM4tq3Zjp8UhSGR/cBsjai0= +github.com/aws/aws-sdk-go-v2/config v1.29.18 h1:x4T1GRPnqKV8HMJOMtNktbpQMl3bIsfx8KbqmveUO2I= +github.com/aws/aws-sdk-go-v2/config v1.29.18/go.mod h1:bvz8oXugIsH8K7HLhBv06vDqnFv3NsGDt2Znpk7zmOU= +github.com/aws/aws-sdk-go-v2/credentials v1.17.71 h1:r2w4mQWnrTMJjOyIsZtGp3R3XGY3nqHn8C26C2lQWgA= +github.com/aws/aws-sdk-go-v2/credentials v1.17.71/go.mod h1:E7VF3acIup4GB5ckzbKFrCK0vTvEQxOxgdq4U3vcMCY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.33 h1:D9ixiWSG4lyUBL2DDNK924Px9V/NBVpML90MHqyTADY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.33/go.mod h1:caS/m4DI+cij2paz3rtProRBI4s/+TCiWoaWZuQ9010= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.37 h1:osMWfm/sC/L4tvEdQ65Gri5ZZDCUpuYJZbTTDrsn4I0= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.37/go.mod h1:ZV2/1fbjOPr4G4v38G3Ww5TBT4+hmsK45s/rxu1fGy0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.37 h1:v+X21AvTb2wZ+ycg1gx+orkB/9U6L7AOp93R7qYxsxM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.37/go.mod h1:G0uM1kyssELxmJ2VZEfG0q2npObR3BAkF3c1VsfVnfs= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 h1:CXV68E2dNqhuynZJPB80bhPQwAKqBWVer887figW6Jc= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4/go.mod h1:/xFi9KtvBXP97ppCz1TAEvU1Uf66qvid89rbem3wCzQ= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 h1:t0E6FzREdtCsiLIoLCWsYliNsRBgyGD/MCK571qk4MI= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17/go.mod h1:ygpklyoaypuyDvOM5ujWGrYWpAK3h7ugnmKCU/76Ys4= -github.com/aws/aws-sdk-go-v2/service/route53 v1.53.0 h1:UglIEyurCqfzZkjNdYAuXUGFu/FNWMKP5eorzggvXe8= -github.com/aws/aws-sdk-go-v2/service/route53 v1.53.0/go.mod h1:wi1naoiPnCQG3cyjsivwPON1ZmQt/EJGxFqXzubBTAw= -github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 h1:AIRJ3lfb2w/1/8wOOSqYb9fUKGwQbtysJ2H1MofRUPg= -github.com/aws/aws-sdk-go-v2/service/sso v1.25.5/go.mod h1:b7SiVprpU+iGazDUqvRSLf5XmCdn+JtT1on7uNL6Ipc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 h1:BpOxT3yhLwSJ77qIY3DoHAQjZsc4HEGfMCE4NGy3uFg= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3/go.mod h1:vq/GQR1gOFLquZMSrxUK/cpvKCNVYibNyJ1m7JrU88E= -github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 h1:NFOJ/NXEGV4Rq//71Hs1jC/NvPs1ezajK+yQmkwnPV0= -github.com/aws/aws-sdk-go-v2/service/sts v1.34.0/go.mod h1:7ph2tGpfQvwzgistp2+zga9f+bCjlQJPkPUmMgDSD7w= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.18 h1:vvbXsA2TVO80/KT7ZqCbx934dt6PY+vQ8hZpUZ/cpYg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.18/go.mod h1:m2JJHledjBGNMsLOF1g9gbAxprzq3KjC8e4lxtn+eWg= +github.com/aws/aws-sdk-go-v2/service/route53 v1.53.1 h1:R3nSX1hguRy6MnknHiepSvqnnL8ansFwK2hidPesAYU= +github.com/aws/aws-sdk-go-v2/service/route53 v1.53.1/go.mod h1:fmSiB4OAghn85lQgk7XN9l9bpFg5Bm1v3HuaXKytPEw= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.6 h1:rGtWqkQbPk7Bkwuv3NzpE/scwwL9sC1Ul3tn9x83DUI= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.6/go.mod h1:u4ku9OLv4TO4bCPdxf4fA1upaMaJmP9ZijGk3AAOC6Q= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.4 h1:OV/pxyXh+eMA0TExHEC4jyWdumLxNbzz1P0zJoezkJc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.4/go.mod h1:8Mm5VGYwtm+r305FfPSuc+aFkrypeylGYhFim6XEPoc= +github.com/aws/aws-sdk-go-v2/service/sts v1.34.1 h1:aUrLQwJfZtwv3/ZNG2xRtEen+NqI3iesuacjP51Mv1s= +github.com/aws/aws-sdk-go-v2/service/sts v1.34.1/go.mod h1:3wFBZKoWnX3r+Sm7in79i54fBmNfwhdNdQuscCw7QIk= github.com/aws/smithy-go v1.22.4 h1:uqXzVZNuNexwc/xrh6Tb56u89WDlJY6HS+KC0S4QSjw= github.com/aws/smithy-go v1.22.4/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= @@ -409,8 +411,8 @@ github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/dgraph-io/badger/v4 v4.7.1-0.20250601174320-4f557a74bbf7 h1:5lz2FNcAiazQK5997Q/dVSCj5IzF8ZE8+TDQTVkNVtM= -github.com/dgraph-io/badger/v4 v4.7.1-0.20250601174320-4f557a74bbf7/go.mod h1:bcFQR+SKfPLdQ0oz6Xc5sY9xTK+ox1UjunIl7aiN2Tw= +github.com/dgraph-io/badger/v4 v4.8.0 h1:JYph1ChBijCw8SLeybvPINizbDKWZ5n/GYbz2yhN/bs= +github.com/dgraph-io/badger/v4 v4.8.0/go.mod h1:U6on6e8k/RTbUWxqKR0MvugJuVmkxSNc79ap4917h4w= github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -757,8 +759,8 @@ github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zt github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU= -github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= @@ -888,8 +890,8 @@ github.com/nutsdb/nutsdb v1.0.4/go.mod h1:jIbbpBXajzTMZ0o33Yn5zoYIo3v0Dz4WstkVce github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= -github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= +github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI= +github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -984,14 +986,14 @@ github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= github.com/pion/rtp v1.3.2/go.mod h1:q9wPnA96pu2urCcW/sK/RiDn597bhGoAQQ+y2fDwHuY= github.com/pion/rtp v1.4.0/go.mod h1:/l4cvcKd0D3u9JLs2xSVI95YkfXW87a3br3nqmVtSlE= -github.com/pion/rtp v1.8.20 h1:8zcyqohadZE8FCBeGdyEvHiclPIezcwRQH9zfapFyYI= -github.com/pion/rtp v1.8.20/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= +github.com/pion/rtp v1.8.21 h1:3yrOwmZFyUpcIosNcWRpQaU+UXIJ6yxLuJ8Bx0mw37Y= +github.com/pion/rtp v1.8.21/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= github.com/pion/sctp v1.7.6/go.mod h1:ichkYQ5tlgCQwEwvgfdcAolqx1nHbYCxo4D7zK/K0X8= github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE= github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= github.com/pion/sdp/v2 v2.3.7/go.mod h1:+ZZf35r1+zbaWYiZLfPutWfx58DAWcGb2QsS3D/s9M8= -github.com/pion/sdp/v3 v3.0.14 h1:1h7gBr9FhOWH5GjWWY5lcw/U85MtdcibTyt/o6RxRUI= -github.com/pion/sdp/v3 v3.0.14/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= +github.com/pion/sdp/v3 v3.0.15 h1:F0I1zds+K/+37ZrzdADmx2Q44OFDOPRLhPnNTaUX9hk= +github.com/pion/sdp/v3 v3.0.15/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= github.com/pion/srtp v1.3.1/go.mod h1:nxEytDDGTN+eNKJ1l5gzOCWQFuksgijorsSlgEjc40Y= github.com/pion/srtp v1.3.2/go.mod h1:snPrfN+gVpRBpmats49oxLWfcFB01eH1N9F+N7+dxKI= github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4= @@ -1234,8 +1236,8 @@ github.com/ucwong/filecache v1.0.6-0.20230405163841-810d53ced4bd h1:gBtlvLAsgLk+ github.com/ucwong/filecache v1.0.6-0.20230405163841-810d53ced4bd/go.mod h1:ddwX+NCjMZPdpzcGh1fcEbNTUTCtKgt2hC2rqvmLKgA= github.com/ucwong/go-ttlmap v1.0.2-0.20221020173635-331e7ddde2bb h1:dVZH3AH9f7zB3VBmsjn25B7lfcAyMP4QxdFYTrfj7tg= github.com/ucwong/go-ttlmap v1.0.2-0.20221020173635-331e7ddde2bb/go.mod h1:3yswsBsVuwsOjDvFfC5Na9XSEf4HC7mj3W3g6jvSY/s= -github.com/ucwong/golang-kv v1.0.24-0.20250606215932-9b6cb1d9814d h1:3BNfWe/Cc/y2qCcA1nhtQMgNn3NDjabLmlgLk8G96vQ= -github.com/ucwong/golang-kv v1.0.24-0.20250606215932-9b6cb1d9814d/go.mod h1:HWWm3Ql1JBUKlCtkPIhbdT/BWNVxnZfxLOy10AXsffs= +github.com/ucwong/golang-kv v1.0.24-0.20250720145037-4c149701f3b8 h1:jzJSJj2kUxaeuSkETrDSXtQAdhU+PLrv6F7qNxrG2s4= +github.com/ucwong/golang-kv v1.0.24-0.20250720145037-4c149701f3b8/go.mod h1:Vm1h6i/FxlEldhi+B51VulzxNqCZGkYn2UskuRFVvxk= github.com/ucwong/golang-set v1.8.1-0.20200419153428-d7b0b1ac2d43/go.mod h1:xu0FaiQFGbBcFZj2o7udZ5rbA8jRTsv47hkPoG5qQNM= github.com/ucwong/goleveldb v1.0.3-0.20200508074755-578cba616f37/go.mod h1:dgJUTtDxq/ne6/JzZhHzF24OL/uqILz9IWk8HmT4V2g= github.com/ucwong/goleveldb v1.0.3-0.20200618184106-f1c6bc3a428b/go.mod h1:7Sq6w7AfEZuB/a6mrlvHCSXCSkqojCMMrM3Ei12QAT0= @@ -1366,8 +1368,8 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20220428152302-39d4317da171/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc h1:TS73t7x3KarrNd5qAipmspBDS1rkMcgVG/fS1aRb4Rc= -golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= +golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4= +golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1730,7 +1732,6 @@ gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eR gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= -gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200316214253-d7b0ff38cac9/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns= diff --git a/node/api.go b/node/api.go index 48c17b5aa6..59fc198490 100644 --- a/node/api.go +++ b/node/api.go @@ -132,8 +132,6 @@ func (api *PrivateAdminAPI) PeerEvents(ctx context.Context) (*rpc.Subscription, return case <-rpcSub.Err(): return - case <-notifier.Closed(): - return } } }() diff --git a/node/endpoints.go b/node/endpoints.go index aca25c17ce..f88b9f7a75 100644 --- a/node/endpoints.go +++ b/node/endpoints.go @@ -76,8 +76,10 @@ func checkModuleAvailability(modules []string, apis []rpc.API) (bad, available [ } } for _, name := range modules { - if _, ok := availableSet[name]; !ok && name != rpc.MetadataAPI { - bad = append(bad, name) + if _, ok := availableSet[name]; !ok { + if name != rpc.MetadataApi && name != rpc.EngineApi { + bad = append(bad, name) + } } } return bad, available diff --git a/rpc/client.go b/rpc/client.go index 11d0edcc2f..06e3fb6047 100644 --- a/rpc/client.go +++ b/rpc/client.go @@ -1,5 +1,5 @@ // Copyright 2016 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -22,6 +22,7 @@ import ( "errors" "fmt" "net/url" + "os" "reflect" "strconv" "sync/atomic" @@ -31,15 +32,17 @@ import ( ) var ( + ErrBadResult = errors.New("bad result in JSON-RPC response") ErrClientQuit = errors.New("client is closed") - ErrNoResult = errors.New("no result in JSON-RPC response") + ErrNoResult = errors.New("JSON-RPC response has no result") + ErrMissingBatchResponse = errors.New("response batch did not contain a response to this call") ErrSubscriptionQueueOverflow = errors.New("subscription queue overflow") errClientReconnected = errors.New("client reconnected") errDead = errors.New("connection lost") ) +// Timeouts const ( - // Timeouts defaultDialTimeout = 10 * time.Second // used if context has no deadline subscribeTimeout = 10 * time.Second // overall timeout ctxc_subscribe, rpc_modules calls unsubscribeTimeout = 10 * time.Second // timeout for *_unsubscribe calls @@ -59,29 +62,23 @@ const ( maxClientSubscriptionBuffer = 20000 ) -const ( - httpScheme = "http" - wsScheme = "ws" - ipcScheme = "ipc" -) - // BatchElem is an element in a batch request. type BatchElem struct { Method string - Args []any + Args []interface{} // The result is unmarshaled into this field. Result must be set to a // non-nil pointer value of the desired type, otherwise the response will be // discarded. - Result any + Result interface{} // Error is set if the server returns an error for this request, or if - // unmarshaling into Result fails. It is not set for I/O errors. + // unmarshalling into Result fails. It is not set for I/O errors. Error error } // Client represents a connection to an RPC server. type Client struct { idgen func() ID // for subscriptions - scheme string // connection type: http, ws or ipc + isHTTP bool // connection type: http, ws or ipc services *serviceRegistry idCounter atomic.Uint32 @@ -89,6 +86,10 @@ type Client struct { // This function, if non-nil, is called when the connection is lost. reconnectFunc reconnectFunc + // config fields + batchItemLimit int + batchResponseMaxSize int + // writeConn is used for writing to the connection on the caller's goroutine. It should // only be accessed outside of dispatch, with the write lock held. The write lock is // taken by sending on reqInit and released by sending on reqSent. @@ -106,7 +107,7 @@ type Client struct { reqTimeout chan *requestOp // removes response IDs when call timeout expires } -type reconnectFunc func(ctx context.Context) (ServerCodec, error) +type reconnectFunc func(context.Context) (ServerCodec, error) type clientContextKey struct{} @@ -116,12 +117,10 @@ type clientConn struct { } func (c *Client) newClientConn(conn ServerCodec) *clientConn { - ctx := context.WithValue(context.Background(), clientContextKey{}, c) - // Http connections have already set the scheme - if !c.isHTTP() && c.scheme != "" { - ctx = context.WithValue(ctx, "scheme", c.scheme) - } - handler := newHandler(ctx, conn, c.idgen, c.services) + ctx := context.Background() + ctx = context.WithValue(ctx, clientContextKey{}, c) + ctx = context.WithValue(ctx, peerInfoContextKey{}, conn.peerInfo()) + handler := newHandler(ctx, conn, c.idgen, c.services, c.batchItemLimit, c.batchResponseMaxSize) return &clientConn{conn, handler} } @@ -135,18 +134,21 @@ type readOp struct { batch bool } +// requestOp represents a pending request. This is used for both batch and non-batch +// requests. type requestOp struct { - ids []json.RawMessage - err error - resp chan *jsonrpcMessage // receives up to len(ids) responses - sub *ClientSubscription // only set for CortexSubscribe requests + ids []json.RawMessage + err error + resp chan []*jsonrpcMessage // the response goes here + sub *ClientSubscription // set for Subscribe requests. + hadResponse bool // true when the request was responded to } -func (op *requestOp) wait(ctx context.Context, c *Client) (*jsonrpcMessage, error) { +func (op *requestOp) wait(ctx context.Context, c *Client) ([]*jsonrpcMessage, error) { select { case <-ctx.Done(): // Send the timeout to dispatch so it can remove the request IDs. - if !c.isHTTP() { + if !c.isHTTP { select { case c.reqTimeout <- op: case <-c.closing: @@ -162,14 +164,16 @@ func (op *requestOp) wait(ctx context.Context, c *Client) (*jsonrpcMessage, erro // // The currently supported URL schemes are "http", "https", "ws" and "wss". If rawurl is a // file name with no URL scheme, a local socket connection is established using UNIX -// domain sockets on supported platforms and named pipes on Windows. If you want to -// configure transport options, use DialHTTP, DialWebsocket or DialIPC instead. +// domain sockets on supported platforms and named pipes on Windows. +// +// If you want to further configure the transport, use DialOptions instead of this +// function. // // For websocket connections, the origin is set to the local host name. // -// The client reconnects automatically if the connection is lost. +// The client reconnects automatically when the connection is lost. func Dial(rawurl string) (*Client, error) { - return DialContext(context.Background(), rawurl) + return DialOptions(context.Background(), rawurl) } // DialContext creates a new RPC client, just like Dial. @@ -177,67 +181,92 @@ func Dial(rawurl string) (*Client, error) { // The context is used to cancel or time out the initial connection establishment. It does // not affect subsequent interactions with the client. func DialContext(ctx context.Context, rawurl string) (*Client, error) { + return DialOptions(ctx, rawurl) +} + +// DialOptions creates a new RPC client for the given URL. You can supply any of the +// pre-defined client options to configure the underlying transport. +// +// The context is used to cancel or time out the initial connection establishment. It does +// not affect subsequent interactions with the client. +// +// The client reconnects automatically when the connection is lost. +func DialOptions(ctx context.Context, rawurl string, options ...ClientOption) (*Client, error) { u, err := url.Parse(rawurl) if err != nil { return nil, err } + + cfg := new(clientConfig) + for _, opt := range options { + opt.applyOption(cfg) + } + + var reconnect reconnectFunc switch u.Scheme { case "http", "https": - return DialHTTP(rawurl) + reconnect = newClientTransportHTTP(rawurl, cfg) case "ws", "wss": - return DialWebsocket(ctx, rawurl, "") + rc, err := newClientTransportWS(rawurl, cfg) + if err != nil { + return nil, err + } + reconnect = rc case "stdio": - return DialStdIO(ctx) + reconnect = newClientTransportIO(os.Stdin, os.Stdout) case "": - return DialIPC(ctx, rawurl) + reconnect = newClientTransportIPC(rawurl) default: return nil, fmt.Errorf("no known transport for URL scheme %q", u.Scheme) } + + return newClient(ctx, cfg, reconnect) } -// Client retrieves the client from the context, if any. This can be used to perform +// ClientFromContext retrieves the client from the context, if any. This can be used to perform // 'reverse calls' in a handler method. func ClientFromContext(ctx context.Context) (*Client, bool) { client, ok := ctx.Value(clientContextKey{}).(*Client) return client, ok } -func newClient(initctx context.Context, connect reconnectFunc) (*Client, error) { +func newClient(initctx context.Context, cfg *clientConfig, connect reconnectFunc) (*Client, error) { conn, err := connect(initctx) if err != nil { return nil, err } - c := initClient(conn, randomIDGenerator(), new(serviceRegistry)) + c := initClient(conn, new(serviceRegistry), cfg) c.reconnectFunc = connect return c, nil } -func initClient(conn ServerCodec, idgen func() ID, services *serviceRegistry) *Client { - scheme := "" - switch conn.(type) { - case *httpConn: - scheme = httpScheme - case *websocketCodec: - scheme = wsScheme - case *jsonCodec: - scheme = ipcScheme - } +func initClient(conn ServerCodec, services *serviceRegistry, cfg *clientConfig) *Client { + _, isHTTP := conn.(*httpConn) c := &Client{ - idgen: idgen, - scheme: scheme, - services: services, - writeConn: conn, - close: make(chan struct{}), - closing: make(chan struct{}), - didClose: make(chan struct{}), - reconnected: make(chan ServerCodec), - readOp: make(chan readOp), - readErr: make(chan error), - reqInit: make(chan *requestOp), - reqSent: make(chan error, 1), - reqTimeout: make(chan *requestOp), - } - if !c.isHTTP() { + isHTTP: isHTTP, + services: services, + idgen: cfg.idgen, + batchItemLimit: cfg.batchItemLimit, + batchResponseMaxSize: cfg.batchResponseLimit, + writeConn: conn, + close: make(chan struct{}), + closing: make(chan struct{}), + didClose: make(chan struct{}), + reconnected: make(chan ServerCodec), + readOp: make(chan readOp), + readErr: make(chan error), + reqInit: make(chan *requestOp), + reqSent: make(chan error, 1), + reqTimeout: make(chan *requestOp), + } + + // Set defaults. + if c.idgen == nil { + c.idgen = randomIDGenerator() + } + + // Launch the main loop. + if !isHTTP { go c.dispatch(conn) } return c @@ -247,7 +276,7 @@ func initClient(conn ServerCodec, idgen func() ID, services *serviceRegistry) *C // methods on the given receiver match the criteria to be either a RPC method or a // subscription an error is returned. Otherwise a new service is created and added to the // service collection this client provides to the server. -func (c *Client) RegisterName(name string, receiver any) error { +func (c *Client) RegisterName(name string, receiver interface{}) error { return c.services.registerName(name, receiver) } @@ -268,7 +297,7 @@ func (c *Client) SupportedModules() (map[string]string, error) { // Close closes the client, aborting any in-flight requests. func (c *Client) Close() { - if c.isHTTP() { + if c.isHTTP { return } select { @@ -282,7 +311,7 @@ func (c *Client) Close() { // This method only works for clients using HTTP, it doesn't have // any effect for clients using another transport. func (c *Client) SetHeader(key, value string) { - if !c.isHTTP() { + if !c.isHTTP { return } conn := c.writeConn.(*httpConn) @@ -296,7 +325,7 @@ func (c *Client) SetHeader(key, value string) { // // The result must be a pointer so that package json can unmarshal into it. You // can also pass nil, in which case the result is ignored. -func (c *Client) Call(result any, method string, args ...any) error { +func (c *Client) Call(result interface{}, method string, args ...interface{}) error { ctx := context.Background() return c.CallContext(ctx, result, method, args...) } @@ -306,7 +335,7 @@ func (c *Client) Call(result any, method string, args ...any) error { // // The result must be a pointer so that package json can unmarshal into it. You // can also pass nil, in which case the result is ignored. -func (c *Client) CallContext(ctx context.Context, result any, method string, args ...any) error { +func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { if result != nil && reflect.TypeOf(result).Kind() != reflect.Ptr { return fmt.Errorf("call result parameter must be pointer or nil interface: %v", result) } @@ -314,9 +343,12 @@ func (c *Client) CallContext(ctx context.Context, result any, method string, arg if err != nil { return err } - op := &requestOp{ids: []json.RawMessage{msg.ID}, resp: make(chan *jsonrpcMessage, 1)} + op := &requestOp{ + ids: []json.RawMessage{msg.ID}, + resp: make(chan []*jsonrpcMessage, 1), + } - if c.isHTTP() { + if c.isHTTP { err = c.sendHTTP(ctx, op, msg) } else { err = c.send(ctx, op, msg) @@ -326,9 +358,12 @@ func (c *Client) CallContext(ctx context.Context, result any, method string, arg } // dispatch has accepted the request and will close the channel when it quits. - switch resp, err := op.wait(ctx, c); { - case err != nil: + batchresp, err := op.wait(ctx, c) + if err != nil { return err + } + resp := batchresp[0] + switch { case resp.Error != nil: return resp.Error case len(resp.Result) == 0: @@ -353,7 +388,7 @@ func (c *Client) BatchCall(b []BatchElem) error { return c.BatchCallContext(ctx, b) } -// BatchCall sends all given requests as a single batch and waits for the server +// BatchCallContext sends all given requests as a single batch and waits for the server // to return a response for all of them. The wait duration is bounded by the // context's deadline. // @@ -369,7 +404,7 @@ func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error { ) op := &requestOp{ ids: make([]json.RawMessage, len(b)), - resp: make(chan *jsonrpcMessage, len(b)), + resp: make(chan []*jsonrpcMessage, 1), } for i, elem := range b { msg, err := c.newMessage(elem.Method, elem.Args...) @@ -382,38 +417,58 @@ func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error { } var err error - if c.isHTTP() { + if c.isHTTP { err = c.sendBatchHTTP(ctx, op, msgs) } else { err = c.send(ctx, op, msgs) } + if err != nil { + return err + } + + batchresp, err := op.wait(ctx, c) + if err != nil { + return err + } // Wait for all responses to come back. - for n := 0; n < len(b) && err == nil; n++ { - var resp *jsonrpcMessage - resp, err = op.wait(ctx, c) - if err != nil { - break + for n := 0; n < len(batchresp); n++ { + resp := batchresp[n] + if resp == nil { + // Ignore null responses. These can happen for batches sent via HTTP. + continue } + // Find the element corresponding to this response. - // The element is guaranteed to be present because dispatch - // only sends valid IDs to our channel. - elem := &b[byID[string(resp.ID)]] - if resp.Error != nil { - elem.Error = resp.Error + index, ok := byID[string(resp.ID)] + if !ok { continue } - if len(resp.Result) == 0 { + delete(byID, string(resp.ID)) + + // Assign result and error. + elem := &b[index] + switch { + case resp.Error != nil: + elem.Error = resp.Error + case resp.Result == nil: elem.Error = ErrNoResult - continue + default: + elem.Error = json.Unmarshal(resp.Result, elem.Result) } - elem.Error = json.Unmarshal(resp.Result, elem.Result) } + + // Check that all expected responses have been received. + for _, index := range byID { + elem := &b[index] + elem.Error = ErrMissingBatchResponse + } + return err } // Notify sends a notification, i.e. a method call that doesn't expect a response. -func (c *Client) Notify(ctx context.Context, method string, args ...any) error { +func (c *Client) Notify(ctx context.Context, method string, args ...interface{}) error { op := new(requestOp) msg, err := c.newMessage(method, args...) if err != nil { @@ -421,14 +476,14 @@ func (c *Client) Notify(ctx context.Context, method string, args ...any) error { } msg.ID = nil - if c.isHTTP() { + if c.isHTTP { return c.sendHTTP(ctx, op, msg) } return c.send(ctx, op, msg) } -// CortexSubscribe registers a subscripion under the "ctxc" namespace. -func (c *Client) CortexSubscribe(ctx context.Context, channel any, args ...any) (*ClientSubscription, error) { +// CortexSubscribe registers a subscription under the "eth" namespace. +func (c *Client) CortexSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) { return c.Subscribe(ctx, "ctxc", channel, args...) } @@ -449,16 +504,16 @@ func (c *Client) ShhSubscribe(ctx context.Context, channel any, args ...any) (*C // before considering the subscriber dead. The subscription Err channel will receive // ErrSubscriptionQueueOverflow. Use a sufficiently large buffer on the channel or ensure // that the channel usually has at least one reader to prevent this issue. -func (c *Client) Subscribe(ctx context.Context, namespace string, channel any, args ...any) (*ClientSubscription, error) { +func (c *Client) Subscribe(ctx context.Context, namespace string, channel interface{}, args ...interface{}) (*ClientSubscription, error) { // Check type of channel first. chanVal := reflect.ValueOf(channel) if chanVal.Kind() != reflect.Chan || chanVal.Type().ChanDir()&reflect.SendDir == 0 { - panic("first argument to Subscribe must be a writable channel") + panic(fmt.Sprintf("channel argument of Subscribe has type %T, need writable channel", channel)) } if chanVal.IsNil() { panic("channel given to Subscribe must not be nil") } - if c.isHTTP() { + if c.isHTTP { return nil, ErrNotificationsUnsupported } @@ -468,7 +523,7 @@ func (c *Client) Subscribe(ctx context.Context, namespace string, channel any, a } op := &requestOp{ ids: []json.RawMessage{msg.ID}, - resp: make(chan *jsonrpcMessage), + resp: make(chan []*jsonrpcMessage, 1), sub: newClientSubscription(c, namespace, chanVal), } @@ -487,11 +542,10 @@ func (c *Client) Subscribe(ctx context.Context, namespace string, channel any, a // transport. When this returns false, Subscribe and related methods will return // ErrNotificationsUnsupported. func (c *Client) SupportsSubscriptions() bool { - //return !c.isHTTP - return false + return !c.isHTTP } -func (c *Client) newMessage(method string, paramsIn ...any) (*jsonrpcMessage, error) { +func (c *Client) newMessage(method string, paramsIn ...interface{}) (*jsonrpcMessage, error) { msg := &jsonrpcMessage{Version: vsn, ID: c.nextID(), Method: method} if paramsIn != nil { // prevent sending "params":null var err error @@ -504,7 +558,7 @@ func (c *Client) newMessage(method string, paramsIn ...any) (*jsonrpcMessage, er // send registers op with the dispatch loop, then sends msg on the connection. // if sending fails, op is deregistered. -func (c *Client) send(ctx context.Context, op *requestOp, msg any) error { +func (c *Client) send(ctx context.Context, op *requestOp, msg interface{}) error { select { case c.reqInit <- op: err := c.write(ctx, msg, false) @@ -519,9 +573,9 @@ func (c *Client) send(ctx context.Context, op *requestOp, msg any) error { } } -func (c *Client) write(ctx context.Context, msg any, retry bool) error { - // The previous write failed. Try to establish a new connection. +func (c *Client) write(ctx context.Context, msg interface{}, retry bool) error { if c.writeConn == nil { + // The previous write failed. Try to establish a new connection. if err := c.reconnect(ctx); err != nil { return err } @@ -669,7 +723,3 @@ func (c *Client) read(codec ServerCodec) { c.readOp <- readOp{msgs, batch} } } - -func (c *Client) isHTTP() bool { - return c.scheme == httpScheme -} diff --git a/rpc/client_example_test.go b/rpc/client_example_test.go index fd3e5553e3..dd1f4a76f3 100644 --- a/rpc/client_example_test.go +++ b/rpc/client_example_test.go @@ -1,5 +1,5 @@ // Copyright 2016 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc_test @@ -28,10 +28,10 @@ import ( // In this example, our client wishes to track the latest 'block number' // known to the server. The server supports two methods: // -// ctxc_getBlockByNumber("latest", {}) +// eth_getBlockByNumber("latest", {}) // returns the latest block object. // -// ctxc_subscribe("newHeads") +// eth_subscribe("newHeads") // creates a subscription which fires block objects when new blocks arrive. type Block struct { @@ -75,7 +75,7 @@ func subscribeBlocks(client *rpc.Client, subch chan Block) { // The connection is established now. // Update the channel with the current block. var lastBlock Block - err = client.CallContext(ctx, &lastBlock, "ctxc_getBlockByNumber", "latest", false) + err = client.CallContext(ctx, &lastBlock, "eth_getBlockByNumber", "latest", false) if err != nil { fmt.Println("can't get latest block:", err) return diff --git a/rpc/client_opt.go b/rpc/client_opt.go new file mode 100644 index 0000000000..3fa045a9b9 --- /dev/null +++ b/rpc/client_opt.go @@ -0,0 +1,144 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "net/http" + + "github.com/gorilla/websocket" +) + +// ClientOption is a configuration option for the RPC client. +type ClientOption interface { + applyOption(*clientConfig) +} + +type clientConfig struct { + // HTTP settings + httpClient *http.Client + httpHeaders http.Header + httpAuth HTTPAuth + + // WebSocket options + wsDialer *websocket.Dialer + wsMessageSizeLimit *int64 // wsMessageSizeLimit nil = default, 0 = no limit + + // RPC handler options + idgen func() ID + batchItemLimit int + batchResponseLimit int +} + +func (cfg *clientConfig) initHeaders() { + if cfg.httpHeaders == nil { + cfg.httpHeaders = make(http.Header) + } +} + +func (cfg *clientConfig) setHeader(key, value string) { + cfg.initHeaders() + cfg.httpHeaders.Set(key, value) +} + +type optionFunc func(*clientConfig) + +func (fn optionFunc) applyOption(opt *clientConfig) { + fn(opt) +} + +// WithWebsocketDialer configures the websocket.Dialer used by the RPC client. +func WithWebsocketDialer(dialer websocket.Dialer) ClientOption { + return optionFunc(func(cfg *clientConfig) { + cfg.wsDialer = &dialer + }) +} + +// WithWebsocketMessageSizeLimit configures the websocket message size limit used by the RPC +// client. Passing a limit of 0 means no limit. +func WithWebsocketMessageSizeLimit(messageSizeLimit int64) ClientOption { + return optionFunc(func(cfg *clientConfig) { + cfg.wsMessageSizeLimit = &messageSizeLimit + }) +} + +// WithHeader configures HTTP headers set by the RPC client. Headers set using this option +// will be used for both HTTP and WebSocket connections. +func WithHeader(key, value string) ClientOption { + return optionFunc(func(cfg *clientConfig) { + cfg.initHeaders() + cfg.httpHeaders.Set(key, value) + }) +} + +// WithHeaders configures HTTP headers set by the RPC client. Headers set using this +// option will be used for both HTTP and WebSocket connections. +func WithHeaders(headers http.Header) ClientOption { + return optionFunc(func(cfg *clientConfig) { + cfg.initHeaders() + for k, vs := range headers { + cfg.httpHeaders[k] = vs + } + }) +} + +// WithHTTPClient configures the http.Client used by the RPC client. +func WithHTTPClient(c *http.Client) ClientOption { + return optionFunc(func(cfg *clientConfig) { + cfg.httpClient = c + }) +} + +// WithHTTPAuth configures HTTP request authentication. The given provider will be called +// whenever a request is made. Note that only one authentication provider can be active at +// any time. +func WithHTTPAuth(a HTTPAuth) ClientOption { + if a == nil { + panic("nil auth") + } + return optionFunc(func(cfg *clientConfig) { + cfg.httpAuth = a + }) +} + +// A HTTPAuth function is called by the client whenever a HTTP request is sent. +// The function must be safe for concurrent use. +// +// Usually, HTTPAuth functions will call h.Set("authorization", "...") to add +// auth information to the request. +type HTTPAuth func(h http.Header) error + +// WithBatchItemLimit changes the maximum number of items allowed in batch requests. +// +// Note: this option applies when processing incoming batch requests. It does not affect +// batch requests sent by the client. +func WithBatchItemLimit(limit int) ClientOption { + return optionFunc(func(cfg *clientConfig) { + cfg.batchItemLimit = limit + }) +} + +// WithBatchResponseSizeLimit changes the maximum number of response bytes that can be +// generated for batch requests. When this limit is reached, further calls in the batch +// will not be processed. +// +// Note: this option applies when processing incoming batch requests. It does not affect +// batch requests sent by the client. +func WithBatchResponseSizeLimit(sizeLimit int) ClientOption { + return optionFunc(func(cfg *clientConfig) { + cfg.batchResponseLimit = sizeLimit + }) +} diff --git a/rpc/client_opt_test.go b/rpc/client_opt_test.go new file mode 100644 index 0000000000..e9197db134 --- /dev/null +++ b/rpc/client_opt_test.go @@ -0,0 +1,41 @@ +// Copyright 2022 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc_test + +import ( + "context" + "net/http" + "time" + + "github.com/CortexFoundation/CortexTheseus/rpc" +) + +// This example configures a HTTP-based RPC client with two options - one setting the +// overall request timeout, the other adding a custom HTTP header to all requests. +func ExampleDialOptions() { + tokenHeader := rpc.WithHeader("x-token", "foo") + httpClient := rpc.WithHTTPClient(&http.Client{ + Timeout: 10 * time.Second, + }) + + ctx := context.Background() + c, err := rpc.DialOptions(ctx, "http://rpc.example.com", httpClient, tokenHeader) + if err != nil { + panic(err) + } + c.Close() +} diff --git a/rpc/client_test.go b/rpc/client_test.go index d46f90100a..c463141b59 100644 --- a/rpc/client_test.go +++ b/rpc/client_test.go @@ -1,5 +1,5 @@ // Copyright 2016 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,13 +12,14 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc import ( "context" "encoding/json" + "errors" "fmt" "math/rand" "net" @@ -37,6 +38,8 @@ import ( ) func TestClientRequest(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() client := DialInProc(server) @@ -52,6 +55,8 @@ func TestClientRequest(t *testing.T) { } func TestClientResponseType(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() client := DialInProc(server) @@ -68,25 +73,53 @@ func TestClientResponseType(t *testing.T) { } } +// This test checks calling a method that returns 'null'. +func TestClientNullResponse(t *testing.T) { + t.Parallel() + + server := newTestServer() + defer server.Stop() + + client := DialInProc(server) + defer client.Close() + + var result json.RawMessage + if err := client.Call(&result, "test_null"); err != nil { + t.Fatal(err) + } + if result == nil { + t.Fatal("Expected non-nil result") + } + if !reflect.DeepEqual(result, json.RawMessage("null")) { + t.Errorf("Expected null, got %s", result) + } +} + // This test checks that server-returned errors with code and data come out of Client.Call. func TestClientErrorData(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() client := DialInProc(server) defer client.Close() - var resp any + var resp interface{} err := client.Call(&resp, "test_returnError") if err == nil { t.Fatal("expected error") } // Check code. + // The method handler returns an error value which implements the rpc.Error + // interface, i.e. it has a custom error code. The server returns this error code. + expectedCode := testError{}.ErrorCode() if e, ok := err.(Error); !ok { t.Fatalf("client did not return rpc.Error, got %#v", e) - } else if e.ErrorCode() != (testError{}.ErrorCode()) { - t.Fatalf("wrong error code %d, want %d", e.ErrorCode(), testError{}.ErrorCode()) + } else if e.ErrorCode() != expectedCode { + t.Fatalf("wrong error code %d, want %d", e.ErrorCode(), expectedCode) } + // Check data. if e, ok := err.(DataError); !ok { t.Fatalf("client did not return rpc.DataError, got %#v", e) @@ -96,6 +129,8 @@ func TestClientErrorData(t *testing.T) { } func TestClientBatchRequest(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() client := DialInProc(server) @@ -104,17 +139,17 @@ func TestClientBatchRequest(t *testing.T) { batch := []BatchElem{ { Method: "test_echo", - Args: []any{"hello", 10, &echoArgs{"world"}}, + Args: []interface{}{"hello", 10, &echoArgs{"world"}}, Result: new(echoResult), }, { Method: "test_echo", - Args: []any{"hello2", 11, &echoArgs{"world"}}, + Args: []interface{}{"hello2", 11, &echoArgs{"world"}}, Result: new(echoResult), }, { Method: "no_such_method", - Args: []any{1, 2, 3}, + Args: []interface{}{1, 2, 3}, Result: new(int), }, } @@ -124,17 +159,17 @@ func TestClientBatchRequest(t *testing.T) { wantResult := []BatchElem{ { Method: "test_echo", - Args: []any{"hello", 10, &echoArgs{"world"}}, + Args: []interface{}{"hello", 10, &echoArgs{"world"}}, Result: &echoResult{"hello", 10, &echoArgs{"world"}}, }, { Method: "test_echo", - Args: []any{"hello2", 11, &echoArgs{"world"}}, + Args: []interface{}{"hello2", 11, &echoArgs{"world"}}, Result: &echoResult{"hello2", 11, &echoArgs{"world"}}, }, { Method: "no_such_method", - Args: []any{1, 2, 3}, + Args: []interface{}{1, 2, 3}, Result: new(int), Error: &jsonError{Code: -32601, Message: "the method no_such_method does not exist/is not available"}, }, @@ -144,7 +179,132 @@ func TestClientBatchRequest(t *testing.T) { } } +// This checks that, for HTTP connections, the length of batch responses is validated to +// match the request exactly. +func TestClientBatchRequest_len(t *testing.T) { + t.Parallel() + + b, err := json.Marshal([]jsonrpcMessage{ + {Version: "2.0", ID: json.RawMessage("1"), Result: json.RawMessage(`"0x1"`)}, + {Version: "2.0", ID: json.RawMessage("2"), Result: json.RawMessage(`"0x2"`)}, + }) + if err != nil { + t.Fatal("failed to encode jsonrpc message:", err) + } + s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + _, err := rw.Write(b) + if err != nil { + t.Error("failed to write response:", err) + } + })) + t.Cleanup(s.Close) + + t.Run("too-few", func(t *testing.T) { + t.Parallel() + + client, err := Dial(s.URL) + if err != nil { + t.Fatal("failed to dial test server:", err) + } + defer client.Close() + + batch := []BatchElem{ + {Method: "foo", Result: new(string)}, + {Method: "bar", Result: new(string)}, + {Method: "baz", Result: new(string)}, + } + ctx, cancelFn := context.WithTimeout(context.Background(), time.Second) + defer cancelFn() + + if err := client.BatchCallContext(ctx, batch); err != nil { + t.Fatal("error:", err) + } + for i, elem := range batch[:2] { + if elem.Error != nil { + t.Errorf("expected no error for batch element %d, got %q", i, elem.Error) + } + } + for i, elem := range batch[2:] { + if elem.Error != ErrMissingBatchResponse { + t.Errorf("wrong error %q for batch element %d", elem.Error, i+2) + } + } + }) + + t.Run("too-many", func(t *testing.T) { + t.Parallel() + + client, err := Dial(s.URL) + if err != nil { + t.Fatal("failed to dial test server:", err) + } + defer client.Close() + + batch := []BatchElem{ + {Method: "foo", Result: new(string)}, + } + ctx, cancelFn := context.WithTimeout(context.Background(), time.Second) + defer cancelFn() + + if err := client.BatchCallContext(ctx, batch); err != nil { + t.Fatal("error:", err) + } + for i, elem := range batch[:1] { + if elem.Error != nil { + t.Errorf("expected no error for batch element %d, got %q", i, elem.Error) + } + } + for i, elem := range batch[1:] { + if elem.Error != ErrMissingBatchResponse { + t.Errorf("wrong error %q for batch element %d", elem.Error, i+2) + } + } + }) +} + +// This checks that the client can handle the case where the server doesn't +// respond to all requests in a batch. +func TestClientBatchRequestLimit(t *testing.T) { + t.Parallel() + + server := newTestServer() + defer server.Stop() + server.SetBatchLimits(2, 100000) + client := DialInProc(server) + defer client.Close() + + batch := []BatchElem{ + {Method: "foo"}, + {Method: "bar"}, + {Method: "baz"}, + } + err := client.BatchCall(batch) + if err != nil { + t.Fatal("unexpected error:", err) + } + + // Check that the first response indicates an error with batch size. + var err0 Error + if !errors.As(batch[0].Error, &err0) { + t.Log("error zero:", batch[0].Error) + t.Fatalf("batch elem 0 has wrong error type: %T", batch[0].Error) + } else { + if err0.ErrorCode() != -32600 || err0.Error() != errMsgBatchTooLarge { + t.Fatalf("wrong error on batch elem zero: %v", err0) + } + } + + // Check that remaining response batch elements are reported as absent. + for i, elem := range batch[1:] { + if elem.Error != ErrMissingBatchResponse { + t.Fatalf("batch elem %d has unexpected error: %v", i+1, elem.Error) + } + } +} + func TestClientNotify(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() client := DialInProc(server) @@ -203,6 +363,7 @@ func testClientCancel(transport string, t *testing.T) { default: panic("unknown transport: " + transport) } + defer client.Close() // The actual test starts here. var ( @@ -238,7 +399,7 @@ func testClientCancel(transport string, t *testing.T) { _, hasDeadline := ctx.Deadline() t.Errorf("no error for call with %v wait time (deadline: %v)", timeout, hasDeadline) // default: - // t.Logf("got expected error with %v wait time: %v", timeout, err) + // t.Logf("got expected error with %v wait time: %v", timeout, err) } cancel() } @@ -251,12 +412,14 @@ func testClientCancel(transport string, t *testing.T) { } func TestClientSubscribeInvalidArg(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() client := DialInProc(server) defer client.Close() - check := func(shouldPanic bool, arg any) { + check := func(shouldPanic bool, arg interface{}) { defer func() { err := recover() if shouldPanic && err == nil { @@ -281,6 +444,8 @@ func TestClientSubscribeInvalidArg(t *testing.T) { } func TestClientSubscribe(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() client := DialInProc(server) @@ -313,6 +478,8 @@ func TestClientSubscribe(t *testing.T) { // In this test, the connection drops while Subscribe is waiting for a response. func TestClientSubscribeClose(t *testing.T) { + t.Parallel() + server := newTestServer() service := ¬ificationTestService{ gotHangSubscriptionReq: make(chan struct{}), @@ -357,6 +524,8 @@ func TestClientSubscribeClose(t *testing.T) { // This test reproduces https://github.com/CortexFoundation/CortexTheseus/issues/17837 where the // client hangs during shutdown when Unsubscribe races with Client.Close. func TestClientCloseUnsubscribeRace(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() @@ -377,6 +546,72 @@ func TestClientCloseUnsubscribeRace(t *testing.T) { } } +// unsubscribeBlocker will wait for the quit channel to process an unsubscribe +// request. +type unsubscribeBlocker struct { + ServerCodec + quit chan struct{} +} + +func (b *unsubscribeBlocker) readBatch() ([]*jsonrpcMessage, bool, error) { + msgs, batch, err := b.ServerCodec.readBatch() + for _, msg := range msgs { + if msg.isUnsubscribe() { + <-b.quit + } + } + return msgs, batch, err +} + +// TestUnsubscribeTimeout verifies that calling the client's Unsubscribe +// function will eventually timeout and not block forever in case the serve does +// not respond. +// It reproducers the issue https://github.com/CortexFoundation/CortexTheseus/issues/30156 +func TestUnsubscribeTimeout(t *testing.T) { + t.Parallel() + + srv := NewServer() + srv.RegisterName("nftest", new(notificationTestService)) + + // Setup middleware to block on unsubscribe. + p1, p2 := net.Pipe() + blocker := &unsubscribeBlocker{ServerCodec: NewCodec(p1), quit: make(chan struct{})} + defer close(blocker.quit) + + // Serve the middleware. + go srv.ServeCodec(blocker, OptionMethodInvocation|OptionSubscriptions) + defer srv.Stop() + + // Create the client on the other end of the pipe. + cfg := new(clientConfig) + client, _ := newClient(context.Background(), cfg, func(context.Context) (ServerCodec, error) { + return NewCodec(p2), nil + }) + defer client.Close() + + // Start subscription. + sub, err := client.Subscribe(context.Background(), "nftest", make(chan int), "someSubscription", 1, 1) + if err != nil { + t.Fatalf("failed to subscribe: %v", err) + } + + // Now on a separate thread, attempt to unsubscribe. Since the middleware + // won't return, the function will only return if it times out on the request. + done := make(chan struct{}) + go func() { + sub.Unsubscribe() + done <- struct{}{} + }() + + // Wait for the timeout. If the expected time for the timeout elapses, the + // test is considered failed. + select { + case <-done: + case <-time.After(unsubscribeTimeout + 3*time.Second): + t.Fatalf("Unsubscribe did not return within %s", unsubscribeTimeout) + } +} + // unsubscribeRecorder collects the subscription IDs of *_unsubscribe calls. type unsubscribeRecorder struct { ServerCodec @@ -415,7 +650,8 @@ func TestClientSubscriptionUnsubscribeServer(t *testing.T) { defer srv.Stop() // Create the client on the other end of the pipe. - client, _ := newClient(context.Background(), func(context.Context) (ServerCodec, error) { + cfg := new(clientConfig) + client, _ := newClient(context.Background(), cfg, func(context.Context) (ServerCodec, error) { return NewCodec(p2), nil }) defer client.Close() @@ -452,6 +688,7 @@ func TestClientSubscriptionChannelClose(t *testing.T) { srv.RegisterName("nftest", new(notificationTestService)) client, _ := Dial(wsURL) + defer client.Close() for i := 0; i < 100; i++ { ch := make(chan int, 100) @@ -467,6 +704,8 @@ func TestClientSubscriptionChannelClose(t *testing.T) { // This test checks that Client doesn't lock up when a single subscriber // doesn't read subscription events. func TestClientNotificationStorm(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() @@ -515,10 +754,12 @@ func TestClientNotificationStorm(t *testing.T) { } doTest(8000, false) - doTest(23000, true) + doTest(24000, true) } func TestClientSetHeader(t *testing.T) { + t.Parallel() + var gotHeader bool srv := newTestServer() httpsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -555,6 +796,8 @@ func TestClientSetHeader(t *testing.T) { } func TestClientHTTP(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() @@ -568,9 +811,7 @@ func TestClientHTTP(t *testing.T) { errc = make(chan error, len(results)) wantResult = echoResult{"a", 1, new(echoArgs)} ) - defer client.Close() for i := range results { - i := i go func() { errc <- client.Call(&results[i], "test_echo", wantResult.String, wantResult.Int, wantResult.Args) }() @@ -599,6 +840,8 @@ func TestClientHTTP(t *testing.T) { } func TestClientReconnect(t *testing.T) { + t.Parallel() + startServer := func(addr string) (*Server, net.Listener) { srv := newTestServer() l, err := net.Listen("tcp", addr) @@ -618,6 +861,7 @@ func TestClientReconnect(t *testing.T) { if err != nil { t.Fatal("can't dial", err) } + defer client.Close() // Perform a call. This should work because the server is up. var resp echoResult @@ -692,7 +936,7 @@ func httpTestClient(srv *Server, transport string, fl *flakeyListener) (*Client, func ipcTestClient(srv *Server, fl *flakeyListener) (*Client, net.Listener) { // Listen on a random endpoint. - endpoint := fmt.Sprintf("CortexTheseus-test-ipc-%d-%d", os.Getpid(), rand.Int63()) + endpoint := fmt.Sprintf("cortex-test-ipc-%d-%d", os.Getpid(), rand.Int63()) if runtime.GOOS == "windows" { endpoint = `\\.\pipe\` + endpoint } else { diff --git a/rpc/constants_unix.go b/rpc/constants_unix.go deleted file mode 100644 index 723c0221a1..0000000000 --- a/rpc/constants_unix.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of The go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . - -//go:build darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris -// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris - -package rpc - -/* -#include - -int max_socket_path_size() { -struct sockaddr_un s; -return sizeof(s.sun_path); -} -*/ -import "C" - -var ( - max_path_size = C.max_socket_path_size() -) diff --git a/rpc/constants_unix_nocgo.go b/rpc/constants_unix_nocgo.go deleted file mode 100644 index a002773af8..0000000000 --- a/rpc/constants_unix_nocgo.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of The go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . - -//go:build !cgo && !windows -// +build !cgo,!windows - -package rpc - -var ( - // On Linux, sun_path is 108 bytes in size - // see http://man7.org/linux/man-pages/man7/unix.7.html - max_path_size = 108 -) diff --git a/rpc/context_headers.go b/rpc/context_headers.go index a743cde7d1..29a58150e3 100644 --- a/rpc/context_headers.go +++ b/rpc/context_headers.go @@ -1,5 +1,5 @@ // Copyright 2022 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc diff --git a/rpc/doc.go b/rpc/doc.go index 58b757dffb..69e7797098 100644 --- a/rpc/doc.go +++ b/rpc/doc.go @@ -1,5 +1,5 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . /* Package rpc implements bi-directional JSON-RPC 2.0 on multiple transports. diff --git a/rpc/endpoints.go b/rpc/endpoints.go index d53b0a3de5..dae70b6a7b 100644 --- a/rpc/endpoints.go +++ b/rpc/endpoints.go @@ -1,5 +1,5 @@ // Copyright 2018 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc diff --git a/rpc/errors.go b/rpc/errors.go index e94080de07..438aff218c 100644 --- a/rpc/errors.go +++ b/rpc/errors.go @@ -1,5 +1,5 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -41,8 +41,8 @@ type Error interface { // A DataError contains some data in addition to the error message. type DataError interface { - Error() string // returns the message - ErrorData() any // returns the error data + Error() string // returns the message + ErrorData() interface{} // returns the error data } // Error types defined below are the built-in JSON-RPC errors. @@ -54,6 +54,7 @@ var ( _ Error = new(invalidRequestError) _ Error = new(invalidMessageError) _ Error = new(invalidParamsError) + _ Error = new(internalServerError) ) const ( @@ -67,7 +68,9 @@ const ( ) const ( - errMsgTimeout = "request timed out" + errMsgTimeout = "request timed out" + errMsgResponseTooLarge = "response too large" + errMsgBatchTooLarge = "batch too large" ) type methodNotFoundError struct{ method string } @@ -121,6 +124,13 @@ func (e *parseError) ErrorCode() int { return -32700 } func (e *parseError) Error() string { return e.message } +// received message isn't a valid request +type invalidRequestError struct{ message string } + +func (e *invalidRequestError) ErrorCode() int { return -32600 } + +func (e *invalidRequestError) Error() string { return e.message } + // received message is invalid type invalidMessageError struct{ message string } @@ -131,15 +141,6 @@ func (e *invalidMessageError) Error() string { return e.message } // unable to decode supplied params, or an invalid number of parameters type invalidParamsError struct{ message string } -// received message isn't a valid request -type invalidRequestError struct{ message string } - -func (e *invalidRequestError) ErrorCode() int { return -32600 } - -func (e *invalidRequestError) Error() string { return e.message } - -// unable to decode supplied params, or an invalid number of parameters - func (e *invalidParamsError) ErrorCode() int { return -32602 } func (e *invalidParamsError) Error() string { return e.message } diff --git a/rpc/handler.go b/rpc/handler.go index d1ddd9f310..3dedabb98a 100644 --- a/rpc/handler.go +++ b/rpc/handler.go @@ -1,5 +1,5 @@ // Copyright 2019 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -49,20 +49,22 @@ import ( // Now send the request, then wait for the reply to be delivered through handleMsg: // // if err := op.wait(...); err != nil { -// h.removeRequestOp(op) // timeout, etc. +// h.removeRequestOp(op) // timeout, etc. // } type handler struct { - reg *serviceRegistry - unsubscribeCb *callback - idgen func() ID // subscription ID generator - respWait map[string]*requestOp // active client requests - clientSubs map[string]*ClientSubscription // active client subscriptions - callWG sync.WaitGroup // pending call goroutines - rootCtx context.Context // canceled by close() - cancelRoot func() // cancel function for rootCtx - conn jsonWriter // where responses will be sent - log log.Logger - allowSubscribe bool + reg *serviceRegistry + unsubscribeCb *callback + idgen func() ID // subscription ID generator + respWait map[string]*requestOp // active client requests + clientSubs map[string]*ClientSubscription // active client subscriptions + callWG sync.WaitGroup // pending call goroutines + rootCtx context.Context // canceled by close() + cancelRoot func() // cancel function for rootCtx + conn jsonWriter // where responses will be sent + log log.Logger + allowSubscribe bool + batchRequestLimit int + batchResponseMaxSize int subLock sync.Mutex serverSubs map[ID]*Subscription @@ -73,19 +75,21 @@ type callProc struct { notifiers []*Notifier } -func newHandler(connCtx context.Context, conn jsonWriter, idgen func() ID, reg *serviceRegistry) *handler { +func newHandler(connCtx context.Context, conn jsonWriter, idgen func() ID, reg *serviceRegistry, batchRequestLimit, batchResponseMaxSize int) *handler { rootCtx, cancelRoot := context.WithCancel(connCtx) h := &handler{ - reg: reg, - idgen: idgen, - conn: conn, - respWait: make(map[string]*requestOp), - clientSubs: make(map[string]*ClientSubscription), - rootCtx: rootCtx, - cancelRoot: cancelRoot, - allowSubscribe: true, - serverSubs: make(map[ID]*Subscription), - log: log.Root(), + reg: reg, + idgen: idgen, + conn: conn, + respWait: make(map[string]*requestOp), + clientSubs: make(map[string]*ClientSubscription), + rootCtx: rootCtx, + cancelRoot: cancelRoot, + allowSubscribe: true, + serverSubs: make(map[ID]*Subscription), + log: log.Root(), + batchRequestLimit: batchRequestLimit, + batchResponseMaxSize: batchResponseMaxSize, } if conn.remoteAddr() != "" { h.log = h.log.New("conn", conn.remoteAddr()) @@ -137,16 +141,15 @@ func (b *batchCallBuffer) write(ctx context.Context, conn jsonWriter) { b.doWrite(ctx, conn, false) } -// timeout sends the responses added so far. For the remaining unanswered call -// messages, it sends a timeout error response. -func (b *batchCallBuffer) timeout(ctx context.Context, conn jsonWriter) { +// respondWithError sends the responses added so far. For the remaining unanswered call +// messages, it responds with the given error. +func (b *batchCallBuffer) respondWithError(ctx context.Context, conn jsonWriter, err error) { b.mutex.Lock() defer b.mutex.Unlock() for _, msg := range b.calls { if !msg.isNotification() { - resp := msg.errorResponse(&internalServerError{errcodeTimeout, errMsgTimeout}) - b.resp = append(b.resp, resp) + b.resp = append(b.resp, msg.errorResponse(err)) } } b.doWrite(ctx, conn, true) @@ -174,17 +177,24 @@ func (h *handler) handleBatch(msgs []*jsonrpcMessage) { }) return } + // Apply limit on total number of requests. + if h.batchRequestLimit != 0 && len(msgs) > h.batchRequestLimit { + h.startCallProc(func(cp *callProc) { + h.respondWithBatchTooLarge(cp, msgs) + }) + return + } - // Handle non-call messages first: + // Handle non-call messages first. + // Here we need to find the requestOp that sent the request batch. calls := make([]*jsonrpcMessage, 0, len(msgs)) - for _, msg := range msgs { - if handled := h.handleImmediate(msg); !handled { - calls = append(calls, msg) - } - } + h.handleResponses(msgs, func(msg *jsonrpcMessage) { + calls = append(calls, msg) + }) if len(calls) == 0 { return } + // Process calls on a goroutine because they may block indefinitely: h.startCallProc(func(cp *callProc) { var ( @@ -202,10 +212,12 @@ func (h *handler) handleBatch(msgs []*jsonrpcMessage) { if timeout, ok := ContextRequestTimeout(cp.ctx); ok { timer = time.AfterFunc(timeout, func() { cancel() - callBuffer.timeout(cp.ctx, h.conn) + err := &internalServerError{errcodeTimeout, errMsgTimeout} + callBuffer.respondWithError(cp.ctx, h.conn, err) }) } + responseBytes := 0 for { // No need to handle rest of calls if timed out. if cp.ctx.Err() != nil { @@ -217,59 +229,86 @@ func (h *handler) handleBatch(msgs []*jsonrpcMessage) { } resp := h.handleCallMsg(cp, msg) callBuffer.pushResponse(resp) + if resp != nil && h.batchResponseMaxSize != 0 { + responseBytes += len(resp.Result) + if responseBytes > h.batchResponseMaxSize { + err := &internalServerError{errcodeResponseTooLarge, errMsgResponseTooLarge} + callBuffer.respondWithError(cp.ctx, h.conn, err) + break + } + } } if timer != nil { timer.Stop() } - callBuffer.write(cp.ctx, h.conn) + h.addSubscriptions(cp.notifiers) + callBuffer.write(cp.ctx, h.conn) for _, n := range cp.notifiers { n.activate() } }) } -// handleMsg handles a single message. -func (h *handler) handleMsg(msg *jsonrpcMessage) { - if ok := h.handleImmediate(msg); ok { - return +func (h *handler) respondWithBatchTooLarge(cp *callProc, batch []*jsonrpcMessage) { + resp := errorMessage(&invalidRequestError{errMsgBatchTooLarge}) + // Find the first call and add its "id" field to the error. + // This is the best we can do, given that the protocol doesn't have a way + // of reporting an error for the entire batch. + for _, msg := range batch { + if msg.isCall() { + resp.ID = msg.ID + break + } } - h.startCallProc(func(cp *callProc) { - var ( - responded sync.Once - timer *time.Timer - cancel context.CancelFunc - ) - cp.ctx, cancel = context.WithCancel(cp.ctx) - defer cancel() + h.conn.writeJSON(cp.ctx, []*jsonrpcMessage{resp}, true) +} - // Cancel the request context after timeout and send an error response. Since the - // running method might not return immediately on timeout, we must wait for the - // timeout concurrently with processing the request. - if timeout, ok := ContextRequestTimeout(cp.ctx); ok { - timer = time.AfterFunc(timeout, func() { - cancel() - responded.Do(func() { - resp := msg.errorResponse(&internalServerError{errcodeTimeout, errMsgTimeout}) - h.conn.writeJSON(cp.ctx, resp, true) - }) - }) - } +// handleMsg handles a single non-batch message. +func (h *handler) handleMsg(msg *jsonrpcMessage) { + msgs := []*jsonrpcMessage{msg} + h.handleResponses(msgs, func(msg *jsonrpcMessage) { + h.startCallProc(func(cp *callProc) { + h.handleNonBatchCall(cp, msg) + }) + }) +} - answer := h.handleCallMsg(cp, msg) - if timer != nil { - timer.Stop() - } - h.addSubscriptions(cp.notifiers) - if answer != nil { +func (h *handler) handleNonBatchCall(cp *callProc, msg *jsonrpcMessage) { + var ( + responded sync.Once + timer *time.Timer + cancel context.CancelFunc + ) + cp.ctx, cancel = context.WithCancel(cp.ctx) + defer cancel() + + // Cancel the request context after timeout and send an error response. Since the + // running method might not return immediately on timeout, we must wait for the + // timeout concurrently with processing the request. + if timeout, ok := ContextRequestTimeout(cp.ctx); ok { + timer = time.AfterFunc(timeout, func() { + cancel() responded.Do(func() { - h.conn.writeJSON(cp.ctx, answer, false) + resp := msg.errorResponse(&internalServerError{errcodeTimeout, errMsgTimeout}) + h.conn.writeJSON(cp.ctx, resp, true) }) - } - for _, n := range cp.notifiers { - n.activate() - } - }) + }) + } + + answer := h.handleCallMsg(cp, msg) + if timer != nil { + timer.Stop() + } + h.addSubscriptions(cp.notifiers) + if answer != nil { + responded.Do(func() { + h.conn.writeJSON(cp.ctx, answer, false) + }) + } + for _, n := range cp.notifiers { + n.activate() + } } // close cancels all requests except for inflightReq and waits for @@ -288,7 +327,7 @@ func (h *handler) addRequestOp(op *requestOp) { } } -// removeRequestOps stops waiting for the given request IDs. +// removeRequestOp stops waiting for the given request IDs. func (h *handler) removeRequestOp(op *requestOp) { for _, id := range op.ids { delete(h.respWait, string(id)) @@ -352,23 +391,60 @@ func (h *handler) startCallProc(fn func(*callProc)) { }() } -// handleImmediate executes non-call messages. It returns false if the message is a -// call or requires a reply. -func (h *handler) handleImmediate(msg *jsonrpcMessage) bool { - start := time.Now() - switch { - case msg.isNotification(): - if strings.HasSuffix(msg.Method, notificationMethodSuffix) { - h.handleSubscriptionResult(msg) - return true +// handleResponses processes method call responses. +func (h *handler) handleResponses(batch []*jsonrpcMessage, handleCall func(*jsonrpcMessage)) { + var resolvedops []*requestOp + handleResp := func(msg *jsonrpcMessage) { + op := h.respWait[string(msg.ID)] + if op == nil { + h.log.Debug("Unsolicited RPC response", "reqid", idForLog{msg.ID}) + return + } + resolvedops = append(resolvedops, op) + delete(h.respWait, string(msg.ID)) + + // For subscription responses, start the subscription if the server + // indicates success. CortexSubscribe gets unblocked in either case through + // the op.resp channel. + if op.sub != nil { + if msg.Error != nil { + op.err = msg.Error + } else { + op.err = json.Unmarshal(msg.Result, &op.sub.subid) + if op.err == nil { + go op.sub.run() + h.clientSubs[op.sub.subid] = op.sub + } + } + } + + if !op.hadResponse { + op.hadResponse = true + op.resp <- batch } - return false - case msg.isResponse(): - h.handleResponse(msg) - h.log.Trace("Handled RPC response", "reqid", idForLog{msg.ID}, "t", time.Since(start)) - return true - default: - return false + } + + for _, msg := range batch { + start := time.Now() + switch { + case msg.isResponse(): + handleResp(msg) + h.log.Trace("Handled RPC response", "reqid", idForLog{msg.ID}, "duration", time.Since(start)) + + case msg.isNotification(): + if strings.HasSuffix(msg.Method, notificationMethodSuffix) { + h.handleSubscriptionResult(msg) + continue + } + handleCall(msg) + + default: + handleCall(msg) + } + } + + for _, op := range resolvedops { + h.removeRequestOp(op) } } @@ -384,41 +460,15 @@ func (h *handler) handleSubscriptionResult(msg *jsonrpcMessage) { } } -// handleResponse processes method call responses. -func (h *handler) handleResponse(msg *jsonrpcMessage) { - op := h.respWait[string(msg.ID)] - if op == nil { - h.log.Debug("Unsolicited RPC response", "reqid", idForLog{msg.ID}) - return - } - delete(h.respWait, string(msg.ID)) - // For normal responses, just forward the reply to Call/BatchCall. - if op.sub == nil { - op.resp <- msg - return - } - // For subscription responses, start the subscription if the server - // indicates success. CortexSubscribe gets unblocked in either case through - // the op.resp channel. - defer close(op.resp) - if msg.Error != nil { - op.err = msg.Error - return - } - if op.err = json.Unmarshal(msg.Result, &op.sub.subid); op.err == nil { - go op.sub.run() - h.clientSubs[op.sub.subid] = op.sub - } -} - // handleCallMsg executes a call message and returns the answer. func (h *handler) handleCallMsg(ctx *callProc, msg *jsonrpcMessage) *jsonrpcMessage { start := time.Now() switch { case msg.isNotification(): h.handleCall(ctx, msg) - h.log.Debug("Served "+msg.Method, "t", time.Since(start)) + h.log.Debug("Served "+msg.Method, "duration", time.Since(start)) return nil + case msg.isCall(): resp := h.handleCall(ctx, msg) var logctx []any @@ -433,8 +483,10 @@ func (h *handler) handleCallMsg(ctx *callProc, msg *jsonrpcMessage) *jsonrpcMess h.log.Debug("Served "+msg.Method, logctx...) } return resp + case msg.hasValidID(): return msg.errorResponse(&invalidRequestError{"invalid request"}) + default: return errorMessage(&invalidRequestError{"invalid request"}) } @@ -458,6 +510,7 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage if callb == nil { return msg.errorResponse(&methodNotFoundError{method: msg.Method}) } + args, err := parsePositionalArguments(msg.Params, callb.argTypes) if err != nil { return msg.errorResponse(&invalidParamsError{err.Error()}) @@ -470,13 +523,14 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage if callb != h.unsubscribeCb { rpcRequestGauge.Inc(1) if answer.Error != nil { - failedReqeustGauge.Inc(1) + failedRequestGauge.Inc(1) } else { successfulRequestGauge.Inc(1) } rpcServingTimer.UpdateSince(start) - newRPCServingTimer(msg.Method, answer.Error == nil).UpdateSince(start) + updateServeTimeHistogram(msg.Method, answer.Error == nil, time.Since(start)) } + return answer } diff --git a/rpc/http.go b/rpc/http.go index cbd16b3f04..f4b99429ef 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -1,5 +1,5 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -32,11 +32,9 @@ import ( "time" ) -type httpContextKey string - const ( - maxRequestContentLength = 1024 * 1024 * 5 - contentType = "application/json" + defaultBodyLimit = 5 * 1024 * 1024 + contentType = "application/json" ) // https://www.jsonrpc.org/historical/json-rpc-over-http.html#id13 @@ -46,16 +44,24 @@ type httpConn struct { client *http.Client url string closeOnce sync.Once - closeCh chan any + closeCh chan interface{} mu sync.Mutex // protects headers headers http.Header + auth HTTPAuth } -// httpConn is treated specially by Client. -func (hc *httpConn) writeJSON(context.Context, any, bool) error { +// httpConn implements ServerCodec, but it is treated specially by Client +// and some methods don't work. The panic() stubs here exist to ensure +// this special treatment is correct. + +func (hc *httpConn) writeJSON(context.Context, interface{}, bool) error { panic("writeJSON called on httpConn") } +func (hc *httpConn) peerInfo() PeerInfo { + panic("peerInfo called on httpConn") +} + func (hc *httpConn) remoteAddr() string { return hc.url } @@ -69,7 +75,7 @@ func (hc *httpConn) close() { hc.closeOnce.Do(func() { close(hc.closeCh) }) } -func (hc *httpConn) closed() <-chan any { +func (hc *httpConn) closed() <-chan interface{} { return hc.closeCh } @@ -114,8 +120,15 @@ var DefaultHTTPTimeouts = HTTPTimeouts{ IdleTimeout: 120 * time.Second, } +// DialHTTP creates a new RPC client that connects to an RPC server over HTTP. +func DialHTTP(endpoint string) (*Client, error) { + return DialHTTPWithClient(endpoint, new(http.Client)) +} + // DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP // using the provided HTTP Client. +// +// Deprecated: use DialOptions and the WithHTTPClient option. func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) { // Sanity check URL so we don't end up with a client that will fail every request. _, err := url.Parse(endpoint) @@ -123,27 +136,39 @@ func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) { return nil, err } - initctx := context.Background() - headers := make(http.Header, 2) + var cfg clientConfig + cfg.httpClient = client + fn := newClientTransportHTTP(endpoint, &cfg) + return newClient(context.Background(), &cfg, fn) +} + +func newClientTransportHTTP(endpoint string, cfg *clientConfig) reconnectFunc { + headers := make(http.Header, 2+len(cfg.httpHeaders)) headers.Set("accept", contentType) headers.Set("content-type", contentType) - return newClient(initctx, func(context.Context) (ServerCodec, error) { - hc := &httpConn{ - client: client, - headers: headers, - url: endpoint, - closeCh: make(chan any), - } - return hc, nil - }) -} + for key, values := range cfg.httpHeaders { + headers[key] = values + } -// DialHTTP creates a new RPC client that connects to an RPC server over HTTP. -func DialHTTP(endpoint string) (*Client, error) { - return DialHTTPWithClient(endpoint, new(http.Client)) + client := cfg.httpClient + if client == nil { + client = new(http.Client) + } + + hc := &httpConn{ + client: client, + headers: headers, + url: endpoint, + auth: cfg.httpAuth, + closeCh: make(chan interface{}), + } + + return func(ctx context.Context) (ServerCodec, error) { + return hc, nil + } } -func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg any) error { +func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error { hc := c.writeConn.(*httpConn) respBody, err := hc.doRequest(ctx, msg) if err != nil { @@ -151,11 +176,12 @@ func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg any) error { } defer respBody.Close() - var respmsg jsonrpcMessage - if err := json.NewDecoder(respBody).Decode(&respmsg); err != nil { + var resp jsonrpcMessage + batch := [1]*jsonrpcMessage{&resp} + if err := json.NewDecoder(respBody).Decode(&resp); err != nil { return err } - op.resp <- &respmsg + op.resp <- batch[:] return nil } @@ -166,17 +192,16 @@ func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonr return err } defer respBody.Close() - var respmsgs []jsonrpcMessage + + var respmsgs []*jsonrpcMessage if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil { return err } - for i := 0; i < len(respmsgs); i++ { - op.resp <- &respmsgs[i] - } + op.resp <- respmsgs return nil } -func (hc *httpConn) doRequest(ctx context.Context, msg any) (io.ReadCloser, error) { +func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadCloser, error) { body, err := json.Marshal(msg) if err != nil { return nil, err @@ -194,6 +219,12 @@ func (hc *httpConn) doRequest(ctx context.Context, msg any) (io.ReadCloser, erro hc.mu.Unlock() setHeaders(req.Header, headersFromContext(ctx)) + if hc.auth != nil { + if err := hc.auth(req.Header); err != nil { + return nil, err + } + } + // do request resp, err := hc.client.Do(req) if err != nil { @@ -222,9 +253,10 @@ type httpServerConn struct { r *http.Request } -func newHTTPServerConn(r *http.Request, w http.ResponseWriter) ServerCodec { - body := io.LimitReader(r.Body, maxRequestContentLength) +func (s *Server) newHTTPServerConn(r *http.Request, w http.ResponseWriter) ServerCodec { + body := io.LimitReader(r.Body, int64(s.httpBodyLimit)) conn := &httpServerConn{Reader: body, Writer: w, r: r} + encoder := func(v any, isErrorResponse bool) error { if !isErrorResponse { return json.NewEncoder(conn).Encode(v) @@ -280,38 +312,37 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) return } - if code, err := validateRequest(r); err != nil { + if code, err := s.validateRequest(r); err != nil { http.Error(w, err.Error(), code) return } + + // Create request-scoped context. + connInfo := PeerInfo{Transport: "http", RemoteAddr: r.RemoteAddr} + connInfo.HTTP.Version = r.Proto + connInfo.HTTP.Host = r.Host + connInfo.HTTP.Origin = r.Header.Get("Origin") + connInfo.HTTP.UserAgent = r.Header.Get("User-Agent") + ctx := r.Context() + ctx = context.WithValue(ctx, peerInfoContextKey{}, connInfo) + // All checks passed, create a codec that reads directly from the request body // until EOF, writes the response to w, and orders the server to process a // single request. - ctx := r.Context() - ctx = context.WithValue(ctx, httpContextKey("remote"), r.RemoteAddr) - ctx = context.WithValue(ctx, httpContextKey("scheme"), r.Proto) - ctx = context.WithValue(ctx, httpContextKey("local"), r.Host) - if ua := r.Header.Get("User-Agent"); ua != "" { - ctx = context.WithValue(ctx, httpContextKey("User-Agent"), ua) - } - if origin := r.Header.Get("Origin"); origin != "" { - ctx = context.WithValue(ctx, httpContextKey("Origin"), origin) - } - w.Header().Set("content-type", contentType) - codec := newHTTPServerConn(r, w) + codec := s.newHTTPServerConn(r, w) defer codec.close() s.serveSingleRequest(ctx, codec) } // validateRequest returns a non-zero response code and error message if the // request is invalid. -func validateRequest(r *http.Request) (int, error) { +func (s *Server) validateRequest(r *http.Request) (int, error) { if r.Method == http.MethodPut || r.Method == http.MethodDelete { return http.StatusMethodNotAllowed, errors.New("method not allowed") } - if r.ContentLength > maxRequestContentLength { - err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxRequestContentLength) + if r.ContentLength > int64(s.httpBodyLimit) { + err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, s.httpBodyLimit) return http.StatusRequestEntityTooLarge, err } // Allow OPTIONS (regardless of content-type) diff --git a/rpc/http_test.go b/rpc/http_test.go index 022cba74e0..6c268b6292 100644 --- a/rpc/http_test.go +++ b/rpc/http_test.go @@ -1,5 +1,5 @@ // Copyright 2017 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,11 +12,13 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc import ( + "context" + "fmt" "net/http" "net/http/httptest" "strings" @@ -38,11 +40,13 @@ func confirmStatusCode(t *testing.T, got, want int) { func confirmRequestValidationCode(t *testing.T, method, contentType, body string, expectedStatusCode int) { t.Helper() + + s := NewServer() request := httptest.NewRequest(method, "http://url.com", strings.NewReader(body)) if len(contentType) > 0 { request.Header.Set("Content-Type", contentType) } - code, err := validateRequest(request) + code, err := s.validateRequest(request) if code == 0 { if err != nil { t.Errorf("validation: got error %v, expected nil", err) @@ -54,24 +58,34 @@ func confirmRequestValidationCode(t *testing.T, method, contentType, body string } func TestHTTPErrorResponseWithDelete(t *testing.T) { + t.Parallel() + confirmRequestValidationCode(t, http.MethodDelete, contentType, "", http.StatusMethodNotAllowed) } func TestHTTPErrorResponseWithPut(t *testing.T) { + t.Parallel() + confirmRequestValidationCode(t, http.MethodPut, contentType, "", http.StatusMethodNotAllowed) } func TestHTTPErrorResponseWithMaxContentLength(t *testing.T) { - body := make([]rune, maxRequestContentLength+1) + t.Parallel() + + body := make([]rune, defaultBodyLimit+1) confirmRequestValidationCode(t, http.MethodPost, contentType, string(body), http.StatusRequestEntityTooLarge) } func TestHTTPErrorResponseWithEmptyContentType(t *testing.T) { + t.Parallel() + confirmRequestValidationCode(t, http.MethodPost, "", "", http.StatusUnsupportedMediaType) } func TestHTTPErrorResponseWithValidRequest(t *testing.T) { + t.Parallel() + confirmRequestValidationCode(t, http.MethodPost, contentType, "", 0) } @@ -92,16 +106,21 @@ func confirmHTTPRequestYieldsStatusCode(t *testing.T, method, contentType, body if err != nil { t.Fatalf("request failed: %v", err) } + resp.Body.Close() confirmStatusCode(t, resp.StatusCode, expectedStatusCode) } func TestHTTPResponseWithEmptyGet(t *testing.T) { + t.Parallel() + confirmHTTPRequestYieldsStatusCode(t, http.MethodGet, "", "", http.StatusOK) } // This checks that maxRequestContentLength is not applied to the response of a request. func TestHTTPRespBodyUnlimited(t *testing.T) { - const respLength = maxRequestContentLength * 3 + t.Parallel() + + const respLength = defaultBodyLimit * 3 s := NewServer() defer s.Stop() @@ -127,6 +146,8 @@ func TestHTTPRespBodyUnlimited(t *testing.T) { // Tests that an HTTP error results in an HTTPError instance // being returned with the expected attributes. func TestHTTPErrorResponse(t *testing.T) { + t.Parallel() + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "error has occurred!", http.StatusTeapot) })) @@ -162,3 +183,83 @@ func TestHTTPErrorResponse(t *testing.T) { t.Error("unexpected error message", errMsg) } } + +func TestHTTPPeerInfo(t *testing.T) { + t.Parallel() + + s := newTestServer() + defer s.Stop() + ts := httptest.NewServer(s) + defer ts.Close() + + c, err := Dial(ts.URL) + if err != nil { + t.Fatal(err) + } + c.SetHeader("user-agent", "ua-testing") + c.SetHeader("origin", "origin.example.com") + + // Request peer information. + var info PeerInfo + if err := c.Call(&info, "test_peerInfo"); err != nil { + t.Fatal(err) + } + + if info.RemoteAddr == "" { + t.Error("RemoteAddr not set") + } + if info.Transport != "http" { + t.Errorf("wrong Transport %q", info.Transport) + } + if info.HTTP.Version != "HTTP/1.1" { + t.Errorf("wrong HTTP.Version %q", info.HTTP.Version) + } + if info.HTTP.UserAgent != "ua-testing" { + t.Errorf("wrong HTTP.UserAgent %q", info.HTTP.UserAgent) + } + if info.HTTP.Origin != "origin.example.com" { + t.Errorf("wrong HTTP.Origin %q", info.HTTP.UserAgent) + } +} + +func TestNewContextWithHeaders(t *testing.T) { + t.Parallel() + + expectedHeaders := 0 + server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + for i := 0; i < expectedHeaders; i++ { + key, want := fmt.Sprintf("key-%d", i), fmt.Sprintf("val-%d", i) + if have := request.Header.Get(key); have != want { + t.Errorf("wrong request headers for %s, want: %s, have: %s", key, want, have) + } + } + writer.WriteHeader(http.StatusOK) + _, _ = writer.Write([]byte(`{}`)) + })) + defer server.Close() + + client, err := Dial(server.URL) + if err != nil { + t.Fatalf("failed to dial: %s", err) + } + defer client.Close() + + newHdr := func(k, v string) http.Header { + header := http.Header{} + header.Set(k, v) + return header + } + ctx1 := NewContextWithHeaders(context.Background(), newHdr("key-0", "val-0")) + ctx2 := NewContextWithHeaders(ctx1, newHdr("key-1", "val-1")) + ctx3 := NewContextWithHeaders(ctx2, newHdr("key-2", "val-2")) + + expectedHeaders = 3 + if err := client.CallContext(ctx3, nil, "test"); err != ErrNoResult { + t.Error("call failed", err) + } + + expectedHeaders = 2 + if err := client.CallContext(ctx2, nil, "test"); err != ErrNoResult { + t.Error("call failed:", err) + } +} diff --git a/rpc/inproc.go b/rpc/inproc.go index 201a038a07..306974e04b 100644 --- a/rpc/inproc.go +++ b/rpc/inproc.go @@ -1,5 +1,5 @@ // Copyright 2016 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -24,7 +24,8 @@ import ( // DialInProc attaches an in-process connection to the given RPC server. func DialInProc(handler *Server) *Client { initctx := context.Background() - c, _ := newClient(initctx, func(context.Context) (ServerCodec, error) { + cfg := new(clientConfig) + c, _ := newClient(initctx, cfg, func(context.Context) (ServerCodec, error) { p1, p2 := net.Pipe() go handler.ServeCodec(NewCodec(p1), 0) return NewCodec(p2), nil diff --git a/rpc/ipc.go b/rpc/ipc.go index f44bc3f7e6..b6da8c232e 100644 --- a/rpc/ipc.go +++ b/rpc/ipc.go @@ -1,5 +1,5 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -46,11 +46,16 @@ func (s *Server) ServeListener(l net.Listener) error { // The context is used for the initial connection establishment. It does not // affect subsequent interactions with the client. func DialIPC(ctx context.Context, endpoint string) (*Client, error) { - return newClient(ctx, func(ctx context.Context) (ServerCodec, error) { + cfg := new(clientConfig) + return newClient(ctx, cfg, newClientTransportIPC(endpoint)) +} + +func newClientTransportIPC(endpoint string) reconnectFunc { + return func(ctx context.Context) (ServerCodec, error) { conn, err := newIPCConnection(ctx, endpoint) if err != nil { return nil, err } return NewCodec(conn), err - }) + } } diff --git a/rpc/ipc_js.go b/rpc/ipc_js.go index babc6ca41b..453a20bc1a 100644 --- a/rpc/ipc_js.go +++ b/rpc/ipc_js.go @@ -1,5 +1,5 @@ // Copyright 2018 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . //go:build js // +build js diff --git a/rpc/ipc_unix.go b/rpc/ipc_unix.go index 8979e0cb7c..8f3af434b6 100644 --- a/rpc/ipc_unix.go +++ b/rpc/ipc_unix.go @@ -1,5 +1,5 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . //go:build darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris diff --git a/rpc/ipc_windows.go b/rpc/ipc_windows.go index 4b0f737e85..efec38cf37 100644 --- a/rpc/ipc_windows.go +++ b/rpc/ipc_windows.go @@ -1,5 +1,5 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . //go:build windows // +build windows @@ -24,7 +24,7 @@ import ( "net" "time" - "gopkg.in/natefinch/npipe.v2" + "github.com/Microsoft/go-winio" ) // This is used if the dialing context has no deadline. It is much smaller than the @@ -33,17 +33,12 @@ const defaultPipeDialTimeout = 2 * time.Second // ipcListen will create a named pipe on the given endpoint. func ipcListen(endpoint string) (net.Listener, error) { - return npipe.Listen(endpoint) + return winio.ListenPipe(endpoint, nil) } // newIPCConnection will connect to a named pipe with the given endpoint as name. func newIPCConnection(ctx context.Context, endpoint string) (net.Conn, error) { - timeout := defaultPipeDialTimeout - if deadline, ok := ctx.Deadline(); ok { - timeout = deadline.Sub(time.Now()) - if timeout < 0 { - timeout = 0 - } - } - return npipe.DialTimeout(endpoint, timeout) + ctx, cancel := context.WithTimeout(ctx, defaultPipeDialTimeout) + defer cancel() + return winio.DialPipeContext(ctx, endpoint) } diff --git a/rpc/json.go b/rpc/json.go index 0a95a61842..df41eba125 100644 --- a/rpc/json.go +++ b/rpc/json.go @@ -1,5 +1,5 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -70,21 +70,25 @@ type jsonrpcMessage struct { } func (msg *jsonrpcMessage) isNotification() bool { - return msg.ID == nil && msg.Method != "" + return msg.hasValidVersion() && msg.ID == nil && msg.Method != "" } func (msg *jsonrpcMessage) isCall() bool { - return msg.hasValidID() && msg.Method != "" + return msg.hasValidVersion() && msg.hasValidID() && msg.Method != "" } func (msg *jsonrpcMessage) isResponse() bool { - return msg.hasValidID() && msg.Method == "" && msg.Params == nil && (msg.Result != nil || msg.Error != nil) + return msg.hasValidVersion() && msg.hasValidID() && msg.Method == "" && msg.Params == nil && (msg.Result != nil || msg.Error != nil) } func (msg *jsonrpcMessage) hasValidID() bool { return len(msg.ID) > 0 && msg.ID[0] != '{' && msg.ID[0] != '[' } +func (msg *jsonrpcMessage) hasValidVersion() bool { + return msg.Version == vsn +} + func (msg *jsonrpcMessage) isSubscribe() bool { return strings.HasSuffix(msg.Method, subscribeMethodSuffix) } @@ -112,11 +116,10 @@ func (msg *jsonrpcMessage) errorResponse(err error) *jsonrpcMessage { return resp } -func (msg *jsonrpcMessage) response(result any) *jsonrpcMessage { +func (msg *jsonrpcMessage) response(result interface{}) *jsonrpcMessage { enc, err := json.Marshal(result) if err != nil { - // TODO: wrap with 'internal server error' - return msg.errorResponse(err) + return msg.errorResponse(&internalServerError{errcodeMarshalError, err.Error()}) } return &jsonrpcMessage{Version: vsn, ID: msg.ID, Result: enc} } @@ -138,9 +141,9 @@ func errorMessage(err error) *jsonrpcMessage { } type jsonError struct { - Code int `json:"code"` - Message string `json:"message"` - Data any `json:"data,omitempty"` + Code int `json:"code"` + Message string `json:"message"` + Data interface{} `json:"data,omitempty"` } func (err *jsonError) Error() string { @@ -154,7 +157,7 @@ func (err *jsonError) ErrorCode() int { return err.Code } -func (err *jsonError) ErrorData() any { +func (err *jsonError) ErrorData() interface{} { return err.Data } @@ -180,24 +183,24 @@ type ConnRemoteAddr interface { // support for parsing arguments and serializing (result) objects. type jsonCodec struct { remote string - closer sync.Once // close closed channel once - closeCh chan any // closed on Close - decode decodeFunc // decoder to allow multiple transports - encMu sync.Mutex // guards the encoder - encode encodeFunc // encoder to allow multiple transports + closer sync.Once // close closed channel once + closeCh chan interface{} // closed on Close + decode decodeFunc // decoder to allow multiple transports + encMu sync.Mutex // guards the encoder + encode encodeFunc // encoder to allow multiple transports conn deadlineCloser } -type encodeFunc = func(v any, isErrorResponse bool) error +type encodeFunc = func(v interface{}, isErrorResponse bool) error -type decodeFunc = func(v any) error +type decodeFunc = func(v interface{}) error // NewFuncCodec creates a codec which uses the given functions to read and write. If conn // implements ConnRemoteAddr, log messages will use it to include the remote address of // the connection. func NewFuncCodec(conn deadlineCloser, encode encodeFunc, decode decodeFunc) ServerCodec { codec := &jsonCodec{ - closeCh: make(chan any), + closeCh: make(chan interface{}), encode: encode, decode: decode, conn: conn, @@ -214,12 +217,18 @@ func NewCodec(conn Conn) ServerCodec { enc := json.NewEncoder(conn) dec := json.NewDecoder(conn) dec.UseNumber() - encode := func(v any, isErrorResponse bool) error { + + encode := func(v interface{}, isErrorResponse bool) error { return enc.Encode(v) } return NewFuncCodec(conn, encode, dec.Decode) } +func (c *jsonCodec) peerInfo() PeerInfo { + // This returns "ipc" because all other built-in transports have a separate codec type. + return PeerInfo{Transport: "ipc", RemoteAddr: c.remote} +} + func (c *jsonCodec) remoteAddr() string { return c.remote } @@ -242,7 +251,7 @@ func (c *jsonCodec) readBatch() (messages []*jsonrpcMessage, batch bool, err err return messages, batch, nil } -func (c *jsonCodec) writeJSON(ctx context.Context, v any, isErrorResponse bool) error { +func (c *jsonCodec) writeJSON(ctx context.Context, v interface{}, isErrorResponse bool) error { c.encMu.Lock() defer c.encMu.Unlock() @@ -261,8 +270,8 @@ func (c *jsonCodec) close() { }) } -// Closed returns a channel which will be closed when Close is called -func (c *jsonCodec) closed() <-chan any { +// closed returns a channel which will be closed when Close is called +func (c *jsonCodec) closed() <-chan interface{} { return c.closeCh } diff --git a/rpc/metrics.go b/rpc/metrics.go index 83354fa403..aca90efa13 100644 --- a/rpc/metrics.go +++ b/rpc/metrics.go @@ -1,5 +1,5 @@ // Copyright 2020 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,12 +12,13 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc import ( "fmt" + "time" "github.com/CortexFoundation/CortexTheseus/metrics" ) @@ -25,15 +26,25 @@ import ( var ( rpcRequestGauge = metrics.NewRegisteredGauge("rpc/requests", nil) successfulRequestGauge = metrics.NewRegisteredGauge("rpc/success", nil) - failedReqeustGauge = metrics.NewRegisteredGauge("rpc/failure", nil) - rpcServingTimer = metrics.NewRegisteredTimer("rpc/duration/all", nil) + failedRequestGauge = metrics.NewRegisteredGauge("rpc/failure", nil) + + // serveTimeHistName is the prefix of the per-request serving time histograms. + serveTimeHistName = "rpc/duration" + + rpcServingTimer = metrics.NewRegisteredTimer("rpc/duration/all", nil) ) -func newRPCServingTimer(method string, valid bool) metrics.Timer { - flag := "success" - if !valid { - flag = "failure" +// updateServeTimeHistogram tracks the serving time of a remote RPC call. +func updateServeTimeHistogram(method string, success bool, elapsed time.Duration) { + note := "success" + if !success { + note = "failure" + } + h := fmt.Sprintf("%s/%s/%s", serveTimeHistName, method, note) + sampler := func() metrics.Sample { + return metrics.ResettingSample( + metrics.NewExpDecaySample(1028, 0.015), + ) } - m := fmt.Sprintf("rpc/duration/%s/%s", method, flag) - return metrics.GetOrRegisterTimer(m, nil) + metrics.GetOrRegisterHistogramLazy(h, nil, sampler).Update(elapsed.Nanoseconds()) } diff --git a/rpc/server.go b/rpc/server.go index bc24635805..62eb71757b 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -1,5 +1,5 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,20 +12,23 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc import ( "context" + "errors" "io" + "net" "sync" "sync/atomic" "github.com/CortexFoundation/CortexTheseus/log" ) -const MetadataAPI = "rpc" +const MetadataApi = "rpc" +const EngineApi = "engine" // CodecOption specifies which type of messages a codec supports. // @@ -45,30 +48,52 @@ type Server struct { services serviceRegistry idgen func() ID - mutex sync.Mutex - codecs map[ServerCodec]struct{} - run atomic.Bool + mutex sync.Mutex + codecs map[ServerCodec]struct{} + run atomic.Bool + batchItemLimit int + batchResponseLimit int + httpBodyLimit int } // NewServer creates a new server instance with no registered handlers. func NewServer() *Server { server := &Server{ - idgen: randomIDGenerator(), - codecs: make(map[ServerCodec]struct{}), + idgen: randomIDGenerator(), + codecs: make(map[ServerCodec]struct{}), + httpBodyLimit: defaultBodyLimit, } server.run.Store(true) // Register the default service providing meta information about the RPC service such // as the services and methods it offers. rpcService := &RPCService{server} - server.RegisterName(MetadataAPI, rpcService) + server.RegisterName(MetadataApi, rpcService) return server } +// SetBatchLimits sets limits applied to batch requests. There are two limits: 'itemLimit' +// is the maximum number of items in a batch. 'maxResponseSize' is the maximum number of +// response bytes across all requests in a batch. +// +// This method should be called before processing any requests via ServeCodec, ServeHTTP, +// ServeListener etc. +func (s *Server) SetBatchLimits(itemLimit, maxResponseSize int) { + s.batchItemLimit = itemLimit + s.batchResponseLimit = maxResponseSize +} + +// SetHTTPBodyLimit sets the size limit for HTTP requests. +// +// This method should be called before processing any requests via ServeHTTP. +func (s *Server) SetHTTPBodyLimit(limit int) { + s.httpBodyLimit = limit +} + // RegisterName creates a service for the given receiver type under the given name. When no // methods on the given receiver match the criteria to be either an RPC method or a // subscription an error is returned. Otherwise a new service is created and added to the // service collection this server provides to clients. -func (s *Server) RegisterName(name string, receiver any) error { +func (s *Server) RegisterName(name string, receiver interface{}) error { return s.services.registerName(name, receiver) } @@ -85,7 +110,12 @@ func (s *Server) ServeCodec(codec ServerCodec, options CodecOption) { } defer s.untrackCodec(codec) - c := initClient(codec, s.idgen, &s.services) + cfg := &clientConfig{ + idgen: s.idgen, + batchItemLimit: s.batchItemLimit, + batchResponseLimit: s.batchResponseLimit, + } + c := initClient(codec, &s.services, cfg) <-codec.closed() c.Close() } @@ -117,14 +147,14 @@ func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) { return } - h := newHandler(ctx, codec, s.idgen, &s.services) + h := newHandler(ctx, codec, s.idgen, &s.services, s.batchItemLimit, s.batchResponseLimit) h.allowSubscribe = false defer h.close(io.EOF, nil) reqs, batch, err := codec.readBatch() if err != nil { - if err != io.EOF { - resp := errorMessage(&invalidMessageError{"parse error"}) + if msg := messageForReadError(err); msg != "" { + resp := errorMessage(&invalidMessageError{msg}) codec.writeJSON(ctx, resp, true) } return @@ -136,6 +166,20 @@ func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) { } } +func messageForReadError(err error) string { + var netErr net.Error + if errors.As(err, &netErr) { + if netErr.Timeout() { + return "read timeout" + } else { + return "read error" + } + } else if err != io.EOF { + return "parse error" + } + return "" +} + // Stop stops reading new requests, waits for stopPendingRequestTimeout to allow pending // requests to finish, then closes all codecs which will cancel pending requests and // subscriptions. @@ -168,3 +212,38 @@ func (s *RPCService) Modules() map[string]string { } return modules } + +// PeerInfo contains information about the remote end of the network connection. +// +// This is available within RPC method handlers through the context. Call +// PeerInfoFromContext to get information about the client connection related to +// the current method call. +type PeerInfo struct { + // Transport is name of the protocol used by the client. + // This can be "http", "ws" or "ipc". + Transport string + + // Address of client. This will usually contain the IP address and port. + RemoteAddr string + + // Additional information for HTTP and WebSocket connections. + HTTP struct { + // Protocol version, i.e. "HTTP/1.1". This is not set for WebSocket. + Version string + // Header values sent by the client. + UserAgent string + Origin string + Host string + } +} + +type peerInfoContextKey struct{} + +// PeerInfoFromContext returns information about the client's network connection. +// Use this with the context passed to RPC method handler functions. +// +// The zero value is returned if no connection info is present in ctx. +func PeerInfoFromContext(ctx context.Context) PeerInfo { + info, _ := ctx.Value(peerInfoContextKey{}).(PeerInfo) + return info +} diff --git a/rpc/server_test.go b/rpc/server_test.go index c7aa56aeec..9ee545d81a 100644 --- a/rpc/server_test.go +++ b/rpc/server_test.go @@ -1,5 +1,5 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -29,10 +29,13 @@ import ( ) func TestServerRegisterName(t *testing.T) { + t.Parallel() + server := NewServer() service := new(testService) - if err := server.RegisterName("test", service); err != nil { + svcName := "test" + if err := server.RegisterName(svcName, service); err != nil { t.Fatalf("%v", err) } @@ -40,18 +43,20 @@ func TestServerRegisterName(t *testing.T) { t.Fatalf("Expected 2 service entries, got %d", len(server.services.services)) } - svc, ok := server.services.services["test"] + svc, ok := server.services.services[svcName] if !ok { - t.Fatalf("Expected service calc to be registered") + t.Fatalf("Expected service %s to be registered", svcName) } - wantCallbacks := 9 + wantCallbacks := 14 if len(svc.callbacks) != wantCallbacks { t.Errorf("Expected %d callbacks for service 'service', got %d", wantCallbacks, len(svc.callbacks)) } } func TestServer(t *testing.T) { + t.Parallel() + files, err := os.ReadDir("testdata") if err != nil { t.Fatal("where'd my testdata go?") @@ -63,6 +68,8 @@ func TestServer(t *testing.T) { path := filepath.Join("testdata", f.Name()) name := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name())) t.Run(name, func(t *testing.T) { + t.Parallel() + runTestScript(t, path) }) } @@ -70,6 +77,7 @@ func TestServer(t *testing.T) { func runTestScript(t *testing.T, file string) { server := newTestServer() + server.SetBatchLimits(4, 100000) content, err := os.ReadFile(file) if err != nil { t.Fatal(err) @@ -114,6 +122,8 @@ func runTestScript(t *testing.T, file string) { // This test checks that responses are delivered for very short-lived connections that // only carry a single request. func TestServerShortLivedConn(t *testing.T) { + t.Parallel() + server := newTestServer() defer server.Stop() @@ -134,7 +144,7 @@ func TestServerShortLivedConn(t *testing.T) { if err != nil { t.Fatal("can't dial:", err) } - defer conn.Close() + conn.SetDeadline(deadline) // Write the request, then half-close the connection so the server stops reading. conn.Write([]byte(request)) @@ -142,6 +152,8 @@ func TestServerShortLivedConn(t *testing.T) { // Now try to get the response. buf := make([]byte, 2000) n, err := conn.Read(buf) + conn.Close() + if err != nil { t.Fatal("read error:", err) } @@ -150,3 +162,43 @@ func TestServerShortLivedConn(t *testing.T) { } } } + +func TestServerBatchResponseSizeLimit(t *testing.T) { + t.Parallel() + + server := newTestServer() + defer server.Stop() + server.SetBatchLimits(100, 60) + var ( + batch []BatchElem + client = DialInProc(server) + ) + for i := 0; i < 5; i++ { + batch = append(batch, BatchElem{ + Method: "test_echo", + Args: []any{"x", 1}, + Result: new(echoResult), + }) + } + if err := client.BatchCall(batch); err != nil { + t.Fatal("error sending batch:", err) + } + for i := range batch { + // We expect the first two queries to be ok, but after that the size limit takes effect. + if i < 2 { + if batch[i].Error != nil { + t.Fatalf("batch elem %d has unexpected error: %v", i, batch[i].Error) + } + continue + } + // After two, we expect an error. + re, ok := batch[i].Error.(Error) + if !ok { + t.Fatalf("batch elem %d has wrong error: %v", i, batch[i].Error) + } + wantedCode := errcodeResponseTooLarge + if re.ErrorCode() != wantedCode { + t.Errorf("batch elem %d wrong error code, have %d want %d", i, re.ErrorCode(), wantedCode) + } + } +} diff --git a/rpc/service.go b/rpc/service.go index a1f5dadfcd..d93e5b5596 100644 --- a/rpc/service.go +++ b/rpc/service.go @@ -1,5 +1,5 @@ // Copyright 2019 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,13 +12,12 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc import ( "context" - "errors" "fmt" "reflect" "runtime" @@ -58,7 +57,7 @@ type callback struct { isSubscribe bool // true if this is a subscription callback } -func (r *serviceRegistry) registerName(name string, rcvr any) error { +func (r *serviceRegistry) registerName(name string, rcvr interface{}) error { rcvrVal := reflect.ValueOf(rcvr) if name == "" { return fmt.Errorf("no service name for type %s", rcvrVal.Type().String()) @@ -184,7 +183,7 @@ func (c *callback) makeArgTypes() { } // call invokes the callback. -func (c *callback) call(ctx context.Context, method string, args []reflect.Value) (res any, errRes error) { +func (c *callback) call(ctx context.Context, method string, args []reflect.Value) (res interface{}, errRes error) { // Create the argument slice. fullargs := make([]reflect.Value, 0, 2+len(args)) if c.rcvr.IsValid() { @@ -202,7 +201,7 @@ func (c *callback) call(ctx context.Context, method string, args []reflect.Value buf := make([]byte, size) buf = buf[:runtime.Stack(buf, false)] log.Error("RPC method " + method + " crashed: " + fmt.Sprintf("%v\n%s", err, buf)) - errRes = errors.New("method handler crashed") + errRes = &internalServerError{errcodePanic, "method handler crashed"} } }() // Run the callback. diff --git a/rpc/stdio.go b/rpc/stdio.go index 9029f5adb9..084e5f0700 100644 --- a/rpc/stdio.go +++ b/rpc/stdio.go @@ -1,5 +1,5 @@ // Copyright 2018 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -32,12 +32,17 @@ func DialStdIO(ctx context.Context) (*Client, error) { // DialIO creates a client which uses the given IO channels func DialIO(ctx context.Context, in io.Reader, out io.Writer) (*Client, error) { - return newClient(ctx, func(_ context.Context) (ServerCodec, error) { + cfg := new(clientConfig) + return newClient(ctx, cfg, newClientTransportIO(in, out)) +} + +func newClientTransportIO(in io.Reader, out io.Writer) reconnectFunc { + return func(context.Context) (ServerCodec, error) { return NewCodec(stdioConn{ in: in, out: out, }), nil - }) + } } type stdioConn struct { diff --git a/rpc/subscription.go b/rpc/subscription.go index c98f055af1..a57764a42f 100644 --- a/rpc/subscription.go +++ b/rpc/subscription.go @@ -1,5 +1,5 @@ // Copyright 2016 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -97,7 +97,7 @@ func NotifierFromContext(ctx context.Context) (*Notifier, bool) { return n, ok } -// Notifier is tied to a RPC connection that supports subscriptions. +// Notifier is tied to an RPC connection that supports subscriptions. // Server callbacks use the notifier to send notifications. type Notifier struct { h *handler @@ -145,12 +145,6 @@ func (n *Notifier) Notify(id ID, data any) error { return nil } -// Closed returns a channel that is closed when the RPC connection is closed. -// Deprecated: use subscription error channel -func (n *Notifier) Closed() <-chan any { - return n.h.conn.closed() -} - // takeSubscription returns the subscription (if one has been created). No subscription can // be created after this call. func (n *Notifier) takeSubscription() *Subscription { @@ -369,7 +363,7 @@ func (sub *ClientSubscription) forward() (unsubscribeServer bool, err error) { } } -func (sub *ClientSubscription) unmarshal(result json.RawMessage) (any, error) { +func (sub *ClientSubscription) unmarshal(result json.RawMessage) (interface{}, error) { val := reflect.New(sub.etype) err := json.Unmarshal(result, val.Interface()) return val.Elem().Interface(), err diff --git a/rpc/subscription_test.go b/rpc/subscription_test.go index b390a24dca..f9730d1295 100644 --- a/rpc/subscription_test.go +++ b/rpc/subscription_test.go @@ -1,5 +1,5 @@ // Copyright 2016 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -33,6 +33,8 @@ import ( ) func TestNewID(t *testing.T) { + t.Parallel() + hexchars := "0123456789ABCDEFabcdef" for i := 0; i < 100; i++ { id := string(NewID()) @@ -54,6 +56,8 @@ func TestNewID(t *testing.T) { } func TestSubscriptions(t *testing.T) { + t.Parallel() + var ( namespaces = []string{"ctxc", "shh", "bzz"} service = ¬ificationTestService{} @@ -83,11 +87,11 @@ func TestSubscriptions(t *testing.T) { // create subscriptions one by one for i, namespace := range namespaces { - request := map[string]any{ + request := map[string]interface{}{ "id": i, "method": fmt.Sprintf("%s_subscribe", namespace), - "version": "2.0", - "params": []any{"someSubscription", notificationCount, i}, + "jsonrpc": "2.0", + "params": []interface{}{"someSubscription", notificationCount, i}, } if err := out.Encode(&request); err != nil { t.Fatalf("Could not create subscription: %v", err) @@ -132,6 +136,8 @@ func TestSubscriptions(t *testing.T) { // This test checks that unsubscribing works. func TestServerUnsubscribe(t *testing.T) { + t.Parallel() + p1, p2 := net.Pipe() defer p2.Close() @@ -231,14 +237,14 @@ type mockConn struct { } // writeJSON writes a message to the connection. -func (c *mockConn) writeJSON(ctx context.Context, msg any, isError bool) error { +func (c *mockConn) writeJSON(ctx context.Context, msg interface{}, isError bool) error { return c.enc.Encode(msg) } -// Closed returns a channel which is closed when the connection is closed. -func (c *mockConn) closed() <-chan any { return nil } +// closed returns a channel which is closed when the connection is closed. +func (c *mockConn) closed() <-chan interface{} { return nil } -// RemoteAddr returns the peer address of the connection. +// remoteAddr returns the peer address of the connection. func (c *mockConn) remoteAddr() string { return "" } // BenchmarkNotify benchmarks the performance of notifying a subscription. @@ -260,6 +266,8 @@ func BenchmarkNotify(b *testing.B) { } func TestNotify(t *testing.T) { + t.Parallel() + out := new(bytes.Buffer) id := ID("test") notifier := &Notifier{ @@ -267,13 +275,9 @@ func TestNotify(t *testing.T) { sub: &Subscription{ID: id}, activated: true, } - msg := &types.Header{ - ParentHash: common.HexToHash("0x01"), - Number: big.NewInt(100), - } - notifier.Notify(id, msg) + notifier.Notify(id, "hello") have := strings.TrimSpace(out.String()) - want := `{"jsonrpc":"2.0","method":"_subscription","params":{"subscription":"test","result":{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000001","sha3Uncles":"0x0000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","stateRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","transactionsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x0000000000000000000000000000000000000000000000000000000000000000","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":null,"number":"0x64","gasLimit":"0x0","gasUsed":"0x0","timestamp":"0x0","extraData":"0x","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","solution":"\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000","quota":"0x0","quotaUsed":"0x0","supply":null,"hash":"0x4df4ee22e206405d75ee5021018141c51d7c5b0b29476ed51933d2e3b2a21c52"}}}` + want := `{"jsonrpc":"2.0","method":"_subscription","params":{"subscription":"test","result":"hello"}}` if have != want { t.Errorf("have:\n%v\nwant:\n%v\n", have, want) } diff --git a/rpc/testservice_test.go b/rpc/testservice_test.go index 4975faccf4..69199e21b7 100644 --- a/rpc/testservice_test.go +++ b/rpc/testservice_test.go @@ -1,5 +1,5 @@ // Copyright 2019 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -66,12 +66,22 @@ type echoResult struct { type testError struct{} -func (testError) Error() string { return "testError" } -func (testError) ErrorCode() int { return 444 } -func (testError) ErrorData() any { return "testError data" } +func (testError) Error() string { return "testError" } +func (testError) ErrorCode() int { return 444 } +func (testError) ErrorData() interface{} { return "testError data" } + +type MarshalErrObj struct{} + +func (o *MarshalErrObj) MarshalText() ([]byte, error) { + return nil, errors.New("marshal error") +} func (s *testService) NoArgsRets() {} +func (s *testService) Null() any { + return nil +} + func (s *testService) Echo(str string, i int, args *echoArgs) echoResult { return echoResult{str, i, args} } @@ -80,6 +90,14 @@ func (s *testService) EchoWithCtx(ctx context.Context, str string, i int, args * return echoResult{str, i, args} } +func (s *testService) Repeat(msg string, i int) string { + return strings.Repeat(msg, i) +} + +func (s *testService) PeerInfo(ctx context.Context) PeerInfo { + return PeerInfoFromContext(ctx) +} + func (s *testService) Sleep(ctx context.Context, duration time.Duration) { time.Sleep(duration) } @@ -110,24 +128,32 @@ func (s *testService) ReturnError() error { return testError{} } -func (s *testService) CallMeBack(ctx context.Context, method string, args []any) (any, error) { +func (s *testService) MarshalError() *MarshalErrObj { + return &MarshalErrObj{} +} + +func (s *testService) Panic() string { + panic("service panic") +} + +func (s *testService) CallMeBack(ctx context.Context, method string, args []interface{}) (interface{}, error) { c, ok := ClientFromContext(ctx) if !ok { return nil, errors.New("no client") } - var result any + var result interface{} err := c.Call(&result, method, args...) return result, err } -func (s *testService) CallMeBackLater(ctx context.Context, method string, args []any) error { +func (s *testService) CallMeBackLater(ctx context.Context, method string, args []interface{}) error { c, ok := ClientFromContext(ctx) if !ok { return errors.New("no client") } go func() { <-ctx.Done() - var result any + var result interface{} c.Call(&result, method, args...) }() return nil @@ -169,10 +195,7 @@ func (s *notificationTestService) SomeSubscription(ctx context.Context, n, val i return } } - select { - case <-notifier.Closed(): - case <-subscription.Err(): - } + <-subscription.Err() if s.unsubscribed != nil { s.unsubscribed <- string(subscription.ID) } diff --git a/rpc/types.go b/rpc/types.go index b68a69d6b3..cba51501b5 100644 --- a/rpc/types.go +++ b/rpc/types.go @@ -1,5 +1,5 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -20,8 +20,8 @@ import ( "context" "encoding/json" "errors" + "fmt" "math" - "strconv" "strings" "github.com/CortexFoundation/CortexTheseus/common" @@ -30,18 +30,21 @@ import ( // API describes the set of methods offered over the RPC interface type API struct { - Namespace string // namespace under which the rpc methods of Service are exposed - Version string // api version for DApp's - Service any // receiver instance which holds the methods - Public bool // indication if the methods must be considered safe for public use + Namespace string // namespace under which the rpc methods of Service are exposed + Version string // deprecated - this field is no longer used, but retained for compatibility + Service interface{} // receiver instance which holds the methods + Public bool // deprecated - this field is no longer used, but retained for compatibility + Authenticated bool // whether the api should only be available behind authentication. } // ServerCodec implements reading, parsing and writing RPC messages for the server side of -// a RPC session. Implementations must be go-routine safe since the codec can be called in +// an RPC session. Implementations must be go-routine safe since the codec can be called in // multiple go-routines concurrently. type ServerCodec interface { + peerInfo() PeerInfo readBatch() (msgs []*jsonrpcMessage, isBatch bool, err error) close() + jsonWriter } @@ -49,9 +52,10 @@ type ServerCodec interface { // Implementations must be safe for concurrent use. type jsonWriter interface { // writeJSON writes a message to the connection. - writeJSON(ctx context.Context, msg any, isError bool) error + writeJSON(ctx context.Context, msg interface{}, isError bool) error + // Closed returns a channel which is closed when the connection is closed. - closed() <-chan any + closed() <-chan interface{} // RemoteAddr returns the peer address of the connection. remoteAddr() string } @@ -59,15 +63,15 @@ type jsonWriter interface { type BlockNumber int64 const ( + EarliestBlockNumber = BlockNumber(-5) SafeBlockNumber = BlockNumber(-4) FinalizedBlockNumber = BlockNumber(-3) - PendingBlockNumber = BlockNumber(-2) - LatestBlockNumber = BlockNumber(-1) - EarliestBlockNumber = BlockNumber(0) + LatestBlockNumber = BlockNumber(-2) + PendingBlockNumber = BlockNumber(-1) ) // UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports: -// - "latest", "earliest" or "pending" as string arguments +// - "safe", "finalized", "latest", "earliest" or "pending" as string arguments // - the block number // Returned errors: // - an invalid block number error when the given argument isn't a known strings @@ -107,30 +111,38 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error { return nil } +// Int64 returns the block number as int64. +func (bn BlockNumber) Int64() int64 { + return (int64)(bn) +} + // MarshalText implements encoding.TextMarshaler. It marshals: -// - "latest", "earliest" or "pending" as strings +// - "safe", "finalized", "latest", "earliest" or "pending" as strings // - other numbers as hex func (bn BlockNumber) MarshalText() ([]byte, error) { + return []byte(bn.String()), nil +} + +func (bn BlockNumber) String() string { switch bn { case EarliestBlockNumber: - return []byte("earliest"), nil + return "earliest" case LatestBlockNumber: - return []byte("latest"), nil + return "latest" case PendingBlockNumber: - return []byte("pending"), nil + return "pending" case FinalizedBlockNumber: - return []byte("finalized"), nil + return "finalized" case SafeBlockNumber: - return []byte("safe"), nil + return "safe" default: - return hexutil.Uint64(bn).MarshalText() + if bn < 0 { + return fmt.Sprintf("", bn) + } + return hexutil.Uint64(bn).String() } } -func (bn BlockNumber) Int64() int64 { - return (int64)(bn) -} - type BlockNumberOrHash struct { BlockNumber *BlockNumber `json:"blockNumber,omitempty"` BlockHash *common.Hash `json:"blockHash,omitempty"` @@ -185,17 +197,18 @@ func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error { } bnh.BlockHash = &hash return nil + } else { + blckNum, err := hexutil.DecodeUint64(input) + if err != nil { + return err + } + if blckNum > math.MaxInt64 { + return errors.New("blocknumber too high") + } + bn := BlockNumber(blckNum) + bnh.BlockNumber = &bn + return nil } - blckNum, err := hexutil.DecodeUint64(input) - if err != nil { - return err - } - if blckNum > math.MaxInt64 { - return errors.New("blocknumber too high") - } - bn := BlockNumber(blckNum) - bnh.BlockNumber = &bn - return nil } } @@ -208,7 +221,7 @@ func (bnh *BlockNumberOrHash) Number() (BlockNumber, bool) { func (bnh *BlockNumberOrHash) String() string { if bnh.BlockNumber != nil { - return strconv.Itoa(int(*bnh.BlockNumber)) + return bnh.BlockNumber.String() } if bnh.BlockHash != nil { return bnh.BlockHash.String() diff --git a/rpc/types_test.go b/rpc/types_test.go index 719e15dfe0..2edda590e2 100644 --- a/rpc/types_test.go +++ b/rpc/types_test.go @@ -1,5 +1,5 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -26,6 +26,8 @@ import ( ) func TestBlockNumberJSONUnmarshal(t *testing.T) { + t.Parallel() + tests := []struct { input string mustFail bool @@ -45,9 +47,11 @@ func TestBlockNumberJSONUnmarshal(t *testing.T) { 11: {`"pending"`, false, PendingBlockNumber}, 12: {`"latest"`, false, LatestBlockNumber}, 13: {`"earliest"`, false, EarliestBlockNumber}, - 14: {`someString`, true, BlockNumber(0)}, - 15: {`""`, true, BlockNumber(0)}, - 16: {``, true, BlockNumber(0)}, + 14: {`"safe"`, false, SafeBlockNumber}, + 15: {`"finalized"`, false, FinalizedBlockNumber}, + 16: {`someString`, true, BlockNumber(0)}, + 17: {`""`, true, BlockNumber(0)}, + 18: {``, true, BlockNumber(0)}, } for i, test := range tests { @@ -68,6 +72,8 @@ func TestBlockNumberJSONUnmarshal(t *testing.T) { } func TestBlockNumberOrHash_UnmarshalJSON(t *testing.T) { + t.Parallel() + tests := []struct { input string mustFail bool @@ -87,18 +93,22 @@ func TestBlockNumberOrHash_UnmarshalJSON(t *testing.T) { 11: {`"pending"`, false, BlockNumberOrHashWithNumber(PendingBlockNumber)}, 12: {`"latest"`, false, BlockNumberOrHashWithNumber(LatestBlockNumber)}, 13: {`"earliest"`, false, BlockNumberOrHashWithNumber(EarliestBlockNumber)}, - 14: {`someString`, true, BlockNumberOrHash{}}, - 15: {`""`, true, BlockNumberOrHash{}}, - 16: {``, true, BlockNumberOrHash{}}, - 17: {`"0x0000000000000000000000000000000000000000000000000000000000000000"`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)}, - 18: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)}, - 19: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","requireCanonical":false}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)}, - 20: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","requireCanonical":true}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), true)}, - 21: {`{"blockNumber":"0x1"}`, false, BlockNumberOrHashWithNumber(1)}, - 22: {`{"blockNumber":"pending"}`, false, BlockNumberOrHashWithNumber(PendingBlockNumber)}, - 23: {`{"blockNumber":"latest"}`, false, BlockNumberOrHashWithNumber(LatestBlockNumber)}, - 24: {`{"blockNumber":"earliest"}`, false, BlockNumberOrHashWithNumber(EarliestBlockNumber)}, - 25: {`{"blockNumber":"0x1", "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`, true, BlockNumberOrHash{}}, + 14: {`"safe"`, false, BlockNumberOrHashWithNumber(SafeBlockNumber)}, + 15: {`"finalized"`, false, BlockNumberOrHashWithNumber(FinalizedBlockNumber)}, + 16: {`someString`, true, BlockNumberOrHash{}}, + 17: {`""`, true, BlockNumberOrHash{}}, + 18: {``, true, BlockNumberOrHash{}}, + 19: {`"0x0000000000000000000000000000000000000000000000000000000000000000"`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)}, + 20: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)}, + 21: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","requireCanonical":false}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)}, + 22: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","requireCanonical":true}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), true)}, + 23: {`{"blockNumber":"0x1"}`, false, BlockNumberOrHashWithNumber(1)}, + 24: {`{"blockNumber":"pending"}`, false, BlockNumberOrHashWithNumber(PendingBlockNumber)}, + 25: {`{"blockNumber":"latest"}`, false, BlockNumberOrHashWithNumber(LatestBlockNumber)}, + 26: {`{"blockNumber":"earliest"}`, false, BlockNumberOrHashWithNumber(EarliestBlockNumber)}, + 27: {`{"blockNumber":"safe"}`, false, BlockNumberOrHashWithNumber(SafeBlockNumber)}, + 28: {`{"blockNumber":"finalized"}`, false, BlockNumberOrHashWithNumber(FinalizedBlockNumber)}, + 29: {`{"blockNumber":"0x1", "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`, true, BlockNumberOrHash{}}, } for i, test := range tests { @@ -125,6 +135,8 @@ func TestBlockNumberOrHash_UnmarshalJSON(t *testing.T) { } func TestBlockNumberOrHash_WithNumber_MarshalAndUnmarshal(t *testing.T) { + t.Parallel() + tests := []struct { name string number int64 @@ -133,10 +145,13 @@ func TestBlockNumberOrHash_WithNumber_MarshalAndUnmarshal(t *testing.T) { {"pending", int64(PendingBlockNumber)}, {"latest", int64(LatestBlockNumber)}, {"earliest", int64(EarliestBlockNumber)}, + {"safe", int64(SafeBlockNumber)}, + {"finalized", int64(FinalizedBlockNumber)}, } for _, test := range tests { - test := test t.Run(test.name, func(t *testing.T) { + t.Parallel() + bnh := BlockNumberOrHashWithNumber(BlockNumber(test.number)) marshalled, err := json.Marshal(bnh) if err != nil { @@ -153,3 +168,28 @@ func TestBlockNumberOrHash_WithNumber_MarshalAndUnmarshal(t *testing.T) { }) } } + +func TestBlockNumberOrHash_StringAndUnmarshal(t *testing.T) { + t.Parallel() + + tests := []BlockNumberOrHash{ + BlockNumberOrHashWithNumber(math.MaxInt64), + BlockNumberOrHashWithNumber(PendingBlockNumber), + BlockNumberOrHashWithNumber(LatestBlockNumber), + BlockNumberOrHashWithNumber(EarliestBlockNumber), + BlockNumberOrHashWithNumber(SafeBlockNumber), + BlockNumberOrHashWithNumber(FinalizedBlockNumber), + BlockNumberOrHashWithNumber(32), + BlockNumberOrHashWithHash(common.Hash{0xaa}, false), + } + for _, want := range tests { + marshalled, _ := json.Marshal(want.String()) + var have BlockNumberOrHash + if err := json.Unmarshal(marshalled, &have); err != nil { + t.Fatalf("cannot unmarshal (%v): %v", string(marshalled), err) + } + if !reflect.DeepEqual(want, have) { + t.Fatalf("wrong result: have %v, want %v", have, want) + } + } +} diff --git a/rpc/websocket.go b/rpc/websocket.go index 7212eafeb7..e27c37f035 100644 --- a/rpc/websocket.go +++ b/rpc/websocket.go @@ -1,5 +1,5 @@ // Copyright 2015 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,7 +12,7 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc @@ -38,7 +38,7 @@ const ( wsPingInterval = 30 * time.Second wsPingWriteTimeout = 5 * time.Second wsPongTimeout = 30 * time.Second - wsMessageSizeLimit = 15 * 1024 * 1024 + wsDefaultReadLimit = 32 * 1024 * 1024 ) var wsBufferPool = new(sync.Pool) @@ -60,7 +60,7 @@ func (s *Server) WebsocketHandler(allowedOrigins []string) http.Handler { log.Debug("WebSocket upgrade failed", "err", err) return } - codec := newWebsocketCodec(conn) + codec := newWebsocketCodec(conn, r.Host, r.Header, wsDefaultReadLimit) s.ServeCodec(codec, 0) }) } @@ -97,7 +97,7 @@ func wsHandshakeValidator(allowedOrigins []string) func(*http.Request) bool { if _, ok := req.Header["Origin"]; !ok { return true } - // Verify origin against whitelist. + // Verify origin against allow list. origin := strings.ToLower(req.Header.Get("Origin")) if allowAllOrigins || originIsAllowed(origins, origin) { return true @@ -122,6 +122,10 @@ func (e wsHandshakeError) Error() string { return s } +func (e wsHandshakeError) Unwrap() error { + return e.err +} + func originIsAllowed(allowedOrigins mapset.Set[string], browserOrigin string) bool { it := allowedOrigins.Iterator() for origin := range it.C { @@ -181,24 +185,23 @@ func parseOriginURL(origin string) (string, string, string, error) { return scheme, hostname, port, nil } -// DialWebsocketWithDialer creates a new RPC client that communicates with a JSON-RPC server -// that is listening on the given endpoint using the provided dialer. +// DialWebsocketWithDialer creates a new RPC client using WebSocket. +// +// The context is used for the initial connection establishment. It does not +// affect subsequent interactions with the client. +// +// Deprecated: use DialOptions and the WithWebsocketDialer option. func DialWebsocketWithDialer(ctx context.Context, endpoint, origin string, dialer websocket.Dialer) (*Client, error) { - endpoint, header, err := wsClientHeaders(endpoint, origin) + cfg := new(clientConfig) + cfg.wsDialer = &dialer + if origin != "" { + cfg.setHeader("origin", origin) + } + connect, err := newClientTransportWS(endpoint, cfg) if err != nil { return nil, err } - return newClient(ctx, func(ctx context.Context) (ServerCodec, error) { - conn, resp, err := dialer.DialContext(ctx, endpoint, header) - if err != nil { - hErr := wsHandshakeError{err: err} - if resp != nil { - hErr.status = resp.Status - } - return nil, hErr - } - return newWebsocketCodec(conn), nil - }) + return newClient(ctx, cfg, connect) } // DialWebsocket creates a new RPC client that communicates with a JSON-RPC server @@ -207,13 +210,58 @@ func DialWebsocketWithDialer(ctx context.Context, endpoint, origin string, diale // The context is used for the initial connection establishment. It does not // affect subsequent interactions with the client. func DialWebsocket(ctx context.Context, endpoint, origin string) (*Client, error) { - dialer := websocket.Dialer{ - ReadBufferSize: wsReadBuffer, - WriteBufferSize: wsWriteBuffer, - WriteBufferPool: wsBufferPool, - Proxy: http.ProxyFromEnvironment, + cfg := new(clientConfig) + if origin != "" { + cfg.setHeader("origin", origin) + } + connect, err := newClientTransportWS(endpoint, cfg) + if err != nil { + return nil, err } - return DialWebsocketWithDialer(ctx, endpoint, origin, dialer) + return newClient(ctx, cfg, connect) +} + +func newClientTransportWS(endpoint string, cfg *clientConfig) (reconnectFunc, error) { + dialer := cfg.wsDialer + if dialer == nil { + dialer = &websocket.Dialer{ + ReadBufferSize: wsReadBuffer, + WriteBufferSize: wsWriteBuffer, + WriteBufferPool: wsBufferPool, + Proxy: http.ProxyFromEnvironment, + } + } + + dialURL, header, err := wsClientHeaders(endpoint, "") + if err != nil { + return nil, err + } + for key, values := range cfg.httpHeaders { + header[key] = values + } + + connect := func(ctx context.Context) (ServerCodec, error) { + header := header.Clone() + if cfg.httpAuth != nil { + if err := cfg.httpAuth(header); err != nil { + return nil, err + } + } + conn, resp, err := dialer.DialContext(ctx, dialURL, header) + if err != nil { + hErr := wsHandshakeError{err: err} + if resp != nil { + hErr.status = resp.Status + } + return nil, hErr + } + messageSizeLimit := int64(wsDefaultReadLimit) + if cfg.wsMessageSizeLimit != nil && *cfg.wsMessageSizeLimit >= 0 { + messageSizeLimit = *cfg.wsMessageSizeLimit + } + return newWebsocketCodec(conn, dialURL, header, messageSizeLimit), nil + } + return connect, nil } func wsClientHeaders(endpoint, origin string) (string, http.Header, error) { @@ -236,26 +284,40 @@ func wsClientHeaders(endpoint, origin string) (string, http.Header, error) { type websocketCodec struct { *jsonCodec conn *websocket.Conn + info PeerInfo - wg sync.WaitGroup - pingReset chan struct{} + wg sync.WaitGroup + pingReset chan struct{} + pongReceived chan struct{} } -func newWebsocketCodec(conn *websocket.Conn) ServerCodec { - conn.SetReadLimit(wsMessageSizeLimit) - conn.SetPongHandler(func(appData string) error { - conn.SetReadDeadline(time.Time{}) - return nil - }) - - encode := func(v any, isErrorResponse bool) error { +func newWebsocketCodec(conn *websocket.Conn, host string, req http.Header, readLimit int64) ServerCodec { + conn.SetReadLimit(readLimit) + encode := func(v interface{}, isErrorResponse bool) error { return conn.WriteJSON(v) } wc := &websocketCodec{ - jsonCodec: NewFuncCodec(conn, encode, conn.ReadJSON).(*jsonCodec), - conn: conn, - pingReset: make(chan struct{}, 1), + jsonCodec: NewFuncCodec(conn, encode, conn.ReadJSON).(*jsonCodec), + conn: conn, + pingReset: make(chan struct{}, 1), + pongReceived: make(chan struct{}), + info: PeerInfo{ + Transport: "ws", + RemoteAddr: conn.RemoteAddr().String(), + }, } + // Fill in connection details. + wc.info.HTTP.Host = host + wc.info.HTTP.Origin = req.Get("Origin") + wc.info.HTTP.UserAgent = req.Get("User-Agent") + // Start pinger. + conn.SetPongHandler(func(appData string) error { + select { + case wc.pongReceived <- struct{}{}: + case <-wc.closed(): + } + return nil + }) wc.wg.Add(1) go wc.pingLoop() return wc @@ -266,7 +328,11 @@ func (wc *websocketCodec) close() { wc.wg.Wait() } -func (wc *websocketCodec) writeJSON(ctx context.Context, v any, isError bool) error { +func (wc *websocketCodec) peerInfo() PeerInfo { + return wc.info +} + +func (wc *websocketCodec) writeJSON(ctx context.Context, v interface{}, isError bool) error { err := wc.jsonCodec.writeJSON(ctx, v, isError) if err == nil { // Notify pingLoop to delay the next idle ping. @@ -280,26 +346,31 @@ func (wc *websocketCodec) writeJSON(ctx context.Context, v any, isError bool) er // pingLoop sends periodic ping frames when the connection is idle. func (wc *websocketCodec) pingLoop() { - var timer = time.NewTimer(wsPingInterval) + var pingTimer = time.NewTimer(wsPingInterval) defer wc.wg.Done() - defer timer.Stop() + defer pingTimer.Stop() for { select { case <-wc.closed(): return + case <-wc.pingReset: - if !timer.Stop() { - <-timer.C + if !pingTimer.Stop() { + <-pingTimer.C } - timer.Reset(wsPingInterval) - case <-timer.C: + pingTimer.Reset(wsPingInterval) + + case <-pingTimer.C: wc.jsonCodec.encMu.Lock() wc.conn.SetWriteDeadline(time.Now().Add(wsPingWriteTimeout)) wc.conn.WriteMessage(websocket.PingMessage, nil) wc.conn.SetReadDeadline(time.Now().Add(wsPongTimeout)) wc.jsonCodec.encMu.Unlock() - timer.Reset(wsPingInterval) + pingTimer.Reset(wsPingInterval) + + case <-wc.pongReceived: + wc.conn.SetReadDeadline(time.Time{}) } } } diff --git a/rpc/websocket_test.go b/rpc/websocket_test.go index 5d738d9e75..d7fec003da 100644 --- a/rpc/websocket_test.go +++ b/rpc/websocket_test.go @@ -1,5 +1,5 @@ // Copyright 2018 The go-ethereum Authors -// This file is part of The go-ethereum library. +// This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by @@ -12,21 +12,17 @@ // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License -// along with The go-ethereum library. If not, see . +// along with the go-ethereum library. If not, see . package rpc import ( "context" - "io" + "errors" "net" "net/http" "net/http/httptest" - "net/http/httputil" - "net/url" - "reflect" "strings" - "sync/atomic" "testing" "time" @@ -69,14 +65,14 @@ func TestWebsocketOriginCheck(t *testing.T) { t.Fatal("no error for wrong origin") } wantErr := wsHandshakeError{websocket.ErrBadHandshake, "403 Forbidden"} - if !reflect.DeepEqual(err, wantErr) { + if !errors.Is(err, wantErr) { t.Fatalf("wrong error for wrong origin: %q", err) } // Connections without origin header should work. client, err = DialWebsocket(context.Background(), wsURL, "") if err != nil { - t.Fatal("error for empty origin") + t.Fatalf("error for empty origin: %v", err) } client.Close() } @@ -101,7 +97,7 @@ func TestWebsocketLargeCall(t *testing.T) { // This call sends slightly less than the limit and should work. var result echoResult - arg := strings.Repeat("x", maxRequestContentLength-200) + arg := strings.Repeat("x", defaultBodyLimit-200) if err := client.Call(&result, "test_echo", arg, 1); err != nil { t.Fatalf("valid call didn't work: %v", err) } @@ -110,13 +106,111 @@ func TestWebsocketLargeCall(t *testing.T) { } // This call sends twice the allowed size and shouldn't work. - arg = strings.Repeat("x", maxRequestContentLength*2) + arg = strings.Repeat("x", defaultBodyLimit*2) err = client.Call(&result, "test_echo", arg) if err == nil { t.Fatal("no error for too large call") } } +// This test checks whether the wsMessageSizeLimit option is obeyed. +func TestWebsocketLargeRead(t *testing.T) { + t.Parallel() + + var ( + srv = newTestServer() + httpsrv = httptest.NewServer(srv.WebsocketHandler([]string{"*"})) + wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") + ) + defer srv.Stop() + defer httpsrv.Close() + + testLimit := func(limit *int64) { + opts := []ClientOption{} + expLimit := int64(wsDefaultReadLimit) + if limit != nil && *limit >= 0 { + opts = append(opts, WithWebsocketMessageSizeLimit(*limit)) + if *limit > 0 { + expLimit = *limit // 0 means infinite + } + } + client, err := DialOptions(context.Background(), wsURL, opts...) + if err != nil { + t.Fatalf("can't dial: %v", err) + } + defer client.Close() + // Remove some bytes for json encoding overhead. + underLimit := int(expLimit - 128) + overLimit := expLimit + 1 + if expLimit == wsDefaultReadLimit { + // No point trying the full 32MB in tests. Just sanity-check that + // it's not obviously limited. + underLimit = 1024 + overLimit = -1 + } + var res string + // Check under limit + if err = client.Call(&res, "test_repeat", "A", underLimit); err != nil { + t.Fatalf("unexpected error with limit %d: %v", expLimit, err) + } + if len(res) != underLimit || strings.Count(res, "A") != underLimit { + t.Fatal("incorrect data") + } + // Check over limit + if overLimit > 0 { + err = client.Call(&res, "test_repeat", "A", expLimit+1) + if err == nil || err != websocket.ErrReadLimit { + t.Fatalf("wrong error with limit %d: %v expecting %v", expLimit, err, websocket.ErrReadLimit) + } + } + } + ptr := func(v int64) *int64 { return &v } + + testLimit(ptr(-1)) // Should be ignored (use default) + testLimit(ptr(0)) // Should be ignored (use default) + testLimit(nil) // Should be ignored (use default) + testLimit(ptr(200)) + testLimit(ptr(wsDefaultReadLimit * 2)) +} + +func TestWebsocketPeerInfo(t *testing.T) { + t.Parallel() + + var ( + s = newTestServer() + ts = httptest.NewServer(s.WebsocketHandler([]string{"origin.example.com"})) + tsurl = "ws:" + strings.TrimPrefix(ts.URL, "http:") + ) + defer s.Stop() + defer ts.Close() + + ctx := context.Background() + c, err := DialWebsocket(ctx, tsurl, "origin.example.com") + if err != nil { + t.Fatal(err) + } + defer c.Close() + + // Request peer information. + var connInfo PeerInfo + if err := c.Call(&connInfo, "test_peerInfo"); err != nil { + t.Fatal(err) + } + + if connInfo.RemoteAddr == "" { + t.Error("RemoteAddr not set") + } + if connInfo.Transport != "ws" { + t.Errorf("wrong Transport %q", connInfo.Transport) + } + if connInfo.HTTP.UserAgent != "Go-http-client/1.1" { + t.Errorf("wrong HTTP.UserAgent %q", connInfo.HTTP.UserAgent) + } + if connInfo.HTTP.Origin != "origin.example.com" { + t.Errorf("wrong HTTP.Origin %q", connInfo.HTTP.UserAgent) + } +} + // This test checks that client handles WebSocket ping frames correctly. func TestClientWebsocketPing(t *testing.T) { t.Parallel() @@ -167,6 +261,8 @@ func TestClientWebsocketPing(t *testing.T) { // This checks that the websocket transport can deal with large messages. func TestClientWebsocketLargeMessage(t *testing.T) { + t.Parallel() + var ( srv = NewServer() httpsrv = httptest.NewServer(srv.WebsocketHandler(nil)) @@ -175,13 +271,14 @@ func TestClientWebsocketLargeMessage(t *testing.T) { defer srv.Stop() defer httpsrv.Close() - respLength := wsMessageSizeLimit - 50 + respLength := wsDefaultReadLimit - 50 srv.RegisterName("test", largeRespService{respLength}) c, err := DialWebsocket(context.Background(), wsURL, "") if err != nil { t.Fatal(err) } + defer c.Close() var r string if err := c.Call(&r, "test_largeResp"); err != nil { @@ -192,63 +289,6 @@ func TestClientWebsocketLargeMessage(t *testing.T) { } } -func TestClientWebsocketSevered(t *testing.T) { - t.Parallel() - - var ( - server = wsPingTestServer(t, nil) - ctx = context.Background() - ) - defer server.Shutdown(ctx) - - u, err := url.Parse("http://" + server.Addr) - if err != nil { - t.Fatal(err) - } - rproxy := httputil.NewSingleHostReverseProxy(u) - var severable *severableReadWriteCloser - rproxy.ModifyResponse = func(response *http.Response) error { - severable = &severableReadWriteCloser{ReadWriteCloser: response.Body.(io.ReadWriteCloser)} - response.Body = severable - return nil - } - frontendProxy := httptest.NewServer(rproxy) - defer frontendProxy.Close() - - wsURL := "ws:" + strings.TrimPrefix(frontendProxy.URL, "http:") - client, err := DialWebsocket(ctx, wsURL, "") - if err != nil { - t.Fatalf("client dial error: %v", err) - } - defer client.Close() - - resultChan := make(chan int) - sub, err := client.CortexSubscribe(ctx, resultChan, "foo") - if err != nil { - t.Fatalf("client subscribe error: %v", err) - } - - // sever the connection - severable.Sever() - - // Wait for subscription error. - timeout := time.NewTimer(10 * wsPingInterval) - defer timeout.Stop() - for { - select { - case err := <-sub.Err(): - t.Log("client subscription error:", err) - return - case result := <-resultChan: - t.Error("unexpected result:", result) - return - case <-timeout.C: - t.Error("didn't get any error within the test timeout") - return - } - } -} - // wsPingTestServer runs a WebSocket server which accepts a single subscription request. // When a value arrives on sendPing, the server sends a ping frame, waits for a matching // pong and finally delivers a single subscription result. @@ -285,10 +325,10 @@ func wsPingTestServer(t *testing.T, sendPing <-chan struct{}) *http.Server { } func wsPingTestHandler(t *testing.T, conn *websocket.Conn, shutdown, sendPing <-chan struct{}) { - // Canned responses for the ctxc_subscribe call in TestClientWebsocketPing. + // Canned responses for the eth_subscribe call in TestClientWebsocketPing. const ( subResp = `{"jsonrpc":"2.0","id":1,"result":"0x00"}` - subNotify = `{"jsonrpc":"2.0","method":"ctxc_subscription","params":{"subscription":"0x00","result":1}}` + subNotify = `{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x00","result":1}}` ) // Handle subscribe request. @@ -352,30 +392,76 @@ func wsPingTestHandler(t *testing.T, conn *websocket.Conn, shutdown, sendPing <- } } -// severableReadWriteCloser wraps an io.ReadWriteCloser and provides a Sever() method to drop writes and read empty. -type severableReadWriteCloser struct { - io.ReadWriteCloser - severed atomic.Int32 // atomic -} +func TestWebsocketMethodNameLengthLimit(t *testing.T) { + t.Parallel() -func (s *severableReadWriteCloser) Sever() { - s.severed.Add(1) -} + var ( + srv = newTestServer() + httpsrv = httptest.NewServer(srv.WebsocketHandler([]string{"*"})) + wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") + ) + defer srv.Stop() + defer httpsrv.Close() -func (s *severableReadWriteCloser) Read(p []byte) (n int, err error) { - if s.severed.Load() > 0 { - return 0, nil + client, err := DialWebsocket(context.Background(), wsURL, "") + if err != nil { + t.Fatalf("can't dial: %v", err) } - return s.ReadWriteCloser.Read(p) -} + defer client.Close() -func (s *severableReadWriteCloser) Write(p []byte) (n int, err error) { - if s.severed.Load() > 0 { - return len(p), nil + // Test cases + tests := []struct { + name string + method string + params []interface{} + expectedError string + isSubscription bool + }{ + { + name: "valid method name", + method: "test_echo", + params: []interface{}{"test", 1}, + expectedError: "", + isSubscription: false, + }, + { + name: "method name too long", + method: "test_" + string(make([]byte, maxMethodNameLength+1)), + params: []interface{}{"test", 1}, + expectedError: "method name too long", + isSubscription: false, + }, + { + name: "valid subscription", + method: "nftest_subscribe", + params: []interface{}{"someSubscription", 1, 2}, + expectedError: "", + isSubscription: true, + }, + { + name: "subscription name too long", + method: string(make([]byte, maxMethodNameLength+1)) + "_subscribe", + params: []interface{}{"newHeads"}, + expectedError: "subscription name too long", + isSubscription: true, + }, } - return s.ReadWriteCloser.Write(p) -} -func (s *severableReadWriteCloser) Close() error { - return s.ReadWriteCloser.Close() + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var result interface{} + err := client.Call(&result, tt.method, tt.params...) + if tt.expectedError == "" { + if err != nil { + t.Errorf("unexpected error: %v", err) + } + } else { + if err == nil { + t.Error("expected error, got nil") + } else if !strings.Contains(err.Error(), tt.expectedError) { + t.Errorf("expected error containing %q, got %q", tt.expectedError, err.Error()) + } + } + }) + } } diff --git a/vendor/github.com/CortexFoundation/robot/monitor.go b/vendor/github.com/CortexFoundation/robot/monitor.go index afce6c35a8..d08fad7494 100644 --- a/vendor/github.com/CortexFoundation/robot/monitor.go +++ b/vendor/github.com/CortexFoundation/robot/monitor.go @@ -579,7 +579,7 @@ func (m *Monitor) syncLastBlock() uint64 { if maxNumber-i >= m.scope { blocks, rpcErr := m.rpcBatchBlockByNumber(i, i+m.scope) if rpcErr != nil { - log.Error("Sync old block failed", "number", i, "error", rpcErr) + log.Error("Batch sync old block failed", "current", maxNumber, "number", i, "batch", m.scope, "error", rpcErr) m.lastNumber.Store(i - 1) return 0 } diff --git a/vendor/github.com/CortexFoundation/robot/rpc.go b/vendor/github.com/CortexFoundation/robot/rpc.go index b9f702789f..f8fd088308 100644 --- a/vendor/github.com/CortexFoundation/robot/rpc.go +++ b/vendor/github.com/CortexFoundation/robot/rpc.go @@ -98,9 +98,9 @@ func (m *Monitor) rpcBatchBlockByNumber(from, to uint64) ([]*types.Block, error) return nil, err } - for i := range reqs { - if reqs[i].Error != nil { - return nil, reqs[i].Error + for i, req := range reqs { + if req.Error != nil { + return nil, req.Error } if result[i] == nil { return nil, fmt.Errorf("got null block %d", i) diff --git a/vendor/github.com/Microsoft/go-winio/.gitattributes b/vendor/github.com/Microsoft/go-winio/.gitattributes new file mode 100644 index 0000000000..94f480de94 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/vendor/github.com/Microsoft/go-winio/.gitignore b/vendor/github.com/Microsoft/go-winio/.gitignore new file mode 100644 index 0000000000..815e20660e --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/.gitignore @@ -0,0 +1,10 @@ +.vscode/ + +*.exe + +# testing +testdata + +# go workspaces +go.work +go.work.sum diff --git a/vendor/github.com/Microsoft/go-winio/.golangci.yml b/vendor/github.com/Microsoft/go-winio/.golangci.yml new file mode 100644 index 0000000000..faedfe937a --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/.golangci.yml @@ -0,0 +1,147 @@ +linters: + enable: + # style + - containedctx # struct contains a context + - dupl # duplicate code + - errname # erorrs are named correctly + - nolintlint # "//nolint" directives are properly explained + - revive # golint replacement + - unconvert # unnecessary conversions + - wastedassign + + # bugs, performance, unused, etc ... + - contextcheck # function uses a non-inherited context + - errorlint # errors not wrapped for 1.13 + - exhaustive # check exhaustiveness of enum switch statements + - gofmt # files are gofmt'ed + - gosec # security + - nilerr # returns nil even with non-nil error + - thelper # test helpers without t.Helper() + - unparam # unused function params + +issues: + exclude-dirs: + - pkg/etw/sample + + exclude-rules: + # err is very often shadowed in nested scopes + - linters: + - govet + text: '^shadow: declaration of "err" shadows declaration' + + # ignore long lines for skip autogen directives + - linters: + - revive + text: "^line-length-limit: " + source: "^//(go:generate|sys) " + + #TODO: remove after upgrading to go1.18 + # ignore comment spacing for nolint and sys directives + - linters: + - revive + text: "^comment-spacings: no space between comment delimiter and comment text" + source: "//(cspell:|nolint:|sys |todo)" + + # not on go 1.18 yet, so no any + - linters: + - revive + text: "^use-any: since GO 1.18 'interface{}' can be replaced by 'any'" + + # allow unjustified ignores of error checks in defer statements + - linters: + - nolintlint + text: "^directive `//nolint:errcheck` should provide explanation" + source: '^\s*defer ' + + # allow unjustified ignores of error lints for io.EOF + - linters: + - nolintlint + text: "^directive `//nolint:errorlint` should provide explanation" + source: '[=|!]= io.EOF' + + +linters-settings: + exhaustive: + default-signifies-exhaustive: true + govet: + enable-all: true + disable: + # struct order is often for Win32 compat + # also, ignore pointer bytes/GC issues for now until performance becomes an issue + - fieldalignment + nolintlint: + require-explanation: true + require-specific: true + revive: + # revive is more configurable than static check, so likely the preferred alternative to static-check + # (once the perf issue is solved: https://github.com/golangci/golangci-lint/issues/2997) + enable-all-rules: + true + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md + rules: + # rules with required arguments + - name: argument-limit + disabled: true + - name: banned-characters + disabled: true + - name: cognitive-complexity + disabled: true + - name: cyclomatic + disabled: true + - name: file-header + disabled: true + - name: function-length + disabled: true + - name: function-result-limit + disabled: true + - name: max-public-structs + disabled: true + # geneally annoying rules + - name: add-constant # complains about any and all strings and integers + disabled: true + - name: confusing-naming # we frequently use "Foo()" and "foo()" together + disabled: true + - name: flag-parameter # excessive, and a common idiom we use + disabled: true + - name: unhandled-error # warns over common fmt.Print* and io.Close; rely on errcheck instead + disabled: true + # general config + - name: line-length-limit + arguments: + - 140 + - name: var-naming + arguments: + - [] + - - CID + - CRI + - CTRD + - DACL + - DLL + - DOS + - ETW + - FSCTL + - GCS + - GMSA + - HCS + - HV + - IO + - LCOW + - LDAP + - LPAC + - LTSC + - MMIO + - NT + - OCI + - PMEM + - PWSH + - RX + - SACl + - SID + - SMB + - TX + - VHD + - VHDX + - VMID + - VPCI + - WCOW + - WIM diff --git a/vendor/github.com/Microsoft/go-winio/CODEOWNERS b/vendor/github.com/Microsoft/go-winio/CODEOWNERS new file mode 100644 index 0000000000..ae1b4942b9 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/CODEOWNERS @@ -0,0 +1 @@ + * @microsoft/containerplat diff --git a/vendor/github.com/Microsoft/go-winio/LICENSE b/vendor/github.com/Microsoft/go-winio/LICENSE new file mode 100644 index 0000000000..b8b569d774 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/Microsoft/go-winio/README.md b/vendor/github.com/Microsoft/go-winio/README.md new file mode 100644 index 0000000000..7474b4f0b6 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/README.md @@ -0,0 +1,89 @@ +# go-winio [![Build Status](https://github.com/microsoft/go-winio/actions/workflows/ci.yml/badge.svg)](https://github.com/microsoft/go-winio/actions/workflows/ci.yml) + +This repository contains utilities for efficiently performing Win32 IO operations in +Go. Currently, this is focused on accessing named pipes and other file handles, and +for using named pipes as a net transport. + +This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go +to reuse the thread to schedule another goroutine. This limits support to Windows Vista and +newer operating systems. This is similar to the implementation of network sockets in Go's net +package. + +Please see the LICENSE file for licensing information. + +## Contributing + +This project welcomes contributions and suggestions. +Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that +you have the right to, and actually do, grant us the rights to use your contribution. +For details, visit [Microsoft CLA](https://cla.microsoft.com). + +When you submit a pull request, a CLA-bot will automatically determine whether you need to +provide a CLA and decorate the PR appropriately (e.g., label, comment). +Simply follow the instructions provided by the bot. +You will only need to do this once across all repos using our CLA. + +Additionally, the pull request pipeline requires the following steps to be performed before +mergining. + +### Code Sign-Off + +We require that contributors sign their commits using [`git commit --signoff`][git-commit-s] +to certify they either authored the work themselves or otherwise have permission to use it in this project. + +A range of commits can be signed off using [`git rebase --signoff`][git-rebase-s]. + +Please see [the developer certificate](https://developercertificate.org) for more info, +as well as to make sure that you can attest to the rules listed. +Our CI uses the DCO Github app to ensure that all commits in a given PR are signed-off. + +### Linting + +Code must pass a linting stage, which uses [`golangci-lint`][lint]. +The linting settings are stored in [`.golangci.yaml`](./.golangci.yaml), and can be run +automatically with VSCode by adding the following to your workspace or folder settings: + +```json + "go.lintTool": "golangci-lint", + "go.lintOnSave": "package", +``` + +Additional editor [integrations options are also available][lint-ide]. + +Alternatively, `golangci-lint` can be [installed locally][lint-install] and run from the repo root: + +```shell +# use . or specify a path to only lint a package +# to show all lint errors, use flags "--max-issues-per-linter=0 --max-same-issues=0" +> golangci-lint run ./... +``` + +### Go Generate + +The pipeline checks that auto-generated code, via `go generate`, are up to date. + +This can be done for the entire repo: + +```shell +> go generate ./... +``` + +## Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## Special Thanks + +Thanks to [natefinch][natefinch] for the inspiration for this library. +See [npipe](https://github.com/natefinch/npipe) for another named pipe implementation. + +[lint]: https://golangci-lint.run/ +[lint-ide]: https://golangci-lint.run/usage/integrations/#editor-integration +[lint-install]: https://golangci-lint.run/usage/install/#local-installation + +[git-commit-s]: https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s +[git-rebase-s]: https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---signoff + +[natefinch]: https://github.com/natefinch diff --git a/vendor/github.com/Microsoft/go-winio/SECURITY.md b/vendor/github.com/Microsoft/go-winio/SECURITY.md new file mode 100644 index 0000000000..869fdfe2b2 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/vendor/github.com/Microsoft/go-winio/backup.go b/vendor/github.com/Microsoft/go-winio/backup.go new file mode 100644 index 0000000000..b54341daac --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/backup.go @@ -0,0 +1,287 @@ +//go:build windows +// +build windows + +package winio + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + "os" + "runtime" + "unicode/utf16" + + "github.com/Microsoft/go-winio/internal/fs" + "golang.org/x/sys/windows" +) + +//sys backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead +//sys backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite + +const ( + BackupData = uint32(iota + 1) + BackupEaData + BackupSecurity + BackupAlternateData + BackupLink + BackupPropertyData + BackupObjectId //revive:disable-line:var-naming ID, not Id + BackupReparseData + BackupSparseBlock + BackupTxfsData +) + +const ( + StreamSparseAttributes = uint32(8) +) + +//nolint:revive // var-naming: ALL_CAPS +const ( + WRITE_DAC = windows.WRITE_DAC + WRITE_OWNER = windows.WRITE_OWNER + ACCESS_SYSTEM_SECURITY = windows.ACCESS_SYSTEM_SECURITY +) + +// BackupHeader represents a backup stream of a file. +type BackupHeader struct { + //revive:disable-next-line:var-naming ID, not Id + Id uint32 // The backup stream ID + Attributes uint32 // Stream attributes + Size int64 // The size of the stream in bytes + Name string // The name of the stream (for BackupAlternateData only). + Offset int64 // The offset of the stream in the file (for BackupSparseBlock only). +} + +type win32StreamID struct { + StreamID uint32 + Attributes uint32 + Size uint64 + NameSize uint32 +} + +// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series +// of BackupHeader values. +type BackupStreamReader struct { + r io.Reader + bytesLeft int64 +} + +// NewBackupStreamReader produces a BackupStreamReader from any io.Reader. +func NewBackupStreamReader(r io.Reader) *BackupStreamReader { + return &BackupStreamReader{r, 0} +} + +// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if +// it was not completely read. +func (r *BackupStreamReader) Next() (*BackupHeader, error) { + if r.bytesLeft > 0 { //nolint:nestif // todo: flatten this + if s, ok := r.r.(io.Seeker); ok { + // Make sure Seek on io.SeekCurrent sometimes succeeds + // before trying the actual seek. + if _, err := s.Seek(0, io.SeekCurrent); err == nil { + if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil { + return nil, err + } + r.bytesLeft = 0 + } + } + if _, err := io.Copy(io.Discard, r); err != nil { + return nil, err + } + } + var wsi win32StreamID + if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil { + return nil, err + } + hdr := &BackupHeader{ + Id: wsi.StreamID, + Attributes: wsi.Attributes, + Size: int64(wsi.Size), + } + if wsi.NameSize != 0 { + name := make([]uint16, int(wsi.NameSize/2)) + if err := binary.Read(r.r, binary.LittleEndian, name); err != nil { + return nil, err + } + hdr.Name = windows.UTF16ToString(name) + } + if wsi.StreamID == BackupSparseBlock { + if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil { + return nil, err + } + hdr.Size -= 8 + } + r.bytesLeft = hdr.Size + return hdr, nil +} + +// Read reads from the current backup stream. +func (r *BackupStreamReader) Read(b []byte) (int, error) { + if r.bytesLeft == 0 { + return 0, io.EOF + } + if int64(len(b)) > r.bytesLeft { + b = b[:r.bytesLeft] + } + n, err := r.r.Read(b) + r.bytesLeft -= int64(n) + if err == io.EOF { + err = io.ErrUnexpectedEOF + } else if r.bytesLeft == 0 && err == nil { + err = io.EOF + } + return n, err +} + +// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API. +type BackupStreamWriter struct { + w io.Writer + bytesLeft int64 +} + +// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer. +func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter { + return &BackupStreamWriter{w, 0} +} + +// WriteHeader writes the next backup stream header and prepares for calls to Write(). +func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error { + if w.bytesLeft != 0 { + return fmt.Errorf("missing %d bytes", w.bytesLeft) + } + name := utf16.Encode([]rune(hdr.Name)) + wsi := win32StreamID{ + StreamID: hdr.Id, + Attributes: hdr.Attributes, + Size: uint64(hdr.Size), + NameSize: uint32(len(name) * 2), + } + if hdr.Id == BackupSparseBlock { + // Include space for the int64 block offset + wsi.Size += 8 + } + if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil { + return err + } + if len(name) != 0 { + if err := binary.Write(w.w, binary.LittleEndian, name); err != nil { + return err + } + } + if hdr.Id == BackupSparseBlock { + if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil { + return err + } + } + w.bytesLeft = hdr.Size + return nil +} + +// Write writes to the current backup stream. +func (w *BackupStreamWriter) Write(b []byte) (int, error) { + if w.bytesLeft < int64(len(b)) { + return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft) + } + n, err := w.w.Write(b) + w.bytesLeft -= int64(n) + return n, err +} + +// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API. +type BackupFileReader struct { + f *os.File + includeSecurity bool + ctx uintptr +} + +// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true, +// Read will attempt to read the security descriptor of the file. +func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader { + r := &BackupFileReader{f, includeSecurity, 0} + return r +} + +// Read reads a backup stream from the file by calling the Win32 API BackupRead(). +func (r *BackupFileReader) Read(b []byte) (int, error) { + var bytesRead uint32 + err := backupRead(windows.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx) + if err != nil { + return 0, &os.PathError{Op: "BackupRead", Path: r.f.Name(), Err: err} + } + runtime.KeepAlive(r.f) + if bytesRead == 0 { + return 0, io.EOF + } + return int(bytesRead), nil +} + +// Close frees Win32 resources associated with the BackupFileReader. It does not close +// the underlying file. +func (r *BackupFileReader) Close() error { + if r.ctx != 0 { + _ = backupRead(windows.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx) + runtime.KeepAlive(r.f) + r.ctx = 0 + } + return nil +} + +// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API. +type BackupFileWriter struct { + f *os.File + includeSecurity bool + ctx uintptr +} + +// NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true, +// Write() will attempt to restore the security descriptor from the stream. +func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter { + w := &BackupFileWriter{f, includeSecurity, 0} + return w +} + +// Write restores a portion of the file using the provided backup stream. +func (w *BackupFileWriter) Write(b []byte) (int, error) { + var bytesWritten uint32 + err := backupWrite(windows.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx) + if err != nil { + return 0, &os.PathError{Op: "BackupWrite", Path: w.f.Name(), Err: err} + } + runtime.KeepAlive(w.f) + if int(bytesWritten) != len(b) { + return int(bytesWritten), errors.New("not all bytes could be written") + } + return len(b), nil +} + +// Close frees Win32 resources associated with the BackupFileWriter. It does not +// close the underlying file. +func (w *BackupFileWriter) Close() error { + if w.ctx != 0 { + _ = backupWrite(windows.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx) + runtime.KeepAlive(w.f) + w.ctx = 0 + } + return nil +} + +// OpenForBackup opens a file or directory, potentially skipping access checks if the backup +// or restore privileges have been acquired. +// +// If the file opened was a directory, it cannot be used with Readdir(). +func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) { + h, err := fs.CreateFile(path, + fs.AccessMask(access), + fs.FileShareMode(share), + nil, + fs.FileCreationDisposition(createmode), + fs.FILE_FLAG_BACKUP_SEMANTICS|fs.FILE_FLAG_OPEN_REPARSE_POINT, + 0, + ) + if err != nil { + err = &os.PathError{Op: "open", Path: path, Err: err} + return nil, err + } + return os.NewFile(uintptr(h), path), nil +} diff --git a/vendor/github.com/Microsoft/go-winio/doc.go b/vendor/github.com/Microsoft/go-winio/doc.go new file mode 100644 index 0000000000..1f5bfe2d54 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/doc.go @@ -0,0 +1,22 @@ +// This package provides utilities for efficiently performing Win32 IO operations in Go. +// Currently, this package is provides support for genreal IO and management of +// - named pipes +// - files +// - [Hyper-V sockets] +// +// This code is similar to Go's [net] package, and uses IO completion ports to avoid +// blocking IO on system threads, allowing Go to reuse the thread to schedule other goroutines. +// +// This limits support to Windows Vista and newer operating systems. +// +// Additionally, this package provides support for: +// - creating and managing GUIDs +// - writing to [ETW] +// - opening and manageing VHDs +// - parsing [Windows Image files] +// - auto-generating Win32 API code +// +// [Hyper-V sockets]: https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service +// [ETW]: https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/event-tracing-for-windows--etw- +// [Windows Image files]: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/work-with-windows-images +package winio diff --git a/vendor/github.com/Microsoft/go-winio/ea.go b/vendor/github.com/Microsoft/go-winio/ea.go new file mode 100644 index 0000000000..e104dbdfdf --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/ea.go @@ -0,0 +1,137 @@ +package winio + +import ( + "bytes" + "encoding/binary" + "errors" +) + +type fileFullEaInformation struct { + NextEntryOffset uint32 + Flags uint8 + NameLength uint8 + ValueLength uint16 +} + +var ( + fileFullEaInformationSize = binary.Size(&fileFullEaInformation{}) + + errInvalidEaBuffer = errors.New("invalid extended attribute buffer") + errEaNameTooLarge = errors.New("extended attribute name too large") + errEaValueTooLarge = errors.New("extended attribute value too large") +) + +// ExtendedAttribute represents a single Windows EA. +type ExtendedAttribute struct { + Name string + Value []byte + Flags uint8 +} + +func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) { + var info fileFullEaInformation + err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info) + if err != nil { + err = errInvalidEaBuffer + return ea, nb, err + } + + nameOffset := fileFullEaInformationSize + nameLen := int(info.NameLength) + valueOffset := nameOffset + int(info.NameLength) + 1 + valueLen := int(info.ValueLength) + nextOffset := int(info.NextEntryOffset) + if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) { + err = errInvalidEaBuffer + return ea, nb, err + } + + ea.Name = string(b[nameOffset : nameOffset+nameLen]) + ea.Value = b[valueOffset : valueOffset+valueLen] + ea.Flags = info.Flags + if info.NextEntryOffset != 0 { + nb = b[info.NextEntryOffset:] + } + return ea, nb, err +} + +// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION +// buffer retrieved from BackupRead, ZwQueryEaFile, etc. +func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) { + for len(b) != 0 { + ea, nb, err := parseEa(b) + if err != nil { + return nil, err + } + + eas = append(eas, ea) + b = nb + } + return eas, err +} + +func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error { + if int(uint8(len(ea.Name))) != len(ea.Name) { + return errEaNameTooLarge + } + if int(uint16(len(ea.Value))) != len(ea.Value) { + return errEaValueTooLarge + } + entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value)) + withPadding := (entrySize + 3) &^ 3 + nextOffset := uint32(0) + if !last { + nextOffset = withPadding + } + info := fileFullEaInformation{ + NextEntryOffset: nextOffset, + Flags: ea.Flags, + NameLength: uint8(len(ea.Name)), + ValueLength: uint16(len(ea.Value)), + } + + err := binary.Write(buf, binary.LittleEndian, &info) + if err != nil { + return err + } + + _, err = buf.Write([]byte(ea.Name)) + if err != nil { + return err + } + + err = buf.WriteByte(0) + if err != nil { + return err + } + + _, err = buf.Write(ea.Value) + if err != nil { + return err + } + + _, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize]) + if err != nil { + return err + } + + return nil +} + +// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION +// buffer for use with BackupWrite, ZwSetEaFile, etc. +func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) { + var buf bytes.Buffer + for i := range eas { + last := false + if i == len(eas)-1 { + last = true + } + + err := writeEa(&buf, &eas[i], last) + if err != nil { + return nil, err + } + } + return buf.Bytes(), nil +} diff --git a/vendor/github.com/Microsoft/go-winio/file.go b/vendor/github.com/Microsoft/go-winio/file.go new file mode 100644 index 0000000000..fe82a180db --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/file.go @@ -0,0 +1,320 @@ +//go:build windows +// +build windows + +package winio + +import ( + "errors" + "io" + "runtime" + "sync" + "sync/atomic" + "syscall" + "time" + + "golang.org/x/sys/windows" +) + +//sys cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) = CancelIoEx +//sys createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) = CreateIoCompletionPort +//sys getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus +//sys setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes +//sys wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) = ws2_32.WSAGetOverlappedResult + +var ( + ErrFileClosed = errors.New("file has already been closed") + ErrTimeout = &timeoutError{} +) + +type timeoutError struct{} + +func (*timeoutError) Error() string { return "i/o timeout" } +func (*timeoutError) Timeout() bool { return true } +func (*timeoutError) Temporary() bool { return true } + +type timeoutChan chan struct{} + +var ioInitOnce sync.Once +var ioCompletionPort windows.Handle + +// ioResult contains the result of an asynchronous IO operation. +type ioResult struct { + bytes uint32 + err error +} + +// ioOperation represents an outstanding asynchronous Win32 IO. +type ioOperation struct { + o windows.Overlapped + ch chan ioResult +} + +func initIO() { + h, err := createIoCompletionPort(windows.InvalidHandle, 0, 0, 0xffffffff) + if err != nil { + panic(err) + } + ioCompletionPort = h + go ioCompletionProcessor(h) +} + +// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall. +// It takes ownership of this handle and will close it if it is garbage collected. +type win32File struct { + handle windows.Handle + wg sync.WaitGroup + wgLock sync.RWMutex + closing atomic.Bool + socket bool + readDeadline deadlineHandler + writeDeadline deadlineHandler +} + +type deadlineHandler struct { + setLock sync.Mutex + channel timeoutChan + channelLock sync.RWMutex + timer *time.Timer + timedout atomic.Bool +} + +// makeWin32File makes a new win32File from an existing file handle. +func makeWin32File(h windows.Handle) (*win32File, error) { + f := &win32File{handle: h} + ioInitOnce.Do(initIO) + _, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff) + if err != nil { + return nil, err + } + err = setFileCompletionNotificationModes(h, windows.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS|windows.FILE_SKIP_SET_EVENT_ON_HANDLE) + if err != nil { + return nil, err + } + f.readDeadline.channel = make(timeoutChan) + f.writeDeadline.channel = make(timeoutChan) + return f, nil +} + +// Deprecated: use NewOpenFile instead. +func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) { + return NewOpenFile(windows.Handle(h)) +} + +func NewOpenFile(h windows.Handle) (io.ReadWriteCloser, error) { + // If we return the result of makeWin32File directly, it can result in an + // interface-wrapped nil, rather than a nil interface value. + f, err := makeWin32File(h) + if err != nil { + return nil, err + } + return f, nil +} + +// closeHandle closes the resources associated with a Win32 handle. +func (f *win32File) closeHandle() { + f.wgLock.Lock() + // Atomically set that we are closing, releasing the resources only once. + if !f.closing.Swap(true) { + f.wgLock.Unlock() + // cancel all IO and wait for it to complete + _ = cancelIoEx(f.handle, nil) + f.wg.Wait() + // at this point, no new IO can start + windows.Close(f.handle) + f.handle = 0 + } else { + f.wgLock.Unlock() + } +} + +// Close closes a win32File. +func (f *win32File) Close() error { + f.closeHandle() + return nil +} + +// IsClosed checks if the file has been closed. +func (f *win32File) IsClosed() bool { + return f.closing.Load() +} + +// prepareIO prepares for a new IO operation. +// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning. +func (f *win32File) prepareIO() (*ioOperation, error) { + f.wgLock.RLock() + if f.closing.Load() { + f.wgLock.RUnlock() + return nil, ErrFileClosed + } + f.wg.Add(1) + f.wgLock.RUnlock() + c := &ioOperation{} + c.ch = make(chan ioResult) + return c, nil +} + +// ioCompletionProcessor processes completed async IOs forever. +func ioCompletionProcessor(h windows.Handle) { + for { + var bytes uint32 + var key uintptr + var op *ioOperation + err := getQueuedCompletionStatus(h, &bytes, &key, &op, windows.INFINITE) + if op == nil { + panic(err) + } + op.ch <- ioResult{bytes, err} + } +} + +// todo: helsaawy - create an asyncIO version that takes a context + +// asyncIO processes the return value from ReadFile or WriteFile, blocking until +// the operation has actually completed. +func (f *win32File) asyncIO(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) { + if err != windows.ERROR_IO_PENDING { //nolint:errorlint // err is Errno + return int(bytes), err + } + + if f.closing.Load() { + _ = cancelIoEx(f.handle, &c.o) + } + + var timeout timeoutChan + if d != nil { + d.channelLock.Lock() + timeout = d.channel + d.channelLock.Unlock() + } + + var r ioResult + select { + case r = <-c.ch: + err = r.err + if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno + if f.closing.Load() { + err = ErrFileClosed + } + } else if err != nil && f.socket { + // err is from Win32. Query the overlapped structure to get the winsock error. + var bytes, flags uint32 + err = wsaGetOverlappedResult(f.handle, &c.o, &bytes, false, &flags) + } + case <-timeout: + _ = cancelIoEx(f.handle, &c.o) + r = <-c.ch + err = r.err + if err == windows.ERROR_OPERATION_ABORTED { //nolint:errorlint // err is Errno + err = ErrTimeout + } + } + + // runtime.KeepAlive is needed, as c is passed via native + // code to ioCompletionProcessor, c must remain alive + // until the channel read is complete. + // todo: (de)allocate *ioOperation via win32 heap functions, instead of needing to KeepAlive? + runtime.KeepAlive(c) + return int(r.bytes), err +} + +// Read reads from a file handle. +func (f *win32File) Read(b []byte) (int, error) { + c, err := f.prepareIO() + if err != nil { + return 0, err + } + defer f.wg.Done() + + if f.readDeadline.timedout.Load() { + return 0, ErrTimeout + } + + var bytes uint32 + err = windows.ReadFile(f.handle, b, &bytes, &c.o) + n, err := f.asyncIO(c, &f.readDeadline, bytes, err) + runtime.KeepAlive(b) + + // Handle EOF conditions. + if err == nil && n == 0 && len(b) != 0 { + return 0, io.EOF + } else if err == windows.ERROR_BROKEN_PIPE { //nolint:errorlint // err is Errno + return 0, io.EOF + } + return n, err +} + +// Write writes to a file handle. +func (f *win32File) Write(b []byte) (int, error) { + c, err := f.prepareIO() + if err != nil { + return 0, err + } + defer f.wg.Done() + + if f.writeDeadline.timedout.Load() { + return 0, ErrTimeout + } + + var bytes uint32 + err = windows.WriteFile(f.handle, b, &bytes, &c.o) + n, err := f.asyncIO(c, &f.writeDeadline, bytes, err) + runtime.KeepAlive(b) + return n, err +} + +func (f *win32File) SetReadDeadline(deadline time.Time) error { + return f.readDeadline.set(deadline) +} + +func (f *win32File) SetWriteDeadline(deadline time.Time) error { + return f.writeDeadline.set(deadline) +} + +func (f *win32File) Flush() error { + return windows.FlushFileBuffers(f.handle) +} + +func (f *win32File) Fd() uintptr { + return uintptr(f.handle) +} + +func (d *deadlineHandler) set(deadline time.Time) error { + d.setLock.Lock() + defer d.setLock.Unlock() + + if d.timer != nil { + if !d.timer.Stop() { + <-d.channel + } + d.timer = nil + } + d.timedout.Store(false) + + select { + case <-d.channel: + d.channelLock.Lock() + d.channel = make(chan struct{}) + d.channelLock.Unlock() + default: + } + + if deadline.IsZero() { + return nil + } + + timeoutIO := func() { + d.timedout.Store(true) + close(d.channel) + } + + now := time.Now() + duration := deadline.Sub(now) + if deadline.After(now) { + // Deadline is in the future, set a timer to wait + d.timer = time.AfterFunc(duration, timeoutIO) + } else { + // Deadline is in the past. Cancel all pending IO now. + timeoutIO() + } + return nil +} diff --git a/vendor/github.com/Microsoft/go-winio/fileinfo.go b/vendor/github.com/Microsoft/go-winio/fileinfo.go new file mode 100644 index 0000000000..c860eb9917 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/fileinfo.go @@ -0,0 +1,106 @@ +//go:build windows +// +build windows + +package winio + +import ( + "os" + "runtime" + "unsafe" + + "golang.org/x/sys/windows" +) + +// FileBasicInfo contains file access time and file attributes information. +type FileBasicInfo struct { + CreationTime, LastAccessTime, LastWriteTime, ChangeTime windows.Filetime + FileAttributes uint32 + _ uint32 // padding +} + +// alignedFileBasicInfo is a FileBasicInfo, but aligned to uint64 by containing +// uint64 rather than windows.Filetime. Filetime contains two uint32s. uint64 +// alignment is necessary to pass this as FILE_BASIC_INFO. +type alignedFileBasicInfo struct { + CreationTime, LastAccessTime, LastWriteTime, ChangeTime uint64 + FileAttributes uint32 + _ uint32 // padding +} + +// GetFileBasicInfo retrieves times and attributes for a file. +func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) { + bi := &alignedFileBasicInfo{} + if err := windows.GetFileInformationByHandleEx( + windows.Handle(f.Fd()), + windows.FileBasicInfo, + (*byte)(unsafe.Pointer(bi)), + uint32(unsafe.Sizeof(*bi)), + ); err != nil { + return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} + } + runtime.KeepAlive(f) + // Reinterpret the alignedFileBasicInfo as a FileBasicInfo so it matches the + // public API of this module. The data may be unnecessarily aligned. + return (*FileBasicInfo)(unsafe.Pointer(bi)), nil +} + +// SetFileBasicInfo sets times and attributes for a file. +func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error { + // Create an alignedFileBasicInfo based on a FileBasicInfo. The copy is + // suitable to pass to GetFileInformationByHandleEx. + biAligned := *(*alignedFileBasicInfo)(unsafe.Pointer(bi)) + if err := windows.SetFileInformationByHandle( + windows.Handle(f.Fd()), + windows.FileBasicInfo, + (*byte)(unsafe.Pointer(&biAligned)), + uint32(unsafe.Sizeof(biAligned)), + ); err != nil { + return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err} + } + runtime.KeepAlive(f) + return nil +} + +// FileStandardInfo contains extended information for the file. +// FILE_STANDARD_INFO in WinBase.h +// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_standard_info +type FileStandardInfo struct { + AllocationSize, EndOfFile int64 + NumberOfLinks uint32 + DeletePending, Directory bool +} + +// GetFileStandardInfo retrieves ended information for the file. +func GetFileStandardInfo(f *os.File) (*FileStandardInfo, error) { + si := &FileStandardInfo{} + if err := windows.GetFileInformationByHandleEx(windows.Handle(f.Fd()), + windows.FileStandardInfo, + (*byte)(unsafe.Pointer(si)), + uint32(unsafe.Sizeof(*si))); err != nil { + return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} + } + runtime.KeepAlive(f) + return si, nil +} + +// FileIDInfo contains the volume serial number and file ID for a file. This pair should be +// unique on a system. +type FileIDInfo struct { + VolumeSerialNumber uint64 + FileID [16]byte +} + +// GetFileID retrieves the unique (volume, file ID) pair for a file. +func GetFileID(f *os.File) (*FileIDInfo, error) { + fileID := &FileIDInfo{} + if err := windows.GetFileInformationByHandleEx( + windows.Handle(f.Fd()), + windows.FileIdInfo, + (*byte)(unsafe.Pointer(fileID)), + uint32(unsafe.Sizeof(*fileID)), + ); err != nil { + return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err} + } + runtime.KeepAlive(f) + return fileID, nil +} diff --git a/vendor/github.com/Microsoft/go-winio/hvsock.go b/vendor/github.com/Microsoft/go-winio/hvsock.go new file mode 100644 index 0000000000..c4fdd9d4ae --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/hvsock.go @@ -0,0 +1,582 @@ +//go:build windows +// +build windows + +package winio + +import ( + "context" + "errors" + "fmt" + "io" + "net" + "os" + "time" + "unsafe" + + "golang.org/x/sys/windows" + + "github.com/Microsoft/go-winio/internal/socket" + "github.com/Microsoft/go-winio/pkg/guid" +) + +const afHVSock = 34 // AF_HYPERV + +// Well known Service and VM IDs +// https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service#vmid-wildcards + +// HvsockGUIDWildcard is the wildcard VmId for accepting connections from all partitions. +func HvsockGUIDWildcard() guid.GUID { // 00000000-0000-0000-0000-000000000000 + return guid.GUID{} +} + +// HvsockGUIDBroadcast is the wildcard VmId for broadcasting sends to all partitions. +func HvsockGUIDBroadcast() guid.GUID { // ffffffff-ffff-ffff-ffff-ffffffffffff + return guid.GUID{ + Data1: 0xffffffff, + Data2: 0xffff, + Data3: 0xffff, + Data4: [8]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + } +} + +// HvsockGUIDLoopback is the Loopback VmId for accepting connections to the same partition as the connector. +func HvsockGUIDLoopback() guid.GUID { // e0e16197-dd56-4a10-9195-5ee7a155a838 + return guid.GUID{ + Data1: 0xe0e16197, + Data2: 0xdd56, + Data3: 0x4a10, + Data4: [8]uint8{0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38}, + } +} + +// HvsockGUIDSiloHost is the address of a silo's host partition: +// - The silo host of a hosted silo is the utility VM. +// - The silo host of a silo on a physical host is the physical host. +func HvsockGUIDSiloHost() guid.GUID { // 36bd0c5c-7276-4223-88ba-7d03b654c568 + return guid.GUID{ + Data1: 0x36bd0c5c, + Data2: 0x7276, + Data3: 0x4223, + Data4: [8]byte{0x88, 0xba, 0x7d, 0x03, 0xb6, 0x54, 0xc5, 0x68}, + } +} + +// HvsockGUIDChildren is the wildcard VmId for accepting connections from the connector's child partitions. +func HvsockGUIDChildren() guid.GUID { // 90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd + return guid.GUID{ + Data1: 0x90db8b89, + Data2: 0xd35, + Data3: 0x4f79, + Data4: [8]uint8{0x8c, 0xe9, 0x49, 0xea, 0xa, 0xc8, 0xb7, 0xcd}, + } +} + +// HvsockGUIDParent is the wildcard VmId for accepting connections from the connector's parent partition. +// Listening on this VmId accepts connection from: +// - Inside silos: silo host partition. +// - Inside hosted silo: host of the VM. +// - Inside VM: VM host. +// - Physical host: Not supported. +func HvsockGUIDParent() guid.GUID { // a42e7cda-d03f-480c-9cc2-a4de20abb878 + return guid.GUID{ + Data1: 0xa42e7cda, + Data2: 0xd03f, + Data3: 0x480c, + Data4: [8]uint8{0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78}, + } +} + +// hvsockVsockServiceTemplate is the Service GUID used for the VSOCK protocol. +func hvsockVsockServiceTemplate() guid.GUID { // 00000000-facb-11e6-bd58-64006a7986d3 + return guid.GUID{ + Data2: 0xfacb, + Data3: 0x11e6, + Data4: [8]uint8{0xbd, 0x58, 0x64, 0x00, 0x6a, 0x79, 0x86, 0xd3}, + } +} + +// An HvsockAddr is an address for a AF_HYPERV socket. +type HvsockAddr struct { + VMID guid.GUID + ServiceID guid.GUID +} + +type rawHvsockAddr struct { + Family uint16 + _ uint16 + VMID guid.GUID + ServiceID guid.GUID +} + +var _ socket.RawSockaddr = &rawHvsockAddr{} + +// Network returns the address's network name, "hvsock". +func (*HvsockAddr) Network() string { + return "hvsock" +} + +func (addr *HvsockAddr) String() string { + return fmt.Sprintf("%s:%s", &addr.VMID, &addr.ServiceID) +} + +// VsockServiceID returns an hvsock service ID corresponding to the specified AF_VSOCK port. +func VsockServiceID(port uint32) guid.GUID { + g := hvsockVsockServiceTemplate() // make a copy + g.Data1 = port + return g +} + +func (addr *HvsockAddr) raw() rawHvsockAddr { + return rawHvsockAddr{ + Family: afHVSock, + VMID: addr.VMID, + ServiceID: addr.ServiceID, + } +} + +func (addr *HvsockAddr) fromRaw(raw *rawHvsockAddr) { + addr.VMID = raw.VMID + addr.ServiceID = raw.ServiceID +} + +// Sockaddr returns a pointer to and the size of this struct. +// +// Implements the [socket.RawSockaddr] interface, and allows use in +// [socket.Bind] and [socket.ConnectEx]. +func (r *rawHvsockAddr) Sockaddr() (unsafe.Pointer, int32, error) { + return unsafe.Pointer(r), int32(unsafe.Sizeof(rawHvsockAddr{})), nil +} + +// Sockaddr interface allows use with `sockets.Bind()` and `.ConnectEx()`. +func (r *rawHvsockAddr) FromBytes(b []byte) error { + n := int(unsafe.Sizeof(rawHvsockAddr{})) + + if len(b) < n { + return fmt.Errorf("got %d, want %d: %w", len(b), n, socket.ErrBufferSize) + } + + copy(unsafe.Slice((*byte)(unsafe.Pointer(r)), n), b[:n]) + if r.Family != afHVSock { + return fmt.Errorf("got %d, want %d: %w", r.Family, afHVSock, socket.ErrAddrFamily) + } + + return nil +} + +// HvsockListener is a socket listener for the AF_HYPERV address family. +type HvsockListener struct { + sock *win32File + addr HvsockAddr +} + +var _ net.Listener = &HvsockListener{} + +// HvsockConn is a connected socket of the AF_HYPERV address family. +type HvsockConn struct { + sock *win32File + local, remote HvsockAddr +} + +var _ net.Conn = &HvsockConn{} + +func newHVSocket() (*win32File, error) { + fd, err := windows.Socket(afHVSock, windows.SOCK_STREAM, 1) + if err != nil { + return nil, os.NewSyscallError("socket", err) + } + f, err := makeWin32File(fd) + if err != nil { + windows.Close(fd) + return nil, err + } + f.socket = true + return f, nil +} + +// ListenHvsock listens for connections on the specified hvsock address. +func ListenHvsock(addr *HvsockAddr) (_ *HvsockListener, err error) { + l := &HvsockListener{addr: *addr} + + var sock *win32File + sock, err = newHVSocket() + if err != nil { + return nil, l.opErr("listen", err) + } + defer func() { + if err != nil { + _ = sock.Close() + } + }() + + sa := addr.raw() + err = socket.Bind(sock.handle, &sa) + if err != nil { + return nil, l.opErr("listen", os.NewSyscallError("socket", err)) + } + err = windows.Listen(sock.handle, 16) + if err != nil { + return nil, l.opErr("listen", os.NewSyscallError("listen", err)) + } + return &HvsockListener{sock: sock, addr: *addr}, nil +} + +func (l *HvsockListener) opErr(op string, err error) error { + return &net.OpError{Op: op, Net: "hvsock", Addr: &l.addr, Err: err} +} + +// Addr returns the listener's network address. +func (l *HvsockListener) Addr() net.Addr { + return &l.addr +} + +// Accept waits for the next connection and returns it. +func (l *HvsockListener) Accept() (_ net.Conn, err error) { + sock, err := newHVSocket() + if err != nil { + return nil, l.opErr("accept", err) + } + defer func() { + if sock != nil { + sock.Close() + } + }() + c, err := l.sock.prepareIO() + if err != nil { + return nil, l.opErr("accept", err) + } + defer l.sock.wg.Done() + + // AcceptEx, per documentation, requires an extra 16 bytes per address. + // + // https://docs.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex + const addrlen = uint32(16 + unsafe.Sizeof(rawHvsockAddr{})) + var addrbuf [addrlen * 2]byte + + var bytes uint32 + err = windows.AcceptEx(l.sock.handle, sock.handle, &addrbuf[0], 0 /* rxdatalen */, addrlen, addrlen, &bytes, &c.o) + if _, err = l.sock.asyncIO(c, nil, bytes, err); err != nil { + return nil, l.opErr("accept", os.NewSyscallError("acceptex", err)) + } + + conn := &HvsockConn{ + sock: sock, + } + // The local address returned in the AcceptEx buffer is the same as the Listener socket's + // address. However, the service GUID reported by GetSockName is different from the Listeners + // socket, and is sometimes the same as the local address of the socket that dialed the + // address, with the service GUID.Data1 incremented, but othertimes is different. + // todo: does the local address matter? is the listener's address or the actual address appropriate? + conn.local.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[0]))) + conn.remote.fromRaw((*rawHvsockAddr)(unsafe.Pointer(&addrbuf[addrlen]))) + + // initialize the accepted socket and update its properties with those of the listening socket + if err = windows.Setsockopt(sock.handle, + windows.SOL_SOCKET, windows.SO_UPDATE_ACCEPT_CONTEXT, + (*byte)(unsafe.Pointer(&l.sock.handle)), int32(unsafe.Sizeof(l.sock.handle))); err != nil { + return nil, conn.opErr("accept", os.NewSyscallError("setsockopt", err)) + } + + sock = nil + return conn, nil +} + +// Close closes the listener, causing any pending Accept calls to fail. +func (l *HvsockListener) Close() error { + return l.sock.Close() +} + +// HvsockDialer configures and dials a Hyper-V Socket (ie, [HvsockConn]). +type HvsockDialer struct { + // Deadline is the time the Dial operation must connect before erroring. + Deadline time.Time + + // Retries is the number of additional connects to try if the connection times out, is refused, + // or the host is unreachable + Retries uint + + // RetryWait is the time to wait after a connection error to retry + RetryWait time.Duration + + rt *time.Timer // redial wait timer +} + +// Dial the Hyper-V socket at addr. +// +// See [HvsockDialer.Dial] for more information. +func Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) { + return (&HvsockDialer{}).Dial(ctx, addr) +} + +// Dial attempts to connect to the Hyper-V socket at addr, and returns a connection if successful. +// Will attempt (HvsockDialer).Retries if dialing fails, waiting (HvsockDialer).RetryWait between +// retries. +// +// Dialing can be cancelled either by providing (HvsockDialer).Deadline, or cancelling ctx. +func (d *HvsockDialer) Dial(ctx context.Context, addr *HvsockAddr) (conn *HvsockConn, err error) { + op := "dial" + // create the conn early to use opErr() + conn = &HvsockConn{ + remote: *addr, + } + + if !d.Deadline.IsZero() { + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, d.Deadline) + defer cancel() + } + + // preemptive timeout/cancellation check + if err = ctx.Err(); err != nil { + return nil, conn.opErr(op, err) + } + + sock, err := newHVSocket() + if err != nil { + return nil, conn.opErr(op, err) + } + defer func() { + if sock != nil { + sock.Close() + } + }() + + sa := addr.raw() + err = socket.Bind(sock.handle, &sa) + if err != nil { + return nil, conn.opErr(op, os.NewSyscallError("bind", err)) + } + + c, err := sock.prepareIO() + if err != nil { + return nil, conn.opErr(op, err) + } + defer sock.wg.Done() + var bytes uint32 + for i := uint(0); i <= d.Retries; i++ { + err = socket.ConnectEx( + sock.handle, + &sa, + nil, // sendBuf + 0, // sendDataLen + &bytes, + (*windows.Overlapped)(unsafe.Pointer(&c.o))) + _, err = sock.asyncIO(c, nil, bytes, err) + if i < d.Retries && canRedial(err) { + if err = d.redialWait(ctx); err == nil { + continue + } + } + break + } + if err != nil { + return nil, conn.opErr(op, os.NewSyscallError("connectex", err)) + } + + // update the connection properties, so shutdown can be used + if err = windows.Setsockopt( + sock.handle, + windows.SOL_SOCKET, + windows.SO_UPDATE_CONNECT_CONTEXT, + nil, // optvalue + 0, // optlen + ); err != nil { + return nil, conn.opErr(op, os.NewSyscallError("setsockopt", err)) + } + + // get the local name + var sal rawHvsockAddr + err = socket.GetSockName(sock.handle, &sal) + if err != nil { + return nil, conn.opErr(op, os.NewSyscallError("getsockname", err)) + } + conn.local.fromRaw(&sal) + + // one last check for timeout, since asyncIO doesn't check the context + if err = ctx.Err(); err != nil { + return nil, conn.opErr(op, err) + } + + conn.sock = sock + sock = nil + + return conn, nil +} + +// redialWait waits before attempting to redial, resetting the timer as appropriate. +func (d *HvsockDialer) redialWait(ctx context.Context) (err error) { + if d.RetryWait == 0 { + return nil + } + + if d.rt == nil { + d.rt = time.NewTimer(d.RetryWait) + } else { + // should already be stopped and drained + d.rt.Reset(d.RetryWait) + } + + select { + case <-ctx.Done(): + case <-d.rt.C: + return nil + } + + // stop and drain the timer + if !d.rt.Stop() { + <-d.rt.C + } + return ctx.Err() +} + +// assumes error is a plain, unwrapped windows.Errno provided by direct syscall. +func canRedial(err error) bool { + //nolint:errorlint // guaranteed to be an Errno + switch err { + case windows.WSAECONNREFUSED, windows.WSAENETUNREACH, windows.WSAETIMEDOUT, + windows.ERROR_CONNECTION_REFUSED, windows.ERROR_CONNECTION_UNAVAIL: + return true + default: + return false + } +} + +func (conn *HvsockConn) opErr(op string, err error) error { + // translate from "file closed" to "socket closed" + if errors.Is(err, ErrFileClosed) { + err = socket.ErrSocketClosed + } + return &net.OpError{Op: op, Net: "hvsock", Source: &conn.local, Addr: &conn.remote, Err: err} +} + +func (conn *HvsockConn) Read(b []byte) (int, error) { + c, err := conn.sock.prepareIO() + if err != nil { + return 0, conn.opErr("read", err) + } + defer conn.sock.wg.Done() + buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))} + var flags, bytes uint32 + err = windows.WSARecv(conn.sock.handle, &buf, 1, &bytes, &flags, &c.o, nil) + n, err := conn.sock.asyncIO(c, &conn.sock.readDeadline, bytes, err) + if err != nil { + var eno windows.Errno + if errors.As(err, &eno) { + err = os.NewSyscallError("wsarecv", eno) + } + return 0, conn.opErr("read", err) + } else if n == 0 { + err = io.EOF + } + return n, err +} + +func (conn *HvsockConn) Write(b []byte) (int, error) { + t := 0 + for len(b) != 0 { + n, err := conn.write(b) + if err != nil { + return t + n, err + } + t += n + b = b[n:] + } + return t, nil +} + +func (conn *HvsockConn) write(b []byte) (int, error) { + c, err := conn.sock.prepareIO() + if err != nil { + return 0, conn.opErr("write", err) + } + defer conn.sock.wg.Done() + buf := windows.WSABuf{Buf: &b[0], Len: uint32(len(b))} + var bytes uint32 + err = windows.WSASend(conn.sock.handle, &buf, 1, &bytes, 0, &c.o, nil) + n, err := conn.sock.asyncIO(c, &conn.sock.writeDeadline, bytes, err) + if err != nil { + var eno windows.Errno + if errors.As(err, &eno) { + err = os.NewSyscallError("wsasend", eno) + } + return 0, conn.opErr("write", err) + } + return n, err +} + +// Close closes the socket connection, failing any pending read or write calls. +func (conn *HvsockConn) Close() error { + return conn.sock.Close() +} + +func (conn *HvsockConn) IsClosed() bool { + return conn.sock.IsClosed() +} + +// shutdown disables sending or receiving on a socket. +func (conn *HvsockConn) shutdown(how int) error { + if conn.IsClosed() { + return socket.ErrSocketClosed + } + + err := windows.Shutdown(conn.sock.handle, how) + if err != nil { + // If the connection was closed, shutdowns fail with "not connected" + if errors.Is(err, windows.WSAENOTCONN) || + errors.Is(err, windows.WSAESHUTDOWN) { + err = socket.ErrSocketClosed + } + return os.NewSyscallError("shutdown", err) + } + return nil +} + +// CloseRead shuts down the read end of the socket, preventing future read operations. +func (conn *HvsockConn) CloseRead() error { + err := conn.shutdown(windows.SHUT_RD) + if err != nil { + return conn.opErr("closeread", err) + } + return nil +} + +// CloseWrite shuts down the write end of the socket, preventing future write operations and +// notifying the other endpoint that no more data will be written. +func (conn *HvsockConn) CloseWrite() error { + err := conn.shutdown(windows.SHUT_WR) + if err != nil { + return conn.opErr("closewrite", err) + } + return nil +} + +// LocalAddr returns the local address of the connection. +func (conn *HvsockConn) LocalAddr() net.Addr { + return &conn.local +} + +// RemoteAddr returns the remote address of the connection. +func (conn *HvsockConn) RemoteAddr() net.Addr { + return &conn.remote +} + +// SetDeadline implements the net.Conn SetDeadline method. +func (conn *HvsockConn) SetDeadline(t time.Time) error { + // todo: implement `SetDeadline` for `win32File` + if err := conn.SetReadDeadline(t); err != nil { + return fmt.Errorf("set read deadline: %w", err) + } + if err := conn.SetWriteDeadline(t); err != nil { + return fmt.Errorf("set write deadline: %w", err) + } + return nil +} + +// SetReadDeadline implements the net.Conn SetReadDeadline method. +func (conn *HvsockConn) SetReadDeadline(t time.Time) error { + return conn.sock.SetReadDeadline(t) +} + +// SetWriteDeadline implements the net.Conn SetWriteDeadline method. +func (conn *HvsockConn) SetWriteDeadline(t time.Time) error { + return conn.sock.SetWriteDeadline(t) +} diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go b/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go new file mode 100644 index 0000000000..1f65388178 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/fs/doc.go @@ -0,0 +1,2 @@ +// This package contains Win32 filesystem functionality. +package fs diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go b/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go new file mode 100644 index 0000000000..0cd9621df7 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/fs/fs.go @@ -0,0 +1,262 @@ +//go:build windows + +package fs + +import ( + "golang.org/x/sys/windows" + + "github.com/Microsoft/go-winio/internal/stringbuffer" +) + +//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go fs.go + +// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew +//sys CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateFileW + +const NullHandle windows.Handle = 0 + +// AccessMask defines standard, specific, and generic rights. +// +// Used with CreateFile and NtCreateFile (and co.). +// +// Bitmask: +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---------------+---------------+-------------------------------+ +// |G|G|G|G|Resvd|A| StandardRights| SpecificRights | +// |R|W|E|A| |S| | | +// +-+-------------+---------------+-------------------------------+ +// +// GR Generic Read +// GW Generic Write +// GE Generic Exectue +// GA Generic All +// Resvd Reserved +// AS Access Security System +// +// https://learn.microsoft.com/en-us/windows/win32/secauthz/access-mask +// +// https://learn.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights +// +// https://learn.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants +type AccessMask = windows.ACCESS_MASK + +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. +const ( + // Not actually any. + // + // For CreateFile: "query certain metadata such as file, directory, or device attributes without accessing that file or device" + // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew#parameters + FILE_ANY_ACCESS AccessMask = 0 + + GENERIC_READ AccessMask = 0x8000_0000 + GENERIC_WRITE AccessMask = 0x4000_0000 + GENERIC_EXECUTE AccessMask = 0x2000_0000 + GENERIC_ALL AccessMask = 0x1000_0000 + ACCESS_SYSTEM_SECURITY AccessMask = 0x0100_0000 + + // Specific Object Access + // from ntioapi.h + + FILE_READ_DATA AccessMask = (0x0001) // file & pipe + FILE_LIST_DIRECTORY AccessMask = (0x0001) // directory + + FILE_WRITE_DATA AccessMask = (0x0002) // file & pipe + FILE_ADD_FILE AccessMask = (0x0002) // directory + + FILE_APPEND_DATA AccessMask = (0x0004) // file + FILE_ADD_SUBDIRECTORY AccessMask = (0x0004) // directory + FILE_CREATE_PIPE_INSTANCE AccessMask = (0x0004) // named pipe + + FILE_READ_EA AccessMask = (0x0008) // file & directory + FILE_READ_PROPERTIES AccessMask = FILE_READ_EA + + FILE_WRITE_EA AccessMask = (0x0010) // file & directory + FILE_WRITE_PROPERTIES AccessMask = FILE_WRITE_EA + + FILE_EXECUTE AccessMask = (0x0020) // file + FILE_TRAVERSE AccessMask = (0x0020) // directory + + FILE_DELETE_CHILD AccessMask = (0x0040) // directory + + FILE_READ_ATTRIBUTES AccessMask = (0x0080) // all + + FILE_WRITE_ATTRIBUTES AccessMask = (0x0100) // all + + FILE_ALL_ACCESS AccessMask = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF) + FILE_GENERIC_READ AccessMask = (STANDARD_RIGHTS_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES | FILE_READ_EA | SYNCHRONIZE) + FILE_GENERIC_WRITE AccessMask = (STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | FILE_APPEND_DATA | SYNCHRONIZE) + FILE_GENERIC_EXECUTE AccessMask = (STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE) + + SPECIFIC_RIGHTS_ALL AccessMask = 0x0000FFFF + + // Standard Access + // from ntseapi.h + + DELETE AccessMask = 0x0001_0000 + READ_CONTROL AccessMask = 0x0002_0000 + WRITE_DAC AccessMask = 0x0004_0000 + WRITE_OWNER AccessMask = 0x0008_0000 + SYNCHRONIZE AccessMask = 0x0010_0000 + + STANDARD_RIGHTS_REQUIRED AccessMask = 0x000F_0000 + + STANDARD_RIGHTS_READ AccessMask = READ_CONTROL + STANDARD_RIGHTS_WRITE AccessMask = READ_CONTROL + STANDARD_RIGHTS_EXECUTE AccessMask = READ_CONTROL + + STANDARD_RIGHTS_ALL AccessMask = 0x001F_0000 +) + +type FileShareMode uint32 + +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. +const ( + FILE_SHARE_NONE FileShareMode = 0x00 + FILE_SHARE_READ FileShareMode = 0x01 + FILE_SHARE_WRITE FileShareMode = 0x02 + FILE_SHARE_DELETE FileShareMode = 0x04 + FILE_SHARE_VALID_FLAGS FileShareMode = 0x07 +) + +type FileCreationDisposition uint32 + +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. +const ( + // from winbase.h + + CREATE_NEW FileCreationDisposition = 0x01 + CREATE_ALWAYS FileCreationDisposition = 0x02 + OPEN_EXISTING FileCreationDisposition = 0x03 + OPEN_ALWAYS FileCreationDisposition = 0x04 + TRUNCATE_EXISTING FileCreationDisposition = 0x05 +) + +// Create disposition values for NtCreate* +type NTFileCreationDisposition uint32 + +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. +const ( + // From ntioapi.h + + FILE_SUPERSEDE NTFileCreationDisposition = 0x00 + FILE_OPEN NTFileCreationDisposition = 0x01 + FILE_CREATE NTFileCreationDisposition = 0x02 + FILE_OPEN_IF NTFileCreationDisposition = 0x03 + FILE_OVERWRITE NTFileCreationDisposition = 0x04 + FILE_OVERWRITE_IF NTFileCreationDisposition = 0x05 + FILE_MAXIMUM_DISPOSITION NTFileCreationDisposition = 0x05 +) + +// CreateFile and co. take flags or attributes together as one parameter. +// Define alias until we can use generics to allow both +// +// https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants +type FileFlagOrAttribute uint32 + +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. +const ( + // from winnt.h + + FILE_FLAG_WRITE_THROUGH FileFlagOrAttribute = 0x8000_0000 + FILE_FLAG_OVERLAPPED FileFlagOrAttribute = 0x4000_0000 + FILE_FLAG_NO_BUFFERING FileFlagOrAttribute = 0x2000_0000 + FILE_FLAG_RANDOM_ACCESS FileFlagOrAttribute = 0x1000_0000 + FILE_FLAG_SEQUENTIAL_SCAN FileFlagOrAttribute = 0x0800_0000 + FILE_FLAG_DELETE_ON_CLOSE FileFlagOrAttribute = 0x0400_0000 + FILE_FLAG_BACKUP_SEMANTICS FileFlagOrAttribute = 0x0200_0000 + FILE_FLAG_POSIX_SEMANTICS FileFlagOrAttribute = 0x0100_0000 + FILE_FLAG_OPEN_REPARSE_POINT FileFlagOrAttribute = 0x0020_0000 + FILE_FLAG_OPEN_NO_RECALL FileFlagOrAttribute = 0x0010_0000 + FILE_FLAG_FIRST_PIPE_INSTANCE FileFlagOrAttribute = 0x0008_0000 +) + +// NtCreate* functions take a dedicated CreateOptions parameter. +// +// https://learn.microsoft.com/en-us/windows/win32/api/Winternl/nf-winternl-ntcreatefile +// +// https://learn.microsoft.com/en-us/windows/win32/devnotes/nt-create-named-pipe-file +type NTCreateOptions uint32 + +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. +const ( + // From ntioapi.h + + FILE_DIRECTORY_FILE NTCreateOptions = 0x0000_0001 + FILE_WRITE_THROUGH NTCreateOptions = 0x0000_0002 + FILE_SEQUENTIAL_ONLY NTCreateOptions = 0x0000_0004 + FILE_NO_INTERMEDIATE_BUFFERING NTCreateOptions = 0x0000_0008 + + FILE_SYNCHRONOUS_IO_ALERT NTCreateOptions = 0x0000_0010 + FILE_SYNCHRONOUS_IO_NONALERT NTCreateOptions = 0x0000_0020 + FILE_NON_DIRECTORY_FILE NTCreateOptions = 0x0000_0040 + FILE_CREATE_TREE_CONNECTION NTCreateOptions = 0x0000_0080 + + FILE_COMPLETE_IF_OPLOCKED NTCreateOptions = 0x0000_0100 + FILE_NO_EA_KNOWLEDGE NTCreateOptions = 0x0000_0200 + FILE_DISABLE_TUNNELING NTCreateOptions = 0x0000_0400 + FILE_RANDOM_ACCESS NTCreateOptions = 0x0000_0800 + + FILE_DELETE_ON_CLOSE NTCreateOptions = 0x0000_1000 + FILE_OPEN_BY_FILE_ID NTCreateOptions = 0x0000_2000 + FILE_OPEN_FOR_BACKUP_INTENT NTCreateOptions = 0x0000_4000 + FILE_NO_COMPRESSION NTCreateOptions = 0x0000_8000 +) + +type FileSQSFlag = FileFlagOrAttribute + +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. +const ( + // from winbase.h + + SECURITY_ANONYMOUS FileSQSFlag = FileSQSFlag(SecurityAnonymous << 16) + SECURITY_IDENTIFICATION FileSQSFlag = FileSQSFlag(SecurityIdentification << 16) + SECURITY_IMPERSONATION FileSQSFlag = FileSQSFlag(SecurityImpersonation << 16) + SECURITY_DELEGATION FileSQSFlag = FileSQSFlag(SecurityDelegation << 16) + + SECURITY_SQOS_PRESENT FileSQSFlag = 0x0010_0000 + SECURITY_VALID_SQOS_FLAGS FileSQSFlag = 0x001F_0000 +) + +// GetFinalPathNameByHandle flags +// +// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew#parameters +type GetFinalPathFlag uint32 + +//nolint:revive // SNAKE_CASE is not idiomatic in Go, but aligned with Win32 API. +const ( + GetFinalPathDefaultFlag GetFinalPathFlag = 0x0 + + FILE_NAME_NORMALIZED GetFinalPathFlag = 0x0 + FILE_NAME_OPENED GetFinalPathFlag = 0x8 + + VOLUME_NAME_DOS GetFinalPathFlag = 0x0 + VOLUME_NAME_GUID GetFinalPathFlag = 0x1 + VOLUME_NAME_NT GetFinalPathFlag = 0x2 + VOLUME_NAME_NONE GetFinalPathFlag = 0x4 +) + +// getFinalPathNameByHandle facilitates calling the Windows API GetFinalPathNameByHandle +// with the given handle and flags. It transparently takes care of creating a buffer of the +// correct size for the call. +// +// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew +func GetFinalPathNameByHandle(h windows.Handle, flags GetFinalPathFlag) (string, error) { + b := stringbuffer.NewWString() + //TODO: can loop infinitely if Win32 keeps returning the same (or a larger) n? + for { + n, err := windows.GetFinalPathNameByHandle(h, b.Pointer(), b.Cap(), uint32(flags)) + if err != nil { + return "", err + } + // If the buffer wasn't large enough, n will be the total size needed (including null terminator). + // Resize and try again. + if n > b.Cap() { + b.ResizeTo(n) + continue + } + // If the buffer is large enough, n will be the size not including the null terminator. + // Convert to a Go string and return. + return b.String(), nil + } +} diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/security.go b/vendor/github.com/Microsoft/go-winio/internal/fs/security.go new file mode 100644 index 0000000000..81760ac67e --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/fs/security.go @@ -0,0 +1,12 @@ +package fs + +// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level +type SecurityImpersonationLevel int32 // C default enums underlying type is `int`, which is Go `int32` + +// Impersonation levels +const ( + SecurityAnonymous SecurityImpersonationLevel = 0 + SecurityIdentification SecurityImpersonationLevel = 1 + SecurityImpersonation SecurityImpersonationLevel = 2 + SecurityDelegation SecurityImpersonationLevel = 3 +) diff --git a/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go new file mode 100644 index 0000000000..a94e234c70 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/fs/zsyscall_windows.go @@ -0,0 +1,61 @@ +//go:build windows + +// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT. + +package fs + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + return e +} + +var ( + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + + procCreateFileW = modkernel32.NewProc("CreateFileW") +) + +func CreateFile(name string, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(name) + if err != nil { + return + } + return _CreateFile(_p0, access, mode, sa, createmode, attrs, templatefile) +} + +func _CreateFile(name *uint16, access AccessMask, mode FileShareMode, sa *windows.SecurityAttributes, createmode FileCreationDisposition, attrs FileFlagOrAttribute, templatefile windows.Handle) (handle windows.Handle, err error) { + r0, _, e1 := syscall.SyscallN(procCreateFileW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile)) + handle = windows.Handle(r0) + if handle == windows.InvalidHandle { + err = errnoErr(e1) + } + return +} diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go b/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go new file mode 100644 index 0000000000..7e82f9afa9 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/socket/rawaddr.go @@ -0,0 +1,20 @@ +package socket + +import ( + "unsafe" +) + +// RawSockaddr allows structs to be used with [Bind] and [ConnectEx]. The +// struct must meet the Win32 sockaddr requirements specified here: +// https://docs.microsoft.com/en-us/windows/win32/winsock/sockaddr-2 +// +// Specifically, the struct size must be least larger than an int16 (unsigned short) +// for the address family. +type RawSockaddr interface { + // Sockaddr returns a pointer to the RawSockaddr and its struct size, allowing + // for the RawSockaddr's data to be overwritten by syscalls (if necessary). + // + // It is the callers responsibility to validate that the values are valid; invalid + // pointers or size can cause a panic. + Sockaddr() (unsafe.Pointer, int32, error) +} diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go b/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go new file mode 100644 index 0000000000..88580d974e --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/socket/socket.go @@ -0,0 +1,177 @@ +//go:build windows + +package socket + +import ( + "errors" + "fmt" + "net" + "sync" + "syscall" + "unsafe" + + "github.com/Microsoft/go-winio/pkg/guid" + "golang.org/x/sys/windows" +) + +//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go socket.go + +//sys getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getsockname +//sys getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) [failretval==socketError] = ws2_32.getpeername +//sys bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socketError] = ws2_32.bind + +const socketError = uintptr(^uint32(0)) + +var ( + // todo(helsaawy): create custom error types to store the desired vs actual size and addr family? + + ErrBufferSize = errors.New("buffer size") + ErrAddrFamily = errors.New("address family") + ErrInvalidPointer = errors.New("invalid pointer") + ErrSocketClosed = fmt.Errorf("socket closed: %w", net.ErrClosed) +) + +// todo(helsaawy): replace these with generics, ie: GetSockName[S RawSockaddr](s windows.Handle) (S, error) + +// GetSockName writes the local address of socket s to the [RawSockaddr] rsa. +// If rsa is not large enough, the [windows.WSAEFAULT] is returned. +func GetSockName(s windows.Handle, rsa RawSockaddr) error { + ptr, l, err := rsa.Sockaddr() + if err != nil { + return fmt.Errorf("could not retrieve socket pointer and size: %w", err) + } + + // although getsockname returns WSAEFAULT if the buffer is too small, it does not set + // &l to the correct size, so--apart from doubling the buffer repeatedly--there is no remedy + return getsockname(s, ptr, &l) +} + +// GetPeerName returns the remote address the socket is connected to. +// +// See [GetSockName] for more information. +func GetPeerName(s windows.Handle, rsa RawSockaddr) error { + ptr, l, err := rsa.Sockaddr() + if err != nil { + return fmt.Errorf("could not retrieve socket pointer and size: %w", err) + } + + return getpeername(s, ptr, &l) +} + +func Bind(s windows.Handle, rsa RawSockaddr) (err error) { + ptr, l, err := rsa.Sockaddr() + if err != nil { + return fmt.Errorf("could not retrieve socket pointer and size: %w", err) + } + + return bind(s, ptr, l) +} + +// "golang.org/x/sys/windows".ConnectEx and .Bind only accept internal implementations of the +// their sockaddr interface, so they cannot be used with HvsockAddr +// Replicate functionality here from +// https://cs.opensource.google/go/x/sys/+/master:windows/syscall_windows.go + +// The function pointers to `AcceptEx`, `ConnectEx` and `GetAcceptExSockaddrs` must be loaded at +// runtime via a WSAIoctl call: +// https://docs.microsoft.com/en-us/windows/win32/api/Mswsock/nc-mswsock-lpfn_connectex#remarks + +type runtimeFunc struct { + id guid.GUID + once sync.Once + addr uintptr + err error +} + +func (f *runtimeFunc) Load() error { + f.once.Do(func() { + var s windows.Handle + s, f.err = windows.Socket(windows.AF_INET, windows.SOCK_STREAM, windows.IPPROTO_TCP) + if f.err != nil { + return + } + defer windows.CloseHandle(s) //nolint:errcheck + + var n uint32 + f.err = windows.WSAIoctl(s, + windows.SIO_GET_EXTENSION_FUNCTION_POINTER, + (*byte)(unsafe.Pointer(&f.id)), + uint32(unsafe.Sizeof(f.id)), + (*byte)(unsafe.Pointer(&f.addr)), + uint32(unsafe.Sizeof(f.addr)), + &n, + nil, // overlapped + 0, // completionRoutine + ) + }) + return f.err +} + +var ( + // todo: add `AcceptEx` and `GetAcceptExSockaddrs` + WSAID_CONNECTEX = guid.GUID{ //revive:disable-line:var-naming ALL_CAPS + Data1: 0x25a207b9, + Data2: 0xddf3, + Data3: 0x4660, + Data4: [8]byte{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}, + } + + connectExFunc = runtimeFunc{id: WSAID_CONNECTEX} +) + +func ConnectEx( + fd windows.Handle, + rsa RawSockaddr, + sendBuf *byte, + sendDataLen uint32, + bytesSent *uint32, + overlapped *windows.Overlapped, +) error { + if err := connectExFunc.Load(); err != nil { + return fmt.Errorf("failed to load ConnectEx function pointer: %w", err) + } + ptr, n, err := rsa.Sockaddr() + if err != nil { + return err + } + return connectEx(fd, ptr, n, sendBuf, sendDataLen, bytesSent, overlapped) +} + +// BOOL LpfnConnectex( +// [in] SOCKET s, +// [in] const sockaddr *name, +// [in] int namelen, +// [in, optional] PVOID lpSendBuffer, +// [in] DWORD dwSendDataLength, +// [out] LPDWORD lpdwBytesSent, +// [in] LPOVERLAPPED lpOverlapped +// ) + +func connectEx( + s windows.Handle, + name unsafe.Pointer, + namelen int32, + sendBuf *byte, + sendDataLen uint32, + bytesSent *uint32, + overlapped *windows.Overlapped, +) (err error) { + r1, _, e1 := syscall.SyscallN(connectExFunc.addr, + uintptr(s), + uintptr(name), + uintptr(namelen), + uintptr(unsafe.Pointer(sendBuf)), + uintptr(sendDataLen), + uintptr(unsafe.Pointer(bytesSent)), + uintptr(unsafe.Pointer(overlapped)), + ) + + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return err +} diff --git a/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go new file mode 100644 index 0000000000..e1504126aa --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/socket/zsyscall_windows.go @@ -0,0 +1,69 @@ +//go:build windows + +// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT. + +package socket + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + return e +} + +var ( + modws2_32 = windows.NewLazySystemDLL("ws2_32.dll") + + procbind = modws2_32.NewProc("bind") + procgetpeername = modws2_32.NewProc("getpeername") + procgetsockname = modws2_32.NewProc("getsockname") +) + +func bind(s windows.Handle, name unsafe.Pointer, namelen int32) (err error) { + r1, _, e1 := syscall.SyscallN(procbind.Addr(), uintptr(s), uintptr(name), uintptr(namelen)) + if r1 == socketError { + err = errnoErr(e1) + } + return +} + +func getpeername(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) { + r1, _, e1 := syscall.SyscallN(procgetpeername.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen))) + if r1 == socketError { + err = errnoErr(e1) + } + return +} + +func getsockname(s windows.Handle, name unsafe.Pointer, namelen *int32) (err error) { + r1, _, e1 := syscall.SyscallN(procgetsockname.Addr(), uintptr(s), uintptr(name), uintptr(unsafe.Pointer(namelen))) + if r1 == socketError { + err = errnoErr(e1) + } + return +} diff --git a/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go b/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go new file mode 100644 index 0000000000..42ebc019fc --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/internal/stringbuffer/wstring.go @@ -0,0 +1,132 @@ +package stringbuffer + +import ( + "sync" + "unicode/utf16" +) + +// TODO: worth exporting and using in mkwinsyscall? + +// Uint16BufferSize is the buffer size in the pool, chosen somewhat arbitrarily to accommodate +// large path strings: +// MAX_PATH (260) + size of volume GUID prefix (49) + null terminator = 310. +const MinWStringCap = 310 + +// use *[]uint16 since []uint16 creates an extra allocation where the slice header +// is copied to heap and then referenced via pointer in the interface header that sync.Pool +// stores. +var pathPool = sync.Pool{ // if go1.18+ adds Pool[T], use that to store []uint16 directly + New: func() interface{} { + b := make([]uint16, MinWStringCap) + return &b + }, +} + +func newBuffer() []uint16 { return *(pathPool.Get().(*[]uint16)) } + +// freeBuffer copies the slice header data, and puts a pointer to that in the pool. +// This avoids taking a pointer to the slice header in WString, which can be set to nil. +func freeBuffer(b []uint16) { pathPool.Put(&b) } + +// WString is a wide string buffer ([]uint16) meant for storing UTF-16 encoded strings +// for interacting with Win32 APIs. +// Sizes are specified as uint32 and not int. +// +// It is not thread safe. +type WString struct { + // type-def allows casting to []uint16 directly, use struct to prevent that and allow adding fields in the future. + + // raw buffer + b []uint16 +} + +// NewWString returns a [WString] allocated from a shared pool with an +// initial capacity of at least [MinWStringCap]. +// Since the buffer may have been previously used, its contents are not guaranteed to be empty. +// +// The buffer should be freed via [WString.Free] +func NewWString() *WString { + return &WString{ + b: newBuffer(), + } +} + +func (b *WString) Free() { + if b.empty() { + return + } + freeBuffer(b.b) + b.b = nil +} + +// ResizeTo grows the buffer to at least c and returns the new capacity, freeing the +// previous buffer back into pool. +func (b *WString) ResizeTo(c uint32) uint32 { + // already sufficient (or n is 0) + if c <= b.Cap() { + return b.Cap() + } + + if c <= MinWStringCap { + c = MinWStringCap + } + // allocate at-least double buffer size, as is done in [bytes.Buffer] and other places + if c <= 2*b.Cap() { + c = 2 * b.Cap() + } + + b2 := make([]uint16, c) + if !b.empty() { + copy(b2, b.b) + freeBuffer(b.b) + } + b.b = b2 + return c +} + +// Buffer returns the underlying []uint16 buffer. +func (b *WString) Buffer() []uint16 { + if b.empty() { + return nil + } + return b.b +} + +// Pointer returns a pointer to the first uint16 in the buffer. +// If the [WString.Free] has already been called, the pointer will be nil. +func (b *WString) Pointer() *uint16 { + if b.empty() { + return nil + } + return &b.b[0] +} + +// String returns the returns the UTF-8 encoding of the UTF-16 string in the buffer. +// +// It assumes that the data is null-terminated. +func (b *WString) String() string { + // Using [windows.UTF16ToString] would require importing "golang.org/x/sys/windows" + // and would make this code Windows-only, which makes no sense. + // So copy UTF16ToString code into here. + // If other windows-specific code is added, switch to [windows.UTF16ToString] + + s := b.b + for i, v := range s { + if v == 0 { + s = s[:i] + break + } + } + return string(utf16.Decode(s)) +} + +// Cap returns the underlying buffer capacity. +func (b *WString) Cap() uint32 { + if b.empty() { + return 0 + } + return b.cap() +} + +func (b *WString) cap() uint32 { return uint32(cap(b.b)) } +func (b *WString) empty() bool { return b == nil || b.cap() == 0 } diff --git a/vendor/github.com/Microsoft/go-winio/pipe.go b/vendor/github.com/Microsoft/go-winio/pipe.go new file mode 100644 index 0000000000..a2da6639d0 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pipe.go @@ -0,0 +1,586 @@ +//go:build windows +// +build windows + +package winio + +import ( + "context" + "errors" + "fmt" + "io" + "net" + "os" + "runtime" + "time" + "unsafe" + + "golang.org/x/sys/windows" + + "github.com/Microsoft/go-winio/internal/fs" +) + +//sys connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) = ConnectNamedPipe +//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) [failretval==windows.InvalidHandle] = CreateNamedPipeW +//sys disconnectNamedPipe(pipe windows.Handle) (err error) = DisconnectNamedPipe +//sys getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo +//sys getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW +//sys ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) = ntdll.NtCreateNamedPipeFile +//sys rtlNtStatusToDosError(status ntStatus) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb +//sys rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) = ntdll.RtlDosPathNameToNtPathName_U +//sys rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) = ntdll.RtlDefaultNpAcl + +type PipeConn interface { + net.Conn + Disconnect() error + Flush() error +} + +// type aliases for mkwinsyscall code +type ( + ntAccessMask = fs.AccessMask + ntFileShareMode = fs.FileShareMode + ntFileCreationDisposition = fs.NTFileCreationDisposition + ntFileOptions = fs.NTCreateOptions +) + +type ioStatusBlock struct { + Status, Information uintptr +} + +// typedef struct _OBJECT_ATTRIBUTES { +// ULONG Length; +// HANDLE RootDirectory; +// PUNICODE_STRING ObjectName; +// ULONG Attributes; +// PVOID SecurityDescriptor; +// PVOID SecurityQualityOfService; +// } OBJECT_ATTRIBUTES; +// +// https://learn.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes +type objectAttributes struct { + Length uintptr + RootDirectory uintptr + ObjectName *unicodeString + Attributes uintptr + SecurityDescriptor *securityDescriptor + SecurityQoS uintptr +} + +type unicodeString struct { + Length uint16 + MaximumLength uint16 + Buffer uintptr +} + +// typedef struct _SECURITY_DESCRIPTOR { +// BYTE Revision; +// BYTE Sbz1; +// SECURITY_DESCRIPTOR_CONTROL Control; +// PSID Owner; +// PSID Group; +// PACL Sacl; +// PACL Dacl; +// } SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR; +// +// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-security_descriptor +type securityDescriptor struct { + Revision byte + Sbz1 byte + Control uint16 + Owner uintptr + Group uintptr + Sacl uintptr //revive:disable-line:var-naming SACL, not Sacl + Dacl uintptr //revive:disable-line:var-naming DACL, not Dacl +} + +type ntStatus int32 + +func (status ntStatus) Err() error { + if status >= 0 { + return nil + } + return rtlNtStatusToDosError(status) +} + +var ( + // ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed. + ErrPipeListenerClosed = net.ErrClosed + + errPipeWriteClosed = errors.New("pipe has been closed for write") +) + +type win32Pipe struct { + *win32File + path string +} + +var _ PipeConn = (*win32Pipe)(nil) + +type win32MessageBytePipe struct { + win32Pipe + writeClosed bool + readEOF bool +} + +type pipeAddress string + +func (f *win32Pipe) LocalAddr() net.Addr { + return pipeAddress(f.path) +} + +func (f *win32Pipe) RemoteAddr() net.Addr { + return pipeAddress(f.path) +} + +func (f *win32Pipe) SetDeadline(t time.Time) error { + if err := f.SetReadDeadline(t); err != nil { + return err + } + return f.SetWriteDeadline(t) +} + +func (f *win32Pipe) Disconnect() error { + return disconnectNamedPipe(f.win32File.handle) +} + +// CloseWrite closes the write side of a message pipe in byte mode. +func (f *win32MessageBytePipe) CloseWrite() error { + if f.writeClosed { + return errPipeWriteClosed + } + err := f.win32File.Flush() + if err != nil { + return err + } + _, err = f.win32File.Write(nil) + if err != nil { + return err + } + f.writeClosed = true + return nil +} + +// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since +// they are used to implement CloseWrite(). +func (f *win32MessageBytePipe) Write(b []byte) (int, error) { + if f.writeClosed { + return 0, errPipeWriteClosed + } + if len(b) == 0 { + return 0, nil + } + return f.win32File.Write(b) +} + +// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message +// mode pipe will return io.EOF, as will all subsequent reads. +func (f *win32MessageBytePipe) Read(b []byte) (int, error) { + if f.readEOF { + return 0, io.EOF + } + n, err := f.win32File.Read(b) + if err == io.EOF { //nolint:errorlint + // If this was the result of a zero-byte read, then + // it is possible that the read was due to a zero-size + // message. Since we are simulating CloseWrite with a + // zero-byte message, ensure that all future Read() calls + // also return EOF. + f.readEOF = true + } else if err == windows.ERROR_MORE_DATA { //nolint:errorlint // err is Errno + // ERROR_MORE_DATA indicates that the pipe's read mode is message mode + // and the message still has more bytes. Treat this as a success, since + // this package presents all named pipes as byte streams. + err = nil + } + return n, err +} + +func (pipeAddress) Network() string { + return "pipe" +} + +func (s pipeAddress) String() string { + return string(s) +} + +// tryDialPipe attempts to dial the pipe at `path` until `ctx` cancellation or timeout. +func tryDialPipe(ctx context.Context, path *string, access fs.AccessMask, impLevel PipeImpLevel) (windows.Handle, error) { + for { + select { + case <-ctx.Done(): + return windows.Handle(0), ctx.Err() + default: + h, err := fs.CreateFile(*path, + access, + 0, // mode + nil, // security attributes + fs.OPEN_EXISTING, + fs.FILE_FLAG_OVERLAPPED|fs.SECURITY_SQOS_PRESENT|fs.FileSQSFlag(impLevel), + 0, // template file handle + ) + if err == nil { + return h, nil + } + if err != windows.ERROR_PIPE_BUSY { //nolint:errorlint // err is Errno + return h, &os.PathError{Err: err, Op: "open", Path: *path} + } + // Wait 10 msec and try again. This is a rather simplistic + // view, as we always try each 10 milliseconds. + time.Sleep(10 * time.Millisecond) + } + } +} + +// DialPipe connects to a named pipe by path, timing out if the connection +// takes longer than the specified duration. If timeout is nil, then we use +// a default timeout of 2 seconds. (We do not use WaitNamedPipe.) +func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { + var absTimeout time.Time + if timeout != nil { + absTimeout = time.Now().Add(*timeout) + } else { + absTimeout = time.Now().Add(2 * time.Second) + } + ctx, cancel := context.WithDeadline(context.Background(), absTimeout) + defer cancel() + conn, err := DialPipeContext(ctx, path) + if errors.Is(err, context.DeadlineExceeded) { + return nil, ErrTimeout + } + return conn, err +} + +// DialPipeContext attempts to connect to a named pipe by `path` until `ctx` +// cancellation or timeout. +func DialPipeContext(ctx context.Context, path string) (net.Conn, error) { + return DialPipeAccess(ctx, path, uint32(fs.GENERIC_READ|fs.GENERIC_WRITE)) +} + +// PipeImpLevel is an enumeration of impersonation levels that may be set +// when calling DialPipeAccessImpersonation. +type PipeImpLevel uint32 + +const ( + PipeImpLevelAnonymous = PipeImpLevel(fs.SECURITY_ANONYMOUS) + PipeImpLevelIdentification = PipeImpLevel(fs.SECURITY_IDENTIFICATION) + PipeImpLevelImpersonation = PipeImpLevel(fs.SECURITY_IMPERSONATION) + PipeImpLevelDelegation = PipeImpLevel(fs.SECURITY_DELEGATION) +) + +// DialPipeAccess attempts to connect to a named pipe by `path` with `access` until `ctx` +// cancellation or timeout. +func DialPipeAccess(ctx context.Context, path string, access uint32) (net.Conn, error) { + return DialPipeAccessImpLevel(ctx, path, access, PipeImpLevelAnonymous) +} + +// DialPipeAccessImpLevel attempts to connect to a named pipe by `path` with +// `access` at `impLevel` until `ctx` cancellation or timeout. The other +// DialPipe* implementations use PipeImpLevelAnonymous. +func DialPipeAccessImpLevel(ctx context.Context, path string, access uint32, impLevel PipeImpLevel) (net.Conn, error) { + var err error + var h windows.Handle + h, err = tryDialPipe(ctx, &path, fs.AccessMask(access), impLevel) + if err != nil { + return nil, err + } + + var flags uint32 + err = getNamedPipeInfo(h, &flags, nil, nil, nil) + if err != nil { + return nil, err + } + + f, err := makeWin32File(h) + if err != nil { + windows.Close(h) + return nil, err + } + + // If the pipe is in message mode, return a message byte pipe, which + // supports CloseWrite(). + if flags&windows.PIPE_TYPE_MESSAGE != 0 { + return &win32MessageBytePipe{ + win32Pipe: win32Pipe{win32File: f, path: path}, + }, nil + } + return &win32Pipe{win32File: f, path: path}, nil +} + +type acceptResponse struct { + f *win32File + err error +} + +type win32PipeListener struct { + firstHandle windows.Handle + path string + config PipeConfig + acceptCh chan (chan acceptResponse) + closeCh chan int + doneCh chan int +} + +func makeServerPipeHandle(path string, sd []byte, c *PipeConfig, first bool) (windows.Handle, error) { + path16, err := windows.UTF16FromString(path) + if err != nil { + return 0, &os.PathError{Op: "open", Path: path, Err: err} + } + + var oa objectAttributes + oa.Length = unsafe.Sizeof(oa) + + var ntPath unicodeString + if err := rtlDosPathNameToNtPathName(&path16[0], + &ntPath, + 0, + 0, + ).Err(); err != nil { + return 0, &os.PathError{Op: "open", Path: path, Err: err} + } + defer windows.LocalFree(windows.Handle(ntPath.Buffer)) //nolint:errcheck + oa.ObjectName = &ntPath + oa.Attributes = windows.OBJ_CASE_INSENSITIVE + + // The security descriptor is only needed for the first pipe. + if first { + if sd != nil { + //todo: does `sdb` need to be allocated on the heap, or can go allocate it? + l := uint32(len(sd)) + sdb, err := windows.LocalAlloc(0, l) + if err != nil { + return 0, fmt.Errorf("LocalAlloc for security descriptor with of length %d: %w", l, err) + } + defer windows.LocalFree(windows.Handle(sdb)) //nolint:errcheck + copy((*[0xffff]byte)(unsafe.Pointer(sdb))[:], sd) + oa.SecurityDescriptor = (*securityDescriptor)(unsafe.Pointer(sdb)) + } else { + // Construct the default named pipe security descriptor. + var dacl uintptr + if err := rtlDefaultNpAcl(&dacl).Err(); err != nil { + return 0, fmt.Errorf("getting default named pipe ACL: %w", err) + } + defer windows.LocalFree(windows.Handle(dacl)) //nolint:errcheck + + sdb := &securityDescriptor{ + Revision: 1, + Control: windows.SE_DACL_PRESENT, + Dacl: dacl, + } + oa.SecurityDescriptor = sdb + } + } + + typ := uint32(windows.FILE_PIPE_REJECT_REMOTE_CLIENTS) + if c.MessageMode { + typ |= windows.FILE_PIPE_MESSAGE_TYPE + } + + disposition := fs.FILE_OPEN + access := fs.GENERIC_READ | fs.GENERIC_WRITE | fs.SYNCHRONIZE + if first { + disposition = fs.FILE_CREATE + // By not asking for read or write access, the named pipe file system + // will put this pipe into an initially disconnected state, blocking + // client connections until the next call with first == false. + access = fs.SYNCHRONIZE + } + + timeout := int64(-50 * 10000) // 50ms + + var ( + h windows.Handle + iosb ioStatusBlock + ) + err = ntCreateNamedPipeFile(&h, + access, + &oa, + &iosb, + fs.FILE_SHARE_READ|fs.FILE_SHARE_WRITE, + disposition, + 0, + typ, + 0, + 0, + 0xffffffff, + uint32(c.InputBufferSize), + uint32(c.OutputBufferSize), + &timeout).Err() + if err != nil { + return 0, &os.PathError{Op: "open", Path: path, Err: err} + } + + runtime.KeepAlive(ntPath) + return h, nil +} + +func (l *win32PipeListener) makeServerPipe() (*win32File, error) { + h, err := makeServerPipeHandle(l.path, nil, &l.config, false) + if err != nil { + return nil, err + } + f, err := makeWin32File(h) + if err != nil { + windows.Close(h) + return nil, err + } + return f, nil +} + +func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) { + p, err := l.makeServerPipe() + if err != nil { + return nil, err + } + + // Wait for the client to connect. + ch := make(chan error) + go func(p *win32File) { + ch <- connectPipe(p) + }(p) + + select { + case err = <-ch: + if err != nil { + p.Close() + p = nil + } + case <-l.closeCh: + // Abort the connect request by closing the handle. + p.Close() + p = nil + err = <-ch + if err == nil || err == ErrFileClosed { //nolint:errorlint // err is Errno + err = ErrPipeListenerClosed + } + } + return p, err +} + +func (l *win32PipeListener) listenerRoutine() { + closed := false + for !closed { + select { + case <-l.closeCh: + closed = true + case responseCh := <-l.acceptCh: + var ( + p *win32File + err error + ) + for { + p, err = l.makeConnectedServerPipe() + // If the connection was immediately closed by the client, try + // again. + if err != windows.ERROR_NO_DATA { //nolint:errorlint // err is Errno + break + } + } + responseCh <- acceptResponse{p, err} + closed = err == ErrPipeListenerClosed //nolint:errorlint // err is Errno + } + } + windows.Close(l.firstHandle) + l.firstHandle = 0 + // Notify Close() and Accept() callers that the handle has been closed. + close(l.doneCh) +} + +// PipeConfig contain configuration for the pipe listener. +type PipeConfig struct { + // SecurityDescriptor contains a Windows security descriptor in SDDL format. + SecurityDescriptor string + + // MessageMode determines whether the pipe is in byte or message mode. In either + // case the pipe is read in byte mode by default. The only practical difference in + // this implementation is that CloseWrite() is only supported for message mode pipes; + // CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only + // transferred to the reader (and returned as io.EOF in this implementation) + // when the pipe is in message mode. + MessageMode bool + + // InputBufferSize specifies the size of the input buffer, in bytes. + InputBufferSize int32 + + // OutputBufferSize specifies the size of the output buffer, in bytes. + OutputBufferSize int32 +} + +// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe. +// The pipe must not already exist. +func ListenPipe(path string, c *PipeConfig) (net.Listener, error) { + var ( + sd []byte + err error + ) + if c == nil { + c = &PipeConfig{} + } + if c.SecurityDescriptor != "" { + sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor) + if err != nil { + return nil, err + } + } + h, err := makeServerPipeHandle(path, sd, c, true) + if err != nil { + return nil, err + } + l := &win32PipeListener{ + firstHandle: h, + path: path, + config: *c, + acceptCh: make(chan (chan acceptResponse)), + closeCh: make(chan int), + doneCh: make(chan int), + } + go l.listenerRoutine() + return l, nil +} + +func connectPipe(p *win32File) error { + c, err := p.prepareIO() + if err != nil { + return err + } + defer p.wg.Done() + + err = connectNamedPipe(p.handle, &c.o) + _, err = p.asyncIO(c, nil, 0, err) + if err != nil && err != windows.ERROR_PIPE_CONNECTED { //nolint:errorlint // err is Errno + return err + } + return nil +} + +func (l *win32PipeListener) Accept() (net.Conn, error) { + ch := make(chan acceptResponse) + select { + case l.acceptCh <- ch: + response := <-ch + err := response.err + if err != nil { + return nil, err + } + if l.config.MessageMode { + return &win32MessageBytePipe{ + win32Pipe: win32Pipe{win32File: response.f, path: l.path}, + }, nil + } + return &win32Pipe{win32File: response.f, path: l.path}, nil + case <-l.doneCh: + return nil, ErrPipeListenerClosed + } +} + +func (l *win32PipeListener) Close() error { + select { + case l.closeCh <- 1: + <-l.doneCh + case <-l.doneCh: + } + return nil +} + +func (l *win32PipeListener) Addr() net.Addr { + return pipeAddress(l.path) +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go new file mode 100644 index 0000000000..48ce4e9243 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid.go @@ -0,0 +1,232 @@ +// Package guid provides a GUID type. The backing structure for a GUID is +// identical to that used by the golang.org/x/sys/windows GUID type. +// There are two main binary encodings used for a GUID, the big-endian encoding, +// and the Windows (mixed-endian) encoding. See here for details: +// https://en.wikipedia.org/wiki/Universally_unique_identifier#Encoding +package guid + +import ( + "crypto/rand" + "crypto/sha1" //nolint:gosec // not used for secure application + "encoding" + "encoding/binary" + "fmt" + "strconv" +) + +//go:generate go run golang.org/x/tools/cmd/stringer -type=Variant -trimprefix=Variant -linecomment + +// Variant specifies which GUID variant (or "type") of the GUID. It determines +// how the entirety of the rest of the GUID is interpreted. +type Variant uint8 + +// The variants specified by RFC 4122 section 4.1.1. +const ( + // VariantUnknown specifies a GUID variant which does not conform to one of + // the variant encodings specified in RFC 4122. + VariantUnknown Variant = iota + VariantNCS + VariantRFC4122 // RFC 4122 + VariantMicrosoft + VariantFuture +) + +// Version specifies how the bits in the GUID were generated. For instance, a +// version 4 GUID is randomly generated, and a version 5 is generated from the +// hash of an input string. +type Version uint8 + +func (v Version) String() string { + return strconv.FormatUint(uint64(v), 10) +} + +var _ = (encoding.TextMarshaler)(GUID{}) +var _ = (encoding.TextUnmarshaler)(&GUID{}) + +// NewV4 returns a new version 4 (pseudorandom) GUID, as defined by RFC 4122. +func NewV4() (GUID, error) { + var b [16]byte + if _, err := rand.Read(b[:]); err != nil { + return GUID{}, err + } + + g := FromArray(b) + g.setVersion(4) // Version 4 means randomly generated. + g.setVariant(VariantRFC4122) + + return g, nil +} + +// NewV5 returns a new version 5 (generated from a string via SHA-1 hashing) +// GUID, as defined by RFC 4122. The RFC is unclear on the encoding of the name, +// and the sample code treats it as a series of bytes, so we do the same here. +// +// Some implementations, such as those found on Windows, treat the name as a +// big-endian UTF16 stream of bytes. If that is desired, the string can be +// encoded as such before being passed to this function. +func NewV5(namespace GUID, name []byte) (GUID, error) { + b := sha1.New() //nolint:gosec // not used for secure application + namespaceBytes := namespace.ToArray() + b.Write(namespaceBytes[:]) + b.Write(name) + + a := [16]byte{} + copy(a[:], b.Sum(nil)) + + g := FromArray(a) + g.setVersion(5) // Version 5 means generated from a string. + g.setVariant(VariantRFC4122) + + return g, nil +} + +func fromArray(b [16]byte, order binary.ByteOrder) GUID { + var g GUID + g.Data1 = order.Uint32(b[0:4]) + g.Data2 = order.Uint16(b[4:6]) + g.Data3 = order.Uint16(b[6:8]) + copy(g.Data4[:], b[8:16]) + return g +} + +func (g GUID) toArray(order binary.ByteOrder) [16]byte { + b := [16]byte{} + order.PutUint32(b[0:4], g.Data1) + order.PutUint16(b[4:6], g.Data2) + order.PutUint16(b[6:8], g.Data3) + copy(b[8:16], g.Data4[:]) + return b +} + +// FromArray constructs a GUID from a big-endian encoding array of 16 bytes. +func FromArray(b [16]byte) GUID { + return fromArray(b, binary.BigEndian) +} + +// ToArray returns an array of 16 bytes representing the GUID in big-endian +// encoding. +func (g GUID) ToArray() [16]byte { + return g.toArray(binary.BigEndian) +} + +// FromWindowsArray constructs a GUID from a Windows encoding array of bytes. +func FromWindowsArray(b [16]byte) GUID { + return fromArray(b, binary.LittleEndian) +} + +// ToWindowsArray returns an array of 16 bytes representing the GUID in Windows +// encoding. +func (g GUID) ToWindowsArray() [16]byte { + return g.toArray(binary.LittleEndian) +} + +func (g GUID) String() string { + return fmt.Sprintf( + "%08x-%04x-%04x-%04x-%012x", + g.Data1, + g.Data2, + g.Data3, + g.Data4[:2], + g.Data4[2:]) +} + +// FromString parses a string containing a GUID and returns the GUID. The only +// format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` +// format. +func FromString(s string) (GUID, error) { + if len(s) != 36 { + return GUID{}, fmt.Errorf("invalid GUID %q", s) + } + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return GUID{}, fmt.Errorf("invalid GUID %q", s) + } + + var g GUID + + data1, err := strconv.ParseUint(s[0:8], 16, 32) + if err != nil { + return GUID{}, fmt.Errorf("invalid GUID %q", s) + } + g.Data1 = uint32(data1) + + data2, err := strconv.ParseUint(s[9:13], 16, 16) + if err != nil { + return GUID{}, fmt.Errorf("invalid GUID %q", s) + } + g.Data2 = uint16(data2) + + data3, err := strconv.ParseUint(s[14:18], 16, 16) + if err != nil { + return GUID{}, fmt.Errorf("invalid GUID %q", s) + } + g.Data3 = uint16(data3) + + for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} { + v, err := strconv.ParseUint(s[x:x+2], 16, 8) + if err != nil { + return GUID{}, fmt.Errorf("invalid GUID %q", s) + } + g.Data4[i] = uint8(v) + } + + return g, nil +} + +func (g *GUID) setVariant(v Variant) { + d := g.Data4[0] + switch v { + case VariantNCS: + d = (d & 0x7f) + case VariantRFC4122: + d = (d & 0x3f) | 0x80 + case VariantMicrosoft: + d = (d & 0x1f) | 0xc0 + case VariantFuture: + d = (d & 0x0f) | 0xe0 + case VariantUnknown: + fallthrough + default: + panic(fmt.Sprintf("invalid variant: %d", v)) + } + g.Data4[0] = d +} + +// Variant returns the GUID variant, as defined in RFC 4122. +func (g GUID) Variant() Variant { + b := g.Data4[0] + if b&0x80 == 0 { + return VariantNCS + } else if b&0xc0 == 0x80 { + return VariantRFC4122 + } else if b&0xe0 == 0xc0 { + return VariantMicrosoft + } else if b&0xe0 == 0xe0 { + return VariantFuture + } + return VariantUnknown +} + +func (g *GUID) setVersion(v Version) { + g.Data3 = (g.Data3 & 0x0fff) | (uint16(v) << 12) +} + +// Version returns the GUID version, as defined in RFC 4122. +func (g GUID) Version() Version { + return Version((g.Data3 & 0xF000) >> 12) +} + +// MarshalText returns the textual representation of the GUID. +func (g GUID) MarshalText() ([]byte, error) { + return []byte(g.String()), nil +} + +// UnmarshalText takes the textual representation of a GUID, and unmarhals it +// into this GUID. +func (g *GUID) UnmarshalText(text []byte) error { + g2, err := FromString(string(text)) + if err != nil { + return err + } + *g = g2 + return nil +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go new file mode 100644 index 0000000000..805bd35484 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_nonwindows.go @@ -0,0 +1,16 @@ +//go:build !windows +// +build !windows + +package guid + +// GUID represents a GUID/UUID. It has the same structure as +// golang.org/x/sys/windows.GUID so that it can be used with functions expecting +// that type. It is defined as its own type as that is only available to builds +// targeted at `windows`. The representation matches that used by native Windows +// code. +type GUID struct { + Data1 uint32 + Data2 uint16 + Data3 uint16 + Data4 [8]byte +} diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go new file mode 100644 index 0000000000..27e45ee5cc --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/guid_windows.go @@ -0,0 +1,13 @@ +//go:build windows +// +build windows + +package guid + +import "golang.org/x/sys/windows" + +// GUID represents a GUID/UUID. It has the same structure as +// golang.org/x/sys/windows.GUID so that it can be used with functions expecting +// that type. It is defined as its own type so that stringification and +// marshaling can be supported. The representation matches that used by native +// Windows code. +type GUID windows.GUID diff --git a/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go b/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go new file mode 100644 index 0000000000..4076d3132f --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/pkg/guid/variant_string.go @@ -0,0 +1,27 @@ +// Code generated by "stringer -type=Variant -trimprefix=Variant -linecomment"; DO NOT EDIT. + +package guid + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[VariantUnknown-0] + _ = x[VariantNCS-1] + _ = x[VariantRFC4122-2] + _ = x[VariantMicrosoft-3] + _ = x[VariantFuture-4] +} + +const _Variant_name = "UnknownNCSRFC 4122MicrosoftFuture" + +var _Variant_index = [...]uint8{0, 7, 10, 18, 27, 33} + +func (i Variant) String() string { + if i >= Variant(len(_Variant_index)-1) { + return "Variant(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Variant_name[_Variant_index[i]:_Variant_index[i+1]] +} diff --git a/vendor/github.com/Microsoft/go-winio/privilege.go b/vendor/github.com/Microsoft/go-winio/privilege.go new file mode 100644 index 0000000000..d9b90b6e86 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/privilege.go @@ -0,0 +1,196 @@ +//go:build windows +// +build windows + +package winio + +import ( + "bytes" + "encoding/binary" + "fmt" + "runtime" + "sync" + "unicode/utf16" + + "golang.org/x/sys/windows" +) + +//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges +//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf +//sys revertToSelf() (err error) = advapi32.RevertToSelf +//sys openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken +//sys getCurrentThread() (h windows.Handle) = GetCurrentThread +//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW +//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW +//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW + +const ( + //revive:disable-next-line:var-naming ALL_CAPS + SE_PRIVILEGE_ENABLED = windows.SE_PRIVILEGE_ENABLED + + //revive:disable-next-line:var-naming ALL_CAPS + ERROR_NOT_ALL_ASSIGNED windows.Errno = windows.ERROR_NOT_ALL_ASSIGNED + + SeBackupPrivilege = "SeBackupPrivilege" + SeRestorePrivilege = "SeRestorePrivilege" + SeSecurityPrivilege = "SeSecurityPrivilege" +) + +var ( + privNames = make(map[string]uint64) + privNameMutex sync.Mutex +) + +// PrivilegeError represents an error enabling privileges. +type PrivilegeError struct { + privileges []uint64 +} + +func (e *PrivilegeError) Error() string { + s := "Could not enable privilege " + if len(e.privileges) > 1 { + s = "Could not enable privileges " + } + for i, p := range e.privileges { + if i != 0 { + s += ", " + } + s += `"` + s += getPrivilegeName(p) + s += `"` + } + return s +} + +// RunWithPrivilege enables a single privilege for a function call. +func RunWithPrivilege(name string, fn func() error) error { + return RunWithPrivileges([]string{name}, fn) +} + +// RunWithPrivileges enables privileges for a function call. +func RunWithPrivileges(names []string, fn func() error) error { + privileges, err := mapPrivileges(names) + if err != nil { + return err + } + runtime.LockOSThread() + defer runtime.UnlockOSThread() + token, err := newThreadToken() + if err != nil { + return err + } + defer releaseThreadToken(token) + err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED) + if err != nil { + return err + } + return fn() +} + +func mapPrivileges(names []string) ([]uint64, error) { + privileges := make([]uint64, 0, len(names)) + privNameMutex.Lock() + defer privNameMutex.Unlock() + for _, name := range names { + p, ok := privNames[name] + if !ok { + err := lookupPrivilegeValue("", name, &p) + if err != nil { + return nil, err + } + privNames[name] = p + } + privileges = append(privileges, p) + } + return privileges, nil +} + +// EnableProcessPrivileges enables privileges globally for the process. +func EnableProcessPrivileges(names []string) error { + return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED) +} + +// DisableProcessPrivileges disables privileges globally for the process. +func DisableProcessPrivileges(names []string) error { + return enableDisableProcessPrivilege(names, 0) +} + +func enableDisableProcessPrivilege(names []string, action uint32) error { + privileges, err := mapPrivileges(names) + if err != nil { + return err + } + + p := windows.CurrentProcess() + var token windows.Token + err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token) + if err != nil { + return err + } + + defer token.Close() + return adjustPrivileges(token, privileges, action) +} + +func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error { + var b bytes.Buffer + _ = binary.Write(&b, binary.LittleEndian, uint32(len(privileges))) + for _, p := range privileges { + _ = binary.Write(&b, binary.LittleEndian, p) + _ = binary.Write(&b, binary.LittleEndian, action) + } + prevState := make([]byte, b.Len()) + reqSize := uint32(0) + success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize) + if !success { + return err + } + if err == ERROR_NOT_ALL_ASSIGNED { //nolint:errorlint // err is Errno + return &PrivilegeError{privileges} + } + return nil +} + +func getPrivilegeName(luid uint64) string { + var nameBuffer [256]uint16 + bufSize := uint32(len(nameBuffer)) + err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize) + if err != nil { + return fmt.Sprintf("", luid) + } + + var displayNameBuffer [256]uint16 + displayBufSize := uint32(len(displayNameBuffer)) + var langID uint32 + err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID) + if err != nil { + return fmt.Sprintf("", string(utf16.Decode(nameBuffer[:bufSize]))) + } + + return string(utf16.Decode(displayNameBuffer[:displayBufSize])) +} + +func newThreadToken() (windows.Token, error) { + err := impersonateSelf(windows.SecurityImpersonation) + if err != nil { + return 0, err + } + + var token windows.Token + err = openThreadToken(getCurrentThread(), windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, false, &token) + if err != nil { + rerr := revertToSelf() + if rerr != nil { + panic(rerr) + } + return 0, err + } + return token, nil +} + +func releaseThreadToken(h windows.Token) { + err := revertToSelf() + if err != nil { + panic(err) + } + h.Close() +} diff --git a/vendor/github.com/Microsoft/go-winio/reparse.go b/vendor/github.com/Microsoft/go-winio/reparse.go new file mode 100644 index 0000000000..67d1a104a6 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/reparse.go @@ -0,0 +1,131 @@ +//go:build windows +// +build windows + +package winio + +import ( + "bytes" + "encoding/binary" + "fmt" + "strings" + "unicode/utf16" + "unsafe" +) + +const ( + reparseTagMountPoint = 0xA0000003 + reparseTagSymlink = 0xA000000C +) + +type reparseDataBuffer struct { + ReparseTag uint32 + ReparseDataLength uint16 + Reserved uint16 + SubstituteNameOffset uint16 + SubstituteNameLength uint16 + PrintNameOffset uint16 + PrintNameLength uint16 +} + +// ReparsePoint describes a Win32 symlink or mount point. +type ReparsePoint struct { + Target string + IsMountPoint bool +} + +// UnsupportedReparsePointError is returned when trying to decode a non-symlink or +// mount point reparse point. +type UnsupportedReparsePointError struct { + Tag uint32 +} + +func (e *UnsupportedReparsePointError) Error() string { + return fmt.Sprintf("unsupported reparse point %x", e.Tag) +} + +// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink +// or a mount point. +func DecodeReparsePoint(b []byte) (*ReparsePoint, error) { + tag := binary.LittleEndian.Uint32(b[0:4]) + return DecodeReparsePointData(tag, b[8:]) +} + +func DecodeReparsePointData(tag uint32, b []byte) (*ReparsePoint, error) { + isMountPoint := false + switch tag { + case reparseTagMountPoint: + isMountPoint = true + case reparseTagSymlink: + default: + return nil, &UnsupportedReparsePointError{tag} + } + nameOffset := 8 + binary.LittleEndian.Uint16(b[4:6]) + if !isMountPoint { + nameOffset += 4 + } + nameLength := binary.LittleEndian.Uint16(b[6:8]) + name := make([]uint16, nameLength/2) + err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name) + if err != nil { + return nil, err + } + return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil +} + +func isDriveLetter(c byte) bool { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') +} + +// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or +// mount point. +func EncodeReparsePoint(rp *ReparsePoint) []byte { + // Generate an NT path and determine if this is a relative path. + var ntTarget string + relative := false + if strings.HasPrefix(rp.Target, `\\?\`) { + ntTarget = `\??\` + rp.Target[4:] + } else if strings.HasPrefix(rp.Target, `\\`) { + ntTarget = `\??\UNC\` + rp.Target[2:] + } else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' { + ntTarget = `\??\` + rp.Target + } else { + ntTarget = rp.Target + relative = true + } + + // The paths must be NUL-terminated even though they are counted strings. + target16 := utf16.Encode([]rune(rp.Target + "\x00")) + ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00")) + + size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8 + size += len(ntTarget16)*2 + len(target16)*2 + + tag := uint32(reparseTagMountPoint) + if !rp.IsMountPoint { + tag = reparseTagSymlink + size += 4 // Add room for symlink flags + } + + data := reparseDataBuffer{ + ReparseTag: tag, + ReparseDataLength: uint16(size), + SubstituteNameOffset: 0, + SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2), + PrintNameOffset: uint16(len(ntTarget16) * 2), + PrintNameLength: uint16((len(target16) - 1) * 2), + } + + var b bytes.Buffer + _ = binary.Write(&b, binary.LittleEndian, &data) + if !rp.IsMountPoint { + flags := uint32(0) + if relative { + flags |= 1 + } + _ = binary.Write(&b, binary.LittleEndian, flags) + } + + _ = binary.Write(&b, binary.LittleEndian, ntTarget16) + _ = binary.Write(&b, binary.LittleEndian, target16) + return b.Bytes() +} diff --git a/vendor/github.com/Microsoft/go-winio/sd.go b/vendor/github.com/Microsoft/go-winio/sd.go new file mode 100644 index 0000000000..c3685e98e1 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/sd.go @@ -0,0 +1,133 @@ +//go:build windows +// +build windows + +package winio + +import ( + "errors" + "fmt" + "unsafe" + + "golang.org/x/sys/windows" +) + +//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW +//sys lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountSidW +//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW +//sys convertStringSidToSid(str *uint16, sid **byte) (err error) = advapi32.ConvertStringSidToSidW + +type AccountLookupError struct { + Name string + Err error +} + +func (e *AccountLookupError) Error() string { + if e.Name == "" { + return "lookup account: empty account name specified" + } + var s string + switch { + case errors.Is(e.Err, windows.ERROR_INVALID_SID): + s = "the security ID structure is invalid" + case errors.Is(e.Err, windows.ERROR_NONE_MAPPED): + s = "not found" + default: + s = e.Err.Error() + } + return "lookup account " + e.Name + ": " + s +} + +func (e *AccountLookupError) Unwrap() error { return e.Err } + +type SddlConversionError struct { + Sddl string + Err error +} + +func (e *SddlConversionError) Error() string { + return "convert " + e.Sddl + ": " + e.Err.Error() +} + +func (e *SddlConversionError) Unwrap() error { return e.Err } + +// LookupSidByName looks up the SID of an account by name +// +//revive:disable-next-line:var-naming SID, not Sid +func LookupSidByName(name string) (sid string, err error) { + if name == "" { + return "", &AccountLookupError{name, windows.ERROR_NONE_MAPPED} + } + + var sidSize, sidNameUse, refDomainSize uint32 + err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse) + if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno + return "", &AccountLookupError{name, err} + } + sidBuffer := make([]byte, sidSize) + refDomainBuffer := make([]uint16, refDomainSize) + err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse) + if err != nil { + return "", &AccountLookupError{name, err} + } + var strBuffer *uint16 + err = convertSidToStringSid(&sidBuffer[0], &strBuffer) + if err != nil { + return "", &AccountLookupError{name, err} + } + sid = windows.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:]) + _, _ = windows.LocalFree(windows.Handle(unsafe.Pointer(strBuffer))) + return sid, nil +} + +// LookupNameBySid looks up the name of an account by SID +// +//revive:disable-next-line:var-naming SID, not Sid +func LookupNameBySid(sid string) (name string, err error) { + if sid == "" { + return "", &AccountLookupError{sid, windows.ERROR_NONE_MAPPED} + } + + sidBuffer, err := windows.UTF16PtrFromString(sid) + if err != nil { + return "", &AccountLookupError{sid, err} + } + + var sidPtr *byte + if err = convertStringSidToSid(sidBuffer, &sidPtr); err != nil { + return "", &AccountLookupError{sid, err} + } + defer windows.LocalFree(windows.Handle(unsafe.Pointer(sidPtr))) //nolint:errcheck + + var nameSize, refDomainSize, sidNameUse uint32 + err = lookupAccountSid(nil, sidPtr, nil, &nameSize, nil, &refDomainSize, &sidNameUse) + if err != nil && err != windows.ERROR_INSUFFICIENT_BUFFER { //nolint:errorlint // err is Errno + return "", &AccountLookupError{sid, err} + } + + nameBuffer := make([]uint16, nameSize) + refDomainBuffer := make([]uint16, refDomainSize) + err = lookupAccountSid(nil, sidPtr, &nameBuffer[0], &nameSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse) + if err != nil { + return "", &AccountLookupError{sid, err} + } + + name = windows.UTF16ToString(nameBuffer) + return name, nil +} + +func SddlToSecurityDescriptor(sddl string) ([]byte, error) { + sd, err := windows.SecurityDescriptorFromString(sddl) + if err != nil { + return nil, &SddlConversionError{Sddl: sddl, Err: err} + } + b := unsafe.Slice((*byte)(unsafe.Pointer(sd)), sd.Length()) + return b, nil +} + +func SecurityDescriptorToSddl(sd []byte) (string, error) { + if l := int(unsafe.Sizeof(windows.SECURITY_DESCRIPTOR{})); len(sd) < l { + return "", fmt.Errorf("SecurityDescriptor (%d) smaller than expected (%d): %w", len(sd), l, windows.ERROR_INCORRECT_SIZE) + } + s := (*windows.SECURITY_DESCRIPTOR)(unsafe.Pointer(&sd[0])) + return s.String(), nil +} diff --git a/vendor/github.com/Microsoft/go-winio/syscall.go b/vendor/github.com/Microsoft/go-winio/syscall.go new file mode 100644 index 0000000000..a6ca111b39 --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/syscall.go @@ -0,0 +1,5 @@ +//go:build windows + +package winio + +//go:generate go run github.com/Microsoft/go-winio/tools/mkwinsyscall -output zsyscall_windows.go ./*.go diff --git a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go new file mode 100644 index 0000000000..89b66eda8c --- /dev/null +++ b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go @@ -0,0 +1,378 @@ +//go:build windows + +// Code generated by 'go generate' using "github.com/Microsoft/go-winio/tools/mkwinsyscall"; DO NOT EDIT. + +package winio + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return errERROR_EINVAL + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + return e +} + +var ( + modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + modntdll = windows.NewLazySystemDLL("ntdll.dll") + modws2_32 = windows.NewLazySystemDLL("ws2_32.dll") + + procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") + procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") + procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW") + procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") + procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") + procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW") + procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW") + procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW") + procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") + procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") + procRevertToSelf = modadvapi32.NewProc("RevertToSelf") + procBackupRead = modkernel32.NewProc("BackupRead") + procBackupWrite = modkernel32.NewProc("BackupWrite") + procCancelIoEx = modkernel32.NewProc("CancelIoEx") + procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") + procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") + procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") + procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe") + procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") + procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") + procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo") + procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") + procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes") + procNtCreateNamedPipeFile = modntdll.NewProc("NtCreateNamedPipeFile") + procRtlDefaultNpAcl = modntdll.NewProc("RtlDefaultNpAcl") + procRtlDosPathNameToNtPathName_U = modntdll.NewProc("RtlDosPathNameToNtPathName_U") + procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb") + procWSAGetOverlappedResult = modws2_32.NewProc("WSAGetOverlappedResult") +) + +func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) { + var _p0 uint32 + if releaseAll { + _p0 = 1 + } + r0, _, e1 := syscall.SyscallN(procAdjustTokenPrivileges.Addr(), uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize))) + success = r0 != 0 + if true { + err = errnoErr(e1) + } + return +} + +func convertSidToStringSid(sid *byte, str **uint16) (err error) { + r1, _, e1 := syscall.SyscallN(procConvertSidToStringSidW.Addr(), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func convertStringSidToSid(str *uint16, sid **byte) (err error) { + r1, _, e1 := syscall.SyscallN(procConvertStringSidToSidW.Addr(), uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(sid))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func impersonateSelf(level uint32) (err error) { + r1, _, e1 := syscall.SyscallN(procImpersonateSelf.Addr(), uintptr(level)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(accountName) + if err != nil { + return + } + return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse) +} + +func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { + r1, _, e1 := syscall.SyscallN(procLookupAccountNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func lookupAccountSid(systemName *uint16, sid *byte, name *uint16, nameSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { + r1, _, e1 := syscall.SyscallN(procLookupAccountSidW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(systemName) + if err != nil { + return + } + return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId) +} + +func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) { + r1, _, e1 := syscall.SyscallN(procLookupPrivilegeDisplayNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(systemName) + if err != nil { + return + } + return _lookupPrivilegeName(_p0, luid, buffer, size) +} + +func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) { + r1, _, e1 := syscall.SyscallN(procLookupPrivilegeNameW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(systemName) + if err != nil { + return + } + var _p1 *uint16 + _p1, err = syscall.UTF16PtrFromString(name) + if err != nil { + return + } + return _lookupPrivilegeValue(_p0, _p1, luid) +} + +func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) { + r1, _, e1 := syscall.SyscallN(procLookupPrivilegeValueW.Addr(), uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func openThreadToken(thread windows.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) { + var _p0 uint32 + if openAsSelf { + _p0 = 1 + } + r1, _, e1 := syscall.SyscallN(procOpenThreadToken.Addr(), uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func revertToSelf() (err error) { + r1, _, e1 := syscall.SyscallN(procRevertToSelf.Addr()) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func backupRead(h windows.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { + var _p0 *byte + if len(b) > 0 { + _p0 = &b[0] + } + var _p1 uint32 + if abort { + _p1 = 1 + } + var _p2 uint32 + if processSecurity { + _p2 = 1 + } + r1, _, e1 := syscall.SyscallN(procBackupRead.Addr(), uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func backupWrite(h windows.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) { + var _p0 *byte + if len(b) > 0 { + _p0 = &b[0] + } + var _p1 uint32 + if abort { + _p1 = 1 + } + var _p2 uint32 + if processSecurity { + _p2 = 1 + } + r1, _, e1 := syscall.SyscallN(procBackupWrite.Addr(), uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func cancelIoEx(file windows.Handle, o *windows.Overlapped) (err error) { + r1, _, e1 := syscall.SyscallN(procCancelIoEx.Addr(), uintptr(file), uintptr(unsafe.Pointer(o))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func connectNamedPipe(pipe windows.Handle, o *windows.Overlapped) (err error) { + r1, _, e1 := syscall.SyscallN(procConnectNamedPipe.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(o))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func createIoCompletionPort(file windows.Handle, port windows.Handle, key uintptr, threadCount uint32) (newport windows.Handle, err error) { + r0, _, e1 := syscall.SyscallN(procCreateIoCompletionPort.Addr(), uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount)) + newport = windows.Handle(r0) + if newport == 0 { + err = errnoErr(e1) + } + return +} + +func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(name) + if err != nil { + return + } + return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa) +} + +func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *windows.SecurityAttributes) (handle windows.Handle, err error) { + r0, _, e1 := syscall.SyscallN(procCreateNamedPipeW.Addr(), uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa))) + handle = windows.Handle(r0) + if handle == windows.InvalidHandle { + err = errnoErr(e1) + } + return +} + +func disconnectNamedPipe(pipe windows.Handle) (err error) { + r1, _, e1 := syscall.SyscallN(procDisconnectNamedPipe.Addr(), uintptr(pipe)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func getCurrentThread() (h windows.Handle) { + r0, _, _ := syscall.SyscallN(procGetCurrentThread.Addr()) + h = windows.Handle(r0) + return +} + +func getNamedPipeHandleState(pipe windows.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) { + r1, _, e1 := syscall.SyscallN(procGetNamedPipeHandleStateW.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func getNamedPipeInfo(pipe windows.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) { + r1, _, e1 := syscall.SyscallN(procGetNamedPipeInfo.Addr(), uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func getQueuedCompletionStatus(port windows.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) { + r1, _, e1 := syscall.SyscallN(procGetQueuedCompletionStatus.Addr(), uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func setFileCompletionNotificationModes(h windows.Handle, flags uint8) (err error) { + r1, _, e1 := syscall.SyscallN(procSetFileCompletionNotificationModes.Addr(), uintptr(h), uintptr(flags)) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func ntCreateNamedPipeFile(pipe *windows.Handle, access ntAccessMask, oa *objectAttributes, iosb *ioStatusBlock, share ntFileShareMode, disposition ntFileCreationDisposition, options ntFileOptions, typ uint32, readMode uint32, completionMode uint32, maxInstances uint32, inboundQuota uint32, outputQuota uint32, timeout *int64) (status ntStatus) { + r0, _, _ := syscall.SyscallN(procNtCreateNamedPipeFile.Addr(), uintptr(unsafe.Pointer(pipe)), uintptr(access), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(share), uintptr(disposition), uintptr(options), uintptr(typ), uintptr(readMode), uintptr(completionMode), uintptr(maxInstances), uintptr(inboundQuota), uintptr(outputQuota), uintptr(unsafe.Pointer(timeout))) + status = ntStatus(r0) + return +} + +func rtlDefaultNpAcl(dacl *uintptr) (status ntStatus) { + r0, _, _ := syscall.SyscallN(procRtlDefaultNpAcl.Addr(), uintptr(unsafe.Pointer(dacl))) + status = ntStatus(r0) + return +} + +func rtlDosPathNameToNtPathName(name *uint16, ntName *unicodeString, filePart uintptr, reserved uintptr) (status ntStatus) { + r0, _, _ := syscall.SyscallN(procRtlDosPathNameToNtPathName_U.Addr(), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(ntName)), uintptr(filePart), uintptr(reserved)) + status = ntStatus(r0) + return +} + +func rtlNtStatusToDosError(status ntStatus) (winerr error) { + r0, _, _ := syscall.SyscallN(procRtlNtStatusToDosErrorNoTeb.Addr(), uintptr(status)) + if r0 != 0 { + winerr = syscall.Errno(r0) + } + return +} + +func wsaGetOverlappedResult(h windows.Handle, o *windows.Overlapped, bytes *uint32, wait bool, flags *uint32) (err error) { + var _p0 uint32 + if wait { + _p0 = 1 + } + r1, _, e1 := syscall.SyscallN(procWSAGetOverlappedResult.Addr(), uintptr(h), uintptr(unsafe.Pointer(o)), uintptr(unsafe.Pointer(bytes)), uintptr(_p0), uintptr(unsafe.Pointer(flags))) + if r1 == 0 { + err = errnoErr(e1) + } + return +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go index d0f3094bc8..5767a007db 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go @@ -3,4 +3,4 @@ package aws // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.36.5" +const goModuleVersion = "1.36.6" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/config/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/config/CHANGELOG.md index a3e49f8931..9329eeda5d 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/config/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/config/CHANGELOG.md @@ -1,3 +1,7 @@ +# v1.29.18 (2025-07-19) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.29.17 (2025-06-17) * **Dependency Update**: Update to smithy-go v1.22.4. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/config/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/config/go_module_metadata.go index ef19c0a7f5..682148feb4 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/config/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/config/go_module_metadata.go @@ -3,4 +3,4 @@ package config // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.29.17" +const goModuleVersion = "1.29.18" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/credentials/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/credentials/CHANGELOG.md index 1df7649ff7..4e0a2c1d5b 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/credentials/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/credentials/CHANGELOG.md @@ -1,3 +1,7 @@ +# v1.17.71 (2025-07-19) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.17.70 (2025-06-17) * **Dependency Update**: Update to smithy-go v1.22.4. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/credentials/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/credentials/go_module_metadata.go index 729137d857..8985623857 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/credentials/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/credentials/go_module_metadata.go @@ -3,4 +3,4 @@ package credentials // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.17.70" +const goModuleVersion = "1.17.71" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/CHANGELOG.md index b204386b53..59fcc717de 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/CHANGELOG.md @@ -1,3 +1,7 @@ +# v1.16.33 (2025-07-19) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.16.32 (2025-06-17) * **Dependency Update**: Update to smithy-go v1.22.4. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/go_module_metadata.go index ebd98386e0..4fb42e3241 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/go_module_metadata.go @@ -3,4 +3,4 @@ package imds // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.16.32" +const goModuleVersion = "1.16.33" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md index a9d68c515b..7bfdbda155 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md @@ -1,3 +1,7 @@ +# v1.3.37 (2025-07-19) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.3.36 (2025-06-17) * **Dependency Update**: Update to smithy-go v1.22.4. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go index dfc815100b..4fb66bc6c4 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go @@ -3,4 +3,4 @@ package configsources // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.3.36" +const goModuleVersion = "1.3.37" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/awsrulesfn/partitions.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/awsrulesfn/partitions.go index 5f0779997d..619c1f5d8f 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/awsrulesfn/partitions.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/awsrulesfn/partitions.go @@ -11,7 +11,7 @@ func GetPartition(region string) *PartitionConfig { var partitions = []Partition{ { ID: "aws", - RegionRegex: "^(us|eu|ap|sa|ca|me|af|il)\\-\\w+\\-\\d+$", + RegionRegex: "^(us|eu|ap|sa|ca|me|af|il|mx)\\-\\w+\\-\\d+$", DefaultConfig: PartitionConfig{ Name: "aws", DnsSuffix: "amazonaws.com", @@ -35,6 +35,13 @@ var partitions = []Partition{ SupportsFIPS: nil, SupportsDualStack: nil, }, + "ap-east-2": { + Name: nil, + DnsSuffix: nil, + DualStackDnsSuffix: nil, + SupportsFIPS: nil, + SupportsDualStack: nil, + }, "ap-northeast-1": { Name: nil, DnsSuffix: nil, @@ -98,6 +105,20 @@ var partitions = []Partition{ SupportsFIPS: nil, SupportsDualStack: nil, }, + "ap-southeast-5": { + Name: nil, + DnsSuffix: nil, + DualStackDnsSuffix: nil, + SupportsFIPS: nil, + SupportsDualStack: nil, + }, + "ap-southeast-7": { + Name: nil, + DnsSuffix: nil, + DualStackDnsSuffix: nil, + SupportsFIPS: nil, + SupportsDualStack: nil, + }, "aws-global": { Name: nil, DnsSuffix: nil, @@ -196,6 +217,13 @@ var partitions = []Partition{ SupportsFIPS: nil, SupportsDualStack: nil, }, + "mx-central-1": { + Name: nil, + DnsSuffix: nil, + DualStackDnsSuffix: nil, + SupportsFIPS: nil, + SupportsDualStack: nil, + }, "sa-east-1": { Name: nil, DnsSuffix: nil, @@ -378,6 +406,13 @@ var partitions = []Partition{ ImplicitGlobalRegion: "eu-isoe-west-1", }, Regions: map[string]RegionOverrides{ + "aws-iso-e-global": { + Name: nil, + DnsSuffix: nil, + DualStackDnsSuffix: nil, + SupportsFIPS: nil, + SupportsDualStack: nil, + }, "eu-isoe-west-1": { Name: nil, DnsSuffix: nil, @@ -398,6 +433,49 @@ var partitions = []Partition{ SupportsDualStack: false, ImplicitGlobalRegion: "us-isof-south-1", }, - Regions: map[string]RegionOverrides{}, + Regions: map[string]RegionOverrides{ + "aws-iso-f-global": { + Name: nil, + DnsSuffix: nil, + DualStackDnsSuffix: nil, + SupportsFIPS: nil, + SupportsDualStack: nil, + }, + "us-isof-east-1": { + Name: nil, + DnsSuffix: nil, + DualStackDnsSuffix: nil, + SupportsFIPS: nil, + SupportsDualStack: nil, + }, + "us-isof-south-1": { + Name: nil, + DnsSuffix: nil, + DualStackDnsSuffix: nil, + SupportsFIPS: nil, + SupportsDualStack: nil, + }, + }, + }, + { + ID: "aws-eusc", + RegionRegex: "^eusc\\-(de)\\-\\w+\\-\\d+$", + DefaultConfig: PartitionConfig{ + Name: "aws-eusc", + DnsSuffix: "amazonaws.eu", + DualStackDnsSuffix: "amazonaws.eu", + SupportsFIPS: true, + SupportsDualStack: false, + ImplicitGlobalRegion: "eusc-de-east-1", + }, + Regions: map[string]RegionOverrides{ + "eusc-de-east-1": { + Name: nil, + DnsSuffix: nil, + DualStackDnsSuffix: nil, + SupportsFIPS: nil, + SupportsDualStack: nil, + }, + }, }, } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md index 01dc55c873..c310db3f35 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md @@ -1,3 +1,7 @@ +# v2.6.37 (2025-07-19) + +* **Dependency Update**: Updated to the latest SDK module versions + # v2.6.36 (2025-06-17) * **Dependency Update**: Update to smithy-go v1.22.4. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go index 44c39bc0ac..390a7bd524 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go @@ -3,4 +3,4 @@ package endpoints // goModuleVersion is the tagged release for this module -const goModuleVersion = "2.6.36" +const goModuleVersion = "2.6.37" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md index 9bbbf0eb43..ce140c8858 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md @@ -1,3 +1,7 @@ +# v1.12.18 (2025-07-19) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.12.17 (2025-06-17) * **Dependency Update**: Update to smithy-go v1.22.4. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go index 72de22c689..be225d761d 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go @@ -3,4 +3,4 @@ package presignedurl // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.12.17" +const goModuleVersion = "1.12.18" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/route53/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/route53/CHANGELOG.md index f11a59a476..b49d5f1203 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/route53/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/route53/CHANGELOG.md @@ -1,3 +1,7 @@ +# v1.53.1 (2025-07-19) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.53.0 (2025-07-03) * **Feature**: Amazon Route 53 now supports the iso-e regions for private DNS Amazon VPCs and cloudwatch healthchecks. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/route53/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/route53/go_module_metadata.go index b8cc1cf9ce..d424e671cd 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/route53/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/route53/go_module_metadata.go @@ -3,4 +3,4 @@ package route53 // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.53.0" +const goModuleVersion = "1.53.1" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/CHANGELOG.md index 6fdc4a2fa8..436a73c253 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/CHANGELOG.md @@ -1,3 +1,7 @@ +# v1.25.6 (2025-07-19) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.25.5 (2025-06-17) * **Dependency Update**: Update to smithy-go v1.22.4. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/go_module_metadata.go index 2b303dc582..1ca7fcfe60 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/go_module_metadata.go @@ -3,4 +3,4 @@ package sso // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.25.5" +const goModuleVersion = "1.25.6" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/CHANGELOG.md index 0f1157c795..5d46699d8a 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/CHANGELOG.md @@ -1,3 +1,7 @@ +# v1.30.4 (2025-07-19) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.30.3 (2025-06-17) * **Dependency Update**: Update to smithy-go v1.22.4. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/go_module_metadata.go index a10fa7b4a4..93c9cbe5af 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/go_module_metadata.go @@ -3,4 +3,4 @@ package ssooidc // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.30.3" +const goModuleVersion = "1.30.4" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md index e1722a6d0f..a4e455bed2 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md @@ -1,3 +1,7 @@ +# v1.34.1 (2025-07-19) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.34.0 (2025-06-17) * **Feature**: The AWS Security Token Service APIs AssumeRoleWithSAML and AssumeRoleWithWebIdentity can now be invoked without pre-configured AWS credentials in the SDK configuration. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go index 0e024c501b..f57082a593 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go @@ -3,4 +3,4 @@ package sts // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.34.0" +const goModuleVersion = "1.34.1" diff --git a/vendor/github.com/dgraph-io/badger/v4/stream.go b/vendor/github.com/dgraph-io/badger/v4/stream.go index 3f5f08d264..ae468abda7 100644 --- a/vendor/github.com/dgraph-io/badger/v4/stream.go +++ b/vendor/github.com/dgraph-io/badger/v4/stream.go @@ -75,6 +75,14 @@ type Stream struct { // // Note: Calls to KeyToList are concurrent. KeyToList func(key []byte, itr *Iterator) (*pb.KVList, error) + // UseKeyToListWithThreadId is used to indicate that KeyToListWithThreadId should be used + // instead of KeyToList. This is a new api that can be used to figure out parallelism + // of the stream. Each threadId would be run serially. KeyToList being concurrent makes you + // take care of concurrency in KeyToList. Here threadId could be used to do some things serially. + // Once a thread finishes FinishThread() would be called. + UseKeyToListWithThreadId bool + KeyToListWithThreadId func(key []byte, itr *Iterator, threadId int) (*pb.KVList, error) + FinishThread func(threadId int) (*pb.KVList, error) // This is the method where Stream sends the final output. All calls to Send are done by a // single goroutine, i.e. logic within Send method can expect single threaded execution. @@ -143,7 +151,7 @@ func (st *Stream) ToList(key []byte, itr *Iterator) (*pb.KVList, error) { // keyRange is [start, end), including start, excluding end. Do ensure that the start, // end byte slices are owned by keyRange struct. func (st *Stream) produceRanges(ctx context.Context) { - ranges := st.db.Ranges(st.Prefix, 16) + ranges := st.db.Ranges(st.Prefix, st.NumGo) y.AssertTrue(len(ranges) > 0) y.AssertTrue(ranges[0].left == nil) y.AssertTrue(ranges[len(ranges)-1].right == nil) @@ -186,7 +194,7 @@ func (st *Stream) produceKVs(ctx context.Context, threadId int) error { iterOpts := DefaultIteratorOptions iterOpts.AllVersions = true iterOpts.Prefix = st.Prefix - iterOpts.PrefetchValues = false + iterOpts.PrefetchValues = true iterOpts.SinceTs = st.SinceTs itr := txn.NewIterator(iterOpts) itr.ThreadId = threadId @@ -233,7 +241,13 @@ func (st *Stream) produceKVs(ctx context.Context, threadId int) error { // Now convert to key value. itr.Alloc.Reset() - list, err := st.KeyToList(item.KeyCopy(nil), itr) + var list *pb.KVList + var err error + if st.UseKeyToListWithThreadId { + list, err = st.KeyToListWithThreadId(item.KeyCopy(nil), itr, threadId) + } else { + list, err = st.KeyToList(item.KeyCopy(nil), itr) + } if err != nil { st.db.opt.Warningf("While reading key: %x, got error: %v", item.Key(), err) continue @@ -252,6 +266,23 @@ func (st *Stream) produceKVs(ctx context.Context, threadId int) error { } } } + + if st.UseKeyToListWithThreadId { + if kvs, err := st.FinishThread(threadId); err != nil { + return err + } else { + for _, kv := range kvs.Kv { + kv.StreamId = streamId + KVToBuffer(kv, outList) + if outList.LenNoPadding() < batchSize { + continue + } + if err := sendIt(); err != nil { + return err + } + } + } + } // Mark the stream as done. if st.doneMarkers { kv := &pb.KV{ diff --git a/vendor/github.com/klauspost/cpuid/v2/README.md b/vendor/github.com/klauspost/cpuid/v2/README.md index 7b1d599211..88d68d5286 100644 --- a/vendor/github.com/klauspost/cpuid/v2/README.md +++ b/vendor/github.com/klauspost/cpuid/v2/README.md @@ -281,7 +281,7 @@ Exit Code 1 | AMXBF16 | Tile computational operations on BFLOAT16 numbers | | AMXINT8 | Tile computational operations on 8-bit integers | | AMXFP16 | Tile computational operations on FP16 numbers | -| AMXFP8 | Tile computational operations on FP8 numbers | +| AMXFP8 | Tile computational operations on FP8 numbers | | AMXCOMPLEX | Tile computational operations on complex numbers | | AMXTILE | Tile architecture | | AMXTF32 | Matrix Multiplication of TF32 Tiles into Packed Single Precision Tile | @@ -418,6 +418,7 @@ Exit Code 1 | SEV_SNP | AMD SEV Secure Nested Paging supported | | SGX | Software Guard Extensions | | SGXLC | Software Guard Extensions Launch Control | +| SGXPQC | Software Guard Extensions 256-bit Encryption | | SHA | Intel SHA Extensions | | SME | AMD Secure Memory Encryption supported | | SME_COHERENT | AMD Hardware cache coherency across encryption domains enforced | @@ -450,6 +451,9 @@ Exit Code 1 | TLB_FLUSH_NESTED | AMD: Flushing includes all the nested translations for guest translations | | TME | Intel Total Memory Encryption. The following MSRs are supported: IA32_TME_CAPABILITY, IA32_TME_ACTIVATE, IA32_TME_EXCLUDE_MASK, and IA32_TME_EXCLUDE_BASE. | | TOPEXT | TopologyExtensions: topology extensions support. Indicates support for CPUID Fn8000_001D_EAX_x[N:0]-CPUID Fn8000_001E_EDX. | +| TSA_L1_NO | AMD only: Not vulnerable to TSA-L1 | +| TSA_SQ_NO | AMD only: Not vulnerable to TSA-SQ | +| TSA_VERW_CLEAR | AMD: If set, the memory form of the VERW instruction may be used to help mitigate TSA | | TSCRATEMSR | MSR based TSC rate control. Indicates support for MSR TSC ratio MSRC000_0104 | | TSXLDTRK | Intel TSX Suspend Load Address Tracking | | VAES | Vector AES. AVX(512) versions requires additional checks. | diff --git a/vendor/github.com/klauspost/cpuid/v2/cpuid.go b/vendor/github.com/klauspost/cpuid/v2/cpuid.go index 248439a9a5..9cf7738a97 100644 --- a/vendor/github.com/klauspost/cpuid/v2/cpuid.go +++ b/vendor/github.com/klauspost/cpuid/v2/cpuid.go @@ -220,6 +220,7 @@ const ( SEV_SNP // AMD SEV Secure Nested Paging supported SGX // Software Guard Extensions SGXLC // Software Guard Extensions Launch Control + SGXPQC // Software Guard Extensions 256-bit Encryption SHA // Intel SHA Extensions SME // AMD Secure Memory Encryption supported SME_COHERENT // AMD Hardware cache coherency across encryption domains enforced @@ -255,6 +256,9 @@ const ( TLB_FLUSH_NESTED // AMD: Flushing includes all the nested translations for guest translations TME // Intel Total Memory Encryption. The following MSRs are supported: IA32_TME_CAPABILITY, IA32_TME_ACTIVATE, IA32_TME_EXCLUDE_MASK, and IA32_TME_EXCLUDE_BASE. TOPEXT // TopologyExtensions: topology extensions support. Indicates support for CPUID Fn8000_001D_EAX_x[N:0]-CPUID Fn8000_001E_EDX. + TSA_L1_NO // AMD only: Not vulnerable to TSA-L1 + TSA_SQ_NO // AM onlyD: Not vulnerable to TSA-SQ + TSA_VERW_CLEAR // If set, the memory form of the VERW instruction may be used to help mitigate TSA TSCRATEMSR // MSR based TSC rate control. Indicates support for MSR TSC ratio MSRC000_0104 TSXLDTRK // Intel TSX Suspend Load Address Tracking VAES // Vector AES. AVX(512) versions requires additional checks. @@ -304,6 +308,13 @@ const ( SM3 // SM3 instructions SM4 // SM4 instructions SVE // Scalable Vector Extension + + // PMU + PMU_FIXEDCOUNTER_CYCLES + PMU_FIXEDCOUNTER_REFCYCLES + PMU_FIXEDCOUNTER_INSTRUCTIONS + PMU_FIXEDCOUNTER_TOPDOWN_SLOTS + // Keep it last. It automatically defines the size of []flagSet lastID @@ -336,11 +347,36 @@ type CPUInfo struct { SGX SGXSupport AMDMemEncryption AMDMemEncryptionSupport AVX10Level uint8 + PMU PerformanceMonitoringInfo // holds information about the PMU maxFunc uint32 maxExFunc uint32 } +// PerformanceMonitoringInfo holds information about CPU performance monitoring capabilities. +// This is primarily populated from CPUID leaf 0xAh on x86 +type PerformanceMonitoringInfo struct { + // VersionID (x86 only): Version ID of architectural performance monitoring. + // A value of 0 means architectural performance monitoring is not supported or information is unavailable. + VersionID uint8 + // NumGPPMC: Number of General-Purpose Performance Monitoring Counters per logical processor. + // On ARM, this is derived from PMCR_EL0.N (number of event counters). + NumGPCounters uint8 + // GPPMCWidth: Bit width of General-Purpose Performance Monitoring Counters. + // On ARM, typically 64 for PMU event counters. + GPPMCWidth uint8 + // NumFixedPMC: Number of Fixed-Function Performance Counters. + // Valid on x86 if VersionID > 1. On ARM, this typically includes at least the cycle counter (PMCCNTR_EL0). + NumFixedPMC uint8 + // FixedPMCWidth: Bit width of Fixed-Function Performance Counters. + // Valid on x86 if VersionID > 1. On ARM, the cycle counter (PMCCNTR_EL0) is 64-bit. + FixedPMCWidth uint8 + // Raw register output from CPUID leaf 0xAh. + RawEBX uint32 + RawEAX uint32 + RawEDX uint32 +} + var cpuid func(op uint32) (eax, ebx, ecx, edx uint32) var cpuidex func(op, op2 uint32) (eax, ebx, ecx, edx uint32) var xgetbv func(index uint32) (eax, edx uint32) @@ -1358,6 +1394,11 @@ func support() flagSet { fs.setIf(edx&(1<<4) != 0, BHI_CTRL) fs.setIf(edx&(1<<5) != 0, MCDT_NO) + if fs.inSet(SGX) { + eax, _, _, _ := cpuidex(0x12, 0) + fs.setIf(eax&(1<<12) != 0, SGXPQC) + } + // Add keylocker features. if fs.inSet(KEYLOCKER) && mfi >= 0x19 { _, ebx, _, _ := cpuidex(0x19, 0) @@ -1371,6 +1412,7 @@ func support() flagSet { fs.setIf(ebx&(1<<17) != 0, AVX10_256) fs.setIf(ebx&(1<<18) != 0, AVX10_512) } + } // Processor Extended State Enumeration Sub-leaf (EAX = 0DH, ECX = 1) @@ -1514,12 +1556,28 @@ func support() flagSet { } if maxExtendedFunction() >= 0x80000021 && vend == AMD { - a, _, _, _ := cpuid(0x80000021) + a, _, c, _ := cpuid(0x80000021) fs.setIf((a>>31)&1 == 1, SRSO_MSR_FIX) fs.setIf((a>>30)&1 == 1, SRSO_USER_KERNEL_NO) fs.setIf((a>>29)&1 == 1, SRSO_NO) fs.setIf((a>>28)&1 == 1, IBPB_BRTYPE) fs.setIf((a>>27)&1 == 1, SBPB) + fs.setIf((c>>1)&1 == 1, TSA_L1_NO) + fs.setIf((c>>2)&1 == 1, TSA_SQ_NO) + fs.setIf((a>>5)&1 == 1, TSA_VERW_CLEAR) + } + if vend == AMD { + if family < 0x19 { + // AMD CPUs that are older than Family 19h are not vulnerable to TSA but do not set TSA_L1_NO or TSA_SQ_NO. + // Source: https://www.amd.com/content/dam/amd/en/documents/resources/bulletin/technical-guidance-for-mitigating-transient-scheduler-attacks.pdf + fs.set(TSA_L1_NO) + fs.set(TSA_SQ_NO) + } else if family == 0x1a { + // AMD Family 1Ah models 00h-4Fh and 60h-7Fh are also not vulnerable to TSA but do not set TSA_L1_NO or TSA_SQ_NO. + // Future AMD CPUs will set these CPUID bits if appropriate. CPUs will be designed to set these CPUID bits if appropriate. + notVuln := model <= 0x4f || (model >= 0x60 && model <= 0x7f) + fs.setIf(notVuln, TSA_L1_NO, TSA_SQ_NO) + } } if mfi >= 0x20 { @@ -1575,3 +1633,47 @@ func valAsString(values ...uint32) []byte { } return r } + +func parseLeaf0AH(c *CPUInfo, eax, ebx, edx uint32) (info PerformanceMonitoringInfo) { + info.VersionID = uint8(eax & 0xFF) + info.NumGPCounters = uint8((eax >> 8) & 0xFF) + info.GPPMCWidth = uint8((eax >> 16) & 0xFF) + + info.RawEBX = ebx + info.RawEAX = eax + info.RawEDX = edx + + if info.VersionID > 1 { // This information is only valid if VersionID > 1 + info.NumFixedPMC = uint8(edx & 0x1F) // Bits 4:0 + info.FixedPMCWidth = uint8((edx >> 5) & 0xFF) // Bits 12:5 + } + if info.VersionID > 0 { + // first 4 fixed events are always instructions retired, cycles, ref cycles and topdown slots + if ebx == 0x0 && info.NumFixedPMC == 3 { + c.featureSet.set(PMU_FIXEDCOUNTER_INSTRUCTIONS) + c.featureSet.set(PMU_FIXEDCOUNTER_CYCLES) + c.featureSet.set(PMU_FIXEDCOUNTER_REFCYCLES) + } + if ebx == 0x0 && info.NumFixedPMC == 4 { + c.featureSet.set(PMU_FIXEDCOUNTER_INSTRUCTIONS) + c.featureSet.set(PMU_FIXEDCOUNTER_CYCLES) + c.featureSet.set(PMU_FIXEDCOUNTER_REFCYCLES) + c.featureSet.set(PMU_FIXEDCOUNTER_TOPDOWN_SLOTS) + } + if ebx != 0x0 { + if ((ebx >> 0) & 1) == 0 { + c.featureSet.set(PMU_FIXEDCOUNTER_INSTRUCTIONS) + } + if ((ebx >> 1) & 1) == 0 { + c.featureSet.set(PMU_FIXEDCOUNTER_CYCLES) + } + if ((ebx >> 2) & 1) == 0 { + c.featureSet.set(PMU_FIXEDCOUNTER_REFCYCLES) + } + if ((ebx >> 3) & 1) == 0 { + c.featureSet.set(PMU_FIXEDCOUNTER_TOPDOWN_SLOTS) + } + } + } + return info +} diff --git a/vendor/github.com/klauspost/cpuid/v2/detect_x86.go b/vendor/github.com/klauspost/cpuid/v2/detect_x86.go index f924c9d839..14a56b9301 100644 --- a/vendor/github.com/klauspost/cpuid/v2/detect_x86.go +++ b/vendor/github.com/klauspost/cpuid/v2/detect_x86.go @@ -36,6 +36,10 @@ func addInfo(c *CPUInfo, safe bool) { c.AVX10Level = c.supportAVX10() c.cacheSize() c.frequencies() + if c.maxFunc >= 0x0A { + eax, ebx, _, edx := cpuid(0x0A) + c.PMU = parseLeaf0AH(c, eax, ebx, edx) + } } func getVectorLength() (vl, pl uint64) { return 0, 0 } diff --git a/vendor/github.com/klauspost/cpuid/v2/featureid_string.go b/vendor/github.com/klauspost/cpuid/v2/featureid_string.go index 07704351fa..2888bae8fa 100644 --- a/vendor/github.com/klauspost/cpuid/v2/featureid_string.go +++ b/vendor/github.com/klauspost/cpuid/v2/featureid_string.go @@ -154,95 +154,103 @@ func _() { _ = x[SEV_SNP-144] _ = x[SGX-145] _ = x[SGXLC-146] - _ = x[SHA-147] - _ = x[SME-148] - _ = x[SME_COHERENT-149] - _ = x[SM3_X86-150] - _ = x[SM4_X86-151] - _ = x[SPEC_CTRL_SSBD-152] - _ = x[SRBDS_CTRL-153] - _ = x[SRSO_MSR_FIX-154] - _ = x[SRSO_NO-155] - _ = x[SRSO_USER_KERNEL_NO-156] - _ = x[SSE-157] - _ = x[SSE2-158] - _ = x[SSE3-159] - _ = x[SSE4-160] - _ = x[SSE42-161] - _ = x[SSE4A-162] - _ = x[SSSE3-163] - _ = x[STIBP-164] - _ = x[STIBP_ALWAYSON-165] - _ = x[STOSB_SHORT-166] - _ = x[SUCCOR-167] - _ = x[SVM-168] - _ = x[SVMDA-169] - _ = x[SVMFBASID-170] - _ = x[SVML-171] - _ = x[SVMNP-172] - _ = x[SVMPF-173] - _ = x[SVMPFT-174] - _ = x[SYSCALL-175] - _ = x[SYSEE-176] - _ = x[TBM-177] - _ = x[TDX_GUEST-178] - _ = x[TLB_FLUSH_NESTED-179] - _ = x[TME-180] - _ = x[TOPEXT-181] - _ = x[TSCRATEMSR-182] - _ = x[TSXLDTRK-183] - _ = x[VAES-184] - _ = x[VMCBCLEAN-185] - _ = x[VMPL-186] - _ = x[VMSA_REGPROT-187] - _ = x[VMX-188] - _ = x[VPCLMULQDQ-189] - _ = x[VTE-190] - _ = x[WAITPKG-191] - _ = x[WBNOINVD-192] - _ = x[WRMSRNS-193] - _ = x[X87-194] - _ = x[XGETBV1-195] - _ = x[XOP-196] - _ = x[XSAVE-197] - _ = x[XSAVEC-198] - _ = x[XSAVEOPT-199] - _ = x[XSAVES-200] - _ = x[AESARM-201] - _ = x[ARMCPUID-202] - _ = x[ASIMD-203] - _ = x[ASIMDDP-204] - _ = x[ASIMDHP-205] - _ = x[ASIMDRDM-206] - _ = x[ATOMICS-207] - _ = x[CRC32-208] - _ = x[DCPOP-209] - _ = x[EVTSTRM-210] - _ = x[FCMA-211] - _ = x[FHM-212] - _ = x[FP-213] - _ = x[FPHP-214] - _ = x[GPA-215] - _ = x[JSCVT-216] - _ = x[LRCPC-217] - _ = x[PMULL-218] - _ = x[RNDR-219] - _ = x[TLB-220] - _ = x[TS-221] - _ = x[SHA1-222] - _ = x[SHA2-223] - _ = x[SHA3-224] - _ = x[SHA512-225] - _ = x[SM3-226] - _ = x[SM4-227] - _ = x[SVE-228] - _ = x[lastID-229] + _ = x[SGXPQC-147] + _ = x[SHA-148] + _ = x[SME-149] + _ = x[SME_COHERENT-150] + _ = x[SM3_X86-151] + _ = x[SM4_X86-152] + _ = x[SPEC_CTRL_SSBD-153] + _ = x[SRBDS_CTRL-154] + _ = x[SRSO_MSR_FIX-155] + _ = x[SRSO_NO-156] + _ = x[SRSO_USER_KERNEL_NO-157] + _ = x[SSE-158] + _ = x[SSE2-159] + _ = x[SSE3-160] + _ = x[SSE4-161] + _ = x[SSE42-162] + _ = x[SSE4A-163] + _ = x[SSSE3-164] + _ = x[STIBP-165] + _ = x[STIBP_ALWAYSON-166] + _ = x[STOSB_SHORT-167] + _ = x[SUCCOR-168] + _ = x[SVM-169] + _ = x[SVMDA-170] + _ = x[SVMFBASID-171] + _ = x[SVML-172] + _ = x[SVMNP-173] + _ = x[SVMPF-174] + _ = x[SVMPFT-175] + _ = x[SYSCALL-176] + _ = x[SYSEE-177] + _ = x[TBM-178] + _ = x[TDX_GUEST-179] + _ = x[TLB_FLUSH_NESTED-180] + _ = x[TME-181] + _ = x[TOPEXT-182] + _ = x[TSA_L1_NO-183] + _ = x[TSA_SQ_NO-184] + _ = x[TSA_VERW_CLEAR-185] + _ = x[TSCRATEMSR-186] + _ = x[TSXLDTRK-187] + _ = x[VAES-188] + _ = x[VMCBCLEAN-189] + _ = x[VMPL-190] + _ = x[VMSA_REGPROT-191] + _ = x[VMX-192] + _ = x[VPCLMULQDQ-193] + _ = x[VTE-194] + _ = x[WAITPKG-195] + _ = x[WBNOINVD-196] + _ = x[WRMSRNS-197] + _ = x[X87-198] + _ = x[XGETBV1-199] + _ = x[XOP-200] + _ = x[XSAVE-201] + _ = x[XSAVEC-202] + _ = x[XSAVEOPT-203] + _ = x[XSAVES-204] + _ = x[AESARM-205] + _ = x[ARMCPUID-206] + _ = x[ASIMD-207] + _ = x[ASIMDDP-208] + _ = x[ASIMDHP-209] + _ = x[ASIMDRDM-210] + _ = x[ATOMICS-211] + _ = x[CRC32-212] + _ = x[DCPOP-213] + _ = x[EVTSTRM-214] + _ = x[FCMA-215] + _ = x[FHM-216] + _ = x[FP-217] + _ = x[FPHP-218] + _ = x[GPA-219] + _ = x[JSCVT-220] + _ = x[LRCPC-221] + _ = x[PMULL-222] + _ = x[RNDR-223] + _ = x[TLB-224] + _ = x[TS-225] + _ = x[SHA1-226] + _ = x[SHA2-227] + _ = x[SHA3-228] + _ = x[SHA512-229] + _ = x[SM3-230] + _ = x[SM4-231] + _ = x[SVE-232] + _ = x[PMU_FIXEDCOUNTER_CYCLES-233] + _ = x[PMU_FIXEDCOUNTER_REFCYCLES-234] + _ = x[PMU_FIXEDCOUNTER_INSTRUCTIONS-235] + _ = x[PMU_FIXEDCOUNTER_TOPDOWN_SLOTS-236] + _ = x[lastID-237] _ = x[firstID-0] } -const _FeatureID_name = "firstIDADXAESNIAMD3DNOWAMD3DNOWEXTAMXBF16AMXFP16AMXINT8AMXFP8AMXTILEAMXTF32AMXCOMPLEXAMXTRANSPOSEAPX_FAVXAVX10AVX10_128AVX10_256AVX10_512AVX2AVX512BF16AVX512BITALGAVX512BWAVX512CDAVX512DQAVX512ERAVX512FAVX512FP16AVX512IFMAAVX512PFAVX512VBMIAVX512VBMI2AVX512VLAVX512VNNIAVX512VP2INTERSECTAVX512VPOPCNTDQAVXIFMAAVXNECONVERTAVXSLOWAVXVNNIAVXVNNIINT8AVXVNNIINT16BHI_CTRLBMI1BMI2CETIBTCETSSCLDEMOTECLMULCLZEROCMOVCMPCCXADDCMPSB_SCADBS_SHORTCMPXCHG8CPBOOSTCPPCCX16EFER_LMSLE_UNSENQCMDERMSF16CFLUSH_L1DFMA3FMA4FP128FP256FSRMFXSRFXSROPTGFNIHLEHRESETHTTHWAHYBRID_CPUHYPERVISORIA32_ARCH_CAPIA32_CORE_CAPIBPBIBPB_BRTYPEIBRSIBRS_PREFERREDIBRS_PROVIDES_SMPIBSIBSBRNTRGTIBSFETCHSAMIBSFFVIBSOPCNTIBSOPCNTEXTIBSOPSAMIBSRDWROPCNTIBSRIPINVALIDCHKIBS_FETCH_CTLXIBS_OPDATA4IBS_OPFUSEIBS_PREVENTHOSTIBS_ZEN4IDPRED_CTRLINT_WBINVDINVLPGBKEYLOCKERKEYLOCKERWLAHFLAMLBRVIRTLZCNTMCAOVERFLOWMCDT_NOMCOMMITMD_CLEARMMXMMXEXTMOVBEMOVDIR64BMOVDIRIMOVSB_ZLMOVUMPXMSRIRCMSRLISTMSR_PAGEFLUSHNRIPSNXOSXSAVEPCONFIGPOPCNTPPINPREFETCHIPSFDRDPRURDRANDRDSEEDRDTSCPRRSBA_CTRLRTMRTM_ALWAYS_ABORTSBPBSERIALIZESEVSEV_64BITSEV_ALTERNATIVESEV_DEBUGSWAPSEV_ESSEV_RESTRICTEDSEV_SNPSGXSGXLCSHASMESME_COHERENTSM3_X86SM4_X86SPEC_CTRL_SSBDSRBDS_CTRLSRSO_MSR_FIXSRSO_NOSRSO_USER_KERNEL_NOSSESSE2SSE3SSE4SSE42SSE4ASSSE3STIBPSTIBP_ALWAYSONSTOSB_SHORTSUCCORSVMSVMDASVMFBASIDSVMLSVMNPSVMPFSVMPFTSYSCALLSYSEETBMTDX_GUESTTLB_FLUSH_NESTEDTMETOPEXTTSCRATEMSRTSXLDTRKVAESVMCBCLEANVMPLVMSA_REGPROTVMXVPCLMULQDQVTEWAITPKGWBNOINVDWRMSRNSX87XGETBV1XOPXSAVEXSAVECXSAVEOPTXSAVESAESARMARMCPUIDASIMDASIMDDPASIMDHPASIMDRDMATOMICSCRC32DCPOPEVTSTRMFCMAFHMFPFPHPGPAJSCVTLRCPCPMULLRNDRTLBTSSHA1SHA2SHA3SHA512SM3SM4SVElastID" +const _FeatureID_name = "firstIDADXAESNIAMD3DNOWAMD3DNOWEXTAMXBF16AMXFP16AMXINT8AMXFP8AMXTILEAMXTF32AMXCOMPLEXAMXTRANSPOSEAPX_FAVXAVX10AVX10_128AVX10_256AVX10_512AVX2AVX512BF16AVX512BITALGAVX512BWAVX512CDAVX512DQAVX512ERAVX512FAVX512FP16AVX512IFMAAVX512PFAVX512VBMIAVX512VBMI2AVX512VLAVX512VNNIAVX512VP2INTERSECTAVX512VPOPCNTDQAVXIFMAAVXNECONVERTAVXSLOWAVXVNNIAVXVNNIINT8AVXVNNIINT16BHI_CTRLBMI1BMI2CETIBTCETSSCLDEMOTECLMULCLZEROCMOVCMPCCXADDCMPSB_SCADBS_SHORTCMPXCHG8CPBOOSTCPPCCX16EFER_LMSLE_UNSENQCMDERMSF16CFLUSH_L1DFMA3FMA4FP128FP256FSRMFXSRFXSROPTGFNIHLEHRESETHTTHWAHYBRID_CPUHYPERVISORIA32_ARCH_CAPIA32_CORE_CAPIBPBIBPB_BRTYPEIBRSIBRS_PREFERREDIBRS_PROVIDES_SMPIBSIBSBRNTRGTIBSFETCHSAMIBSFFVIBSOPCNTIBSOPCNTEXTIBSOPSAMIBSRDWROPCNTIBSRIPINVALIDCHKIBS_FETCH_CTLXIBS_OPDATA4IBS_OPFUSEIBS_PREVENTHOSTIBS_ZEN4IDPRED_CTRLINT_WBINVDINVLPGBKEYLOCKERKEYLOCKERWLAHFLAMLBRVIRTLZCNTMCAOVERFLOWMCDT_NOMCOMMITMD_CLEARMMXMMXEXTMOVBEMOVDIR64BMOVDIRIMOVSB_ZLMOVUMPXMSRIRCMSRLISTMSR_PAGEFLUSHNRIPSNXOSXSAVEPCONFIGPOPCNTPPINPREFETCHIPSFDRDPRURDRANDRDSEEDRDTSCPRRSBA_CTRLRTMRTM_ALWAYS_ABORTSBPBSERIALIZESEVSEV_64BITSEV_ALTERNATIVESEV_DEBUGSWAPSEV_ESSEV_RESTRICTEDSEV_SNPSGXSGXLCSGXPQCSHASMESME_COHERENTSM3_X86SM4_X86SPEC_CTRL_SSBDSRBDS_CTRLSRSO_MSR_FIXSRSO_NOSRSO_USER_KERNEL_NOSSESSE2SSE3SSE4SSE42SSE4ASSSE3STIBPSTIBP_ALWAYSONSTOSB_SHORTSUCCORSVMSVMDASVMFBASIDSVMLSVMNPSVMPFSVMPFTSYSCALLSYSEETBMTDX_GUESTTLB_FLUSH_NESTEDTMETOPEXTTSA_L1_NOTSA_SQ_NOTSA_VERW_CLEARTSCRATEMSRTSXLDTRKVAESVMCBCLEANVMPLVMSA_REGPROTVMXVPCLMULQDQVTEWAITPKGWBNOINVDWRMSRNSX87XGETBV1XOPXSAVEXSAVECXSAVEOPTXSAVESAESARMARMCPUIDASIMDASIMDDPASIMDHPASIMDRDMATOMICSCRC32DCPOPEVTSTRMFCMAFHMFPFPHPGPAJSCVTLRCPCPMULLRNDRTLBTSSHA1SHA2SHA3SHA512SM3SM4SVEPMU_FIXEDCOUNTER_CYCLESPMU_FIXEDCOUNTER_REFCYCLESPMU_FIXEDCOUNTER_INSTRUCTIONSPMU_FIXEDCOUNTER_TOPDOWN_SLOTSlastID" -var _FeatureID_index = [...]uint16{0, 7, 10, 15, 23, 34, 41, 48, 55, 61, 68, 75, 85, 97, 102, 105, 110, 119, 128, 137, 141, 151, 163, 171, 179, 187, 195, 202, 212, 222, 230, 240, 251, 259, 269, 287, 302, 309, 321, 328, 335, 346, 358, 366, 370, 374, 380, 385, 393, 398, 404, 408, 417, 435, 443, 450, 454, 458, 472, 478, 482, 486, 495, 499, 503, 508, 513, 517, 521, 528, 532, 535, 541, 544, 547, 557, 567, 580, 593, 597, 608, 612, 626, 643, 646, 656, 667, 673, 681, 692, 700, 712, 728, 742, 753, 763, 778, 786, 797, 807, 814, 823, 833, 837, 840, 847, 852, 863, 870, 877, 885, 888, 894, 899, 908, 915, 923, 927, 930, 936, 943, 956, 961, 963, 970, 977, 983, 987, 996, 1000, 1005, 1011, 1017, 1023, 1033, 1036, 1052, 1056, 1065, 1068, 1077, 1092, 1105, 1111, 1125, 1132, 1135, 1140, 1143, 1146, 1158, 1165, 1172, 1186, 1196, 1208, 1215, 1234, 1237, 1241, 1245, 1249, 1254, 1259, 1264, 1269, 1283, 1294, 1300, 1303, 1308, 1317, 1321, 1326, 1331, 1337, 1344, 1349, 1352, 1361, 1377, 1380, 1386, 1396, 1404, 1408, 1417, 1421, 1433, 1436, 1446, 1449, 1456, 1464, 1471, 1474, 1481, 1484, 1489, 1495, 1503, 1509, 1515, 1523, 1528, 1535, 1542, 1550, 1557, 1562, 1567, 1574, 1578, 1581, 1583, 1587, 1590, 1595, 1600, 1605, 1609, 1612, 1614, 1618, 1622, 1626, 1632, 1635, 1638, 1641, 1647} +var _FeatureID_index = [...]uint16{0, 7, 10, 15, 23, 34, 41, 48, 55, 61, 68, 75, 85, 97, 102, 105, 110, 119, 128, 137, 141, 151, 163, 171, 179, 187, 195, 202, 212, 222, 230, 240, 251, 259, 269, 287, 302, 309, 321, 328, 335, 346, 358, 366, 370, 374, 380, 385, 393, 398, 404, 408, 417, 435, 443, 450, 454, 458, 472, 478, 482, 486, 495, 499, 503, 508, 513, 517, 521, 528, 532, 535, 541, 544, 547, 557, 567, 580, 593, 597, 608, 612, 626, 643, 646, 656, 667, 673, 681, 692, 700, 712, 728, 742, 753, 763, 778, 786, 797, 807, 814, 823, 833, 837, 840, 847, 852, 863, 870, 877, 885, 888, 894, 899, 908, 915, 923, 927, 930, 936, 943, 956, 961, 963, 970, 977, 983, 987, 996, 1000, 1005, 1011, 1017, 1023, 1033, 1036, 1052, 1056, 1065, 1068, 1077, 1092, 1105, 1111, 1125, 1132, 1135, 1140, 1146, 1149, 1152, 1164, 1171, 1178, 1192, 1202, 1214, 1221, 1240, 1243, 1247, 1251, 1255, 1260, 1265, 1270, 1275, 1289, 1300, 1306, 1309, 1314, 1323, 1327, 1332, 1337, 1343, 1350, 1355, 1358, 1367, 1383, 1386, 1392, 1401, 1410, 1424, 1434, 1442, 1446, 1455, 1459, 1471, 1474, 1484, 1487, 1494, 1502, 1509, 1512, 1519, 1522, 1527, 1533, 1541, 1547, 1553, 1561, 1566, 1573, 1580, 1588, 1595, 1600, 1605, 1612, 1616, 1619, 1621, 1625, 1628, 1633, 1638, 1643, 1647, 1650, 1652, 1656, 1660, 1664, 1670, 1673, 1676, 1679, 1702, 1728, 1757, 1787, 1793} func (i FeatureID) String() string { if i < 0 || i >= FeatureID(len(_FeatureID_index)-1) { diff --git a/vendor/github.com/oapi-codegen/runtime/bindparam.go b/vendor/github.com/oapi-codegen/runtime/bindparam.go index 63e42a382f..df069c9d33 100644 --- a/vendor/github.com/oapi-codegen/runtime/bindparam.go +++ b/vendor/github.com/oapi-codegen/runtime/bindparam.go @@ -83,12 +83,12 @@ func BindStyledParameterWithOptions(style string, paramName string, value string // since prior to this refactoring, they always query unescaped. value, err = url.QueryUnescape(value) if err != nil { - return fmt.Errorf("error unescaping query parameter '%s': %v", paramName, err) + return fmt.Errorf("error unescaping query parameter '%s': %w", paramName, err) } case ParamLocationPath: value, err = url.PathUnescape(value) if err != nil { - return fmt.Errorf("error unescaping path parameter '%s': %v", paramName, err) + return fmt.Errorf("error unescaping path parameter '%s': %w", paramName, err) } default: // Headers and cookies aren't escaped. @@ -97,7 +97,7 @@ func BindStyledParameterWithOptions(style string, paramName string, value string // If the destination implements encoding.TextUnmarshaler we use it for binding if tu, ok := dest.(encoding.TextUnmarshaler); ok { if err := tu.UnmarshalText([]byte(value)); err != nil { - return fmt.Errorf("error unmarshaling '%s' text as %T: %s", value, dest, err) + return fmt.Errorf("error unmarshaling '%s' text as %T: %w", value, dest, err) } return nil @@ -124,7 +124,7 @@ func BindStyledParameterWithOptions(style string, paramName string, value string // Chop up the parameter into parts based on its style parts, err := splitStyledParameter(style, opts.Explode, false, paramName, value) if err != nil { - return fmt.Errorf("error splitting input '%s' into parts: %s", value, err) + return fmt.Errorf("error splitting input '%s' into parts: %w", value, err) } return bindSplitPartsToDestinationArray(parts, dest) @@ -287,7 +287,7 @@ func bindSplitPartsToDestinationStruct(paramName string, parts []string, explode jsonParam := "{" + strings.Join(fields, ",") + "}" err := json.Unmarshal([]byte(jsonParam), dest) if err != nil { - return fmt.Errorf("error binding parameter %s fields: %s", paramName, err) + return fmt.Errorf("error binding parameter %s fields: %w", paramName, err) } return nil } @@ -318,7 +318,11 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str // inner code will bind the string's value to this interface. var output interface{} - if required { + // required params are never pointers, but it may happen that optional param + // is not pointer as well if user decides to annotate it with + // x-go-type-skip-optional-pointer + var extraIndirect = !required && v.Kind() == reflect.Pointer + if !extraIndirect { // If the parameter is required, then the generated code will pass us // a pointer to it: &int, &object, and so forth. We can directly set // them. @@ -414,9 +418,10 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str if err != nil { return err } - // If the parameter is required, and we've successfully unmarshaled - // it, this assigns the new object to the pointer pointer. - if !required { + // If the parameter is required (or relies on x-go-type-skip-optional-pointer), + // and we've successfully unmarshaled it, this assigns the new object to the + // pointer pointer. + if extraIndirect { dv.Set(reflect.ValueOf(output)) } return nil @@ -456,7 +461,7 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str if err != nil { return err } - if !required { + if extraIndirect { dv.Set(reflect.ValueOf(output)) } return nil diff --git a/vendor/github.com/oapi-codegen/runtime/bindstring.go b/vendor/github.com/oapi-codegen/runtime/bindstring.go index c88fdc20c2..764036d2a7 100644 --- a/vendor/github.com/oapi-codegen/runtime/bindstring.go +++ b/vendor/github.com/oapi-codegen/runtime/bindstring.go @@ -100,7 +100,7 @@ func BindStringToObject(src string, dst interface{}) error { case reflect.Array: if tu, ok := dst.(encoding.TextUnmarshaler); ok { if err := tu.UnmarshalText([]byte(src)); err != nil { - return fmt.Errorf("error unmarshaling '%s' text as %T: %s", src, dst, err) + return fmt.Errorf("error unmarshaling '%s' text as %T: %w", src, dst, err) } return nil @@ -122,7 +122,7 @@ func BindStringToObject(src string, dst interface{}) error { if err != nil { parsedTime, err = time.Parse(types.DateFormat, src) if err != nil { - return fmt.Errorf("error parsing '%s' as RFC3339 or 2006-01-02 time: %s", src, err) + return fmt.Errorf("error parsing '%s' as RFC3339 or 2006-01-02 time: %w", src, err) } } // So, assigning this gets a little fun. We have a value to the @@ -145,7 +145,7 @@ func BindStringToObject(src string, dst interface{}) error { } parsedTime, err := time.Parse(types.DateFormat, src) if err != nil { - return fmt.Errorf("error parsing '%s' as date: %s", src, err) + return fmt.Errorf("error parsing '%s' as date: %w", src, err) } parsedDate := types.Date{Time: parsedTime} diff --git a/vendor/github.com/oapi-codegen/runtime/deepobject.go b/vendor/github.com/oapi-codegen/runtime/deepobject.go index 7ec2f027b6..588465c03c 100644 --- a/vendor/github.com/oapi-codegen/runtime/deepobject.go +++ b/vendor/github.com/oapi-codegen/runtime/deepobject.go @@ -258,7 +258,7 @@ func assignPathValues(dst interface{}, pathValues fieldOrValue) error { // TODO: why is this marked as an ineffassign? tm, err = time.Parse(types.DateFormat, pathValues.value) //nolint:ineffassign,staticcheck if err != nil { - return fmt.Errorf("error parsing '%s' as RFC3339 or 2006-01-02 time: %s", pathValues.value, err) + return fmt.Errorf("error parsing '%s' as RFC3339 or 2006-01-02 time: %w", pathValues.value, err) } return fmt.Errorf("invalid date format: %w", err) } diff --git a/vendor/github.com/oapi-codegen/runtime/styleparam.go b/vendor/github.com/oapi-codegen/runtime/styleparam.go index 57ed41b533..8491c85035 100644 --- a/vendor/github.com/oapi-codegen/runtime/styleparam.go +++ b/vendor/github.com/oapi-codegen/runtime/styleparam.go @@ -26,8 +26,9 @@ import ( "strings" "time" - "github.com/oapi-codegen/runtime/types" "github.com/google/uuid" + + "github.com/oapi-codegen/runtime/types" ) // Parameter escaping works differently based on where a header is found @@ -79,7 +80,7 @@ func StyleParamWithLocation(style string, explode bool, paramName string, paramL if !convertableToTime && !convertableToDate { b, err := tu.MarshalText() if err != nil { - return "", fmt.Errorf("error marshaling '%s' as text: %s", value, err) + return "", fmt.Errorf("error marshaling '%s' as text: %w", value, err) } return stylePrimitive(style, explode, paramName, paramLocation, string(b)) @@ -165,7 +166,7 @@ func styleSlice(style string, explode bool, paramName string, paramLocation Para part = escapeParameterString(part, paramLocation) parts[i] = part if err != nil { - return "", fmt.Errorf("error formatting '%s': %s", paramName, err) + return "", fmt.Errorf("error formatting '%s': %w", paramName, err) } } return prefix + strings.Join(parts, separator), nil @@ -273,7 +274,7 @@ func styleStruct(style string, explode bool, paramName string, paramLocation Par } str, err := primitiveToString(f.Interface()) if err != nil { - return "", fmt.Errorf("error formatting '%s': %s", paramName, err) + return "", fmt.Errorf("error formatting '%s': %w", paramName, err) } fieldDict[fieldName] = str } @@ -288,19 +289,15 @@ func styleMap(style string, explode bool, paramName string, paramLocation ParamL } return MarshalDeepObject(value, paramName) } - - dict, ok := value.(map[string]interface{}) - if !ok { - return "", errors.New("map not of type map[string]interface{}") - } + v := reflect.ValueOf(value) fieldDict := make(map[string]string) - for fieldName, value := range dict { - str, err := primitiveToString(value) + for _, fieldName := range v.MapKeys() { + str, err := primitiveToString(v.MapIndex(fieldName).Interface()) if err != nil { - return "", fmt.Errorf("error formatting '%s': %s", paramName, err) + return "", fmt.Errorf("error formatting '%s': %w", paramName, err) } - fieldDict[fieldName] = str + fieldDict[fieldName.String()] = str } return processFieldDict(style, explode, paramName, paramLocation, fieldDict) } diff --git a/vendor/github.com/pion/rtp/packet.go b/vendor/github.com/pion/rtp/packet.go index b0166730db..9297668be8 100644 --- a/vendor/github.com/pion/rtp/packet.go +++ b/vendor/github.com/pion/rtp/packet.go @@ -337,12 +337,16 @@ func (h Header) MarshalTo(buf []byte) (n int, err error) { //nolint:cyclop n += copy(buf[n:], extension.payload) } default: // RFC3550 Extension - extlen := len(h.Extensions[0].payload) - if extlen%4 != 0 { - // the payload must be in 32-bit words. - return 0, io.ErrShortBuffer + // Zero length extension is valid per the RFC3550 spec + // https://www.rfc-editor.org/rfc/rfc3550#section-5.3.1 + if len(h.Extensions) > 0 { + extlen := len(h.Extensions[0].payload) + if extlen%4 != 0 { + // the payload must be in 32-bit words. + return 0, io.ErrShortBuffer + } + n += copy(buf[n:], h.Extensions[0].payload) } - n += copy(buf[n:], h.Extensions[0].payload) } // calculate extensions size and round to 4 bytes boundaries @@ -382,7 +386,9 @@ func (h Header) MarshalSize() int { extSize += 2 + len(extension.payload) } default: - extSize += len(h.Extensions[0].payload) + if len(h.Extensions) > 0 { + extSize += len(h.Extensions[0].payload) + } } // extensions size must have 4 bytes boundaries diff --git a/vendor/github.com/pion/sdp/v3/.golangci.yml b/vendor/github.com/pion/sdp/v3/.golangci.yml index 120faf29be..59edee274f 100644 --- a/vendor/github.com/pion/sdp/v3/.golangci.yml +++ b/vendor/github.com/pion/sdp/v3/.golangci.yml @@ -41,6 +41,12 @@ linters-settings: - w io.Writer - r io.Reader - b []byte + revive: + rules: + # Prefer 'any' type alias over 'interface{}' for Go 1.18+ compatibility + - name: use-any + severity: warning + disabled: false linters: enable: diff --git a/vendor/github.com/pion/sdp/v3/base_lexer.go b/vendor/github.com/pion/sdp/v3/base_lexer.go index 4cd9dc5bab..a0fa6f7f9f 100644 --- a/vendor/github.com/pion/sdp/v3/base_lexer.go +++ b/vendor/github.com/pion/sdp/v3/base_lexer.go @@ -168,6 +168,19 @@ func (l *baseLexer) readField() (string, error) { return l.value[start:stop], nil } +func (l *lexer) readRequiredField() (string, error) { + field, err := l.readField() + if err != nil { + return "", err + } + + if field == "" { + return "", errFieldMissing + } + + return field, nil +} + // Returns symbols until line end. func (l *baseLexer) readLine() (string, error) { start := l.pos diff --git a/vendor/github.com/pion/sdp/v3/unmarshal.go b/vendor/github.com/pion/sdp/v3/unmarshal.go index 8ef01e15cc..d694ccbd5d 100644 --- a/vendor/github.com/pion/sdp/v3/unmarshal.go +++ b/vendor/github.com/pion/sdp/v3/unmarshal.go @@ -21,7 +21,7 @@ var ( //nolint: gochecknoglobals unmarshalCachePool = sync.Pool{ - New: func() interface{} { + New: func() any { return &unmarshalCache{} }, } @@ -465,7 +465,8 @@ func unmarshalOrigin(lex *lexer) (stateFn, error) { return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, lex.desc.Origin.NetworkType) } - lex.desc.Origin.AddressType, err = lex.readField() + // Handle potentially missing AddressType field + err = handleAddressType(lex) if err != nil { return nil, err } @@ -476,7 +477,8 @@ func unmarshalOrigin(lex *lexer) (stateFn, error) { return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, lex.desc.Origin.AddressType) } - lex.desc.Origin.UnicastAddress, err = lex.readField() + // Handle potentially missing UnicastAddress field + err = handleUnicastAddress(lex) if err != nil { return nil, err } @@ -488,6 +490,49 @@ func unmarshalOrigin(lex *lexer) (stateFn, error) { return s3, nil } +// handleAddressType processes AddressType field with graceful handling for missing fields. +func handleAddressType(lex *lexer) error { + addressType, err := lex.readRequiredField() + if err != nil { + if errors.Is(err, errFieldMissing) { + // Field missing - use defaults for camera compatibility + lex.desc.Origin.AddressType = "IP4" + lex.desc.Origin.UnicastAddress = "0.0.0.0" + + return nil + } + + return err + } + + lex.desc.Origin.AddressType = addressType + + return nil +} + +// handleUnicastAddress processes UnicastAddress field with graceful handling for missing fields. +func handleUnicastAddress(lex *lexer) error { + unicastAddress, err := lex.readRequiredField() + if err != nil { + if errors.Is(err, errFieldMissing) { + // Use appropriate default based on address type + if lex.desc.Origin.AddressType == "IP6" { + lex.desc.Origin.UnicastAddress = "::" + } else { + lex.desc.Origin.UnicastAddress = "0.0.0.0" + } + + return nil + } + + return err + } + + lex.desc.Origin.UnicastAddress = unicastAddress + + return nil +} + func unmarshalSessionName(l *lexer) (stateFn, error) { value, err := l.readLine() if err != nil { diff --git a/vendor/github.com/pion/sdp/v3/util.go b/vendor/github.com/pion/sdp/v3/util.go index 7cf17a9611..a862253704 100644 --- a/vendor/github.com/pion/sdp/v3/util.go +++ b/vendor/github.com/pion/sdp/v3/util.go @@ -25,6 +25,7 @@ var ( errPayloadTypeNotFound = errors.New("payload type not found") errCodecNotFound = errors.New("codec not found") errSyntaxError = errors.New("SyntaxError") + errFieldMissing = errors.New("field missing") ) // ConnectionRole indicates which of the end points should initiate the connection establishment. diff --git a/vendor/gopkg.in/natefinch/npipe.v2/.gitignore b/vendor/gopkg.in/natefinch/npipe.v2/.gitignore deleted file mode 100644 index 00268614f0..0000000000 --- a/vendor/gopkg.in/natefinch/npipe.v2/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe diff --git a/vendor/gopkg.in/natefinch/npipe.v2/LICENSE.txt b/vendor/gopkg.in/natefinch/npipe.v2/LICENSE.txt deleted file mode 100644 index a4a11046cc..0000000000 --- a/vendor/gopkg.in/natefinch/npipe.v2/LICENSE.txt +++ /dev/null @@ -1,8 +0,0 @@ -The MIT License (MIT) -Copyright (c) 2013 npipe authors - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/vendor/gopkg.in/natefinch/npipe.v2/README.md b/vendor/gopkg.in/natefinch/npipe.v2/README.md deleted file mode 100644 index 420a4d16c7..0000000000 --- a/vendor/gopkg.in/natefinch/npipe.v2/README.md +++ /dev/null @@ -1,308 +0,0 @@ -npipe [![Build status](https://ci.appveyor.com/api/projects/status/00vuepirsot29qwi)](https://ci.appveyor.com/project/natefinch/npipe) [![GoDoc](https://godoc.org/gopkg.in/natefinch/npipe.v2?status.svg)](https://godoc.org/gopkg.in/natefinch/npipe.v2) -===== -Package npipe provides a pure Go wrapper around Windows named pipes. - -Windows named pipe documentation: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365780 - -Note that the code lives at https://github.com/natefinch/npipe (v2 branch) -but should be imported as gopkg.in/natefinch/npipe.v2 (the package name is -still npipe). - -npipe provides an interface based on stdlib's net package, with Dial, Listen, -and Accept functions, as well as associated implementations of net.Conn and -net.Listener. It supports rpc over the connection. - -### Notes -* Deadlines for reading/writing to the connection are only functional in Windows Vista/Server 2008 and above, due to limitations with the Windows API. - -* The pipes support byte mode only (no support for message mode) - -### Examples -The Dial function connects a client to a named pipe: - - - conn, err := npipe.Dial(`\\.\pipe\mypipename`) - if err != nil { - - } - fmt.Fprintf(conn, "Hi server!\n") - msg, err := bufio.NewReader(conn).ReadString('\n') - ... - -The Listen function creates servers: - - - ln, err := npipe.Listen(`\\.\pipe\mypipename`) - if err != nil { - // handle error - } - for { - conn, err := ln.Accept() - if err != nil { - // handle error - continue - } - go handleConnection(conn) - } - - - - - -## Variables -``` go -var ErrClosed = PipeError{"Pipe has been closed.", false} -``` -ErrClosed is the error returned by PipeListener.Accept when Close is called -on the PipeListener. - - - -## type PipeAddr -``` go -type PipeAddr string -``` -PipeAddr represents the address of a named pipe. - - - - - - - - - - - -### func (PipeAddr) Network -``` go -func (a PipeAddr) Network() string -``` -Network returns the address's network name, "pipe". - - - -### func (PipeAddr) String -``` go -func (a PipeAddr) String() string -``` -String returns the address of the pipe - - - -## type PipeConn -``` go -type PipeConn struct { - // contains filtered or unexported fields -} -``` -PipeConn is the implementation of the net.Conn interface for named pipe connections. - - - - - - - - - -### func Dial -``` go -func Dial(address string) (*PipeConn, error) -``` -Dial connects to a named pipe with the given address. If the specified pipe is not available, -it will wait indefinitely for the pipe to become available. - -The address must be of the form \\.\\pipe\ for local pipes and \\\pipe\ -for remote pipes. - -Dial will return a PipeError if you pass in a badly formatted pipe name. - -Examples: - - - // local pipe - conn, err := Dial(`\\.\pipe\mypipename`) - - // remote pipe - conn, err := Dial(`\\othercomp\pipe\mypipename`) - - -### func DialTimeout -``` go -func DialTimeout(address string, timeout time.Duration) (*PipeConn, error) -``` -DialTimeout acts like Dial, but will time out after the duration of timeout - - - - -### func (\*PipeConn) Close -``` go -func (c *PipeConn) Close() error -``` -Close closes the connection. - - - -### func (\*PipeConn) LocalAddr -``` go -func (c *PipeConn) LocalAddr() net.Addr -``` -LocalAddr returns the local network address. - - - -### func (\*PipeConn) Read -``` go -func (c *PipeConn) Read(b []byte) (int, error) -``` -Read implements the net.Conn Read method. - - - -### func (\*PipeConn) RemoteAddr -``` go -func (c *PipeConn) RemoteAddr() net.Addr -``` -RemoteAddr returns the remote network address. - - - -### func (\*PipeConn) SetDeadline -``` go -func (c *PipeConn) SetDeadline(t time.Time) error -``` -SetDeadline implements the net.Conn SetDeadline method. -Note that timeouts are only supported on Windows Vista/Server 2008 and above - - - -### func (\*PipeConn) SetReadDeadline -``` go -func (c *PipeConn) SetReadDeadline(t time.Time) error -``` -SetReadDeadline implements the net.Conn SetReadDeadline method. -Note that timeouts are only supported on Windows Vista/Server 2008 and above - - - -### func (\*PipeConn) SetWriteDeadline -``` go -func (c *PipeConn) SetWriteDeadline(t time.Time) error -``` -SetWriteDeadline implements the net.Conn SetWriteDeadline method. -Note that timeouts are only supported on Windows Vista/Server 2008 and above - - - -### func (\*PipeConn) Write -``` go -func (c *PipeConn) Write(b []byte) (int, error) -``` -Write implements the net.Conn Write method. - - - -## type PipeError -``` go -type PipeError struct { - // contains filtered or unexported fields -} -``` -PipeError is an error related to a call to a pipe - - - - - - - - - - - -### func (PipeError) Error -``` go -func (e PipeError) Error() string -``` -Error implements the error interface - - - -### func (PipeError) Temporary -``` go -func (e PipeError) Temporary() bool -``` -Temporary implements net.AddrError.Temporary() - - - -### func (PipeError) Timeout -``` go -func (e PipeError) Timeout() bool -``` -Timeout implements net.AddrError.Timeout() - - - -## type PipeListener -``` go -type PipeListener struct { - // contains filtered or unexported fields -} -``` -PipeListener is a named pipe listener. Clients should typically -use variables of type net.Listener instead of assuming named pipe. - - - - - - - - - -### func Listen -``` go -func Listen(address string) (*PipeListener, error) -``` -Listen returns a new PipeListener that will listen on a pipe with the given -address. The address must be of the form \\.\pipe\ - -Listen will return a PipeError for an incorrectly formatted pipe name. - - - - -### func (\*PipeListener) Accept -``` go -func (l *PipeListener) Accept() (net.Conn, error) -``` -Accept implements the Accept method in the net.Listener interface; it -waits for the next call and returns a generic net.Conn. - - - -### func (\*PipeListener) AcceptPipe -``` go -func (l *PipeListener) AcceptPipe() (*PipeConn, error) -``` -AcceptPipe accepts the next incoming call and returns the new connection. - - - -### func (\*PipeListener) Addr -``` go -func (l *PipeListener) Addr() net.Addr -``` -Addr returns the listener's network address, a PipeAddr. - - - -### func (\*PipeListener) Close -``` go -func (l *PipeListener) Close() error -``` -Close stops listening on the address. -Already Accepted connections are not closed. diff --git a/vendor/gopkg.in/natefinch/npipe.v2/doc.go b/vendor/gopkg.in/natefinch/npipe.v2/doc.go deleted file mode 100644 index 3445773b46..0000000000 --- a/vendor/gopkg.in/natefinch/npipe.v2/doc.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2013 Nate Finch. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -// Package npipe provides a pure Go wrapper around Windows named pipes. -// -// !! Note, this package is Windows-only. There is no code to compile on linux. -// -// Windows named pipe documentation: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365780 -// -// Note that the code lives at https://github.com/natefinch/npipe (v2 branch) -// but should be imported as gopkg.in/natefinch/npipe.v2 (the package name is -// still npipe). -// -// npipe provides an interface based on stdlib's net package, with Dial, Listen, -// and Accept functions, as well as associated implementations of net.Conn and -// net.Listener. It supports rpc over the connection. -// -// Notes -// -// * Deadlines for reading/writing to the connection are only functional in Windows Vista/Server 2008 and above, due to limitations with the Windows API. -// -// * The pipes support byte mode only (no support for message mode) -// -// Examples -// -// The Dial function connects a client to a named pipe: -// conn, err := npipe.Dial(`\\.\pipe\mypipename`) -// if err != nil { -// -// } -// fmt.Fprintf(conn, "Hi server!\n") -// msg, err := bufio.NewReader(conn).ReadString('\n') -// ... -// -// The Listen function creates servers: -// -// ln, err := npipe.Listen(`\\.\pipe\mypipename`) -// if err != nil { -// // handle error -// } -// for { -// conn, err := ln.Accept() -// if err != nil { -// // handle error -// continue -// } -// go handleConnection(conn) -// } -package npipe diff --git a/vendor/gopkg.in/natefinch/npipe.v2/npipe_windows.go b/vendor/gopkg.in/natefinch/npipe.v2/npipe_windows.go deleted file mode 100644 index 5e7cf13ee3..0000000000 --- a/vendor/gopkg.in/natefinch/npipe.v2/npipe_windows.go +++ /dev/null @@ -1,531 +0,0 @@ -package npipe - -//sys createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW -//sys connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) = ConnectNamedPipe -//sys disconnectNamedPipe(handle syscall.Handle) (err error) = DisconnectNamedPipe -//sys waitNamedPipe(name *uint16, timeout uint32) (err error) = WaitNamedPipeW -//sys createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateEventW -//sys getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) = GetOverlappedResult -//sys cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) = CancelIoEx - -import ( - "fmt" - "io" - "net" - "sync" - "syscall" - "time" -) - -const ( - // openMode - pipe_access_duplex = 0x3 - pipe_access_inbound = 0x1 - pipe_access_outbound = 0x2 - - // openMode write flags - file_flag_first_pipe_instance = 0x00080000 - file_flag_write_through = 0x80000000 - file_flag_overlapped = 0x40000000 - - // openMode ACL flags - write_dac = 0x00040000 - write_owner = 0x00080000 - access_system_security = 0x01000000 - - // pipeMode - pipe_type_byte = 0x0 - pipe_type_message = 0x4 - - // pipeMode read mode flags - pipe_readmode_byte = 0x0 - pipe_readmode_message = 0x2 - - // pipeMode wait mode flags - pipe_wait = 0x0 - pipe_nowait = 0x1 - - // pipeMode remote-client mode flags - pipe_accept_remote_clients = 0x0 - pipe_reject_remote_clients = 0x8 - - pipe_unlimited_instances = 255 - - nmpwait_wait_forever = 0xFFFFFFFF - - // the two not-an-errors below occur if a client connects to the pipe between - // the server's CreateNamedPipe and ConnectNamedPipe calls. - error_no_data syscall.Errno = 0xE8 - error_pipe_connected syscall.Errno = 0x217 - error_pipe_busy syscall.Errno = 0xE7 - error_sem_timeout syscall.Errno = 0x79 - - error_bad_pathname syscall.Errno = 0xA1 - error_invalid_name syscall.Errno = 0x7B - - error_io_incomplete syscall.Errno = 0x3e4 -) - -var _ net.Conn = (*PipeConn)(nil) -var _ net.Listener = (*PipeListener)(nil) - -// ErrClosed is the error returned by PipeListener.Accept when Close is called -// on the PipeListener. -var ErrClosed = PipeError{"Pipe has been closed.", false} - -// PipeError is an error related to a call to a pipe -type PipeError struct { - msg string - timeout bool -} - -// Error implements the error interface -func (e PipeError) Error() string { - return e.msg -} - -// Timeout implements net.AddrError.Timeout() -func (e PipeError) Timeout() bool { - return e.timeout -} - -// Temporary implements net.AddrError.Temporary() -func (e PipeError) Temporary() bool { - return false -} - -// Dial connects to a named pipe with the given address. If the specified pipe is not available, -// it will wait indefinitely for the pipe to become available. -// -// The address must be of the form \\.\\pipe\ for local pipes and \\\pipe\ -// for remote pipes. -// -// Dial will return a PipeError if you pass in a badly formatted pipe name. -// -// Examples: -// // local pipe -// conn, err := Dial(`\\.\pipe\mypipename`) -// -// // remote pipe -// conn, err := Dial(`\\othercomp\pipe\mypipename`) -func Dial(address string) (*PipeConn, error) { - for { - conn, err := dial(address, nmpwait_wait_forever) - if err == nil { - return conn, nil - } - if isPipeNotReady(err) { - <-time.After(100 * time.Millisecond) - continue - } - return nil, err - } -} - -// DialTimeout acts like Dial, but will time out after the duration of timeout -func DialTimeout(address string, timeout time.Duration) (*PipeConn, error) { - deadline := time.Now().Add(timeout) - - now := time.Now() - for now.Before(deadline) { - millis := uint32(deadline.Sub(now) / time.Millisecond) - conn, err := dial(address, millis) - if err == nil { - return conn, nil - } - if err == error_sem_timeout { - // This is WaitNamedPipe's timeout error, so we know we're done - return nil, PipeError{fmt.Sprintf( - "Timed out waiting for pipe '%s' to come available", address), true} - } - if isPipeNotReady(err) { - left := deadline.Sub(time.Now()) - retry := 100 * time.Millisecond - if left > retry { - <-time.After(retry) - } else { - <-time.After(left - time.Millisecond) - } - now = time.Now() - continue - } - return nil, err - } - return nil, PipeError{fmt.Sprintf( - "Timed out waiting for pipe '%s' to come available", address), true} -} - -// isPipeNotReady checks the error to see if it indicates the pipe is not ready -func isPipeNotReady(err error) bool { - // Pipe Busy means another client just grabbed the open pipe end, - // and the server hasn't made a new one yet. - // File Not Found means the server hasn't created the pipe yet. - // Neither is a fatal error. - - return err == syscall.ERROR_FILE_NOT_FOUND || err == error_pipe_busy -} - -// newOverlapped creates a structure used to track asynchronous -// I/O requests that have been issued. -func newOverlapped() (*syscall.Overlapped, error) { - event, err := createEvent(nil, true, true, nil) - if err != nil { - return nil, err - } - return &syscall.Overlapped{HEvent: event}, nil -} - -// waitForCompletion waits for an asynchronous I/O request referred to by overlapped to complete. -// This function returns the number of bytes transferred by the operation and an error code if -// applicable (nil otherwise). -func waitForCompletion(handle syscall.Handle, overlapped *syscall.Overlapped) (uint32, error) { - _, err := syscall.WaitForSingleObject(overlapped.HEvent, syscall.INFINITE) - if err != nil { - return 0, err - } - var transferred uint32 - err = getOverlappedResult(handle, overlapped, &transferred, true) - return transferred, err -} - -// dial is a helper to initiate a connection to a named pipe that has been started by a server. -// The timeout is only enforced if the pipe server has already created the pipe, otherwise -// this function will return immediately. -func dial(address string, timeout uint32) (*PipeConn, error) { - name, err := syscall.UTF16PtrFromString(string(address)) - if err != nil { - return nil, err - } - // If at least one instance of the pipe has been created, this function - // will wait timeout milliseconds for it to become available. - // It will return immediately regardless of timeout, if no instances - // of the named pipe have been created yet. - // If this returns with no error, there is a pipe available. - if err := waitNamedPipe(name, timeout); err != nil { - if err == error_bad_pathname { - // badly formatted pipe name - return nil, badAddr(address) - } - return nil, err - } - pathp, err := syscall.UTF16PtrFromString(address) - if err != nil { - return nil, err - } - handle, err := syscall.CreateFile(pathp, syscall.GENERIC_READ|syscall.GENERIC_WRITE, - uint32(syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE), nil, syscall.OPEN_EXISTING, - syscall.FILE_FLAG_OVERLAPPED, 0) - if err != nil { - return nil, err - } - return &PipeConn{handle: handle, addr: PipeAddr(address)}, nil -} - -// Listen returns a new PipeListener that will listen on a pipe with the given -// address. The address must be of the form \\.\pipe\ -// -// Listen will return a PipeError for an incorrectly formatted pipe name. -func Listen(address string) (*PipeListener, error) { - handle, err := createPipe(address, true) - if err == error_invalid_name { - return nil, badAddr(address) - } - if err != nil { - return nil, err - } - - return &PipeListener{ - addr: PipeAddr(address), - handle: handle, - }, nil -} - -// PipeListener is a named pipe listener. Clients should typically -// use variables of type net.Listener instead of assuming named pipe. -type PipeListener struct { - mu sync.Mutex - - addr PipeAddr - handle syscall.Handle - closed bool - - // acceptHandle contains the current handle waiting for - // an incoming connection or nil. - acceptHandle syscall.Handle - // acceptOverlapped is set before waiting on a connection. - // If not waiting, it is nil. - acceptOverlapped *syscall.Overlapped -} - -// Accept implements the Accept method in the net.Listener interface; it -// waits for the next call and returns a generic net.Conn. -func (l *PipeListener) Accept() (net.Conn, error) { - c, err := l.AcceptPipe() - for err == error_no_data { - // Ignore clients that connect and immediately disconnect. - c, err = l.AcceptPipe() - } - if err != nil { - return nil, err - } - return c, nil -} - -// AcceptPipe accepts the next incoming call and returns the new connection. -// It might return an error if a client connected and immediately cancelled -// the connection. -func (l *PipeListener) AcceptPipe() (*PipeConn, error) { - if l == nil { - return nil, syscall.EINVAL - } - - l.mu.Lock() - defer l.mu.Unlock() - - if l.addr == "" || l.closed { - return nil, syscall.EINVAL - } - - // the first time we call accept, the handle will have been created by the Listen - // call. This is to prevent race conditions where the client thinks the server - // isn't listening because it hasn't actually called create yet. After the first time, we'll - // have to create a new handle each time - handle := l.handle - if handle == 0 { - var err error - handle, err = createPipe(string(l.addr), false) - if err != nil { - return nil, err - } - } else { - l.handle = 0 - } - - overlapped, err := newOverlapped() - if err != nil { - return nil, err - } - defer syscall.CloseHandle(overlapped.HEvent) - err = connectNamedPipe(handle, overlapped) - if err == nil || err == error_pipe_connected { - return &PipeConn{handle: handle, addr: l.addr}, nil - } - - if err == error_io_incomplete || err == syscall.ERROR_IO_PENDING { - l.acceptOverlapped = overlapped - l.acceptHandle = handle - // unlock here so close can function correctly while we wait (we'll - // get relocked via the defer below, before the original defer - // unlock happens.) - l.mu.Unlock() - defer func() { - l.mu.Lock() - l.acceptOverlapped = nil - l.acceptHandle = 0 - // unlock is via defer above. - }() - _, err = waitForCompletion(handle, overlapped) - } - if err == syscall.ERROR_OPERATION_ABORTED { - // Return error compatible to net.Listener.Accept() in case the - // listener was closed. - return nil, ErrClosed - } - if err != nil { - return nil, err - } - return &PipeConn{handle: handle, addr: l.addr}, nil -} - -// Close stops listening on the address. -// Already Accepted connections are not closed. -func (l *PipeListener) Close() error { - l.mu.Lock() - defer l.mu.Unlock() - - if l.closed { - return nil - } - l.closed = true - if l.handle != 0 { - err := disconnectNamedPipe(l.handle) - if err != nil { - return err - } - err = syscall.CloseHandle(l.handle) - if err != nil { - return err - } - l.handle = 0 - } - if l.acceptOverlapped != nil && l.acceptHandle != 0 { - // Cancel the pending IO. This call does not block, so it is safe - // to hold onto the mutex above. - if err := cancelIoEx(l.acceptHandle, l.acceptOverlapped); err != nil { - return err - } - err := syscall.CloseHandle(l.acceptOverlapped.HEvent) - if err != nil { - return err - } - l.acceptOverlapped.HEvent = 0 - err = syscall.CloseHandle(l.acceptHandle) - if err != nil { - return err - } - l.acceptHandle = 0 - } - return nil -} - -// Addr returns the listener's network address, a PipeAddr. -func (l *PipeListener) Addr() net.Addr { return l.addr } - -// PipeConn is the implementation of the net.Conn interface for named pipe connections. -type PipeConn struct { - handle syscall.Handle - addr PipeAddr - - // these aren't actually used yet - readDeadline *time.Time - writeDeadline *time.Time -} - -type iodata struct { - n uint32 - err error -} - -// completeRequest looks at iodata to see if a request is pending. If so, it waits for it to either complete or to -// abort due to hitting the specified deadline. Deadline may be set to nil to wait forever. If no request is pending, -// the content of iodata is returned. -func (c *PipeConn) completeRequest(data iodata, deadline *time.Time, overlapped *syscall.Overlapped) (int, error) { - if data.err == error_io_incomplete || data.err == syscall.ERROR_IO_PENDING { - var timer <-chan time.Time - if deadline != nil { - if timeDiff := deadline.Sub(time.Now()); timeDiff > 0 { - timer = time.After(timeDiff) - } - } - done := make(chan iodata) - go func() { - n, err := waitForCompletion(c.handle, overlapped) - done <- iodata{n, err} - }() - select { - case data = <-done: - case <-timer: - syscall.CancelIoEx(c.handle, overlapped) - data = iodata{0, timeout(c.addr.String())} - } - } - // Windows will produce ERROR_BROKEN_PIPE upon closing - // a handle on the other end of a connection. Go RPC - // expects an io.EOF error in this case. - if data.err == syscall.ERROR_BROKEN_PIPE { - data.err = io.EOF - } - return int(data.n), data.err -} - -// Read implements the net.Conn Read method. -func (c *PipeConn) Read(b []byte) (int, error) { - // Use ReadFile() rather than Read() because the latter - // contains a workaround that eats ERROR_BROKEN_PIPE. - overlapped, err := newOverlapped() - if err != nil { - return 0, err - } - defer syscall.CloseHandle(overlapped.HEvent) - var n uint32 - err = syscall.ReadFile(c.handle, b, &n, overlapped) - return c.completeRequest(iodata{n, err}, c.readDeadline, overlapped) -} - -// Write implements the net.Conn Write method. -func (c *PipeConn) Write(b []byte) (int, error) { - overlapped, err := newOverlapped() - if err != nil { - return 0, err - } - defer syscall.CloseHandle(overlapped.HEvent) - var n uint32 - err = syscall.WriteFile(c.handle, b, &n, overlapped) - return c.completeRequest(iodata{n, err}, c.writeDeadline, overlapped) -} - -// Close closes the connection. -func (c *PipeConn) Close() error { - return syscall.CloseHandle(c.handle) -} - -// LocalAddr returns the local network address. -func (c *PipeConn) LocalAddr() net.Addr { - return c.addr -} - -// RemoteAddr returns the remote network address. -func (c *PipeConn) RemoteAddr() net.Addr { - // not sure what to do here, we don't have remote addr.... - return c.addr -} - -// SetDeadline implements the net.Conn SetDeadline method. -// Note that timeouts are only supported on Windows Vista/Server 2008 and above -func (c *PipeConn) SetDeadline(t time.Time) error { - c.SetReadDeadline(t) - c.SetWriteDeadline(t) - return nil -} - -// SetReadDeadline implements the net.Conn SetReadDeadline method. -// Note that timeouts are only supported on Windows Vista/Server 2008 and above -func (c *PipeConn) SetReadDeadline(t time.Time) error { - c.readDeadline = &t - return nil -} - -// SetWriteDeadline implements the net.Conn SetWriteDeadline method. -// Note that timeouts are only supported on Windows Vista/Server 2008 and above -func (c *PipeConn) SetWriteDeadline(t time.Time) error { - c.writeDeadline = &t - return nil -} - -// PipeAddr represents the address of a named pipe. -type PipeAddr string - -// Network returns the address's network name, "pipe". -func (a PipeAddr) Network() string { return "pipe" } - -// String returns the address of the pipe -func (a PipeAddr) String() string { - return string(a) -} - -// createPipe is a helper function to make sure we always create pipes -// with the same arguments, since subsequent calls to create pipe need -// to use the same arguments as the first one. If first is set, fail -// if the pipe already exists. -func createPipe(address string, first bool) (syscall.Handle, error) { - n, err := syscall.UTF16PtrFromString(address) - if err != nil { - return 0, err - } - mode := uint32(pipe_access_duplex | syscall.FILE_FLAG_OVERLAPPED) - if first { - mode |= file_flag_first_pipe_instance - } - return createNamedPipe(n, - mode, - pipe_type_byte, - pipe_unlimited_instances, - 512, 512, 0, nil) -} - -func badAddr(addr string) PipeError { - return PipeError{fmt.Sprintf("Invalid pipe address '%s'.", addr), false} -} -func timeout(addr string) PipeError { - return PipeError{fmt.Sprintf("Pipe IO timed out waiting for '%s'", addr), true} -} diff --git a/vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_386.go b/vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_386.go deleted file mode 100644 index c283e6cf95..0000000000 --- a/vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_386.go +++ /dev/null @@ -1,124 +0,0 @@ -// +build windows -// go build mksyscall_windows.go && ./mksyscall_windows npipe_windows.go -// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT - -package npipe - -import "unsafe" -import "syscall" - -var ( - modkernel32 = syscall.NewLazyDLL("kernel32.dll") - - procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") - procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") - procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe") - procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") - procCancelIoEx = modkernel32.NewProc("CancelIoEx") -) - -func createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { - r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(openMode), uintptr(pipeMode), uintptr(maxInstances), uintptr(outBufSize), uintptr(inBufSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) - handle = syscall.Handle(r0) - if handle == syscall.InvalidHandle { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { - r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { - r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func disconnectNamedPipe(handle syscall.Handle) (err error) { - r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(handle), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func waitNamedPipe(name *uint16, timeout uint32) (err error) { - r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { - var _p0 uint32 - if manualReset { - _p0 = 1 - } else { - _p0 = 0 - } - var _p1 uint32 - if initialState { - _p1 = 1 - } else { - _p1 = 0 - } - r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(sa)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) - handle = syscall.Handle(r0) - if handle == syscall.InvalidHandle { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) { - var _p0 uint32 - if wait { - _p0 = 1 - } else { - _p0 = 0 - } - r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transferred)), uintptr(_p0), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} diff --git a/vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_amd64.go b/vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_amd64.go deleted file mode 100644 index c283e6cf95..0000000000 --- a/vendor/gopkg.in/natefinch/npipe.v2/znpipe_windows_amd64.go +++ /dev/null @@ -1,124 +0,0 @@ -// +build windows -// go build mksyscall_windows.go && ./mksyscall_windows npipe_windows.go -// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT - -package npipe - -import "unsafe" -import "syscall" - -var ( - modkernel32 = syscall.NewLazyDLL("kernel32.dll") - - procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW") - procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe") - procDisconnectNamedPipe = modkernel32.NewProc("DisconnectNamedPipe") - procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") - procCancelIoEx = modkernel32.NewProc("CancelIoEx") -) - -func createNamedPipe(name *uint16, openMode uint32, pipeMode uint32, maxInstances uint32, outBufSize uint32, inBufSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) { - r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(openMode), uintptr(pipeMode), uintptr(maxInstances), uintptr(outBufSize), uintptr(inBufSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0) - handle = syscall.Handle(r0) - if handle == syscall.InvalidHandle { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func cancelIoEx(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { - r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func connectNamedPipe(handle syscall.Handle, overlapped *syscall.Overlapped) (err error) { - r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func disconnectNamedPipe(handle syscall.Handle) (err error) { - r1, _, e1 := syscall.Syscall(procDisconnectNamedPipe.Addr(), 1, uintptr(handle), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func waitNamedPipe(name *uint16, timeout uint32) (err error) { - r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { - var _p0 uint32 - if manualReset { - _p0 = 1 - } else { - _p0 = 0 - } - var _p1 uint32 - if initialState { - _p1 = 1 - } else { - _p1 = 0 - } - r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(sa)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) - handle = syscall.Handle(r0) - if handle == syscall.InvalidHandle { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func getOverlappedResult(handle syscall.Handle, overlapped *syscall.Overlapped, transferred *uint32, wait bool) (err error) { - var _p0 uint32 - if wait { - _p0 = 1 - } else { - _p0 = 0 - } - r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transferred)), uintptr(_p0), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} diff --git a/vendor/modules.txt b/vendor/modules.txt index f80a17ecb8..f887eaad35 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -54,14 +54,14 @@ github.com/CortexFoundation/inference/synapse # github.com/CortexFoundation/merkletree v0.0.0-20240407093305-416581a62b5b ## explicit; go 1.22 github.com/CortexFoundation/merkletree -# github.com/CortexFoundation/robot v1.0.7-0.20250226143617-b3819ddaddc6 +# github.com/CortexFoundation/robot v1.0.7-0.20250720133840-49217aa96f7e ## explicit; go 1.22 github.com/CortexFoundation/robot github.com/CortexFoundation/robot/backend # github.com/CortexFoundation/statik v0.0.0-20210315012922-8bb8a7b5dc66 ## explicit; go 1.16 github.com/CortexFoundation/statik -# github.com/CortexFoundation/torrentfs v1.0.70-0.20250705184717-83e763fd9aef +# github.com/CortexFoundation/torrentfs v1.0.70-0.20250720163701-a81b9d33f725 ## explicit; go 1.24.4 github.com/CortexFoundation/torrentfs github.com/CortexFoundation/torrentfs/backend @@ -74,6 +74,13 @@ github.com/CortexFoundation/wormhole # github.com/DataDog/zstd v1.5.7 ## explicit; go 1.14 github.com/DataDog/zstd +# github.com/Microsoft/go-winio v0.6.2 +## explicit; go 1.21 +github.com/Microsoft/go-winio +github.com/Microsoft/go-winio/internal/fs +github.com/Microsoft/go-winio/internal/socket +github.com/Microsoft/go-winio/internal/stringbuffer +github.com/Microsoft/go-winio/pkg/guid # github.com/RoaringBitmap/roaring v1.9.4 ## explicit; go 1.14 github.com/RoaringBitmap/roaring @@ -209,7 +216,7 @@ github.com/arsham/figurine/figurine # github.com/arsham/rainbow v1.2.1 ## explicit; go 1.18 github.com/arsham/rainbow/rainbow -# github.com/aws/aws-sdk-go-v2 v1.36.5 +# github.com/aws/aws-sdk-go-v2 v1.36.6 ## explicit; go 1.22 github.com/aws/aws-sdk-go-v2/aws github.com/aws/aws-sdk-go-v2/aws/defaults @@ -235,10 +242,10 @@ github.com/aws/aws-sdk-go-v2/internal/shareddefaults github.com/aws/aws-sdk-go-v2/internal/strings github.com/aws/aws-sdk-go-v2/internal/sync/singleflight github.com/aws/aws-sdk-go-v2/internal/timeconv -# github.com/aws/aws-sdk-go-v2/config v1.29.17 +# github.com/aws/aws-sdk-go-v2/config v1.29.18 ## explicit; go 1.22 github.com/aws/aws-sdk-go-v2/config -# github.com/aws/aws-sdk-go-v2/credentials v1.17.70 +# github.com/aws/aws-sdk-go-v2/credentials v1.17.71 ## explicit; go 1.22 github.com/aws/aws-sdk-go-v2/credentials github.com/aws/aws-sdk-go-v2/credentials/ec2rolecreds @@ -247,14 +254,14 @@ github.com/aws/aws-sdk-go-v2/credentials/endpointcreds/internal/client github.com/aws/aws-sdk-go-v2/credentials/processcreds github.com/aws/aws-sdk-go-v2/credentials/ssocreds github.com/aws/aws-sdk-go-v2/credentials/stscreds -# github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 +# github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.33 ## explicit; go 1.22 github.com/aws/aws-sdk-go-v2/feature/ec2/imds github.com/aws/aws-sdk-go-v2/feature/ec2/imds/internal/config -# github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36 +# github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.37 ## explicit; go 1.22 github.com/aws/aws-sdk-go-v2/internal/configsources -# github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36 +# github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.37 ## explicit; go 1.22 github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 # github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 @@ -263,26 +270,26 @@ github.com/aws/aws-sdk-go-v2/internal/ini # github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 ## explicit; go 1.22 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding -# github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 +# github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.18 ## explicit; go 1.22 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url -# github.com/aws/aws-sdk-go-v2/service/route53 v1.53.0 +# github.com/aws/aws-sdk-go-v2/service/route53 v1.53.1 ## explicit; go 1.22 github.com/aws/aws-sdk-go-v2/service/route53 github.com/aws/aws-sdk-go-v2/service/route53/internal/customizations github.com/aws/aws-sdk-go-v2/service/route53/internal/endpoints github.com/aws/aws-sdk-go-v2/service/route53/types -# github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 +# github.com/aws/aws-sdk-go-v2/service/sso v1.25.6 ## explicit; go 1.22 github.com/aws/aws-sdk-go-v2/service/sso github.com/aws/aws-sdk-go-v2/service/sso/internal/endpoints github.com/aws/aws-sdk-go-v2/service/sso/types -# github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 +# github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.4 ## explicit; go 1.22 github.com/aws/aws-sdk-go-v2/service/ssooidc github.com/aws/aws-sdk-go-v2/service/ssooidc/internal/endpoints github.com/aws/aws-sdk-go-v2/service/ssooidc/types -# github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 +# github.com/aws/aws-sdk-go-v2/service/sts v1.34.1 ## explicit; go 1.22 github.com/aws/aws-sdk-go-v2/service/sts github.com/aws/aws-sdk-go-v2/service/sts/internal/endpoints @@ -499,7 +506,7 @@ github.com/deckarep/golang-set/v2 ## explicit; go 1.17 github.com/decred/dcrd/dcrec/secp256k1/v4 github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa -# github.com/dgraph-io/badger/v4 v4.7.1-0.20250601174320-4f557a74bbf7 +# github.com/dgraph-io/badger/v4 v4.8.0 ## explicit; go 1.23.0 github.com/dgraph-io/badger/v4 github.com/dgraph-io/badger/v4/fb @@ -728,7 +735,7 @@ github.com/klauspost/compress/s2 github.com/klauspost/compress/snappy github.com/klauspost/compress/zstd github.com/klauspost/compress/zstd/internal/xxhash -# github.com/klauspost/cpuid/v2 v2.2.11 +# github.com/klauspost/cpuid/v2 v2.3.0 ## explicit; go 1.22 github.com/klauspost/cpuid/v2 # github.com/kr/pretty v0.3.1 @@ -808,7 +815,7 @@ github.com/ncruces/go-strftime # github.com/nutsdb/nutsdb v1.0.4 ## explicit; go 1.18 github.com/nutsdb/nutsdb -# github.com/oapi-codegen/runtime v1.1.1 +# github.com/oapi-codegen/runtime v1.1.2 ## explicit; go 1.20 github.com/oapi-codegen/runtime github.com/oapi-codegen/runtime/types @@ -915,7 +922,7 @@ github.com/pion/randutil # github.com/pion/rtcp v1.2.15 ## explicit; go 1.20 github.com/pion/rtcp -# github.com/pion/rtp v1.8.20 +# github.com/pion/rtp v1.8.21 ## explicit; go 1.20 github.com/pion/rtp github.com/pion/rtp/codecs @@ -924,7 +931,7 @@ github.com/pion/rtp/codecs/vp9 # github.com/pion/sctp v1.8.39 ## explicit; go 1.20 github.com/pion/sctp -# github.com/pion/sdp/v3 v3.0.14 +# github.com/pion/sdp/v3 v3.0.15 ## explicit; go 1.20 github.com/pion/sdp/v3 # github.com/pion/srtp/v3 v3.0.6 @@ -1078,7 +1085,7 @@ github.com/ucwong/filecache # github.com/ucwong/go-ttlmap v1.0.2-0.20221020173635-331e7ddde2bb ## explicit; go 1.19 github.com/ucwong/go-ttlmap -# github.com/ucwong/golang-kv v1.0.24-0.20250606215932-9b6cb1d9814d +# github.com/ucwong/golang-kv v1.0.24-0.20250720145037-4c149701f3b8 ## explicit; go 1.23.0 github.com/ucwong/golang-kv github.com/ucwong/golang-kv/badger @@ -1174,7 +1181,7 @@ golang.org/x/crypto/ripemd160 golang.org/x/crypto/scrypt golang.org/x/crypto/sha3 golang.org/x/crypto/ssh/terminal -# golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc +# golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 ## explicit; go 1.23.0 golang.org/x/exp/constraints golang.org/x/exp/rand @@ -1317,9 +1324,6 @@ google.golang.org/protobuf/types/pluginpb # gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c ## explicit; go 1.11 gopkg.in/check.v1 -# gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce -## explicit -gopkg.in/natefinch/npipe.v2 # gopkg.in/urfave/cli.v1 v1.20.0 ## explicit gopkg.in/urfave/cli.v1 diff --git a/whisper/whisperv6/api.go b/whisper/whisperv6/api.go index 3f8f0b5ca8..8614d0acb9 100644 --- a/whisper/whisperv6/api.go +++ b/whisper/whisperv6/api.go @@ -420,9 +420,6 @@ func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc. case <-rpcSub.Err(): api.w.Unsubscribe(id) return - case <-notifier.Closed(): - api.w.Unsubscribe(id) - return } } }()