diff --git a/eth-node/bridge/geth/wakuv2.go b/eth-node/bridge/geth/wakuv2.go index f17128d72..e4227266f 100644 --- a/eth-node/bridge/geth/wakuv2.go +++ b/eth-node/bridge/geth/wakuv2.go @@ -8,8 +8,8 @@ import ( "github.com/libp2p/go-libp2p/core/peer" - "github.com/waku-org/go-waku/waku/v2/protocol/store" - storepb "github.com/waku-org/go-waku/waku/v2/protocol/store/pb" + "github.com/waku-org/go-waku/waku/v2/protocol/legacy_store" + storepb "github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb" "github.com/ethereum/go-ethereum/common" "github.com/status-im/status-go/connection" @@ -177,19 +177,19 @@ func (w *gethWakuV2Wrapper) SendMessagesRequest(peerID []byte, r types.MessagesR } func (w *gethWakuV2Wrapper) RequestStoreMessages(ctx context.Context, peerID []byte, r types.MessagesRequest, processEnvelopes bool) (*types.StoreRequestCursor, int, error) { - var options []store.HistoryRequestOption + var options []legacy_store.HistoryRequestOption peer, err := peer.Decode(string(peerID)) if err != nil { return nil, 0, err } - options = []store.HistoryRequestOption{ - store.WithPaging(false, uint64(r.Limit)), + options = []legacy_store.HistoryRequestOption{ + legacy_store.WithPaging(false, uint64(r.Limit)), } if r.StoreCursor != nil { - options = append(options, store.WithCursor(&storepb.Index{ + options = append(options, legacy_store.WithCursor(&storepb.Index{ Digest: r.StoreCursor.Digest, ReceiverTime: r.StoreCursor.ReceiverTime, SenderTime: r.StoreCursor.SenderTime, diff --git a/go.mod b/go.mod index c3ea253e4..5260769a9 100644 --- a/go.mod +++ b/go.mod @@ -33,11 +33,11 @@ require ( github.com/keighl/metabolize v0.0.0-20150915210303-97ab655d4034 github.com/kilic/bls12-381 v0.0.0-20200607163746-32e1441c8a9f github.com/lib/pq v1.10.4 - github.com/libp2p/go-libp2p v0.29.2 - github.com/libp2p/go-libp2p-pubsub v0.9.3 + github.com/libp2p/go-libp2p v0.32.2 + github.com/libp2p/go-libp2p-pubsub v0.10.1 github.com/lucasb-eyer/go-colorful v1.0.3 github.com/mat/besticon v0.0.0-20210314201728-1579f269edb7 - github.com/multiformats/go-multiaddr v0.10.1 + github.com/multiformats/go-multiaddr v0.12.3 github.com/multiformats/go-multibase v0.2.0 github.com/multiformats/go-multihash v0.2.3 github.com/multiformats/go-varint v0.0.7 @@ -52,19 +52,19 @@ require ( github.com/status-im/markdown v0.0.0-20240404192634-b7e33c6ac3d4 github.com/status-im/migrate/v4 v4.6.2-status.3 github.com/status-im/mvds v0.0.27-0.20240111144448-92d364e4be82 - github.com/status-im/rendezvous v1.3.7 + github.com/status-im/rendezvous v1.3.8-0.20240110194857-cc5be22bf83e github.com/status-im/status-go/extkeys v1.1.2 github.com/status-im/tcp-shaker v1.1.1-status github.com/status-im/zxcvbn-go v0.0.0-20220311183720-5e8676676857 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a github.com/tsenart/tb v0.0.0-20181025101425-0d2499c8b6e9 github.com/wealdtech/go-ens/v3 v3.5.0 github.com/wealdtech/go-multicodec v1.4.0 github.com/xeipuuv/gojsonschema v1.2.0 github.com/zenthangplus/goccm v0.0.0-20211005163543-2f2e522aca15 - go.uber.org/zap v1.24.0 - golang.org/x/crypto v0.12.0 + go.uber.org/zap v1.27.0 + golang.org/x/crypto v0.18.0 golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb google.golang.org/protobuf v1.31.0 gopkg.in/go-playground/assert.v1 v1.2.1 // indirect @@ -90,20 +90,20 @@ require ( github.com/mutecomm/go-sqlcipher/v4 v4.4.2 github.com/schollz/peerdiscovery v1.7.0 github.com/siphiuel/lc-proxy-wrapper v0.0.0-20230516150924-246507cee8c7 - github.com/urfave/cli/v2 v2.24.4 - github.com/waku-org/go-waku v0.8.1-0.20240415131212-6d889ca3e2fe + github.com/urfave/cli/v2 v2.27.2 + github.com/waku-org/go-waku v0.8.1-0.20240507175626-19d27befd98b github.com/wk8/go-ordered-map/v2 v2.1.7 github.com/yeqown/go-qrcode/v2 v2.2.1 github.com/yeqown/go-qrcode/writer/standard v1.2.1 go.uber.org/multierr v1.11.0 - golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 - golang.org/x/net v0.14.0 - golang.org/x/text v0.12.0 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d + golang.org/x/net v0.17.0 + golang.org/x/text v0.14.0 golang.org/x/time v0.0.0-20220922220347-f3bd1da661af ) require ( - github.com/BurntSushi/toml v1.2.1 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/PuerkitoBio/goquery v1.6.1 // indirect github.com/RoaringBitmap/roaring v0.9.4 // indirect github.com/VictoriaMetrics/fastcache v1.6.0 // indirect @@ -137,7 +137,7 @@ require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/cruxic/go-hmac-drbg v0.0.0-20170206035330-84c46983886d // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect @@ -163,21 +163,21 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect + github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect github.com/gorilla/securecookie v1.1.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-bexpr v0.1.10 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect - github.com/hashicorp/golang-lru/v2 v2.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/holiman/uint256 v1.2.0 // indirect github.com/huandu/xstrings v1.3.2 // indirect - github.com/huin/goupnp v1.2.0 // indirect + github.com/huin/goupnp v1.3.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.2 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/koron/go-ssdp v0.0.4 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect @@ -187,19 +187,20 @@ require ( github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.3.0 // indirect + github.com/libp2p/go-libp2p-mplex v0.9.0 // indirect github.com/libp2p/go-mplex v0.7.0 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect github.com/libp2p/go-nat v0.2.0 // indirect github.com/libp2p/go-netroute v0.2.1 // indirect - github.com/libp2p/go-reuseport v0.3.0 // indirect + github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/libp2p/go-yamux/v4 v4.0.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.8 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/miekg/dns v1.1.55 // indirect + github.com/miekg/dns v1.1.56 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.1 // indirect @@ -212,38 +213,36 @@ require ( github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect github.com/multiformats/go-multicodec v0.9.0 // indirect - github.com/multiformats/go-multistream v0.4.1 // indirect + github.com/multiformats/go-multistream v0.5.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/onsi/ginkgo/v2 v2.11.0 // indirect - github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 // indirect + github.com/onsi/ginkgo/v2 v2.13.0 // indirect + github.com/opencontainers/runtime-spec v1.1.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/pion/datachannel v1.5.2 // indirect - github.com/pion/dtls/v2 v2.1.2 // indirect - github.com/pion/ice/v2 v2.1.20 // indirect - github.com/pion/interceptor v0.1.7 // indirect + github.com/pion/datachannel v1.5.5 // indirect + github.com/pion/dtls/v2 v2.2.7 // indirect + github.com/pion/ice/v2 v2.3.6 // indirect + github.com/pion/interceptor v0.1.17 // indirect github.com/pion/logging v0.2.2 // indirect - github.com/pion/mdns v0.0.5 // indirect + github.com/pion/mdns v0.0.7 // indirect github.com/pion/randutil v0.1.0 // indirect - github.com/pion/rtcp v1.2.9 // indirect - github.com/pion/rtp v1.7.4 // indirect - github.com/pion/sctp v1.8.2 // indirect - github.com/pion/sdp/v3 v3.0.4 // indirect - github.com/pion/srtp/v2 v2.0.5 // indirect - github.com/pion/stun v0.3.5 // indirect - github.com/pion/transport v0.13.0 // indirect - github.com/pion/turn/v2 v2.0.6 // indirect - github.com/pion/udp v0.1.1 // indirect - github.com/pion/webrtc/v3 v3.1.24-0.20220208053747-94262c1b2b38 // indirect + github.com/pion/rtcp v1.2.10 // indirect + github.com/pion/rtp v1.7.13 // indirect + github.com/pion/sctp v1.8.7 // indirect + github.com/pion/sdp/v3 v3.0.6 // indirect + github.com/pion/srtp/v2 v2.0.15 // indirect + github.com/pion/stun v0.6.0 // indirect + github.com/pion/transport/v2 v2.2.1 // indirect + github.com/pion/turn/v2 v2.1.0 // indirect + github.com/pion/webrtc/v3 v3.2.9 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/prometheus/tsdb v0.10.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-19 v0.3.3 // indirect - github.com/quic-go/qtls-go1-20 v0.2.3 // indirect - github.com/quic-go/quic-go v0.36.4 // indirect - github.com/quic-go/webtransport-go v0.5.3 // indirect + github.com/quic-go/qtls-go1-20 v0.3.4 // indirect + github.com/quic-go/quic-go v0.39.4 // indirect + github.com/quic-go/webtransport-go v0.6.0 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/rivo/uniseg v0.2.0 // indirect @@ -258,12 +257,12 @@ require ( github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/status-im/go-multiaddr-ethv4 v1.2.5 // indirect github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/tklauser/go-sysconf v0.3.6 // indirect github.com/tklauser/numcpus v0.2.2 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect - github.com/waku-org/go-discover v0.0.0-20240129014929-85f2c00b96a3 // indirect - github.com/waku-org/go-libp2p-rendezvous v0.0.0-20230628220917-7b4e5ae4c0e7 // indirect + github.com/waku-org/go-discover v0.0.0-20240506173252-4912704efdc5 // indirect + github.com/waku-org/go-libp2p-rendezvous v0.0.0-20240110193335-a67d1cc760a0 // indirect github.com/waku-org/go-zerokit-rln v0.1.14-0.20240102145250-fa738c0bdf59 // indirect github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230916172309-ee0ee61dde2b // indirect github.com/waku-org/go-zerokit-rln-arm v0.0.0-20230916171929-1dd9494ff065 // indirect @@ -271,19 +270,19 @@ require ( github.com/wk8/go-ordered-map v1.0.0 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect github.com/yeqown/reedsolomon v1.0.0 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/atomic v1.11.0 // indirect - go.uber.org/dig v1.17.0 // indirect - go.uber.org/fx v1.20.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/sync v0.3.0 // indirect + go.uber.org/dig v1.17.1 // indirect + go.uber.org/fx v1.20.1 // indirect + go.uber.org/mock v0.3.0 // indirect + golang.org/x/mod v0.13.0 // indirect + golang.org/x/sync v0.4.0 // indirect golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.11.0 // indirect - golang.org/x/tools v0.12.1-0.20230818130535-1517d1a3ba60 // indirect - golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect + golang.org/x/term v0.16.0 // indirect + golang.org/x/tools v0.14.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect diff --git a/go.sum b/go.sum index dde981db2..5c09ca662 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,8 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= @@ -632,8 +632,8 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -1023,8 +1023,8 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA= -github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -1112,8 +1112,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU= -github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= +github.com/hashicorp/golang-lru/v2 v2.0.5/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -1135,8 +1135,8 @@ github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88/go.mod h1:nNs7wvRfN1eKaMknBydLNQU6146XQim8t4h+q90biWo= github.com/huin/goupnp v1.0.2/go.mod h1:0dxJBVBHqTMjIUMkESDTNgOOx/Mw5wYIfyFmdzSamkM= -github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY= -github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -1290,8 +1290,8 @@ github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdY github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -1348,12 +1348,14 @@ github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38y github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.29.2 h1:uPw/c8hOxoLP/KhFnzlc5Ejqf+OmAL1dwIsqE31WBtY= -github.com/libp2p/go-libp2p v0.29.2/go.mod h1:OU7nSq0aEZMsV2wY8nXn1+XNNt9q2UiR8LjW3Kmp2UE= +github.com/libp2p/go-libp2p v0.32.2 h1:s8GYN4YJzgUoyeYNPdW7JZeZ5Ee31iNaIBfGYMAY4FQ= +github.com/libp2p/go-libp2p v0.32.2/go.mod h1:E0LKe+diV/ZVJVnOJby8VC5xzHF0660osg71skcxJvk= github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= -github.com/libp2p/go-libp2p-pubsub v0.9.3 h1:ihcz9oIBMaCK9kcx+yHWm3mLAFBMAUsM4ux42aikDxo= -github.com/libp2p/go-libp2p-pubsub v0.9.3/go.mod h1:RYA7aM9jIic5VV47WXu4GkcRxRhrdElWf8xtyli+Dzc= +github.com/libp2p/go-libp2p-mplex v0.9.0 h1:R58pDRAmuBXkYugbSSXR9wrTX3+1pFM1xP2bLuodIq8= +github.com/libp2p/go-libp2p-mplex v0.9.0/go.mod h1:ro1i4kuwiFT+uMPbIDIFkcLs1KRbNp0QwnUXM+P64Og= +github.com/libp2p/go-libp2p-pubsub v0.10.1 h1:/RqOZpEtAolsr8/9CC8KqROJSOZeu7lK7fPftn4MwNg= +github.com/libp2p/go-libp2p-pubsub v0.10.1/go.mod h1:1OxbaT/pFRO5h+Dpze8hdHQ63R0ke55XTs6b6NwLLkw= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY= @@ -1364,8 +1366,8 @@ github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk= github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk= github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= -github.com/libp2p/go-reuseport v0.3.0 h1:iiZslO5byUYZEg9iCwJGf5h+sf1Agmqx2V2FDjPyvUw= -github.com/libp2p/go-reuseport v0.3.0/go.mod h1:laea40AimhtfEqysZ71UpYj4S+R9VpH8PgqLo7L+SwI= +github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= +github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ= github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= @@ -1426,8 +1428,8 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -1458,8 +1460,8 @@ github.com/meirf/gopart v0.0.0-20180520194036-37e9492a85a8/go.mod h1:Uz8uoD6o+eQ github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= -github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= @@ -1526,8 +1528,8 @@ github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= github.com/multiformats/go-multiaddr v0.3.2/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= -github.com/multiformats/go-multiaddr v0.10.1 h1:HghtFrWyZEPrpTvgAMFJi6gFdgHfs2cb0pyfDsk+lqU= -github.com/multiformats/go-multiaddr v0.10.1/go.mod h1:jLEZsA61rwWNZQTHHnqq2HNa+4os/Hz54eqiRnsRqYQ= +github.com/multiformats/go-multiaddr v0.12.3 h1:hVBXvPRcKG0w80VinQ23P5t7czWgg65BmIvQKjDydU8= +github.com/multiformats/go-multiaddr v0.12.3/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII= github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= @@ -1543,8 +1545,8 @@ github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUj github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= -github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo= -github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= +github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE= +github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= @@ -1602,8 +1604,8 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= -github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= @@ -1618,7 +1620,7 @@ github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQ github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -1643,8 +1645,9 @@ github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/ github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= +github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= @@ -1684,34 +1687,39 @@ github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg= -github.com/pion/datachannel v1.5.2 h1:piB93s8LGmbECrpO84DnkIVWasRMk3IimbcXkTQLE6E= github.com/pion/datachannel v1.5.2/go.mod h1:FTGQWaHrdCwIJ1rw6xBIfZVkslikjShim5yr05XFuCQ= +github.com/pion/datachannel v1.5.5 h1:10ef4kwdjije+M9d7Xm9im2Y3O6A6ccQb0zcqZcJew8= +github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0= github.com/pion/dtls/v2 v2.0.1/go.mod h1:uMQkz2W0cSqY00xav7WByQ4Hb+18xeQh2oH2fRezr5U= github.com/pion/dtls/v2 v2.0.2/go.mod h1:27PEO3MDdaCfo21heT59/vsdmZc0zMt9wQPcSlLu/1I= github.com/pion/dtls/v2 v2.0.4/go.mod h1:qAkFscX0ZHoI1E07RfYPoRw3manThveu+mlTDdOxoGI= github.com/pion/dtls/v2 v2.0.7/go.mod h1:QuDII+8FVvk9Dp5t5vYIMTo7hh7uBkra+8QIm7QGm10= github.com/pion/dtls/v2 v2.0.9/go.mod h1:O0Wr7si/Zj5/EBFlDzDd6UtVxx25CE1r7XM7BQKYQho= github.com/pion/dtls/v2 v2.1.1/go.mod h1:qG3gA7ZPZemBqpEFqRKyURYdKEwFZQCGb7gv9T3ON3Y= -github.com/pion/dtls/v2 v2.1.2 h1:22Q1Jk9L++Yo7BIf9130MonNPfPVb+YgdYLeyQotuAA= github.com/pion/dtls/v2 v2.1.2/go.mod h1:o6+WvyLDAlXF7YiPB/RlskRoeK+/JtuaZa5emwQcWus= +github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= +github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= github.com/pion/ice v0.7.18/go.mod h1:+Bvnm3nYC6Nnp7VV6glUkuOfToB/AtMRZpOU8ihuf4c= github.com/pion/ice/v2 v2.0.15/go.mod h1:ZIiVGevpgAxF/cXiIVmuIUtCb3Xs4gCzCbXB6+nFkSI= github.com/pion/ice/v2 v2.1.7/go.mod h1:kV4EODVD5ux2z8XncbLHIOtcXKtYXVgLVCeVqnpoeP0= github.com/pion/ice/v2 v2.1.10/go.mod h1:kV4EODVD5ux2z8XncbLHIOtcXKtYXVgLVCeVqnpoeP0= github.com/pion/ice/v2 v2.1.12/go.mod h1:ovgYHUmwYLlRvcCLI67PnQ5YGe+upXZbGgllBDG/ktU= -github.com/pion/ice/v2 v2.1.20 h1:xpxXyX5b4WjCh/D905gzBeW/hbJxMEPx2ptVfrhVE6M= github.com/pion/ice/v2 v2.1.20/go.mod h1:hEAldRzBhTtAfvlU1V/2/nLCMvveQWFKPNCop+63/Iw= +github.com/pion/ice/v2 v2.3.6 h1:Jgqw36cAud47iD+N6rNX225uHvrgWtAlHfVyOQc3Heg= +github.com/pion/ice/v2 v2.3.6/go.mod h1:9/TzKDRwBVAPsC+YOrKH/e3xDrubeTRACU9/sHQarsU= github.com/pion/interceptor v0.0.9/go.mod h1:dHgEP5dtxOTf21MObuBAjJeAayPxLUAZjerGH8Xr07c= github.com/pion/interceptor v0.0.12/go.mod h1:qzeuWuD/ZXvPqOnxNcnhWfkCZ2e1kwwslicyyPnhoK4= github.com/pion/interceptor v0.0.13/go.mod h1:svsW2QoLHLoGLUr4pDoSopGBEWk8FZwlfxId/OKRKzo= github.com/pion/interceptor v0.0.15/go.mod h1:pg3J253eGi5bqyKzA74+ej5Y19ez2jkWANVnF+Z9Dfk= -github.com/pion/interceptor v0.1.7 h1:HThW0tIIKT9RRoDWGURe8rlZVOx0fJHxBHpA0ej0+bo= github.com/pion/interceptor v0.1.7/go.mod h1:Lh3JSl/cbJ2wP8I3ccrjh1K/deRGRn3UlSPuOTiHb6U= +github.com/pion/interceptor v0.1.17 h1:prJtgwFh/gB8zMqGZoOgJPHivOwVAp61i2aG61Du/1w= +github.com/pion/interceptor v0.1.17/go.mod h1:SY8kpmfVBvrbUzvj2bsXz7OJt5JvmVNZ+4Kjq7FcwrI= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns v0.0.4/go.mod h1:R1sL0p50l42S5lJs91oNdUL58nm0QHrhxnSegr++qC0= -github.com/pion/mdns v0.0.5 h1:Q2oj/JB3NqfzY9xGZ1fPzZzK7sDSD8rZPOvcIQ10BCw= github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g= +github.com/pion/mdns v0.0.7 h1:P0UB4Sr6xDWEox0kTVxF0LmQihtCbSAdW0H2nEgkA3U= +github.com/pion/mdns v0.0.7/go.mod h1:4iP2UbeFhLI/vWju/bw6ZfwjJzk0z8DNValjGxR/dD8= github.com/pion/quic v0.1.1/go.mod h1:zEU51v7ru8Mp4AUBJvj6psrSth5eEFNnVQK5K48oV3k= github.com/pion/quic v0.1.4/go.mod h1:dBhNvkLoQqRwfi6h3Vqj3IcPLgiW7rkZxBbRdp7Vzvk= github.com/pion/randutil v0.0.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= @@ -1720,33 +1728,41 @@ github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TB github.com/pion/rtcp v1.2.3/go.mod h1:zGhIv0RPRF0Z1Wiij22pUt5W/c9fevqSzT4jje/oK7I= github.com/pion/rtcp v1.2.4/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0= github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0= -github.com/pion/rtcp v1.2.9 h1:1ujStwg++IOLIEoOiIQ2s+qBuJ1VN81KW+9pMPsif+U= github.com/pion/rtcp v1.2.9/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo= +github.com/pion/rtcp v1.2.10 h1:nkr3uj+8Sp97zyItdN60tE/S6vk4al5CPRR6Gejsdjc= +github.com/pion/rtcp v1.2.10/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL1I= github.com/pion/rtp v1.6.0/go.mod h1:QgfogHsMBVE/RFNno467U/KBqfUywEH+HK+0rtnwsdI= github.com/pion/rtp v1.6.1/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= github.com/pion/rtp v1.6.5/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= github.com/pion/rtp v1.7.0/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= github.com/pion/rtp v1.7.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= -github.com/pion/rtp v1.7.4 h1:4dMbjb1SuynU5OpA3kz1zHK+u+eOCQjW3MAeVHf1ODA= github.com/pion/rtp v1.7.4/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/rtp v1.7.13 h1:qcHwlmtiI50t1XivvoawdCGTP4Uiypzfrsap+bijcoA= +github.com/pion/rtp v1.7.13/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0= github.com/pion/sctp v1.7.11/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0= github.com/pion/sctp v1.7.12/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= github.com/pion/sctp v1.8.0/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= -github.com/pion/sctp v1.8.2 h1:yBBCIrUMJ4yFICL3RIvR4eh/H2BTTvlligmSTy+3kiA= github.com/pion/sctp v1.8.2/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= +github.com/pion/sctp v1.8.5/go.mod h1:SUFFfDpViyKejTAdwD1d/HQsCu+V/40cCs2nZIvC3s0= +github.com/pion/sctp v1.8.7 h1:JnABvFakZueGAn4KU/4PSKg+GWbF6QWbKTWZOSGJjXw= +github.com/pion/sctp v1.8.7/go.mod h1:g1Ul+ARqZq5JEmoFy87Q/4CePtKnTJ1QCL9dBBdN6AU= github.com/pion/sdp/v2 v2.4.0/go.mod h1:L2LxrOpSTJbAns244vfPChbciR/ReU1KWfG04OpkR7E= -github.com/pion/sdp/v3 v3.0.4 h1:2Kf+dgrzJflNCSw3TV5v2VLeI0s/qkzy2r5jlR0wzf8= github.com/pion/sdp/v3 v3.0.4/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk= +github.com/pion/sdp/v3 v3.0.6 h1:WuDLhtuFUUVpTfus9ILC4HRyHsW6TdugjEX/QY9OiUw= +github.com/pion/sdp/v3 v3.0.6/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw= github.com/pion/srtp v1.5.1/go.mod h1:B+QgX5xPeQTNc1CJStJPHzOlHK66ViMDWTT0HZTCkcA= github.com/pion/srtp v1.5.2/go.mod h1:NiBff/MSxUwMUwx/fRNyD/xGE+dVvf8BOCeXhjCXZ9U= github.com/pion/srtp/v2 v2.0.1/go.mod h1:c8NWHhhkFf/drmHTAblkdu8++lsISEBBdAuiyxgqIsE= github.com/pion/srtp/v2 v2.0.2/go.mod h1:VEyLv4CuxrwGY8cxM+Ng3bmVy8ckz/1t6A0q/msKOw0= -github.com/pion/srtp/v2 v2.0.5 h1:ks3wcTvIUE/GHndO3FAvROQ9opy0uLELpwHJaQ1yqhQ= github.com/pion/srtp/v2 v2.0.5/go.mod h1:8k6AJlal740mrZ6WYxc4Dg6qDqqhxoRG2GSjlUhDF0A= -github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg= +github.com/pion/srtp/v2 v2.0.15 h1:+tqRtXGsGwHC0G0IUIAzRmdkHvriF79IHVfZGfHrQoA= +github.com/pion/srtp/v2 v2.0.15/go.mod h1:b/pQOlDrbB0HEH5EUAQXzSYxikFbNcNuKmF8tM0hCtw= github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA= +github.com/pion/stun v0.4.0/go.mod h1:QPsh1/SbXASntw3zkkrIk3ZJVKz4saBY2G7S10P3wCw= +github.com/pion/stun v0.6.0 h1:JHT/2iyGDPrFWE8NNC15wnddBN8KifsEDw8swQmrEmU= +github.com/pion/stun v0.6.0/go.mod h1:HPqcfoeqQn9cuaet7AOmB5e5xkObu9DwBdurwLKO9oA= github.com/pion/transport v0.6.0/go.mod h1:iWZ07doqOosSLMhZ+FXUTq+TamDoXSllxpbGcfkCmbE= github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8= github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE= @@ -1754,21 +1770,28 @@ github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+D github.com/pion/transport v0.12.1/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A= -github.com/pion/transport v0.13.0 h1:KWTA5ZrQogizzYwPEciGtHPLwpAjE91FgXnyu+Hv2uY= github.com/pion/transport v0.13.0/go.mod h1:yxm9uXpK9bpBBWkITk13cLo1y5/ur5VQpG22ny6EP7g= +github.com/pion/transport v0.14.1 h1:XSM6olwW+o8J4SCmOBb/BpwZypkHeyM0PGFCxNQBr40= +github.com/pion/transport v0.14.1/go.mod h1:4tGmbk00NeYA3rUa9+n+dzCCoKkcy3YlYb99Jn2fNnI= +github.com/pion/transport/v2 v2.0.0/go.mod h1:HS2MEBJTwD+1ZI2eSXSvHJx/HnzQqRy2/LXxt6eVMHc= +github.com/pion/transport/v2 v2.1.0/go.mod h1:AdSw4YBZVDkZm8fpoz+fclXyQwANWmZAlDuQdctTThQ= +github.com/pion/transport/v2 v2.2.0/go.mod h1:AdSw4YBZVDkZm8fpoz+fclXyQwANWmZAlDuQdctTThQ= +github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c= +github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= github.com/pion/turn/v2 v2.0.4/go.mod h1:1812p4DcGVbYVBTiraUmP50XoKye++AMkbfp+N27mog= github.com/pion/turn/v2 v2.0.5/go.mod h1:APg43CFyt/14Uy7heYUOGWdkem/Wu4PhCO/bjyrTqMw= -github.com/pion/turn/v2 v2.0.6 h1:AsXjSPR6Im15DMTB39NlfdTY9BQfieANPBjdg/aVNwY= github.com/pion/turn/v2 v2.0.6/go.mod h1:+y7xl719J8bAEVpSXBXvTxStjJv3hbz9YFflvkpcGPw= +github.com/pion/turn/v2 v2.1.0 h1:5wGHSgGhJhP/RpabkUb/T9PdsAjkGLS6toYz5HNzoSI= +github.com/pion/turn/v2 v2.1.0/go.mod h1:yrT5XbXSGX1VFSF31A3c1kCNB5bBZgk/uu5LET162qs= github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths= -github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o= github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= github.com/pion/webrtc/v2 v2.2.26/go.mod h1:XMZbZRNHyPDe1gzTIHFcQu02283YO45CbiwFgKvXnmc= github.com/pion/webrtc/v3 v3.0.11/go.mod h1:WEvXneGTeqNmiR59v5jTsxMc4yXQyOQcRsrdAbNwSEU= github.com/pion/webrtc/v3 v3.0.27/go.mod h1:QpLDmsU5a/a05n230gRtxZRvfHhFzn9ukGUL2x4G5ic= github.com/pion/webrtc/v3 v3.0.32/go.mod h1:wX3V5dQQUGCifhT1mYftC2kCrDQX6ZJ3B7Yad0R9JK0= -github.com/pion/webrtc/v3 v3.1.24-0.20220208053747-94262c1b2b38 h1:+IEql+S+YAj3S5e7Ftl/u4xPcZGG0WwLFsyFj6NRTz4= github.com/pion/webrtc/v3 v3.1.24-0.20220208053747-94262c1b2b38/go.mod h1:L5S/oAhL0Fzt/rnftVQRrP80/j5jygY7XRZzWwFx6P4= +github.com/pion/webrtc/v3 v3.2.9 h1:U8NSjQDlZZ+Iy/hg42Q/u6mhEVSXYvKrOIZiZwYTfLc= +github.com/pion/webrtc/v3 v3.2.9/go.mod h1:gjQLMZeyN3jXBGdxGmUYCyKjOuYX/c99BDjGqmadq0A= github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -1847,14 +1870,12 @@ github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38i github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-19 v0.3.3 h1:wznEHvJwd+2X3PqftRha0SUKmGsnb6dfArMhy9PeJVE= -github.com/quic-go/qtls-go1-19 v0.3.3/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= -github.com/quic-go/qtls-go1-20 v0.2.3 h1:m575dovXn1y2ATOb1XrRFcrv0F+EQmlowTkoraNkDPI= -github.com/quic-go/qtls-go1-20 v0.2.3/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= -github.com/quic-go/quic-go v0.36.4 h1:CXn/ZLN5Vntlk53fjR+kUMC8Jt7flfQe+I5Ty5A+k0o= -github.com/quic-go/quic-go v0.36.4/go.mod h1:qxQumdeKw5GmWs1OsTZZnOxzSI+RJWuhf1O8FN35L2o= -github.com/quic-go/webtransport-go v0.5.3 h1:5XMlzemqB4qmOlgIus5zB45AcZ2kCgCy2EptUrfOPWU= -github.com/quic-go/webtransport-go v0.5.3/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= +github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg= +github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/quic-go v0.39.4 h1:PelfiuG7wXEffUT2yceiqz5V6Pc0TA5ruOd1LcmFc1s= +github.com/quic-go/quic-go v0.39.4/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q= +github.com/quic-go/webtransport-go v0.6.0 h1:CvNsKqc4W2HljHJnoT+rMmbRJybShZ0YPFDD3NxaZLY= +github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1abRaosM2yGOyiikEc= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -2007,8 +2028,8 @@ github.com/status-im/mvds v0.0.27-0.20240111144448-92d364e4be82 h1:A7jtwOlDMUGUP github.com/status-im/mvds v0.0.27-0.20240111144448-92d364e4be82/go.mod h1:2fiAx0q9XYIPKYRq2B1oiO9zZESy/n4D32gWw6lMDsE= github.com/status-im/notify v1.0.2-status h1:x8wev0Sh8H8KAf4bVcv+L0dVHldBESOKUlqRqRY7uL8= github.com/status-im/notify v1.0.2-status/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc= -github.com/status-im/rendezvous v1.3.7 h1:rZGWsFCjPV3MWeUkLkZSOGTAvyRf+rxx5hnEGLE4OHg= -github.com/status-im/rendezvous v1.3.7/go.mod h1:r0vCbQJByTteMajN0f+Mcet/Vd7uAXxFPfewNpI2iXQ= +github.com/status-im/rendezvous v1.3.8-0.20240110194857-cc5be22bf83e h1:pCOHeAYmYttXQBCn+6u01bs5d/W3XslxmplFhru4X1Y= +github.com/status-im/rendezvous v1.3.8-0.20240110194857-cc5be22bf83e/go.mod h1:LEPENTHDBGCxXVZx6FEKNKN+tfPaIK+lmiGv1DxkJW4= github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088 h1:ClCAP2FPCvl8hGMhbUx/tq/sOu2wibztAa5jAvQEe4Q= github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088/go.mod h1:+92j1tN27DypDeBFxkg0uzkqfh1bNHTZe3Bv2PjvxpM= github.com/status-im/status-go/extkeys v1.1.2 h1:FSjARgDathJ3rIapJt851LsIXP9Oyuu2M2jPJKuzloU= @@ -2028,8 +2049,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -2043,8 +2065,12 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syncthing/syncthing v0.14.48-rc.4/go.mod h1:nw3siZwHPA6M8iSfjDCWQ402eqvEIasMQOE8nFOxy7M= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -2084,8 +2110,8 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/cli/v2 v2.24.4 h1:0gyJJEBYtCV87zI/x2nZCPyDxD51K6xM8SkwjHFCNEU= -github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI= +github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= @@ -2099,12 +2125,12 @@ github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmF github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/waku-org/go-discover v0.0.0-20240129014929-85f2c00b96a3 h1:Kk0KYXZE/uNnARF2TbCQyvyZ/w4SgF8VhquNdOVVsNU= -github.com/waku-org/go-discover v0.0.0-20240129014929-85f2c00b96a3/go.mod h1:eBHgM6T4EG0RZzxpxKy+rGz/6Dw2Nd8DWxS0lm9ESDw= -github.com/waku-org/go-libp2p-rendezvous v0.0.0-20230628220917-7b4e5ae4c0e7 h1:0e1h+p84yBp0IN7AqgbZlV7lgFBjm214lgSOE7CeJmE= -github.com/waku-org/go-libp2p-rendezvous v0.0.0-20230628220917-7b4e5ae4c0e7/go.mod h1:pFvOZ9YTFsW0o5zJW7a0B5tr1owAijRWJctXJ2toL04= -github.com/waku-org/go-waku v0.8.1-0.20240415131212-6d889ca3e2fe h1:rJF7qKODzvWx03iaLbYyjvA62crnCaDqIN661aqCQ8c= -github.com/waku-org/go-waku v0.8.1-0.20240415131212-6d889ca3e2fe/go.mod h1:RjTvkTrIwpoT1cM9HeQqwa2Q7t7WOkb3hpuB/zuZ6SM= +github.com/waku-org/go-discover v0.0.0-20240506173252-4912704efdc5 h1:4K3IS97JryAEV8pRXB//qPcg+8bPXl/O+AOLt3FeCKc= +github.com/waku-org/go-discover v0.0.0-20240506173252-4912704efdc5/go.mod h1:eBHgM6T4EG0RZzxpxKy+rGz/6Dw2Nd8DWxS0lm9ESDw= +github.com/waku-org/go-libp2p-rendezvous v0.0.0-20240110193335-a67d1cc760a0 h1:R4YYx2QamhBRl/moIxkDCNW+OP7AHbyWLBygDc/xIMo= +github.com/waku-org/go-libp2p-rendezvous v0.0.0-20240110193335-a67d1cc760a0/go.mod h1:EhZP9fee0DYjKH/IOQvoNSy1tSHp2iZadsHGphcAJgY= +github.com/waku-org/go-waku v0.8.1-0.20240507175626-19d27befd98b h1:2NR0UCjuuAFmnkhsvlCKn7PTs4JxUjSq4s7lSWaG0ek= +github.com/waku-org/go-waku v0.8.1-0.20240507175626-19d27befd98b/go.mod h1:yXnWChXRKTb+NhALbFysluxgSwuxeTF2rhanDJkIx+k= github.com/waku-org/go-zerokit-rln v0.1.14-0.20240102145250-fa738c0bdf59 h1:jisj+OCI6QydLtFq3Pyhu49wl9ytPN7oAHjMfepHDrA= github.com/waku-org/go-zerokit-rln v0.1.14-0.20240102145250-fa738c0bdf59/go.mod h1:1PdBdPzyTaKt3VnpAHk3zj+r9dXPFOr3IHZP9nFle6E= github.com/waku-org/go-zerokit-rln-apple v0.0.0-20230916172309-ee0ee61dde2b h1:KgZVhsLkxsj5gb/FfndSCQu6VYwALrCOgYI3poR95yE= @@ -2146,8 +2172,8 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw= +github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913/go.mod h1:4aEEwZQutDLsQv2Deui4iYQ6DWTxR14g6m8Wv88+Xqk= github.com/yeqown/go-qrcode/v2 v2.2.1 h1:Jc1Q916fwC05R8C7mpWDbrT9tyLPaLLKDABoC5XBCe8= github.com/yeqown/go-qrcode/v2 v2.2.1/go.mod h1:2Qsk2APUCPne0TsRo40DIkI5MYnbzYKCnKGEFWrxd24= github.com/yeqown/go-qrcode/writer/standard v1.2.1 h1:FMRZiur5yApUIe4fqtqmcdl/XQTZAZWt2DhkPx4VIW0= @@ -2226,14 +2252,16 @@ go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI= -go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU= -go.uber.org/fx v1.20.0 h1:ZMC/pnRvhsthOZh9MZjMq5U8Or3mA9zBSPaLnzs3ihQ= -go.uber.org/fx v1.20.0/go.mod h1:qCUj0btiR3/JnanEr1TYEePfSw6o/4qYJscgvzQ5Ub0= +go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= +go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= +go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= +go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -2246,8 +2274,8 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -2295,8 +2323,10 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2311,8 +2341,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= -golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= 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= @@ -2350,8 +2380,9 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2441,9 +2472,15 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -2479,8 +2516,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2636,9 +2674,14 @@ golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -2646,9 +2689,15 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2658,9 +2707,14 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2760,8 +2814,9 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.12.1-0.20230818130535-1517d1a3ba60 h1:o4bs4seAAlSiZQAZbO6/RP5XBCZCooQS3Pgc0AUjWts= -golang.org/x/tools v0.12.1-0.20230818130535-1517d1a3ba60/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2769,8 +2824,6 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= diff --git a/protocol/messenger_storenode_request_test.go b/protocol/messenger_storenode_request_test.go index 72e246279..3f5efe146 100644 --- a/protocol/messenger_storenode_request_test.go +++ b/protocol/messenger_storenode_request_test.go @@ -11,7 +11,7 @@ import ( "go.uber.org/zap" "google.golang.org/protobuf/proto" - "github.com/waku-org/go-waku/waku/v2/protocol/store" + "github.com/waku-org/go-waku/waku/v2/protocol/legacy_store" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" @@ -380,10 +380,10 @@ func (s *MessengerStoreNodeRequestSuite) ensureStoreNodeEnvelopes(contentTopic * time.Sleep(100 * time.Millisecond) // Directly ensure profile is available on store node - queryOptions := []store.HistoryRequestOption{ - store.WithLocalQuery(), + queryOptions := []legacy_store.HistoryRequestOption{ + legacy_store.WithLocalQuery(), } - query := store.Query{ + query := legacy_store.Query{ PubsubTopic: "", ContentTopics: []string{contentTopic.ContentTopic()}, } diff --git a/services/mailservers/database.go b/services/mailservers/database.go index 383e8c8b0..729c2316f 100644 --- a/services/mailservers/database.go +++ b/services/mailservers/database.go @@ -37,7 +37,7 @@ func (m Mailserver) IDBytes() ([]byte, error) { if err != nil { return nil, err } - return []byte(id.Pretty()), err + return []byte(id.String()), err } node, err := enode.ParseV4(m.Address) diff --git a/telemetry/client.go b/telemetry/client.go index 3336d0c5b..14b671897 100644 --- a/telemetry/client.go +++ b/telemetry/client.go @@ -62,7 +62,7 @@ func (c *Client) PushReceivedMessages(filter transport.Filter, sshMessage *types func (c *Client) PushReceivedEnvelope(envelope *v2protocol.Envelope) { url := fmt.Sprintf("%s/received-envelope", c.serverURL) postBody := map[string]interface{}{ - "messageHash": types.EncodeHex(envelope.Hash()), + "messageHash": envelope.Hash().String(), "sentAt": uint32(envelope.Message().GetTimestamp() / int64(time.Second)), "pubsubTopic": envelope.PubsubTopic(), "topic": envelope.Message().ContentTopic, diff --git a/vendor/github.com/BurntSushi/toml/decode.go b/vendor/github.com/BurntSushi/toml/decode.go index 0ca1dc4fe..4d38f3bfc 100644 --- a/vendor/github.com/BurntSushi/toml/decode.go +++ b/vendor/github.com/BurntSushi/toml/decode.go @@ -91,7 +91,7 @@ const ( // UnmarshalText method. See the Unmarshaler example for a demonstration with // email addresses. // -// ### Key mapping +// # Key mapping // // TOML keys can map to either keys in a Go map or field names in a Go struct. // The special `toml` struct tag can be used to map TOML keys to struct fields @@ -248,7 +248,7 @@ func (md *MetaData) unify(data interface{}, rv reflect.Value) error { case reflect.Bool: return md.unifyBool(data, rv) case reflect.Interface: - if rv.NumMethod() > 0 { // Only support empty interfaces are supported. + if rv.NumMethod() > 0 { /// Only empty interfaces are supported. return md.e("unsupported type %s", rv.Type()) } return md.unifyAnything(data, rv) diff --git a/vendor/github.com/BurntSushi/toml/deprecated.go b/vendor/github.com/BurntSushi/toml/deprecated.go index c6af3f239..b9e309717 100644 --- a/vendor/github.com/BurntSushi/toml/deprecated.go +++ b/vendor/github.com/BurntSushi/toml/deprecated.go @@ -5,17 +5,25 @@ import ( "io" ) +// TextMarshaler is an alias for encoding.TextMarshaler. +// // Deprecated: use encoding.TextMarshaler type TextMarshaler encoding.TextMarshaler +// TextUnmarshaler is an alias for encoding.TextUnmarshaler. +// // Deprecated: use encoding.TextUnmarshaler type TextUnmarshaler encoding.TextUnmarshaler +// PrimitiveDecode is an alias for MetaData.PrimitiveDecode(). +// // Deprecated: use MetaData.PrimitiveDecode. func PrimitiveDecode(primValue Primitive, v interface{}) error { md := MetaData{decoded: make(map[string]struct{})} return md.unify(primValue.undecoded, rvalue(v)) } +// DecodeReader is an alias for NewDecoder(r).Decode(v). +// // Deprecated: use NewDecoder(reader).Decode(&value). func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { return NewDecoder(r).Decode(v) } diff --git a/vendor/github.com/BurntSushi/toml/encode.go b/vendor/github.com/BurntSushi/toml/encode.go index 930e1d521..9cd25d757 100644 --- a/vendor/github.com/BurntSushi/toml/encode.go +++ b/vendor/github.com/BurntSushi/toml/encode.go @@ -136,7 +136,8 @@ func NewEncoder(w io.Writer) *Encoder { // document. func (enc *Encoder) Encode(v interface{}) error { rv := eindirect(reflect.ValueOf(v)) - if err := enc.safeEncode(Key([]string{}), rv); err != nil { + err := enc.safeEncode(Key([]string{}), rv) + if err != nil { return err } return enc.w.Flush() @@ -457,6 +458,16 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { frv := eindirect(rv.Field(i)) + if is32Bit { + // Copy so it works correct on 32bit archs; not clear why this + // is needed. See #314, and https://www.reddit.com/r/golang/comments/pnx8v4 + // This also works fine on 64bit, but 32bit archs are somewhat + // rare and this is a wee bit faster. + copyStart := make([]int, len(start)) + copy(copyStart, start) + start = copyStart + } + // Treat anonymous struct fields with tag names as though they are // not anonymous, like encoding/json does. // @@ -471,17 +482,7 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { if typeIsTable(tomlTypeOfGo(frv)) { fieldsSub = append(fieldsSub, append(start, f.Index...)) } else { - // Copy so it works correct on 32bit archs; not clear why this - // is needed. See #314, and https://www.reddit.com/r/golang/comments/pnx8v4 - // This also works fine on 64bit, but 32bit archs are somewhat - // rare and this is a wee bit faster. - if is32Bit { - copyStart := make([]int, len(start)) - copy(copyStart, start) - fieldsDirect = append(fieldsDirect, append(copyStart, f.Index...)) - } else { - fieldsDirect = append(fieldsDirect, append(start, f.Index...)) - } + fieldsDirect = append(fieldsDirect, append(start, f.Index...)) } } } @@ -490,24 +491,27 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { writeFields := func(fields [][]int) { for _, fieldIndex := range fields { fieldType := rt.FieldByIndex(fieldIndex) - fieldVal := eindirect(rv.FieldByIndex(fieldIndex)) - - if isNil(fieldVal) { /// Don't write anything for nil fields. - continue - } + fieldVal := rv.FieldByIndex(fieldIndex) opts := getOptions(fieldType.Tag) if opts.skip { continue } + if opts.omitempty && isEmpty(fieldVal) { + continue + } + + fieldVal = eindirect(fieldVal) + + if isNil(fieldVal) { /// Don't write anything for nil fields. + continue + } + keyName := fieldType.Name if opts.name != "" { keyName = opts.name } - if opts.omitempty && enc.isEmpty(fieldVal) { - continue - } if opts.omitzero && isZero(fieldVal) { continue } @@ -649,7 +653,7 @@ func isZero(rv reflect.Value) bool { return false } -func (enc *Encoder) isEmpty(rv reflect.Value) bool { +func isEmpty(rv reflect.Value) bool { switch rv.Kind() { case reflect.Array, reflect.Slice, reflect.Map, reflect.String: return rv.Len() == 0 @@ -664,13 +668,15 @@ func (enc *Encoder) isEmpty(rv reflect.Value) bool { // type b struct{ s []string } // s := a{field: b{s: []string{"AAA"}}} for i := 0; i < rv.NumField(); i++ { - if !enc.isEmpty(rv.Field(i)) { + if !isEmpty(rv.Field(i)) { return false } } return true case reflect.Bool: return !rv.Bool() + case reflect.Ptr: + return rv.IsNil() } return false } @@ -693,8 +699,11 @@ func (enc *Encoder) newline() { // v v v v vv // key = {k = 1, k2 = 2} func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) { + /// Marshaler used on top-level document; call eElement() to just call + /// Marshal{TOML,Text}. if len(key) == 0 { - encPanic(errNoKey) + enc.eElement(val) + return } enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1)) enc.eElement(val) diff --git a/vendor/github.com/BurntSushi/toml/error.go b/vendor/github.com/BurntSushi/toml/error.go index f4f390e64..efd68865b 100644 --- a/vendor/github.com/BurntSushi/toml/error.go +++ b/vendor/github.com/BurntSushi/toml/error.go @@ -84,7 +84,7 @@ func (pe ParseError) Error() string { pe.Position.Line, pe.LastKey, msg) } -// ErrorWithUsage() returns the error with detailed location context. +// ErrorWithPosition returns the error with detailed location context. // // See the documentation on [ParseError]. func (pe ParseError) ErrorWithPosition() string { @@ -124,7 +124,7 @@ func (pe ParseError) ErrorWithPosition() string { return b.String() } -// ErrorWithUsage() returns the error with detailed location context and usage +// ErrorWithUsage returns the error with detailed location context and usage // guidance. // // See the documentation on [ParseError]. diff --git a/vendor/github.com/BurntSushi/toml/lex.go b/vendor/github.com/BurntSushi/toml/lex.go index d4d70871d..3545a6ad6 100644 --- a/vendor/github.com/BurntSushi/toml/lex.go +++ b/vendor/github.com/BurntSushi/toml/lex.go @@ -46,12 +46,13 @@ func (p Position) String() string { } type lexer struct { - input string - start int - pos int - line int - state stateFn - items chan item + input string + start int + pos int + line int + state stateFn + items chan item + tomlNext bool // Allow for backing up up to 4 runes. This is necessary because TOML // contains 3-rune tokens (""" and '''). @@ -87,13 +88,14 @@ func (lx *lexer) nextItem() item { } } -func lex(input string) *lexer { +func lex(input string, tomlNext bool) *lexer { lx := &lexer{ - input: input, - state: lexTop, - items: make(chan item, 10), - stack: make([]stateFn, 0, 10), - line: 1, + input: input, + state: lexTop, + items: make(chan item, 10), + stack: make([]stateFn, 0, 10), + line: 1, + tomlNext: tomlNext, } return lx } @@ -408,7 +410,7 @@ func lexTableNameEnd(lx *lexer) stateFn { // Lexes only one part, e.g. only 'a' inside 'a.b'. func lexBareName(lx *lexer) stateFn { r := lx.next() - if isBareKeyChar(r) { + if isBareKeyChar(r, lx.tomlNext) { return lexBareName } lx.backup() @@ -618,6 +620,9 @@ func lexInlineTableValue(lx *lexer) stateFn { case isWhitespace(r): return lexSkip(lx, lexInlineTableValue) case isNL(r): + if lx.tomlNext { + return lexSkip(lx, lexInlineTableValue) + } return lx.errorPrevLine(errLexInlineTableNL{}) case r == '#': lx.push(lexInlineTableValue) @@ -640,6 +645,9 @@ func lexInlineTableValueEnd(lx *lexer) stateFn { case isWhitespace(r): return lexSkip(lx, lexInlineTableValueEnd) case isNL(r): + if lx.tomlNext { + return lexSkip(lx, lexInlineTableValueEnd) + } return lx.errorPrevLine(errLexInlineTableNL{}) case r == '#': lx.push(lexInlineTableValueEnd) @@ -648,6 +656,9 @@ func lexInlineTableValueEnd(lx *lexer) stateFn { lx.ignore() lx.skip(isWhitespace) if lx.peek() == '}' { + if lx.tomlNext { + return lexInlineTableValueEnd + } return lx.errorf("trailing comma not allowed in inline tables") } return lexInlineTableValue @@ -770,8 +781,8 @@ func lexRawString(lx *lexer) stateFn { } } -// lexMultilineRawString consumes a raw string. Nothing can be escaped in such -// a string. It assumes that the beginning ''' has already been consumed and +// lexMultilineRawString consumes a raw string. Nothing can be escaped in such a +// string. It assumes that the beginning triple-' has already been consumed and // ignored. func lexMultilineRawString(lx *lexer) stateFn { r := lx.next() @@ -828,6 +839,11 @@ func lexMultilineStringEscape(lx *lexer) stateFn { func lexStringEscape(lx *lexer) stateFn { r := lx.next() switch r { + case 'e': + if !lx.tomlNext { + return lx.error(errLexEscape{r}) + } + fallthrough case 'b': fallthrough case 't': @@ -846,6 +862,11 @@ func lexStringEscape(lx *lexer) stateFn { fallthrough case '\\': return lx.pop() + case 'x': + if !lx.tomlNext { + return lx.error(errLexEscape{r}) + } + return lexHexEscape case 'u': return lexShortUnicodeEscape case 'U': @@ -854,6 +875,19 @@ func lexStringEscape(lx *lexer) stateFn { return lx.error(errLexEscape{r}) } +func lexHexEscape(lx *lexer) stateFn { + var r rune + for i := 0; i < 2; i++ { + r = lx.next() + if !isHexadecimal(r) { + return lx.errorf( + `expected two hexadecimal digits after '\x', but got %q instead`, + lx.current()) + } + } + return lx.pop() +} + func lexShortUnicodeEscape(lx *lexer) stateFn { var r rune for i := 0; i < 4; i++ { @@ -1225,7 +1259,23 @@ func isOctal(r rune) bool { return r >= '0' && r <= '7' } func isHexadecimal(r rune) bool { return (r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F') } -func isBareKeyChar(r rune) bool { + +func isBareKeyChar(r rune, tomlNext bool) bool { + if tomlNext { + return (r >= 'A' && r <= 'Z') || + (r >= 'a' && r <= 'z') || + (r >= '0' && r <= '9') || + r == '_' || r == '-' || + r == 0xb2 || r == 0xb3 || r == 0xb9 || (r >= 0xbc && r <= 0xbe) || + (r >= 0xc0 && r <= 0xd6) || (r >= 0xd8 && r <= 0xf6) || (r >= 0xf8 && r <= 0x037d) || + (r >= 0x037f && r <= 0x1fff) || + (r >= 0x200c && r <= 0x200d) || (r >= 0x203f && r <= 0x2040) || + (r >= 0x2070 && r <= 0x218f) || (r >= 0x2460 && r <= 0x24ff) || + (r >= 0x2c00 && r <= 0x2fef) || (r >= 0x3001 && r <= 0xd7ff) || + (r >= 0xf900 && r <= 0xfdcf) || (r >= 0xfdf0 && r <= 0xfffd) || + (r >= 0x10000 && r <= 0xeffff) + } + return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || diff --git a/vendor/github.com/BurntSushi/toml/meta.go b/vendor/github.com/BurntSushi/toml/meta.go index 71847a041..2e78b24e9 100644 --- a/vendor/github.com/BurntSushi/toml/meta.go +++ b/vendor/github.com/BurntSushi/toml/meta.go @@ -106,7 +106,7 @@ func (k Key) maybeQuoted(i int) string { return `""` } for _, c := range k[i] { - if !isBareKeyChar(c) { + if !isBareKeyChar(c, false) { return `"` + dblQuotedReplacer.Replace(k[i]) + `"` } } diff --git a/vendor/github.com/BurntSushi/toml/parse.go b/vendor/github.com/BurntSushi/toml/parse.go index d2542d6f9..9c1915369 100644 --- a/vendor/github.com/BurntSushi/toml/parse.go +++ b/vendor/github.com/BurntSushi/toml/parse.go @@ -2,6 +2,7 @@ package toml import ( "fmt" + "os" "strconv" "strings" "time" @@ -15,6 +16,7 @@ type parser struct { context Key // Full key for the current hash in scope. currentKey string // Base key name for everything except hashes. pos Position // Current position in the TOML file. + tomlNext bool ordered []Key // List of keys in the order that they appear in the TOML data. @@ -29,6 +31,8 @@ type keyInfo struct { } func parse(data string) (p *parser, err error) { + _, tomlNext := os.LookupEnv("BURNTSUSHI_TOML_110") + defer func() { if r := recover(); r != nil { if pErr, ok := r.(ParseError); ok { @@ -41,9 +45,12 @@ func parse(data string) (p *parser, err error) { }() // Read over BOM; do this here as the lexer calls utf8.DecodeRuneInString() - // which mangles stuff. - if strings.HasPrefix(data, "\xff\xfe") || strings.HasPrefix(data, "\xfe\xff") { + // which mangles stuff. UTF-16 BOM isn't strictly valid, but some tools add + // it anyway. + if strings.HasPrefix(data, "\xff\xfe") || strings.HasPrefix(data, "\xfe\xff") { // UTF-16 data = data[2:] + } else if strings.HasPrefix(data, "\xef\xbb\xbf") { // UTF-8 + data = data[3:] } // Examine first few bytes for NULL bytes; this probably means it's a UTF-16 @@ -65,9 +72,10 @@ func parse(data string) (p *parser, err error) { p = &parser{ keyInfo: make(map[string]keyInfo), mapping: make(map[string]interface{}), - lx: lex(data), + lx: lex(data, tomlNext), ordered: make([]Key, 0), implicits: make(map[string]struct{}), + tomlNext: tomlNext, } for { item := p.next() @@ -194,12 +202,12 @@ func (p *parser) topLevel(item item) { for i := range context { p.addImplicitContext(append(p.context, context[i:i+1]...)) } + p.ordered = append(p.ordered, p.context.add(p.currentKey)) /// Set value. vItem := p.next() val, typ := p.value(vItem, false) p.set(p.currentKey, val, typ, vItem.pos) - p.ordered = append(p.ordered, p.context.add(p.currentKey)) /// Remove the context we added (preserving any context from [tbl] lines). p.context = outerContext @@ -236,7 +244,7 @@ func (p *parser) value(it item, parentIsArray bool) (interface{}, tomlType) { case itemString: return p.replaceEscapes(it, it.val), p.typeOfPrimitive(it) case itemMultilineString: - return p.replaceEscapes(it, stripFirstNewline(p.stripEscapedNewlines(it.val))), p.typeOfPrimitive(it) + return p.replaceEscapes(it, p.stripEscapedNewlines(stripFirstNewline(it.val))), p.typeOfPrimitive(it) case itemRawString: return it.val, p.typeOfPrimitive(it) case itemRawMultilineString: @@ -331,11 +339,17 @@ func (p *parser) valueFloat(it item) (interface{}, tomlType) { var dtTypes = []struct { fmt string zone *time.Location + next bool }{ - {time.RFC3339Nano, time.Local}, - {"2006-01-02T15:04:05.999999999", internal.LocalDatetime}, - {"2006-01-02", internal.LocalDate}, - {"15:04:05.999999999", internal.LocalTime}, + {time.RFC3339Nano, time.Local, false}, + {"2006-01-02T15:04:05.999999999", internal.LocalDatetime, false}, + {"2006-01-02", internal.LocalDate, false}, + {"15:04:05.999999999", internal.LocalTime, false}, + + // tomlNext + {"2006-01-02T15:04Z07:00", time.Local, true}, + {"2006-01-02T15:04", internal.LocalDatetime, true}, + {"15:04", internal.LocalTime, true}, } func (p *parser) valueDatetime(it item) (interface{}, tomlType) { @@ -346,6 +360,9 @@ func (p *parser) valueDatetime(it item) (interface{}, tomlType) { err error ) for _, dt := range dtTypes { + if dt.next && !p.tomlNext { + continue + } t, err = time.ParseInLocation(dt.fmt, it.val, dt.zone) if err == nil { ok = true @@ -384,6 +401,7 @@ func (p *parser) valueArray(it item) (interface{}, tomlType) { // // Not entirely sure how to best store this; could use "key[0]", // "key[1]" notation, or maybe store it on the Array type? + _ = types } return array, tomlArray } @@ -426,11 +444,11 @@ func (p *parser) valueInlineTable(it item, parentIsArray bool) (interface{}, tom for i := range context { p.addImplicitContext(append(p.context, context[i:i+1]...)) } + p.ordered = append(p.ordered, p.context.add(p.currentKey)) /// Set the value. val, typ := p.value(p.next(), false) p.set(p.currentKey, val, typ, it.pos) - p.ordered = append(p.ordered, p.context.add(p.currentKey)) hash[p.currentKey] = val /// Restore context. @@ -551,7 +569,6 @@ func (p *parser) addContext(key Key, array bool) { func (p *parser) set(key string, val interface{}, typ tomlType, pos Position) { p.setValue(key, val) p.setType(key, typ, pos) - } // setValue sets the given key to the given value in the current context. @@ -632,14 +649,11 @@ func (p *parser) setType(key string, typ tomlType, pos Position) { // Implicit keys need to be created when tables are implied in "a.b.c.d = 1" and // "[a.b.c]" (the "a", "b", and "c" hashes are never created explicitly). -func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = struct{}{} } -func (p *parser) removeImplicit(key Key) { delete(p.implicits, key.String()) } -func (p *parser) isImplicit(key Key) bool { _, ok := p.implicits[key.String()]; return ok } -func (p *parser) isArray(key Key) bool { return p.keyInfo[key.String()].tomlType == tomlArray } -func (p *parser) addImplicitContext(key Key) { - p.addImplicit(key) - p.addContext(key, false) -} +func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = struct{}{} } +func (p *parser) removeImplicit(key Key) { delete(p.implicits, key.String()) } +func (p *parser) isImplicit(key Key) bool { _, ok := p.implicits[key.String()]; return ok } +func (p *parser) isArray(key Key) bool { return p.keyInfo[key.String()].tomlType == tomlArray } +func (p *parser) addImplicitContext(key Key) { p.addImplicit(key); p.addContext(key, false) } // current returns the full key name of the current context. func (p *parser) current() string { @@ -662,49 +676,54 @@ func stripFirstNewline(s string) string { return s } -// Remove newlines inside triple-quoted strings if a line ends with "\". +// stripEscapedNewlines removes whitespace after line-ending backslashes in +// multiline strings. +// +// A line-ending backslash is an unescaped \ followed only by whitespace until +// the next newline. After a line-ending backslash, all whitespace is removed +// until the next non-whitespace character. func (p *parser) stripEscapedNewlines(s string) string { - split := strings.Split(s, "\n") - if len(split) < 1 { - return s - } + var b strings.Builder + var i int + for { + ix := strings.Index(s[i:], `\`) + if ix < 0 { + b.WriteString(s) + return b.String() + } + i += ix - escNL := false // Keep track of the last non-blank line was escaped. - for i, line := range split { - line = strings.TrimRight(line, " \t\r") - - if len(line) == 0 || line[len(line)-1] != '\\' { - split[i] = strings.TrimRight(split[i], "\r") - if !escNL && i != len(split)-1 { - split[i] += "\n" + if len(s) > i+1 && s[i+1] == '\\' { + // Escaped backslash. + i += 2 + continue + } + // Scan until the next non-whitespace. + j := i + 1 + whitespaceLoop: + for ; j < len(s); j++ { + switch s[j] { + case ' ', '\t', '\r', '\n': + default: + break whitespaceLoop } + } + if j == i+1 { + // Not a whitespace escape. + i++ continue } - - escBS := true - for j := len(line) - 1; j >= 0 && line[j] == '\\'; j-- { - escBS = !escBS - } - if escNL { - line = strings.TrimLeft(line, " \t\r") - } - escNL = !escBS - - if escBS { - split[i] += "\n" + if !strings.Contains(s[i:j], "\n") { + // This is not a line-ending backslash. + // (It's a bad escape sequence, but we can let + // replaceEscapes catch it.) + i++ continue } - - if i == len(split)-1 { - p.panicf("invalid escape: '\\ '") - } - - split[i] = line[:len(line)-1] // Remove \ - if len(split)-1 > i { - split[i+1] = strings.TrimLeft(split[i+1], " \t\r") - } + b.WriteString(s[:i]) + s = s[j:] + i = 0 } - return strings.Join(split, "") } func (p *parser) replaceEscapes(it item, str string) string { @@ -743,12 +762,23 @@ func (p *parser) replaceEscapes(it item, str string) string { case 'r': replaced = append(replaced, rune(0x000D)) r += 1 + case 'e': + if p.tomlNext { + replaced = append(replaced, rune(0x001B)) + r += 1 + } case '"': replaced = append(replaced, rune(0x0022)) r += 1 case '\\': replaced = append(replaced, rune(0x005C)) r += 1 + case 'x': + if p.tomlNext { + escaped := p.asciiEscapeToUnicode(it, s[r+1:r+3]) + replaced = append(replaced, escaped) + r += 3 + } case 'u': // At this point, we know we have a Unicode escape of the form // `uXXXX` at [r, r+5). (Because the lexer guarantees this diff --git a/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go index b48005673..42bf32aab 100644 --- a/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go +++ b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go @@ -9,6 +9,8 @@ func Render(doc []byte) []byte { renderer := NewRoffRenderer() return blackfriday.Run(doc, - []blackfriday.Option{blackfriday.WithRenderer(renderer), - blackfriday.WithExtensions(renderer.GetExtensions())}...) + []blackfriday.Option{ + blackfriday.WithRenderer(renderer), + blackfriday.WithExtensions(renderer.GetExtensions()), + }...) } diff --git a/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go index be2b34360..8a290f197 100644 --- a/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go +++ b/vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go @@ -1,6 +1,8 @@ package md2man import ( + "bufio" + "bytes" "fmt" "io" "os" @@ -20,34 +22,35 @@ type roffRenderer struct { } const ( - titleHeader = ".TH " - topLevelHeader = "\n\n.SH " - secondLevelHdr = "\n.SH " - otherHeader = "\n.SS " - crTag = "\n" - emphTag = "\\fI" - emphCloseTag = "\\fP" - strongTag = "\\fB" - strongCloseTag = "\\fP" - breakTag = "\n.br\n" - paraTag = "\n.PP\n" - hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n" - linkTag = "\n\\[la]" - linkCloseTag = "\\[ra]" - codespanTag = "\\fB\\fC" - codespanCloseTag = "\\fR" - codeTag = "\n.PP\n.RS\n\n.nf\n" - codeCloseTag = "\n.fi\n.RE\n" - quoteTag = "\n.PP\n.RS\n" - quoteCloseTag = "\n.RE\n" - listTag = "\n.RS\n" - listCloseTag = "\n.RE\n" - dtTag = "\n.TP\n" - dd2Tag = "\n" - tableStart = "\n.TS\nallbox;\n" - tableEnd = ".TE\n" - tableCellStart = "T{\n" - tableCellEnd = "\nT}\n" + titleHeader = ".TH " + topLevelHeader = "\n\n.SH " + secondLevelHdr = "\n.SH " + otherHeader = "\n.SS " + crTag = "\n" + emphTag = "\\fI" + emphCloseTag = "\\fP" + strongTag = "\\fB" + strongCloseTag = "\\fP" + breakTag = "\n.br\n" + paraTag = "\n.PP\n" + hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n" + linkTag = "\n\\[la]" + linkCloseTag = "\\[ra]" + codespanTag = "\\fB" + codespanCloseTag = "\\fR" + codeTag = "\n.EX\n" + codeCloseTag = ".EE\n" // Do not prepend a newline character since code blocks, by definition, include a newline already (or at least as how blackfriday gives us on). + quoteTag = "\n.PP\n.RS\n" + quoteCloseTag = "\n.RE\n" + listTag = "\n.RS\n" + listCloseTag = "\n.RE\n" + dtTag = "\n.TP\n" + dd2Tag = "\n" + tableStart = "\n.TS\nallbox;\n" + tableEnd = ".TE\n" + tableCellStart = "T{\n" + tableCellEnd = "\nT}\n" + tablePreprocessor = `'\" t` ) // NewRoffRenderer creates a new blackfriday Renderer for generating roff documents @@ -74,6 +77,16 @@ func (r *roffRenderer) GetExtensions() blackfriday.Extensions { // RenderHeader handles outputting the header at document start func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) { + // We need to walk the tree to check if there are any tables. + // If there are, we need to enable the roff table preprocessor. + ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus { + if node.Type == blackfriday.Table { + out(w, tablePreprocessor+"\n") + return blackfriday.Terminate + } + return blackfriday.GoToNext + }) + // disable hyphenation out(w, ".nh\n") } @@ -86,8 +99,7 @@ func (r *roffRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) { // RenderNode is called for each node in a markdown document; based on the node // type the equivalent roff output is sent to the writer func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { - - var walkAction = blackfriday.GoToNext + walkAction := blackfriday.GoToNext switch node.Type { case blackfriday.Text: @@ -109,9 +121,16 @@ func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering out(w, strongCloseTag) } case blackfriday.Link: - if !entering { - out(w, linkTag+string(node.LinkData.Destination)+linkCloseTag) + // Don't render the link text for automatic links, because this + // will only duplicate the URL in the roff output. + // See https://daringfireball.net/projects/markdown/syntax#autolink + if !bytes.Equal(node.LinkData.Destination, node.FirstChild.Literal) { + out(w, string(node.FirstChild.Literal)) } + // Hyphens in a link must be escaped to avoid word-wrap in the rendered man page. + escapedLink := strings.ReplaceAll(string(node.LinkData.Destination), "-", "\\-") + out(w, linkTag+escapedLink+linkCloseTag) + walkAction = blackfriday.SkipChildren case blackfriday.Image: // ignore images walkAction = blackfriday.SkipChildren @@ -160,6 +179,11 @@ func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering r.handleTableCell(w, node, entering) case blackfriday.HTMLSpan: // ignore other HTML tags + case blackfriday.HTMLBlock: + if bytes.HasPrefix(node.Literal, []byte(" - [Background](#background) - [Roadmap](#roadmap) - [Usage](#usage) - [Examples](#examples) -- [Development](#development) - - [Tests](#tests) + - [Dashboards](#dashboards) - [Contribute](#contribute) -- [Supported Go Versions](#supported-go-versions) + - [Supported Go Versions](#supported-go-versions) +- [Notable Users](#notable-users) -## Background +# Background [libp2p](https://github.com/libp2p/specs) is a networking stack and library modularized out of [The IPFS Project](https://github.com/ipfs/ipfs), and bundled separately for other tools to use. > @@ -37,12 +36,12 @@ To learn more, check out the following resources: - [**js-libp2p implementation**](https://github.com/libp2p/js-libp2p) - [**rust-libp2p implementation**](https://github.com/libp2p/rust-libp2p) -## Roadmap +# Roadmap Our roadmap for go-libp2p can be found here: https://github.com/libp2p/go-libp2p/blob/master/ROADMAP.md -This document represents current projects the go-libp2p team is focused on and provides an estimation of completion targets. It is a completementary roadmap to the overarching libp2p project roadmap: https://github.com/libp2p/specs/blob/master/ROADMAP.md +This document represents current projects the go-libp2p team is focused on and provides an estimation of completion targets. It is a complementary roadmap to the overarching libp2p project roadmap: https://github.com/libp2p/specs/blob/master/ROADMAP.md -## Usage +# Usage This repository (`go-libp2p`) serves as the entrypoint to the universe of packages that compose the Go implementation of the libp2p stack. @@ -52,10 +51,17 @@ You can start using go-libp2p in your Go application simply by adding imports fr import "github.com/libp2p/go-libp2p" ``` -### Examples +## Examples Examples can be found in the [examples folder](examples). +## Dashboards + +We provide prebuilt Grafana dashboards so that applications can better monitor libp2p in production. +You can find the [dashboard JSON files here](https://github.com/libp2p/go-libp2p/tree/master/dashboards). + +We also have live [Public Dashboards](https://github.com/libp2p/go-libp2p/tree/master/dashboards/README.md#public-dashboards) that you can check out to see real time monitoring in action. + # Contribute @@ -70,7 +76,7 @@ Guidelines: - have fun! There's a few things you can do right now to help out: - - Go through the modules below and **check out existing issues**. This would be especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrasture behind it - for instance, you may need to read up on p2p and more complex operations like muxing to be able to help technically. + - Go through the modules below and **check out existing issues**. This would be especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrastructure behind it - for instance, you may need to read up on p2p and more complex operations like muxing to be able to help technically. - **Perform code reviews**. - **Add tests**. There can never be enough tests. @@ -99,4 +105,4 @@ Some notable users of go-libp2p are: - [Kairos](https://github.com/kairos-io/kairos) - A Kubernetes-focused, Cloud Native Linux meta-distribution. - [Oasis Core](https://github.com/oasisprotocol/oasis-core) - The consensus and runtime layers of the [Oasis protocol](https://oasisprotocol.org/). -Please open a pull request if you want your project to be added here. +Please open a pull request if you want your project (min. 250 GitHub stars) to be added here. diff --git a/vendor/github.com/libp2p/go-libp2p/config/config.go b/vendor/github.com/libp2p/go-libp2p/config/config.go index cea8ef131..8be5a4399 100644 --- a/vendor/github.com/libp2p/go-libp2p/config/config.go +++ b/vendor/github.com/libp2p/go-libp2p/config/config.go @@ -261,6 +261,7 @@ func (cfg *Config) addTransports(h host.Host) error { } fxopts = append(fxopts, fx.Provide(PrivKeyToStatelessResetKey)) + fxopts = append(fxopts, fx.Provide(PrivKeyToTokenGeneratorKey)) if cfg.QUICReuse != nil { fxopts = append(fxopts, cfg.QUICReuse...) } else { @@ -295,6 +296,15 @@ func (cfg *Config) addTransports(h host.Host) error { // // This function consumes the config. Do not reuse it (really!). func (cfg *Config) NewNode() (host.Host, error) { + // If possible check that the resource manager conn limit is higher than the + // limit set in the conn manager. + if l, ok := cfg.ResourceManager.(connmgr.GetConnLimiter); ok { + err := cfg.ConnManager.CheckLimit(l) + if err != nil { + log.Warn(fmt.Sprintf("rcmgr limit conflicts with connmgr limit: %v", err)) + } + } + eventBus := eventbus.NewBus(eventbus.WithMetricsTracer(eventbus.NewMetricsTracer(eventbus.WithRegisterer(cfg.PrometheusRegisterer)))) swrm, err := cfg.makeSwarm(eventBus, !cfg.DisableMetrics) if err != nil { @@ -419,6 +429,11 @@ func (cfg *Config) NewNode() (host.Host, error) { PeerKey: autonatPrivKey, Peerstore: ps, DialRanker: swarm.NoDelayDialRanker, + SwarmOpts: []swarm.Option{ + // It is better to disable black hole detection and just attempt a dial for autonat + swarm.WithUDPBlackHoleConfig(false, 0, 0), + swarm.WithIPv6BlackHoleConfig(false, 0, 0), + }, } dialer, err := autoNatCfg.makeSwarm(eventbus.NewBus(), false) diff --git a/vendor/github.com/libp2p/go-libp2p/config/quic_stateless_reset.go b/vendor/github.com/libp2p/go-libp2p/config/quic.go similarity index 53% rename from vendor/github.com/libp2p/go-libp2p/config/quic_stateless_reset.go rename to vendor/github.com/libp2p/go-libp2p/config/quic.go index a12be56f5..66c40da97 100644 --- a/vendor/github.com/libp2p/go-libp2p/config/quic_stateless_reset.go +++ b/vendor/github.com/libp2p/go-libp2p/config/quic.go @@ -11,7 +11,10 @@ import ( "github.com/quic-go/quic-go" ) -const statelessResetKeyInfo = "libp2p quic stateless reset key" +const ( + statelessResetKeyInfo = "libp2p quic stateless reset key" + tokenGeneratorKeyInfo = "libp2p quic token generator key" +) func PrivKeyToStatelessResetKey(key crypto.PrivKey) (quic.StatelessResetKey, error) { var statelessResetKey quic.StatelessResetKey @@ -25,3 +28,16 @@ func PrivKeyToStatelessResetKey(key crypto.PrivKey) (quic.StatelessResetKey, err } return statelessResetKey, nil } + +func PrivKeyToTokenGeneratorKey(key crypto.PrivKey) (quic.TokenGeneratorKey, error) { + var tokenKey quic.TokenGeneratorKey + keyBytes, err := key.Raw() + if err != nil { + return tokenKey, err + } + keyReader := hkdf.New(sha256.New, keyBytes, nil, []byte(tokenGeneratorKeyInfo)) + if _, err := io.ReadFull(keyReader, tokenKey[:]); err != nil { + return tokenKey, err + } + return tokenKey, nil +} diff --git a/vendor/github.com/libp2p/go-libp2p/core/connmgr/manager.go b/vendor/github.com/libp2p/go-libp2p/core/connmgr/manager.go index e0d6c5201..d756e4399 100644 --- a/vendor/github.com/libp2p/go-libp2p/core/connmgr/manager.go +++ b/vendor/github.com/libp2p/go-libp2p/core/connmgr/manager.go @@ -74,6 +74,10 @@ type ConnManager interface { // then it will return true if the peer is protected for any tag IsProtected(id peer.ID, tag string) (protected bool) + // CheckLimit will return an error if the connection manager's internal + // connection limit exceeds the provided system limit. + CheckLimit(l GetConnLimiter) error + // Close closes the connection manager and stops background processes. Close() error } @@ -89,3 +93,9 @@ type TagInfo struct { // Conns maps connection ids (such as remote multiaddr) to their creation time. Conns map[string]time.Time } + +// GetConnLimiter provides access to a component's total connection limit. +type GetConnLimiter interface { + // GetConnLimit returns the total connection limit of the implementing component. + GetConnLimit() int +} diff --git a/vendor/github.com/libp2p/go-libp2p/core/connmgr/null.go b/vendor/github.com/libp2p/go-libp2p/core/connmgr/null.go index 25743f4ec..735411712 100644 --- a/vendor/github.com/libp2p/go-libp2p/core/connmgr/null.go +++ b/vendor/github.com/libp2p/go-libp2p/core/connmgr/null.go @@ -21,4 +21,5 @@ func (NullConnMgr) Notifee() network.Notifiee { return network.Gl func (NullConnMgr) Protect(peer.ID, string) {} func (NullConnMgr) Unprotect(peer.ID, string) bool { return false } func (NullConnMgr) IsProtected(peer.ID, string) bool { return false } +func (NullConnMgr) CheckLimit(l GetConnLimiter) error { return nil } func (NullConnMgr) Close() error { return nil } diff --git a/vendor/github.com/libp2p/go-libp2p/core/crypto/ecdsa.go b/vendor/github.com/libp2p/go-libp2p/core/crypto/ecdsa.go index c936d502a..8e392c9ed 100644 --- a/vendor/github.com/libp2p/go-libp2p/core/crypto/ecdsa.go +++ b/vendor/github.com/libp2p/go-libp2p/core/crypto/ecdsa.go @@ -12,8 +12,7 @@ import ( pb "github.com/libp2p/go-libp2p/core/crypto/pb" "github.com/libp2p/go-libp2p/core/internal/catch" - - "github.com/minio/sha256-simd" + "github.com/libp2p/go-libp2p/internal/sha256" ) // ECDSAPrivateKey is an implementation of an ECDSA private key diff --git a/vendor/github.com/libp2p/go-libp2p/core/crypto/rsa_go.go b/vendor/github.com/libp2p/go-libp2p/core/crypto/rsa_go.go index 8981ba6aa..c955cf8e0 100644 --- a/vendor/github.com/libp2p/go-libp2p/core/crypto/rsa_go.go +++ b/vendor/github.com/libp2p/go-libp2p/core/crypto/rsa_go.go @@ -10,8 +10,7 @@ import ( pb "github.com/libp2p/go-libp2p/core/crypto/pb" "github.com/libp2p/go-libp2p/core/internal/catch" - - "github.com/minio/sha256-simd" + "github.com/libp2p/go-libp2p/internal/sha256" ) // RsaPrivateKey is a rsa private key diff --git a/vendor/github.com/libp2p/go-libp2p/core/crypto/secp256k1.go b/vendor/github.com/libp2p/go-libp2p/core/crypto/secp256k1.go index 27544a59f..bcd68ac6d 100644 --- a/vendor/github.com/libp2p/go-libp2p/core/crypto/secp256k1.go +++ b/vendor/github.com/libp2p/go-libp2p/core/crypto/secp256k1.go @@ -9,7 +9,7 @@ import ( "github.com/decred/dcrd/dcrec/secp256k1/v4" "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" - "github.com/minio/sha256-simd" + "github.com/libp2p/go-libp2p/internal/sha256" ) // Secp256k1PrivateKey is a Secp256k1 private key diff --git a/vendor/github.com/libp2p/go-libp2p/core/peer/addrinfo.go b/vendor/github.com/libp2p/go-libp2p/core/peer/addrinfo.go index fba4cfd0e..de7dd4d98 100644 --- a/vendor/github.com/libp2p/go-libp2p/core/peer/addrinfo.go +++ b/vendor/github.com/libp2p/go-libp2p/core/peer/addrinfo.go @@ -86,7 +86,7 @@ func AddrInfoFromP2pAddr(m ma.Multiaddr) (*AddrInfo, error) { // AddrInfoToP2pAddrs converts an AddrInfo to a list of Multiaddrs. func AddrInfoToP2pAddrs(pi *AddrInfo) ([]ma.Multiaddr, error) { - p2ppart, err := ma.NewComponent("p2p", Encode(pi.ID)) + p2ppart, err := ma.NewComponent("p2p", pi.ID.String()) if err != nil { return nil, err } @@ -102,7 +102,7 @@ func AddrInfoToP2pAddrs(pi *AddrInfo) ([]ma.Multiaddr, error) { func (pi *AddrInfo) Loggable() map[string]interface{} { return map[string]interface{}{ - "peerID": pi.ID.Pretty(), + "peerID": pi.ID.String(), "addrs": pi.Addrs, } } diff --git a/vendor/github.com/libp2p/go-libp2p/core/peer/peer.go b/vendor/github.com/libp2p/go-libp2p/core/peer/peer.go index f7f649f24..b77fb684e 100644 --- a/vendor/github.com/libp2p/go-libp2p/core/peer/peer.go +++ b/vendor/github.com/libp2p/go-libp2p/core/peer/peer.go @@ -41,12 +41,6 @@ const maxInlineKeyLength = 42 // hash output as a multihash. See IDFromPublicKey for details. type ID string -// Pretty returns a base58-encoded string representation of the ID. -// Deprecated: use String() instead. -func (id ID) Pretty() string { - return id.String() -} - // Loggable returns a pretty peer ID string in loggable JSON format. func (id ID) Loggable() map[string]interface{} { return map[string]interface{}{ @@ -145,16 +139,6 @@ func Decode(s string) (ID, error) { return FromCid(c) } -// Encode encodes a peer ID as a string. -// -// At the moment, it base58 encodes the peer ID but, in the future, it will -// switch to encoding it as a CID by default. -// -// Deprecated: use id.String instead. -func Encode(id ID) string { - return id.String() -} - // FromCid converts a CID to a peer ID, if possible. func FromCid(c cid.Cid) (ID, error) { code := mc.Code(c.Type()) diff --git a/vendor/github.com/libp2p/go-libp2p/core/peer/peer_serde.go b/vendor/github.com/libp2p/go-libp2p/core/peer/peer_serde.go index 5fd1cd50c..3e2f71793 100644 --- a/vendor/github.com/libp2p/go-libp2p/core/peer/peer_serde.go +++ b/vendor/github.com/libp2p/go-libp2p/core/peer/peer_serde.go @@ -45,7 +45,7 @@ func (id ID) Size() int { } func (id ID) MarshalJSON() ([]byte, error) { - return json.Marshal(Encode(id)) + return json.Marshal(id.String()) } func (id *ID) UnmarshalJSON(data []byte) (err error) { @@ -59,7 +59,7 @@ func (id *ID) UnmarshalJSON(data []byte) (err error) { // MarshalText returns the text encoding of the ID. func (id ID) MarshalText() ([]byte, error) { - return []byte(Encode(id)), nil + return []byte(id.String()), nil } // UnmarshalText restores the ID from its text encoding. diff --git a/vendor/github.com/libp2p/go-libp2p/core/sec/security.go b/vendor/github.com/libp2p/go-libp2p/core/sec/security.go index 83059d94c..d9e918329 100644 --- a/vendor/github.com/libp2p/go-libp2p/core/sec/security.go +++ b/vendor/github.com/libp2p/go-libp2p/core/sec/security.go @@ -3,6 +3,7 @@ package sec import ( "context" + "fmt" "net" "github.com/libp2p/go-libp2p/core/network" @@ -29,3 +30,14 @@ type SecureTransport interface { // ID is the protocol ID of the security protocol. ID() protocol.ID } + +type ErrPeerIDMismatch struct { + Expected peer.ID + Actual peer.ID +} + +func (e ErrPeerIDMismatch) Error() string { + return fmt.Sprintf("peer id mismatch: expected %s, but remote key matches %s", e.Expected, e.Actual) +} + +var _ error = (*ErrPeerIDMismatch)(nil) diff --git a/vendor/github.com/libp2p/go-libp2p/core/transport/transport.go b/vendor/github.com/libp2p/go-libp2p/core/transport/transport.go index 89a9608d4..d56a3cff0 100644 --- a/vendor/github.com/libp2p/go-libp2p/core/transport/transport.go +++ b/vendor/github.com/libp2p/go-libp2p/core/transport/transport.go @@ -5,6 +5,7 @@ package transport import ( "context" "errors" + "fmt" "net" "github.com/libp2p/go-libp2p/core/network" @@ -124,3 +125,47 @@ type Upgrader interface { // Upgrade upgrades the multiaddr/net connection into a full libp2p-transport connection. Upgrade(ctx context.Context, t Transport, maconn manet.Conn, dir network.Direction, p peer.ID, scope network.ConnManagementScope) (CapableConn, error) } + +// DialUpdater provides updates on in progress dials. +type DialUpdater interface { + // DialWithUpdates dials a remote peer and provides updates on the passed channel. + DialWithUpdates(context.Context, ma.Multiaddr, peer.ID, chan<- DialUpdate) (CapableConn, error) +} + +// DialUpdateKind indicates the type of DialUpdate event. +type DialUpdateKind int + +const ( + // UpdateKindDialFailed indicates dial failed. + UpdateKindDialFailed DialUpdateKind = iota + // UpdateKindDialSuccessful indicates dial succeeded. + UpdateKindDialSuccessful + // UpdateKindHandshakeProgressed indicates successful completion of the TCP 3-way + // handshake + UpdateKindHandshakeProgressed +) + +func (k DialUpdateKind) String() string { + switch k { + case UpdateKindDialFailed: + return "DialFailed" + case UpdateKindDialSuccessful: + return "DialSuccessful" + case UpdateKindHandshakeProgressed: + return "UpdateKindHandshakeProgressed" + default: + return fmt.Sprintf("DialUpdateKind", k) + } +} + +// DialUpdate is used by DialUpdater to provide dial updates. +type DialUpdate struct { + // Kind is the kind of update event. + Kind DialUpdateKind + // Addr is the peer's address. + Addr ma.Multiaddr + // Conn is the resulting connection on success. + Conn CapableConn + // Err is the reason for dial failure. + Err error +} diff --git a/vendor/github.com/libp2p/go-libp2p/defaults.go b/vendor/github.com/libp2p/go-libp2p/defaults.go index c0ed6698a..d11302690 100644 --- a/vendor/github.com/libp2p/go-libp2p/defaults.go +++ b/vendor/github.com/libp2p/go-libp2p/defaults.go @@ -79,11 +79,9 @@ var RandomIdentity = func(cfg *Config) error { var DefaultListenAddrs = func(cfg *Config) error { addrs := []string{ "/ip4/0.0.0.0/tcp/0", - "/ip4/0.0.0.0/udp/0/quic", "/ip4/0.0.0.0/udp/0/quic-v1", "/ip4/0.0.0.0/udp/0/quic-v1/webtransport", "/ip6/::/tcp/0", - "/ip6/::/udp/0/quic", "/ip6/::/udp/0/quic-v1", "/ip6/::/udp/0/quic-v1/webtransport", } diff --git a/vendor/github.com/libp2p/go-libp2p/internal/sha256/post_go1_21.go b/vendor/github.com/libp2p/go-libp2p/internal/sha256/post_go1_21.go new file mode 100644 index 000000000..98c14b609 --- /dev/null +++ b/vendor/github.com/libp2p/go-libp2p/internal/sha256/post_go1_21.go @@ -0,0 +1,23 @@ +//go:build go1.21 + +// This package use build tags to select between github.com/minio/sha256-simd +// for go1.20 and bellow and crypto/sha256 for go1.21 and above. +// This is used because a fast SHANI implementation of sha256 is only avaiable +// in the std for go1.21 and above. See https://go.dev/issue/50543. +// TODO: Once go1.22 releases remove this package and replace all uses +// with crypto/sha256 because the two supported version of go will have the fast +// implementation. +package sha256 + +import ( + "crypto/sha256" + "hash" +) + +func Sum256(b []byte) [sha256.Size]byte { + return sha256.Sum256(b) +} + +func New() hash.Hash { + return sha256.New() +} diff --git a/vendor/github.com/libp2p/go-libp2p/internal/sha256/pre_go1_21.go b/vendor/github.com/libp2p/go-libp2p/internal/sha256/pre_go1_21.go new file mode 100644 index 000000000..db0573333 --- /dev/null +++ b/vendor/github.com/libp2p/go-libp2p/internal/sha256/pre_go1_21.go @@ -0,0 +1,24 @@ +//go:build !go1.21 + +// This package use build tags to select between github.com/minio/sha256-simd +// for go1.20 and bellow and crypto/sha256 for go1.21 and above. +// This is used because a fast SHANI implementation of sha256 is only avaiable +// in the std for go1.21 and above. See https://go.dev/issue/50543. +// TODO: Once go1.22 releases remove this package and replace all uses +// with crypto/sha256 because the two supported version of go will have the fast +// implementation. +package sha256 + +import ( + "hash" + + "github.com/minio/sha256-simd" +) + +func Sum256(b []byte) [sha256.Size]byte { + return sha256.Sum256(b) +} + +func New() hash.Hash { + return sha256.New() +} diff --git a/vendor/github.com/libp2p/go-libp2p/options.go b/vendor/github.com/libp2p/go-libp2p/options.go index beb4930f7..1a1e9d398 100644 --- a/vendor/github.com/libp2p/go-libp2p/options.go +++ b/vendor/github.com/libp2p/go-libp2p/options.go @@ -579,6 +579,7 @@ func PrometheusRegisterer(reg prometheus.Registerer) Option { // DialRanker configures libp2p to use d as the dial ranker. To enable smart // dialing use `swarm.DefaultDialRanker`. use `swarm.NoDelayDialRanker` to // disable smart dialing. +// // Deprecated: use SwarmOpts(swarm.WithDialRanker(d)) instead func DialRanker(d network.DialRanker) Option { return func(cfg *Config) error { diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/host/autonat/svc.go b/vendor/github.com/libp2p/go-libp2p/p2p/host/autonat/svc.go index 98b421c9b..cf1dff8e7 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/host/autonat/svc.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/host/autonat/svc.go @@ -68,7 +68,7 @@ func (as *autoNATService) handleStream(s network.Stream) { defer s.Close() pid := s.Conn().RemotePeer() - log.Debugf("New stream from %s", pid.Pretty()) + log.Debugf("New stream from %s", pid) r := pbio.NewDelimitedReader(s, maxMsgSize) w := pbio.NewDelimitedWriter(s) @@ -78,14 +78,14 @@ func (as *autoNATService) handleStream(s network.Stream) { err := r.ReadMsg(&req) if err != nil { - log.Debugf("Error reading message from %s: %s", pid.Pretty(), err.Error()) + log.Debugf("Error reading message from %s: %s", pid, err.Error()) s.Reset() return } t := req.GetType() if t != pb.Message_DIAL { - log.Debugf("Unexpected message from %s: %s (%d)", pid.Pretty(), t.String(), t) + log.Debugf("Unexpected message from %s: %s (%d)", pid, t.String(), t) s.Reset() return } @@ -96,7 +96,7 @@ func (as *autoNATService) handleStream(s network.Stream) { err = w.WriteMsg(&res) if err != nil { - log.Debugf("Error writing response to %s: %s", pid.Pretty(), err.Error()) + log.Debugf("Error writing response to %s: %s", pid, err.Error()) s.Reset() return } @@ -234,7 +234,7 @@ func (as *autoNATService) doDial(pi peer.AddrInfo) *pb.Message_DialResponse { conn, err := as.config.dialer.DialPeer(ctx, pi.ID) if err != nil { - log.Debugf("error dialing %s: %s", pi.ID.Pretty(), err.Error()) + log.Debugf("error dialing %s: %s", pi.ID, err.Error()) // wait for the context to timeout to avoid leaking timing information // this renders the service ineffective as a port scanner <-ctx.Done() diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/host/autorelay/relay_finder.go b/vendor/github.com/libp2p/go-libp2p/p2p/host/autorelay/relay_finder.go index 3133b7a51..ef79950b7 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/host/autorelay/relay_finder.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/host/autorelay/relay_finder.go @@ -736,7 +736,7 @@ func (rf *relayFinder) relayAddrs(addrs []ma.Multiaddr) []ma.Multiaddr { for p := range rf.relays { addrs := cleanupAddressSet(rf.host.Peerstore().Addrs(p)) relayAddrCnt += len(addrs) - circuit := ma.StringCast(fmt.Sprintf("/p2p/%s/p2p-circuit", p.Pretty())) + circuit := ma.StringCast(fmt.Sprintf("/p2p/%s/p2p-circuit", p)) for _, addr := range addrs { pub := addr.Encapsulate(circuit) raddrs = append(raddrs, pub) diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/host/basic/basic_host.go b/vendor/github.com/libp2p/go-libp2p/p2p/host/basic/basic_host.go index 89f5d28db..6c3ba53e5 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/host/basic/basic_host.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/host/basic/basic_host.go @@ -437,7 +437,7 @@ func (h *BasicHost) newStreamHandler(s network.Stream) { log.Debugf("negotiated: %s (took %s)", protoID, took) - go handle(protoID, s) + handle(protoID, s) } // SignalAddressChange signals to the host that it needs to determine whether our listen addresses have recently @@ -629,9 +629,6 @@ func (h *BasicHost) RemoveStreamHandler(pid protocol.ID) { // to create one. If ProtocolID is "", writes no header. // (Thread-safe) func (h *BasicHost) NewStream(ctx context.Context, p peer.ID, pids ...protocol.ID) (network.Stream, error) { - // Ensure we have a connection, with peer addresses resolved by the routing system (#207) - // It is not sufficient to let the underlying host connect, it will most likely not have - // any addresses for the peer without any prior connections. // If the caller wants to prevent the host from dialing, it should use the NoDial option. if nodial, _ := network.GetNoDial(ctx); !nodial { err := h.Connect(ctx, peer.AddrInfo{ID: p}) @@ -669,7 +666,9 @@ func (h *BasicHost) NewStream(ctx context.Context, p peer.ID, pids ...protocol.I } if pref != "" { - s.SetProtocol(pref) + if err := s.SetProtocol(pref); err != nil { + return nil, err + } lzcon := msmux.NewMSSelect(s, pref) return &streamWrapper{ Stream: s, @@ -795,10 +794,11 @@ func (h *BasicHost) Addrs() []ma.Multiaddr { continue } addrWithCerthash, added := tpt.AddCertHashes(addr) - addrs[i] = addrWithCerthash if !added { log.Debug("Couldn't add certhashes to webtransport multiaddr because we aren't listening on webtransport") + continue } + addrs[i] = addrWithCerthash } } return addrs @@ -945,17 +945,17 @@ func inferWebtransportAddrsFromQuic(in []ma.Multiaddr) []ma.Multiaddr { // Remove certhashes addr, _ = ma.SplitLast(addr) } - webtransportAddrs[addr.String()] = struct{}{} + webtransportAddrs[string(addr.Bytes())] = struct{}{} // Remove webtransport component, now it's a multiaddr that ends in /quic-v1 addr, _ = ma.SplitLast(addr) } if _, lastComponent := ma.SplitLast(addr); lastComponent.Protocol().Code == ma.P_QUIC_V1 { - addrStr := addr.String() - if _, ok := quicOrWebtransportAddrs[addrStr]; ok { + bytes := addr.Bytes() + if _, ok := quicOrWebtransportAddrs[string(bytes)]; ok { foundSameListeningAddr = true } else { - quicOrWebtransportAddrs[addrStr] = struct{}{} + quicOrWebtransportAddrs[string(bytes)] = struct{}{} } } } @@ -977,7 +977,7 @@ func inferWebtransportAddrsFromQuic(in []ma.Multiaddr) []ma.Multiaddr { if _, lastComponent := ma.SplitLast(addr); lastComponent.Protocol().Code == ma.P_QUIC_V1 { // Convert quic to webtransport addr = addr.Encapsulate(wtComponent) - if _, ok := webtransportAddrs[addr.String()]; ok { + if _, ok := webtransportAddrs[string(addr.Bytes())]; ok { // We already have this address continue } diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/host/basic/mocks.go b/vendor/github.com/libp2p/go-libp2p/p2p/host/basic/mocks.go index 3ad4d4e90..a29a0c5ef 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/host/basic/mocks.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/host/basic/mocks.go @@ -2,5 +2,5 @@ package basichost -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package basichost -destination mock_nat_test.go github.com/libp2p/go-libp2p/p2p/host/basic NAT" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package basichost -destination mock_nat_test.go github.com/libp2p/go-libp2p/p2p/host/basic NAT" type NAT nat diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/host/blank/blank.go b/vendor/github.com/libp2p/go-libp2p/p2p/host/blank/blank.go index 24304498b..0fdded30f 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/host/blank/blank.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/host/blank/blank.go @@ -210,7 +210,7 @@ func (bh *BlankHost) newStreamHandler(s network.Stream) { s.SetProtocol(protoID) - go handle(protoID, s) + handle(protoID, s) } // TODO: i'm not sure this really needs to be here diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/host/resource-manager/README.md b/vendor/github.com/libp2p/go-libp2p/p2p/host/resource-manager/README.md index 9371832c9..72eb192ad 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/host/resource-manager/README.md +++ b/vendor/github.com/libp2p/go-libp2p/p2p/host/resource-manager/README.md @@ -541,7 +541,7 @@ works best. ## Examples -Here we consider some concrete examples that can ellucidate the abstract +Here we consider some concrete examples that can elucidate the abstract design as described so far. ### Stream Lifetime @@ -578,7 +578,7 @@ More specifically the following constraints apply: - the peer scope, where the limits for the peer at the other end of the stream apply. - the service scope, where the limits of the specific service owning the stream apply. -- the protcol scope, where the limits of the specific protocol for the stream apply. +- the protocol scope, where the limits of the specific protocol for the stream apply. The resource transfer that happens in the `SetProtocol` and `SetService` diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/host/resource-manager/extapi.go b/vendor/github.com/libp2p/go-libp2p/p2p/host/resource-manager/extapi.go index 03edcd79e..b3214f814 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/host/resource-manager/extapi.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/host/resource-manager/extapi.go @@ -145,3 +145,7 @@ func (r *resourceManager) Stat() (result ResourceManagerStat) { return result } + +func (r *resourceManager) GetConnLimit() int { + return r.limits.GetConnLimits().GetConnTotalLimit() +} diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/metricshelper/conn.go b/vendor/github.com/libp2p/go-libp2p/p2p/metricshelper/conn.go index ef367ac9b..b07016ce8 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/metricshelper/conn.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/metricshelper/conn.go @@ -2,7 +2,7 @@ package metricshelper import ma "github.com/multiformats/go-multiaddr" -var transports = [...]int{ma.P_CIRCUIT, ma.P_WEBRTC, ma.P_WEBTRANSPORT, ma.P_QUIC, ma.P_QUIC_V1, ma.P_WSS, ma.P_WS, ma.P_TCP} +var transports = [...]int{ma.P_CIRCUIT, ma.P_WEBRTC, ma.P_WEBRTC_DIRECT, ma.P_WEBTRANSPORT, ma.P_QUIC, ma.P_QUIC_V1, ma.P_WSS, ma.P_WS, ma.P_TCP} func GetTransport(a ma.Multiaddr) string { for _, t := range transports { diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/connmgr/connmgr.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/connmgr/connmgr.go index b42a122fa..6caa3dc1a 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/connmgr/connmgr.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/net/connmgr/connmgr.go @@ -2,6 +2,7 @@ package connmgr import ( "context" + "fmt" "sort" "sync" "sync/atomic" @@ -239,6 +240,17 @@ func (cm *BasicConnMgr) IsProtected(id peer.ID, tag string) (protected bool) { return protected } +func (cm *BasicConnMgr) CheckLimit(systemLimit connmgr.GetConnLimiter) error { + if cm.cfg.highWater > systemLimit.GetConnLimit() { + return fmt.Errorf( + "conn manager high watermark limit: %d, exceeds the system connection limit of: %d", + cm.cfg.highWater, + systemLimit.GetConnLimit(), + ) + } + return nil +} + // peerInfo stores metadata for a given peer. type peerInfo struct { id peer.ID diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/connmgr/decay.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/connmgr/decay.go index bdac0bef7..76f1c6872 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/connmgr/decay.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/net/connmgr/decay.go @@ -320,7 +320,7 @@ func (t *decayingTag) Bump(p peer.ID, delta int) error { default: return fmt.Errorf( "unable to bump decaying tag for peer %s, tag %s, delta %d; queue full (len=%d)", - p.Pretty(), t.name, delta, len(t.trkr.bumpTagCh)) + p, t.name, delta, len(t.trkr.bumpTagCh)) } } @@ -337,7 +337,7 @@ func (t *decayingTag) Remove(p peer.ID) error { default: return fmt.Errorf( "unable to remove decaying tag for peer %s, tag %s; queue full (len=%d)", - p.Pretty(), t.name, len(t.trkr.removeTagCh)) + p, t.name, len(t.trkr.removeTagCh)) } } diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/addrs.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/addrs.go deleted file mode 100644 index 392900e06..000000000 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/addrs.go +++ /dev/null @@ -1,39 +0,0 @@ -package swarm - -import ( - ma "github.com/multiformats/go-multiaddr" - manet "github.com/multiformats/go-multiaddr/net" -) - -// http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml -var lowTimeoutFilters = ma.NewFilters() - -func init() { - for _, p := range []string{ - "/ip4/10.0.0.0/ipcidr/8", - "/ip4/100.64.0.0/ipcidr/10", - "/ip4/169.254.0.0/ipcidr/16", - "/ip4/172.16.0.0/ipcidr/12", - "/ip4/192.0.0.0/ipcidr/24", - "/ip4/192.0.0.0/ipcidr/29", - "/ip4/192.0.0.8/ipcidr/32", - "/ip4/192.0.0.170/ipcidr/32", - "/ip4/192.0.0.171/ipcidr/32", - "/ip4/192.0.2.0/ipcidr/24", - "/ip4/192.168.0.0/ipcidr/16", - "/ip4/198.18.0.0/ipcidr/15", - "/ip4/198.51.100.0/ipcidr/24", - "/ip4/203.0.113.0/ipcidr/24", - "/ip4/240.0.0.0/ipcidr/4", - } { - f, err := ma.NewMultiaddr(p) - if err != nil { - panic("error in lowTimeoutFilters init: " + err.Error()) - } - ipnet, err := manet.MultiaddrToIPNet(f) - if err != nil { - panic("error in lowTimeoutFilters init: " + err.Error()) - } - lowTimeoutFilters.AddFilter(*ipnet, ma.ActionDeny) - } -} diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/black_hole_detector.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/black_hole_detector.go index 078b1126c..dd7849eea 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/black_hole_detector.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/black_hole_detector.go @@ -178,7 +178,7 @@ type blackHoleDetector struct { } // FilterAddrs filters the peer's addresses removing black holed addresses -func (d *blackHoleDetector) FilterAddrs(addrs []ma.Multiaddr) []ma.Multiaddr { +func (d *blackHoleDetector) FilterAddrs(addrs []ma.Multiaddr) (valid []ma.Multiaddr, blackHoled []ma.Multiaddr) { hasUDP, hasIPv6 := false, false for _, a := range addrs { if !manet.IsPublicAddr(a) { @@ -202,6 +202,7 @@ func (d *blackHoleDetector) FilterAddrs(addrs []ma.Multiaddr) []ma.Multiaddr { ipv6Res = d.ipv6.HandleRequest() } + blackHoled = make([]ma.Multiaddr, 0, len(addrs)) return ma.FilterAddrs( addrs, func(a ma.Multiaddr) bool { @@ -218,14 +219,16 @@ func (d *blackHoleDetector) FilterAddrs(addrs []ma.Multiaddr) []ma.Multiaddr { } if udpRes == blackHoleResultBlocked && isProtocolAddr(a, ma.P_UDP) { + blackHoled = append(blackHoled, a) return false } if ipv6Res == blackHoleResultBlocked && isProtocolAddr(a, ma.P_IP6) { + blackHoled = append(blackHoled, a) return false } return true }, - ) + ), blackHoled } // RecordResult updates the state of the relevant `blackHoleFilter`s for addr diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_error.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_error.go index 711ee0607..4de682204 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_error.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_error.go @@ -30,10 +30,7 @@ func (e *DialError) recordErr(addr ma.Multiaddr, err error) { e.Skipped++ return } - e.DialErrors = append(e.DialErrors, TransportError{ - Address: addr, - Cause: err, - }) + e.DialErrors = append(e.DialErrors, TransportError{Address: addr, Cause: err}) } func (e *DialError) Error() string { @@ -51,9 +48,19 @@ func (e *DialError) Error() string { return builder.String() } -// Unwrap implements https://godoc.org/golang.org/x/xerrors#Wrapper. -func (e *DialError) Unwrap() error { - return e.Cause +func (e *DialError) Unwrap() []error { + if e == nil { + return nil + } + + errs := make([]error, len(e.DialErrors)+1) + if e.Cause != nil { + errs = append(errs, e.Cause) + } + for i := 0; i < len(e.DialErrors); i++ { + errs = append(errs, &e.DialErrors[i]) + } + return errs } var _ error = (*DialError)(nil) @@ -68,4 +75,8 @@ func (e *TransportError) Error() string { return fmt.Sprintf("failed to dial %s: %s", e.Address, e.Cause) } +func (e *TransportError) Unwrap() error { + return e.Cause +} + var _ error = (*TransportError)(nil) diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_ranker.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_ranker.go index 3725884e2..7e58876b9 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_ranker.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_ranker.go @@ -58,8 +58,19 @@ func NoDelayDialRanker(addrs []ma.Multiaddr) []network.AddrDelay { // 3. If a QUIC or WebTransport address is present, TCP addresses dials are delayed relative to the last QUIC dial: // We prefer to end up with a QUIC connection. For public addresses, the delay introduced is 250ms (PublicTCPDelay), // and for private addresses 30ms (PrivateTCPDelay). +// 4. For the TCP addresses we follow a strategy similar to QUIC with an optimisation for handling the long TCP +// handshake time described in 6. If both IPv6 TCP and IPv4 TCP addresses are present, we do a Happy Eyeballs +// style ranking. First dial the IPv6 TCP address with the lowest port. After this, dial the IPv4 TCP address +// with the lowest port delayed by 250ms (PublicTCPDelay) for public addresses, and 30ms (PrivateTCPDelay) +// for local addresses. After this we dial all the rest of the addresses delayed by 250ms (PublicTCPDelay) for +// public addresses, and 30ms (PrivateTCPDelay) for local addresses. +// 5. If only one of TCP IPv6 or TCP IPv4 addresses are present, dial the TCP address with the lowest port +// first. After this we dial the rest of the TCP addresses delayed by 250ms (PublicTCPDelay) for public +// addresses, and 30ms (PrivateTCPDelay) for local addresses. +// 6. When a TCP socket is connected and awaiting security and muxer upgrade, we stop new dials for 2*PrivateTCPDelay +// to allow for the upgrade to complete. // -// We dial lowest ports first for QUIC addresses as they are more likely to be the listen port. +// We dial lowest ports first as they are more likely to be the listen port. func DefaultDialRanker(addrs []ma.Multiaddr) []network.AddrDelay { relay, addrs := filterAddrs(addrs, isRelayAddr) pvt, addrs := filterAddrs(addrs, manet.IsPrivateAddr) @@ -88,22 +99,57 @@ func DefaultDialRanker(addrs []ma.Multiaddr) []network.AddrDelay { // addresses relative to direct addresses. func getAddrDelay(addrs []ma.Multiaddr, tcpDelay time.Duration, quicDelay time.Duration, offset time.Duration) []network.AddrDelay { + if len(addrs) == 0 { + return nil + } sort.Slice(addrs, func(i, j int) bool { return score(addrs[i]) < score(addrs[j]) }) - // If the first address is (QUIC, IPv6), make the second address (QUIC, IPv4). - happyEyeballs := false - if len(addrs) > 0 { + // addrs is now sorted by (Transport, IPVersion). Reorder addrs for happy eyeballs dialing. + // For QUIC and TCP, if we have both IPv6 and IPv4 addresses, move the + // highest priority IPv4 address to the second position. + happyEyeballsQUIC := false + happyEyeballsTCP := false + // tcpStartIdx is the index of the first TCP Address + var tcpStartIdx int + { + i := 0 + // If the first QUIC address is IPv6 move the first QUIC IPv4 address to second position if isQUICAddr(addrs[0]) && isProtocolAddr(addrs[0], ma.P_IP6) { - for i := 1; i < len(addrs); i++ { - if isQUICAddr(addrs[i]) && isProtocolAddr(addrs[i], ma.P_IP4) { - // make IPv4 address the second element - if i > 1 { - a := addrs[i] - copy(addrs[2:], addrs[1:i]) + for j := 1; j < len(addrs); j++ { + if isQUICAddr(addrs[j]) && isProtocolAddr(addrs[j], ma.P_IP4) { + // The first IPv4 address is at position j + // Move the jth element at position 1 shifting the affected elements + if j > 1 { + a := addrs[j] + copy(addrs[2:], addrs[1:j]) addrs[1] = a } - happyEyeballs = true + happyEyeballsQUIC = true + i = j + 1 + break + } + } + } + + for tcpStartIdx = i; tcpStartIdx < len(addrs); tcpStartIdx++ { + if isProtocolAddr(addrs[tcpStartIdx], ma.P_TCP) { + break + } + } + + // If the first TCP address is IPv6 move the first TCP IPv4 address to second position + if tcpStartIdx < len(addrs) && isProtocolAddr(addrs[tcpStartIdx], ma.P_IP6) { + for j := tcpStartIdx + 1; j < len(addrs); j++ { + if isProtocolAddr(addrs[j], ma.P_TCP) && isProtocolAddr(addrs[j], ma.P_IP4) { + // First TCP IPv4 address is at position j, move it to position tcpStartIdx+1 + // which is the second priority TCP address + if j > tcpStartIdx+1 { + a := addrs[j] + copy(addrs[tcpStartIdx+2:], addrs[tcpStartIdx+1:j]) + addrs[tcpStartIdx+1] = a + } + happyEyeballsTCP = true break } } @@ -111,25 +157,42 @@ func getAddrDelay(addrs []ma.Multiaddr, tcpDelay time.Duration, quicDelay time.D } res := make([]network.AddrDelay, 0, len(addrs)) - - var totalTCPDelay time.Duration + var tcpFirstDialDelay time.Duration for i, addr := range addrs { var delay time.Duration switch { case isQUICAddr(addr): - // For QUIC addresses we dial an IPv6 address, then after quicDelay an IPv4 - // address, then after quicDelay we dial rest of the addresses. + // We dial an IPv6 address, then after quicDelay an IPv4 + // address, then after a further quicDelay we dial the rest of the addresses. if i == 1 { delay = quicDelay } - if i > 1 && happyEyeballs { - delay = 2 * quicDelay - } else if i > 1 { - delay = quicDelay + if i > 1 { + // If we have happy eyeballs for QUIC, dials after the second position + // will be delayed by 2*quicDelay + if happyEyeballsQUIC { + delay = 2 * quicDelay + } else { + delay = quicDelay + } } - totalTCPDelay = delay + tcpDelay + tcpFirstDialDelay = delay + tcpDelay case isProtocolAddr(addr, ma.P_TCP): - delay = totalTCPDelay + // We dial an IPv6 address, then after tcpDelay an IPv4 + // address, then after a further tcpDelay we dial the rest of the addresses. + if i == tcpStartIdx+1 { + delay = tcpDelay + } + if i > tcpStartIdx+1 { + // If we have happy eyeballs for TCP, dials after the second position + // will be delayed by 2*tcpDelay + if happyEyeballsTCP { + delay = 2 * tcpDelay + } else { + delay = tcpDelay + } + } + delay += tcpFirstDialDelay } res = append(res, network.AddrDelay{Addr: addr, Delay: offset + delay}) } diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_sync.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_sync.go index 2a8ff4317..3cc854728 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_sync.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_sync.go @@ -2,6 +2,7 @@ package swarm import ( "context" + "errors" "sync" "github.com/libp2p/go-libp2p/core/network" @@ -11,6 +12,9 @@ import ( // dialWorkerFunc is used by dialSync to spawn a new dial worker type dialWorkerFunc func(peer.ID, <-chan dialRequest) +// errConcurrentDialSuccessful is used to signal that a concurrent dial succeeded +var errConcurrentDialSuccessful = errors.New("concurrent dial successful") + // newDialSync constructs a new dialSync func newDialSync(worker dialWorkerFunc) *dialSync { return &dialSync{ @@ -30,17 +34,12 @@ type dialSync struct { type activeDial struct { refCnt int - ctx context.Context - cancel func() + ctx context.Context + cancelCause func(error) reqch chan dialRequest } -func (ad *activeDial) close() { - ad.cancel() - close(ad.reqch) -} - func (ad *activeDial) dial(ctx context.Context) (*Conn, error) { dialCtx := ad.ctx @@ -74,11 +73,11 @@ func (ds *dialSync) getActiveDial(p peer.ID) (*activeDial, error) { if !ok { // This code intentionally uses the background context. Otherwise, if the first call // to Dial is canceled, subsequent dial calls will also be canceled. - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancelCause(context.Background()) actd = &activeDial{ - ctx: ctx, - cancel: cancel, - reqch: make(chan dialRequest), + ctx: ctx, + cancelCause: cancel, + reqch: make(chan dialRequest), } go ds.dialWorker(p, actd.reqch) ds.dials[p] = actd @@ -96,14 +95,21 @@ func (ds *dialSync) Dial(ctx context.Context, p peer.ID) (*Conn, error) { return nil, err } - defer func() { - ds.mutex.Lock() - defer ds.mutex.Unlock() - ad.refCnt-- - if ad.refCnt == 0 { - ad.close() - delete(ds.dials, p) + conn, err := ad.dial(ctx) + + ds.mutex.Lock() + defer ds.mutex.Unlock() + + ad.refCnt-- + if ad.refCnt == 0 { + if err == nil { + ad.cancelCause(errConcurrentDialSuccessful) + } else { + ad.cancelCause(err) } - }() - return ad.dial(ctx) + close(ad.reqch) + delete(ds.dials, p) + } + + return conn, err } diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_worker.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_worker.go index 0334ac863..0cac6e4fa 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_worker.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/dial_worker.go @@ -8,15 +8,12 @@ import ( "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" + tpt "github.com/libp2p/go-libp2p/core/transport" ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" ) -// ///////////////////////////////////////////////////////////////////////////////// -// lo and behold, The Dialer -// TODO explain how all this works -// //////////////////////////////////////////////////////////////////////////////// - // dialRequest is structure used to request dials to the peer associated with a // worker loop type dialRequest struct { @@ -61,15 +58,14 @@ type addrDial struct { conn *Conn // err is the err on dialing the address err error - // requests is the list of pendRequests interested in this dial - // the value in the slice is the request number assigned to this request by the dialWorker - requests []int // dialed indicates whether we have triggered the dial to the address dialed bool // createdAt is the time this struct was created createdAt time.Time // dialRankingDelay is the delay in dialing this address introduced by the ranking logic dialRankingDelay time.Duration + // expectedTCPUpgradeTime is the expected time by which security upgrade will complete + expectedTCPUpgradeTime time.Time } // dialWorker synchronises concurrent dials to a peer. It ensures that we make at most one dial to a @@ -79,17 +75,13 @@ type dialWorker struct { peer peer.ID // reqch is used to send dial requests to the worker. close reqch to end the worker loop reqch <-chan dialRequest - // reqno is the request number used to track different dialRequests for a peer. - // Each incoming request is assigned a reqno. This reqno is used in pendingRequests and in - // addrDial objects in trackedDials to track this request - reqno int - // pendingRequests maps reqno to the pendRequest object for a dialRequest - pendingRequests map[int]*pendRequest - // trackedDials tracks dials to the peers addresses. An entry here is used to ensure that + // pendingRequests is the set of pendingRequests + pendingRequests map[*pendRequest]struct{} + // trackedDials tracks dials to the peer's addresses. An entry here is used to ensure that // we dial an address at most once trackedDials map[string]*addrDial // resch is used to receive response for dials to the peers addresses. - resch chan dialResult + resch chan tpt.DialUpdate connected bool // true when a connection has been successfully established @@ -106,9 +98,9 @@ func newDialWorker(s *Swarm, p peer.ID, reqch <-chan dialRequest, cl Clock) *dia s: s, peer: p, reqch: reqch, - pendingRequests: make(map[int]*pendRequest), + pendingRequests: make(map[*pendRequest]struct{}), trackedDials: make(map[string]*addrDial), - resch: make(chan dialResult), + resch: make(chan tpt.DialUpdate), cl: cl, } } @@ -128,6 +120,8 @@ func (w *dialWorker) loop() { startTime := w.cl.Now() // dialTimer is the dialTimer used to trigger dials dialTimer := w.cl.InstantTimer(startTime.Add(math.MaxInt64)) + defer dialTimer.Stop() + timerRunning := true // scheduleNextDial updates timer for triggering the next dial scheduleNextDial := func() { @@ -135,12 +129,18 @@ func (w *dialWorker) loop() { <-dialTimer.Ch() } timerRunning = false - if dq.len() > 0 { + if dq.Len() > 0 { if dialsInFlight == 0 && !w.connected { // if there are no dials in flight, trigger the next dials immediately dialTimer.Reset(startTime) } else { - dialTimer.Reset(startTime.Add(dq.top().Delay)) + resetTime := startTime.Add(dq.top().Delay) + for _, ad := range w.trackedDials { + if !ad.expectedTCPUpgradeTime.IsZero() && ad.expectedTCPUpgradeTime.After(resetTime) { + resetTime = ad.expectedTCPUpgradeTime + } + } + dialTimer.Reset(resetTime) } timerRunning = true } @@ -171,15 +171,20 @@ loop: // Enqueue the peer's addresses relevant to this request in dq and // track dials to the addresses relevant to this request. - c, err := w.s.bestAcceptableConnToPeer(req.ctx, w.peer) - if c != nil || err != nil { - req.resch <- dialResponse{conn: c, err: err} + c := w.s.bestAcceptableConnToPeer(req.ctx, w.peer) + if c != nil { + req.resch <- dialResponse{conn: c} continue loop } - addrs, err := w.s.addrsForDial(req.ctx, w.peer) + addrs, addrErrs, err := w.s.addrsForDial(req.ctx, w.peer) if err != nil { - req.resch <- dialResponse{err: err} + req.resch <- dialResponse{ + err: &DialError{ + Peer: w.peer, + DialErrors: addrErrs, + Cause: err, + }} continue loop } @@ -191,8 +196,8 @@ loop: // create the pending request object pr := &pendRequest{ req: req, - err: &DialError{Peer: w.peer}, addrs: make(map[string]struct{}, len(addrRanking)), + err: &DialError{Peer: w.peer, DialErrors: addrErrs}, } for _, adelay := range addrRanking { pr.addrs[string(adelay.Addr.Bytes())] = struct{}{} @@ -233,14 +238,13 @@ loop: if len(todial) == 0 && len(tojoin) == 0 { // all request applicable addrs have been dialed, we must have errored + pr.err.Cause = ErrAllDialsFailed req.resch <- dialResponse{err: pr.err} continue loop } - // The request has some pending or new dials. We assign this request a request number. - // This value of w.reqno is used to track this request in all the structures - w.reqno++ - w.pendingRequests[w.reqno] = pr + // The request has some pending or new dials + w.pendingRequests[pr] = struct{}{} for _, ad := range tojoin { if !ad.dialed { @@ -258,7 +262,6 @@ loop: } } // add the request to the addrDial - ad.requests = append(ad.requests, w.reqno) } if len(todial) > 0 { @@ -268,7 +271,6 @@ loop: w.trackedDials[string(a.Bytes())] = &addrDial{ addr: a, ctx: req.ctx, - requests: []int{w.reqno}, createdAt: now, } dq.Add(network.AddrDelay{Addr: a, Delay: addrDelay[string(a.Bytes())]}) @@ -313,16 +315,29 @@ loop: // Update all requests waiting on this address. On success, complete the request. // On error, record the error - dialsInFlight-- ad, ok := w.trackedDials[string(res.Addr.Bytes())] if !ok { log.Errorf("SWARM BUG: no entry for address %s in trackedDials", res.Addr) if res.Conn != nil { res.Conn.Close() } + dialsInFlight-- continue } + // TCP Connection has been established. Wait for connection upgrade on this address + // before making new dials. + if res.Kind == tpt.UpdateKindHandshakeProgressed { + // Only wait for public addresses to complete dialing since private dials + // are quick any way + if manet.IsPublicAddr(res.Addr) { + ad.expectedTCPUpgradeTime = w.cl.Now().Add(PublicTCPDelay) + } + scheduleNextDial() + continue + } + dialsInFlight-- + ad.expectedTCPUpgradeTime = time.Time{} if res.Conn != nil { // we got a connection, add it to the swarm conn, err := w.s.addConn(res.Conn, network.DirOutbound) @@ -333,20 +348,14 @@ loop: continue loop } - // request succeeded, respond to all pending requests - for _, reqno := range ad.requests { - pr, ok := w.pendingRequests[reqno] - if !ok { - // some other dial for this request succeeded before this one - continue + for pr := range w.pendingRequests { + if _, ok := pr.addrs[string(ad.addr.Bytes())]; ok { + pr.req.resch <- dialResponse{conn: conn} + delete(w.pendingRequests, pr) } - pr.req.resch <- dialResponse{conn: conn} - delete(w.pendingRequests, reqno) } ad.conn = conn - ad.requests = nil - if !w.connected { w.connected = true if w.s.metricsTracer != nil { @@ -380,33 +389,27 @@ loop: // dispatches an error to a specific addr dial func (w *dialWorker) dispatchError(ad *addrDial, err error) { ad.err = err - for _, reqno := range ad.requests { - pr, ok := w.pendingRequests[reqno] - if !ok { - // some other dial for this request succeeded before this one - continue - } - + for pr := range w.pendingRequests { // accumulate the error - pr.err.recordErr(ad.addr, err) - - delete(pr.addrs, string(ad.addr.Bytes())) - if len(pr.addrs) == 0 { - // all addrs have erred, dispatch dial error - // but first do a last one check in case an acceptable connection has landed from - // a simultaneous dial that started later and added new acceptable addrs - c, _ := w.s.bestAcceptableConnToPeer(pr.req.ctx, w.peer) - if c != nil { - pr.req.resch <- dialResponse{conn: c} - } else { - pr.req.resch <- dialResponse{err: pr.err} + if _, ok := pr.addrs[string(ad.addr.Bytes())]; ok { + pr.err.recordErr(ad.addr, err) + delete(pr.addrs, string(ad.addr.Bytes())) + if len(pr.addrs) == 0 { + // all addrs have erred, dispatch dial error + // but first do a last one check in case an acceptable connection has landed from + // a simultaneous dial that started later and added new acceptable addrs + c := w.s.bestAcceptableConnToPeer(pr.req.ctx, w.peer) + if c != nil { + pr.req.resch <- dialResponse{conn: c} + } else { + pr.err.Cause = ErrAllDialsFailed + pr.req.resch <- dialResponse{err: pr.err} + } + delete(w.pendingRequests, pr) } - delete(w.pendingRequests, reqno) } } - ad.requests = nil - // if it was a backoff, clear the address dial so that it doesn't inhibit new dial requests. // this is necessary to support active listen scenarios, where a new dial comes in while // another dial is in progress, and needs to do a direct connection without inhibitions from @@ -439,7 +442,7 @@ func newDialQueue() *dialQueue { // Add adds adelay to the queue. If another element exists in the queue with // the same address, it replaces that element. func (dq *dialQueue) Add(adelay network.AddrDelay) { - for i := 0; i < dq.len(); i++ { + for i := 0; i < dq.Len(); i++ { if dq.q[i].Addr.Equal(adelay.Addr) { if dq.q[i].Delay == adelay.Delay { // existing element is the same. nothing to do @@ -452,7 +455,7 @@ func (dq *dialQueue) Add(adelay network.AddrDelay) { } } - for i := 0; i < dq.len(); i++ { + for i := 0; i < dq.Len(); i++ { if dq.q[i].Delay > adelay.Delay { dq.q = append(dq.q, network.AddrDelay{}) // extend the slice copy(dq.q[i+1:], dq.q[i:]) @@ -465,13 +468,13 @@ func (dq *dialQueue) Add(adelay network.AddrDelay) { // NextBatch returns all the elements in the queue with the highest priority func (dq *dialQueue) NextBatch() []network.AddrDelay { - if dq.len() == 0 { + if dq.Len() == 0 { return nil } // i is the index of the second highest priority element var i int - for i = 0; i < dq.len(); i++ { + for i = 0; i < dq.Len(); i++ { if dq.q[i].Delay != dq.q[0].Delay { break } @@ -486,7 +489,7 @@ func (dq *dialQueue) top() network.AddrDelay { return dq.q[0] } -// len returns the number of elements in the queue -func (dq *dialQueue) len() int { +// Len returns the number of elements in the queue +func (dq *dialQueue) Len() int { return len(dq.q) } diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/limiter.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/limiter.go index ccfe7d237..f5638bbe7 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/limiter.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/limiter.go @@ -13,17 +13,11 @@ import ( ma "github.com/multiformats/go-multiaddr" ) -type dialResult struct { - Conn transport.CapableConn - Addr ma.Multiaddr - Err error -} - type dialJob struct { addr ma.Multiaddr peer peer.ID ctx context.Context - resp chan dialResult + resp chan transport.DialUpdate timeout time.Duration } @@ -45,7 +39,7 @@ type dialLimiter struct { waitingOnPeerLimit map[peer.ID][]*dialJob } -type dialfunc func(context.Context, peer.ID, ma.Multiaddr) (transport.CapableConn, error) +type dialfunc func(context.Context, peer.ID, ma.Multiaddr, chan<- transport.DialUpdate) (transport.CapableConn, error) func newDialLimiter(df dialfunc) *dialLimiter { fd := ConcurrentFdDials @@ -216,9 +210,13 @@ func (dl *dialLimiter) executeDial(j *dialJob) { dctx, cancel := context.WithTimeout(j.ctx, j.timeout) defer cancel() - con, err := dl.dialFunc(dctx, j.peer, j.addr) + con, err := dl.dialFunc(dctx, j.peer, j.addr, j.resp) + kind := transport.UpdateKindDialSuccessful + if err != nil { + kind = transport.UpdateKindDialFailed + } select { - case j.resp <- dialResult{Conn: con, Addr: j.addr, Err: err}: + case j.resp <- transport.DialUpdate{Kind: kind, Conn: con, Addr: j.addr, Err: err}: case <-j.ctx.Done(): if con != nil { con.Close() diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm.go index 5155cd222..a76edce6c 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm.go @@ -17,6 +17,7 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peerstore" "github.com/libp2p/go-libp2p/core/transport" + "golang.org/x/exp/slices" logging "github.com/ipfs/go-log/v2" ma "github.com/multiformats/go-multiaddr" @@ -136,8 +137,8 @@ func WithIPv6BlackHoleConfig(enabled bool, n, min int) Option { // communication. The Chan sends/receives Messages, which note the // destination or source Peer. type Swarm struct { - nextConnID uint64 // guarded by atomic - nextStreamID uint64 // guarded by atomic + nextConnID atomic.Uint64 + nextStreamID atomic.Uint64 // Close refcount. This allows us to fully wait for the swarm to be torn // down before continuing. @@ -172,6 +173,11 @@ type Swarm struct { m map[network.Notifiee]struct{} } + directConnNotifs struct { + sync.Mutex + m map[peer.ID][]chan struct{} + } + transports struct { sync.RWMutex m map[int]transport.Transport @@ -231,6 +237,7 @@ func NewSwarm(local peer.ID, peers peerstore.Peerstore, eventBus event.Bus, opts s.listeners.m = make(map[transport.Listener]struct{}) s.transports.m = make(map[int]transport.Transport) s.notifs.m = make(map[network.Notifiee]struct{}) + s.directConnNotifs.m = make(map[peer.ID][]chan struct{}) for _, opt := range opts { if err := opt(s); err != nil { @@ -343,7 +350,7 @@ func (s *Swarm) addConn(tc transport.CapableConn, dir network.Direction) (*Conn, conn: tc, swarm: s, stat: stat, - id: atomic.AddUint64(&s.nextConnID, 1), + id: s.nextConnID.Add(1), } // we ONLY check upgraded connections here so we can send them a Disconnect message. @@ -353,7 +360,7 @@ func (s *Swarm) addConn(tc transport.CapableConn, dir network.Direction) (*Conn, // TODO Send disconnect with reason here err := tc.Close() if err != nil { - log.Warnf("failed to close connection with peer %s and addr %s; err: %s", p.Pretty(), addr, err) + log.Warnf("failed to close connection with peer %s and addr %s; err: %s", p, addr, err) } return nil, ErrGaterDisallowedConnection } @@ -390,6 +397,19 @@ func (s *Swarm) addConn(tc transport.CapableConn, dir network.Direction) (*Conn, c.notifyLk.Lock() s.conns.Unlock() + // Notify goroutines waiting for a direct connection + if !c.Stat().Transient { + // Go routines interested in waiting for direct connection first acquire this lock + // and then acquire s.conns.RLock. Do not acquire this lock before conns.Unlock to + // prevent deadlock. + s.directConnNotifs.Lock() + for _, ch := range s.directConnNotifs.m[p] { + close(ch) + } + delete(s.directConnNotifs.m, p) + s.directConnNotifs.Unlock() + } + // Emit event after releasing `s.conns` lock so that a consumer can still // use swarm methods that need the `s.conns` lock. if isFirstConnection { @@ -429,54 +449,110 @@ func (s *Swarm) StreamHandler() network.StreamHandler { // NewStream creates a new stream on any available connection to peer, dialing // if necessary. +// Use network.WithUseTransient to open a stream over a transient(relayed) +// connection. func (s *Swarm) NewStream(ctx context.Context, p peer.ID) (network.Stream, error) { log.Debugf("[%s] opening stream to peer [%s]", s.local, p) // Algorithm: // 1. Find the best connection, otherwise, dial. - // 2. Try opening a stream. - // 3. If the underlying connection is, in fact, closed, close the outer + // 2. If the best connection is transient, wait for a direct conn via conn + // reversal or hole punching. + // 3. Try opening a stream. + // 4. If the underlying connection is, in fact, closed, close the outer // connection and try again. We do this in case we have a closed // connection but don't notice it until we actually try to open a // stream. // - // Note: We only dial once. - // // TODO: Try all connections even if we get an error opening a stream on // a non-closed connection. - dials := 0 + numDials := 0 for { - // will prefer direct connections over relayed connections for opening streams - c, err := s.bestAcceptableConnToPeer(ctx, p) - if err != nil { - return nil, err - } - + c := s.bestConnToPeer(p) if c == nil { - if nodial, _ := network.GetNoDial(ctx); nodial { + if nodial, _ := network.GetNoDial(ctx); !nodial { + numDials++ + if numDials > DialAttempts { + return nil, errors.New("max dial attempts exceeded") + } + var err error + c, err = s.dialPeer(ctx, p) + if err != nil { + return nil, err + } + } else { return nil, network.ErrNoConn } + } - if dials >= DialAttempts { - return nil, errors.New("max dial attempts exceeded") - } - dials++ - + useTransient, _ := network.GetUseTransient(ctx) + if !useTransient && c.Stat().Transient { var err error - c, err = s.dialPeer(ctx, p) + c, err = s.waitForDirectConn(ctx, p) if err != nil { return nil, err } } - s, err := c.NewStream(ctx) + str, err := c.NewStream(ctx) if err != nil { if c.conn.IsClosed() { continue } return nil, err } - return s, nil + return str, nil + } +} + +// waitForDirectConn waits for a direct connection established through hole punching or connection reversal. +func (s *Swarm) waitForDirectConn(ctx context.Context, p peer.ID) (*Conn, error) { + s.directConnNotifs.Lock() + c := s.bestConnToPeer(p) + if c == nil { + s.directConnNotifs.Unlock() + return nil, network.ErrNoConn + } else if !c.Stat().Transient { + s.directConnNotifs.Unlock() + return c, nil + } + + // Wait for transient connection to upgrade to a direct connection either by + // connection reversal or hole punching. + ch := make(chan struct{}) + s.directConnNotifs.m[p] = append(s.directConnNotifs.m[p], ch) + s.directConnNotifs.Unlock() + + // apply the DialPeer timeout + ctx, cancel := context.WithTimeout(ctx, network.GetDialPeerTimeout(ctx)) + defer cancel() + + // Wait for notification. + select { + case <-ctx.Done(): + // Remove ourselves from the notification list + s.directConnNotifs.Lock() + defer s.directConnNotifs.Unlock() + + s.directConnNotifs.m[p] = slices.DeleteFunc( + s.directConnNotifs.m[p], + func(c chan struct{}) bool { return c == ch }, + ) + if len(s.directConnNotifs.m[p]) == 0 { + delete(s.directConnNotifs.m, p) + } + return nil, ctx.Err() + case <-ch: + // We do not need to remove ourselves from the list here as the notifier + // clears the map entry + c := s.bestConnToPeer(p) + if c == nil { + return nil, network.ErrNoConn + } + if c.Stat().Transient { + return nil, network.ErrTransientConn + } + return c, nil } } @@ -548,26 +624,17 @@ func (s *Swarm) bestConnToPeer(p peer.ID) *Conn { return best } -// - Returns the best "acceptable" connection, if available. -// - Returns nothing if no such connection exists, but if we should try dialing anyways. -// - Returns an error if no such connection exists, but we should not try dialing. -func (s *Swarm) bestAcceptableConnToPeer(ctx context.Context, p peer.ID) (*Conn, error) { +// bestAcceptableConnToPeer returns the best acceptable connection, considering the passed in ctx. +// If network.WithForceDirectDial is used, it only returns a direct connections, ignoring +// any transient (relayed) connections to the peer. +func (s *Swarm) bestAcceptableConnToPeer(ctx context.Context, p peer.ID) *Conn { conn := s.bestConnToPeer(p) - if conn == nil { - return nil, nil - } forceDirect, _ := network.GetForceDirectDial(ctx) if forceDirect && !isDirectConn(conn) { - return nil, nil + return nil } - - useTransient, _ := network.GetUseTransient(ctx) - if useTransient || !conn.Stat().Transient { - return conn, nil - } - - return nil, network.ErrTransientConn + return conn } func isDirectConn(c *Conn) bool { diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_conn.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_conn.go index e770381a2..8c3ce7c5a 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_conn.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_conn.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "sync" - "sync/atomic" "time" ic "github.com/libp2p/go-libp2p/core/crypto" @@ -49,7 +48,7 @@ func (c *Conn) IsClosed() bool { func (c *Conn) ID() string { // format: - - return fmt.Sprintf("%s-%d", c.RemotePeer().Pretty()[0:10], c.id) + return fmt.Sprintf("%s-%d", c.RemotePeer().String()[:10], c.id) } // Close closes this connection. @@ -137,6 +136,7 @@ func (c *Conn) start() { if h := c.swarm.StreamHandler(); h != nil { h(s) } + s.completeAcceptStreamGoroutine() }() } }() @@ -147,9 +147,9 @@ func (c *Conn) String() string { " %s (%s)>", c.conn.Transport(), c.conn.LocalMultiaddr(), - c.conn.LocalPeer().Pretty(), + c.conn.LocalPeer(), c.conn.RemoteMultiaddr(), - c.conn.RemotePeer().Pretty(), + c.conn.RemotePeer(), ) } @@ -238,7 +238,8 @@ func (c *Conn) addStream(ts network.MuxedStream, dir network.Direction, scope ne Direction: dir, Opened: time.Now(), }, - id: atomic.AddUint64(&c.swarm.nextStreamID, 1), + id: c.swarm.nextStreamID.Add(1), + acceptStreamGoroutineCompleted: dir != network.DirInbound, } c.stat.NumStreams++ c.streams.m[s] = struct{}{} diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_dial.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_dial.go index f2df93af2..3fb15383a 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_dial.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_dial.go @@ -14,8 +14,10 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peerstore" "github.com/libp2p/go-libp2p/core/transport" + ma "github.com/multiformats/go-multiaddr" madns "github.com/multiformats/go-multiaddr-dns" + mafmt "github.com/multiformats/go-multiaddr-fmt" manet "github.com/multiformats/go-multiaddr/net" ) @@ -65,6 +67,19 @@ var ( ErrGaterDisallowedConnection = errors.New("gater disallows connection to peer") ) +// ErrQUICDraft29 wraps ErrNoTransport and provide a more meaningful error message +var ErrQUICDraft29 errQUICDraft29 + +type errQUICDraft29 struct{} + +func (errQUICDraft29) Error() string { + return "QUIC draft-29 has been removed, QUIC (RFC 9000) is accessible with /quic-v1" +} + +func (errQUICDraft29) Unwrap() error { + return ErrNoTransport +} + // DialAttempts governs how many times a goroutine will try to dial a given peer. // Note: this is down to one, as we have _too many dials_ atm. To add back in, // add loop back in Dial(.) @@ -201,7 +216,8 @@ func (db *DialBackoff) cleanup() { } } -// DialPeer connects to a peer. +// DialPeer connects to a peer. Use network.WithForceDirectDial to force a +// direct connection. // // The idea is that the client of Swarm does not need to know what network // the connection will happen over. Swarm can use whichever it choses. @@ -231,15 +247,14 @@ func (s *Swarm) dialPeer(ctx context.Context, p peer.ID) (*Conn, error) { return nil, ErrDialToSelf } - // check if we already have an open (usable) connection first, or can't have a usable - // connection. - conn, err := s.bestAcceptableConnToPeer(ctx, p) - if conn != nil || err != nil { - return conn, err + // check if we already have an open (usable) connection. + conn := s.bestAcceptableConnToPeer(ctx, p) + if conn != nil { + return conn, nil } if s.gater != nil && !s.gater.InterceptPeerDial(p) { - log.Debugf("gater disallowed outbound connection to peer %s", p.Pretty()) + log.Debugf("gater disallowed outbound connection to peer %s", p) return nil, &DialError{Peer: p, Cause: ErrGaterDisallowedConnection} } @@ -280,68 +295,47 @@ func (s *Swarm) dialWorkerLoop(p peer.ID, reqch <-chan dialRequest) { w.loop() } -func (s *Swarm) addrsForDial(ctx context.Context, p peer.ID) ([]ma.Multiaddr, error) { +func (s *Swarm) addrsForDial(ctx context.Context, p peer.ID) (goodAddrs []ma.Multiaddr, addrErrs []TransportError, err error) { peerAddrs := s.peers.Addrs(p) if len(peerAddrs) == 0 { - return nil, ErrNoAddresses - } - - peerAddrsAfterTransportResolved := make([]ma.Multiaddr, 0, len(peerAddrs)) - for _, a := range peerAddrs { - tpt := s.TransportForDialing(a) - resolver, ok := tpt.(transport.Resolver) - if ok { - resolvedAddrs, err := resolver.Resolve(ctx, a) - if err != nil { - log.Warnf("Failed to resolve multiaddr %s by transport %v: %v", a, tpt, err) - continue - } - peerAddrsAfterTransportResolved = append(peerAddrsAfterTransportResolved, resolvedAddrs...) - } else { - peerAddrsAfterTransportResolved = append(peerAddrsAfterTransportResolved, a) - } + return nil, nil, ErrNoAddresses } // Resolve dns or dnsaddrs - resolved, err := s.resolveAddrs(ctx, peer.AddrInfo{ - ID: p, - Addrs: peerAddrsAfterTransportResolved, - }) + resolved, err := s.resolveAddrs(ctx, peer.AddrInfo{ID: p, Addrs: peerAddrs}) if err != nil { - return nil, err + return nil, nil, err } - goodAddrs := s.filterKnownUndialables(p, resolved) + goodAddrs = ma.Unique(resolved) + goodAddrs, addrErrs = s.filterKnownUndialables(p, goodAddrs) if forceDirect, _ := network.GetForceDirectDial(ctx); forceDirect { goodAddrs = ma.FilterAddrs(goodAddrs, s.nonProxyAddr) } - goodAddrs = ma.Unique(goodAddrs) if len(goodAddrs) == 0 { - return nil, ErrNoGoodAddresses + return nil, addrErrs, ErrNoGoodAddresses } s.peers.AddAddrs(p, goodAddrs, peerstore.TempAddrTTL) - return goodAddrs, nil + return goodAddrs, addrErrs, nil } func (s *Swarm) resolveAddrs(ctx context.Context, pi peer.AddrInfo) ([]ma.Multiaddr, error) { - proto := ma.ProtocolWithCode(ma.P_P2P).Name - p2paddr, err := ma.NewMultiaddr("/" + proto + "/" + pi.ID.Pretty()) + p2paddr, err := ma.NewMultiaddr("/" + ma.ProtocolWithCode(ma.P_P2P).Name + "/" + pi.ID.String()) if err != nil { return nil, err } - resolveSteps := 0 - + var resolveSteps int // Recursively resolve all addrs. // // While the toResolve list is non-empty: // * Pop an address off. // * If the address is fully resolved, add it to the resolved list. // * Otherwise, resolve it and add the results to the "to resolve" list. - toResolve := append(([]ma.Multiaddr)(nil), pi.Addrs...) + toResolve := append([]ma.Multiaddr{}, pi.Addrs...) resolved := make([]ma.Multiaddr, 0, len(pi.Addrs)) for len(toResolve) > 0 { // pop the last addr off. @@ -368,6 +362,26 @@ func (s *Swarm) resolveAddrs(ctx context.Context, pi peer.AddrInfo) ([]ma.Multia continue } + tpt := s.TransportForDialing(addr) + resolver, ok := tpt.(transport.Resolver) + if ok { + resolvedAddrs, err := resolver.Resolve(ctx, addr) + if err != nil { + log.Warnf("Failed to resolve multiaddr %s by transport %v: %v", addr, tpt, err) + continue + } + var added bool + for _, a := range resolvedAddrs { + if !addr.Equal(a) { + toResolve = append(toResolve, a) + added = true + } + } + if added { + continue + } + } + // otherwise, resolve it reqaddr := addr.Encapsulate(p2paddr) resaddrs, err := s.maResolver.Resolve(ctx, reqaddr) @@ -388,7 +402,7 @@ func (s *Swarm) resolveAddrs(ctx context.Context, pi peer.AddrInfo) ([]ma.Multia return resolved, nil } -func (s *Swarm) dialNextAddr(ctx context.Context, p peer.ID, addr ma.Multiaddr, resch chan dialResult) error { +func (s *Swarm) dialNextAddr(ctx context.Context, p peer.ID, addr ma.Multiaddr, resch chan transport.DialUpdate) error { // check the dial backoff if forceDirect, _ := network.GetForceDirectDial(ctx); !forceDirect { if s.backf.Backoff(p, addr) { @@ -402,23 +416,20 @@ func (s *Swarm) dialNextAddr(ctx context.Context, p peer.ID, addr ma.Multiaddr, return nil } -func (s *Swarm) canDial(addr ma.Multiaddr) bool { - t := s.TransportForDialing(addr) - return t != nil && t.CanDial(addr) -} - func (s *Swarm) nonProxyAddr(addr ma.Multiaddr) bool { t := s.TransportForDialing(addr) return !t.Proxy() } +var quicDraft29DialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_UDP), mafmt.Base(ma.P_QUIC)) + // filterKnownUndialables takes a list of multiaddrs, and removes those // that we definitely don't want to dial: addresses configured to be blocked, // IPv6 link-local addresses, addresses without a dial-capable transport, // addresses that we know to be our own, and addresses with a better tranport // available. This is an optimization to avoid wasting time on dials that we // know are going to fail or for which we have a better alternative. -func (s *Swarm) filterKnownUndialables(p peer.ID, addrs []ma.Multiaddr) []ma.Multiaddr { +func (s *Swarm) filterKnownUndialables(p peer.ID, addrs []ma.Multiaddr) (goodAddrs []ma.Multiaddr, addrErrs []TransportError) { lisAddrs, _ := s.InterfaceListenAddresses() var ourAddrs []ma.Multiaddr for _, addr := range lisAddrs { @@ -431,35 +442,71 @@ func (s *Swarm) filterKnownUndialables(p peer.ID, addrs []ma.Multiaddr) []ma.Mul }) } - // The order of these two filters is important. If we can only dial /webtransport, - // we don't want to filter /webtransport addresses out because the peer had a /quic-v1 - // address + addrErrs = make([]TransportError, 0, len(addrs)) - // filter addresses we cannot dial - addrs = ma.FilterAddrs(addrs, s.canDial) + // The order of checking for transport and filtering low priority addrs is important. If we + // can only dial /webtransport, we don't want to filter /webtransport addresses out because + // the peer had a /quic-v1 address + + // filter addresses with no transport + addrs = ma.FilterAddrs(addrs, func(a ma.Multiaddr) bool { + if s.TransportForDialing(a) == nil { + e := ErrNoTransport + // We used to support QUIC draft-29 for a long time. + // Provide a more useful error when attempting to dial a QUIC draft-29 address. + if quicDraft29DialMatcher.Matches(a) { + e = ErrQUICDraft29 + } + addrErrs = append(addrErrs, TransportError{Address: a, Cause: e}) + return false + } + return true + }) // filter low priority addresses among the addresses we can dial + // We don't return an error for these addresses addrs = filterLowPriorityAddresses(addrs) // remove black holed addrs - addrs = s.bhd.FilterAddrs(addrs) + addrs, blackHoledAddrs := s.bhd.FilterAddrs(addrs) + for _, a := range blackHoledAddrs { + addrErrs = append(addrErrs, TransportError{Address: a, Cause: ErrDialRefusedBlackHole}) + } return ma.FilterAddrs(addrs, - func(addr ma.Multiaddr) bool { return !ma.Contains(ourAddrs, addr) }, + // Linux and BSD treat an unspecified address when dialing as a localhost address. + // Windows doesn't support this. We filter all such addresses out because peers + // listening on unspecified addresses will advertise more specific addresses. + // https://unix.stackexchange.com/a/419881 + // https://superuser.com/a/1755455 + func(addr ma.Multiaddr) bool { + return !manet.IsIPUnspecified(addr) + }, + func(addr ma.Multiaddr) bool { + if ma.Contains(ourAddrs, addr) { + addrErrs = append(addrErrs, TransportError{Address: addr, Cause: ErrDialToSelf}) + return false + } + return true + }, // TODO: Consider allowing link-local addresses func(addr ma.Multiaddr) bool { return !manet.IsIP6LinkLocal(addr) }, func(addr ma.Multiaddr) bool { - return s.gater == nil || s.gater.InterceptAddrDial(p, addr) + if s.gater != nil && !s.gater.InterceptAddrDial(p, addr) { + addrErrs = append(addrErrs, TransportError{Address: addr, Cause: ErrGaterDisallowedConnection}) + return false + } + return true }, - ) + ), addrErrs } // limitedDial will start a dial to the given peer when // it is able, respecting the various different types of rate // limiting that occur without using extra goroutines per addr -func (s *Swarm) limitedDial(ctx context.Context, p peer.ID, a ma.Multiaddr, resp chan dialResult) { +func (s *Swarm) limitedDial(ctx context.Context, p peer.ID, a ma.Multiaddr, resp chan transport.DialUpdate) { timeout := s.dialTimeout - if lowTimeoutFilters.AddrBlocked(a) && s.dialTimeoutLocal < s.dialTimeout { + if manet.IsPrivateAddr(a) && s.dialTimeoutLocal < s.dialTimeout { timeout = s.dialTimeoutLocal } s.limiter.AddDialJob(&dialJob{ @@ -472,7 +519,7 @@ func (s *Swarm) limitedDial(ctx context.Context, p peer.ID, a ma.Multiaddr, resp } // dialAddr is the actual dial for an addr, indirectly invoked through the limiter -func (s *Swarm) dialAddr(ctx context.Context, p peer.ID, addr ma.Multiaddr) (transport.CapableConn, error) { +func (s *Swarm) dialAddr(ctx context.Context, p peer.ID, addr ma.Multiaddr, updCh chan<- transport.DialUpdate) (transport.CapableConn, error) { // Just to double check. Costs nothing. if s.local == p { return nil, ErrDialToSelf @@ -490,7 +537,13 @@ func (s *Swarm) dialAddr(ctx context.Context, p peer.ID, addr ma.Multiaddr) (tra } start := time.Now() - connC, err := tpt.Dial(ctx, addr, p) + var connC transport.CapableConn + var err error + if du, ok := tpt.(transport.DialUpdater); ok { + connC, err = du.DialWithUpdates(ctx, addr, p, updCh) + } else { + connC, err = tpt.Dial(ctx, addr, p) + } // We're recording any error as a failure here. // Notably, this also applies to cancelations (i.e. if another dial attempt was faster). @@ -499,7 +552,7 @@ func (s *Swarm) dialAddr(ctx context.Context, p peer.ID, addr ma.Multiaddr) (tra if err != nil { if s.metricsTracer != nil { - s.metricsTracer.FailedDialing(addr, err) + s.metricsTracer.FailedDialing(addr, err, context.Cause(ctx)) } return nil, err } diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_metrics.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_metrics.go index 28564e9e5..9cc34599d 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_metrics.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_metrics.go @@ -128,7 +128,7 @@ type MetricsTracer interface { OpenedConnection(network.Direction, crypto.PubKey, network.ConnectionState, ma.Multiaddr) ClosedConnection(network.Direction, time.Duration, network.ConnectionState, ma.Multiaddr) CompletedHandshake(time.Duration, network.ConnectionState, ma.Multiaddr) - FailedDialing(ma.Multiaddr, error) + FailedDialing(ma.Multiaddr, error, error) DialCompleted(success bool, totalDials int) DialRankingDelay(d time.Duration) UpdatedBlackHoleFilterState(name string, state blackHoleState, nextProbeAfter int, successFraction float64) @@ -216,18 +216,28 @@ func (m *metricsTracer) CompletedHandshake(t time.Duration, cs network.Connectio connHandshakeLatency.WithLabelValues(*tags...).Observe(t.Seconds()) } -func (m *metricsTracer) FailedDialing(addr ma.Multiaddr, err error) { +func (m *metricsTracer) FailedDialing(addr ma.Multiaddr, dialErr error, cause error) { transport := metricshelper.GetTransport(addr) e := "other" - if errors.Is(err, context.Canceled) { - e = "canceled" - } else if errors.Is(err, context.DeadlineExceeded) { + // dial deadline exceeded or the the parent contexts deadline exceeded + if errors.Is(dialErr, context.DeadlineExceeded) || errors.Is(cause, context.DeadlineExceeded) { e = "deadline" + } else if errors.Is(dialErr, context.Canceled) { + // dial was cancelled. + if errors.Is(cause, context.Canceled) { + // parent context was canceled + e = "application canceled" + } else if errors.Is(cause, errConcurrentDialSuccessful) { + e = "canceled: concurrent dial successful" + } else { + // something else + e = "canceled: other" + } } else { - nerr, ok := err.(net.Error) + nerr, ok := dialErr.(net.Error) if ok && nerr.Timeout() { e = "timeout" - } else if strings.Contains(err.Error(), "connect: connection refused") { + } else if strings.Contains(dialErr.Error(), "connect: connection refused") { e = "connection refused" } } diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_stream.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_stream.go index d372bcd8e..b7846adec 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_stream.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/net/swarm/swarm_stream.go @@ -22,7 +22,10 @@ type Stream struct { conn *Conn scope network.StreamManagementScope - closeOnce sync.Once + closeMx sync.Mutex + isClosed bool + // acceptStreamGoroutineCompleted indicates whether the goroutine handling the incoming stream has exited + acceptStreamGoroutineCompleted bool protocol atomic.Pointer[protocol.ID] @@ -76,7 +79,7 @@ func (s *Stream) Write(p []byte) (int, error) { // resources. func (s *Stream) Close() error { err := s.stream.Close() - s.closeOnce.Do(s.remove) + s.closeAndRemoveStream() return err } @@ -84,10 +87,25 @@ func (s *Stream) Close() error { // associated resources. func (s *Stream) Reset() error { err := s.stream.Reset() - s.closeOnce.Do(s.remove) + s.closeAndRemoveStream() return err } +func (s *Stream) closeAndRemoveStream() { + s.closeMx.Lock() + defer s.closeMx.Unlock() + if s.isClosed { + return + } + s.isClosed = true + // We don't want to keep swarm from closing till the stream handler has exited + s.conn.swarm.refs.Done() + // Cleanup the stream from connection only after the stream handler has completed + if s.acceptStreamGoroutineCompleted { + s.conn.removeStream(s) + } +} + // CloseWrite closes the stream for writing, flushing all data and sending an EOF. // This function does not free resources, call Close or Reset when done with the // stream. @@ -101,9 +119,16 @@ func (s *Stream) CloseRead() error { return s.stream.CloseRead() } -func (s *Stream) remove() { - s.conn.removeStream(s) - s.conn.swarm.refs.Done() +func (s *Stream) completeAcceptStreamGoroutine() { + s.closeMx.Lock() + defer s.closeMx.Unlock() + if s.acceptStreamGoroutineCompleted { + return + } + s.acceptStreamGoroutineCompleted = true + if s.isClosed { + s.conn.removeStream(s) + } } // Protocol returns the protocol negotiated on this stream (if set). diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/net/upgrader/upgrader.go b/vendor/github.com/libp2p/go-libp2p/p2p/net/upgrader/upgrader.go index d18c16ea0..3a6f8b9f5 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/net/upgrader/upgrader.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/net/upgrader/upgrader.go @@ -152,7 +152,8 @@ func (u *upgrader) upgrade(ctx context.Context, t transport.Transport, maconn ma return nil, ipnet.ErrNotInPrivateNetwork } - sconn, security, server, err := u.setupSecurity(ctx, conn, p, dir) + isServer := dir == network.DirInbound + sconn, security, err := u.setupSecurity(ctx, conn, p, isServer) if err != nil { conn.Close() return nil, fmt.Errorf("failed to negotiate security protocol: %w", err) @@ -179,7 +180,7 @@ func (u *upgrader) upgrade(ctx context.Context, t transport.Transport, maconn ma } } - muxer, smconn, err := u.setupMuxer(ctx, sconn, server, connScope.PeerScope()) + muxer, smconn, err := u.setupMuxer(ctx, sconn, isServer, connScope.PeerScope()) if err != nil { sconn.Close() return nil, fmt.Errorf("failed to negotiate stream multiplexer: %w", err) @@ -199,20 +200,17 @@ func (u *upgrader) upgrade(ctx context.Context, t transport.Transport, maconn ma return tc, nil } -func (u *upgrader) setupSecurity(ctx context.Context, conn net.Conn, p peer.ID, dir network.Direction) (sec.SecureConn, protocol.ID, bool, error) { - isServer := dir == network.DirInbound - var st sec.SecureTransport - var err error - st, isServer, err = u.negotiateSecurity(ctx, conn, isServer) +func (u *upgrader) setupSecurity(ctx context.Context, conn net.Conn, p peer.ID, isServer bool) (sec.SecureConn, protocol.ID, error) { + st, err := u.negotiateSecurity(ctx, conn, isServer) if err != nil { - return nil, "", false, err + return nil, "", err } if isServer { sconn, err := st.SecureInbound(ctx, conn, p) - return sconn, st.ID(), true, err + return sconn, st.ID(), err } sconn, err := st.SecureOutbound(ctx, conn, p) - return sconn, st.ID(), false, err + return sconn, st.ID(), err } func (u *upgrader) negotiateMuxer(nc net.Conn, isServer bool) (*StreamMuxer, error) { @@ -308,41 +306,38 @@ func (u *upgrader) getSecurityByID(id protocol.ID) sec.SecureTransport { return nil } -func (u *upgrader) negotiateSecurity(ctx context.Context, insecure net.Conn, server bool) (sec.SecureTransport, bool, error) { +func (u *upgrader) negotiateSecurity(ctx context.Context, insecure net.Conn, server bool) (sec.SecureTransport, error) { type result struct { - proto protocol.ID - iamserver bool - err error + proto protocol.ID + err error } done := make(chan result, 1) go func() { if server { var r result - r.iamserver = true r.proto, _, r.err = u.securityMuxer.Negotiate(insecure) done <- r return } var r result - r.proto, r.iamserver, r.err = mss.SelectWithSimopenOrFail(u.securityIDs, insecure) + r.proto, r.err = mss.SelectOneOf(u.securityIDs, insecure) done <- r }() select { case r := <-done: if r.err != nil { - return nil, false, r.err + return nil, r.err } if s := u.getSecurityByID(r.proto); s != nil { - return s, r.iamserver, nil + return s, nil } - return nil, false, fmt.Errorf("selected unknown security transport: %s", r.proto) + return nil, fmt.Errorf("selected unknown security transport: %s", r.proto) case <-ctx.Done(): - // We *must* do this. We have outstanding work on the connection - // and it's no longer safe to use. + // We *must* do this. We have outstanding work on the connection, and it's no longer safe to use. insecure.Close() <-done // wait to stop using the connection. - return nil, false, ctx.Err() + return nil, ctx.Err() } } diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/constraints.go b/vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/constraints.go index 24353086f..4b9b54cd8 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/constraints.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay/constraints.go @@ -74,7 +74,10 @@ func (c *constraints) AddReservation(p peer.ID, a ma.Multiaddr) error { var asnReservations []time.Time var asn string - if ip.To4() == nil { + // Only public addresses have an ASN. Skip checking ASN for private addresses as + // initialising the ASN store is a costly operation. Skipping this check reduces a lot of + // flakiness in tests + if ip.To4() == nil && manet.IsPublicAddr(a) { asn, _ = asnutil.Store.AsnForIPv6(ip) if asn != "" { asnReservations = c.asns[asn] diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/id.go b/vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/id.go index 69506e9b3..6c07dbdc8 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/id.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/id.go @@ -556,7 +556,7 @@ func readAllIDMessages(r pbio.Reader, finalMsg proto.Message) error { func (ids *idService) updateSnapshot() (updated bool) { addrs := ids.Host.Addrs() - slices.SortFunc(addrs, func(a, b ma.Multiaddr) bool { return bytes.Compare(a.Bytes(), b.Bytes()) == -1 }) + slices.SortFunc(addrs, func(a, b ma.Multiaddr) int { return bytes.Compare(a.Bytes(), b.Bytes()) }) protos := ids.Host.Mux().Protocols() slices.Sort(protos) snapshot := identifySnapshot{ diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/obsaddr.go b/vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/obsaddr.go index 0412541f5..70a7eccd4 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/obsaddr.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/protocol/identify/obsaddr.go @@ -212,13 +212,16 @@ func (oas *ObservedAddrManager) filter(observedAddrs []*observedAddr) []ma.Multi for pat := range pmap { s := pmap[pat] - // We prefer inbound connection observations over outbound. - // For ties, we prefer the ones with more votes. - slices.SortFunc(s, func(first, second *observedAddr) bool { + slices.SortFunc(s, func(first, second *observedAddr) int { + // We prefer inbound connection observations over outbound. if first.numInbound > second.numInbound { - return true + return -1 } - return len(first.seenBy) > len(second.seenBy) + // For ties, we prefer the ones with more votes. + if first.numInbound == second.numInbound && len(first.seenBy) > len(second.seenBy) { + return -1 + } + return 1 }) for i := 0; i < maxObservedAddrsPerIPAndTransport && i < len(s); i++ { diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/security/noise/handshake.go b/vendor/github.com/libp2p/go-libp2p/p2p/security/noise/handshake.go index e1a18e9b6..a9493bf8d 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/security/noise/handshake.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/security/noise/handshake.go @@ -12,11 +12,12 @@ import ( "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/sec" + "github.com/libp2p/go-libp2p/internal/sha256" "github.com/libp2p/go-libp2p/p2p/security/noise/pb" "github.com/flynn/noise" pool "github.com/libp2p/go-buffer-pool" - "github.com/minio/sha256-simd" "google.golang.org/protobuf/proto" ) @@ -276,7 +277,7 @@ func (s *secureSession) handleRemoteHandshakePayload(payload []byte, remoteStati // check the peer ID if enabled if s.checkPeerID && s.remoteID != id { - return nil, fmt.Errorf("peer id mismatch: expected %s, but remote key matches %s", s.remoteID.Pretty(), id.Pretty()) + return nil, sec.ErrPeerIDMismatch{Expected: s.remoteID, Actual: id} } // verify payload is signed by asserted remote libp2p key. diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/security/tls/crypto.go b/vendor/github.com/libp2p/go-libp2p/p2p/security/tls/crypto.go index b8f23f39e..385de5a16 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/security/tls/crypto.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/security/tls/crypto.go @@ -18,6 +18,7 @@ import ( ic "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/sec" ) const certValidityPeriod = 100 * 365 * 24 * time.Hour // ~100 years @@ -129,7 +130,7 @@ func (i *Identity) ConfigForPeer(remote peer.ID) (*tls.Config, <-chan ic.PubKey) if err != nil { peerID = peer.ID(fmt.Sprintf("(not determined: %s)", err.Error())) } - return fmt.Errorf("peer IDs don't match: expected %s, got %s", remote, peerID) + return sec.ErrPeerIDMismatch{Expected: remote, Actual: peerID} } keyCh <- pubKey return nil diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/listener.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/listener.go index 73bb5026b..d49b68649 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/listener.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/listener.go @@ -29,9 +29,6 @@ type listener struct { func newListener(ln quicreuse.Listener, t *transport, localPeer peer.ID, key ic.PrivKey, rcmgr network.ResourceManager) (listener, error) { localMultiaddrs := make(map[quic.VersionNumber]ma.Multiaddr) for _, addr := range ln.Multiaddrs() { - if _, err := addr.ValueForProtocol(ma.P_QUIC); err == nil { - localMultiaddrs[quic.VersionDraft29] = addr - } if _, err := addr.ValueForProtocol(ma.P_QUIC_V1); err == nil { localMultiaddrs[quic.Version1] = addr } diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/transport.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/transport.go index aef3f4c9d..18d198bbe 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/transport.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quic/transport.go @@ -268,8 +268,8 @@ loop: } } -// Don't use mafmt.QUIC as we don't want to dial DNS addresses. Just /ip{4,6}/udp/quic -var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_UDP), mafmt.Or(mafmt.Base(ma.P_QUIC), mafmt.Base(ma.P_QUIC_V1))) +// Don't use mafmt.QUIC as we don't want to dial DNS addresses. Just /ip{4,6}/udp/quic-v1 +var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_UDP), mafmt.Base(ma.P_QUIC_V1)) // CanDial determines if we can dial to an address func (t *transport) CanDial(addr ma.Multiaddr) bool { diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/config.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/config.go index 76a2c8cc4..45b1d4b8c 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/config.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/config.go @@ -17,9 +17,7 @@ var quicConfig = &quic.Config{ return false }, KeepAlivePeriod: 15 * time.Second, - Versions: []quic.VersionNumber{quic.VersionDraft29, quic.Version1}, + Versions: []quic.VersionNumber{quic.Version1}, // We don't use datagrams (yet), but this is necessary for WebTransport EnableDatagrams: true, - // The multiaddress encodes the QUIC version, thus there's no need to send Version Negotiation packets. - DisableVersionNegotiationPackets: true, } diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/connmgr.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/connmgr.go index c12b86671..0add5ddfc 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/connmgr.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/connmgr.go @@ -16,7 +16,6 @@ import ( type ConnManager struct { reuseUDP4 *reuse reuseUDP6 *reuse - enableDraft29 bool enableReuseport bool enableMetrics bool @@ -26,8 +25,8 @@ type ConnManager struct { quicListenersMu sync.Mutex quicListeners map[string]quicListenerEntry - srk quic.StatelessResetKey - mt *metricsTracer + srk quic.StatelessResetKey + tokenKey quic.TokenGeneratorKey } type quicListenerEntry struct { @@ -35,12 +34,12 @@ type quicListenerEntry struct { ln *quicListener } -func NewConnManager(statelessResetKey quic.StatelessResetKey, opts ...Option) (*ConnManager, error) { +func NewConnManager(statelessResetKey quic.StatelessResetKey, tokenKey quic.TokenGeneratorKey, opts ...Option) (*ConnManager, error) { cm := &ConnManager{ enableReuseport: true, - enableDraft29: true, quicListeners: make(map[string]quicListenerEntry), srk: statelessResetKey, + tokenKey: tokenKey, } for _, o := range opts { if err := o(cm); err != nil { @@ -50,29 +49,20 @@ func NewConnManager(statelessResetKey quic.StatelessResetKey, opts ...Option) (* quicConf := quicConfig.Clone() - if cm.enableMetrics { - cm.mt = newMetricsTracer() - } - quicConf.Tracer = func(ctx context.Context, p quiclogging.Perspective, ci quic.ConnectionID) quiclogging.ConnectionTracer { - tracers := make([]quiclogging.ConnectionTracer, 0, 2) + quicConf.Tracer = func(ctx context.Context, p quiclogging.Perspective, ci quic.ConnectionID) *quiclogging.ConnectionTracer { + var tracer *quiclogging.ConnectionTracer if qlogTracerDir != "" { - tracers = append(tracers, qloggerForDir(qlogTracerDir, p, ci)) + tracer = qloggerForDir(qlogTracerDir, p, ci) } - if cm.mt != nil { - tracers = append(tracers, cm.mt.TracerForConnection(ctx, p, ci)) - } - return quiclogging.NewMultiplexedConnectionTracer(tracers...) + return tracer } serverConfig := quicConf.Clone() - if !cm.enableDraft29 { - serverConfig.Versions = []quic.VersionNumber{quic.Version1} - } cm.clientConfig = quicConf cm.serverConfig = serverConfig if cm.enableReuseport { - cm.reuseUDP4 = newReuse(&statelessResetKey, cm.mt) - cm.reuseUDP6 = newReuse(&statelessResetKey, cm.mt) + cm.reuseUDP4 = newReuse(&statelessResetKey, &tokenKey) + cm.reuseUDP6 = newReuse(&statelessResetKey, &tokenKey) } return cm, nil } @@ -89,12 +79,6 @@ func (c *ConnManager) getReuse(network string) (*reuse, error) { } func (c *ConnManager) ListenQUIC(addr ma.Multiaddr, tlsConf *tls.Config, allowWindowIncrease func(conn quic.Connection, delta uint64) bool) (Listener, error) { - if !c.enableDraft29 { - if _, err := addr.ValueForProtocol(ma.P_QUIC); err == nil { - return nil, errors.New("can't listen on `/quic` multiaddr (QUIC draft 29 version) when draft 29 support is disabled") - } - } - netw, host, err := manet.DialArgs(addr) if err != nil { return nil, err @@ -114,7 +98,7 @@ func (c *ConnManager) ListenQUIC(addr ma.Multiaddr, tlsConf *tls.Config, allowWi if err != nil { return nil, err } - ln, err := newQuicListener(tr, c.serverConfig, c.enableDraft29) + ln, err := newQuicListener(tr, c.serverConfig) if err != nil { return nil, err } @@ -156,15 +140,18 @@ func (c *ConnManager) transportForListen(network string, laddr *net.UDPAddr) (re return reuse.TransportForListen(network, laddr) } - conn, err := listenAndOptimize(network, laddr) + conn, err := net.ListenUDP(network, laddr) if err != nil { return nil, err } - tr := &singleOwnerTransport{Transport: quic.Transport{Conn: conn, StatelessResetKey: &c.srk}, packetConn: conn} - if c.mt != nil { - tr.Transport.Tracer = c.mt - } - return tr, nil + return &singleOwnerTransport{ + packetConn: conn, + Transport: quic.Transport{ + Conn: conn, + StatelessResetKey: &c.srk, + TokenGeneratorKey: &c.tokenKey, + }, + }, nil } func (c *ConnManager) DialQUIC(ctx context.Context, raddr ma.Multiaddr, tlsConf *tls.Config, allowWindowIncrease func(conn quic.Connection, delta uint64) bool) (quic.Connection, error) { @@ -183,8 +170,6 @@ func (c *ConnManager) DialQUIC(ctx context.Context, raddr ma.Multiaddr, tlsConf if v == quic.Version1 { // The endpoint has explicit support for QUIC v1, so we'll only use that version. quicConf.Versions = []quic.VersionNumber{quic.Version1} - } else if v == quic.VersionDraft29 { - quicConf.Versions = []quic.VersionNumber{quic.VersionDraft29} } else { return nil, errors.New("unknown QUIC version") } @@ -217,22 +202,14 @@ func (c *ConnManager) TransportForDial(network string, raddr *net.UDPAddr) (refC case "udp6": laddr = &net.UDPAddr{IP: net.IPv6zero, Port: 0} } - conn, err := listenAndOptimize(network, laddr) + conn, err := net.ListenUDP(network, laddr) if err != nil { return nil, err } - tr := &singleOwnerTransport{Transport: quic.Transport{Conn: conn, StatelessResetKey: &c.srk}, packetConn: conn} - if c.mt != nil { - tr.Transport.Tracer = c.mt - } - - return tr, nil + return &singleOwnerTransport{Transport: quic.Transport{Conn: conn, StatelessResetKey: &c.srk}, packetConn: conn}, nil } func (c *ConnManager) Protocols() []int { - if c.enableDraft29 { - return []int{ma.P_QUIC, ma.P_QUIC_V1} - } return []int{ma.P_QUIC_V1} } @@ -245,12 +222,3 @@ func (c *ConnManager) Close() error { } return c.reuseUDP4.Close() } - -// listenAndOptimize same as net.ListenUDP, but also calls quic.OptimizeConn -func listenAndOptimize(network string, laddr *net.UDPAddr) (net.PacketConn, error) { - conn, err := net.ListenUDP(network, laddr) - if err != nil { - return nil, err - } - return quic.OptimizeConn(conn) -} diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/listener.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/listener.go index 50b793451..4ee20042d 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/listener.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/listener.go @@ -38,20 +38,13 @@ type quicListener struct { protocols map[string]protoConf } -func newQuicListener(tr refCountedQuicTransport, quicConfig *quic.Config, enableDraft29 bool) (*quicListener, error) { +func newQuicListener(tr refCountedQuicTransport, quicConfig *quic.Config) (*quicListener, error) { localMultiaddrs := make([]ma.Multiaddr, 0, 2) a, err := ToQuicMultiaddr(tr.LocalAddr(), quic.Version1) if err != nil { return nil, err } localMultiaddrs = append(localMultiaddrs, a) - if enableDraft29 { - a, err := ToQuicMultiaddr(tr.LocalAddr(), quic.VersionDraft29) - if err != nil { - return nil, err - } - localMultiaddrs = append(localMultiaddrs, a) - } cl := &quicListener{ protocols: map[string]protoConf{}, running: make(chan struct{}), @@ -59,6 +52,7 @@ func newQuicListener(tr refCountedQuicTransport, quicConfig *quic.Config, enable addrs: localMultiaddrs, } tlsConf := &tls.Config{ + SessionTicketsDisabled: true, // This is set for the config for client, but we set it here as well: https://github.com/quic-go/quic-go/issues/4029 GetConfigForClient: func(info *tls.ClientHelloInfo) (*tls.Config, error) { cl.protocolsMu.Lock() defer cl.protocolsMu.Unlock() @@ -89,7 +83,7 @@ func (l *quicListener) allowWindowIncrease(conn quic.Connection, delta uint64) b l.protocolsMu.Lock() defer l.protocolsMu.Unlock() - conf, ok := l.protocols[conn.ConnectionState().TLS.ConnectionState.NegotiatedProtocol] + conf, ok := l.protocols[conn.ConnectionState().TLS.NegotiatedProtocol] if !ok { return false } diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/options.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/options.go index a700a0544..c32506920 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/options.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/options.go @@ -9,16 +9,6 @@ func DisableReuseport() Option { } } -// DisableDraft29 disables support for QUIC draft-29. -// This option should be set, unless support for this legacy QUIC version is needed for backwards compatibility. -// Support for QUIC draft-29 is already deprecated and will be removed in the future, see https://github.com/libp2p/go-libp2p/issues/1841. -func DisableDraft29() Option { - return func(m *ConnManager) error { - m.enableDraft29 = false - return nil - } -} - // EnableMetrics enables Prometheus metrics collection. func EnableMetrics() Option { return func(m *ConnManager) error { diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/quic_multiaddr.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/quic_multiaddr.go index 12eb7d8ab..3da4721b5 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/quic_multiaddr.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/quic_multiaddr.go @@ -10,8 +10,7 @@ import ( ) var ( - quicV1MA = ma.StringCast("/quic-v1") - quicDraft29MA = ma.StringCast("/quic") + quicV1MA = ma.StringCast("/quic-v1") ) func ToQuicMultiaddr(na net.Addr, version quic.VersionNumber) (ma.Multiaddr, error) { @@ -20,8 +19,6 @@ func ToQuicMultiaddr(na net.Addr, version quic.VersionNumber) (ma.Multiaddr, err return nil, err } switch version { - case quic.VersionDraft29: - return udpMA.Encapsulate(quicDraft29MA), nil case quic.Version1: return udpMA.Encapsulate(quicV1MA), nil default: @@ -34,9 +31,6 @@ func FromQuicMultiaddr(addr ma.Multiaddr) (*net.UDPAddr, quic.VersionNumber, err var partsBeforeQUIC []ma.Multiaddr ma.ForEach(addr, func(c ma.Component) bool { switch c.Protocol().Code { - case ma.P_QUIC: - version = quic.VersionDraft29 - return false case ma.P_QUIC_V1: version = quic.Version1 return false diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/reuse.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/reuse.go index 1584b2925..dc2b33b85 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/reuse.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/reuse.go @@ -51,8 +51,7 @@ func (c *singleOwnerTransport) Close() error { } func (c *singleOwnerTransport) WriteTo(b []byte, addr net.Addr) (int, error) { - // Safe because we called quic.OptimizeConn ourselves. - return c.packetConn.WriteTo(b, addr) + return c.Transport.WriteTo(b, addr) } // Constant. Defined as variables to simplify testing. @@ -86,8 +85,7 @@ func (c *refcountedTransport) Close() error { } func (c *refcountedTransport) WriteTo(b []byte, addr net.Addr) (int, error) { - // Safe because we called quic.OptimizeConn ourselves. - return c.packetConn.WriteTo(b, addr) + return c.Transport.WriteTo(b, addr) } func (c *refcountedTransport) LocalAddr() net.Addr { @@ -125,10 +123,10 @@ type reuse struct { globalDialers map[int]*refcountedTransport statelessResetKey *quic.StatelessResetKey - metricsTracer *metricsTracer + tokenGeneratorKey *quic.TokenGeneratorKey } -func newReuse(srk *quic.StatelessResetKey, mt *metricsTracer) *reuse { +func newReuse(srk *quic.StatelessResetKey, tokenKey *quic.TokenGeneratorKey) *reuse { r := &reuse{ unicast: make(map[string]map[int]*refcountedTransport), globalListeners: make(map[int]*refcountedTransport), @@ -136,7 +134,7 @@ func newReuse(srk *quic.StatelessResetKey, mt *metricsTracer) *reuse { closeChan: make(chan struct{}), gcStopChan: make(chan struct{}), statelessResetKey: srk, - metricsTracer: mt, + tokenGeneratorKey: tokenKey, } go r.gc() return r @@ -265,17 +263,15 @@ func (r *reuse) transportForDialLocked(network string, source *net.IP) (*refcoun case "udp6": addr = &net.UDPAddr{IP: net.IPv6zero, Port: 0} } - conn, err := listenAndOptimize(network, addr) + conn, err := net.ListenUDP(network, addr) if err != nil { return nil, err } tr := &refcountedTransport{Transport: quic.Transport{ Conn: conn, StatelessResetKey: r.statelessResetKey, + TokenGeneratorKey: r.tokenGeneratorKey, }, packetConn: conn} - if r.metricsTracer != nil { - tr.Transport.Tracer = r.metricsTracer - } r.globalDialers[conn.LocalAddr().(*net.UDPAddr).Port] = tr return tr, nil } @@ -314,19 +310,18 @@ func (r *reuse) TransportForListen(network string, laddr *net.UDPAddr) (*refcoun } } - conn, err := listenAndOptimize(network, laddr) + conn, err := net.ListenUDP(network, laddr) if err != nil { return nil, err } localAddr := conn.LocalAddr().(*net.UDPAddr) - tr := &refcountedTransport{Transport: quic.Transport{ - Conn: conn, - StatelessResetKey: r.statelessResetKey, - }, packetConn: conn} - if r.metricsTracer != nil { - tr.Transport.Tracer = r.metricsTracer + tr := &refcountedTransport{ + Transport: quic.Transport{ + Conn: conn, + StatelessResetKey: r.statelessResetKey, + }, + packetConn: conn, } - tr.IncreaseCount() // Deal with listen on a global address diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/tracer.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/tracer.go index 1386a5c3d..16c7dce4f 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/tracer.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/tracer.go @@ -24,7 +24,7 @@ func init() { qlogTracerDir = os.Getenv("QLOGDIR") } -func qloggerForDir(qlogDir string, p logging.Perspective, ci quic.ConnectionID) logging.ConnectionTracer { +func qloggerForDir(qlogDir string, p logging.Perspective, ci quic.ConnectionID) *logging.ConnectionTracer { // create the QLOGDIR, if it doesn't exist if err := os.MkdirAll(qlogDir, 0777); err != nil { log.Errorf("creating the QLOGDIR failed: %s", err) diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/tracer_metrics.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/tracer_metrics.go deleted file mode 100644 index 03e73fd25..000000000 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/tracer_metrics.go +++ /dev/null @@ -1,372 +0,0 @@ -package quicreuse - -import ( - "context" - "errors" - "fmt" - "net" - "sync" - "time" - - "github.com/prometheus/client_golang/prometheus" - "github.com/quic-go/quic-go" - "github.com/quic-go/quic-go/logging" -) - -var ( - bytesTransferred *prometheus.CounterVec - newConns *prometheus.CounterVec - closedConns *prometheus.CounterVec - sentPackets *prometheus.CounterVec - rcvdPackets *prometheus.CounterVec - bufferedPackets *prometheus.CounterVec - droppedPackets *prometheus.CounterVec - lostPackets *prometheus.CounterVec - connErrors *prometheus.CounterVec -) - -type aggregatingCollector struct { - mutex sync.Mutex - - conns map[string] /* conn ID */ *metricsConnTracer - rtts prometheus.Histogram - connDurations prometheus.Histogram -} - -func newAggregatingCollector() *aggregatingCollector { - return &aggregatingCollector{ - conns: make(map[string]*metricsConnTracer), - rtts: prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "quic_smoothed_rtt", - Help: "Smoothed RTT", - Buckets: prometheus.ExponentialBuckets(0.001, 1.25, 40), // 1ms to ~6000ms - }), - connDurations: prometheus.NewHistogram(prometheus.HistogramOpts{ - Name: "quic_connection_duration", - Help: "Connection Duration", - Buckets: prometheus.ExponentialBuckets(1, 1.5, 40), // 1s to ~12 weeks - }), - } -} - -var _ prometheus.Collector = &aggregatingCollector{} - -func (c *aggregatingCollector) Describe(descs chan<- *prometheus.Desc) { - descs <- c.rtts.Desc() - descs <- c.connDurations.Desc() -} - -func (c *aggregatingCollector) Collect(metrics chan<- prometheus.Metric) { - now := time.Now() - c.mutex.Lock() - for _, conn := range c.conns { - if rtt, valid := conn.getSmoothedRTT(); valid { - c.rtts.Observe(rtt.Seconds()) - } - c.connDurations.Observe(now.Sub(conn.startTime).Seconds()) - } - c.mutex.Unlock() - metrics <- c.rtts - metrics <- c.connDurations -} - -func (c *aggregatingCollector) AddConn(id string, t *metricsConnTracer) { - c.mutex.Lock() - c.conns[id] = t - c.mutex.Unlock() -} - -func (c *aggregatingCollector) RemoveConn(id string) { - c.mutex.Lock() - delete(c.conns, id) - c.mutex.Unlock() -} - -var collector *aggregatingCollector - -var initMetricsOnce sync.Once - -func initMetrics() { - const ( - direction = "direction" - encLevel = "encryption_level" - ) - - closedConns = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "quic_connections_closed_total", - Help: "closed QUIC connection", - }, - []string{direction}, - ) - prometheus.MustRegister(closedConns) - newConns = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "quic_connections_new_total", - Help: "new QUIC connection", - }, - []string{direction, "handshake_successful"}, - ) - prometheus.MustRegister(newConns) - bytesTransferred = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "quic_transferred_bytes", - Help: "QUIC bytes transferred", - }, - []string{direction}, // TODO: this is confusing. Other times, we use direction for the perspective - ) - prometheus.MustRegister(bytesTransferred) - sentPackets = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "quic_packets_sent_total", - Help: "QUIC packets sent", - }, - []string{encLevel}, - ) - prometheus.MustRegister(sentPackets) - rcvdPackets = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "quic_packets_rcvd_total", - Help: "QUIC packets received", - }, - []string{encLevel}, - ) - prometheus.MustRegister(rcvdPackets) - bufferedPackets = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "quic_packets_buffered_total", - Help: "Buffered packets", - }, - []string{"packet_type"}, - ) - prometheus.MustRegister(bufferedPackets) - droppedPackets = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "quic_packets_dropped_total", - Help: "Dropped packets", - }, - []string{"packet_type", "reason"}, - ) - prometheus.MustRegister(droppedPackets) - connErrors = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "quic_connection_errors_total", - Help: "QUIC connection errors", - }, - []string{"side", "error_code"}, - ) - prometheus.MustRegister(connErrors) - lostPackets = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "quic_packets_lost_total", - Help: "QUIC lost received", - }, - []string{encLevel, "reason"}, - ) - prometheus.MustRegister(lostPackets) - collector = newAggregatingCollector() - prometheus.MustRegister(collector) -} - -type metricsTracer struct { - logging.NullTracer -} - -var _ logging.Tracer = &metricsTracer{} - -func newMetricsTracer() *metricsTracer { - initMetricsOnce.Do(func() { initMetrics() }) - return &metricsTracer{} -} - -func (m *metricsTracer) TracerForConnection(_ context.Context, p logging.Perspective, connID logging.ConnectionID) logging.ConnectionTracer { - return &metricsConnTracer{perspective: p, connID: connID} -} - -func (m *metricsTracer) SentPacket(_ net.Addr, _ *logging.Header, size logging.ByteCount, _ []logging.Frame) { - bytesTransferred.WithLabelValues("sent").Add(float64(size)) -} - -type metricsConnTracer struct { - logging.NullConnectionTracer - - perspective logging.Perspective - startTime time.Time - connID logging.ConnectionID - handshakeComplete bool - - mutex sync.Mutex - numRTTMeasurements int - rtt time.Duration -} - -var _ logging.ConnectionTracer = &metricsConnTracer{} - -func (m *metricsConnTracer) getDirection() string { - if m.perspective == logging.PerspectiveClient { - return "outgoing" - } - return "incoming" -} - -func (m *metricsConnTracer) getEncLevel(packetType logging.PacketType) string { - switch packetType { - case logging.PacketType0RTT: - return "0-RTT" - case logging.PacketTypeInitial: - return "Initial" - case logging.PacketTypeHandshake: - return "Handshake" - case logging.PacketTypeRetry: - return "Retry" - case logging.PacketType1RTT: - return "1-RTT" - default: - return "unknown" - } -} - -func (m *metricsConnTracer) StartedConnection(net.Addr, net.Addr, logging.ConnectionID, logging.ConnectionID) { - m.startTime = time.Now() - collector.AddConn(m.connID.String(), m) -} - -func (m *metricsConnTracer) ClosedConnection(e error) { - var ( - applicationErr *quic.ApplicationError - transportErr *quic.TransportError - statelessResetErr *quic.StatelessResetError - vnErr *quic.VersionNegotiationError - idleTimeoutErr *quic.IdleTimeoutError - handshakeTimeoutErr *quic.HandshakeTimeoutError - remote bool - desc string - ) - - switch { - case errors.As(e, &applicationErr): - return - case errors.As(e, &transportErr): - remote = transportErr.Remote - desc = transportErr.ErrorCode.String() - case errors.As(e, &statelessResetErr): - remote = true - desc = "stateless_reset" - case errors.As(e, &vnErr): - desc = "version_negotiation" - case errors.As(e, &idleTimeoutErr): - desc = "idle_timeout" - case errors.As(e, &handshakeTimeoutErr): - desc = "handshake_timeout" - default: - desc = fmt.Sprintf("unknown error: %v", e) - } - - side := "local" - if remote { - side = "remote" - } - connErrors.WithLabelValues(side, desc).Inc() -} -func (m *metricsConnTracer) SentPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, _ *logging.AckFrame, _ []logging.Frame) { - bytesTransferred.WithLabelValues("sent").Add(float64(size)) - sentPackets.WithLabelValues(m.getEncLevel(logging.PacketTypeFromHeader(&hdr.Header))).Inc() -} - -func (m *metricsConnTracer) ReceivedVersionNegotiationPacket(dst, src logging.ArbitraryLenConnectionID, v []logging.VersionNumber) { - bytesTransferred.WithLabelValues("rcvd").Add(1 /* header form byte */ + 4 /* version number */ + 2 /* src and dest conn id length fields */ + float64(dst.Len()+src.Len()) + float64(4*len(v))) - rcvdPackets.WithLabelValues("Version Negotiation").Inc() -} - -func (m *metricsConnTracer) ReceivedRetry(*logging.Header) { - rcvdPackets.WithLabelValues("Retry").Inc() -} - -func (m *metricsConnTracer) ReceivedPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, _ []logging.Frame) { - bytesTransferred.WithLabelValues("rcvd").Add(float64(size)) - rcvdPackets.WithLabelValues(m.getEncLevel(logging.PacketTypeFromHeader(&hdr.Header))).Inc() -} - -func (m *metricsConnTracer) BufferedPacket(packetType logging.PacketType, _ logging.ByteCount) { - bufferedPackets.WithLabelValues(m.getEncLevel(packetType)).Inc() -} - -func (m *metricsConnTracer) DroppedPacket(packetType logging.PacketType, size logging.ByteCount, r logging.PacketDropReason) { - bytesTransferred.WithLabelValues("rcvd").Add(float64(size)) - var reason string - switch r { - case logging.PacketDropKeyUnavailable: - reason = "key_unavailable" - case logging.PacketDropUnknownConnectionID: - reason = "unknown_connection_id" - case logging.PacketDropHeaderParseError: - reason = "header_parse_error" - case logging.PacketDropPayloadDecryptError: - reason = "payload_decrypt_error" - case logging.PacketDropProtocolViolation: - reason = "protocol_violation" - case logging.PacketDropDOSPrevention: - reason = "dos_prevention" - case logging.PacketDropUnsupportedVersion: - reason = "unsupported_version" - case logging.PacketDropUnexpectedPacket: - reason = "unexpected_packet" - case logging.PacketDropUnexpectedSourceConnectionID: - reason = "unexpected_source_connection_id" - case logging.PacketDropUnexpectedVersion: - reason = "unexpected_version" - case logging.PacketDropDuplicate: - reason = "duplicate" - default: - reason = "unknown" - } - droppedPackets.WithLabelValues(m.getEncLevel(packetType), reason).Inc() -} - -func (m *metricsConnTracer) UpdatedMetrics(rttStats *logging.RTTStats, cwnd, bytesInFlight logging.ByteCount, packetsInFlight int) { - m.mutex.Lock() - m.rtt = rttStats.SmoothedRTT() - m.numRTTMeasurements++ - m.mutex.Unlock() -} - -func (m *metricsConnTracer) LostPacket(level logging.EncryptionLevel, _ logging.PacketNumber, r logging.PacketLossReason) { - var reason string - switch r { - case logging.PacketLossReorderingThreshold: - reason = "reordering_threshold" - case logging.PacketLossTimeThreshold: - reason = "time_threshold" - default: - reason = "unknown" - } - lostPackets.WithLabelValues(level.String(), reason).Inc() -} - -func (m *metricsConnTracer) DroppedEncryptionLevel(level logging.EncryptionLevel) { - if level == logging.EncryptionHandshake { - m.handleHandshakeComplete() - } -} - -func (m *metricsConnTracer) Close() { - if m.handshakeComplete { - closedConns.WithLabelValues(m.getDirection()).Inc() - } else { - newConns.WithLabelValues(m.getDirection(), "false").Inc() - } - collector.RemoveConn(m.connID.String()) -} - -func (m *metricsConnTracer) handleHandshakeComplete() { - m.handshakeComplete = true - newConns.WithLabelValues(m.getDirection(), "true").Inc() -} - -func (m *metricsConnTracer) getSmoothedRTT() (rtt time.Duration, valid bool) { - m.mutex.Lock() - rtt = m.rtt - valid = m.numRTTMeasurements > 10 - m.mutex.Unlock() - return -} diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/metrics.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/metrics.go index fc2add49b..8e7308f3a 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/metrics.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/metrics.go @@ -1,4 +1,4 @@ -//go:build !windows +//go:build !windows && !riscv64 package tcp diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/metrics_general.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/metrics_general.go index 6b6728344..e9868609f 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/metrics_general.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/metrics_general.go @@ -1,4 +1,4 @@ -//go:build !linux && !darwin && !windows +//go:build !linux && !darwin && !windows && !riscv64 package tcp diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/metrics_windows.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/metrics_none.go similarity index 71% rename from vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/metrics_windows.go rename to vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/metrics_none.go index 7142e7dbe..01cf3e9c5 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/metrics_windows.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/metrics_none.go @@ -1,4 +1,6 @@ -//go:build windows +// riscv64 see: https://github.com/marten-seemann/tcp/pull/1 + +//go:build windows || riscv64 package tcp diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/tcp.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/tcp.go index f277b3f8f..d52bb9601 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/tcp.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/tcp/tcp.go @@ -131,6 +131,7 @@ type TcpTransport struct { } var _ transport.Transport = &TcpTransport{} +var _ transport.DialUpdater = &TcpTransport{} // NewTCPTransport creates a tcp transport object that tracks dialers and listeners // created. It represents an entire TCP stack (though it might not necessarily be). @@ -176,13 +177,17 @@ func (t *TcpTransport) maDial(ctx context.Context, raddr ma.Multiaddr) (manet.Co // Dial dials the peer at the remote address. func (t *TcpTransport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (transport.CapableConn, error) { + return t.DialWithUpdates(ctx, raddr, p, nil) +} + +func (t *TcpTransport) DialWithUpdates(ctx context.Context, raddr ma.Multiaddr, p peer.ID, updateChan chan<- transport.DialUpdate) (transport.CapableConn, error) { connScope, err := t.rcmgr.OpenConnection(network.DirOutbound, true, raddr) if err != nil { log.Debugw("resource manager blocked outgoing connection", "peer", p, "addr", raddr, "error", err) return nil, err } - c, err := t.dialWithScope(ctx, raddr, p, connScope) + c, err := t.dialWithScope(ctx, raddr, p, connScope, updateChan) if err != nil { connScope.Done() return nil, err @@ -190,7 +195,7 @@ func (t *TcpTransport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) return c, nil } -func (t *TcpTransport) dialWithScope(ctx context.Context, raddr ma.Multiaddr, p peer.ID, connScope network.ConnManagementScope) (transport.CapableConn, error) { +func (t *TcpTransport) dialWithScope(ctx context.Context, raddr ma.Multiaddr, p peer.ID, connScope network.ConnManagementScope, updateChan chan<- transport.DialUpdate) (transport.CapableConn, error) { if err := connScope.SetPeer(p); err != nil { log.Debugw("resource manager blocked outgoing connection for peer", "peer", p, "addr", raddr, "error", err) return nil, err @@ -212,6 +217,13 @@ func (t *TcpTransport) dialWithScope(ctx context.Context, raddr ma.Multiaddr, p return nil, err } } + if updateChan != nil { + select { + case updateChan <- transport.DialUpdate{Kind: transport.UpdateKindHandshakeProgressed, Addr: raddr}: + default: + // It is better to skip the update than to delay upgrading the connection + } + } direction := network.DirOutbound if ok, isClient, _ := network.GetSimultaneousConnect(ctx); ok && !isClient { direction = network.DirInbound diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/websocket/websocket.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/websocket/websocket.go index e1965123d..5142ca97a 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/websocket/websocket.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/websocket/websocket.go @@ -120,7 +120,7 @@ func (t *WebsocketTransport) Proxy() bool { return false } -func (t *WebsocketTransport) Resolve(ctx context.Context, maddr ma.Multiaddr) ([]ma.Multiaddr, error) { +func (t *WebsocketTransport) Resolve(_ context.Context, maddr ma.Multiaddr) ([]ma.Multiaddr, error) { parsed, err := parseWebsocketMultiaddr(maddr) if err != nil { return nil, err @@ -136,7 +136,7 @@ func (t *WebsocketTransport) Resolve(ctx context.Context, maddr ma.Multiaddr) ([ // We don't have an sni component, we'll use dns/dnsaddr ma.ForEach(parsed.restMultiaddr, func(c ma.Component) bool { switch c.Protocol().Code { - case ma.P_DNS, ma.P_DNS4, ma.P_DNS6, ma.P_DNSADDR: + case ma.P_DNS, ma.P_DNS4, ma.P_DNS6: // err shouldn't happen since this means we couldn't parse a dns hostname for an sni value. parsed.sni, err = ma.NewComponent("sni", c.Value()) return false diff --git a/vendor/github.com/libp2p/go-libp2p/p2p/transport/webtransport/multiaddr.go b/vendor/github.com/libp2p/go-libp2p/p2p/transport/webtransport/multiaddr.go index d6930af36..bd90638b6 100644 --- a/vendor/github.com/libp2p/go-libp2p/p2p/transport/webtransport/multiaddr.go +++ b/vendor/github.com/libp2p/go-libp2p/p2p/transport/webtransport/multiaddr.go @@ -89,17 +89,23 @@ func IsWebtransportMultiaddr(multiaddr ma.Multiaddr) (bool, int) { certhashCount := 0 ma.ForEach(multiaddr, func(c ma.Component) bool { - if c.Protocol().Code == ma.P_QUIC_V1 && state == init { - state = foundUDP - } - if c.Protocol().Code == ma.P_QUIC_V1 && state == foundUDP { - state = foundQuicV1 - } - if c.Protocol().Code == ma.P_WEBTRANSPORT && state == foundQuicV1 { - state = foundWebTransport - } - if c.Protocol().Code == ma.P_CERTHASH && state == foundWebTransport { - certhashCount++ + switch c.Protocol().Code { + case ma.P_UDP: + if state == init { + state = foundUDP + } + case ma.P_QUIC_V1: + if state == foundUDP { + state = foundQuicV1 + } + case ma.P_WEBTRANSPORT: + if state == foundQuicV1 { + state = foundWebTransport + } + case ma.P_CERTHASH: + if state == foundWebTransport { + certhashCount++ + } } return true }) diff --git a/vendor/github.com/libp2p/go-libp2p/package-list.json b/vendor/github.com/libp2p/go-libp2p/package-list.json deleted file mode 100644 index fec3fac19..000000000 --- a/vendor/github.com/libp2p/go-libp2p/package-list.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "columns": [ - "Name", - "CI/Travis", - "Coverage", - "Description" - ], - "rows": [ - "Libp2p", - ["libp2p/go-libp2p", "go-libp2p", "go-libp2p entry point"], - ["libp2p/go-libp2p-core", "go-libp2p-core", "core interfaces, types, and abstractions"], - ["libp2p/go-libp2p-blankhost", "go-libp2p-blankhost", "minimal implementation of the \"host\" interface"], - - "Network", - ["libp2p/go-libp2p-swarm", "go-libp2p-swarm", "reference implementation of network state machine"], - - "Transport", - ["libp2p/go-ws-transport", "go-ws-transport", "WebSocket transport"], - ["libp2p/go-tcp-transport", "go-tcp-transport", "TCP transport"], - ["libp2p/go-libp2p-quic-transport", "go-libp2p-quic-transport", "QUIC transport"], - ["libp2p/go-udp-transport", "go-udp-transport", "UDP transport"], - ["libp2p/go-utp-transport", "go-utp-transport", "uTorrent transport (UTP)"], - ["libp2p/go-libp2p-circuit", "go-libp2p-circuit", "relay transport"], - ["libp2p/go-libp2p-transport-upgrader", "go-libp2p-transport-upgrader", "upgrades multiaddr-net connections into full libp2p transports"], - ["libp2p/go-libp2p-reuseport-transport", "go-libp2p-reuseport-transport", "partial transport for building transports that reuse ports"], - - "Encrypted Channels", - ["libp2p/go-libp2p-secio", "go-libp2p-secio", "SecIO crypto channel"], - ["libp2p/go-libp2p-tls-transport", "go-libp2p-tls-transport", "TLS 1.3+ crypto channel"], - ["libp2p/go-conn-security-multistream", "go-conn-security-multistream", "multistream multiplexed meta crypto channel"], - - "Private Network", - ["libp2p/go-libp2p-pnet", "go-libp2p-pnet", "reference private networking implementation"], - - "Stream Muxers", - ["libp2p/go-libp2p-yamux", "go-libp2p-yamux", "YAMUX stream multiplexer"], - ["libp2p/go-libp2p-mplex", "go-libp2p-mplex", "MPLEX stream multiplexer"], - - "NAT Traversal", - ["libp2p/go-libp2p-nat", "go-libp2p-nat"], - - "Peerstore", - ["libp2p/go-libp2p-peerstore", "go-libp2p-peerstore", "reference implementation of peer metadata storage component"], - - "Connection Manager", - ["libp2p/go-libp2p-connmgr", "go-libp2p-connmgr", "reference implementation of connection manager"], - - "Routing", - ["libp2p/go-libp2p-record", "go-libp2p-record", "record type and validator logic"], - ["libp2p/go-libp2p-kad-dht", "go-libp2p-kad-dht", "Kademlia-like router"], - ["libp2p/go-libp2p-kbucket", "go-libp2p-kbucket", "Kademlia routing table helper types"], - ["libp2p/go-libp2p-coral-dht", "go-libp2p-coral-dht", "Router based on Coral DHT"], - ["libp2p/go-libp2p-pubsub-router", "go-libp2p-pubsub-router", "record-store over pubsub adapter"], - - "Consensus", - ["libp2p/go-libp2p-consensus", "go-libp2p-consensus", "consensus protocols interfaces"], - ["libp2p/go-libp2p-raft", "go-libp2p-raft", "consensus implementation over raft"], - - "Pubsub", - ["libp2p/go-libp2p-pubsub", "go-libp2p-pubsub", "multiple pubsub over libp2p implementations"], - - "RPC", - ["libp2p/go-libp2p-gorpc", "go-libp2p-gorpc", "a simple RPC library for libp2p"], - - "Utilities/miscellaneous", - ["libp2p/go-libp2p-loggables", "go-libp2p-loggables", "logging helpers"], - ["libp2p/go-maddr-filter", "go-maddr-filter", "multiaddr filtering helpers"], - ["libp2p/go-libp2p-netutil", "go-libp2p-netutil", "misc utilities"], - ["libp2p/go-msgio", "go-msgio", "length prefixed data channel"], - ["libp2p/go-addr-util", "go-addr-util", "address utilities for libp2p swarm"], - ["libp2p/go-buffer-pool", "go-buffer-pool", "a variable size buffer pool for go"], - ["libp2p/go-libp2p-routing-helpers", "go-libp2p-routing-helpers", "routing helpers"], - ["libp2p/go-reuseport", "go-reuseport", "enables reuse of addresses"], - ["libp2p/go-sockaddr", "go-sockaddr", "utils for sockaddr conversions"], - ["libp2p/go-flow-metrics", "go-flow-metrics", "metrics library"], - ["libp2p/go-libp2p-gostream", "go-libp2p-gostream", "Go 'net' wrappers for libp2p"], - ["libp2p/go-libp2p-http", "go-libp2p-http", "HTTP on top of libp2p streams"], - - "Testing and examples", - ["libp2p/go-libp2p-testing", "go-libp2p-testing", "a collection of testing utilities for libp2p"] - ] -} diff --git a/vendor/github.com/libp2p/go-libp2p/tools.go b/vendor/github.com/libp2p/go-libp2p/tools.go index 46a8037df..7a650d9c1 100644 --- a/vendor/github.com/libp2p/go-libp2p/tools.go +++ b/vendor/github.com/libp2p/go-libp2p/tools.go @@ -3,7 +3,7 @@ package libp2p import ( - _ "github.com/golang/mock/mockgen" + _ "go.uber.org/mock/mockgen" _ "golang.org/x/tools/cmd/goimports" _ "google.golang.org/protobuf/cmd/protoc-gen-go" ) diff --git a/vendor/github.com/libp2p/go-libp2p/version.json b/vendor/github.com/libp2p/go-libp2p/version.json index 462969c8e..7b5ac4c94 100644 --- a/vendor/github.com/libp2p/go-libp2p/version.json +++ b/vendor/github.com/libp2p/go-libp2p/version.json @@ -1,3 +1,3 @@ { - "version": "v0.29.2" + "version": "v0.32.2" } diff --git a/vendor/github.com/libp2p/go-reuseport/control_freebsd.go b/vendor/github.com/libp2p/go-reuseport/control_freebsd.go new file mode 100644 index 000000000..cec1b11aa --- /dev/null +++ b/vendor/github.com/libp2p/go-reuseport/control_freebsd.go @@ -0,0 +1,27 @@ +//go:build freebsd + +package reuseport + +import ( + "syscall" + + "golang.org/x/sys/unix" +) + +func Control(network, address string, c syscall.RawConn) (err error) { + controlErr := c.Control(func(fd uintptr) { + err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) + if err != nil { + return + } + err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) + if err != nil { + return + } + err = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT_LB, 1) + }) + if controlErr != nil { + err = controlErr + } + return +} diff --git a/vendor/github.com/libp2p/go-reuseport/control_unix.go b/vendor/github.com/libp2p/go-reuseport/control_unix.go index 4197d1f74..e80688b5e 100644 --- a/vendor/github.com/libp2p/go-reuseport/control_unix.go +++ b/vendor/github.com/libp2p/go-reuseport/control_unix.go @@ -1,4 +1,4 @@ -//go:build !plan9 && !windows && !wasm +//go:build !plan9 && !windows && !wasm && !freebsd package reuseport diff --git a/vendor/github.com/libp2p/go-reuseport/version.json b/vendor/github.com/libp2p/go-reuseport/version.json index a654d65ab..372b6eab3 100644 --- a/vendor/github.com/libp2p/go-reuseport/version.json +++ b/vendor/github.com/libp2p/go-reuseport/version.json @@ -1,3 +1,3 @@ { - "version": "v0.3.0" + "version": "v0.4.0" } diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go index d569c0c94..d0ea68f40 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_bsd.go +++ b/vendor/github.com/mattn/go-isatty/isatty_bsd.go @@ -1,6 +1,7 @@ -//go:build (darwin || freebsd || openbsd || netbsd || dragonfly || hurd) && !appengine +//go:build (darwin || freebsd || openbsd || netbsd || dragonfly || hurd) && !appengine && !tinygo // +build darwin freebsd openbsd netbsd dragonfly hurd // +build !appengine +// +build !tinygo package isatty diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go index 31503226f..7402e0618 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_others.go +++ b/vendor/github.com/mattn/go-isatty/isatty_others.go @@ -1,5 +1,6 @@ -//go:build appengine || js || nacl || wasm -// +build appengine js nacl wasm +//go:build (appengine || js || nacl || tinygo || wasm) && !windows +// +build appengine js nacl tinygo wasm +// +build !windows package isatty diff --git a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go index 67787657f..0337d8cf6 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go +++ b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go @@ -1,6 +1,7 @@ -//go:build (linux || aix || zos) && !appengine +//go:build (linux || aix || zos) && !appengine && !tinygo // +build linux aix zos // +build !appengine +// +build !tinygo package isatty diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md index 06bea9fab..95bc08d5c 100644 --- a/vendor/github.com/miekg/dns/README.md +++ b/vendor/github.com/miekg/dns/README.md @@ -81,6 +81,7 @@ A not-so-up-to-date-list-that-may-be-actually-current: * https://addr.tools/ * https://dnscheck.tools/ * https://github.com/egbakou/domainverifier +* https://github.com/semihalev/sdns Send pull request if you want to be listed here. diff --git a/vendor/github.com/miekg/dns/defaults.go b/vendor/github.com/miekg/dns/defaults.go index c1558b79c..6d7e17605 100644 --- a/vendor/github.com/miekg/dns/defaults.go +++ b/vendor/github.com/miekg/dns/defaults.go @@ -5,6 +5,7 @@ import ( "net" "strconv" "strings" + "unicode" ) const hexDigit = "0123456789abcdef" @@ -330,8 +331,18 @@ func Fqdn(s string) string { // CanonicalName returns the domain name in canonical form. A name in canonical // form is lowercase and fully qualified. See Section 6.2 in RFC 4034. +// According to the RFC all uppercase US-ASCII letters in the owner name of the +// RR areeplaced by the corresponding lowercase US-ASCII letters. func CanonicalName(s string) string { - return strings.ToLower(Fqdn(s)) + var result strings.Builder + for _, ch := range s { + if unicode.IsUpper(ch) && (ch >= 0x00 && ch <= 0x7F) { + result.WriteRune(unicode.ToLower(ch)) + } else { + result.WriteRune(ch) + } + } + return Fqdn(result.String()) } // Copied from the official Go code. diff --git a/vendor/github.com/miekg/dns/msg.go b/vendor/github.com/miekg/dns/msg.go index d5049a4f9..b05cf14e9 100644 --- a/vendor/github.com/miekg/dns/msg.go +++ b/vendor/github.com/miekg/dns/msg.go @@ -896,23 +896,38 @@ func (dns *Msg) String() string { return " MsgHdr" } s := dns.MsgHdr.String() + " " - s += "QUERY: " + strconv.Itoa(len(dns.Question)) + ", " - s += "ANSWER: " + strconv.Itoa(len(dns.Answer)) + ", " - s += "AUTHORITY: " + strconv.Itoa(len(dns.Ns)) + ", " - s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" + if dns.MsgHdr.Opcode == OpcodeUpdate { + s += "ZONE: " + strconv.Itoa(len(dns.Question)) + ", " + s += "PREREQ: " + strconv.Itoa(len(dns.Answer)) + ", " + s += "UPDATE: " + strconv.Itoa(len(dns.Ns)) + ", " + s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" + } else { + s += "QUERY: " + strconv.Itoa(len(dns.Question)) + ", " + s += "ANSWER: " + strconv.Itoa(len(dns.Answer)) + ", " + s += "AUTHORITY: " + strconv.Itoa(len(dns.Ns)) + ", " + s += "ADDITIONAL: " + strconv.Itoa(len(dns.Extra)) + "\n" + } opt := dns.IsEdns0() if opt != nil { // OPT PSEUDOSECTION s += opt.String() + "\n" } if len(dns.Question) > 0 { - s += "\n;; QUESTION SECTION:\n" + if dns.MsgHdr.Opcode == OpcodeUpdate { + s += "\n;; ZONE SECTION:\n" + } else { + s += "\n;; QUESTION SECTION:\n" + } for _, r := range dns.Question { s += r.String() + "\n" } } if len(dns.Answer) > 0 { - s += "\n;; ANSWER SECTION:\n" + if dns.MsgHdr.Opcode == OpcodeUpdate { + s += "\n;; PREREQUISITE SECTION:\n" + } else { + s += "\n;; ANSWER SECTION:\n" + } for _, r := range dns.Answer { if r != nil { s += r.String() + "\n" @@ -920,7 +935,11 @@ func (dns *Msg) String() string { } } if len(dns.Ns) > 0 { - s += "\n;; AUTHORITY SECTION:\n" + if dns.MsgHdr.Opcode == OpcodeUpdate { + s += "\n;; UPDATE SECTION:\n" + } else { + s += "\n;; AUTHORITY SECTION:\n" + } for _, r := range dns.Ns { if r != nil { s += r.String() + "\n" diff --git a/vendor/github.com/miekg/dns/types.go b/vendor/github.com/miekg/dns/types.go index 03afeccda..c9a03dec6 100644 --- a/vendor/github.com/miekg/dns/types.go +++ b/vendor/github.com/miekg/dns/types.go @@ -236,6 +236,9 @@ var CertTypeToString = map[uint16]string{ CertOID: "OID", } +// Prefix for IPv4 encoded as IPv6 address +const ipv4InIPv6Prefix = "::ffff:" + //go:generate go run types_generate.go // Question holds a DNS question. Usually there is just one. While the @@ -751,6 +754,11 @@ func (rr *AAAA) String() string { if rr.AAAA == nil { return rr.Hdr.String() } + + if rr.AAAA.To4() != nil { + return rr.Hdr.String() + ipv4InIPv6Prefix + rr.AAAA.String() + } + return rr.Hdr.String() + rr.AAAA.String() } @@ -1517,7 +1525,7 @@ func (a *APLPrefix) str() string { case net.IPv6len: // add prefix for IPv4-mapped IPv6 if v4 := a.Network.IP.To4(); v4 != nil { - sb.WriteString("::ffff:") + sb.WriteString(ipv4InIPv6Prefix) } sb.WriteString(a.Network.IP.String()) } diff --git a/vendor/github.com/miekg/dns/version.go b/vendor/github.com/miekg/dns/version.go index 5891044a3..a09113662 100644 --- a/vendor/github.com/miekg/dns/version.go +++ b/vendor/github.com/miekg/dns/version.go @@ -3,7 +3,7 @@ package dns import "fmt" // Version is current version of this library. -var Version = v{1, 1, 55} +var Version = v{1, 1, 56} // v holds the version of this library. type v struct { diff --git a/vendor/github.com/multiformats/go-multiaddr/codec.go b/vendor/github.com/multiformats/go-multiaddr/codec.go index c0da1f94c..f2951c48d 100644 --- a/vendor/github.com/multiformats/go-multiaddr/codec.go +++ b/vendor/github.com/multiformats/go-multiaddr/codec.go @@ -93,6 +93,9 @@ func validateBytes(b []byte) (err error) { if len(b) < size || size < 0 { return fmt.Errorf("invalid value for size %d", len(b)) } + if p.Path && len(b) != size { + return fmt.Errorf("invalid size of component for path protocol %d: expected %d", size, len(b)) + } err = p.Transcoder.ValidateBytes(b[:size]) if err != nil { diff --git a/vendor/github.com/multiformats/go-multiaddr/multiaddr.go b/vendor/github.com/multiformats/go-multiaddr/multiaddr.go index 7c567c9fd..4b3a36086 100644 --- a/vendor/github.com/multiformats/go-multiaddr/multiaddr.go +++ b/vendor/github.com/multiformats/go-multiaddr/multiaddr.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "log" - "strings" "golang.org/x/exp/slices" ) @@ -150,26 +149,46 @@ func (m *multiaddr) Encapsulate(o Multiaddr) Multiaddr { } // Decapsulate unwraps Multiaddr up until the given Multiaddr is found. -func (m *multiaddr) Decapsulate(o Multiaddr) Multiaddr { - s1 := m.String() - s2 := o.String() - i := strings.LastIndex(s1, s2) - if i < 0 { +func (m *multiaddr) Decapsulate(right Multiaddr) Multiaddr { + if right == nil { + return m + } + + leftParts := Split(m) + rightParts := Split(right) + + lastIndex := -1 + for i := range leftParts { + foundMatch := false + for j, rightC := range rightParts { + if len(leftParts) <= i+j { + foundMatch = false + break + } + + foundMatch = rightC.Equal(leftParts[i+j]) + if !foundMatch { + break + } + } + + if foundMatch { + lastIndex = i + } + } + + if lastIndex == 0 { + return nil + } + + if lastIndex < 0 { // if multiaddr not contained, returns a copy. cpy := make([]byte, len(m.bytes)) copy(cpy, m.bytes) return &multiaddr{bytes: cpy} } - if i == 0 { - return nil - } - - ma, err := NewMultiaddr(s1[:i]) - if err != nil { - panic("Multiaddr.Decapsulate incorrect byte boundaries.") - } - return ma + return Join(leftParts[:lastIndex]...) } var ErrProtocolNotFound = fmt.Errorf("protocol not found in multiaddr") @@ -220,7 +239,7 @@ func Unique(addrs []Multiaddr) []Multiaddr { return addrs } // Use the new slices package here, as the sort function doesn't allocate (sort.Slice does). - slices.SortFunc(addrs, func(a, b Multiaddr) bool { return bytes.Compare(a.Bytes(), b.Bytes()) < 0 }) + slices.SortFunc(addrs, func(a, b Multiaddr) int { return bytes.Compare(a.Bytes(), b.Bytes()) }) idx := 1 for i := 1; i < len(addrs); i++ { if !addrs[i-1].Equal(addrs[i]) { diff --git a/vendor/github.com/multiformats/go-multiaddr/net/ip.go b/vendor/github.com/multiformats/go-multiaddr/net/ip.go index 66f91979a..ef936c562 100644 --- a/vendor/github.com/multiformats/go-multiaddr/net/ip.go +++ b/vendor/github.com/multiformats/go-multiaddr/net/ip.go @@ -60,6 +60,9 @@ func IsThinWaist(m ma.Multiaddr) bool { // or /ip6zone//ip6//* func IsIPLoopback(m ma.Multiaddr) bool { m = zoneless(m) + if m == nil { + return false + } c, _ := ma.SplitFirst(m) if c == nil { return false @@ -76,6 +79,9 @@ func IsIPLoopback(m ma.Multiaddr) bool { // routable. func IsIP6LinkLocal(m ma.Multiaddr) bool { m = zoneless(m) + if m == nil { + return false + } c, _ := ma.SplitFirst(m) if c == nil || c.Protocol().Code != ma.P_IP6 { return false diff --git a/vendor/github.com/multiformats/go-multiaddr/net/private.go b/vendor/github.com/multiformats/go-multiaddr/net/private.go index ba57f00c3..9f966de0f 100644 --- a/vendor/github.com/multiformats/go-multiaddr/net/private.go +++ b/vendor/github.com/multiformats/go-multiaddr/net/private.go @@ -2,6 +2,7 @@ package manet import ( "net" + "strings" ma "github.com/multiformats/go-multiaddr" ) @@ -46,6 +47,36 @@ var unroutableCIDR6 = []string{ "ff00::/8", } +// unResolvableDomains do not resolve to an IP address. +// Ref: https://en.wikipedia.org/wiki/Special-use_domain_name#Reserved_domain_names +var unResolvableDomains = []string{ + // Reverse DNS Lookup + ".in-addr.arpa", + ".ip6.arpa", + + // RFC 6761: Users MAY assume that queries for "invalid" names will always return NXDOMAIN + // responses + ".invalid", +} + +// privateUseDomains are reserved for private use and have no central authority for consistent +// address resolution +// Ref: https://en.wikipedia.org/wiki/Special-use_domain_name#Reserved_domain_names +var privateUseDomains = []string{ + // RFC 8375: Reserved for home networks + ".home.arpa", + + // MDNS + ".local", + + // RFC 6761: No central authority for .test names + ".test", +} + +// RFC 6761: Users may assume that IPv4 and IPv6 address queries for localhost names will +// always resolve to the respective IP loopback address +const localHostDomain = ".localhost" + func init() { Private4 = parseCIDR(privateCIDR4) Private6 = parseCIDR(privateCIDR6) @@ -65,7 +96,8 @@ func parseCIDR(cidrs []string) []*net.IPNet { return ipnets } -// IsPublicAddr retruns true if the IP part of the multiaddr is a publicly routable address +// IsPublicAddr returns true if the IP part of the multiaddr is a publicly routable address +// or if it's a dns address without a special use domain e.g. .local. func IsPublicAddr(a ma.Multiaddr) bool { isPublic := false ma.ForEach(a, func(c ma.Component) bool { @@ -78,12 +110,38 @@ func IsPublicAddr(a ma.Multiaddr) bool { case ma.P_IP6: ip := net.IP(c.RawValue()) isPublic = !inAddrRange(ip, Private6) && !inAddrRange(ip, Unroutable6) + case ma.P_DNS, ma.P_DNS4, ma.P_DNS6, ma.P_DNSADDR: + dnsAddr := c.Value() + isPublic = true + if isSubdomain(dnsAddr, localHostDomain) { + isPublic = false + return false + } + for _, ud := range unResolvableDomains { + if isSubdomain(dnsAddr, ud) { + isPublic = false + return false + } + } + for _, pd := range privateUseDomains { + if isSubdomain(dnsAddr, pd) { + isPublic = false + break + } + } } return false }) return isPublic } +// isSubdomain checks if child is sub domain of parent. It also returns true if child and parent are +// the same domain. +// Parent must have a "." prefix. +func isSubdomain(child, parent string) bool { + return strings.HasSuffix(child, parent) || child == parent[1:] +} + // IsPrivateAddr returns true if the IP part of the mutiaddr is in a private network func IsPrivateAddr(a ma.Multiaddr) bool { isPrivate := false @@ -95,6 +153,13 @@ func IsPrivateAddr(a ma.Multiaddr) bool { isPrivate = inAddrRange(net.IP(c.RawValue()), Private4) case ma.P_IP6: isPrivate = inAddrRange(net.IP(c.RawValue()), Private6) + case ma.P_DNS, ma.P_DNS4, ma.P_DNS6, ma.P_DNSADDR: + dnsAddr := c.Value() + if isSubdomain(dnsAddr, localHostDomain) { + isPrivate = true + } + // We don't check for privateUseDomains because private use domains can + // resolve to public IP addresses } return false }) diff --git a/vendor/github.com/multiformats/go-multiaddr/net/resolve.go b/vendor/github.com/multiformats/go-multiaddr/net/resolve.go index a2478e707..44c2ef1fc 100644 --- a/vendor/github.com/multiformats/go-multiaddr/net/resolve.go +++ b/vendor/github.com/multiformats/go-multiaddr/net/resolve.go @@ -11,22 +11,26 @@ import ( // from the network stack. (this is so you can provide a cached value if resolving many addrs) func ResolveUnspecifiedAddress(resolve ma.Multiaddr, ifaceAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) { // split address into its components - split := ma.Split(resolve) + first, rest := ma.SplitFirst(resolve) // if first component (ip) is not unspecified, use it as is. - if !IsIPUnspecified(split[0]) { + if !IsIPUnspecified(first) { return []ma.Multiaddr{resolve}, nil } + resolveProto := resolve.Protocols()[0].Code out := make([]ma.Multiaddr, 0, len(ifaceAddrs)) for _, ia := range ifaceAddrs { + iafirst, _ := ma.SplitFirst(ia) // must match the first protocol to be resolve. - if ia.Protocols()[0].Code != resolve.Protocols()[0].Code { + if iafirst.Protocol().Code != resolveProto { continue } - split[0] = ia - joined := ma.Join(split...) + joined := ia + if rest != nil { + joined = ma.Join(ia, rest) + } out = append(out, joined) } if len(out) < 1 { diff --git a/vendor/github.com/multiformats/go-multiaddr/transcoders.go b/vendor/github.com/multiformats/go-multiaddr/transcoders.go index 764bb29f1..538774016 100644 --- a/vendor/github.com/multiformats/go-multiaddr/transcoders.go +++ b/vendor/github.com/multiformats/go-multiaddr/transcoders.go @@ -5,6 +5,7 @@ import ( "encoding/base32" "encoding/base64" "encoding/binary" + "errors" "fmt" "net" "strconv" @@ -132,13 +133,10 @@ func ip4BtS(b []byte) (string, error) { var TranscoderPort = NewTranscoderFromFunctions(portStB, portBtS, nil) func portStB(s string) ([]byte, error) { - i, err := strconv.Atoi(s) + i, err := strconv.ParseUint(s, 10, 16) if err != nil { return nil, fmt.Errorf("failed to parse port addr: %s", err) } - if i >= 65536 { - return nil, fmt.Errorf("failed to parse port addr: %s", "greater than 65536") - } b := make([]byte, 2) binary.BigEndian.PutUint16(b, uint16(i)) return b, nil @@ -146,10 +144,10 @@ func portStB(s string) ([]byte, error) { func portBtS(b []byte) (string, error) { i := binary.BigEndian.Uint16(b) - return strconv.Itoa(int(i)), nil + return strconv.FormatUint(uint64(i), 10), nil } -var TranscoderOnion = NewTranscoderFromFunctions(onionStB, onionBtS, nil) +var TranscoderOnion = NewTranscoderFromFunctions(onionStB, onionBtS, onionValidate) func onionStB(s string) ([]byte, error) { addr := strings.Split(s, ":") @@ -157,25 +155,23 @@ func onionStB(s string) ([]byte, error) { return nil, fmt.Errorf("failed to parse onion addr: %s does not contain a port number", s) } - // onion address without the ".onion" substring - if len(addr[0]) != 16 { - return nil, fmt.Errorf("failed to parse onion addr: %s not a Tor onion address", s) - } onionHostBytes, err := base32.StdEncoding.DecodeString(strings.ToUpper(addr[0])) if err != nil { return nil, fmt.Errorf("failed to decode base32 onion addr: %s %s", s, err) } + // onion address without the ".onion" substring are 10 bytes long + if len(onionHostBytes) != 10 { + return nil, fmt.Errorf("failed to parse onion addr: %s not a Tor onion address", s) + } + // onion port number - i, err := strconv.Atoi(addr[1]) + i, err := strconv.ParseUint(addr[1], 10, 16) if err != nil { return nil, fmt.Errorf("failed to parse onion addr: %s", err) } - if i >= 65536 { - return nil, fmt.Errorf("failed to parse onion addr: %s", "port greater than 65536") - } - if i < 1 { - return nil, fmt.Errorf("failed to parse onion addr: %s", "port less than 1") + if i == 0 { + return nil, fmt.Errorf("failed to parse onion addr: %s", "non-zero port") } onionPortBytes := make([]byte, 2) @@ -189,10 +185,24 @@ func onionStB(s string) ([]byte, error) { func onionBtS(b []byte) (string, error) { addr := strings.ToLower(base32.StdEncoding.EncodeToString(b[0:10])) port := binary.BigEndian.Uint16(b[10:12]) - return addr + ":" + strconv.Itoa(int(port)), nil + if port == 0 { + return "", fmt.Errorf("failed to parse onion addr: %s", "non-zero port") + } + return addr + ":" + strconv.FormatUint(uint64(port), 10), nil } -var TranscoderOnion3 = NewTranscoderFromFunctions(onion3StB, onion3BtS, nil) +func onionValidate(b []byte) error { + if len(b) != 12 { + return fmt.Errorf("invalid len for onion addr: got %d expected 12", len(b)) + } + port := binary.BigEndian.Uint16(b[10:12]) + if port == 0 { + return fmt.Errorf("invalid port 0 for onion addr") + } + return nil +} + +var TranscoderOnion3 = NewTranscoderFromFunctions(onion3StB, onion3BtS, onion3Validate) func onion3StB(s string) ([]byte, error) { addr := strings.Split(s, ":") @@ -210,15 +220,12 @@ func onion3StB(s string) ([]byte, error) { } // onion port number - i, err := strconv.Atoi(addr[1]) + i, err := strconv.ParseUint(addr[1], 10, 16) if err != nil { return nil, fmt.Errorf("failed to parse onion addr: %s", err) } - if i >= 65536 { - return nil, fmt.Errorf("failed to parse onion addr: %s", "port greater than 65536") - } - if i < 1 { - return nil, fmt.Errorf("failed to parse onion addr: %s", "port less than 1") + if i == 0 { + return nil, fmt.Errorf("failed to parse onion addr: %s", "non-zero port") } onionPortBytes := make([]byte, 2) @@ -232,26 +239,38 @@ func onion3StB(s string) ([]byte, error) { func onion3BtS(b []byte) (string, error) { addr := strings.ToLower(base32.StdEncoding.EncodeToString(b[0:35])) port := binary.BigEndian.Uint16(b[35:37]) - str := addr + ":" + strconv.Itoa(int(port)) + if port < 1 { + return "", fmt.Errorf("failed to parse onion addr: %s", "port less than 1") + } + str := addr + ":" + strconv.FormatUint(uint64(port), 10) return str, nil } +func onion3Validate(b []byte) error { + if len(b) != 37 { + return fmt.Errorf("invalid len for onion addr: got %d expected 37", len(b)) + } + port := binary.BigEndian.Uint16(b[35:37]) + if port == 0 { + return fmt.Errorf("invalid port 0 for onion addr") + } + return nil +} + var TranscoderGarlic64 = NewTranscoderFromFunctions(garlic64StB, garlic64BtS, garlic64Validate) // i2p uses an alternate character set for base64 addresses. This returns an appropriate encoder. var garlicBase64Encoding = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~") func garlic64StB(s string) ([]byte, error) { - // i2p base64 address will be between 516 and 616 characters long, depending on - // certificate type - if len(s) < 516 || len(s) > 616 { - return nil, fmt.Errorf("failed to parse garlic addr: %s not an i2p base64 address. len: %d", s, len(s)) - } garlicHostBytes, err := garlicBase64Encoding.DecodeString(s) if err != nil { return nil, fmt.Errorf("failed to decode base64 i2p addr: %s %s", s, err) } + if err := garlic64Validate(garlicHostBytes); err != nil { + return nil, err + } return garlicHostBytes, nil } @@ -276,12 +295,6 @@ var TranscoderGarlic32 = NewTranscoderFromFunctions(garlic32StB, garlic32BtS, ga var garlicBase32Encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567") func garlic32StB(s string) ([]byte, error) { - // an i2p base32 address with a length of greater than 55 characters is - // using an Encrypted Leaseset v2. all other base32 addresses will always be - // exactly 52 characters - if len(s) < 55 && len(s) != 52 { - return nil, fmt.Errorf("failed to parse garlic addr: %s not a i2p base32 address. len: %d", s, len(s)) - } for len(s)%8 != 0 { s += "=" } @@ -289,6 +302,10 @@ func garlic32StB(s string) ([]byte, error) { if err != nil { return nil, fmt.Errorf("failed to decode base32 garlic addr: %s, err: %v len: %v", s, err, len(s)) } + + if err := garlic32Validate(garlicHostBytes); err != nil { + return nil, err + } return garlicHostBytes, nil } @@ -300,8 +317,9 @@ func garlic32BtS(b []byte) (string, error) { } func garlic32Validate(b []byte) error { - // an i2p base64 for an Encrypted Leaseset v2 will be at least 35 bytes - // long other than that, they will be exactly 32 bytes + // an i2p address with encrypted leaseset has len >= 35 bytes + // all other addresses will always be exactly 32 bytes + // https://geti2p.net/spec/b32encrypted if len(b) < 35 && len(b) != 32 { return fmt.Errorf("failed to validate garlic addr: %s not an i2p base32 address. len: %d", b, len(b)) } @@ -319,6 +337,9 @@ func p2pStB(s string) ([]byte, error) { if err != nil { return nil, fmt.Errorf("failed to parse p2p addr: %s %s", s, err) } + if err := p2pVal(m); err != nil { + return nil, err + } return m, nil } @@ -329,6 +350,9 @@ func p2pStB(s string) ([]byte, error) { } if ty := c.Type(); ty == cid.Libp2pKey { + if err := p2pVal(c.Hash()); err != nil { + return nil, err + } return c.Hash(), nil } else { return nil, fmt.Errorf("failed to parse p2p addr: %s has the invalid codec %d", s, ty) @@ -336,8 +360,20 @@ func p2pStB(s string) ([]byte, error) { } func p2pVal(b []byte) error { - _, err := mh.Cast(b) - return err + h, err := mh.Decode([]byte(b)) + if err != nil { + return fmt.Errorf("invalid multihash: %s", err) + } + // Peer IDs require either sha256 or identity multihash + // https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids + if h.Code != mh.SHA2_256 && h.Code != mh.IDENTITY { + return fmt.Errorf("invalid multihash code %d expected sha-256 or identity", h.Code) + } + // This check should ideally be in multihash. sha256 digest lengths MUST be 32 + if h.Code == mh.SHA2_256 && h.Length != 32 { + return fmt.Errorf("invalid digest length %d for sha256 addr: expected 32", h.Length) + } + return nil } func p2pBtS(b []byte) (string, error) { @@ -348,7 +384,7 @@ func p2pBtS(b []byte) (string, error) { return m.B58String(), nil } -var TranscoderUnix = NewTranscoderFromFunctions(unixStB, unixBtS, nil) +var TranscoderUnix = NewTranscoderFromFunctions(unixStB, unixBtS, unixValidate) func unixStB(s string) ([]byte, error) { return []byte(s), nil @@ -358,9 +394,27 @@ func unixBtS(b []byte) (string, error) { return string(b), nil } +func unixValidate(b []byte) error { + // The string to bytes parser requires that all Path protocols begin with a '/' + // file://./codec.go#L49 + if len(b) < 2 { + return fmt.Errorf("byte slice too short: %d", len(b)) + } + if b[0] != '/' { + return errors.New("path protocol must begin with '/'") + } + if b[len(b)-1] == '/' { + return errors.New("unix socket path must not end in '/'") + } + return nil +} + var TranscoderDns = NewTranscoderFromFunctions(dnsStB, dnsBtS, dnsVal) func dnsVal(b []byte) error { + if len(b) == 0 { + return fmt.Errorf("empty dns addr") + } if bytes.IndexByte(b, '/') >= 0 { return fmt.Errorf("domain name %q contains a slash", string(b)) } @@ -368,14 +422,18 @@ func dnsVal(b []byte) error { } func dnsStB(s string) ([]byte, error) { - return []byte(s), nil + b := []byte(s) + if err := dnsVal(b); err != nil { + return nil, err + } + return b, nil } func dnsBtS(b []byte) (string, error) { return string(b), nil } -var TranscoderCertHash = NewTranscoderFromFunctions(certHashStB, certHashBtS, nil) +var TranscoderCertHash = NewTranscoderFromFunctions(certHashStB, certHashBtS, validateCertHash) func certHashStB(s string) ([]byte, error) { _, data, err := multibase.Decode(s) @@ -391,3 +449,8 @@ func certHashStB(s string) ([]byte, error) { func certHashBtS(b []byte) (string, error) { return multibase.Encode(multibase.Base64url, b) } + +func validateCertHash(b []byte) error { + _, err := mh.Decode(b) + return err +} diff --git a/vendor/github.com/multiformats/go-multiaddr/util.go b/vendor/github.com/multiformats/go-multiaddr/util.go index cf4469aff..b0ac7ee16 100644 --- a/vendor/github.com/multiformats/go-multiaddr/util.go +++ b/vendor/github.com/multiformats/go-multiaddr/util.go @@ -27,16 +27,14 @@ func Join(ms ...Multiaddr) Multiaddr { } length := 0 - bs := make([][]byte, len(ms)) - for i, m := range ms { - bs[i] = m.Bytes() - length += len(bs[i]) + for _, m := range ms { + length += len(m.Bytes()) } bidx := 0 b := make([]byte, length) - for _, mb := range bs { - bidx += copy(b[bidx:], mb) + for _, mb := range ms { + bidx += copy(b[bidx:], mb.Bytes()) } return &multiaddr{bytes: b} } @@ -159,6 +157,7 @@ func SplitFunc(m Multiaddr, cb func(Component) bool) (Multiaddr, Multiaddr) { // ForEach walks over the multiaddr, component by component. // // This function iterates over components *by value* to avoid allocating. +// Return true to continue iteration, false to stop. func ForEach(m Multiaddr, cb func(c Component) bool) { // Shortcut if we already have a component if c, ok := m.(*Component); ok { diff --git a/vendor/github.com/multiformats/go-multiaddr/version.json b/vendor/github.com/multiformats/go-multiaddr/version.json index b6bb0741a..5aa93fc5e 100644 --- a/vendor/github.com/multiformats/go-multiaddr/version.json +++ b/vendor/github.com/multiformats/go-multiaddr/version.json @@ -1,3 +1,3 @@ { - "version": "v0.10.1" + "version": "v0.12.3" } diff --git a/vendor/github.com/multiformats/go-multistream/client.go b/vendor/github.com/multiformats/go-multistream/client.go index 013dd5abc..25e81e2ac 100644 --- a/vendor/github.com/multiformats/go-multistream/client.go +++ b/vendor/github.com/multiformats/go-multistream/client.go @@ -2,15 +2,11 @@ package multistream import ( "bytes" - "crypto/rand" - "encoding/binary" "errors" "fmt" "io" "os" "runtime/debug" - "strconv" - "strings" ) // ErrNotSupported is the error returned when the muxer doesn't support @@ -34,12 +30,6 @@ func (e ErrNotSupported[T]) Is(target error) bool { // specified. var ErrNoProtocols = errors.New("no protocols specified") -const ( - tieBreakerPrefix = "select:" - initiator = "initiator" - responder = "responder" -) - // SelectProtoOrFail performs the initial multistream handshake // to inform the muxer of the protocol that will be used to communicate // on this ReadWriteCloser. It returns an error if, for example, @@ -110,84 +100,6 @@ func SelectOneOf[T StringLike](protos []T, rwc io.ReadWriteCloser) (proto T, err return proto, err } -const simOpenProtocol = "/libp2p/simultaneous-connect" - -// SelectWithSimopenOrFail performs protocol negotiation with the simultaneous open extension. -// The returned boolean indicator will be true if we should act as a server. -func SelectWithSimopenOrFail[T StringLike](protos []T, rwc io.ReadWriteCloser) (proto T, isServer bool, err error) { - defer func() { - if rerr := recover(); rerr != nil { - fmt.Fprintf(os.Stderr, "caught panic: %s\n%s\n", rerr, debug.Stack()) - err = fmt.Errorf("panic selecting protocol with simopen: %s", rerr) - } - }() - - if len(protos) == 0 { - return "", false, ErrNoProtocols - } - - werrCh := make(chan error, 1) - go func() { - var buf bytes.Buffer - if err := delitmWriteAll(&buf, []byte(ProtocolID), []byte(simOpenProtocol), []byte(protos[0])); err != nil { - werrCh <- err - return - } - - _, err := io.Copy(rwc, &buf) - werrCh <- err - }() - - if err := readMultistreamHeader(rwc); err != nil { - return "", false, err - } - - tok, err := ReadNextToken[T](rwc) - if err != nil { - return "", false, err - } - - if err = <-werrCh; err != nil { - return "", false, err - } - - switch tok { - case simOpenProtocol: - // simultaneous open - return simOpen(protos, rwc) - case "na": - // client open - proto, err := clientOpen(protos, rwc) - if err != nil { - return "", false, err - } - return proto, false, nil - default: - return "", false, fmt.Errorf("unexpected response: %s", tok) - } -} - -func clientOpen[T StringLike](protos []T, rwc io.ReadWriteCloser) (T, error) { - // check to see if we selected the pipelined protocol - tok, err := ReadNextToken[T](rwc) - if err != nil { - return "", err - } - - switch tok { - case protos[0]: - return tok, nil - case "na": - proto, err := selectProtosOrFail(protos[1:], rwc) - if _, ok := err.(ErrNotSupported[T]); ok { - return "", ErrNotSupported[T]{protos} - } - return proto, err - default: - return "", fmt.Errorf("unexpected response: %s", tok) - } -} - func selectProtosOrFail[T StringLike](protos []T, rwc io.ReadWriteCloser) (T, error) { for _, p := range protos { err := trySelect(p, rwc) @@ -202,131 +114,6 @@ func selectProtosOrFail[T StringLike](protos []T, rwc io.ReadWriteCloser) (T, er return "", ErrNotSupported[T]{protos} } -func simOpen[T StringLike](protos []T, rwc io.ReadWriteCloser) (T, bool, error) { - randBytes := make([]byte, 8) - _, err := rand.Read(randBytes) - if err != nil { - return "", false, err - } - myNonce := binary.LittleEndian.Uint64(randBytes) - - werrCh := make(chan error, 1) - go func() { - myselect := []byte(tieBreakerPrefix + strconv.FormatUint(myNonce, 10)) - err := delimWriteBuffered(rwc, myselect) - werrCh <- err - }() - - // skip exactly one protocol - // see https://github.com/multiformats/go-multistream/pull/42#discussion_r558757135 - _, err = ReadNextToken[T](rwc) - if err != nil { - return "", false, err - } - - // read the tie breaker nonce - tok, err := ReadNextToken[T](rwc) - if err != nil { - return "", false, err - } - if !strings.HasPrefix(string(tok), tieBreakerPrefix) { - return "", false, errors.New("tie breaker nonce not sent with the correct prefix") - } - - if err = <-werrCh; err != nil { - return "", false, err - } - - peerNonce, err := strconv.ParseUint(string(tok[len(tieBreakerPrefix):]), 10, 64) - if err != nil { - return "", false, err - } - - var iamserver bool - - if peerNonce == myNonce { - return "", false, errors.New("failed client selection; identical nonces") - } - iamserver = peerNonce > myNonce - - var proto T - if iamserver { - proto, err = simOpenSelectServer(protos, rwc) - } else { - proto, err = simOpenSelectClient(protos, rwc) - } - - return proto, iamserver, err -} - -func simOpenSelectServer[T StringLike](protos []T, rwc io.ReadWriteCloser) (T, error) { - werrCh := make(chan error, 1) - go func() { - err := delimWriteBuffered(rwc, []byte(responder)) - werrCh <- err - }() - - tok, err := ReadNextToken[T](rwc) - if err != nil { - return "", err - } - if tok != initiator { - return "", fmt.Errorf("unexpected response: %s", tok) - } - if err = <-werrCh; err != nil { - return "", err - } - for { - tok, err = ReadNextToken[T](rwc) - - if err == io.EOF { - return "", ErrNotSupported[T]{protos} - } - - if err != nil { - return "", err - } - - for _, p := range protos { - if tok == p { - err = delimWriteBuffered(rwc, []byte(p)) - if err != nil { - return "", err - } - - return p, nil - } - } - - err = delimWriteBuffered(rwc, []byte("na")) - if err != nil { - return "", err - } - } - -} - -func simOpenSelectClient[T StringLike](protos []T, rwc io.ReadWriteCloser) (T, error) { - werrCh := make(chan error, 1) - go func() { - err := delimWriteBuffered(rwc, []byte(initiator)) - werrCh <- err - }() - - tok, err := ReadNextToken[T](rwc) - if err != nil { - return "", err - } - if tok != responder { - return "", fmt.Errorf("unexpected response: %s", tok) - } - if err = <-werrCh; err != nil { - return "", err - } - - return selectProtosOrFail(protos, rwc) -} - func readMultistreamHeader(r io.Reader) error { tok, err := ReadNextToken[string](r) if err != nil { diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go index 0b9b19fe7..958daccbf 100644 --- a/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go +++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go @@ -244,9 +244,7 @@ func labelFromCallExpr(ce *ast.CallExpr) []string { } if id.Name == "Label" { ls := extractLabels(expr) - for _, label := range ls { - labels = append(labels, label) - } + labels = append(labels, ls...) } } } diff --git a/vendor/github.com/onsi/ginkgo/v2/types/config.go b/vendor/github.com/onsi/ginkgo/v2/types/config.go index 1014c7b49..c88fc85a7 100644 --- a/vendor/github.com/onsi/ginkgo/v2/types/config.go +++ b/vendor/github.com/onsi/ginkgo/v2/types/config.go @@ -27,6 +27,7 @@ type SuiteConfig struct { FailOnPending bool FailFast bool FlakeAttempts int + MustPassRepeatedly int DryRun bool PollProgressAfter time.Duration PollProgressInterval time.Duration diff --git a/vendor/github.com/onsi/ginkgo/v2/types/errors.go b/vendor/github.com/onsi/ginkgo/v2/types/errors.go index 1e0dbfd9d..4fbdc3e9b 100644 --- a/vendor/github.com/onsi/ginkgo/v2/types/errors.go +++ b/vendor/github.com/onsi/ginkgo/v2/types/errors.go @@ -453,8 +453,8 @@ func (g ginkgoErrors) InvalidEntryDescription(cl CodeLocation) error { func (g ginkgoErrors) MissingParametersForTableFunction(cl CodeLocation) error { return GinkgoError{ - Heading: fmt.Sprintf("No parameters have been passed to the Table Function"), - Message: fmt.Sprintf("The Table Function expected at least 1 parameter"), + Heading: "No parameters have been passed to the Table Function", + Message: "The Table Function expected at least 1 parameter", CodeLocation: cl, DocLink: "table-specs", } diff --git a/vendor/github.com/onsi/ginkgo/v2/types/types.go b/vendor/github.com/onsi/ginkgo/v2/types/types.go index d048a8ada..aae69b04c 100644 --- a/vendor/github.com/onsi/ginkgo/v2/types/types.go +++ b/vendor/github.com/onsi/ginkgo/v2/types/types.go @@ -97,9 +97,7 @@ func (report Report) Add(other Report) Report { report.RunTime = report.EndTime.Sub(report.StartTime) reports := make(SpecReports, len(report.SpecReports)+len(other.SpecReports)) - for i := range report.SpecReports { - reports[i] = report.SpecReports[i] - } + copy(reports, report.SpecReports) offset := len(report.SpecReports) for i := range other.SpecReports { reports[i+offset] = other.SpecReports[i] diff --git a/vendor/github.com/onsi/ginkgo/v2/types/version.go b/vendor/github.com/onsi/ginkgo/v2/types/version.go index f895739b8..a37f30828 100644 --- a/vendor/github.com/onsi/ginkgo/v2/types/version.go +++ b/vendor/github.com/onsi/ginkgo/v2/types/version.go @@ -1,3 +1,3 @@ package types -const VERSION = "2.11.0" +const VERSION = "2.13.0" diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go index 6a7a91e55..4e7717d53 100644 --- a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go +++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go @@ -12,10 +12,12 @@ type Spec struct { Root *Root `json:"root,omitempty"` // Hostname configures the container's hostname. Hostname string `json:"hostname,omitempty"` + // Domainname configures the container's domainname. + Domainname string `json:"domainname,omitempty"` // Mounts configures additional mounts (on top of Root). Mounts []Mount `json:"mounts,omitempty"` // Hooks configures callbacks for container lifecycle events. - Hooks *Hooks `json:"hooks,omitempty" platform:"linux,solaris"` + Hooks *Hooks `json:"hooks,omitempty" platform:"linux,solaris,zos"` // Annotations contains arbitrary metadata for the container. Annotations map[string]string `json:"annotations,omitempty"` @@ -27,6 +29,36 @@ type Spec struct { Windows *Windows `json:"windows,omitempty" platform:"windows"` // VM specifies configuration for virtual-machine-based containers. VM *VM `json:"vm,omitempty" platform:"vm"` + // ZOS is platform-specific configuration for z/OS based containers. + ZOS *ZOS `json:"zos,omitempty" platform:"zos"` +} + +// Scheduler represents the scheduling attributes for a process. It is based on +// the Linux sched_setattr(2) syscall. +type Scheduler struct { + // Policy represents the scheduling policy (e.g., SCHED_FIFO, SCHED_RR, SCHED_OTHER). + Policy LinuxSchedulerPolicy `json:"policy"` + + // Nice is the nice value for the process, which affects its priority. + Nice int32 `json:"nice,omitempty"` + + // Priority represents the static priority of the process. + Priority int32 `json:"priority,omitempty"` + + // Flags is an array of scheduling flags. + Flags []LinuxSchedulerFlag `json:"flags,omitempty"` + + // The following ones are used by the DEADLINE scheduler. + + // Runtime is the amount of time in nanoseconds during which the process + // is allowed to run in a given period. + Runtime uint64 `json:"runtime,omitempty"` + + // Deadline is the absolute deadline for the process to complete its execution. + Deadline uint64 `json:"deadline,omitempty"` + + // Period is the length of the period in nanoseconds used for determining the process runtime. + Period uint64 `json:"period,omitempty"` } // Process contains information to start a specific application inside the container. @@ -49,15 +81,19 @@ type Process struct { // Capabilities are Linux capabilities that are kept for the process. Capabilities *LinuxCapabilities `json:"capabilities,omitempty" platform:"linux"` // Rlimits specifies rlimit options to apply to the process. - Rlimits []POSIXRlimit `json:"rlimits,omitempty" platform:"linux,solaris"` + Rlimits []POSIXRlimit `json:"rlimits,omitempty" platform:"linux,solaris,zos"` // NoNewPrivileges controls whether additional privileges could be gained by processes in the container. NoNewPrivileges bool `json:"noNewPrivileges,omitempty" platform:"linux"` // ApparmorProfile specifies the apparmor profile for the container. ApparmorProfile string `json:"apparmorProfile,omitempty" platform:"linux"` // Specify an oom_score_adj for the container. OOMScoreAdj *int `json:"oomScoreAdj,omitempty" platform:"linux"` + // Scheduler specifies the scheduling attributes for a process + Scheduler *Scheduler `json:"scheduler,omitempty" platform:"linux"` // SelinuxLabel specifies the selinux context that the container process is run as. SelinuxLabel string `json:"selinuxLabel,omitempty" platform:"linux"` + // IOPriority contains the I/O priority settings for the cgroup. + IOPriority *LinuxIOPriority `json:"ioPriority,omitempty" platform:"linux"` } // LinuxCapabilities specifies the list of allowed capabilities that are kept for a process. @@ -75,6 +111,22 @@ type LinuxCapabilities struct { Ambient []string `json:"ambient,omitempty" platform:"linux"` } +// IOPriority represents I/O priority settings for the container's processes within the process group. +type LinuxIOPriority struct { + Class IOPriorityClass `json:"class"` + Priority int `json:"priority"` +} + +// IOPriorityClass represents an I/O scheduling class. +type IOPriorityClass string + +// Possible values for IOPriorityClass. +const ( + IOPRIO_CLASS_RT IOPriorityClass = "IOPRIO_CLASS_RT" + IOPRIO_CLASS_BE IOPriorityClass = "IOPRIO_CLASS_BE" + IOPRIO_CLASS_IDLE IOPriorityClass = "IOPRIO_CLASS_IDLE" +) + // Box specifies dimensions of a rectangle. Used for specifying the size of a console. type Box struct { // Height is the vertical dimension of a box. @@ -86,11 +138,11 @@ type Box struct { // User specifies specific user (and group) information for the container process. type User struct { // UID is the user id. - UID uint32 `json:"uid" platform:"linux,solaris"` + UID uint32 `json:"uid" platform:"linux,solaris,zos"` // GID is the group id. - GID uint32 `json:"gid" platform:"linux,solaris"` + GID uint32 `json:"gid" platform:"linux,solaris,zos"` // Umask is the umask for the init process. - Umask *uint32 `json:"umask,omitempty" platform:"linux,solaris"` + Umask *uint32 `json:"umask,omitempty" platform:"linux,solaris,zos"` // AdditionalGids are additional group ids set for the container's process. AdditionalGids []uint32 `json:"additionalGids,omitempty" platform:"linux,solaris"` // Username is the user name. @@ -110,11 +162,16 @@ type Mount struct { // Destination is the absolute path where the mount will be placed in the container. Destination string `json:"destination"` // Type specifies the mount kind. - Type string `json:"type,omitempty" platform:"linux,solaris"` + Type string `json:"type,omitempty" platform:"linux,solaris,zos"` // Source specifies the source path of the mount. Source string `json:"source,omitempty"` // Options are fstab style mount options. Options []string `json:"options,omitempty"` + + // UID/GID mappings used for changing file owners w/o calling chown, fs should support it. + // Every mount point could have its own mapping. + UIDMappings []LinuxIDMapping `json:"uidMappings,omitempty" platform:"linux"` + GIDMappings []LinuxIDMapping `json:"gidMappings,omitempty" platform:"linux"` } // Hook specifies a command that is run at a particular event in the lifecycle of a container @@ -178,10 +235,12 @@ type Linux struct { // MountLabel specifies the selinux context for the mounts in the container. MountLabel string `json:"mountLabel,omitempty"` // IntelRdt contains Intel Resource Director Technology (RDT) information for - // handling resource constraints (e.g., L3 cache, memory bandwidth) for the container + // handling resource constraints and monitoring metrics (e.g., L3 cache, memory bandwidth) for the container IntelRdt *LinuxIntelRdt `json:"intelRdt,omitempty"` // Personality contains configuration for the Linux personality syscall Personality *LinuxPersonality `json:"personality,omitempty"` + // TimeOffsets specifies the offset for supporting time namespaces. + TimeOffsets map[string]LinuxTimeOffset `json:"timeOffsets,omitempty"` } // LinuxNamespace is the configuration for a Linux namespace @@ -211,6 +270,8 @@ const ( UserNamespace LinuxNamespaceType = "user" // CgroupNamespace for isolating cgroup hierarchies CgroupNamespace LinuxNamespaceType = "cgroup" + // TimeNamespace for isolating the clocks + TimeNamespace LinuxNamespaceType = "time" ) // LinuxIDMapping specifies UID/GID mappings @@ -223,6 +284,14 @@ type LinuxIDMapping struct { Size uint32 `json:"size"` } +// LinuxTimeOffset specifies the offset for Time Namespace +type LinuxTimeOffset struct { + // Secs is the offset of clock (in secs) in the container + Secs int64 `json:"secs,omitempty"` + // Nanosecs is the additional offset for Secs (in nanosecs) + Nanosecs uint32 `json:"nanosecs,omitempty"` +} + // POSIXRlimit type and restrictions type POSIXRlimit struct { // Type of the rlimit to set @@ -233,12 +302,13 @@ type POSIXRlimit struct { Soft uint64 `json:"soft"` } -// LinuxHugepageLimit structure corresponds to limiting kernel hugepages +// LinuxHugepageLimit structure corresponds to limiting kernel hugepages. +// Default to reservation limits if supported. Otherwise fallback to page fault limits. type LinuxHugepageLimit struct { - // Pagesize is the hugepage size - // Format: "B' (e.g. 64KB, 2MB, 1GB, etc.) + // Pagesize is the hugepage size. + // Format: "B' (e.g. 64KB, 2MB, 1GB, etc.). Pagesize string `json:"pageSize"` - // Limit is the limit of "hugepagesize" hugetlb usage + // Limit is the limit of "hugepagesize" hugetlb reservations (if supported) or usage. Limit uint64 `json:"limit"` } @@ -250,8 +320,8 @@ type LinuxInterfacePriority struct { Priority uint32 `json:"priority"` } -// linuxBlockIODevice holds major:minor format supported in blkio cgroup -type linuxBlockIODevice struct { +// LinuxBlockIODevice holds major:minor format supported in blkio cgroup +type LinuxBlockIODevice struct { // Major is the device's major number. Major int64 `json:"major"` // Minor is the device's minor number. @@ -260,7 +330,7 @@ type linuxBlockIODevice struct { // LinuxWeightDevice struct holds a `major:minor weight` pair for weightDevice type LinuxWeightDevice struct { - linuxBlockIODevice + LinuxBlockIODevice // Weight is the bandwidth rate for the device. Weight *uint16 `json:"weight,omitempty"` // LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, CFQ scheduler only @@ -269,7 +339,7 @@ type LinuxWeightDevice struct { // LinuxThrottleDevice struct holds a `major:minor rate_per_second` pair type LinuxThrottleDevice struct { - linuxBlockIODevice + LinuxBlockIODevice // Rate is the IO rate limit per cgroup per device Rate uint64 `json:"rate"` } @@ -310,6 +380,10 @@ type LinuxMemory struct { DisableOOMKiller *bool `json:"disableOOMKiller,omitempty"` // Enables hierarchical memory accounting UseHierarchy *bool `json:"useHierarchy,omitempty"` + // CheckBeforeUpdate enables checking if a new memory limit is lower + // than the current usage during update, and if so, rejecting the new + // limit. + CheckBeforeUpdate *bool `json:"checkBeforeUpdate,omitempty"` } // LinuxCPU for Linux cgroup 'cpu' resource management @@ -318,6 +392,9 @@ type LinuxCPU struct { Shares *uint64 `json:"shares,omitempty"` // CPU hardcap limit (in usecs). Allowed cpu time in a given period. Quota *int64 `json:"quota,omitempty"` + // CPU hardcap burst limit (in usecs). Allowed accumulated cpu time additionally for burst in a + // given period. + Burst *uint64 `json:"burst,omitempty"` // CPU period to be used for hardcapping (in usecs). Period *uint64 `json:"period,omitempty"` // How much time realtime scheduling may use (in usecs). @@ -328,6 +405,8 @@ type LinuxCPU struct { Cpus string `json:"cpus,omitempty"` // List of memory nodes in the cpuset. Default is to use any available memory node. Mems string `json:"mems,omitempty"` + // cgroups are configured with minimum weight, 0: default behavior, 1: SCHED_IDLE. + Idle *int64 `json:"idle,omitempty"` } // LinuxPids for Linux cgroup 'pids' resource management (Linux 4.3) @@ -364,7 +443,7 @@ type LinuxResources struct { Pids *LinuxPids `json:"pids,omitempty"` // BlockIO restriction configuration BlockIO *LinuxBlockIO `json:"blockIO,omitempty"` - // Hugetlb limit (in bytes) + // Hugetlb limits (in bytes). Default to reservation limits if supported. HugepageLimits []LinuxHugepageLimit `json:"hugepageLimits,omitempty"` // Network restriction configuration Network *LinuxNetwork `json:"network,omitempty"` @@ -522,11 +601,21 @@ type WindowsMemoryResources struct { // WindowsCPUResources contains CPU resource management settings. type WindowsCPUResources struct { - // Number of CPUs available to the container. + // Count is the number of CPUs available to the container. It represents the + // fraction of the configured processor `count` in a container in relation + // to the processors available in the host. The fraction ultimately + // determines the portion of processor cycles that the threads in a + // container can use during each scheduling interval, as the number of + // cycles per 10,000 cycles. Count *uint64 `json:"count,omitempty"` - // CPU shares (relative weight to other containers with cpu shares). + // Shares limits the share of processor time given to the container relative + // to other workloads on the processor. The processor `shares` (`weight` at + // the platform level) is a value between 0 and 10000. Shares *uint16 `json:"shares,omitempty"` - // Specifies the portion of processor cycles that this container can use as a percentage times 100. + // Maximum determines the portion of processor cycles that the threads in a + // container can use during each scheduling interval, as the number of + // cycles per 10,000 cycles. Set processor `maximum` to a percentage times + // 100. Maximum *uint16 `json:"maximum,omitempty"` } @@ -613,6 +702,23 @@ type Arch string // LinuxSeccompFlag is a flag to pass to seccomp(2). type LinuxSeccompFlag string +const ( + // LinuxSeccompFlagLog is a seccomp flag to request all returned + // actions except SECCOMP_RET_ALLOW to be logged. An administrator may + // override this filter flag by preventing specific actions from being + // logged via the /proc/sys/kernel/seccomp/actions_logged file. (since + // Linux 4.14) + LinuxSeccompFlagLog LinuxSeccompFlag = "SECCOMP_FILTER_FLAG_LOG" + + // LinuxSeccompFlagSpecAllow can be used to disable Speculative Store + // Bypass mitigation. (since Linux 4.17) + LinuxSeccompFlagSpecAllow LinuxSeccompFlag = "SECCOMP_FILTER_FLAG_SPEC_ALLOW" + + // LinuxSeccompFlagWaitKillableRecv can be used to switch to the wait + // killable semantics. (since Linux 5.19) + LinuxSeccompFlagWaitKillableRecv LinuxSeccompFlag = "SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV" +) + // Additional architectures permitted to be used for system calls // By default only the native architecture of the kernel is permitted const ( @@ -683,8 +789,9 @@ type LinuxSyscall struct { Args []LinuxSeccompArg `json:"args,omitempty"` } -// LinuxIntelRdt has container runtime resource constraints for Intel RDT -// CAT and MBA features which introduced in Linux 4.10 and 4.12 kernel +// LinuxIntelRdt has container runtime resource constraints for Intel RDT CAT and MBA +// features and flags enabling Intel RDT CMT and MBM features. +// Intel RDT features are available in Linux 4.14 and newer kernel versions. type LinuxIntelRdt struct { // The identity for RDT Class of Service ClosID string `json:"closID,omitempty"` @@ -697,4 +804,76 @@ type LinuxIntelRdt struct { // The unit of memory bandwidth is specified in "percentages" by // default, and in "MBps" if MBA Software Controller is enabled. MemBwSchema string `json:"memBwSchema,omitempty"` + + // EnableCMT is the flag to indicate if the Intel RDT CMT is enabled. CMT (Cache Monitoring Technology) supports monitoring of + // the last-level cache (LLC) occupancy for the container. + EnableCMT bool `json:"enableCMT,omitempty"` + + // EnableMBM is the flag to indicate if the Intel RDT MBM is enabled. MBM (Memory Bandwidth Monitoring) supports monitoring of + // total and local memory bandwidth for the container. + EnableMBM bool `json:"enableMBM,omitempty"` } + +// ZOS contains platform-specific configuration for z/OS based containers. +type ZOS struct { + // Devices are a list of device nodes that are created for the container + Devices []ZOSDevice `json:"devices,omitempty"` +} + +// ZOSDevice represents the mknod information for a z/OS special device file +type ZOSDevice struct { + // Path to the device. + Path string `json:"path"` + // Device type, block, char, etc. + Type string `json:"type"` + // Major is the device's major number. + Major int64 `json:"major"` + // Minor is the device's minor number. + Minor int64 `json:"minor"` + // FileMode permission bits for the device. + FileMode *os.FileMode `json:"fileMode,omitempty"` + // UID of the device. + UID *uint32 `json:"uid,omitempty"` + // Gid of the device. + GID *uint32 `json:"gid,omitempty"` +} + +// LinuxSchedulerPolicy represents different scheduling policies used with the Linux Scheduler +type LinuxSchedulerPolicy string + +const ( + // SchedOther is the default scheduling policy + SchedOther LinuxSchedulerPolicy = "SCHED_OTHER" + // SchedFIFO is the First-In-First-Out scheduling policy + SchedFIFO LinuxSchedulerPolicy = "SCHED_FIFO" + // SchedRR is the Round-Robin scheduling policy + SchedRR LinuxSchedulerPolicy = "SCHED_RR" + // SchedBatch is the Batch scheduling policy + SchedBatch LinuxSchedulerPolicy = "SCHED_BATCH" + // SchedISO is the Isolation scheduling policy + SchedISO LinuxSchedulerPolicy = "SCHED_ISO" + // SchedIdle is the Idle scheduling policy + SchedIdle LinuxSchedulerPolicy = "SCHED_IDLE" + // SchedDeadline is the Deadline scheduling policy + SchedDeadline LinuxSchedulerPolicy = "SCHED_DEADLINE" +) + +// LinuxSchedulerFlag represents the flags used by the Linux Scheduler. +type LinuxSchedulerFlag string + +const ( + // SchedFlagResetOnFork represents the reset on fork scheduling flag + SchedFlagResetOnFork LinuxSchedulerFlag = "SCHED_FLAG_RESET_ON_FORK" + // SchedFlagReclaim represents the reclaim scheduling flag + SchedFlagReclaim LinuxSchedulerFlag = "SCHED_FLAG_RECLAIM" + // SchedFlagDLOverrun represents the deadline overrun scheduling flag + SchedFlagDLOverrun LinuxSchedulerFlag = "SCHED_FLAG_DL_OVERRUN" + // SchedFlagKeepPolicy represents the keep policy scheduling flag + SchedFlagKeepPolicy LinuxSchedulerFlag = "SCHED_FLAG_KEEP_POLICY" + // SchedFlagKeepParams represents the keep parameters scheduling flag + SchedFlagKeepParams LinuxSchedulerFlag = "SCHED_FLAG_KEEP_PARAMS" + // SchedFlagUtilClampMin represents the utilization clamp minimum scheduling flag + SchedFlagUtilClampMin LinuxSchedulerFlag = "SCHED_FLAG_UTIL_CLAMP_MIN" + // SchedFlagUtilClampMin represents the utilization clamp maximum scheduling flag + SchedFlagUtilClampMax LinuxSchedulerFlag = "SCHED_FLAG_UTIL_CLAMP_MAX" +) diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go index 596af0c2f..b3fca349c 100644 --- a/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go +++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/version.go @@ -6,12 +6,12 @@ const ( // VersionMajor is for an API incompatible changes VersionMajor = 1 // VersionMinor is for functionality in a backwards-compatible manner - VersionMinor = 0 + VersionMinor = 1 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 2 + VersionPatch = 0 // VersionDev indicates development branch. Releases will be empty string. - VersionDev = "-dev" + VersionDev = "" ) // Version is the specification version that the package types support. diff --git a/vendor/github.com/pion/datachannel/.gitignore b/vendor/github.com/pion/datachannel/.gitignore index 83db74ba5..f977e7485 100644 --- a/vendor/github.com/pion/datachannel/.gitignore +++ b/vendor/github.com/pion/datachannel/.gitignore @@ -22,3 +22,4 @@ cover.out *.wasm examples/sfu-ws/cert.pem examples/sfu-ws/key.pem +wasm_exec.js diff --git a/vendor/github.com/pion/datachannel/.golangci.yml b/vendor/github.com/pion/datachannel/.golangci.yml index d6162c970..d7a88eca3 100644 --- a/vendor/github.com/pion/datachannel/.golangci.yml +++ b/vendor/github.com/pion/datachannel/.golangci.yml @@ -15,14 +15,22 @@ linters-settings: linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully + - contextcheck # check the function whether use a non-inherited context - deadcode # Finds unused code + - decorder # check declaration order and count of types, constants, variables and functions - depguard # Go linter that checks if package imports are in a list of acceptable packages - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection + - durationcheck # check for two durations multiplied together - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. - exhaustive # check exhaustiveness of enum switch statements - exportloopref # checks for pointers to enclosing loop variables + - forcetypeassert # finds forced type assertions - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - gochecknoinits # Checks that no init functions are present in Go code @@ -35,40 +43,62 @@ linters: - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - gosimple # Linter for Go source code that specializes in simplifying a code - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - grouper # An analyzer to analyze expression groups. + - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used - misspell # Finds commonly misspelled English words in comments - nakedret # Finds naked returns in functions greater than a specified function length + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. - noctx # noctx finds sending http request without context.Context - - scopelint # Scopelint checks for unpinned variables in go programs + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # golint replacement, finds style mistakes - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - structcheck # Finds unused struct fields - stylecheck # Stylecheck is a replacement for golint + - tagliatelle # Checks the struct tags. + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types - varcheck # Finds unused global variables and constants + - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: + - containedctx # containedctx is a linter that detects struct contained context.Context field + - cyclop # checks function and package cyclomatic complexity + - exhaustivestruct # Checks if all struct's fields are initialized + - forbidigo # Forbids identifiers - funlen # Tool for detection of long functions - gocyclo # Computes and checks the cyclomatic complexity of functions - godot # Check if comments end in a period - gomnd # An analyzer to detect magic numbers. + - ifshort # Checks that your code uses short syntax for if-statements whenever possible + - ireturn # Accept Interfaces, Return Concrete Types - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - nestif # Reports deeply nested if statements - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - nolintlint # Reports ill-formed or insufficient nolint directives + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - prealloc # Finds slice declarations that could potentially be preallocated + - promlinter # Check Prometheus metrics naming via promlint - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. - testpackage # linter that makes you use a separate _test package + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers + - varnamelen # checks that the length of a variable's name matches its scope + - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! issues: diff --git a/vendor/github.com/pion/datachannel/AUTHORS.txt b/vendor/github.com/pion/datachannel/AUTHORS.txt index 92073e2c1..c5d9d8d34 100644 --- a/vendor/github.com/pion/datachannel/AUTHORS.txt +++ b/vendor/github.com/pion/datachannel/AUTHORS.txt @@ -6,6 +6,7 @@ Atsushi Watanabe backkem Benny Daon +Chinmay Kousik Eric Daniels Hugo Arregui Hugo Arregui diff --git a/vendor/github.com/pion/datachannel/datachannel.go b/vendor/github.com/pion/datachannel/datachannel.go index 11f30cc78..0b125d662 100644 --- a/vendor/github.com/pion/datachannel/datachannel.go +++ b/vendor/github.com/pion/datachannel/datachannel.go @@ -2,10 +2,12 @@ package datachannel import ( + "errors" "fmt" "io" "sync" "sync/atomic" + "time" "github.com/pion/logging" "github.com/pion/sctp" @@ -19,6 +21,11 @@ type Reader interface { ReadDataChannel([]byte) (int, bool, error) } +// ReadDeadliner extends an io.Reader to expose setting a read deadline. +type ReadDeadliner interface { + SetReadDeadline(time.Time) error +} + // Writer is an extended io.Writer // that also allows indicating if a message is text. type Writer interface { @@ -184,7 +191,7 @@ func (c *DataChannel) Read(p []byte) (int, error) { func (c *DataChannel) ReadDataChannel(p []byte) (int, bool, error) { for { n, ppi, err := c.stream.ReadSCTP(p) - if err == io.EOF { + if errors.Is(err, io.EOF) { // When the peer sees that an incoming stream was // reset, it also resets its corresponding outgoing stream. if closeErr := c.stream.Close(); closeErr != nil { @@ -212,6 +219,11 @@ func (c *DataChannel) ReadDataChannel(p []byte) (int, bool, error) { } } +// SetReadDeadline sets a deadline for reads to return +func (c *DataChannel) SetReadDeadline(t time.Time) error { + return c.stream.SetReadDeadline(t) +} + // MessagesSent returns the number of messages sent func (c *DataChannel) MessagesSent() uint32 { return atomic.LoadUint32(&c.messagesSent) diff --git a/vendor/github.com/pion/datachannel/message_channel_open.go b/vendor/github.com/pion/datachannel/message_channel_open.go index c257bea8d..5eb58633c 100644 --- a/vendor/github.com/pion/datachannel/message_channel_open.go +++ b/vendor/github.com/pion/datachannel/message_channel_open.go @@ -8,8 +8,9 @@ import ( /* channelOpen represents a DATA_CHANNEL_OPEN Message - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Message Type | Channel Type | Priority | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ diff --git a/vendor/github.com/pion/datachannel/renovate.json b/vendor/github.com/pion/datachannel/renovate.json index 08c1e39d6..f1614058a 100644 --- a/vendor/github.com/pion/datachannel/renovate.json +++ b/vendor/github.com/pion/datachannel/renovate.json @@ -1,6 +1,7 @@ { "extends": [ - "config:base" + "config:base", + ":disableDependencyDashboard" ], "postUpdateOptions": [ "gomodTidy" diff --git a/vendor/github.com/pion/dtls/v2/.editorconfig b/vendor/github.com/pion/dtls/v2/.editorconfig index d2b32061a..1dca00f8a 100644 --- a/vendor/github.com/pion/dtls/v2/.editorconfig +++ b/vendor/github.com/pion/dtls/v2/.editorconfig @@ -1,4 +1,6 @@ # http://editorconfig.org/ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT root = true diff --git a/vendor/github.com/pion/dtls/v2/.gitignore b/vendor/github.com/pion/dtls/v2/.gitignore index 83db74ba5..6e2f206a9 100644 --- a/vendor/github.com/pion/dtls/v2/.gitignore +++ b/vendor/github.com/pion/dtls/v2/.gitignore @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + ### JetBrains IDE ### ##################### .idea/ @@ -22,3 +25,4 @@ cover.out *.wasm examples/sfu-ws/cert.pem examples/sfu-ws/key.pem +wasm_exec.js diff --git a/vendor/github.com/pion/dtls/v2/.golangci.yml b/vendor/github.com/pion/dtls/v2/.golangci.yml index d6162c970..4e3eddf42 100644 --- a/vendor/github.com/pion/dtls/v2/.golangci.yml +++ b/vendor/github.com/pion/dtls/v2/.golangci.yml @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + linters-settings: govet: check-shadowing: true @@ -10,19 +13,34 @@ linters-settings: modules: - github.com/pkg/errors: recommendations: - - errors + - errors + forbidigo: + forbid: + - ^fmt.Print(f|ln)?$ + - ^log.(Panic|Fatal|Print)(f|ln)?$ + - ^os.Exit$ + - ^panic$ + - ^print(ln)?$ linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully - - deadcode # Finds unused code + - contextcheck # check the function whether use a non-inherited context + - decorder # check declaration order and count of types, constants, variables and functions - depguard # Go linter that checks if package imports are in a list of acceptable packages - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection + - durationcheck # check for two durations multiplied together - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. - exhaustive # check exhaustiveness of enum switch statements - exportloopref # checks for pointers to enclosing loop variables + - forbidigo # Forbids identifiers + - forcetypeassert # finds forced type assertions - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - gochecknoinits # Checks that no init functions are present in Go code @@ -35,40 +53,59 @@ linters: - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - gosimple # Linter for Go source code that specializes in simplifying a code - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - grouper # An analyzer to analyze expression groups. + - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used - misspell # Finds commonly misspelled English words in comments - nakedret # Finds naked returns in functions greater than a specified function length + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. - noctx # noctx finds sending http request without context.Context - - scopelint # Scopelint checks for unpinned variables in go programs + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # golint replacement, finds style mistakes - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - - structcheck # Finds unused struct fields - stylecheck # Stylecheck is a replacement for golint + - tagliatelle # Checks the struct tags. + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types - - varcheck # Finds unused global variables and constants + - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: + - containedctx # containedctx is a linter that detects struct contained context.Context field + - cyclop # checks function and package cyclomatic complexity + - exhaustivestruct # Checks if all struct's fields are initialized - funlen # Tool for detection of long functions - gocyclo # Computes and checks the cyclomatic complexity of functions - godot # Check if comments end in a period - gomnd # An analyzer to detect magic numbers. + - ifshort # Checks that your code uses short syntax for if-statements whenever possible + - ireturn # Accept Interfaces, Return Concrete Types - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - nestif # Reports deeply nested if statements - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - nolintlint # Reports ill-formed or insufficient nolint directives + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - prealloc # Finds slice declarations that could potentially be preallocated + - promlinter # Check Prometheus metrics naming via promlint - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. - testpackage # linter that makes you use a separate _test package + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers + - varnamelen # checks that the length of a variable's name matches its scope + - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! issues: @@ -78,12 +115,23 @@ issues: - path: _test\.go linters: - gocognit + - forbidigo # Allow complex main function in examples - path: examples text: "of func `main` is high" linters: - gocognit + + # Allow forbidden identifiers in examples + - path: examples + linters: + - forbidigo + + # Allow forbidden identifiers in CLI commands + - path: cmd + linters: + - forbidigo run: skip-dirs-use-default: false diff --git a/vendor/github.com/pion/dtls/v2/.goreleaser.yml b/vendor/github.com/pion/dtls/v2/.goreleaser.yml new file mode 100644 index 000000000..30093e9d6 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/.goreleaser.yml @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + +builds: +- skip: true diff --git a/vendor/github.com/pion/dtls/v2/AUTHORS.txt b/vendor/github.com/pion/dtls/v2/AUTHORS.txt index a8e6fb46f..e14fae4c0 100644 --- a/vendor/github.com/pion/dtls/v2/AUTHORS.txt +++ b/vendor/github.com/pion/dtls/v2/AUTHORS.txt @@ -2,7 +2,7 @@ # we would love to have you https://github.com/pion/webrtc/wiki/Contributing # # This file is auto generated, using git to list all individuals contributors. -# see `.github/generate-authors.sh` for the scripting +# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting Aleksandr Razumov alvarowolfx Arlo Breault @@ -14,6 +14,7 @@ Bragadeesh Carson Hoffman Cecylia Bocovich Chris Hiszpanski +cnderrauber Daniele Sluijters folbrich Hayden James @@ -27,19 +28,30 @@ Jim Wert jinleileiking Jozef Kralik Julien Salleyron +Juliusz Chroboczek Kegan Dougal +Kevin Wang Lander Noterman Len Lukas Lihotzki +ManuelBk <26275612+ManuelBk@users.noreply.github.com> Michael Zabka Michiel De Backker +Rachel Chen Robert Eperjesi Ryan Gordon +Sam Lancia +Sean DuBois Sean DuBois Sean DuBois +Shelikhoo Stefan Tatschner +Steffen Vogel Vadim Vadim Filimonov wmiao ZHENK 吕海涛 + +# List of contributors not appearing in Git history + diff --git a/vendor/github.com/pion/dtls/v2/LICENSE b/vendor/github.com/pion/dtls/v2/LICENSE index ab602974d..491caf6b0 100644 --- a/vendor/github.com/pion/dtls/v2/LICENSE +++ b/vendor/github.com/pion/dtls/v2/LICENSE @@ -1,21 +1,9 @@ MIT License -Copyright (c) 2018 +Copyright (c) 2023 The Pion community -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: +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 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. +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/pion/dtls/v2/Makefile b/vendor/github.com/pion/dtls/v2/Makefile deleted file mode 100644 index 1df38b223..000000000 --- a/vendor/github.com/pion/dtls/v2/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -fuzz-build-record-layer: fuzz-prepare - go-fuzz-build -tags gofuzz -func FuzzRecordLayer -fuzz-run-record-layer: - go-fuzz -bin dtls-fuzz.zip -workdir fuzz -fuzz-prepare: - @GO111MODULE=on go mod vendor diff --git a/vendor/github.com/pion/dtls/v2/README.md b/vendor/github.com/pion/dtls/v2/README.md index d6da5b33a..0c0659593 100644 --- a/vendor/github.com/pion/dtls/v2/README.md +++ b/vendor/github.com/pion/dtls/v2/README.md @@ -9,11 +9,10 @@ Sourcegraph Widget Slack Widget
- Build Status - GoDoc + GitHub Workflow Status + Go Reference Coverage Status - Go Report Card - Codacy Badge + Go Report Card License: MIT


@@ -22,7 +21,22 @@ Native [DTLS 1.2][rfc6347] implementation in the Go programming language. A long term goal is a professional security review, and maybe an inclusion in stdlib. +### RFCs +#### Implemented +- **RFC 6347**: [Datagram Transport Layer Security Version 1.2][rfc6347] +- **RFC 5705**: [Keying Material Exporters for Transport Layer Security (TLS)][rfc5705] +- **RFC 7627**: [Transport Layer Security (TLS) - Session Hash and Extended Master Secret Extension][rfc7627] +- **RFC 7301**: [Transport Layer Security (TLS) - Application-Layer Protocol Negotiation Extension][rfc7301] + +[rfc5289]: https://tools.ietf.org/html/rfc5289 +[rfc5487]: https://tools.ietf.org/html/rfc5487 +[rfc5489]: https://tools.ietf.org/html/rfc5489 +[rfc5705]: https://tools.ietf.org/html/rfc5705 [rfc6347]: https://tools.ietf.org/html/rfc6347 +[rfc6655]: https://tools.ietf.org/html/rfc6655 +[rfc7301]: https://tools.ietf.org/html/rfc7301 +[rfc7627]: https://tools.ietf.org/html/rfc7627 +[rfc8422]: https://tools.ietf.org/html/rfc8422 ### Goals/Progress This will only be targeting DTLS 1.2, and the most modern/common cipher suites. @@ -37,13 +51,10 @@ We would love contributions that fall under the 'Planned Features' and any bug f * Extended Master Secret extension ([RFC 7627][rfc7627]) * ALPN extension ([RFC 7301][rfc7301]) -[rfc5705]: https://tools.ietf.org/html/rfc5705 -[rfc7627]: https://tools.ietf.org/html/rfc7627 -[rfc7301]: https://tools.ietf.org/html/rfc7301 - #### Supported ciphers ##### ECDHE + * TLS_ECDHE_ECDSA_WITH_AES_128_CCM ([RFC 6655][rfc6655]) * TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655]) * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ([RFC 5289][rfc5289]) @@ -54,15 +65,16 @@ We would love contributions that fall under the 'Planned Features' and any bug f * TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ([RFC 8422][rfc8422]) ##### PSK + * TLS_PSK_WITH_AES_128_CCM ([RFC 6655][rfc6655]) * TLS_PSK_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655]) +* TLS_PSK_WITH_AES_256_CCM_8 ([RFC 6655][rfc6655]) * TLS_PSK_WITH_AES_128_GCM_SHA256 ([RFC 5487][rfc5487]) * TLS_PSK_WITH_AES_128_CBC_SHA256 ([RFC 5487][rfc5487]) -[rfc5289]: https://tools.ietf.org/html/rfc5289 -[rfc8422]: https://tools.ietf.org/html/rfc8422 -[rfc6655]: https://tools.ietf.org/html/rfc6655 -[rfc5487]: https://tools.ietf.org/html/rfc5487 +##### ECDHE & PSK + +* TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 ([RFC 5489][rfc5489]) #### Planned Features * Chacha20Poly1305 @@ -106,7 +118,6 @@ Pion DTLS can connect to itself and OpenSSL. ### Using with PSK Pion DTLS also comes with examples that do key exchange via PSK - #### Pion DTLS ```sh go run examples/listen/psk/main.go @@ -125,8 +136,16 @@ go run examples/dial/psk/main.go openssl s_client -dtls1_2 -connect 127.0.0.1:4444 -psk abc123 -cipher PSK-AES128-CCM8 ``` +### Community +Pion has an active community on the [Slack](https://pion.ly/slack). + +Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news. + +We are always looking to support **your projects**. Please reach out if you have something to build! +If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly) + ### Contributing -Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible: +Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible: [AUTHORS.txt](./AUTHORS.txt) ### License MIT License - see [LICENSE](LICENSE) for full text diff --git a/vendor/github.com/pion/dtls/v2/certificate.go b/vendor/github.com/pion/dtls/v2/certificate.go index c99e1c93d..519fc875f 100644 --- a/vendor/github.com/pion/dtls/v2/certificate.go +++ b/vendor/github.com/pion/dtls/v2/certificate.go @@ -1,35 +1,105 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( + "bytes" "crypto/tls" "crypto/x509" + "fmt" "strings" ) -func (c *handshakeConfig) getCertificate(serverName string) (*tls.Certificate, error) { +// ClientHelloInfo contains information from a ClientHello message in order to +// guide application logic in the GetCertificate. +type ClientHelloInfo struct { + // ServerName indicates the name of the server requested by the client + // in order to support virtual hosting. ServerName is only set if the + // client is using SNI (see RFC 4366, Section 3.1). + ServerName string + + // CipherSuites lists the CipherSuites supported by the client (e.g. + // TLS_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256). + CipherSuites []CipherSuiteID +} + +// CertificateRequestInfo contains information from a server's +// CertificateRequest message, which is used to demand a certificate and proof +// of control from a client. +type CertificateRequestInfo struct { + // AcceptableCAs contains zero or more, DER-encoded, X.501 + // Distinguished Names. These are the names of root or intermediate CAs + // that the server wishes the returned certificate to be signed by. An + // empty slice indicates that the server has no preference. + AcceptableCAs [][]byte +} + +// SupportsCertificate returns nil if the provided certificate is supported by +// the server that sent the CertificateRequest. Otherwise, it returns an error +// describing the reason for the incompatibility. +// NOTE: original src: https://github.com/golang/go/blob/29b9a328d268d53833d2cc063d1d8b4bf6852675/src/crypto/tls/common.go#L1273 +func (cri *CertificateRequestInfo) SupportsCertificate(c *tls.Certificate) error { + if len(cri.AcceptableCAs) == 0 { + return nil + } + + for j, cert := range c.Certificate { + x509Cert := c.Leaf + // Parse the certificate if this isn't the leaf node, or if + // chain.Leaf was nil. + if j != 0 || x509Cert == nil { + var err error + if x509Cert, err = x509.ParseCertificate(cert); err != nil { + return fmt.Errorf("failed to parse certificate #%d in the chain: %w", j, err) + } + } + + for _, ca := range cri.AcceptableCAs { + if bytes.Equal(x509Cert.RawIssuer, ca) { + return nil + } + } + } + return errNotAcceptableCertificateChain +} + +func (c *handshakeConfig) setNameToCertificateLocked() { + nameToCertificate := make(map[string]*tls.Certificate) + for i := range c.localCertificates { + cert := &c.localCertificates[i] + x509Cert := cert.Leaf + if x509Cert == nil { + var parseErr error + x509Cert, parseErr = x509.ParseCertificate(cert.Certificate[0]) + if parseErr != nil { + continue + } + } + if len(x509Cert.Subject.CommonName) > 0 { + nameToCertificate[strings.ToLower(x509Cert.Subject.CommonName)] = cert + } + for _, san := range x509Cert.DNSNames { + nameToCertificate[strings.ToLower(san)] = cert + } + } + c.nameToCertificate = nameToCertificate +} + +func (c *handshakeConfig) getCertificate(clientHelloInfo *ClientHelloInfo) (*tls.Certificate, error) { c.mu.Lock() defer c.mu.Unlock() - if c.nameToCertificate == nil { - nameToCertificate := make(map[string]*tls.Certificate) - for i := range c.localCertificates { - cert := &c.localCertificates[i] - x509Cert := cert.Leaf - if x509Cert == nil { - var parseErr error - x509Cert, parseErr = x509.ParseCertificate(cert.Certificate[0]) - if parseErr != nil { - continue - } - } - if len(x509Cert.Subject.CommonName) > 0 { - nameToCertificate[strings.ToLower(x509Cert.Subject.CommonName)] = cert - } - for _, san := range x509Cert.DNSNames { - nameToCertificate[strings.ToLower(san)] = cert - } + if c.localGetCertificate != nil && + (len(c.localCertificates) == 0 || len(clientHelloInfo.ServerName) > 0) { + cert, err := c.localGetCertificate(clientHelloInfo) + if cert != nil || err != nil { + return cert, err } - c.nameToCertificate = nameToCertificate + } + + if c.nameToCertificate == nil { + c.setNameToCertificateLocked() } if len(c.localCertificates) == 0 { @@ -41,11 +111,11 @@ func (c *handshakeConfig) getCertificate(serverName string) (*tls.Certificate, e return &c.localCertificates[0], nil } - if len(serverName) == 0 { + if len(clientHelloInfo.ServerName) == 0 { return &c.localCertificates[0], nil } - name := strings.TrimRight(strings.ToLower(serverName), ".") + name := strings.TrimRight(strings.ToLower(clientHelloInfo.ServerName), ".") if cert, ok := c.nameToCertificate[name]; ok { return cert, nil @@ -65,3 +135,23 @@ func (c *handshakeConfig) getCertificate(serverName string) (*tls.Certificate, e // If nothing matches, return the first certificate. return &c.localCertificates[0], nil } + +// NOTE: original src: https://github.com/golang/go/blob/29b9a328d268d53833d2cc063d1d8b4bf6852675/src/crypto/tls/handshake_client.go#L974 +func (c *handshakeConfig) getClientCertificate(cri *CertificateRequestInfo) (*tls.Certificate, error) { + c.mu.Lock() + defer c.mu.Unlock() + if c.localGetClientCertificate != nil { + return c.localGetClientCertificate(cri) + } + + for i := range c.localCertificates { + chain := c.localCertificates[i] + if err := cri.SupportsCertificate(&chain); err != nil { + continue + } + return &chain, nil + } + + // No acceptable certificate found. Don't send a certificate. + return new(tls.Certificate), nil +} diff --git a/vendor/github.com/pion/dtls/v2/cipher_suite.go b/vendor/github.com/pion/dtls/v2/cipher_suite.go index 5f35a8504..7a5bb4a58 100644 --- a/vendor/github.com/pion/dtls/v2/cipher_suite.go +++ b/vendor/github.com/pion/dtls/v2/cipher_suite.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -19,24 +22,27 @@ type CipherSuiteID = ciphersuite.ID // Supported Cipher Suites const ( // AES-128-CCM - TLS_ECDHE_ECDSA_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM //nolint:golint,stylecheck - TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 //nolint:golint,stylecheck + TLS_ECDHE_ECDSA_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM //nolint:revive,stylecheck + TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 //nolint:revive,stylecheck // AES-128-GCM-SHA256 - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 //nolint:golint,stylecheck - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 //nolint:golint,stylecheck + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 //nolint:revive,stylecheck + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 //nolint:revive,stylecheck - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 //nolint:golint,stylecheck - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 //nolint:golint,stylecheck + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 //nolint:revive,stylecheck + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 //nolint:revive,stylecheck // AES-256-CBC-SHA - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA //nolint:golint,stylecheck - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA //nolint:golint,stylecheck + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA //nolint:revive,stylecheck + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA //nolint:revive,stylecheck - TLS_PSK_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM //nolint:golint,stylecheck - TLS_PSK_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM_8 //nolint:golint,stylecheck - TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_GCM_SHA256 //nolint:golint,stylecheck - TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CBC_SHA256 //nolint:golint,stylecheck + TLS_PSK_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM //nolint:revive,stylecheck + TLS_PSK_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM_8 //nolint:revive,stylecheck + TLS_PSK_WITH_AES_256_CCM_8 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_256_CCM_8 //nolint:revive,stylecheck + TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_GCM_SHA256 //nolint:revive,stylecheck + TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CBC_SHA256 //nolint:revive,stylecheck + + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 //nolint:revive,stylecheck ) // CipherSuiteAuthenticationType controls what authentication method is using during the handshake for a CipherSuite @@ -49,6 +55,16 @@ const ( CipherSuiteAuthenticationTypeAnonymous CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypeAnonymous ) +// CipherSuiteKeyExchangeAlgorithm controls what exchange algorithm is using during the handshake for a CipherSuite +type CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithm + +// CipherSuiteKeyExchangeAlgorithm Bitmask +const ( + CipherSuiteKeyExchangeAlgorithmNone CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmNone + CipherSuiteKeyExchangeAlgorithmPsk CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmPsk + CipherSuiteKeyExchangeAlgorithmEcdhe CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmEcdhe +) + var _ = allCipherSuites() // Necessary until this function isn't only used by Go 1.14 // CipherSuite is an interface that all DTLS CipherSuites must satisfy @@ -68,6 +84,13 @@ type CipherSuite interface { // AuthenticationType controls what authentication method is using during the handshake AuthenticationType() CipherSuiteAuthenticationType + // KeyExchangeAlgorithm controls what exchange algorithm is using during the handshake + KeyExchangeAlgorithm() CipherSuiteKeyExchangeAlgorithm + + // ECC (Elliptic Curve Cryptography) determines whether ECC extesions will be send during handshake. + // https://datatracker.ietf.org/doc/html/rfc4492#page-10 + ECC() bool + // Called when keying material has been generated, should initialize the internal cipher Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error IsInitialized() bool @@ -109,6 +132,8 @@ func cipherSuiteForID(id CipherSuiteID, customCiphers func() []CipherSuite) Ciph return ciphersuite.NewTLSPskWithAes128Ccm() case TLS_PSK_WITH_AES_128_CCM_8: return ciphersuite.NewTLSPskWithAes128Ccm8() + case TLS_PSK_WITH_AES_256_CCM_8: + return ciphersuite.NewTLSPskWithAes256Ccm8() case TLS_PSK_WITH_AES_128_GCM_SHA256: return &ciphersuite.TLSPskWithAes128GcmSha256{} case TLS_PSK_WITH_AES_128_CBC_SHA256: @@ -117,6 +142,8 @@ func cipherSuiteForID(id CipherSuiteID, customCiphers func() []CipherSuite) Ciph return &ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{} case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: return &ciphersuite.TLSEcdheRsaWithAes256GcmSha384{} + case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + return ciphersuite.NewTLSEcdhePskWithAes128CbcSha256() } if customCiphers != nil { @@ -152,6 +179,7 @@ func allCipherSuites() []CipherSuite { &ciphersuite.TLSEcdheRsaWithAes256CbcSha{}, ciphersuite.NewTLSPskWithAes128Ccm(), ciphersuite.NewTLSPskWithAes128Ccm8(), + ciphersuite.NewTLSPskWithAes256Ccm8(), &ciphersuite.TLSPskWithAes128GcmSha256{}, &ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{}, &ciphersuite.TLSEcdheRsaWithAes256GcmSha384{}, @@ -172,7 +200,7 @@ func parseCipherSuites(userSelectedSuites []CipherSuiteID, customCipherSuites fu for _, id := range ids { c := cipherSuiteForID(id, nil) if c == nil { - return nil, &invalidCipherSuite{id} + return nil, &invalidCipherSuiteError{id} } cipherSuites = append(cipherSuites, c) } diff --git a/vendor/github.com/pion/dtls/v2/cipher_suite_go114.go b/vendor/github.com/pion/dtls/v2/cipher_suite_go114.go index 5c63c0913..fd46d7bd9 100644 --- a/vendor/github.com/pion/dtls/v2/cipher_suite_go114.go +++ b/vendor/github.com/pion/dtls/v2/cipher_suite_go114.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build go1.14 // +build go1.14 diff --git a/vendor/github.com/pion/dtls/v2/codecov.yml b/vendor/github.com/pion/dtls/v2/codecov.yml index 085200a48..263e4d45c 100644 --- a/vendor/github.com/pion/dtls/v2/codecov.yml +++ b/vendor/github.com/pion/dtls/v2/codecov.yml @@ -3,6 +3,8 @@ # # It is automatically copied from https://github.com/pion/.goassets repository. # +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT coverage: status: diff --git a/vendor/github.com/pion/dtls/v2/compression_method.go b/vendor/github.com/pion/dtls/v2/compression_method.go index 693eb7a52..7e44de009 100644 --- a/vendor/github.com/pion/dtls/v2/compression_method.go +++ b/vendor/github.com/pion/dtls/v2/compression_method.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import "github.com/pion/dtls/v2/pkg/protocol" diff --git a/vendor/github.com/pion/dtls/v2/config.go b/vendor/github.com/pion/dtls/v2/config.go index 7f68c03b1..fbc3ee247 100644 --- a/vendor/github.com/pion/dtls/v2/config.go +++ b/vendor/github.com/pion/dtls/v2/config.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -10,6 +13,7 @@ import ( "io" "time" + "github.com/pion/dtls/v2/pkg/crypto/elliptic" "github.com/pion/logging" ) @@ -82,6 +86,16 @@ type Config struct { // be considered but the verifiedChains will always be nil. VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error + // VerifyConnection, if not nil, is called after normal certificate + // verification/PSK and after VerifyPeerCertificate by either a TLS client + // or server. If it returns a non-nil error, the handshake is aborted + // and that error results. + // + // If normal verification fails then the handshake will abort before + // considering this callback. This callback will run for all connections + // regardless of InsecureSkipVerify or ClientAuth settings. + VerifyConnection func(*State) error + // RootCAs defines the set of root certificate authorities // that one peer uses when verifying the other peer's certificates. // If RootCAs is nil, TLS uses the host's root CA set. @@ -130,6 +144,38 @@ type Config struct { // List of application protocols the peer supports, for ALPN SupportedProtocols []string + + // List of Elliptic Curves to use + // + // If an ECC ciphersuite is configured and EllipticCurves is empty + // it will default to X25519, P-256, P-384 in this specific order. + EllipticCurves []elliptic.Curve + + // GetCertificate returns a Certificate based on the given + // ClientHelloInfo. It will only be called if the client supplies SNI + // information or if Certificates is empty. + // + // If GetCertificate is nil or returns nil, then the certificate is + // retrieved from NameToCertificate. If NameToCertificate is nil, the + // best element of Certificates will be used. + GetCertificate func(*ClientHelloInfo) (*tls.Certificate, error) + + // GetClientCertificate, if not nil, is called when a server requests a + // certificate from a client. If set, the contents of Certificates will + // be ignored. + // + // If GetClientCertificate returns an error, the handshake will be + // aborted and that error will be returned. Otherwise + // GetClientCertificate must return a non-nil Certificate. If + // Certificate.Certificate is empty then no certificate will be sent to + // the server. If this is unacceptable to the server then it may abort + // the handshake. + GetClientCertificate func(*CertificateRequestInfo) (*tls.Certificate, error) + + // InsecureSkipVerifyHello, if true and when acting as server, allow client to + // skip hello verify phase and receive ServerHello after initial ClientHello. + // This have implication on DoS attack resistance. + InsecureSkipVerifyHello bool } func defaultConnectContextMaker() (context.Context, func()) { @@ -143,8 +189,14 @@ func (c *Config) connectContextMaker() (context.Context, func()) { return c.ConnectContextMaker() } +func (c *Config) includeCertificateSuites() bool { + return c.PSK == nil || len(c.Certificates) > 0 || c.GetCertificate != nil || c.GetClientCertificate != nil +} + const defaultMTU = 1200 // bytes +var defaultCurves = []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384} //nolint:gochecknoglobals + // PSKCallback is called once we have the remote's PSKIdentityHint. // If the remote provided none it will be nil type PSKCallback func([]byte) ([]byte, error) @@ -196,6 +248,6 @@ func validateConfig(config *Config) error { } } - _, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.PSK == nil || len(config.Certificates) > 0, config.PSK != nil) + _, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.includeCertificateSuites(), config.PSK != nil) return err } diff --git a/vendor/github.com/pion/dtls/v2/conn.go b/vendor/github.com/pion/dtls/v2/conn.go index fd8cf734d..2b7585108 100644 --- a/vendor/github.com/pion/dtls/v2/conn.go +++ b/vendor/github.com/pion/dtls/v2/conn.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -18,9 +21,9 @@ import ( "github.com/pion/dtls/v2/pkg/protocol/handshake" "github.com/pion/dtls/v2/pkg/protocol/recordlayer" "github.com/pion/logging" - "github.com/pion/transport/connctx" - "github.com/pion/transport/deadline" - "github.com/pion/transport/replaydetector" + "github.com/pion/transport/v2/connctx" + "github.com/pion/transport/v2/deadline" + "github.com/pion/transport/v2/replaydetector" ) const ( @@ -88,7 +91,7 @@ func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient return nil, errNilNextConn } - cipherSuites, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.PSK == nil || len(config.Certificates) > 0, config.PSK != nil) + cipherSuites, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.includeCertificateSuites(), config.PSK != nil) if err != nil { return nil, err } @@ -154,6 +157,11 @@ func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient serverName = "" } + curves := config.EllipticCurves + if len(curves) == 0 { + curves = defaultCurves + } + hsCfg := &handshakeConfig{ localPSKCallback: config.PSK, localPSKIdentityHint: config.PSKIdentityHint, @@ -167,6 +175,7 @@ func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient localCertificates: config.Certificates, insecureSkipVerify: config.InsecureSkipVerify, verifyPeerCertificate: config.VerifyPeerCertificate, + verifyConnection: config.VerifyConnection, rootCAs: config.RootCAs, clientCAs: config.ClientCAs, customCipherSuites: config.CustomCipherSuites, @@ -175,13 +184,22 @@ func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient initialEpoch: 0, keyLogWriter: config.KeyLogWriter, sessionStore: config.SessionStore, + ellipticCurves: curves, + localGetCertificate: config.GetCertificate, + localGetClientCertificate: config.GetClientCertificate, + insecureSkipHelloVerify: config.InsecureSkipVerifyHello, } - cert, err := hsCfg.getCertificate(serverName) - if err != nil && !errors.Is(err, errNoCertificates) { - return nil, err + // rfc5246#section-7.4.3 + // In addition, the hash and signature algorithms MUST be compatible + // with the key in the server's end-entity certificate. + if !isClient { + cert, err := hsCfg.getCertificate(&ClientHelloInfo{}) + if err != nil && !errors.Is(err, errNoCertificates) { + return nil, err + } + hsCfg.localCipherSuites = filterCipherSuitesForCertificate(cert, cipherSuites) } - hsCfg.localCipherSuites = filterCipherSuitesForCertificate(cert, cipherSuites) var initialFlight flightVal var initialFSMState handshakeState @@ -327,7 +345,7 @@ func (c *Conn) Write(p []byte) (int, error) { { record: &recordlayer.RecordLayer{ Header: recordlayer.Header{ - Epoch: c.getLocalEpoch(), + Epoch: c.state.getLocalEpoch(), Version: protocol.Version1_2, }, Content: &protocol.ApplicationData{ @@ -341,7 +359,7 @@ func (c *Conn) Write(p []byte) (int, error) { // Close closes the connection. func (c *Conn) Close() error { - err := c.close(true) + err := c.close(true) //nolint:contextcheck c.handshakeLoopsFinished.Wait() return err } @@ -412,6 +430,11 @@ func (c *Conn) writePackets(ctx context.Context, pkts []*packet) error { } func (c *Conn) compactRawPackets(rawPackets [][]byte) [][]byte { + // avoid a useless copy in the common case + if len(rawPackets) == 1 { + return rawPackets + } + combinedRawPackets := make([][]byte, 0) currentCombinedRawPacket := make([]byte, 0) @@ -484,14 +507,14 @@ func (c *Conn) processHandshakePacket(p *packet, h *handshake.Handshake) ([][]by SequenceNumber: seq, } - recordlayerHeaderBytes, err := recordlayerHeader.Marshal() + rawPacket, err := recordlayerHeader.Marshal() if err != nil { return nil, err } p.record.Header = *recordlayerHeader - rawPacket := append(recordlayerHeaderBytes, handshakeFragment...) + rawPacket = append(rawPacket, handshakeFragment...) if p.shouldEncrypt { var err error rawPacket, err = c.state.cipherSuite.Encrypt(p.record, rawPacket) @@ -535,12 +558,12 @@ func (c *Conn) fragmentHandshake(h *handshake.Handshake) ([][]byte, error) { offset += contentFragmentLen - headerFragmentRaw, err := headerFragment.Marshal() + fragmentedHandshake, err := headerFragment.Marshal() if err != nil { return nil, err } - fragmentedHandshake := append(headerFragmentRaw, contentFragment...) + fragmentedHandshake = append(fragmentedHandshake, contentFragment...) fragmentedHandshakes = append(fragmentedHandshakes, fragmentedHandshake) } @@ -555,7 +578,10 @@ var poolReadBuffer = sync.Pool{ //nolint:gochecknoglobals } func (c *Conn) readAndBuffer(ctx context.Context) error { - bufptr := poolReadBuffer.Get().(*[]byte) + bufptr, ok := poolReadBuffer.Get().(*[]byte) + if !ok { + return errFailedToAccessPoolReadBuffer + } defer poolReadBuffer.Put(bufptr) b := *bufptr @@ -582,13 +608,13 @@ func (c *Conn) readAndBuffer(ctx context.Context) error { if hs { hasHandshake = true } - switch e := err.(type) { - case nil: - case *errAlert: + + var e *alertError + if errors.As(err, &e) { if e.IsFatalOrCloseNotify() { return e } - default: + } else if err != nil { return e } } @@ -618,13 +644,12 @@ func (c *Conn) handleQueuedPackets(ctx context.Context) error { } } } - switch e := err.(type) { - case nil: - case *errAlert: + var e *alertError + if errors.As(err, &e) { if e.IsFatalOrCloseNotify() { return e } - default: + } else if err != nil { return e } } @@ -641,7 +666,7 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, enqueue boo } // Validate epoch - remoteEpoch := c.getRemoteEpoch() + remoteEpoch := c.state.getRemoteEpoch() if h.Epoch > remoteEpoch { if h.Epoch > remoteEpoch+1 { c.log.Debugf("discarded future packet (epoch: %d, seq: %d)", @@ -697,13 +722,12 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, enqueue boo } else if isHandshake { markPacketAsValid() for out, epoch := c.fragmentBuffer.pop(); out != nil; out, epoch = c.fragmentBuffer.pop() { - rawHandshake := &handshake.Handshake{} - if err := rawHandshake.Unmarshal(out); err != nil { + header := &handshake.Header{} + if err := header.Unmarshal(out); err != nil { c.log.Debugf("%s: handshake parse failed: %s", srvCliStr(c.state.isClient), err) continue } - - _ = c.handshakeCache.push(out, epoch, rawHandshake.Header.MessageSequence, rawHandshake.Header.Type, !c.state.isClient) + c.handshakeCache.push(out, epoch, header.MessageSequence, header.Type, !c.state.isClient) } return true, nil, nil @@ -723,7 +747,7 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, enqueue boo a = &alert.Alert{Level: alert.Warning, Description: alert.CloseNotify} } markPacketAsValid() - return false, a, &errAlert{content} + return false, a, &alertError{content} case *protocol.ChangeCipherSpec: if c.state.cipherSuite == nil || !c.state.cipherSuite.IsInitialized() { if enqueue { @@ -736,7 +760,7 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, enqueue boo newRemoteEpoch := h.Epoch + 1 c.log.Tracef("%s: <- ChangeCipherSpec (epoch: %d)", srvCliStr(c.state.isClient), newRemoteEpoch) - if c.getRemoteEpoch()+1 == newRemoteEpoch { + if c.state.getRemoteEpoch()+1 == newRemoteEpoch { c.setRemoteEpoch(newRemoteEpoch) markPacketAsValid() } @@ -778,7 +802,7 @@ func (c *Conn) notify(ctx context.Context, level alert.Level, desc alert.Descrip { record: &recordlayer.RecordLayer{ Header: recordlayer.Header{ - Epoch: c.getLocalEpoch(), + Epoch: c.state.getLocalEpoch(), Version: protocol.Version1_2, }, Content: &alert.Alert{ @@ -844,8 +868,8 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh defer c.handshakeLoopsFinished.Done() for { if err := c.readAndBuffer(ctxRead); err != nil { - switch e := err.(type) { - case *errAlert: + var e *alertError + if errors.As(err, &e) { if !e.IsFatalOrCloseNotify() { if c.isHandshakeCompletedSuccessfully() { // Pass the error to Read() @@ -857,9 +881,9 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh } continue // non-fatal alert must not stop read loop } - case error: - switch err { - case context.DeadlineExceeded, context.Canceled, io.EOF: + } else { + switch { + case errors.Is(err, context.DeadlineExceeded), errors.Is(err, context.Canceled), errors.Is(err, io.EOF): default: if c.isHandshakeCompletedSuccessfully() { // Keep read loop and pass the read error to Read() @@ -872,16 +896,21 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh } } } + select { case firstErr <- err: default: } - if e, ok := err.(*errAlert); ok { + if e != nil { if e.IsFatalOrCloseNotify() { - _ = c.close(false) + _ = c.close(false) //nolint:contextcheck } } + if !c.isConnectionClosed() && errors.Is(err, context.Canceled) { + c.log.Trace("handshake timeouts - closing underline connection") + _ = c.close(false) //nolint:contextcheck + } return } } @@ -891,10 +920,12 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh case err := <-firstErr: cancelRead() cancel() + c.handshakeLoopsFinished.Wait() return c.translateHandshakeCtxError(err) case <-ctx.Done(): cancelRead() cancel() + c.handshakeLoopsFinished.Wait() return c.translateHandshakeCtxError(ctx.Err()) case <-done: return nil @@ -927,6 +958,7 @@ func (c *Conn) close(byUser bool) error { if byUser { c.connectionClosedByUser = true } + isClosed := c.isConnectionClosed() c.closed.Close() c.closeLock.Unlock() @@ -934,6 +966,10 @@ func (c *Conn) close(byUser bool) error { return ErrConnClosed } + if isClosed { + return nil + } + return c.nextConn.Close() } @@ -950,18 +986,10 @@ func (c *Conn) setLocalEpoch(epoch uint16) { c.state.localEpoch.Store(epoch) } -func (c *Conn) getLocalEpoch() uint16 { - return c.state.localEpoch.Load().(uint16) -} - func (c *Conn) setRemoteEpoch(epoch uint16) { c.state.remoteEpoch.Store(epoch) } -func (c *Conn) getRemoteEpoch() uint16 { - return c.state.remoteEpoch.Load().(uint16) -} - // LocalAddr implements net.Conn.LocalAddr func (c *Conn) LocalAddr() net.Addr { return c.nextConn.LocalAddr() diff --git a/vendor/github.com/pion/dtls/v2/crypto.go b/vendor/github.com/pion/dtls/v2/crypto.go index 768ee470e..968910c7e 100644 --- a/vendor/github.com/pion/dtls/v2/crypto.go +++ b/vendor/github.com/pion/dtls/v2/crypto.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -108,6 +111,13 @@ func verifyKeySignature(message, remoteKeySignature []byte, hashAlgorithm hash.A // the private key in the certificate. // https://tools.ietf.org/html/rfc5246#section-7.3 func generateCertificateVerify(handshakeBodies []byte, privateKey crypto.PrivateKey, hashAlgorithm hash.Algorithm) ([]byte, error) { + if p, ok := privateKey.(ed25519.PrivateKey); ok { + // https://pkg.go.dev/crypto/ed25519#PrivateKey.Sign + // Sign signs the given message with priv. Ed25519 performs two passes over + // messages to be signed and therefore cannot handle pre-hashed messages. + return p.Sign(rand.Reader, handshakeBodies, crypto.Hash(0)) + } + h := sha256.New() if _, err := h.Write(handshakeBodies); err != nil { return nil, err @@ -115,9 +125,6 @@ func generateCertificateVerify(handshakeBodies []byte, privateKey crypto.Private hashed := h.Sum(nil) switch p := privateKey.(type) { - case ed25519.PrivateKey: - // https://crypto.stackexchange.com/a/55483 - return p.Sign(rand.Reader, hashed, crypto.Hash(0)) case *ecdsa.PrivateKey: return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash()) case *rsa.PrivateKey: diff --git a/vendor/github.com/pion/dtls/v2/dtls.go b/vendor/github.com/pion/dtls/v2/dtls.go index 125b904e5..b799770d8 100644 --- a/vendor/github.com/pion/dtls/v2/dtls.go +++ b/vendor/github.com/pion/dtls/v2/dtls.go @@ -1,2 +1,5 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package dtls implements Datagram Transport Layer Security (DTLS) 1.2 package dtls diff --git a/vendor/github.com/pion/dtls/v2/errors.go b/vendor/github.com/pion/dtls/v2/errors.go index 2e1638828..025d8645e 100644 --- a/vendor/github.com/pion/dtls/v2/errors.go +++ b/vendor/github.com/pion/dtls/v2/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -10,14 +13,13 @@ import ( "github.com/pion/dtls/v2/pkg/protocol" "github.com/pion/dtls/v2/pkg/protocol/alert" - "golang.org/x/xerrors" ) // Typed errors var ( ErrConnClosed = &FatalError{Err: errors.New("conn is closed")} //nolint:goerr113 - errDeadlineExceeded = &TimeoutError{Err: xerrors.Errorf("read/write timeout: %w", context.DeadlineExceeded)} + errDeadlineExceeded = &TimeoutError{Err: fmt.Errorf("read/write timeout: %w", context.DeadlineExceeded)} errInvalidContentType = &TemporaryError{Err: errors.New("invalid content type")} //nolint:goerr113 errBufferTooSmall = &TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113 @@ -55,6 +57,7 @@ var ( errServerNoMatchingSRTPProfile = &FatalError{Err: errors.New("client requested SRTP but we have no matching profiles")} //nolint:goerr113 errServerRequiredButNoClientEMS = &FatalError{Err: errors.New("server requires the Extended Master Secret extension, but the client does not support it")} //nolint:goerr113 errVerifyDataMismatch = &FatalError{Err: errors.New("expected and actual verify data does not match")} //nolint:goerr113 + errNotAcceptableCertificateChain = &FatalError{Err: errors.New("certificate chain is not signed by an acceptable CA")} //nolint:goerr113 errInvalidFlight = &InternalError{Err: errors.New("invalid flight number")} //nolint:goerr113 errKeySignatureGenerateUnimplemented = &InternalError{Err: errors.New("unable to generate key signature, unimplemented")} //nolint:goerr113 @@ -62,6 +65,8 @@ var ( errLengthMismatch = &InternalError{Err: errors.New("data length and declared length do not match")} //nolint:goerr113 errSequenceNumberOverflow = &InternalError{Err: errors.New("sequence number overflow")} //nolint:goerr113 errInvalidFSMTransition = &InternalError{Err: errors.New("invalid state machine transition")} //nolint:goerr113 + errFailedToAccessPoolReadBuffer = &InternalError{Err: errors.New("failed to access pool read buffer")} //nolint:goerr113 + errFragmentBufferOverflow = &InternalError{Err: errors.New("fragment buffer overflow")} //nolint:goerr113 ) // FatalError indicates that the DTLS connection is no longer available. @@ -81,37 +86,39 @@ type TimeoutError = protocol.TimeoutError // HandshakeError indicates that the handshake failed. type HandshakeError = protocol.HandshakeError -// invalidCipherSuite indicates an attempt at using an unsupported cipher suite. -type invalidCipherSuite struct { +// errInvalidCipherSuite indicates an attempt at using an unsupported cipher suite. +type invalidCipherSuiteError struct { id CipherSuiteID } -func (e *invalidCipherSuite) Error() string { +func (e *invalidCipherSuiteError) Error() string { return fmt.Sprintf("CipherSuite with id(%d) is not valid", e.id) } -func (e *invalidCipherSuite) Is(err error) bool { - if other, ok := err.(*invalidCipherSuite); ok { +func (e *invalidCipherSuiteError) Is(err error) bool { + var other *invalidCipherSuiteError + if errors.As(err, &other) { return e.id == other.id } return false } // errAlert wraps DTLS alert notification as an error -type errAlert struct { +type alertError struct { *alert.Alert } -func (e *errAlert) Error() string { +func (e *alertError) Error() string { return fmt.Sprintf("alert: %s", e.Alert.String()) } -func (e *errAlert) IsFatalOrCloseNotify() bool { +func (e *alertError) IsFatalOrCloseNotify() bool { return e.Level == alert.Fatal || e.Description == alert.CloseNotify } -func (e *errAlert) Is(err error) bool { - if other, ok := err.(*errAlert); ok { +func (e *alertError) Is(err error) bool { + var other *alertError + if errors.As(err, &other) { return e.Level == other.Level && e.Description == other.Description } return false @@ -119,14 +126,20 @@ func (e *errAlert) Is(err error) bool { // netError translates an error from underlying Conn to corresponding net.Error. func netError(err error) error { - switch err { - case io.EOF, context.Canceled, context.DeadlineExceeded: + switch { + case errors.Is(err, io.EOF), errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded): // Return io.EOF and context errors as is. return err } - switch e := err.(type) { - case (*net.OpError): - if se, ok := e.Err.(*os.SyscallError); ok { + + var ( + ne net.Error + opError *net.OpError + se *os.SyscallError + ) + + if errors.As(err, &opError) { + if errors.As(opError, &se) { if se.Timeout() { return &TimeoutError{Err: err} } @@ -134,8 +147,11 @@ func netError(err error) error { return &TemporaryError{Err: err} } } - case (net.Error): + } + + if errors.As(err, &ne) { return err } + return &FatalError{Err: err} } diff --git a/vendor/github.com/pion/dtls/v2/errors_errno.go b/vendor/github.com/pion/dtls/v2/errors_errno.go index c03779a45..f8e424eb3 100644 --- a/vendor/github.com/pion/dtls/v2/errors_errno.go +++ b/vendor/github.com/pion/dtls/v2/errors_errno.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build aix || darwin || dragonfly || freebsd || linux || nacl || nacljs || netbsd || openbsd || solaris || windows // +build aix darwin dragonfly freebsd linux nacl nacljs netbsd openbsd solaris windows @@ -9,18 +12,11 @@ package dtls import ( + "errors" "os" "syscall" ) func isOpErrorTemporary(err *os.SyscallError) bool { - if ne, ok := err.Err.(syscall.Errno); ok { - switch ne { - case syscall.ECONNREFUSED: - return true - default: - return false - } - } - return false + return errors.Is(err.Err, syscall.ECONNREFUSED) } diff --git a/vendor/github.com/pion/dtls/v2/errors_noerrno.go b/vendor/github.com/pion/dtls/v2/errors_noerrno.go index ad1bf8523..844ff1e75 100644 --- a/vendor/github.com/pion/dtls/v2/errors_noerrno.go +++ b/vendor/github.com/pion/dtls/v2/errors_noerrno.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !nacl && !nacljs && !netbsd && !openbsd && !solaris && !windows // +build !aix,!darwin,!dragonfly,!freebsd,!linux,!nacl,!nacljs,!netbsd,!openbsd,!solaris,!windows diff --git a/vendor/github.com/pion/dtls/v2/flight.go b/vendor/github.com/pion/dtls/v2/flight.go index bed3f8c9a..cfa58c574 100644 --- a/vendor/github.com/pion/dtls/v2/flight.go +++ b/vendor/github.com/pion/dtls/v2/flight.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls /* diff --git a/vendor/github.com/pion/dtls/v2/flight0handler.go b/vendor/github.com/pion/dtls/v2/flight0handler.go index 23dddeda6..ec766ddff 100644 --- a/vendor/github.com/pion/dtls/v2/flight0handler.go +++ b/vendor/github.com/pion/dtls/v2/flight0handler.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -11,8 +14,8 @@ import ( "github.com/pion/dtls/v2/pkg/protocol/handshake" ) -func flight0Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { - seq, msgs, ok := cache.fullPullMap(0, +func flight0Parse(_ context.Context, _ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + seq, msgs, ok := cache.fullPullMap(0, state.cipherSuite, handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, ) if !ok { @@ -81,7 +84,13 @@ func flight0Parse(ctx context.Context, c flightConn, state *State, cache *handsh } } - return handleHelloResume(clientHello.SessionID, state, cfg, flight2) + nextFlight := flight2 + + if cfg.insecureSkipHelloVerify { + nextFlight = flight4 + } + + return handleHelloResume(clientHello.SessionID, state, cfg, nextFlight) } func handleHelloResume(sessionID []byte, state *State, cfg *handshakeConfig, next flightVal) (flightVal, *alert.Alert, error) { @@ -107,11 +116,13 @@ func handleHelloResume(sessionID []byte, state *State, cfg *handshakeConfig, nex return next, nil, nil } -func flight0Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { +func flight0Generate(_ flightConn, state *State, _ *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { // Initialize - state.cookie = make([]byte, cookieLength) - if _, err := rand.Read(state.cookie); err != nil { - return nil, nil, err + if !cfg.insecureSkipHelloVerify { + state.cookie = make([]byte, cookieLength) + if _, err := rand.Read(state.cookie); err != nil { + return nil, nil, err + } } var zeroEpoch uint16 diff --git a/vendor/github.com/pion/dtls/v2/flight1handler.go b/vendor/github.com/pion/dtls/v2/flight1handler.go index 48c82013c..94fdc222d 100644 --- a/vendor/github.com/pion/dtls/v2/flight1handler.go +++ b/vendor/github.com/pion/dtls/v2/flight1handler.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -14,7 +17,7 @@ import ( func flight1Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { // HelloVerifyRequest can be skipped by the server, // so allow ServerHello during flight1 also - seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, + seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite, handshakeCachePullRule{handshake.TypeHelloVerifyRequest, cfg.initialEpoch, false, true}, handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, true}, ) @@ -43,7 +46,7 @@ func flight1Parse(ctx context.Context, c flightConn, state *State, cache *handsh return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil } -func flight1Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { +func flight1Generate(c flightConn, state *State, _ *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { var zeroEpoch uint16 state.localEpoch.Store(zeroEpoch) state.remoteEpoch.Store(zeroEpoch) @@ -62,10 +65,19 @@ func flight1Generate(c flightConn, state *State, cache *handshakeCache, cfg *han RenegotiatedConnection: 0, }, } - if cfg.localPSKCallback == nil { + + var setEllipticCurveCryptographyClientHelloExtensions bool + for _, c := range cfg.localCipherSuites { + if c.ECC() { + setEllipticCurveCryptographyClientHelloExtensions = true + break + } + } + + if setEllipticCurveCryptographyClientHelloExtensions { extensions = append(extensions, []extension.Extension{ &extension.SupportedEllipticCurves{ - EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384}, + EllipticCurves: cfg.ellipticCurves, }, &extension.SupportedPointFormats{ PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed}, diff --git a/vendor/github.com/pion/dtls/v2/flight2handler.go b/vendor/github.com/pion/dtls/v2/flight2handler.go index bb8e91db0..26e57d2f2 100644 --- a/vendor/github.com/pion/dtls/v2/flight2handler.go +++ b/vendor/github.com/pion/dtls/v2/flight2handler.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -11,7 +14,7 @@ import ( ) func flight2Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { - seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, + seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite, handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, ) if !ok { @@ -41,7 +44,7 @@ func flight2Parse(ctx context.Context, c flightConn, state *State, cache *handsh return flight4, nil, nil } -func flight2Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { +func flight2Generate(_ flightConn, state *State, _ *handshakeCache, _ *handshakeConfig) ([]*packet, *alert.Alert, error) { state.handshakeSendSequence = 0 return []*packet{ { diff --git a/vendor/github.com/pion/dtls/v2/flight3handler.go b/vendor/github.com/pion/dtls/v2/flight3handler.go index 697b304c4..5a763dc08 100644 --- a/vendor/github.com/pion/dtls/v2/flight3handler.go +++ b/vendor/github.com/pion/dtls/v2/flight3handler.go @@ -1,9 +1,13 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( "bytes" "context" + "github.com/pion/dtls/v2/internal/ciphersuite/types" "github.com/pion/dtls/v2/pkg/crypto/elliptic" "github.com/pion/dtls/v2/pkg/crypto/prf" "github.com/pion/dtls/v2/pkg/protocol" @@ -17,7 +21,7 @@ func flight3Parse(ctx context.Context, c flightConn, state *State, cache *handsh // Clients may receive multiple HelloVerifyRequest messages with different cookies. // Clients SHOULD handle this by sending a new ClientHello with a cookie in response // to the new HelloVerifyRequest. RFC 6347 Section 4.2.1 - seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, + seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite, handshakeCachePullRule{handshake.TypeHelloVerifyRequest, cfg.initialEpoch, false, true}, ) if ok { @@ -33,7 +37,7 @@ func flight3Parse(ctx context.Context, c flightConn, state *State, cache *handsh } } - _, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence, + _, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite, handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, ) if !ok { @@ -106,12 +110,12 @@ func flight3Parse(ctx context.Context, c flightConn, state *State, cache *handsh } if cfg.localPSKCallback != nil { - seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence+1, + seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite, handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, true}, handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false}, ) } else { - seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence+1, + seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite, handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, true}, handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false}, handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, true}, @@ -154,7 +158,7 @@ func handleResumption(ctx context.Context, c flightConn, state *State, cache *ha return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err } - _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence+1, + _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite, handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false}, ) if !ok { @@ -187,13 +191,29 @@ func handleResumption(ctx context.Context, c flightConn, state *State, cache *ha func handleServerKeyExchange(_ flightConn, state *State, cfg *handshakeConfig, h *handshake.MessageServerKeyExchange) (*alert.Alert, error) { var err error + if state.cipherSuite == nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite + } if cfg.localPSKCallback != nil { var psk []byte if psk, err = cfg.localPSKCallback(h.IdentityHint); err != nil { return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err } state.IdentityHint = h.IdentityHint - state.preMasterSecret = prf.PSKPreMasterSecret(psk) + switch state.cipherSuite.KeyExchangeAlgorithm() { + case types.KeyExchangeAlgorithmPsk: + state.preMasterSecret = prf.PSKPreMasterSecret(psk) + case (types.KeyExchangeAlgorithmEcdhe | types.KeyExchangeAlgorithmPsk): + if state.localKeypair, err = elliptic.GenerateKeypair(h.NamedCurve); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + state.preMasterSecret, err = prf.EcdhePSKPreMasterSecret(psk, h.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve) + if err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + default: + return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite + } } else { if state.localKeypair, err = elliptic.GenerateKeypair(h.NamedCurve); err != nil { return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err @@ -204,10 +224,10 @@ func handleServerKeyExchange(_ flightConn, state *State, cfg *handshakeConfig, h } } - return nil, nil + return nil, nil //nolint:nilnil } -func flight3Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { +func flight3Generate(_ flightConn, state *State, _ *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { extensions := []extension.Extension{ &extension.SupportedSignatureAlgorithms{ SignatureHashAlgorithms: cfg.localSignatureSchemes, @@ -216,7 +236,7 @@ func flight3Generate(c flightConn, state *State, cache *handshakeCache, cfg *han RenegotiatedConnection: 0, }, } - if cfg.localPSKCallback == nil { + if state.namedCurve != 0 { extensions = append(extensions, []extension.Extension{ &extension.SupportedEllipticCurves{ EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384}, diff --git a/vendor/github.com/pion/dtls/v2/flight4bhandler.go b/vendor/github.com/pion/dtls/v2/flight4bhandler.go index 36cd0b45f..6bbbc5972 100644 --- a/vendor/github.com/pion/dtls/v2/flight4bhandler.go +++ b/vendor/github.com/pion/dtls/v2/flight4bhandler.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -12,8 +15,8 @@ import ( "github.com/pion/dtls/v2/pkg/protocol/recordlayer" ) -func flight4bParse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { - _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, +func flight4bParse(_ context.Context, _ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite, handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false}, ) if !ok { @@ -44,7 +47,7 @@ func flight4bParse(ctx context.Context, c flightConn, state *State, cache *hands return flight4b, nil, nil } -func flight4bGenerate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { +func flight4bGenerate(_ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { var pkts []*packet extensions := []extension.Extension{&extension.RenegotiationInfo{ diff --git a/vendor/github.com/pion/dtls/v2/flight4handler.go b/vendor/github.com/pion/dtls/v2/flight4handler.go index a6ba36c2c..67a486461 100644 --- a/vendor/github.com/pion/dtls/v2/flight4handler.go +++ b/vendor/github.com/pion/dtls/v2/flight4handler.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -5,6 +8,7 @@ import ( "crypto/rand" "crypto/x509" + "github.com/pion/dtls/v2/internal/ciphersuite" "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" "github.com/pion/dtls/v2/pkg/crypto/elliptic" "github.com/pion/dtls/v2/pkg/crypto/prf" @@ -17,7 +21,7 @@ import ( ) func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { //nolint:gocognit - seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, + seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite, handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, true}, handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false}, handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, true}, @@ -89,6 +93,10 @@ func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handsh } } state.peerCertificatesVerified = verified + } else if state.PeerCertificates != nil { + // A certificate was received, but we haven't seen a CertificateVerify + // keep reading until we receive one + return 0, nil, nil } if !state.cipherSuite.IsInitialized() { @@ -103,7 +111,16 @@ func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handsh return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err } state.IdentityHint = clientKeyExchange.IdentityHint - preMasterSecret = prf.PSKPreMasterSecret(psk) + switch state.cipherSuite.KeyExchangeAlgorithm() { + case CipherSuiteKeyExchangeAlgorithmPsk: + preMasterSecret = prf.PSKPreMasterSecret(psk) + case (CipherSuiteKeyExchangeAlgorithmPsk | CipherSuiteKeyExchangeAlgorithmEcdhe): + if preMasterSecret, err = prf.EcdhePSKPreMasterSecret(psk, clientKeyExchange.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err + } + default: + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidCipherSuite + } } else { preMasterSecret, err = prf.PreMasterSecret(clientKeyExchange.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve) if err != nil { @@ -151,7 +168,7 @@ func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handsh return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err } - seq, msgs, ok = cache.fullPullMap(seq, + seq, msgs, ok = cache.fullPullMap(seq, state.cipherSuite, handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false}, ) if !ok { @@ -165,6 +182,11 @@ func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handsh } if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous { + if cfg.verifyConnection != nil { + if err := cfg.verifyConnection(state.clone()); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } + } return flight6, nil, nil } @@ -185,13 +207,18 @@ func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handsh return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, errClientCertificateNotVerified } case NoClientCert, RequestClientCert: - return flight6, nil, nil + // go to flight6 + } + if cfg.verifyConnection != nil { + if err := cfg.verifyConnection(state.clone()); err != nil { + return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } } return flight6, nil, nil } -func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { +func flight4Generate(_ flightConn, state *State, _ *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { extensions := []extension.Extension{&extension.RenegotiationInfo{ RenegotiatedConnection: 0, }} @@ -253,7 +280,10 @@ func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *han switch { case state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate: - certificate, err := cfg.getCertificate(state.serverName) + certificate, err := cfg.getCertificate(&ClientHelloInfo{ + ServerName: state.serverName, + CipherSuites: []ciphersuite.ID{state.cipherSuite.ID()}, + }) if err != nil { return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, err } @@ -305,6 +335,16 @@ func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *han }) if cfg.clientAuth > NoClientCert { + // An empty list of certificateAuthorities signals to + // the client that it may send any certificate in response + // to our request. When we know the CAs we trust, then + // we can send them down, so that the client can choose + // an appropriate certificate to give to us. + var certificateAuthorities [][]byte + if cfg.clientCAs != nil { + // nolint:staticcheck // ignoring tlsCert.RootCAs.Subjects is deprecated ERR because cert does not come from SystemCertPool and it's ok if certificate authorities is empty. + certificateAuthorities = cfg.clientCAs.Subjects() + } pkts = append(pkts, &packet{ record: &recordlayer.RecordLayer{ Header: recordlayer.Header{ @@ -312,43 +352,36 @@ func flight4Generate(c flightConn, state *State, cache *handshakeCache, cfg *han }, Content: &handshake.Handshake{ Message: &handshake.MessageCertificateRequest{ - CertificateTypes: []clientcertificate.Type{clientcertificate.RSASign, clientcertificate.ECDSASign}, - SignatureHashAlgorithms: cfg.localSignatureSchemes, + CertificateTypes: []clientcertificate.Type{clientcertificate.RSASign, clientcertificate.ECDSASign}, + SignatureHashAlgorithms: cfg.localSignatureSchemes, + CertificateAuthoritiesNames: certificateAuthorities, }, }, }, }) } - case cfg.localPSKIdentityHint != nil: + case cfg.localPSKIdentityHint != nil || state.cipherSuite.KeyExchangeAlgorithm().Has(CipherSuiteKeyExchangeAlgorithmEcdhe): // To help the client in selecting which identity to use, the server // can provide a "PSK identity hint" in the ServerKeyExchange message. - // If no hint is provided, the ServerKeyExchange message is omitted. + // If no hint is provided and cipher suite doesn't use elliptic curve, + // the ServerKeyExchange message is omitted. // // https://tools.ietf.org/html/rfc4279#section-2 + srvExchange := &handshake.MessageServerKeyExchange{ + IdentityHint: cfg.localPSKIdentityHint, + } + if state.cipherSuite.KeyExchangeAlgorithm().Has(CipherSuiteKeyExchangeAlgorithmEcdhe) { + srvExchange.EllipticCurveType = elliptic.CurveTypeNamedCurve + srvExchange.NamedCurve = state.namedCurve + srvExchange.PublicKey = state.localKeypair.PublicKey + } pkts = append(pkts, &packet{ record: &recordlayer.RecordLayer{ Header: recordlayer.Header{ Version: protocol.Version1_2, }, Content: &handshake.Handshake{ - Message: &handshake.MessageServerKeyExchange{ - IdentityHint: cfg.localPSKIdentityHint, - }, - }, - }, - }) - case state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous: - pkts = append(pkts, &packet{ - record: &recordlayer.RecordLayer{ - Header: recordlayer.Header{ - Version: protocol.Version1_2, - }, - Content: &handshake.Handshake{ - Message: &handshake.MessageServerKeyExchange{ - EllipticCurveType: elliptic.CurveTypeNamedCurve, - NamedCurve: state.namedCurve, - PublicKey: state.localKeypair.PublicKey, - }, + Message: srvExchange, }, }, }) diff --git a/vendor/github.com/pion/dtls/v2/flight5bhandler.go b/vendor/github.com/pion/dtls/v2/flight5bhandler.go index 577342e5b..ddd37324c 100644 --- a/vendor/github.com/pion/dtls/v2/flight5bhandler.go +++ b/vendor/github.com/pion/dtls/v2/flight5bhandler.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -10,8 +13,8 @@ import ( "github.com/pion/dtls/v2/pkg/protocol/recordlayer" ) -func flight5bParse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { - _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1, +func flight5bParse(_ context.Context, _ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1, state.cipherSuite, handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false}, ) if !ok { @@ -27,7 +30,7 @@ func flight5bParse(ctx context.Context, c flightConn, state *State, cache *hands return flight5b, nil, nil } -func flight5bGenerate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { //nolint:gocognit +func flight5bGenerate(_ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { //nolint:gocognit var pkts []*packet pkts = append(pkts, diff --git a/vendor/github.com/pion/dtls/v2/flight5handler.go b/vendor/github.com/pion/dtls/v2/flight5handler.go index 510fa4512..e8adf4f36 100644 --- a/vendor/github.com/pion/dtls/v2/flight5handler.go +++ b/vendor/github.com/pion/dtls/v2/flight5handler.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -14,8 +17,8 @@ import ( "github.com/pion/dtls/v2/pkg/protocol/recordlayer" ) -func flight5Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { - _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, +func flight5Parse(_ context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite, handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false}, ) if !ok { @@ -63,20 +66,30 @@ func flight5Parse(ctx context.Context, c flightConn, state *State, cache *handsh } func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { //nolint:gocognit - var certBytes [][]byte var privateKey crypto.PrivateKey - if len(cfg.localCertificates) > 0 { - certificate, err := cfg.getCertificate(cfg.serverName) + var pkts []*packet + if state.remoteRequestedCertificate { + _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-2, state.cipherSuite, + handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false}) + if !ok { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errClientCertificateRequired + } + reqInfo := CertificateRequestInfo{} + if r, ok := msgs[handshake.TypeCertificateRequest].(*handshake.MessageCertificateRequest); ok { + reqInfo.AcceptableCAs = r.CertificateAuthoritiesNames + } else { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errClientCertificateRequired + } + certificate, err := cfg.getClientCertificate(&reqInfo) if err != nil { return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, err } - certBytes = certificate.Certificate - privateKey = certificate.PrivateKey - } - - var pkts []*packet - - if state.remoteRequestedCertificate { + if certificate == nil { + return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errNotAcceptableCertificateChain + } + if certificate.Certificate != nil { + privateKey = certificate.PrivateKey + } pkts = append(pkts, &packet{ record: &recordlayer.RecordLayer{ @@ -85,7 +98,7 @@ func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *han }, Content: &handshake.Handshake{ Message: &handshake.MessageCertificate{ - Certificate: certBytes, + Certificate: certificate.Certificate, }, }, }, @@ -98,6 +111,9 @@ func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *han } else { clientKeyExchange.IdentityHint = cfg.localPSKIdentityHint } + if state != nil && state.localKeypair != nil && len(state.localKeypair.PublicKey) > 0 { + clientKeyExchange.PublicKey = state.localKeypair.PublicKey + } pkts = append(pkts, &packet{ @@ -124,7 +140,9 @@ func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *han return nil, alertPtr, err } } else { - rawHandshake := &handshake.Handshake{} + rawHandshake := &handshake.Handshake{ + KeyExchangeAlgorithm: state.cipherSuite.KeyExchangeAlgorithm(), + } err := rawHandshake.Unmarshal(serverKeyExchangeData) if err != nil { return nil, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, err @@ -162,7 +180,7 @@ func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *han // If the client has sent a certificate with signing ability, a digitally-signed // CertificateVerify message is sent to explicitly verify possession of the // private key in the certificate. - if state.remoteRequestedCertificate && len(cfg.localCertificates) > 0 { + if state.remoteRequestedCertificate && privateKey != nil { plainText := append(cache.pullAndMerge( handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false}, handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false}, @@ -268,7 +286,7 @@ func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *han func initalizeCipherSuite(state *State, cache *handshakeCache, cfg *handshakeConfig, h *handshake.MessageServerKeyExchange, sendingPlainText []byte) (*alert.Alert, error) { //nolint:gocognit if state.cipherSuite.IsInitialized() { - return nil, nil + return nil, nil //nolint } clientRandom := state.localRandom.MarshalFixed() @@ -323,6 +341,11 @@ func initalizeCipherSuite(state *State, cache *handshakeCache, cfg *handshakeCon } } } + if cfg.verifyConnection != nil { + if err = cfg.verifyConnection(state.clone()); err != nil { + return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err + } + } if err = state.cipherSuite.Init(state.masterSecret, clientRandom[:], serverRandom[:], true); err != nil { return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err @@ -330,5 +353,5 @@ func initalizeCipherSuite(state *State, cache *handshakeCache, cfg *handshakeCon cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret) - return nil, nil + return nil, nil //nolint } diff --git a/vendor/github.com/pion/dtls/v2/flight6handler.go b/vendor/github.com/pion/dtls/v2/flight6handler.go index fddaa0e6b..57ac14360 100644 --- a/vendor/github.com/pion/dtls/v2/flight6handler.go +++ b/vendor/github.com/pion/dtls/v2/flight6handler.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -10,8 +13,8 @@ import ( "github.com/pion/dtls/v2/pkg/protocol/recordlayer" ) -func flight6Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { - _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1, +func flight6Parse(_ context.Context, _ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { + _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1, state.cipherSuite, handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false}, ) if !ok { @@ -27,7 +30,7 @@ func flight6Parse(ctx context.Context, c flightConn, state *State, cache *handsh return flight6, nil, nil } -func flight6Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { +func flight6Generate(_ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { var pkts []*packet pkts = append(pkts, diff --git a/vendor/github.com/pion/dtls/v2/flighthandler.go b/vendor/github.com/pion/dtls/v2/flighthandler.go index f899ffa5b..ceb4a992b 100644 --- a/vendor/github.com/pion/dtls/v2/flighthandler.go +++ b/vendor/github.com/pion/dtls/v2/flighthandler.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( diff --git a/vendor/github.com/pion/dtls/v2/fragment_buffer.go b/vendor/github.com/pion/dtls/v2/fragment_buffer.go index 02749939f..f20033758 100644 --- a/vendor/github.com/pion/dtls/v2/fragment_buffer.go +++ b/vendor/github.com/pion/dtls/v2/fragment_buffer.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -6,6 +9,9 @@ import ( "github.com/pion/dtls/v2/pkg/protocol/recordlayer" ) +// 2 megabytes +const fragmentBufferMaxSize = 2000000 + type fragment struct { recordLayerHeader recordlayer.Header handshakeHeader handshake.Header @@ -23,10 +29,25 @@ func newFragmentBuffer() *fragmentBuffer { return &fragmentBuffer{cache: map[uint16][]*fragment{}} } +// current total size of buffer +func (f *fragmentBuffer) size() int { + size := 0 + for i := range f.cache { + for j := range f.cache[i] { + size += len(f.cache[i][j].data) + } + } + return size +} + // Attempts to push a DTLS packet to the fragmentBuffer // when it returns true it means the fragmentBuffer has inserted and the buffer shouldn't be handled // when an error returns it is fatal, and the DTLS connection should be stopped func (f *fragmentBuffer) push(buf []byte) (bool, error) { + if f.size()+len(buf) >= fragmentBufferMaxSize { + return false, errFragmentBufferOverflow + } + frag := new(fragment) if err := frag.recordLayerHeader.Unmarshal(buf); err != nil { return false, err @@ -76,7 +97,7 @@ func (f *fragmentBuffer) pop() (content []byte, epoch uint16) { for _, f := range frags { if f.handshakeHeader.FragmentOffset == targetOffset { fragmentEnd := (f.handshakeHeader.FragmentOffset + f.handshakeHeader.FragmentLength) - if fragmentEnd != f.handshakeHeader.Length { + if fragmentEnd != f.handshakeHeader.Length && f.handshakeHeader.FragmentLength != 0 { if !appendMessage(fragmentEnd) { return false } diff --git a/vendor/github.com/pion/dtls/v2/fuzz.go b/vendor/github.com/pion/dtls/v2/fuzz.go deleted file mode 100644 index d6863241e..000000000 --- a/vendor/github.com/pion/dtls/v2/fuzz.go +++ /dev/null @@ -1,39 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -package dtls - -import "fmt" - -func partialHeaderMismatch(a, b recordlayer.Header) bool { - // Ignoring content length for now. - a.contentLen = b.contentLen - return a != b -} - -func FuzzRecordLayer(data []byte) int { - var r recordLayer - if err := r.Unmarshal(data); err != nil { - return 0 - } - buf, err := r.Marshal() - if err != nil { - return 1 - } - if len(buf) == 0 { - panic("zero buff") // nolint - } - var nr recordLayer - if err = nr.Unmarshal(data); err != nil { - panic(err) // nolint - } - if partialHeaderMismatch(nr.recordlayer.Header, r.recordlayer.Header) { - panic( // nolint - fmt.Sprintf("header mismatch: %+v != %+v", - nr.recordlayer.Header, r.recordlayer.Header, - ), - ) - } - - return 1 -} diff --git a/vendor/github.com/pion/dtls/v2/handshake_cache.go b/vendor/github.com/pion/dtls/v2/handshake_cache.go index 063a85807..8d5960568 100644 --- a/vendor/github.com/pion/dtls/v2/handshake_cache.go +++ b/vendor/github.com/pion/dtls/v2/handshake_cache.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -31,17 +34,10 @@ func newHandshakeCache() *handshakeCache { return &handshakeCache{} } -func (h *handshakeCache) push(data []byte, epoch, messageSequence uint16, typ handshake.Type, isClient bool) bool { //nolint +func (h *handshakeCache) push(data []byte, epoch, messageSequence uint16, typ handshake.Type, isClient bool) { h.mu.Lock() defer h.mu.Unlock() - for _, i := range h.cache { - if i.messageSequence == messageSequence && - i.isClient == isClient { - return false - } - } - h.cache = append(h.cache, &handshakeCacheItem{ data: append([]byte{}, data...), epoch: epoch, @@ -49,7 +45,6 @@ func (h *handshakeCache) push(data []byte, epoch, messageSequence uint16, typ ha typ: typ, isClient: isClient, }) - return true } // returns a list handshakes that match the requested rules @@ -77,7 +72,7 @@ func (h *handshakeCache) pull(rules ...handshakeCachePullRule) []*handshakeCache } // fullPullMap pulls all handshakes between rules[0] to rules[len(rules)-1] as map. -func (h *handshakeCache) fullPullMap(startSeq int, rules ...handshakeCachePullRule) (int, map[handshake.Type]handshake.Message, bool) { +func (h *handshakeCache) fullPullMap(startSeq int, cipherSuite CipherSuite, rules ...handshakeCachePullRule) (int, map[handshake.Type]handshake.Message, bool) { h.mu.Lock() defer h.mu.Unlock() @@ -108,7 +103,13 @@ func (h *handshakeCache) fullPullMap(startSeq int, rules ...handshakeCachePullRu if i == nil { continue } - rawHandshake := &handshake.Handshake{} + var keyExchangeAlgorithm CipherSuiteKeyExchangeAlgorithm + if cipherSuite != nil { + keyExchangeAlgorithm = cipherSuite.KeyExchangeAlgorithm() + } + rawHandshake := &handshake.Handshake{ + KeyExchangeAlgorithm: keyExchangeAlgorithm, + } if err := rawHandshake.Unmarshal(i.data); err != nil { return startSeq, nil, false } diff --git a/vendor/github.com/pion/dtls/v2/handshaker.go b/vendor/github.com/pion/dtls/v2/handshaker.go index 1c7b9ffa2..1c6d58fe9 100644 --- a/vendor/github.com/pion/dtls/v2/handshaker.go +++ b/vendor/github.com/pion/dtls/v2/handshaker.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -9,6 +12,7 @@ import ( "sync" "time" + "github.com/pion/dtls/v2/pkg/crypto/elliptic" "github.com/pion/dtls/v2/pkg/crypto/signaturehash" "github.com/pion/dtls/v2/pkg/protocol/alert" "github.com/pion/dtls/v2/pkg/protocol/handshake" @@ -101,16 +105,22 @@ type handshakeConfig struct { nameToCertificate map[string]*tls.Certificate insecureSkipVerify bool verifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error + verifyConnection func(*State) error sessionStore SessionStore rootCAs *x509.CertPool clientCAs *x509.CertPool retransmitInterval time.Duration customCipherSuites func() []CipherSuite + ellipticCurves []elliptic.Curve + insecureSkipHelloVerify bool onFlightState func(flightVal, handshakeState) log logging.LeveledLogger keyLogWriter io.Writer + localGetCertificate func(*ClientHelloInfo) (*tls.Certificate, error) + localGetClientCertificate func(*CertificateRequestInfo) (*tls.Certificate, error) + initialEpoch uint16 mu sync.Mutex diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go index dcc537991..f78b6dc2c 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go @@ -1,108 +1,33 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import ( - "crypto/sha256" - "fmt" - "hash" - "sync/atomic" - "github.com/pion/dtls/v2/pkg/crypto/ciphersuite" "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" - "github.com/pion/dtls/v2/pkg/crypto/prf" - "github.com/pion/dtls/v2/pkg/protocol/recordlayer" ) // Aes128Ccm is a base class used by multiple AES-CCM Ciphers type Aes128Ccm struct { - ccm atomic.Value // *cryptoCCM - clientCertificateType clientcertificate.Type - id ID - psk bool - cryptoCCMTagLen ciphersuite.CCMTagLen + AesCcm } -func newAes128Ccm(clientCertificateType clientcertificate.Type, id ID, psk bool, cryptoCCMTagLen ciphersuite.CCMTagLen) *Aes128Ccm { +func newAes128Ccm(clientCertificateType clientcertificate.Type, id ID, psk bool, cryptoCCMTagLen ciphersuite.CCMTagLen, keyExchangeAlgorithm KeyExchangeAlgorithm, ecc bool) *Aes128Ccm { return &Aes128Ccm{ - clientCertificateType: clientCertificateType, - id: id, - psk: psk, - cryptoCCMTagLen: cryptoCCMTagLen, + AesCcm: AesCcm{ + clientCertificateType: clientCertificateType, + id: id, + psk: psk, + cryptoCCMTagLen: cryptoCCMTagLen, + keyExchangeAlgorithm: keyExchangeAlgorithm, + ecc: ecc, + }, } } -// CertificateType returns what type of certificate this CipherSuite exchanges -func (c *Aes128Ccm) CertificateType() clientcertificate.Type { - return c.clientCertificateType -} - -// ID returns the ID of the CipherSuite -func (c *Aes128Ccm) ID() ID { - return c.id -} - -func (c *Aes128Ccm) String() string { - return c.id.String() -} - -// HashFunc returns the hashing func for this CipherSuite -func (c *Aes128Ccm) HashFunc() func() hash.Hash { - return sha256.New -} - -// AuthenticationType controls what authentication method is using during the handshake -func (c *Aes128Ccm) AuthenticationType() AuthenticationType { - if c.psk { - return AuthenticationTypePreSharedKey - } - return AuthenticationTypeCertificate -} - -// IsInitialized returns if the CipherSuite has keying material and can -// encrypt/decrypt packets -func (c *Aes128Ccm) IsInitialized() bool { - return c.ccm.Load() != nil -} - // Init initializes the internal Cipher with keying material func (c *Aes128Ccm) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error { - const ( - prfMacLen = 0 - prfKeyLen = 16 - prfIvLen = 4 - ) - - keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc()) - if err != nil { - return err - } - - var ccm *ciphersuite.CCM - if isClient { - ccm, err = ciphersuite.NewCCM(c.cryptoCCMTagLen, keys.ClientWriteKey, keys.ClientWriteIV, keys.ServerWriteKey, keys.ServerWriteIV) - } else { - ccm, err = ciphersuite.NewCCM(c.cryptoCCMTagLen, keys.ServerWriteKey, keys.ServerWriteIV, keys.ClientWriteKey, keys.ClientWriteIV) - } - c.ccm.Store(ccm) - - return err -} - -// Encrypt encrypts a single TLS RecordLayer -func (c *Aes128Ccm) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) { - ccm := c.ccm.Load() - if ccm == nil { - return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit) - } - - return ccm.(*ciphersuite.CCM).Encrypt(pkt, raw) -} - -// Decrypt decrypts a single TLS RecordLayer -func (c *Aes128Ccm) Decrypt(raw []byte) ([]byte, error) { - ccm := c.ccm.Load() - if ccm == nil { - return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) - } - - return ccm.(*ciphersuite.CCM).Decrypt(raw) + const prfKeyLen = 16 + return c.AesCcm.Init(masterSecret, clientRandom, serverRandom, isClient, prfKeyLen) } diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_256_ccm.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_256_ccm.go new file mode 100644 index 000000000..bb8128627 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_256_ccm.go @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package ciphersuite + +import ( + "github.com/pion/dtls/v2/pkg/crypto/ciphersuite" + "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" +) + +// Aes256Ccm is a base class used by multiple AES-CCM Ciphers +type Aes256Ccm struct { + AesCcm +} + +func newAes256Ccm(clientCertificateType clientcertificate.Type, id ID, psk bool, cryptoCCMTagLen ciphersuite.CCMTagLen, keyExchangeAlgorithm KeyExchangeAlgorithm, ecc bool) *Aes256Ccm { + return &Aes256Ccm{ + AesCcm: AesCcm{ + clientCertificateType: clientCertificateType, + id: id, + psk: psk, + cryptoCCMTagLen: cryptoCCMTagLen, + keyExchangeAlgorithm: keyExchangeAlgorithm, + ecc: ecc, + }, + } +} + +// Init initializes the internal Cipher with keying material +func (c *Aes256Ccm) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error { + const prfKeyLen = 32 + return c.AesCcm.Init(masterSecret, clientRandom, serverRandom, isClient, prfKeyLen) +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_ccm.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_ccm.go new file mode 100644 index 000000000..dc5119823 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_ccm.go @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package ciphersuite + +import ( + "crypto/sha256" + "fmt" + "hash" + "sync/atomic" + + "github.com/pion/dtls/v2/pkg/crypto/ciphersuite" + "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +// AesCcm is a base class used by multiple AES-CCM Ciphers +type AesCcm struct { + ccm atomic.Value // *cryptoCCM + clientCertificateType clientcertificate.Type + id ID + psk bool + keyExchangeAlgorithm KeyExchangeAlgorithm + cryptoCCMTagLen ciphersuite.CCMTagLen + ecc bool +} + +// CertificateType returns what type of certificate this CipherSuite exchanges +func (c *AesCcm) CertificateType() clientcertificate.Type { + return c.clientCertificateType +} + +// ID returns the ID of the CipherSuite +func (c *AesCcm) ID() ID { + return c.id +} + +func (c *AesCcm) String() string { + return c.id.String() +} + +// ECC uses Elliptic Curve Cryptography +func (c *AesCcm) ECC() bool { + return c.ecc +} + +// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake +func (c *AesCcm) KeyExchangeAlgorithm() KeyExchangeAlgorithm { + return c.keyExchangeAlgorithm +} + +// HashFunc returns the hashing func for this CipherSuite +func (c *AesCcm) HashFunc() func() hash.Hash { + return sha256.New +} + +// AuthenticationType controls what authentication method is using during the handshake +func (c *AesCcm) AuthenticationType() AuthenticationType { + if c.psk { + return AuthenticationTypePreSharedKey + } + return AuthenticationTypeCertificate +} + +// IsInitialized returns if the CipherSuite has keying material and can +// encrypt/decrypt packets +func (c *AesCcm) IsInitialized() bool { + return c.ccm.Load() != nil +} + +// Init initializes the internal Cipher with keying material +func (c *AesCcm) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool, prfKeyLen int) error { + const ( + prfMacLen = 0 + prfIvLen = 4 + ) + + keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc()) + if err != nil { + return err + } + + var ccm *ciphersuite.CCM + if isClient { + ccm, err = ciphersuite.NewCCM(c.cryptoCCMTagLen, keys.ClientWriteKey, keys.ClientWriteIV, keys.ServerWriteKey, keys.ServerWriteIV) + } else { + ccm, err = ciphersuite.NewCCM(c.cryptoCCMTagLen, keys.ServerWriteKey, keys.ServerWriteIV, keys.ClientWriteKey, keys.ClientWriteIV) + } + c.ccm.Store(ccm) + + return err +} + +// Encrypt encrypts a single TLS RecordLayer +func (c *AesCcm) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) { + cipherSuite, ok := c.ccm.Load().(*ciphersuite.CCM) + if !ok { + return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit) + } + + return cipherSuite.Encrypt(pkt, raw) +} + +// Decrypt decrypts a single TLS RecordLayer +func (c *AesCcm) Decrypt(raw []byte) ([]byte, error) { + cipherSuite, ok := c.ccm.Load().(*ciphersuite.CCM) + if !ok { + return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) + } + + return cipherSuite.Decrypt(raw) +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go index 7405792c2..f44f29fd3 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package ciphersuite provides TLS Ciphers as registered with the IANA https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4 package ciphersuite @@ -5,6 +8,7 @@ import ( "errors" "fmt" + "github.com/pion/dtls/v2/internal/ciphersuite/types" "github.com/pion/dtls/v2/pkg/protocol" ) @@ -31,6 +35,8 @@ func (i ID) String() string { return "TLS_PSK_WITH_AES_128_CCM" case TLS_PSK_WITH_AES_128_CCM_8: return "TLS_PSK_WITH_AES_128_CCM_8" + case TLS_PSK_WITH_AES_256_CCM_8: + return "TLS_PSK_WITH_AES_256_CCM_8" case TLS_PSK_WITH_AES_128_GCM_SHA256: return "TLS_PSK_WITH_AES_128_GCM_SHA256" case TLS_PSK_WITH_AES_128_CBC_SHA256: @@ -39,6 +45,8 @@ func (i ID) String() string { return "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: return "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" + case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256: + return "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256" default: return fmt.Sprintf("unknown(%v)", uint16(i)) } @@ -47,31 +55,44 @@ func (i ID) String() string { // Supported Cipher Suites const ( // AES-128-CCM - TLS_ECDHE_ECDSA_WITH_AES_128_CCM ID = 0xc0ac //nolint:golint,stylecheck - TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ID = 0xc0ae //nolint:golint,stylecheck + TLS_ECDHE_ECDSA_WITH_AES_128_CCM ID = 0xc0ac //nolint:revive,stylecheck + TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ID = 0xc0ae //nolint:revive,stylecheck // AES-128-GCM-SHA256 - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ID = 0xc02b //nolint:golint,stylecheck - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ID = 0xc02f //nolint:golint,stylecheck + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ID = 0xc02b //nolint:revive,stylecheck + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ID = 0xc02f //nolint:revive,stylecheck - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ID = 0xc02c //nolint:golint,stylecheck - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ID = 0xc030 //nolint:golint,stylecheck + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ID = 0xc02c //nolint:revive,stylecheck + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ID = 0xc030 //nolint:revive,stylecheck // AES-256-CBC-SHA - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ID = 0xc00a //nolint:golint,stylecheck - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ID = 0xc014 //nolint:golint,stylecheck + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ID = 0xc00a //nolint:revive,stylecheck + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ID = 0xc014 //nolint:revive,stylecheck - TLS_PSK_WITH_AES_128_CCM ID = 0xc0a4 //nolint:golint,stylecheck - TLS_PSK_WITH_AES_128_CCM_8 ID = 0xc0a8 //nolint:golint,stylecheck - TLS_PSK_WITH_AES_128_GCM_SHA256 ID = 0x00a8 //nolint:golint,stylecheck - TLS_PSK_WITH_AES_128_CBC_SHA256 ID = 0x00ae //nolint:golint,stylecheck + TLS_PSK_WITH_AES_128_CCM ID = 0xc0a4 //nolint:revive,stylecheck + TLS_PSK_WITH_AES_128_CCM_8 ID = 0xc0a8 //nolint:revive,stylecheck + TLS_PSK_WITH_AES_256_CCM_8 ID = 0xc0a9 //nolint:revive,stylecheck + TLS_PSK_WITH_AES_128_GCM_SHA256 ID = 0x00a8 //nolint:revive,stylecheck + TLS_PSK_WITH_AES_128_CBC_SHA256 ID = 0x00ae //nolint:revive,stylecheck + + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 ID = 0xC037 //nolint:revive,stylecheck ) // AuthenticationType controls what authentication method is using during the handshake -type AuthenticationType int +type AuthenticationType = types.AuthenticationType // AuthenticationType Enums const ( - AuthenticationTypeCertificate AuthenticationType = iota + 1 - AuthenticationTypePreSharedKey - AuthenticationTypeAnonymous + AuthenticationTypeCertificate AuthenticationType = types.AuthenticationTypeCertificate + AuthenticationTypePreSharedKey AuthenticationType = types.AuthenticationTypePreSharedKey + AuthenticationTypeAnonymous AuthenticationType = types.AuthenticationTypeAnonymous +) + +// KeyExchangeAlgorithm controls what exchange algorithm was chosen. +type KeyExchangeAlgorithm = types.KeyExchangeAlgorithm + +// KeyExchangeAlgorithm Bitmask +const ( + KeyExchangeAlgorithmNone KeyExchangeAlgorithm = types.KeyExchangeAlgorithmNone + KeyExchangeAlgorithmPsk KeyExchangeAlgorithm = types.KeyExchangeAlgorithmPsk + KeyExchangeAlgorithmEcdhe KeyExchangeAlgorithm = types.KeyExchangeAlgorithmEcdhe ) diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go index ac73556fb..8367b2c6d 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import ( @@ -7,5 +10,5 @@ import ( // NewTLSEcdheEcdsaWithAes128Ccm constructs a TLS_ECDHE_ECDSA_WITH_AES_128_CCM Cipher func NewTLSEcdheEcdsaWithAes128Ccm() *Aes128Ccm { - return newAes128Ccm(clientcertificate.ECDSASign, TLS_ECDHE_ECDSA_WITH_AES_128_CCM, false, ciphersuite.CCMTagLength) + return newAes128Ccm(clientcertificate.ECDSASign, TLS_ECDHE_ECDSA_WITH_AES_128_CCM, false, ciphersuite.CCMTagLength, KeyExchangeAlgorithmEcdhe, true) } diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go index 49b1a8304..11b687327 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import ( @@ -7,5 +10,5 @@ import ( // NewTLSEcdheEcdsaWithAes128Ccm8 creates a new TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuite func NewTLSEcdheEcdsaWithAes128Ccm8() *Aes128Ccm { - return newAes128Ccm(clientcertificate.ECDSASign, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, false, ciphersuite.CCMTagLength8) + return newAes128Ccm(clientcertificate.ECDSASign, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, false, ciphersuite.CCMTagLength8, KeyExchangeAlgorithmEcdhe, true) } diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go index ede12bbe5..0c919fe47 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import ( @@ -22,6 +25,16 @@ func (c *TLSEcdheEcdsaWithAes128GcmSha256) CertificateType() clientcertificate.T return clientcertificate.ECDSASign } +// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake +func (c *TLSEcdheEcdsaWithAes128GcmSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm { + return KeyExchangeAlgorithmEcdhe +} + +// ECC uses Elliptic Curve Cryptography +func (c *TLSEcdheEcdsaWithAes128GcmSha256) ECC() bool { + return true +} + // ID returns the ID of the CipherSuite func (c *TLSEcdheEcdsaWithAes128GcmSha256) ID() ID { return TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 @@ -76,20 +89,20 @@ func (c *TLSEcdheEcdsaWithAes128GcmSha256) Init(masterSecret, clientRandom, serv // Encrypt encrypts a single TLS RecordLayer func (c *TLSEcdheEcdsaWithAes128GcmSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) { - gcm := c.gcm.Load() - if gcm == nil { + cipherSuite, ok := c.gcm.Load().(*ciphersuite.GCM) + if !ok { return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit) } - return gcm.(*ciphersuite.GCM).Encrypt(pkt, raw) + return cipherSuite.Encrypt(pkt, raw) } // Decrypt decrypts a single TLS RecordLayer func (c *TLSEcdheEcdsaWithAes128GcmSha256) Decrypt(raw []byte) ([]byte, error) { - gcm := c.gcm.Load() - if gcm == nil { + cipherSuite, ok := c.gcm.Load().(*ciphersuite.GCM) + if !ok { return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) } - return gcm.(*ciphersuite.GCM).Decrypt(raw) + return cipherSuite.Decrypt(raw) } diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go index f7a33ad8d..577192c89 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import ( @@ -23,6 +26,16 @@ func (c *TLSEcdheEcdsaWithAes256CbcSha) CertificateType() clientcertificate.Type return clientcertificate.ECDSASign } +// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake +func (c *TLSEcdheEcdsaWithAes256CbcSha) KeyExchangeAlgorithm() KeyExchangeAlgorithm { + return KeyExchangeAlgorithmEcdhe +} + +// ECC uses Elliptic Curve Cryptography +func (c *TLSEcdheEcdsaWithAes256CbcSha) ECC() bool { + return true +} + // ID returns the ID of the CipherSuite func (c *TLSEcdheEcdsaWithAes256CbcSha) ID() ID { return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA @@ -82,20 +95,20 @@ func (c *TLSEcdheEcdsaWithAes256CbcSha) Init(masterSecret, clientRandom, serverR // Encrypt encrypts a single TLS RecordLayer func (c *TLSEcdheEcdsaWithAes256CbcSha) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) { - cbc := c.cbc.Load() - if cbc == nil { // !c.isInitialized() + cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC) + if !ok { return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit) } - return cbc.(*ciphersuite.CBC).Encrypt(pkt, raw) + return cipherSuite.Encrypt(pkt, raw) } // Decrypt decrypts a single TLS RecordLayer func (c *TLSEcdheEcdsaWithAes256CbcSha) Decrypt(raw []byte) ([]byte, error) { - cbc := c.cbc.Load() - if cbc == nil { // !c.isInitialized() + cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC) + if !ok { return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) } - return cbc.(*ciphersuite.CBC).Decrypt(raw) + return cipherSuite.Decrypt(raw) } diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_gcm_sha384.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_gcm_sha384.go index a2fe30244..2a3cfa4f5 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_gcm_sha384.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_gcm_sha384.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import ( diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_psk_with_aes_128_cbc_sha256.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_psk_with_aes_128_cbc_sha256.go new file mode 100644 index 000000000..75a25633a --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_psk_with_aes_128_cbc_sha256.go @@ -0,0 +1,118 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package ciphersuite + +import ( + "crypto/sha256" + "fmt" + "hash" + "sync/atomic" + + "github.com/pion/dtls/v2/pkg/crypto/ciphersuite" + "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" + "github.com/pion/dtls/v2/pkg/crypto/prf" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +// TLSEcdhePskWithAes128CbcSha256 implements the TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CipherSuite +type TLSEcdhePskWithAes128CbcSha256 struct { + cbc atomic.Value // *cryptoCBC +} + +// NewTLSEcdhePskWithAes128CbcSha256 creates TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 cipher. +func NewTLSEcdhePskWithAes128CbcSha256() *TLSEcdhePskWithAes128CbcSha256 { + return &TLSEcdhePskWithAes128CbcSha256{} +} + +// CertificateType returns what type of certificate this CipherSuite exchanges +func (c *TLSEcdhePskWithAes128CbcSha256) CertificateType() clientcertificate.Type { + return clientcertificate.Type(0) +} + +// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake +func (c *TLSEcdhePskWithAes128CbcSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm { + return (KeyExchangeAlgorithmPsk | KeyExchangeAlgorithmEcdhe) +} + +// ECC uses Elliptic Curve Cryptography +func (c *TLSEcdhePskWithAes128CbcSha256) ECC() bool { + return true +} + +// ID returns the ID of the CipherSuite +func (c *TLSEcdhePskWithAes128CbcSha256) ID() ID { + return TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 +} + +func (c *TLSEcdhePskWithAes128CbcSha256) String() string { + return "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA256" +} + +// HashFunc returns the hashing func for this CipherSuite +func (c *TLSEcdhePskWithAes128CbcSha256) HashFunc() func() hash.Hash { + return sha256.New +} + +// AuthenticationType controls what authentication method is using during the handshake +func (c *TLSEcdhePskWithAes128CbcSha256) AuthenticationType() AuthenticationType { + return AuthenticationTypePreSharedKey +} + +// IsInitialized returns if the CipherSuite has keying material and can +// encrypt/decrypt packets +func (c *TLSEcdhePskWithAes128CbcSha256) IsInitialized() bool { + return c.cbc.Load() != nil +} + +// Init initializes the internal Cipher with keying material +func (c *TLSEcdhePskWithAes128CbcSha256) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error { + const ( + prfMacLen = 32 + prfKeyLen = 16 + prfIvLen = 16 + ) + + keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc()) + if err != nil { + return err + } + + var cbc *ciphersuite.CBC + if isClient { + cbc, err = ciphersuite.NewCBC( + keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey, + keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey, + c.HashFunc(), + ) + } else { + cbc, err = ciphersuite.NewCBC( + keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey, + keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey, + c.HashFunc(), + ) + } + c.cbc.Store(cbc) + + return err +} + +// Encrypt encrypts a single TLS RecordLayer +func (c *TLSEcdhePskWithAes128CbcSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) { + cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC) + if !ok { // !c.isInitialized() + return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit) + } + + return cipherSuite.Encrypt(pkt, raw) +} + +// Decrypt decrypts a single TLS RecordLayer +func (c *TLSEcdhePskWithAes128CbcSha256) Decrypt(raw []byte) ([]byte, error) { + cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC) + if !ok { // !c.isInitialized() + return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) + } + + return cipherSuite.Decrypt(raw) +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go index 70400c37d..478a2e0dc 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go index 0d82dc3ad..8e88ee639 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_gcm_sha384.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_gcm_sha384.go index 3473527e7..752fb529c 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_gcm_sha384.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_gcm_sha384.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go index 43e5e3800..7336ad946 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import ( @@ -22,6 +25,16 @@ func (c *TLSPskWithAes128CbcSha256) CertificateType() clientcertificate.Type { return clientcertificate.Type(0) } +// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake +func (c *TLSPskWithAes128CbcSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm { + return KeyExchangeAlgorithmPsk +} + +// ECC uses Elliptic Curve Cryptography +func (c *TLSPskWithAes128CbcSha256) ECC() bool { + return false +} + // ID returns the ID of the CipherSuite func (c *TLSPskWithAes128CbcSha256) ID() ID { return TLS_PSK_WITH_AES_128_CBC_SHA256 @@ -81,20 +94,20 @@ func (c *TLSPskWithAes128CbcSha256) Init(masterSecret, clientRandom, serverRando // Encrypt encrypts a single TLS RecordLayer func (c *TLSPskWithAes128CbcSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) { - cbc := c.cbc.Load() - if cbc == nil { // !c.isInitialized() - return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) + cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC) + if !ok { + return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit) } - return cbc.(*ciphersuite.CBC).Encrypt(pkt, raw) + return cipherSuite.Encrypt(pkt, raw) } // Decrypt decrypts a single TLS RecordLayer func (c *TLSPskWithAes128CbcSha256) Decrypt(raw []byte) ([]byte, error) { - cbc := c.cbc.Load() - if cbc == nil { // !c.isInitialized() + cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC) + if !ok { return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) } - return cbc.(*ciphersuite.CBC).Decrypt(raw) + return cipherSuite.Decrypt(raw) } diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go index 8c13bb1b3..1ded09b88 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import ( @@ -7,5 +10,5 @@ import ( // NewTLSPskWithAes128Ccm returns the TLS_PSK_WITH_AES_128_CCM CipherSuite func NewTLSPskWithAes128Ccm() *Aes128Ccm { - return newAes128Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_128_CCM, true, ciphersuite.CCMTagLength) + return newAes128Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_128_CCM, true, ciphersuite.CCMTagLength, KeyExchangeAlgorithmPsk, false) } diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go index d04abb4d2..478197074 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import ( @@ -7,5 +10,5 @@ import ( // NewTLSPskWithAes128Ccm8 returns the TLS_PSK_WITH_AES_128_CCM_8 CipherSuite func NewTLSPskWithAes128Ccm8() *Aes128Ccm { - return newAes128Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_128_CCM_8, true, ciphersuite.CCMTagLength8) + return newAes128Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_128_CCM_8, true, ciphersuite.CCMTagLength8, KeyExchangeAlgorithmPsk, false) } diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go index 5f1033559..8ab5b89a8 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" @@ -12,6 +15,11 @@ func (c *TLSPskWithAes128GcmSha256) CertificateType() clientcertificate.Type { return clientcertificate.Type(0) } +// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake +func (c *TLSPskWithAes128GcmSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm { + return KeyExchangeAlgorithmPsk +} + // ID returns the ID of the CipherSuite func (c *TLSPskWithAes128GcmSha256) ID() ID { return TLS_PSK_WITH_AES_128_GCM_SHA256 diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_256_ccm8.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_256_ccm8.go new file mode 100644 index 000000000..32d503018 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_256_ccm8.go @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package ciphersuite + +import ( + "github.com/pion/dtls/v2/pkg/crypto/ciphersuite" + "github.com/pion/dtls/v2/pkg/crypto/clientcertificate" +) + +// NewTLSPskWithAes256Ccm8 returns the TLS_PSK_WITH_AES_256_CCM_8 CipherSuite +func NewTLSPskWithAes256Ccm8() *Aes256Ccm { + return newAes256Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_256_CCM_8, true, ciphersuite.CCMTagLength8, KeyExchangeAlgorithmPsk, false) +} diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/types/authentication_type.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/types/authentication_type.go new file mode 100644 index 000000000..2da21e642 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/types/authentication_type.go @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package types + +// AuthenticationType controls what authentication method is using during the handshake +type AuthenticationType int + +// AuthenticationType Enums +const ( + AuthenticationTypeCertificate AuthenticationType = iota + 1 + AuthenticationTypePreSharedKey + AuthenticationTypeAnonymous +) diff --git a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/types/key_exchange_algorithm.go b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/types/key_exchange_algorithm.go new file mode 100644 index 000000000..c2c39113a --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/types/key_exchange_algorithm.go @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package types provides types for TLS Ciphers +package types + +// KeyExchangeAlgorithm controls what exchange algorithm was chosen. +type KeyExchangeAlgorithm int + +// KeyExchangeAlgorithm Bitmask +const ( + KeyExchangeAlgorithmNone KeyExchangeAlgorithm = 0 + KeyExchangeAlgorithmPsk KeyExchangeAlgorithm = iota << 1 + KeyExchangeAlgorithmEcdhe +) + +// Has check if keyExchangeAlgorithm is supported. +func (a KeyExchangeAlgorithm) Has(v KeyExchangeAlgorithm) bool { + return (a & v) == v +} diff --git a/vendor/github.com/pion/dtls/v2/internal/closer/closer.go b/vendor/github.com/pion/dtls/v2/internal/closer/closer.go index b99e13e44..bfa171cda 100644 --- a/vendor/github.com/pion/dtls/v2/internal/closer/closer.go +++ b/vendor/github.com/pion/dtls/v2/internal/closer/closer.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package closer provides signaling channel for shutdown package closer diff --git a/vendor/github.com/pion/dtls/v2/internal/util/util.go b/vendor/github.com/pion/dtls/v2/internal/util/util.go index 746a670f4..685910fc2 100644 --- a/vendor/github.com/pion/dtls/v2/internal/util/util.go +++ b/vendor/github.com/pion/dtls/v2/internal/util/util.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package util contains small helpers used across the repo package util diff --git a/vendor/github.com/pion/dtls/v2/listener.go b/vendor/github.com/pion/dtls/v2/listener.go index bf80345b1..190d236c7 100644 --- a/vendor/github.com/pion/dtls/v2/listener.go +++ b/vendor/github.com/pion/dtls/v2/listener.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -5,7 +8,7 @@ import ( "github.com/pion/dtls/v2/pkg/protocol" "github.com/pion/dtls/v2/pkg/protocol/recordlayer" - "github.com/pion/udp" + "github.com/pion/transport/v2/udp" ) // Listen creates a DTLS listener diff --git a/vendor/github.com/pion/dtls/v2/packet.go b/vendor/github.com/pion/dtls/v2/packet.go index 8366a3c3d..55d6272ee 100644 --- a/vendor/github.com/pion/dtls/v2/packet.go +++ b/vendor/github.com/pion/dtls/v2/packet.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import "github.com/pion/dtls/v2/pkg/protocol/recordlayer" diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go index 20e3436e2..d6e6fc479 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package ccm implements a CCM, Counter with CBC-MAC // as per RFC 3610. // diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go index 8ff163486..460fb1437 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import ( //nolint:gci @@ -39,11 +42,21 @@ func NewCBC(localKey, localWriteIV, localMac, remoteKey, remoteWriteIV, remoteMa return nil, err } + writeCBC, ok := cipher.NewCBCEncrypter(writeBlock, localWriteIV).(cbcMode) + if !ok { + return nil, errFailedToCast + } + + readCBC, ok := cipher.NewCBCDecrypter(readBlock, remoteWriteIV).(cbcMode) + if !ok { + return nil, errFailedToCast + } + return &CBC{ - writeCBC: cipher.NewCBCEncrypter(writeBlock, localWriteIV).(cbcMode), + writeCBC: writeCBC, writeMac: localMac, - readCBC: cipher.NewCBCDecrypter(readBlock, remoteWriteIV).(cbcMode), + readCBC: readCBC, readMac: remoteMac, h: h, }, nil diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go index 354b1cc50..24050dc92 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import ( @@ -98,7 +101,7 @@ func (c *CCM) Decrypt(in []byte) ([]byte, error) { additionalData := generateAEADAdditionalData(&h, len(out)-int(c.tagLen)) out, err = c.remoteCCM.Open(out[:0], nonce, out, additionalData) if err != nil { - return nil, fmt.Errorf("%w: %v", errDecryptPacket, err) + return nil, fmt.Errorf("%w: %v", errDecryptPacket, err) //nolint:errorlint } return append(in[:recordlayer.HeaderSize], out...), nil } diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go index 72beffd09..9d9fb7418 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package ciphersuite provides the crypto operations needed for a DTLS CipherSuite package ciphersuite @@ -13,6 +16,7 @@ var ( errNotEnoughRoomForNonce = &protocol.InternalError{Err: errors.New("buffer not long enough to contain nonce")} //nolint:goerr113 errDecryptPacket = &protocol.TemporaryError{Err: errors.New("failed to decrypt packet")} //nolint:goerr113 errInvalidMAC = &protocol.TemporaryError{Err: errors.New("invalid mac")} //nolint:goerr113 + errFailedToCast = &protocol.FatalError{Err: errors.New("failed to cast")} //nolint:goerr113 ) func generateAEADAdditionalData(h *recordlayer.Header, payloadLen int) []byte { diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go index af986d46e..c0fd1f76f 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ciphersuite import ( @@ -94,7 +97,7 @@ func (g *GCM) Decrypt(in []byte) ([]byte, error) { additionalData := generateAEADAdditionalData(&h, len(out)-gcmTagLength) out, err = g.remoteGCM.Open(out[:0], nonce, out, additionalData) if err != nil { - return nil, fmt.Errorf("%w: %v", errDecryptPacket, err) + return nil, fmt.Errorf("%w: %v", errDecryptPacket, err) //nolint:errorlint } return append(in[:recordlayer.HeaderSize], out...), nil } diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go index c222c01c7..ddfa39ebe 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go @@ -1,10 +1,13 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package clientcertificate provides all the support Client Certificate types package clientcertificate // Type is used to communicate what // type of certificate is being transported // -//https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-2 +// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-2 type Type byte // ClientCertificateType enums diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go index 5b0e4fa14..126523872 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package elliptic provides elliptic curve cryptography for DTLS package elliptic @@ -5,6 +8,7 @@ import ( "crypto/elliptic" "crypto/rand" "errors" + "fmt" "golang.org/x/crypto/curve25519" ) @@ -57,6 +61,18 @@ const ( X25519 Curve = 0x001d ) +func (c Curve) String() string { + switch c { + case P256: + return "P-256" + case P384: + return "P-384" + case X25519: + return "X25519" + } + return fmt.Sprintf("%#x", uint16(c)) +} + // Curves returns all curves we implement func Curves() map[Curve]bool { return map[Curve]bool{ @@ -68,7 +84,7 @@ func Curves() map[Curve]bool { // GenerateKeypair generates a keypair for the given Curve func GenerateKeypair(c Curve) (*Keypair, error) { - switch c { //nolint:golint + switch c { //nolint:revive case X25519: tmp := make([]byte, 32) if _, err := rand.Read(tmp); err != nil { diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go index 215b44ec7..7c66265c7 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package fingerprint provides a helper to create fingerprint string from certificate package fingerprint diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go index 09107db92..3f988ffb7 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go @@ -1,8 +1,12 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package fingerprint import ( "crypto" "errors" + "strings" ) var errInvalidHashAlgorithm = errors.New("fingerprint: invalid hash algorithm") @@ -20,7 +24,7 @@ func nameToHash() map[string]crypto.Hash { // HashFromString allows looking up a hash algorithm by it's string representation func HashFromString(s string) (crypto.Hash, error) { - if h, ok := nameToHash()[s]; ok { + if h, ok := nameToHash()[strings.ToLower(s)]; ok { return h, nil } return 0, errInvalidHashAlgorithm diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go index 660326f78..9966626e3 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package hash provides TLS HashAlgorithm as defined in TLS 1.2 package hash diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go index d33df19cb..6e7b3ecba 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package prf implements TLS 1.2 Pseudorandom functions package prf @@ -74,6 +77,34 @@ func PSKPreMasterSecret(psk []byte) []byte { return out } +// EcdhePSKPreMasterSecret implements TLS 1.2 Premaster Secret generation given a psk, a keypair and a curve +// +// https://datatracker.ietf.org/doc/html/rfc5489#section-2 +func EcdhePSKPreMasterSecret(psk, publicKey, privateKey []byte, curve elliptic.Curve) ([]byte, error) { + preMasterSecret, err := PreMasterSecret(publicKey, privateKey, curve) + if err != nil { + return nil, err + } + out := make([]byte, 2+len(preMasterSecret)+2+len(psk)) + + // write preMasterSecret length + offset := 0 + binary.BigEndian.PutUint16(out[offset:], uint16(len(preMasterSecret))) + offset += 2 + + // write preMasterSecret + copy(out[offset:], preMasterSecret) + offset += len(preMasterSecret) + + // write psk length + binary.BigEndian.PutUint16(out[offset:], uint16(len(psk))) + offset += 2 + + // write psk + copy(out[offset:], psk) + return out, nil +} + // PreMasterSecret implements TLS 1.2 Premaster Secret generation given a keypair and a curve func PreMasterSecret(publicKey, privateKey []byte, curve elliptic.Curve) ([]byte, error) { switch curve { @@ -107,14 +138,14 @@ func ellipticCurvePreMasterSecret(publicKey, privateKey []byte, c1, c2 ellipticS // specify a PRF and, in general, SHOULD use the TLS PRF with SHA-256 or a // stronger standard hash function. // -// P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + -// HMAC_hash(secret, A(2) + seed) + -// HMAC_hash(secret, A(3) + seed) + ... +// P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + +// HMAC_hash(secret, A(2) + seed) + +// HMAC_hash(secret, A(3) + seed) + ... // // A() is defined as: // -// A(0) = seed -// A(i) = HMAC_hash(secret, A(i-1)) +// A(0) = seed +// A(i) = HMAC_hash(secret, A(i-1)) // // P_hash can be iterated as many times as necessary to produce the // required quantity of data. For example, if P_SHA256 is being used to diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go index d9150eb8c..fec7fba3b 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package signature provides our implemented Signature Algorithms package signature diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go index 9d9d3b309..4aeb3e40a 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package signaturehash import "errors" diff --git a/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go b/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go index f2017bc28..2561accd1 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go +++ b/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package signaturehash provides the SignatureHashAlgorithm as defined in TLS 1.2 package signaturehash @@ -7,10 +10,10 @@ import ( "crypto/ed25519" "crypto/rsa" "crypto/tls" + "fmt" "github.com/pion/dtls/v2/pkg/crypto/hash" "github.com/pion/dtls/v2/pkg/crypto/signature" - "golang.org/x/xerrors" ) // Algorithm is a signature/hash algorithm pairs which may be used in @@ -70,11 +73,11 @@ func ParseSignatureSchemes(sigs []tls.SignatureScheme, insecureHashes bool) ([]A sig := signature.Algorithm(ss & 0xFF) if _, ok := signature.Algorithms()[sig]; !ok { return nil, - xerrors.Errorf("SignatureScheme %04x: %w", ss, errInvalidSignatureAlgorithm) + fmt.Errorf("SignatureScheme %04x: %w", ss, errInvalidSignatureAlgorithm) } h := hash.Algorithm(ss >> 8) if _, ok := hash.Algorithms()[h]; !ok || (ok && h == hash.None) { - return nil, xerrors.Errorf("SignatureScheme %04x: %w", ss, errInvalidHashAlgorithm) + return nil, fmt.Errorf("SignatureScheme %04x: %w", ss, errInvalidHashAlgorithm) } if h.Insecure() && !insecureHashes { continue diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go index 663c6b379..91e9f4d60 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package alert implements TLS alert protocol https://tools.ietf.org/html/rfc5246#section-7.2 package alert diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go index e5fd6f549..f42211511 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package protocol // ApplicationData messages are carried by the record layer and are diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go index b42647a05..87f28bc37 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package protocol // ChangeCipherSpec protocol exists to signal transitions in diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go index 678e816cb..3478ee38c 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package protocol // CompressionMethodID is the ID for a CompressionMethod diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go index 47e5c96bb..92c9db2bf 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package protocol // ContentType represents the IANA Registered ContentTypes diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go index e52014a1e..d87aff7fb 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package protocol import ( @@ -84,7 +87,8 @@ func (e *TimeoutError) Error() string { return fmt.Sprintf("dtls timeout: %v", e // Timeout implements net.Error.Timeout() func (e *HandshakeError) Timeout() bool { - if netErr, ok := e.Err.(net.Error); ok { + var netErr net.Error + if errors.As(e.Err, &netErr) { return netErr.Timeout() } return false @@ -92,8 +96,9 @@ func (e *HandshakeError) Timeout() bool { // Temporary implements net.Error.Temporary() func (e *HandshakeError) Temporary() bool { - if netErr, ok := e.Err.(net.Error); ok { - return netErr.Temporary() + var netErr net.Error + if errors.As(e.Err, &netErr) { + return netErr.Temporary() //nolint } return false } diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/alpn.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/alpn.go index 8d7e1123e..e780dc9e1 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/alpn.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/alpn.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package extension import ( diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go index 82d8b3408..c5e954ce5 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package extension import ( diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go index ec4c1ff5c..5173a5863 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package extension implements the extension values in the ClientHello/ServerHello package extension diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go index 8378c3d94..c5092a7db 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package extension import "encoding/binary" diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go index 9a1cc2926..183e08e6e 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package extension import ( diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go index 2c4d1d4a6..2966966dd 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package extension // SRTPProtectionProfile defines the parameters and options that are in effect for the SRTP processing diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go index 8f077fcc7..dd9b54f0d 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package extension import ( diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go index 873d07827..9c2543e6e 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package extension import ( diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go index ee284f6e1..2ff4b90b6 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package extension import ( diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go index 04ddc956a..d0b70cafb 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package extension import "encoding/binary" diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go index 729fa3a98..ea9f10872 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package extension import "encoding/binary" diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go index e8fbdeae7..b29629717 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package handshake import "encoding/binary" diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go index ac77c0434..1354300c4 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package handshake import ( diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go index f25458178..b1f682bf5 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go @@ -1,7 +1,11 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package handshake provides the DTLS wire protocol for handshakes package handshake import ( + "github.com/pion/dtls/v2/internal/ciphersuite/types" "github.com/pion/dtls/v2/internal/util" "github.com/pion/dtls/v2/pkg/protocol" ) @@ -70,6 +74,8 @@ type Message interface { type Handshake struct { Header Header Message Message + + KeyExchangeAlgorithm types.KeyExchangeAlgorithm } // ContentType returns what kind of content this message is carying @@ -126,13 +132,13 @@ func (h *Handshake) Unmarshal(data []byte) error { case TypeCertificate: h.Message = &MessageCertificate{} case TypeServerKeyExchange: - h.Message = &MessageServerKeyExchange{} + h.Message = &MessageServerKeyExchange{KeyExchangeAlgorithm: h.KeyExchangeAlgorithm} case TypeCertificateRequest: h.Message = &MessageCertificateRequest{} case TypeServerHelloDone: h.Message = &MessageServerHelloDone{} case TypeClientKeyExchange: - h.Message = &MessageClientKeyExchange{} + h.Message = &MessageClientKeyExchange{KeyExchangeAlgorithm: h.KeyExchangeAlgorithm} case TypeFinished: h.Message = &MessageFinished{} case TypeCertificateVerify: diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go index cb6a22489..4f9a96287 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package handshake import ( diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go index 05fb74656..d5c861d90 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package handshake import ( diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go index e711f392b..11a44d440 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package handshake import ( @@ -19,8 +22,9 @@ server's Certificate message). https://tools.ietf.org/html/rfc5246#section-7.4.4 */ type MessageCertificateRequest struct { - CertificateTypes []clientcertificate.Type - SignatureHashAlgorithms []signaturehash.Algorithm + CertificateTypes []clientcertificate.Type + SignatureHashAlgorithms []signaturehash.Algorithm + CertificateAuthoritiesNames [][]byte } const ( @@ -46,7 +50,20 @@ func (m *MessageCertificateRequest) Marshal() ([]byte, error) { out = append(out, byte(v.Signature)) } - out = append(out, []byte{0x00, 0x00}...) // Distinguished Names Length + // Distinguished Names + casLength := 0 + for _, ca := range m.CertificateAuthoritiesNames { + casLength += len(ca) + 2 + } + out = append(out, []byte{0x00, 0x00}...) + binary.BigEndian.PutUint16(out[len(out)-2:], uint16(casLength)) + if casLength > 0 { + for _, ca := range m.CertificateAuthoritiesNames { + out = append(out, []byte{0x00, 0x00}...) + binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(ca))) + out = append(out, ca...) + } + } return out, nil } @@ -96,5 +113,32 @@ func (m *MessageCertificateRequest) Unmarshal(data []byte) error { m.SignatureHashAlgorithms = append(m.SignatureHashAlgorithms, signaturehash.Algorithm{Signature: s, Hash: h}) } + offset += signatureHashAlgorithmsLength + if len(data) < offset+2 { + return errBufferTooSmall + } + casLength := int(binary.BigEndian.Uint16(data[offset:])) + offset += 2 + if (offset + casLength) > len(data) { + return errBufferTooSmall + } + cas := make([]byte, casLength) + copy(cas, data[offset:offset+casLength]) + m.CertificateAuthoritiesNames = nil + for len(cas) > 0 { + if len(cas) < 2 { + return errBufferTooSmall + } + caLen := binary.BigEndian.Uint16(cas) + cas = cas[2:] + + if len(cas) < int(caLen) { + return errBufferTooSmall + } + + m.CertificateAuthoritiesNames = append(m.CertificateAuthoritiesNames, cas[:caLen]) + cas = cas[caLen:] + } + return nil } diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go index fb5e4639d..9e02a9c11 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package handshake import ( diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go index 1deca38aa..bea6dd969 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package handshake import ( diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go index f8fc36985..2abcd5bf7 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go @@ -1,7 +1,12 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package handshake import ( "encoding/binary" + + "github.com/pion/dtls/v2/internal/ciphersuite/types" ) // MessageClientKeyExchange is a DTLS Handshake Message @@ -14,6 +19,9 @@ import ( type MessageClientKeyExchange struct { IdentityHint []byte PublicKey []byte + + // for unmarshaling + KeyExchangeAlgorithm types.KeyExchangeAlgorithm } // Type returns the Handshake Type @@ -22,35 +30,52 @@ func (m MessageClientKeyExchange) Type() Type { } // Marshal encodes the Handshake -func (m *MessageClientKeyExchange) Marshal() ([]byte, error) { - switch { - case (m.IdentityHint != nil && m.PublicKey != nil) || (m.IdentityHint == nil && m.PublicKey == nil): +func (m *MessageClientKeyExchange) Marshal() (out []byte, err error) { + if m.IdentityHint == nil && m.PublicKey == nil { return nil, errInvalidClientKeyExchange - case m.PublicKey != nil: - return append([]byte{byte(len(m.PublicKey))}, m.PublicKey...), nil - default: - out := append([]byte{0x00, 0x00}, m.IdentityHint...) - binary.BigEndian.PutUint16(out, uint16(len(out)-2)) - return out, nil } + + if m.IdentityHint != nil { + out = append([]byte{0x00, 0x00}, m.IdentityHint...) + binary.BigEndian.PutUint16(out, uint16(len(out)-2)) + } + + if m.PublicKey != nil { + out = append(out, byte(len(m.PublicKey))) + out = append(out, m.PublicKey...) + } + + return out, nil } // Unmarshal populates the message from encoded data func (m *MessageClientKeyExchange) Unmarshal(data []byte) error { - if len(data) < 2 { + switch { + case len(data) < 2: return errBufferTooSmall + case m.KeyExchangeAlgorithm == types.KeyExchangeAlgorithmNone: + return errCipherSuiteUnset } - // If parsed as PSK return early and only populate PSK Identity Hint - if pskLength := binary.BigEndian.Uint16(data); len(data) == int(pskLength+2) { - m.IdentityHint = append([]byte{}, data[2:]...) - return nil + offset := 0 + if m.KeyExchangeAlgorithm.Has(types.KeyExchangeAlgorithmPsk) { + pskLength := int(binary.BigEndian.Uint16(data)) + if pskLength > len(data)-2 { + return errBufferTooSmall + } + + m.IdentityHint = append([]byte{}, data[2:pskLength+2]...) + offset += pskLength + 2 } - if publicKeyLength := int(data[0]); len(data) != publicKeyLength+1 { - return errBufferTooSmall + if m.KeyExchangeAlgorithm.Has(types.KeyExchangeAlgorithmEcdhe) { + publicKeyLength := int(data[offset]) + if publicKeyLength > len(data)-1-offset { + return errBufferTooSmall + } + + m.PublicKey = append([]byte{}, data[offset+1:]...) } - m.PublicKey = append([]byte{}, data[1:]...) return nil } diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go index c65d42abb..255aedd7e 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package handshake // MessageFinished is a DTLS Handshake Message diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go index ef834dc85..398e59cc3 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package handshake import ( @@ -6,19 +9,19 @@ import ( // MessageHelloVerifyRequest is as follows: // -// struct { -// ProtocolVersion server_version; -// opaque cookie<0..2^8-1>; -// } HelloVerifyRequest; +// struct { +// ProtocolVersion server_version; +// opaque cookie<0..2^8-1>; +// } HelloVerifyRequest; // -// The HelloVerifyRequest message type is hello_verify_request(3). +// The HelloVerifyRequest message type is hello_verify_request(3). // -// When the client sends its ClientHello message to the server, the server -// MAY respond with a HelloVerifyRequest message. This message contains -// a stateless cookie generated using the technique of [PHOTURIS]. The -// client MUST retransmit the ClientHello with the cookie added. +// When the client sends its ClientHello message to the server, the server +// MAY respond with a HelloVerifyRequest message. This message contains +// a stateless cookie generated using the technique of [PHOTURIS]. The +// client MUST retransmit the ClientHello with the cookie added. // -// https://tools.ietf.org/html/rfc6347#section-4.2.1 +// https://tools.ietf.org/html/rfc6347#section-4.2.1 type MessageHelloVerifyRequest struct { Version protocol.Version Cookie []byte @@ -51,8 +54,8 @@ func (m *MessageHelloVerifyRequest) Unmarshal(data []byte) error { } m.Version.Major = data[0] m.Version.Minor = data[1] - cookieLength := data[2] - if len(data) < (int(cookieLength) + 3) { + cookieLength := int(data[2]) + if len(data) < cookieLength+3 { return errBufferTooSmall } m.Cookie = make([]byte, cookieLength) diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go index 9c1cc2218..caf186da8 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package handshake import ( @@ -88,11 +91,14 @@ func (m *MessageServerHello) Unmarshal(data []byte) error { m.SessionID = append([]byte{}, data[currOffset:currOffset+n]...) currOffset += len(m.SessionID) + if len(data) < currOffset+2 { + return errBufferTooSmall + } m.CipherSuiteID = new(uint16) *m.CipherSuiteID = binary.BigEndian.Uint16(data[currOffset:]) currOffset += 2 - if len(data) < currOffset { + if len(data) <= currOffset { return errBufferTooSmall } if compressionMethod, ok := protocol.CompressionMethods()[protocol.CompressionMethodID(data[currOffset])]; ok { diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go index 0f65b198e..b187dd417 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package handshake // MessageServerHelloDone is final non-encrypted message from server @@ -16,6 +19,6 @@ func (m *MessageServerHelloDone) Marshal() ([]byte, error) { } // Unmarshal populates the message from encoded data -func (m *MessageServerHelloDone) Unmarshal(data []byte) error { +func (m *MessageServerHelloDone) Unmarshal([]byte) error { return nil } diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go index 4148fe05b..82abbe0d4 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go @@ -1,8 +1,12 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package handshake import ( "encoding/binary" + "github.com/pion/dtls/v2/internal/ciphersuite/types" "github.com/pion/dtls/v2/pkg/crypto/elliptic" "github.com/pion/dtls/v2/pkg/crypto/hash" "github.com/pion/dtls/v2/pkg/crypto/signature" @@ -18,6 +22,9 @@ type MessageServerKeyExchange struct { HashAlgorithm hash.Algorithm SignatureAlgorithm signature.Algorithm Signature []byte + + // for unmarshaling + KeyExchangeAlgorithm types.KeyExchangeAlgorithm } // Type returns the Handshake Type @@ -27,19 +34,28 @@ func (m MessageServerKeyExchange) Type() Type { // Marshal encodes the Handshake func (m *MessageServerKeyExchange) Marshal() ([]byte, error) { + var out []byte if m.IdentityHint != nil { - out := append([]byte{0x00, 0x00}, m.IdentityHint...) + out = append([]byte{0x00, 0x00}, m.IdentityHint...) binary.BigEndian.PutUint16(out, uint16(len(out)-2)) - return out, nil } - out := []byte{byte(m.EllipticCurveType), 0x00, 0x00} - binary.BigEndian.PutUint16(out[1:], uint16(m.NamedCurve)) + if m.EllipticCurveType == 0 || len(m.PublicKey) == 0 { + return out, nil + } + out = append(out, byte(m.EllipticCurveType), 0x00, 0x00) + binary.BigEndian.PutUint16(out[len(out)-2:], uint16(m.NamedCurve)) out = append(out, byte(len(m.PublicKey))) out = append(out, m.PublicKey...) - - if m.HashAlgorithm == hash.None && m.SignatureAlgorithm == signature.Anonymous && len(m.Signature) == 0 { + switch { + case m.HashAlgorithm != hash.None && len(m.Signature) == 0: + return nil, errInvalidHashAlgorithm + case m.HashAlgorithm == hash.None && len(m.Signature) > 0: + return nil, errInvalidHashAlgorithm + case m.SignatureAlgorithm == signature.Anonymous && (m.HashAlgorithm != hash.None || len(m.Signature) > 0): + return nil, errInvalidSignatureAlgorithm + case m.SignatureAlgorithm == signature.Anonymous: return out, nil } @@ -52,14 +68,27 @@ func (m *MessageServerKeyExchange) Marshal() ([]byte, error) { // Unmarshal populates the message from encoded data func (m *MessageServerKeyExchange) Unmarshal(data []byte) error { - if len(data) < 2 { + switch { + case len(data) < 2: return errBufferTooSmall + case m.KeyExchangeAlgorithm == types.KeyExchangeAlgorithmNone: + return errCipherSuiteUnset } - // If parsed as PSK return early and only populate PSK Identity Hint - if pskLength := binary.BigEndian.Uint16(data); len(data) == int(pskLength+2) { - m.IdentityHint = append([]byte{}, data[2:]...) - return nil + hintLength := binary.BigEndian.Uint16(data) + if int(hintLength) <= len(data)-2 && m.KeyExchangeAlgorithm.Has(types.KeyExchangeAlgorithmPsk) { + m.IdentityHint = append([]byte{}, data[2:2+hintLength]...) + data = data[2+hintLength:] + } + if m.KeyExchangeAlgorithm == types.KeyExchangeAlgorithmPsk { + if len(data) == 0 { + return nil + } + return errLengthMismatch + } + + if !m.KeyExchangeAlgorithm.Has(types.KeyExchangeAlgorithmEcdhe) { + return errLengthMismatch } if _, ok := elliptic.CurveTypes()[elliptic.CurveType(data[0])]; ok { diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go index 0ade936eb..56f37569b 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package handshake import ( diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go index 7033d4058..cd4cb60a5 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package recordlayer implements the TLS Record Layer https://tools.ietf.org/html/rfc5246#section-6 package recordlayer diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go index 65047d767..92252502b 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package recordlayer import ( diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go index 67e5a727b..02325fd2d 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package recordlayer import ( diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/version.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/version.go index d5ddb1d00..c4d94ac3a 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/version.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/version.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package protocol provides the DTLS wire format package protocol diff --git a/vendor/github.com/pion/dtls/v2/renovate.json b/vendor/github.com/pion/dtls/v2/renovate.json index f1614058a..f1bb98c6a 100644 --- a/vendor/github.com/pion/dtls/v2/renovate.json +++ b/vendor/github.com/pion/dtls/v2/renovate.json @@ -1,27 +1,6 @@ { + "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:base", - ":disableDependencyDashboard" - ], - "postUpdateOptions": [ - "gomodTidy" - ], - "commitBody": "Generated by renovateBot", - "packageRules": [ - { - "matchUpdateTypes": ["minor", "patch", "pin", "digest"], - "automerge": true - }, - { - "packagePatterns": ["^golang.org/x/"], - "schedule": ["on the first day of the month"] - } - ], - "ignorePaths": [ - ".github/workflows/generate-authors.yml", - ".github/workflows/lint.yaml", - ".github/workflows/renovate-go-mod-fix.yaml", - ".github/workflows/test.yaml", - ".github/workflows/tidy-check.yaml" + "github>pion/renovate-config" ] } diff --git a/vendor/github.com/pion/dtls/v2/resume.go b/vendor/github.com/pion/dtls/v2/resume.go index 40e55e449..c470d856b 100644 --- a/vendor/github.com/pion/dtls/v2/resume.go +++ b/vendor/github.com/pion/dtls/v2/resume.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( diff --git a/vendor/github.com/pion/dtls/v2/session.go b/vendor/github.com/pion/dtls/v2/session.go index f52120cd8..99bf5a499 100644 --- a/vendor/github.com/pion/dtls/v2/session.go +++ b/vendor/github.com/pion/dtls/v2/session.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls // Session store data needed in resumption diff --git a/vendor/github.com/pion/dtls/v2/srtp_protection_profile.go b/vendor/github.com/pion/dtls/v2/srtp_protection_profile.go index 1c3ae55dc..e306e9e6a 100644 --- a/vendor/github.com/pion/dtls/v2/srtp_protection_profile.go +++ b/vendor/github.com/pion/dtls/v2/srtp_protection_profile.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import "github.com/pion/dtls/v2/pkg/protocol/extension" @@ -7,8 +10,8 @@ import "github.com/pion/dtls/v2/pkg/protocol/extension" type SRTPProtectionProfile = extension.SRTPProtectionProfile const ( - SRTP_AES128_CM_HMAC_SHA1_80 SRTPProtectionProfile = extension.SRTP_AES128_CM_HMAC_SHA1_80 // nolint - SRTP_AES128_CM_HMAC_SHA1_32 SRTPProtectionProfile = extension.SRTP_AES128_CM_HMAC_SHA1_32 // nolint - SRTP_AEAD_AES_128_GCM SRTPProtectionProfile = extension.SRTP_AEAD_AES_128_GCM // nolint - SRTP_AEAD_AES_256_GCM SRTPProtectionProfile = extension.SRTP_AEAD_AES_256_GCM // nolint + SRTP_AES128_CM_HMAC_SHA1_80 SRTPProtectionProfile = extension.SRTP_AES128_CM_HMAC_SHA1_80 // nolint:revive,stylecheck + SRTP_AES128_CM_HMAC_SHA1_32 SRTPProtectionProfile = extension.SRTP_AES128_CM_HMAC_SHA1_32 // nolint:revive,stylecheck + SRTP_AEAD_AES_128_GCM SRTPProtectionProfile = extension.SRTP_AEAD_AES_128_GCM // nolint:revive,stylecheck + SRTP_AEAD_AES_256_GCM SRTPProtectionProfile = extension.SRTP_AEAD_AES_256_GCM // nolint:revive,stylecheck ) diff --git a/vendor/github.com/pion/dtls/v2/state.go b/vendor/github.com/pion/dtls/v2/state.go index 96fc71b12..e9f86a80b 100644 --- a/vendor/github.com/pion/dtls/v2/state.go +++ b/vendor/github.com/pion/dtls/v2/state.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls import ( @@ -8,7 +11,7 @@ import ( "github.com/pion/dtls/v2/pkg/crypto/elliptic" "github.com/pion/dtls/v2/pkg/crypto/prf" "github.com/pion/dtls/v2/pkg/protocol/handshake" - "github.com/pion/transport/replaydetector" + "github.com/pion/transport/v2/replaydetector" ) // State holds the dtls connection state and implements both encoding.BinaryMarshaler and encoding.BinaryUnmarshaler @@ -75,10 +78,10 @@ func (s *State) serialize() *serializedState { localRnd := s.localRandom.MarshalFixed() remoteRnd := s.remoteRandom.MarshalFixed() - epoch := s.localEpoch.Load().(uint16) + epoch := s.getLocalEpoch() return &serializedState{ - LocalEpoch: epoch, - RemoteEpoch: s.remoteEpoch.Load().(uint16), + LocalEpoch: s.getLocalEpoch(), + RemoteEpoch: s.getRemoteEpoch(), CipherSuiteID: uint16(s.cipherSuite.ID()), MasterSecret: s.masterSecret, SequenceNumber: atomic.LoadUint64(&s.localSequenceNumber[epoch]), @@ -169,10 +172,8 @@ func (s *State) UnmarshalBinary(data []byte) error { } s.deserialize(serialized) - if err := s.initCipherSuite(); err != nil { - return err - } - return nil + + return s.initCipherSuite() } // ExportKeyingMaterial returns length bytes of exported key material in a new @@ -180,7 +181,7 @@ func (s *State) UnmarshalBinary(data []byte) error { // This allows protocols to use DTLS for key establishment, but // then use some of the keying material for their own purposes func (s *State) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) { - if s.localEpoch.Load().(uint16) == 0 { + if s.getLocalEpoch() == 0 { return nil, errHandshakeInProgress } else if len(context) != 0 { return nil, errContextUnsupported @@ -199,3 +200,17 @@ func (s *State) ExportKeyingMaterial(label string, context []byte, length int) ( } return prf.PHash(s.masterSecret, seed, length, s.cipherSuite.HashFunc()) } + +func (s *State) getRemoteEpoch() uint16 { + if remoteEpoch, ok := s.remoteEpoch.Load().(uint16); ok { + return remoteEpoch + } + return 0 +} + +func (s *State) getLocalEpoch() uint16 { + if localEpoch, ok := s.localEpoch.Load().(uint16); ok { + return localEpoch + } + return 0 +} diff --git a/vendor/github.com/pion/dtls/v2/util.go b/vendor/github.com/pion/dtls/v2/util.go index 745182dcd..663c4437c 100644 --- a/vendor/github.com/pion/dtls/v2/util.go +++ b/vendor/github.com/pion/dtls/v2/util.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package dtls func findMatchingSRTPProfile(a, b []SRTPProtectionProfile) (SRTPProtectionProfile, bool) { @@ -11,7 +14,7 @@ func findMatchingSRTPProfile(a, b []SRTPProtectionProfile) (SRTPProtectionProfil return 0, false } -func findMatchingCipherSuite(a, b []CipherSuite) (CipherSuite, bool) { //nolint +func findMatchingCipherSuite(a, b []CipherSuite) (CipherSuite, bool) { for _, aSuite := range a { for _, bSuite := range b { if aSuite.ID() == bSuite.ID() { diff --git a/vendor/github.com/pion/ice/v2/.gitignore b/vendor/github.com/pion/ice/v2/.gitignore index 83db74ba5..6e2f206a9 100644 --- a/vendor/github.com/pion/ice/v2/.gitignore +++ b/vendor/github.com/pion/ice/v2/.gitignore @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + ### JetBrains IDE ### ##################### .idea/ @@ -22,3 +25,4 @@ cover.out *.wasm examples/sfu-ws/cert.pem examples/sfu-ws/key.pem +wasm_exec.js diff --git a/vendor/github.com/pion/ice/v2/.golangci.yml b/vendor/github.com/pion/ice/v2/.golangci.yml index d6162c970..4e3eddf42 100644 --- a/vendor/github.com/pion/ice/v2/.golangci.yml +++ b/vendor/github.com/pion/ice/v2/.golangci.yml @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + linters-settings: govet: check-shadowing: true @@ -10,19 +13,34 @@ linters-settings: modules: - github.com/pkg/errors: recommendations: - - errors + - errors + forbidigo: + forbid: + - ^fmt.Print(f|ln)?$ + - ^log.(Panic|Fatal|Print)(f|ln)?$ + - ^os.Exit$ + - ^panic$ + - ^print(ln)?$ linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully - - deadcode # Finds unused code + - contextcheck # check the function whether use a non-inherited context + - decorder # check declaration order and count of types, constants, variables and functions - depguard # Go linter that checks if package imports are in a list of acceptable packages - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection + - durationcheck # check for two durations multiplied together - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. - exhaustive # check exhaustiveness of enum switch statements - exportloopref # checks for pointers to enclosing loop variables + - forbidigo # Forbids identifiers + - forcetypeassert # finds forced type assertions - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - gochecknoinits # Checks that no init functions are present in Go code @@ -35,40 +53,59 @@ linters: - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - gosimple # Linter for Go source code that specializes in simplifying a code - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - grouper # An analyzer to analyze expression groups. + - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used - misspell # Finds commonly misspelled English words in comments - nakedret # Finds naked returns in functions greater than a specified function length + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. - noctx # noctx finds sending http request without context.Context - - scopelint # Scopelint checks for unpinned variables in go programs + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # golint replacement, finds style mistakes - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - - structcheck # Finds unused struct fields - stylecheck # Stylecheck is a replacement for golint + - tagliatelle # Checks the struct tags. + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types - - varcheck # Finds unused global variables and constants + - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: + - containedctx # containedctx is a linter that detects struct contained context.Context field + - cyclop # checks function and package cyclomatic complexity + - exhaustivestruct # Checks if all struct's fields are initialized - funlen # Tool for detection of long functions - gocyclo # Computes and checks the cyclomatic complexity of functions - godot # Check if comments end in a period - gomnd # An analyzer to detect magic numbers. + - ifshort # Checks that your code uses short syntax for if-statements whenever possible + - ireturn # Accept Interfaces, Return Concrete Types - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - nestif # Reports deeply nested if statements - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - nolintlint # Reports ill-formed or insufficient nolint directives + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - prealloc # Finds slice declarations that could potentially be preallocated + - promlinter # Check Prometheus metrics naming via promlint - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. - testpackage # linter that makes you use a separate _test package + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers + - varnamelen # checks that the length of a variable's name matches its scope + - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! issues: @@ -78,12 +115,23 @@ issues: - path: _test\.go linters: - gocognit + - forbidigo # Allow complex main function in examples - path: examples text: "of func `main` is high" linters: - gocognit + + # Allow forbidden identifiers in examples + - path: examples + linters: + - forbidigo + + # Allow forbidden identifiers in CLI commands + - path: cmd + linters: + - forbidigo run: skip-dirs-use-default: false diff --git a/vendor/github.com/pion/ice/v2/.goreleaser.yml b/vendor/github.com/pion/ice/v2/.goreleaser.yml new file mode 100644 index 000000000..30093e9d6 --- /dev/null +++ b/vendor/github.com/pion/ice/v2/.goreleaser.yml @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + +builds: +- skip: true diff --git a/vendor/github.com/pion/ice/v2/AUTHORS.txt b/vendor/github.com/pion/ice/v2/AUTHORS.txt index 8c8b66b05..57eb69378 100644 --- a/vendor/github.com/pion/ice/v2/AUTHORS.txt +++ b/vendor/github.com/pion/ice/v2/AUTHORS.txt @@ -2,20 +2,26 @@ # we would love to have you https://github.com/pion/webrtc/wiki/Contributing # # This file is auto generated, using git to list all individuals contributors. -# see `.github/generate-authors.sh` for the scripting +# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting Aaron France Adam Kiss adwpc Aleksandr Razumov +aler9 <46489434+aler9@users.noreply.github.com> Antoine Baché +Artur Shellunts Assad Obaid Atsushi Watanabe backkem buptczq cgojin Chao Yuan +cnderrauber David Hamilton David Zhao +David Zhao +Eric Daniels +Genteure Henry hexiang hn8 <10730886+hn8@users.noreply.github.com> @@ -25,6 +31,8 @@ Jason Maldonis Jerko Steiner JooYoung Juliusz Chroboczek +Kacper Bąk <56700396+53jk1@users.noreply.github.com> +Kevin Caffrey Konstantin Itskov korymiller1489 Kyle Carberry @@ -33,17 +41,25 @@ Luke Curley Meelap Shah Michael MacDonald Michael MacDonald +Mikhail Bragin +Miroslav Šedivý Nevio Vesic Ori Bernstein +Rasmus Hanning Robert Eperjesi Sam Lancia Sam Lancia +San9H0 Sean DuBois Sean DuBois Sebastian Waisbrot Sidney San Martín +Steffen Vogel Will Forcey Woodrow Douglass Yutaka Takeda ZHENK Zizheng Tai + +# List of contributors not appearing in Git history + diff --git a/vendor/github.com/pion/ice/v2/LICENSE b/vendor/github.com/pion/ice/v2/LICENSE index ab602974d..491caf6b0 100644 --- a/vendor/github.com/pion/ice/v2/LICENSE +++ b/vendor/github.com/pion/ice/v2/LICENSE @@ -1,21 +1,9 @@ MIT License -Copyright (c) 2018 +Copyright (c) 2023 The Pion community -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: +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 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. +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/pion/ice/v2/README.md b/vendor/github.com/pion/ice/v2/README.md index 8191fc479..111ca7057 100644 --- a/vendor/github.com/pion/ice/v2/README.md +++ b/vendor/github.com/pion/ice/v2/README.md @@ -5,11 +5,11 @@

A Go implementation of ICE

- Pion transport + Pion ICE Slack Widget
- Build Status - GoDoc + GitHub Workflow Status + Go Reference Coverage Status Go Report Card License: MIT @@ -20,14 +20,15 @@ The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones. ### Community -Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion). +Pion has an active community on the [Slack](https://pion.ly/slack). + +Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news. We are always looking to support **your projects**. Please reach out if you have something to build! - If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly) ### Contributing -Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible: +Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible: [AUTHORS.txt](./AUTHORS.txt) ### License MIT License - see [LICENSE](LICENSE) for full text diff --git a/vendor/github.com/pion/ice/v2/addr.go b/vendor/github.com/pion/ice/v2/addr.go new file mode 100644 index 000000000..1d70025be --- /dev/null +++ b/vendor/github.com/pion/ice/v2/addr.go @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package ice + +import ( + "net" +) + +func parseMulticastAnswerAddr(in net.Addr) (net.IP, bool) { + switch addr := in.(type) { + case *net.IPAddr: + return addr.IP, true + case *net.UDPAddr: + return addr.IP, true + case *net.TCPAddr: + return addr.IP, true + } + return nil, false +} + +func parseAddr(in net.Addr) (net.IP, int, NetworkType, bool) { + switch addr := in.(type) { + case *net.UDPAddr: + return addr.IP, addr.Port, NetworkTypeUDP4, true + case *net.TCPAddr: + return addr.IP, addr.Port, NetworkTypeTCP4, true + } + return nil, 0, 0, false +} + +func createAddr(network NetworkType, ip net.IP, port int) net.Addr { + switch { + case network.IsTCP(): + return &net.TCPAddr{IP: ip, Port: port} + default: + return &net.UDPAddr{IP: ip, Port: port} + } +} + +func addrEqual(a, b net.Addr) bool { + aIP, aPort, aType, aOk := parseAddr(a) + if !aOk { + return false + } + + bIP, bPort, bType, bOk := parseAddr(b) + if !bOk { + return false + } + + return aType == bType && aIP.Equal(bIP) && aPort == bPort +} + +// AddrPort is an IP and a port number. +type AddrPort [18]byte + +func toAddrPort(addr net.Addr) AddrPort { + var ap AddrPort + switch addr := addr.(type) { + case *net.UDPAddr: + copy(ap[:16], addr.IP.To16()) + ap[16] = uint8(addr.Port >> 8) + ap[17] = uint8(addr.Port) + case *net.TCPAddr: + copy(ap[:16], addr.IP.To16()) + ap[16] = uint8(addr.Port >> 8) + ap[17] = uint8(addr.Port) + } + return ap +} diff --git a/vendor/github.com/pion/ice/v2/agent.go b/vendor/github.com/pion/ice/v2/agent.go index b4aba19f5..5350330f6 100644 --- a/vendor/github.com/pion/ice/v2/agent.go +++ b/vendor/github.com/pion/ice/v2/agent.go @@ -1,20 +1,28 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package ice implements the Interactive Connectivity Establishment (ICE) // protocol defined in rfc5245. package ice import ( "context" + "fmt" "net" "strings" "sync" "sync/atomic" "time" + atomicx "github.com/pion/ice/v2/internal/atomic" + stunx "github.com/pion/ice/v2/internal/stun" "github.com/pion/logging" "github.com/pion/mdns" "github.com/pion/stun" - "github.com/pion/transport/packetio" - "github.com/pion/transport/vnet" + "github.com/pion/transport/v2" + "github.com/pion/transport/v2/packetio" + "github.com/pion/transport/v2/stdnet" + "github.com/pion/transport/v2/vnet" "golang.org/x/net/proxy" ) @@ -39,7 +47,7 @@ type Agent struct { onConnected chan struct{} onConnectedOnce sync.Once - // force candidate to be contacted immediately (instead of waiting for task ticker) + // Force candidate to be contacted immediately (instead of waiting for task ticker) forceCandidateContact chan bool tieBreaker uint64 @@ -64,8 +72,8 @@ type Agent struct { prflxAcceptanceMinWait time.Duration relayAcceptanceMinWait time.Duration - portmin uint16 - portmax uint16 + portMin uint16 + portMax uint16 candidateTypes []CandidateType @@ -97,10 +105,10 @@ type Agent struct { selectedPair atomic.Value // *CandidatePair - urls []*URL + urls []*stun.URI networkTypes []NetworkType - buffer *packetio.Buffer + buf *packetio.Buffer // LRU of outbound Binding request Transaction IDs pendingBindingRequests []bindingRequest @@ -111,9 +119,10 @@ type Agent struct { // State for closing done chan struct{} taskLoopDone chan struct{} - err atomicError + err atomicx.Error gatherCandidateCancel func() + gatherCandidateDone chan struct{} chanCandidate chan Candidate chanCandidatePair chan *CandidatePair @@ -122,11 +131,14 @@ type Agent struct { loggerFactory logging.LoggerFactory log logging.LeveledLogger - net *vnet.Net - tcpMux TCPMux - udpMux UDPMux + net transport.Net + tcpMux TCPMux + udpMux UDPMux + udpMuxSrflx UniversalUDPMux interfaceFilter func(string) bool + ipFilter func(net.IP) bool + includeLoopback bool insecureSkipVerify bool @@ -202,8 +214,8 @@ func (a *Agent) taskLoop() { a.deleteAllCandidates() a.startedFn() - if err := a.buffer.Close(); err != nil { - a.log.Warnf("failed to close buffer: %v", err) + if err := a.buf.Close(); err != nil { + a.log.Warnf("Failed to close buffer: %v", err) } a.closeMulticastConn() @@ -258,21 +270,6 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit } log := loggerFactory.NewLogger("ice") - var mDNSConn *mdns.Conn - mDNSConn, mDNSMode, err = createMulticastDNS(mDNSMode, mDNSName, log) - // Opportunistic mDNS: If we can't open the connection, that's ok: we - // can continue without it. - if err != nil { - log.Warnf("Failed to initialize mDNS %s: %v", mDNSName, err) - } - closeMDNSConn := func() { - if mDNSConn != nil { - if mdnsCloseErr := mDNSConn.Close(); mdnsCloseErr != nil { - log.Warnf("Failed to close mDNS: %v", mdnsCloseErr) - } - } - } - startedCtx, startedFn := context.WithCancel(context.Background()) a := &Agent{ @@ -289,21 +286,23 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit urls: config.Urls, networkTypes: config.NetworkTypes, onConnected: make(chan struct{}), - buffer: packetio.NewBuffer(), + buf: packetio.NewBuffer(), done: make(chan struct{}), taskLoopDone: make(chan struct{}), startedCh: startedCtx.Done(), startedFn: startedFn, - portmin: config.PortMin, - portmax: config.PortMax, + portMin: config.PortMin, + portMax: config.PortMax, loggerFactory: loggerFactory, log: log, net: config.Net, proxyDialer: config.ProxyDialer, + tcpMux: config.TCPMux, + udpMux: config.UDPMux, + udpMuxSrflx: config.UDPMuxSrflx, mDNSMode: mDNSMode, mDNSName: mDNSName, - mDNSConn: mDNSConn, gatherCandidateCancel: func() {}, @@ -311,22 +310,29 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit interfaceFilter: config.InterfaceFilter, - insecureSkipVerify: config.InsecureSkipVerify, - } + ipFilter: config.IPFilter, - a.tcpMux = config.TCPMux - if a.tcpMux == nil { - a.tcpMux = newInvalidTCPMux() + insecureSkipVerify: config.InsecureSkipVerify, + + includeLoopback: config.IncludeLoopback, } - a.udpMux = config.UDPMux if a.net == nil { - a.net = vnet.NewNet(nil) - } else if a.net.IsVirtual() { - a.log.Warn("vnet is enabled") - if a.mDNSMode != MulticastDNSModeDisabled { - a.log.Warn("vnet does not support mDNS yet") + a.net, err = stdnet.NewNet() + if err != nil { + return nil, fmt.Errorf("failed to create network: %w", err) } + } else if _, isVirtual := a.net.(*vnet.Net); isVirtual { + a.log.Warn("Virtual network is enabled") + if a.mDNSMode != MulticastDNSModeDisabled { + a.log.Warn("Virtual network does not support mDNS yet") + } + } + + // Opportunistic mDNS: If we can't open the connection, that's ok: we + // can continue without it. + if a.mDNSConn, a.mDNSMode, err = createMulticastDNS(a.net, mDNSMode, mDNSName, log); err != nil { + log.Warnf("Failed to initialize mDNS %s: %v", mDNSName, err) } config.initWithDefaults(a) @@ -334,29 +340,35 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit // Make sure the buffer doesn't grow indefinitely. // NOTE: We actually won't get anywhere close to this limit. // SRTP will constantly read from the endpoint and drop packets if it's full. - a.buffer.SetLimitSize(maxBufferSize) + a.buf.SetLimitSize(maxBufferSize) if a.lite && (len(a.candidateTypes) != 1 || a.candidateTypes[0] != CandidateTypeHost) { - closeMDNSConn() + a.closeMulticastConn() return nil, ErrLiteUsingNonHostCandidates } if config.Urls != nil && len(config.Urls) > 0 && !containsCandidateType(CandidateTypeServerReflexive, a.candidateTypes) && !containsCandidateType(CandidateTypeRelay, a.candidateTypes) { - closeMDNSConn() + a.closeMulticastConn() return nil, ErrUselessUrlsProvided } if err = config.initExtIPMapping(a); err != nil { - closeMDNSConn() + a.closeMulticastConn() return nil, err } go a.taskLoop() - a.startOnConnectionStateChangeRoutine() + + // CandidatePair and ConnectionState are usually changed at once. + // Blocking one by the other one causes deadlock. + // Hence, we call handlers from independent Goroutines. + go a.candidatePairRoutine() + go a.connectionStateRoutine() + go a.candidateRoutine() // Restart is also used to initialize the agent for the first time if err := a.Restart(config.LocalUfrag, config.LocalPwd); err != nil { - closeMDNSConn() + a.closeMulticastConn() _ = a.Close() return nil, err } @@ -364,81 +376,6 @@ func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit return a, nil } -// OnConnectionStateChange sets a handler that is fired when the connection state changes -func (a *Agent) OnConnectionStateChange(f func(ConnectionState)) error { - a.onConnectionStateChangeHdlr.Store(f) - return nil -} - -// OnSelectedCandidatePairChange sets a handler that is fired when the final candidate -// pair is selected -func (a *Agent) OnSelectedCandidatePairChange(f func(Candidate, Candidate)) error { - a.onSelectedCandidatePairChangeHdlr.Store(f) - return nil -} - -// OnCandidate sets a handler that is fired when new candidates gathered. When -// the gathering process complete the last candidate is nil. -func (a *Agent) OnCandidate(f func(Candidate)) error { - a.onCandidateHdlr.Store(f) - return nil -} - -func (a *Agent) onSelectedCandidatePairChange(p *CandidatePair) { - if h, ok := a.onSelectedCandidatePairChangeHdlr.Load().(func(Candidate, Candidate)); ok { - h(p.Local, p.Remote) - } -} - -func (a *Agent) onCandidate(c Candidate) { - if onCandidateHdlr, ok := a.onCandidateHdlr.Load().(func(Candidate)); ok { - onCandidateHdlr(c) - } -} - -func (a *Agent) onConnectionStateChange(s ConnectionState) { - if hdlr, ok := a.onConnectionStateChangeHdlr.Load().(func(ConnectionState)); ok { - hdlr(s) - } -} - -func (a *Agent) startOnConnectionStateChangeRoutine() { - go func() { - for { - // CandidatePair and ConnectionState are usually changed at once. - // Blocking one by the other one causes deadlock. - p, isOpen := <-a.chanCandidatePair - if !isOpen { - return - } - a.onSelectedCandidatePairChange(p) - } - }() - go func() { - for { - select { - case s, isOpen := <-a.chanState: - if !isOpen { - for c := range a.chanCandidate { - a.onCandidate(c) - } - return - } - go a.onConnectionStateChange(s) - - case c, isOpen := <-a.chanCandidate: - if !isOpen { - for s := range a.chanState { - go a.onConnectionStateChange(s) - } - return - } - a.onCandidate(c) - } - } - }() -} - func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remotePwd string) error { a.muHaveStarted.Lock() defer a.muHaveStarted.Unlock() @@ -447,7 +384,7 @@ func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remoteP return ErrMultipleStart default: } - if err := a.SetRemoteCredentials(remoteUfrag, remotePwd); err != nil { + if err := a.SetRemoteCredentials(remoteUfrag, remotePwd); err != nil { //nolint:contextcheck return err } @@ -474,7 +411,7 @@ func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remoteP agent.updateConnectionState(ConnectionStateChecking) a.requestConnectivityCheck() - go a.connectivityChecks() + go a.connectivityChecks() //nolint:contextcheck }) } @@ -504,11 +441,12 @@ func (a *Agent) connectivityChecks() { a.updateConnectionState(ConnectionStateFailed) return } + default: } a.selector.ContactCandidates() }); err != nil { - a.log.Warnf("taskLoop failed: %v", err) + a.log.Warnf("Failed to start connectivity checks: %v", err) } } @@ -565,28 +503,26 @@ func (a *Agent) updateConnectionState(newState ConnectionState) { } func (a *Agent) setSelectedPair(p *CandidatePair) { - a.log.Tracef("Set selected candidate pair: %s", p) - if p == nil { var nilPair *CandidatePair a.selectedPair.Store(nilPair) + a.log.Tracef("Unset selected candidate pair") return } p.nominated = true a.selectedPair.Store(p) + a.log.Tracef("Set selected candidate pair: %s", p) a.updateConnectionState(ConnectionStateConnected) // Notify when the selected pair changes - if p != nil { - a.afterRun(func(ctx context.Context) { - select { - case a.chanCandidatePair <- p: - case <-ctx.Done(): - } - }) - } + a.afterRun(func(ctx context.Context) { + select { + case a.chanCandidatePair <- p: + case <-ctx.Done(): + } + }) // Signal connected a.onConnectedOnce.Do(func() { close(a.onConnected) }) @@ -596,7 +532,7 @@ func (a *Agent) pingAllCandidates() { a.log.Trace("pinging all candidates") if len(a.checklist) == 0 { - a.log.Warn("pingAllCandidates called with no candidate pairs. Connection is not possible yet.") + a.log.Warn("Failed to ping without candidate pairs. Connection is not possible yet.") } for _, p := range a.checklist { @@ -607,7 +543,7 @@ func (a *Agent) pingAllCandidates() { } if p.bindingRequestCount > a.maxBindingRequests { - a.log.Tracef("max requests reached for pair %s, marking it as failed\n", p) + a.log.Tracef("max requests reached for pair %s, marking it as failed", p) p.state = CandidatePairStateFailed } else { a.selector.PingCandidate(p.Local, p.Remote) @@ -703,7 +639,7 @@ func (a *Agent) checkKeepalive() { if (a.keepaliveInterval != 0) && ((time.Since(selectedPair.Local.LastSent()) > a.keepaliveInterval) || (time.Since(selectedPair.Remote.LastReceived()) > a.keepaliveInterval)) { - // we use binding request instead of indication to support refresh consent schemas + // We use binding request instead of indication to support refresh consent schemas // see https://tools.ietf.org/html/rfc7675 a.selector.PingCandidate(selectedPair.Local, selectedPair.Remote) } @@ -715,10 +651,10 @@ func (a *Agent) AddRemoteCandidate(c Candidate) error { return nil } - // cannot check for network yet because it might not be applied - // when mDNS hostame is used. + // Cannot check for network yet because it might not be applied + // when mDNS hostname is used. if c.TCPType() == TCPTypeActive { - // TCP Candidates with tcptype active will probe server passive ones, so + // TCP Candidates with TCP type active will probe server passive ones, so // no need to do anything with them. a.log.Infof("Ignoring remote candidate with tcpType active: %s", c) return nil @@ -727,7 +663,7 @@ func (a *Agent) AddRemoteCandidate(c Candidate) error { // If we have a mDNS Candidate lets fully resolve it before adding it locally if c.Type() == CandidateTypeHost && strings.HasSuffix(c.Address(), ".local") { if a.mDNSMode == MulticastDNSModeDisabled { - a.log.Warnf("remote mDNS candidate added, but mDNS is disabled: (%s)", c.Address()) + a.log.Warnf("Remote mDNS candidate added, but mDNS is disabled: (%s)", c.Address()) return nil } @@ -761,8 +697,8 @@ func (a *Agent) resolveAndAddMulticastCandidate(c *CandidateHost) { return } - ip, _, _, _ := parseAddr(src) //nolint:dogsled - if ip == nil { + ip, ipOk := parseMulticastAnswerAddr(src) + if !ipOk { a.log.Warnf("Failed to discover mDNS candidate %s: failed to parse IP", c.Address()) return } @@ -818,6 +754,9 @@ func (a *Agent) addCandidate(ctx context.Context, c Candidate, candidateConn net if err := c.close(); err != nil { a.log.Warnf("Failed to close duplicate candidate: %v", err) } + if err := candidateConn.Close(); err != nil { + a.log.Warnf("Failed to close duplicate candidate connection: %v", err) + } return } } @@ -888,10 +827,15 @@ func (a *Agent) GetRemoteUserCredentials() (frag string, pwd string, err error) } func (a *Agent) removeUfragFromMux() { - a.tcpMux.RemoveConnByUfrag(a.localUfrag) + if a.tcpMux != nil { + a.tcpMux.RemoveConnByUfrag(a.localUfrag) + } if a.udpMux != nil { a.udpMux.RemoveConnByUfrag(a.localUfrag) } + if a.udpMuxSrflx != nil { + a.udpMuxSrflx.RemoveConnByUfrag(a.localUfrag) + } } // Close cleans up the Agent @@ -902,6 +846,9 @@ func (a *Agent) Close() error { a.afterRun(func(context.Context) { a.gatherCandidateCancel() + if a.gatherCandidateDone != nil { + <-a.gatherCandidateDone + } }) a.err.Store(ErrClosed) @@ -938,7 +885,7 @@ func (a *Agent) deleteAllCandidates() { func (a *Agent) findRemoteCandidate(networkType NetworkType, addr net.Addr) Candidate { ip, port, _, ok := parseAddr(addr) if !ok { - a.log.Warnf("Error parsing addr: %s", addr) + a.log.Warnf("Failed to parse address: %s", addr) return nil } @@ -952,7 +899,7 @@ func (a *Agent) findRemoteCandidate(networkType NetworkType, addr net.Addr) Cand } func (a *Agent) sendBindingRequest(m *stun.Message, local, remote Candidate) { - a.log.Tracef("ping STUN from %s to %s\n", local.String(), remote.String()) + a.log.Tracef("ping STUN from %s to %s", local.String(), remote.String()) a.invalidatePendingBindingRequests(time.Now()) a.pendingBindingRequests = append(a.pendingBindingRequests, bindingRequest{ @@ -970,7 +917,7 @@ func (a *Agent) sendBindingSuccess(m *stun.Message, local, remote Candidate) { ip, port, _, ok := parseAddr(base.addr()) if !ok { - a.log.Warnf("Error parsing addr: %s", base.addr()) + a.log.Warnf("Failed to parse address: %s", base.addr()) return } @@ -988,12 +935,11 @@ func (a *Agent) sendBindingSuccess(m *stun.Message, local, remote Candidate) { } } -/* Removes pending binding requests that are over maxBindingRequestTimeout old - - Let HTO be the transaction timeout, which SHOULD be 2*RTT if - RTT is known or 500 ms otherwise. - https://tools.ietf.org/html/rfc8445#appendix-B.1 -*/ +// Removes pending binding requests that are over maxBindingRequestTimeout old +// +// Let HTO be the transaction timeout, which SHOULD be 2*RTT if +// RTT is known or 500 ms otherwise. +// https://tools.ietf.org/html/rfc8445#appendix-B.1 func (a *Agent) invalidatePendingBindingRequests(filterTime time.Time) { initialSize := len(a.pendingBindingRequests) @@ -1041,38 +987,38 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) if a.isControlling { if m.Contains(stun.AttrICEControlling) { - a.log.Debug("inbound isControlling && a.isControlling == true") + a.log.Debug("Inbound STUN message: isControlling && a.isControlling == true") return } else if m.Contains(stun.AttrUseCandidate) { - a.log.Debug("useCandidate && a.isControlling == true") + a.log.Debug("Inbound STUN message: useCandidate && a.isControlling == true") return } } else { if m.Contains(stun.AttrICEControlled) { - a.log.Debug("inbound isControlled && a.isControlling == false") + a.log.Debug("Inbound STUN message: isControlled && a.isControlling == false") return } } remoteCandidate := a.findRemoteCandidate(local.NetworkType(), remote) if m.Type.Class == stun.ClassSuccessResponse { - if err = assertInboundMessageIntegrity(m, []byte(a.remotePwd)); err != nil { - a.log.Warnf("discard message from (%s), %v", remote, err) + if err = stun.MessageIntegrity([]byte(a.remotePwd)).Check(m); err != nil { + a.log.Warnf("Discard message from (%s), %v", remote, err) return } if remoteCandidate == nil { - a.log.Warnf("discard success message from (%s), no such remote", remote) + a.log.Warnf("Discard success message from (%s), no such remote", remote) return } a.selector.HandleSuccessResponse(m, local, remoteCandidate, remote) } else if m.Type.Class == stun.ClassRequest { - if err = assertInboundUsername(m, a.localUfrag+":"+a.remoteUfrag); err != nil { - a.log.Warnf("discard message from (%s), %v", remote, err) + if err = stunx.AssertUsername(m, a.localUfrag+":"+a.remoteUfrag); err != nil { + a.log.Warnf("Discard message from (%s), %v", remote, err) return - } else if err = assertInboundMessageIntegrity(m, []byte(a.localPwd)); err != nil { - a.log.Warnf("discard message from (%s), %v", remote, err) + } else if err = stun.MessageIntegrity([]byte(a.localPwd)).Check(m); err != nil { + a.log.Warnf("Discard message from (%s), %v", remote, err) return } @@ -1099,7 +1045,7 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) } remoteCandidate = prflxCandidate - a.log.Debugf("adding a new peer-reflexive candidate: %s ", remote) + a.log.Debugf("Adding a new peer-reflexive candidate: %s ", remote) a.addRemoteCandidate(remoteCandidate) } @@ -1115,26 +1061,25 @@ func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) // validateNonSTUNTraffic processes non STUN traffic from a remote candidate, // and returns true if it is an actual remote candidate -func (a *Agent) validateNonSTUNTraffic(local Candidate, remote net.Addr) bool { - var isValidCandidate uint64 +func (a *Agent) validateNonSTUNTraffic(local Candidate, remote net.Addr) (Candidate, bool) { + var remoteCandidate Candidate if err := a.run(local.context(), func(ctx context.Context, agent *Agent) { - remoteCandidate := a.findRemoteCandidate(local.NetworkType(), remote) + remoteCandidate = a.findRemoteCandidate(local.NetworkType(), remote) if remoteCandidate != nil { remoteCandidate.seen(false) - atomic.AddUint64(&isValidCandidate, 1) } }); err != nil { - a.log.Warnf("failed to validate remote candidate: %v", err) + a.log.Warnf("Failed to validate remote candidate: %v", err) } - return atomic.LoadUint64(&isValidCandidate) == 1 + return remoteCandidate, remoteCandidate != nil } // GetSelectedCandidatePair returns the selected pair or nil if there is none func (a *Agent) GetSelectedCandidatePair() (*CandidatePair, error) { selectedPair := a.getSelectedPair() if selectedPair == nil { - return nil, nil + return nil, nil //nolint:nilnil } local, err := selectedPair.Local.copy() @@ -1151,18 +1096,17 @@ func (a *Agent) GetSelectedCandidatePair() (*CandidatePair, error) { } func (a *Agent) getSelectedPair() *CandidatePair { - selectedPair := a.selectedPair.Load() - if selectedPair == nil { - return nil + if selectedPair, ok := a.selectedPair.Load().(*CandidatePair); ok { + return selectedPair } - return selectedPair.(*CandidatePair) + return nil } func (a *Agent) closeMulticastConn() { if a.mDNSConn != nil { if err := a.mDNSConn.Close(); err != nil { - a.log.Warnf("failed to close mDNS Conn: %v", err) + a.log.Warnf("Failed to close mDNS Conn: %v", err) } } } @@ -1185,8 +1129,10 @@ func (a *Agent) SetRemoteCredentials(remoteUfrag, remotePwd string) error { // Restart restarts the ICE Agent with the provided ufrag/pwd // If no ufrag/pwd is provided the Agent will generate one itself // -// Restart must only be called when GatheringState is GatheringStateComplete -// a user must then call GatherCandidates explicitly to start generating new ones +// If there is a gatherer routine currently running, Restart will +// cancel it. +// After a Restart, the user must then call GatherCandidates explicitly +// to start generating new ones. func (a *Agent) Restart(ufrag, pwd string) error { if ufrag == "" { var err error @@ -1213,8 +1159,7 @@ func (a *Agent) Restart(ufrag, pwd string) error { var err error if runErr := a.run(a.context(), func(ctx context.Context, agent *Agent) { if agent.gatheringState == GatheringStateGathering { - err = ErrRestartWhenGathering - return + agent.gatherCandidateCancel() } // Clear all agent needed to take back to fresh state diff --git a/vendor/github.com/pion/ice/v2/agent_config.go b/vendor/github.com/pion/ice/v2/agent_config.go index e577af101..f5897cd4a 100644 --- a/vendor/github.com/pion/ice/v2/agent_config.go +++ b/vendor/github.com/pion/ice/v2/agent_config.go @@ -1,10 +1,15 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( + "net" "time" "github.com/pion/logging" - "github.com/pion/transport/vnet" + "github.com/pion/stun" + "github.com/pion/transport/v2" "golang.org/x/net/proxy" ) @@ -21,25 +26,25 @@ const ( // defaultFailedTimeout is the default time till an Agent transitions to failed after disconnected defaultFailedTimeout = 25 * time.Second - // wait time before nominating a host candidate + // defaultHostAcceptanceMinWait is the wait time before nominating a host candidate defaultHostAcceptanceMinWait = 0 - // wait time before nominating a srflx candidate + // defaultSrflxAcceptanceMinWait is the wait time before nominating a srflx candidate defaultSrflxAcceptanceMinWait = 500 * time.Millisecond - // wait time before nominating a prflx candidate + // defaultPrflxAcceptanceMinWait is the wait time before nominating a prflx candidate defaultPrflxAcceptanceMinWait = 1000 * time.Millisecond - // wait time before nominating a relay candidate + // defaultRelayAcceptanceMinWait is the wait time before nominating a relay candidate defaultRelayAcceptanceMinWait = 2000 * time.Millisecond - // max binding request before considering a pair failed + // defaultMaxBindingRequests is the maximum number of binding requests before considering a pair failed defaultMaxBindingRequests = 7 - // the number of bytes that can be buffered before we start to error + // maxBufferSize is the number of bytes that can be buffered before we start to error maxBufferSize = 1000 * 1000 // 1MB - // wait time before binding requests can be deleted + // maxBindingRequestTimeout is the wait time before binding requests can be deleted maxBindingRequestTimeout = 4000 * time.Millisecond ) @@ -50,7 +55,7 @@ func defaultCandidateTypes() []CandidateType { // AgentConfig collects the arguments to ice.Agent construction into // a single structure, for future-proofness of the interface type AgentConfig struct { - Urls []*URL + Urls []*stun.URI // PortMin and PortMax are optional. Leave them 0 for the default UDP port allocation strategy. PortMin uint16 @@ -108,14 +113,14 @@ type AgentConfig struct { // NAT1To1IPCandidateType is used along with NAT1To1IPs to specify which candidate type // the 1:1 NAT IP addresses should be mapped to. // If unspecified or CandidateTypeHost, NAT1To1IPs are used to replace host candidate IPs. - // If CandidateTypeServerReflexive, it will insert a srflx candidate (as if it was dervied + // If CandidateTypeServerReflexive, it will insert a srflx candidate (as if it was derived // from a STUN server) with its port number being the one for the actual host candidate. // Other values will result in an error. NAT1To1IPCandidateType CandidateType // NAT1To1IPs contains a list of public IP addresses that are to be used as a host // candidate or srflx candidate. This is used typically for servers that are behind - // 1:1 D-NAT (e.g. AWS EC2 instances) and to eliminate the need of server reflexisive + // 1:1 D-NAT (e.g. AWS EC2 instances) and to eliminate the need of server reflexive // candidate gathering. NAT1To1IPs []string @@ -129,13 +134,17 @@ type AgentConfig struct { RelayAcceptanceMinWait *time.Duration // Net is the our abstracted network interface for internal development purpose only - // (see github.com/pion/transport/vnet) - Net *vnet.Net + // (see https://github.com/pion/transport) + Net transport.Net - // InterfaceFilter is a function that you can use in order to whitelist or blacklist + // InterfaceFilter is a function that you can use in order to whitelist or blacklist // the interfaces which are used to gather ICE candidates. InterfaceFilter func(string) bool + // IPFilter is a function that you can use in order to whitelist or blacklist + // the ips which are used to gather ICE candidates. + IPFilter func(net.IP) bool + // InsecureSkipVerify controls if self-signed certificates are accepted when connecting // to TURN servers via TLS or DTLS InsecureSkipVerify bool @@ -150,9 +159,21 @@ type AgentConfig struct { // defer to UDPMux for incoming connections UDPMux UDPMux + // UDPMuxSrflx is used for multiplexing multiple incoming UDP connections of server reflexive candidates + // on a single port when this is set, the agent ignores PortMin and PortMax configurations and will + // defer to UDPMuxSrflx for incoming connections + // It embeds UDPMux to do the actual connection multiplexing + UDPMuxSrflx UniversalUDPMux + // Proxy Dialer is a dialer that should be implemented by the user based on golang.org/x/net/proxy // dial interface in order to support corporate proxies ProxyDialer proxy.Dialer + + // Deprecated: AcceptAggressiveNomination always enabled. + AcceptAggressiveNomination bool + + // Include loopback addresses in the candidate list. + IncludeLoopback bool } // initWithDefaults populates an agent and falls back to defaults if fields are unset @@ -225,7 +246,7 @@ func (config *AgentConfig) initExtIPMapping(a *Agent) error { return err } if a.extIPMapper == nil { - return nil // this may happen when config.NAT1To1IPs is an empty array + return nil // This may happen when config.NAT1To1IPs is an empty array } if a.extIPMapper.candidateType == CandidateTypeHost { if a.mDNSMode == MulticastDNSModeQueryAndGather { diff --git a/vendor/github.com/pion/ice/v2/agent_handlers.go b/vendor/github.com/pion/ice/v2/agent_handlers.go new file mode 100644 index 000000000..c5a5ec03b --- /dev/null +++ b/vendor/github.com/pion/ice/v2/agent_handlers.go @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package ice + +// OnConnectionStateChange sets a handler that is fired when the connection state changes +func (a *Agent) OnConnectionStateChange(f func(ConnectionState)) error { + a.onConnectionStateChangeHdlr.Store(f) + return nil +} + +// OnSelectedCandidatePairChange sets a handler that is fired when the final candidate +// pair is selected +func (a *Agent) OnSelectedCandidatePairChange(f func(Candidate, Candidate)) error { + a.onSelectedCandidatePairChangeHdlr.Store(f) + return nil +} + +// OnCandidate sets a handler that is fired when new candidates gathered. When +// the gathering process complete the last candidate is nil. +func (a *Agent) OnCandidate(f func(Candidate)) error { + a.onCandidateHdlr.Store(f) + return nil +} + +func (a *Agent) onSelectedCandidatePairChange(p *CandidatePair) { + if h, ok := a.onSelectedCandidatePairChangeHdlr.Load().(func(Candidate, Candidate)); ok { + h(p.Local, p.Remote) + } +} + +func (a *Agent) onCandidate(c Candidate) { + if onCandidateHdlr, ok := a.onCandidateHdlr.Load().(func(Candidate)); ok { + onCandidateHdlr(c) + } +} + +func (a *Agent) onConnectionStateChange(s ConnectionState) { + if hdlr, ok := a.onConnectionStateChangeHdlr.Load().(func(ConnectionState)); ok { + hdlr(s) + } +} + +func (a *Agent) candidatePairRoutine() { + for p := range a.chanCandidatePair { + a.onSelectedCandidatePairChange(p) + } +} + +func (a *Agent) connectionStateRoutine() { + for s := range a.chanState { + go a.onConnectionStateChange(s) + } +} + +func (a *Agent) candidateRoutine() { + for c := range a.chanCandidate { + a.onCandidate(c) + } +} diff --git a/vendor/github.com/pion/ice/v2/agent_stats.go b/vendor/github.com/pion/ice/v2/agent_stats.go index b249560b6..b9ad7187e 100644 --- a/vendor/github.com/pion/ice/v2/agent_stats.go +++ b/vendor/github.com/pion/ice/v2/agent_stats.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( @@ -45,7 +48,7 @@ func (a *Agent) GetCandidatePairsStats() []CandidatePairStats { res = result }) if err != nil { - a.log.Errorf("error getting candidate pairs stats %v", err) + a.log.Errorf("Failed to get candidate pairs stats: %v", err) return []CandidatePairStats{} } return res @@ -60,7 +63,9 @@ func (a *Agent) GetLocalCandidatesStats() []CandidateStats { for _, c := range localCandidates { relayProtocol := "" if c.Type() == CandidateTypeRelay { - relayProtocol = c.(*CandidateRelay).RelayProtocol() + if cRelay, ok := c.(*CandidateRelay); ok { + relayProtocol = cRelay.RelayProtocol() + } } stat := CandidateStats{ Timestamp: time.Now(), @@ -80,7 +85,7 @@ func (a *Agent) GetLocalCandidatesStats() []CandidateStats { res = result }) if err != nil { - a.log.Errorf("error getting candidate pairs stats %v", err) + a.log.Errorf("Failed to get candidate pair stats: %v", err) return []CandidateStats{} } return res @@ -91,8 +96,8 @@ func (a *Agent) GetRemoteCandidatesStats() []CandidateStats { var res []CandidateStats err := a.run(a.context(), func(ctx context.Context, agent *Agent) { result := make([]CandidateStats, 0, len(agent.remoteCandidates)) - for networkType, localCandidates := range agent.remoteCandidates { - for _, c := range localCandidates { + for networkType, remoteCandidates := range agent.remoteCandidates { + for _, c := range remoteCandidates { stat := CandidateStats{ Timestamp: time.Now(), ID: c.ID(), @@ -110,7 +115,7 @@ func (a *Agent) GetRemoteCandidatesStats() []CandidateStats { res = result }) if err != nil { - a.log.Errorf("error getting candidate pairs stats %v", err) + a.log.Errorf("Failed to get candidate pair stats: %v", err) return []CandidateStats{} } return res diff --git a/vendor/github.com/pion/ice/v2/candidate.go b/vendor/github.com/pion/ice/v2/candidate.go index c3049dd00..92a007688 100644 --- a/vendor/github.com/pion/ice/v2/candidate.go +++ b/vendor/github.com/pion/ice/v2/candidate.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( diff --git a/vendor/github.com/pion/ice/v2/candidate_base.go b/vendor/github.com/pion/ice/v2/candidate_base.go index 7703e7106..8e551d8f2 100644 --- a/vendor/github.com/pion/ice/v2/candidate_base.go +++ b/vendor/github.com/pion/ice/v2/candidate_base.go @@ -1,16 +1,20 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( "context" + "errors" "fmt" "hash/crc32" + "io" "net" "strconv" "strings" "sync/atomic" "time" - "github.com/pion/logging" "github.com/pion/stun" ) @@ -37,6 +41,8 @@ type candidateBase struct { foundationOverride string priorityOverride uint32 + + remoteCandidateCaches map[AddrPort]Candidate } // Done implements context.Context @@ -60,7 +66,7 @@ func (c *candidateBase) Deadline() (deadline time.Time, ok bool) { } // Value implements context.Context -func (c *candidateBase) Value(key interface{}) interface{} { +func (c *candidateBase) Value(interface{}) interface{} { return nil } @@ -206,9 +212,9 @@ func (c *candidateBase) start(a *Agent, conn net.PacketConn, initializedCh <-cha } func (c *candidateBase) recvLoop(initializedCh <-chan struct{}) { - defer func() { - close(c.closedCh) - }() + a := c.agent() + + defer close(c.closedCh) select { case <-initializedCh: @@ -216,47 +222,73 @@ func (c *candidateBase) recvLoop(initializedCh <-chan struct{}) { return } - log := c.agent().log - buffer := make([]byte, receiveMTU) + buf := make([]byte, receiveMTU) for { - n, srcAddr, err := c.conn.ReadFrom(buffer) + n, srcAddr, err := c.conn.ReadFrom(buf) if err != nil { + if !(errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed)) { + a.log.Warnf("Failed to read from candidate %s: %v", c, err) + } return } - handleInboundCandidateMsg(c, c, buffer[:n], srcAddr, log) + c.handleInboundPacket(buf[:n], srcAddr) } } -func handleInboundCandidateMsg(ctx context.Context, c Candidate, buffer []byte, srcAddr net.Addr, log logging.LeveledLogger) { - if stun.IsMessage(buffer) { +func (c *candidateBase) validateSTUNTrafficCache(addr net.Addr) bool { + if candidate, ok := c.remoteCandidateCaches[toAddrPort(addr)]; ok { + candidate.seen(false) + return true + } + return false +} + +func (c *candidateBase) addRemoteCandidateCache(candidate Candidate, srcAddr net.Addr) { + if c.validateSTUNTrafficCache(srcAddr) { + return + } + c.remoteCandidateCaches[toAddrPort(srcAddr)] = candidate +} + +func (c *candidateBase) handleInboundPacket(buf []byte, srcAddr net.Addr) { + a := c.agent() + + if stun.IsMessage(buf) { m := &stun.Message{ - Raw: make([]byte, len(buffer)), + Raw: make([]byte, len(buf)), } + // Explicitly copy raw buffer so Message can own the memory. - copy(m.Raw, buffer) + copy(m.Raw, buf) + if err := m.Decode(); err != nil { - log.Warnf("Failed to handle decode ICE from %s to %s: %v", c.addr(), srcAddr, err) + a.log.Warnf("Failed to handle decode ICE from %s to %s: %v", c.addr(), srcAddr, err) return } - err := c.agent().run(ctx, func(ctx context.Context, agent *Agent) { - agent.handleInbound(m, c, srcAddr) - }) - if err != nil { - log.Warnf("Failed to handle message: %v", err) + + if err := a.run(c, func(ctx context.Context, a *Agent) { + a.handleInbound(m, c, srcAddr) + }); err != nil { + a.log.Warnf("Failed to handle message: %v", err) } return } - if !c.agent().validateNonSTUNTraffic(c, srcAddr) { - log.Warnf("Discarded message from %s, not a valid remote candidate", c.addr()) - return + if !c.validateSTUNTrafficCache(srcAddr) { + remoteCandidate, valid := a.validateNonSTUNTraffic(c, srcAddr) //nolint:contextcheck + if !valid { + a.log.Warnf("Discarded message from %s, not a valid remote candidate", c.addr()) + return + } + c.addRemoteCandidateCache(remoteCandidate, srcAddr) } - // NOTE This will return packetio.ErrFull if the buffer ever manages to fill up. - if _, err := c.agent().buffer.Write(buffer); err != nil { - log.Warnf("failed to write packet") + // Note: This will return packetio.ErrFull if the buffer ever manages to fill up. + if _, err := a.buf.Write(buf); err != nil { + a.log.Warnf("Failed to write packet: %s", err) + return } } @@ -300,7 +332,11 @@ func (c *candidateBase) close() error { func (c *candidateBase) writeTo(raw []byte, dst Candidate) (int, error) { n, err := c.conn.WriteTo(raw, dst.addr()) if err != nil { - c.agent().log.Warnf("%s: %v", errSendPacket, err) + // If the connection is closed, we should return the error + if errors.Is(err, io.ErrClosedPipe) { + return n, err + } + c.agent().log.Infof("%s: %v", errSendPacket, err) return n, nil } c.seen(true) @@ -336,17 +372,16 @@ func (c *candidateBase) Equal(other Candidate) bool { // String makes the candidateBase printable func (c *candidateBase) String() string { - return fmt.Sprintf("%s %s %s:%d%s", c.NetworkType(), c.Type(), c.Address(), c.Port(), c.relatedAddress) + return fmt.Sprintf("%s %s %s%s", c.NetworkType(), c.Type(), net.JoinHostPort(c.Address(), strconv.Itoa(c.Port())), c.relatedAddress) } // LastReceived returns a time.Time indicating the last time // this candidate was received func (c *candidateBase) LastReceived() time.Time { - lastReceived := c.lastReceived.Load() - if lastReceived == nil { - return time.Time{} + if lastReceived, ok := c.lastReceived.Load().(time.Time); ok { + return lastReceived } - return lastReceived.(time.Time) + return time.Time{} } func (c *candidateBase) setLastReceived(t time.Time) { @@ -356,11 +391,10 @@ func (c *candidateBase) setLastReceived(t time.Time) { // LastSent returns a time.Time indicating the last time // this candidate was sent func (c *candidateBase) LastSent() time.Time { - lastSent := c.lastSent.Load() - if lastSent == nil { - return time.Time{} + if lastSent, ok := c.lastSent.Load().(time.Time); ok { + return lastSent } - return lastSent.(time.Time) + return time.Time{} } func (c *candidateBase) setLastSent(t time.Time) { @@ -438,7 +472,7 @@ func UnmarshalCandidate(raw string) (Candidate, error) { // Component rawComponent, err := strconv.ParseUint(split[1], 10, 16) if err != nil { - return nil, fmt.Errorf("%w: %v", errParseComponent, err) + return nil, fmt.Errorf("%w: %v", errParseComponent, err) //nolint:errorlint } component := uint16(rawComponent) @@ -448,7 +482,7 @@ func UnmarshalCandidate(raw string) (Candidate, error) { // Priority priorityRaw, err := strconv.ParseUint(split[3], 10, 32) if err != nil { - return nil, fmt.Errorf("%w: %v", errParsePriority, err) + return nil, fmt.Errorf("%w: %v", errParsePriority, err) //nolint:errorlint } priority := uint32(priorityRaw) @@ -458,7 +492,7 @@ func UnmarshalCandidate(raw string) (Candidate, error) { // Port rawPort, err := strconv.ParseUint(split[5], 10, 16) if err != nil { - return nil, fmt.Errorf("%w: %v", errParsePort, err) + return nil, fmt.Errorf("%w: %v", errParsePort, err) //nolint:errorlint } port := int(rawPort) typ := split[7] @@ -481,12 +515,12 @@ func UnmarshalCandidate(raw string) (Candidate, error) { // RelatedPort rawRelatedPort, parseErr := strconv.ParseUint(split[3], 10, 16) if parseErr != nil { - return nil, fmt.Errorf("%w: %v", errParsePort, parseErr) + return nil, fmt.Errorf("%w: %v", errParsePort, parseErr) //nolint:errorlint } relatedPort = int(rawRelatedPort) } else if split[0] == "tcptype" { if len(split) < 2 { - return nil, fmt.Errorf("%w: incorrect length", errParseTypType) + return nil, fmt.Errorf("%w: incorrect length", errParseTCPType) } tcpType = NewTCPType(split[1]) diff --git a/vendor/github.com/pion/ice/v2/candidate_host.go b/vendor/github.com/pion/ice/v2/candidate_host.go index b03dbdb51..5d207dd91 100644 --- a/vendor/github.com/pion/ice/v2/candidate_host.go +++ b/vendor/github.com/pion/ice/v2/candidate_host.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( @@ -34,14 +37,15 @@ func NewCandidateHost(config *CandidateHostConfig) (*CandidateHost, error) { c := &CandidateHost{ candidateBase: candidateBase{ - id: candidateID, - address: config.Address, - candidateType: CandidateTypeHost, - component: config.Component, - port: config.Port, - tcpType: config.TCPType, - foundationOverride: config.Foundation, - priorityOverride: config.Priority, + id: candidateID, + address: config.Address, + candidateType: CandidateTypeHost, + component: config.Component, + port: config.Port, + tcpType: config.TCPType, + foundationOverride: config.Foundation, + priorityOverride: config.Priority, + remoteCandidateCaches: map[AddrPort]Candidate{}, }, network: config.Network, } diff --git a/vendor/github.com/pion/ice/v2/candidate_peer_reflexive.go b/vendor/github.com/pion/ice/v2/candidate_peer_reflexive.go index 0b330d1c9..f019ec698 100644 --- a/vendor/github.com/pion/ice/v2/candidate_peer_reflexive.go +++ b/vendor/github.com/pion/ice/v2/candidate_peer_reflexive.go @@ -1,4 +1,8 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package ice ... +// //nolint:dupl package ice @@ -55,6 +59,7 @@ func NewCandidatePeerReflexive(config *CandidatePeerReflexiveConfig) (*Candidate Address: config.RelAddr, Port: config.RelPort, }, + remoteCandidateCaches: map[AddrPort]Candidate{}, }, }, nil } diff --git a/vendor/github.com/pion/ice/v2/candidate_relay.go b/vendor/github.com/pion/ice/v2/candidate_relay.go index 3d0be3168..449d077a6 100644 --- a/vendor/github.com/pion/ice/v2/candidate_relay.go +++ b/vendor/github.com/pion/ice/v2/candidate_relay.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( @@ -60,6 +63,7 @@ func NewCandidateRelay(config *CandidateRelayConfig) (*CandidateRelay, error) { Address: config.RelAddr, Port: config.RelPort, }, + remoteCandidateCaches: map[AddrPort]Candidate{}, }, relayProtocol: config.RelayProtocol, onClose: config.OnClose, @@ -79,3 +83,16 @@ func (c *CandidateRelay) close() error { } return err } + +func (c *CandidateRelay) copy() (Candidate, error) { + cc, err := c.candidateBase.copy() + if err != nil { + return nil, err + } + + if ccr, ok := cc.(*CandidateRelay); ok { + ccr.relayProtocol = c.relayProtocol + } + + return cc, nil +} diff --git a/vendor/github.com/pion/ice/v2/candidate_server_reflexive.go b/vendor/github.com/pion/ice/v2/candidate_server_reflexive.go index 125a53782..3a8ac0ff7 100644 --- a/vendor/github.com/pion/ice/v2/candidate_server_reflexive.go +++ b/vendor/github.com/pion/ice/v2/candidate_server_reflexive.go @@ -1,5 +1,6 @@ -// Package ice ... -//nolint:dupl +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import "net" @@ -54,6 +55,7 @@ func NewCandidateServerReflexive(config *CandidateServerReflexiveConfig) (*Candi Address: config.RelAddr, Port: config.RelPort, }, + remoteCandidateCaches: map[AddrPort]Candidate{}, }, }, nil } diff --git a/vendor/github.com/pion/ice/v2/candidatepair.go b/vendor/github.com/pion/ice/v2/candidatepair.go index 79cab1a9f..f33be539c 100644 --- a/vendor/github.com/pion/ice/v2/candidatepair.go +++ b/vendor/github.com/pion/ice/v2/candidatepair.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( @@ -18,12 +21,13 @@ func newCandidatePair(local, remote Candidate, controlling bool) *CandidatePair // CandidatePair is a combination of a // local and remote candidate type CandidatePair struct { - iceRoleControlling bool - Remote Candidate - Local Candidate - bindingRequestCount uint16 - state CandidatePairState - nominated bool + iceRoleControlling bool + Remote Candidate + Local Candidate + bindingRequestCount uint16 + state CandidatePairState + nominated bool + nominateOnBindingSuccess bool } func (p *CandidatePair) String() string { diff --git a/vendor/github.com/pion/ice/v2/candidatepair_state.go b/vendor/github.com/pion/ice/v2/candidatepair_state.go index 28c7187eb..1a1e827b0 100644 --- a/vendor/github.com/pion/ice/v2/candidatepair_state.go +++ b/vendor/github.com/pion/ice/v2/candidatepair_state.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice // CandidatePairState represent the ICE candidate pair state diff --git a/vendor/github.com/pion/ice/v2/candidaterelatedaddress.go b/vendor/github.com/pion/ice/v2/candidaterelatedaddress.go index 18cf31831..e87c70514 100644 --- a/vendor/github.com/pion/ice/v2/candidaterelatedaddress.go +++ b/vendor/github.com/pion/ice/v2/candidaterelatedaddress.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import "fmt" diff --git a/vendor/github.com/pion/ice/v2/candidatetype.go b/vendor/github.com/pion/ice/v2/candidatetype.go index 376c4089f..3972934cb 100644 --- a/vendor/github.com/pion/ice/v2/candidatetype.go +++ b/vendor/github.com/pion/ice/v2/candidatetype.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice // CandidateType represents the type of candidate diff --git a/vendor/github.com/pion/ice/v2/codecov.yml b/vendor/github.com/pion/ice/v2/codecov.yml index 085200a48..263e4d45c 100644 --- a/vendor/github.com/pion/ice/v2/codecov.yml +++ b/vendor/github.com/pion/ice/v2/codecov.yml @@ -3,6 +3,8 @@ # # It is automatically copied from https://github.com/pion/.goassets repository. # +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT coverage: status: diff --git a/vendor/github.com/pion/ice/v2/context.go b/vendor/github.com/pion/ice/v2/context.go index 627d81ef4..36454450c 100644 --- a/vendor/github.com/pion/ice/v2/context.go +++ b/vendor/github.com/pion/ice/v2/context.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( @@ -32,6 +35,6 @@ func (a agentContext) Deadline() (deadline time.Time, ok bool) { } // Value implements context.Context -func (a agentContext) Value(key interface{}) interface{} { +func (a agentContext) Value(interface{}) interface{} { return nil } diff --git a/vendor/github.com/pion/ice/v2/errors.go b/vendor/github.com/pion/ice/v2/errors.go index 8ca9c2cde..e05cce898 100644 --- a/vendor/github.com/pion/ice/v2/errors.go +++ b/vendor/github.com/pion/ice/v2/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import "errors" @@ -10,7 +13,7 @@ var ( ErrSchemeType = errors.New("unknown scheme type") // ErrSTUNQuery indicates query arguments are provided in a STUN URL. - ErrSTUNQuery = errors.New("queries not supported in stun address") + ErrSTUNQuery = errors.New("queries not supported in STUN address") // ErrInvalidQuery indicates an malformed query is provided. ErrInvalidQuery = errors.New("invalid query") @@ -25,7 +28,7 @@ var ( // Have to be at least 24 bits long ErrLocalUfragInsufficientBits = errors.New("local username fragment is less than 24 bits long") - // ErrLocalPwdInsufficientBits indicates local passoword insufficient bits are provided. + // ErrLocalPwdInsufficientBits indicates local password insufficient bits are provided. // Have to be at least 128 bits long ErrLocalPwdInsufficientBits = errors.New("local password is less than 128 bits long") @@ -97,15 +100,9 @@ var ( // ErrInvalidMulticastDNSHostName indicates an invalid MulticastDNSHostName ErrInvalidMulticastDNSHostName = errors.New("invalid mDNS HostName, must end with .local and can only contain a single '.'") - // ErrRestartWhenGathering indicates Restart was called when Agent is in GatheringStateGathering - ErrRestartWhenGathering = errors.New("ICE Agent can not be restarted when gathering") - // ErrRunCanceled indicates a run operation was canceled by its individual done ErrRunCanceled = errors.New("run was canceled by done") - // ErrTCPMuxNotInitialized indicates TCPMux is not initialized and that invalidTCPMux is used. - ErrTCPMuxNotInitialized = errors.New("TCPMux is not initialized") - // ErrTCPRemoteAddrAlreadyExists indicates we already have the connection with same remote addr. ErrTCPRemoteAddrAlreadyExists = errors.New("conn with same remote addr already exists") @@ -121,18 +118,25 @@ var ( errParsePriority = errors.New("could not parse priority") errParsePort = errors.New("could not parse port") errParseRelatedAddr = errors.New("could not parse related addresses") - errParseTypType = errors.New("could not parse typtype") + errParseTCPType = errors.New("could not parse TCP type") errGetXorMappedAddrResponse = errors.New("failed to get XOR-MAPPED-ADDRESS response") errConnectionAddrAlreadyExist = errors.New("connection with same remote address already exists") errReadingStreamingPacket = errors.New("error reading streaming packet") errWriting = errors.New("error writing to") errClosingConnection = errors.New("error closing connection") - errMissingProtocolScheme = errors.New("missing protocol scheme") - errTooManyColonsAddr = errors.New("too many colons in address") errRead = errors.New("unexpected error trying to read") errUnknownRole = errors.New("unknown role") - errMismatchUsername = errors.New("username mismatch") errICEWriteSTUNMessage = errors.New("the ICE conn can't write STUN messages") errUDPMuxDisabled = errors.New("UDPMux is not enabled") - errCandidateIPNotFound = errors.New("could not determine local IP for Mux candidate") + errNoXorAddrMapping = errors.New("no address mapping") + errSendSTUNPacket = errors.New("failed to send STUN packet") + errXORMappedAddrTimeout = errors.New("timeout while waiting for XORMappedAddr") + errNotImplemented = errors.New("not implemented yet") + errNoUDPMuxAvailable = errors.New("no UDP mux is available") + errNoTCPMuxAvailable = errors.New("no TCP mux is available") + errInvalidAddress = errors.New("invalid address") + + // UDPMuxDefault should not listen on unspecified address, but to keep backward compatibility, don't return error now. + // will be used in the future. + // errListenUnspecified = errors.New("can't listen on unspecified address") ) diff --git a/vendor/github.com/pion/ice/v2/external_ip_mapper.go b/vendor/github.com/pion/ice/v2/external_ip_mapper.go index 5310cc0ae..3d542fb1a 100644 --- a/vendor/github.com/pion/ice/v2/external_ip_mapper.go +++ b/vendor/github.com/pion/ice/v2/external_ip_mapper.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( @@ -15,8 +18,9 @@ func validateIPString(ipStr string) (net.IP, bool, error) { // ipMapping holds the mapping of local and external IP address for a particular IP family type ipMapping struct { - ipSole net.IP // when non-nil, this is the sole external IP for one local IP assumed - ipMap map[string]net.IP // local-to-external IP mapping (k: local, v: external) + ipSole net.IP // When non-nil, this is the sole external IP for one local IP assumed + ipMap map[string]net.IP // Local-to-external IP mapping (k: local, v: external) + valid bool // If not set any external IP, valid is false } func (m *ipMapping) setSoleIP(ip net.IP) error { @@ -25,6 +29,7 @@ func (m *ipMapping) setSoleIP(ip net.IP) error { } m.ipSole = ip + m.valid = true return nil } @@ -36,17 +41,22 @@ func (m *ipMapping) addIPMapping(locIP, extIP net.IP) error { locIPStr := locIP.String() - // check if dup of local IP + // Check if dup of local IP if _, ok := m.ipMap[locIPStr]; ok { return ErrInvalidNAT1To1IPMapping } m.ipMap[locIPStr] = extIP + m.valid = true return nil } func (m *ipMapping) findExternalIP(locIP net.IP) (net.IP, error) { + if !m.valid { + return locIP, nil + } + if m.ipSole != nil { return m.ipSole, nil } @@ -67,10 +77,10 @@ type externalIPMapper struct { func newExternalIPMapper(candidateType CandidateType, ips []string) (*externalIPMapper, error) { //nolint:gocognit if len(ips) == 0 { - return nil, nil + return nil, nil //nolint:nilnil } if candidateType == CandidateTypeUnspecified { - candidateType = CandidateTypeHost // defaults to host + candidateType = CandidateTypeHost // Defaults to host } else if candidateType != CandidateTypeHost && candidateType != CandidateTypeServerReflexive { return nil, ErrUnsupportedNAT1To1IPCandidateType } diff --git a/vendor/github.com/pion/ice/v2/gather.go b/vendor/github.com/pion/ice/v2/gather.go index e248c08de..15e2da146 100644 --- a/vendor/github.com/pion/ice/v2/gather.go +++ b/vendor/github.com/pion/ice/v2/gather.go @@ -1,17 +1,23 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( "context" "crypto/tls" - "errors" "fmt" + "io" "net" "reflect" "sync" "time" "github.com/pion/dtls/v2" + "github.com/pion/ice/v2/internal/fakenet" + stunx "github.com/pion/ice/v2/internal/stun" "github.com/pion/logging" + "github.com/pion/stun" "github.com/pion/turn/v2" ) @@ -19,42 +25,19 @@ const ( stunGatherTimeout = time.Second * 5 ) -type closeable interface { - Close() error -} - // Close a net.Conn and log if we have a failure -func closeConnAndLog(c closeable, log logging.LeveledLogger, msg string) { +func closeConnAndLog(c io.Closer, log logging.LeveledLogger, msg string, args ...interface{}) { if c == nil || (reflect.ValueOf(c).Kind() == reflect.Ptr && reflect.ValueOf(c).IsNil()) { - log.Warnf("Conn is not allocated (%s)", msg) + log.Warnf("Connection is not allocated: "+msg, args...) return } log.Warnf(msg) if err := c.Close(); err != nil { - log.Warnf("Failed to close conn: %v", err) + log.Warnf("Failed to close connection: %v", err) } } -// fakePacketConn wraps a net.Conn and emulates net.PacketConn -type fakePacketConn struct { - nextConn net.Conn -} - -func (f *fakePacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - n, err = f.nextConn.Read(p) - addr = f.nextConn.RemoteAddr() - return -} -func (f *fakePacketConn) Close() error { return f.nextConn.Close() } -func (f *fakePacketConn) LocalAddr() net.Addr { return f.nextConn.LocalAddr() } -func (f *fakePacketConn) SetDeadline(t time.Time) error { return f.nextConn.SetDeadline(t) } -func (f *fakePacketConn) SetReadDeadline(t time.Time) error { return f.nextConn.SetReadDeadline(t) } -func (f *fakePacketConn) SetWriteDeadline(t time.Time) error { return f.nextConn.SetWriteDeadline(t) } -func (f *fakePacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - return f.nextConn.Write(p) -} - // GatherCandidates initiates the trickle based gathering process. func (a *Agent) GatherCandidates() error { var gatherErr error @@ -71,17 +54,20 @@ func (a *Agent) GatherCandidates() error { a.gatherCandidateCancel() // Cancel previous gathering routine ctx, cancel := context.WithCancel(ctx) a.gatherCandidateCancel = cancel + done := make(chan struct{}) + a.gatherCandidateDone = done - go a.gatherCandidates(ctx) + go a.gatherCandidates(ctx, done) }); runErr != nil { return runErr } return gatherErr } -func (a *Agent) gatherCandidates(ctx context.Context) { - if err := a.setGatheringState(GatheringStateGathering); err != nil { - a.log.Warnf("failed to set gatheringState to GatheringStateGathering: %v", err) +func (a *Agent) gatherCandidates(ctx context.Context, done chan struct{}) { + defer close(done) + if err := a.setGatheringState(GatheringStateGathering); err != nil { //nolint:contextcheck + a.log.Warnf("Failed to set gatheringState to GatheringStateGathering: %v", err) return } @@ -97,7 +83,11 @@ func (a *Agent) gatherCandidates(ctx context.Context) { case CandidateTypeServerReflexive: wg.Add(1) go func() { - a.gatherCandidatesSrflx(ctx, a.urls, a.networkTypes) + if a.udpMuxSrflx != nil { + a.gatherCandidatesSrflxUDPMux(ctx, a.urls, a.networkTypes) + } else { + a.gatherCandidatesSrflx(ctx, a.urls, a.networkTypes) + } wg.Done() }() if a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeServerReflexive { @@ -116,11 +106,12 @@ func (a *Agent) gatherCandidates(ctx context.Context) { case CandidateTypePeerReflexive, CandidateTypeUnspecified: } } + // Block until all STUN and TURN URLs have been gathered (or timed out) wg.Wait() - if err := a.setGatheringState(GatheringStateComplete); err != nil { - a.log.Warnf("failed to set gatheringState to GatheringStateComplete: %v", err) + if err := a.setGatheringState(GatheringStateComplete); err != nil { //nolint:contextcheck + a.log.Warnf("Failed to set gatheringState to GatheringStateComplete: %v", err) } } @@ -134,27 +125,27 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ } } - // when UDPMux is enabled, skip other UDP candidates + // When UDPMux is enabled, skip other UDP candidates if a.udpMux != nil { if err := a.gatherCandidatesLocalUDPMux(ctx); err != nil { - a.log.Warnf("could not create host candidate for UDPMux") + a.log.Warnf("Failed to create host candidate for UDPMux: %s", err) } delete(networks, udp) } - localIPs, err := localInterfaces(a.net, a.interfaceFilter, networkTypes) + localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, networkTypes, a.includeLoopback) if err != nil { - a.log.Warnf("failed to iterate local interfaces, host candidates will not be gathered %s", err) + a.log.Warnf("Failed to iterate local interfaces, host candidates will not be gathered %s", err) return } for _, ip := range localIPs { mappedIP := ip if a.mDNSMode != MulticastDNSModeQueryAndGather && a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeHost { - if _mappedIP, err := a.extIPMapper.findExternalIP(ip.String()); err == nil { + if _mappedIP, innerErr := a.extIPMapper.findExternalIP(ip.String()); innerErr == nil { mappedIP = _mappedIP } else { - a.log.Warnf("1:1 NAT mapping is enabled but no external IP is found for %s\n", ip.String()) + a.log.Warnf("1:1 NAT mapping is enabled but no external IP is found for %s", ip.String()) } } @@ -164,115 +155,163 @@ func (a *Agent) gatherCandidatesLocal(ctx context.Context, networkTypes []Networ } for network := range networks { - var port int - var conn net.PacketConn - var err error - var tcpType TCPType + type connAndPort struct { + conn net.PacketConn + port int + } + var ( + conns []connAndPort + tcpType TCPType + ) switch network { case tcp: - // Handle ICE TCP passive mode - a.log.Debugf("GetConn by ufrag: %s\n", a.localUfrag) - conn, err = a.tcpMux.GetConnByUfrag(a.localUfrag) - if err != nil { - if !errors.Is(err, ErrTCPMuxNotInitialized) { - a.log.Warnf("error getting tcp conn by ufrag: %s %s %s\n", network, ip, a.localUfrag) - } + if a.tcpMux == nil { + continue + } + + // Handle ICE TCP passive mode + var muxConns []net.PacketConn + if multi, ok := a.tcpMux.(AllConnsGetter); ok { + a.log.Debugf("GetAllConns by ufrag: %s", a.localUfrag) + muxConns, err = multi.GetAllConns(a.localUfrag, mappedIP.To4() == nil, ip) + if err != nil { + a.log.Warnf("Failed to get all TCP connections by ufrag: %s %s %s", network, ip, a.localUfrag) + continue + } + } else { + a.log.Debugf("GetConn by ufrag: %s", a.localUfrag) + conn, err := a.tcpMux.GetConnByUfrag(a.localUfrag, mappedIP.To4() == nil, ip) + if err != nil { + a.log.Warnf("Failed to get TCP connections by ufrag: %s %s %s", network, ip, a.localUfrag) + continue + } + muxConns = []net.PacketConn{conn} + } + + // Extract the port for each PacketConn we got. + for _, conn := range muxConns { + if tcpConn, ok := conn.LocalAddr().(*net.TCPAddr); ok { + conns = append(conns, connAndPort{conn, tcpConn.Port}) + } else { + a.log.Warnf("Failed to get port of connection from TCPMux: %s %s %s", network, ip, a.localUfrag) + } + } + if len(conns) == 0 { + // Didn't succeed with any, try the next network. continue } - port = conn.LocalAddr().(*net.TCPAddr).Port tcpType = TCPTypePassive - // is there a way to verify that the listen address is even + // Is there a way to verify that the listen address is even // accessible from the current interface. case udp: - conn, err = listenUDPInPortRange(a.net, a.log, int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: ip, Port: 0}) + conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{IP: ip, Port: 0}) if err != nil { - a.log.Warnf("could not listen %s %s\n", network, ip) + a.log.Warnf("Failed to listen %s %s", network, ip) continue } - port = conn.LocalAddr().(*net.UDPAddr).Port - } - hostConfig := CandidateHostConfig{ - Network: network, - Address: address, - Port: port, - Component: ComponentRTP, - TCPType: tcpType, - } - - c, err := NewCandidateHost(&hostConfig) - if err != nil { - closeConnAndLog(conn, a.log, fmt.Sprintf("Failed to create host candidate: %s %s %d: %v\n", network, mappedIP, port, err)) - continue - } - - if a.mDNSMode == MulticastDNSModeQueryAndGather { - if err = c.setIP(ip); err != nil { - closeConnAndLog(conn, a.log, fmt.Sprintf("Failed to create host candidate: %s %s %d: %v\n", network, mappedIP, port, err)) + if udpConn, ok := conn.LocalAddr().(*net.UDPAddr); ok { + conns = append(conns, connAndPort{conn, udpConn.Port}) + } else { + a.log.Warnf("Failed to get port of UDPAddr from ListenUDPInPortRange: %s %s %s", network, ip, a.localUfrag) continue } } - if err := a.addCandidate(ctx, c, conn); err != nil { - if closeErr := c.close(); closeErr != nil { - a.log.Warnf("Failed to close candidate: %v", closeErr) + for _, connAndPort := range conns { + hostConfig := CandidateHostConfig{ + Network: network, + Address: address, + Port: connAndPort.port, + Component: ComponentRTP, + TCPType: tcpType, + } + + c, err := NewCandidateHost(&hostConfig) + if err != nil { + closeConnAndLog(connAndPort.conn, a.log, "failed to create host candidate: %s %s %d: %v", network, mappedIP, connAndPort.port, err) + continue + } + + if a.mDNSMode == MulticastDNSModeQueryAndGather { + if err = c.setIP(ip); err != nil { + closeConnAndLog(connAndPort.conn, a.log, "failed to create host candidate: %s %s %d: %v", network, mappedIP, connAndPort.port, err) + continue + } + } + + if err := a.addCandidate(ctx, c, connAndPort.conn); err != nil { + if closeErr := c.close(); closeErr != nil { + a.log.Warnf("Failed to close candidate: %v", closeErr) + } + a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v", err) } - a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v\n", err) } } } } -func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { +func (a *Agent) gatherCandidatesLocalUDPMux(ctx context.Context) error { //nolint:gocognit if a.udpMux == nil { return errUDPMuxDisabled } - localIPs, err := localInterfaces(a.net, a.interfaceFilter, []NetworkType{NetworkTypeUDP4}) - switch { - case err != nil: - return err - case len(localIPs) == 0: - return errCandidateIPNotFound - } + localAddresses := a.udpMux.GetListenAddresses() + existingConfigs := make(map[CandidateHostConfig]struct{}) - for _, candidateIP := range localIPs { + for _, addr := range localAddresses { + udpAddr, ok := addr.(*net.UDPAddr) + if !ok { + return errInvalidAddress + } + candidateIP := udpAddr.IP if a.extIPMapper != nil && a.extIPMapper.candidateType == CandidateTypeHost { - if mappedIP, err := a.extIPMapper.findExternalIP(candidateIP.String()); err != nil { + mappedIP, err := a.extIPMapper.findExternalIP(candidateIP.String()) + if err != nil { a.log.Warnf("1:1 NAT mapping is enabled but no external IP is found for %s", candidateIP.String()) continue - } else { - candidateIP = mappedIP } - } - conn, err := a.udpMux.GetConn(a.localUfrag) - if err != nil { - return err + candidateIP = mappedIP } - port := conn.LocalAddr().(*net.UDPAddr).Port hostConfig := CandidateHostConfig{ Network: udp, Address: candidateIP.String(), - Port: port, + Port: udpAddr.Port, Component: ComponentRTP, } + // Detect a duplicate candidate before calling addCandidate(). + // otherwise, addCandidate() detects the duplicate candidate + // and close its connection, invalidating all candidates + // that share the same connection. + if _, ok := existingConfigs[hostConfig]; ok { + continue + } + + conn, err := a.udpMux.GetConn(a.localUfrag, udpAddr) + if err != nil { + return err + } + c, err := NewCandidateHost(&hostConfig) if err != nil { - closeConnAndLog(conn, a.log, fmt.Sprintf("Failed to create host mux candidate: %s %d: %v\n", candidateIP, port, err)) - // already logged error - return nil + closeConnAndLog(conn, a.log, "failed to create host mux candidate: %s %d: %v", candidateIP, udpAddr.Port, err) + continue } if err := a.addCandidate(ctx, c, conn); err != nil { if closeErr := c.close(); closeErr != nil { a.log.Warnf("Failed to close candidate: %v", closeErr) } - return err + + closeConnAndLog(conn, a.log, "failed to add candidate: %s %d: %v", candidateIP, udpAddr.Port, err) + continue } + + existingConfigs[hostConfig] = struct{}{} } return nil @@ -292,34 +331,39 @@ func (a *Agent) gatherCandidatesSrflxMapped(ctx context.Context, networkTypes [] go func() { defer wg.Done() - conn, err := listenUDPInPortRange(a.net, a.log, int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: nil, Port: 0}) + conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{IP: nil, Port: 0}) if err != nil { - a.log.Warnf("Failed to listen %s: %v\n", network, err) + a.log.Warnf("Failed to listen %s: %v", network, err) return } - laddr := conn.LocalAddr().(*net.UDPAddr) - mappedIP, err := a.extIPMapper.findExternalIP(laddr.IP.String()) + lAddr, ok := conn.LocalAddr().(*net.UDPAddr) + if !ok { + closeConnAndLog(conn, a.log, "1:1 NAT mapping is enabled but LocalAddr is not a UDPAddr") + return + } + + mappedIP, err := a.extIPMapper.findExternalIP(lAddr.IP.String()) if err != nil { - closeConnAndLog(conn, a.log, fmt.Sprintf("1:1 NAT mapping is enabled but no external IP is found for %s\n", laddr.IP.String())) + closeConnAndLog(conn, a.log, "1:1 NAT mapping is enabled but no external IP is found for %s", lAddr.IP.String()) return } srflxConfig := CandidateServerReflexiveConfig{ Network: network, Address: mappedIP.String(), - Port: laddr.Port, + Port: lAddr.Port, Component: ComponentRTP, - RelAddr: laddr.IP.String(), - RelPort: laddr.Port, + RelAddr: lAddr.IP.String(), + RelPort: lAddr.Port, } c, err := NewCandidateServerReflexive(&srflxConfig) if err != nil { - closeConnAndLog(conn, a.log, fmt.Sprintf("Failed to create server reflexive candidate: %s %s %d: %v\n", + closeConnAndLog(conn, a.log, "failed to create server reflexive candidate: %s %s %d: %v", network, mappedIP.String(), - laddr.Port, - err)) + lAddr.Port, + err) return } @@ -327,13 +371,81 @@ func (a *Agent) gatherCandidatesSrflxMapped(ctx context.Context, networkTypes [] if closeErr := c.close(); closeErr != nil { a.log.Warnf("Failed to close candidate: %v", closeErr) } - a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v\n", err) + a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v", err) } }() } } -func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*URL, networkTypes []NetworkType) { +func (a *Agent) gatherCandidatesSrflxUDPMux(ctx context.Context, urls []*stun.URI, networkTypes []NetworkType) { //nolint:gocognit + var wg sync.WaitGroup + defer wg.Wait() + + for _, networkType := range networkTypes { + if networkType.IsTCP() { + continue + } + + for i := range urls { + for _, listenAddr := range a.udpMuxSrflx.GetListenAddresses() { + udpAddr, ok := listenAddr.(*net.UDPAddr) + if !ok { + a.log.Warn("Failed to cast udpMuxSrflx listen address to UDPAddr") + continue + } + wg.Add(1) + go func(url stun.URI, network string, localAddr *net.UDPAddr) { + defer wg.Done() + + hostPort := fmt.Sprintf("%s:%d", url.Host, url.Port) + serverAddr, err := a.net.ResolveUDPAddr(network, hostPort) + if err != nil { + a.log.Warnf("Failed to resolve STUN host: %s: %v", hostPort, err) + return + } + + xorAddr, err := a.udpMuxSrflx.GetXORMappedAddr(serverAddr, stunGatherTimeout) + if err != nil { + a.log.Warnf("Failed get server reflexive address %s %s: %v", network, url, err) + return + } + + conn, err := a.udpMuxSrflx.GetConnForURL(a.localUfrag, url.String(), localAddr) + if err != nil { + a.log.Warnf("Failed to find connection in UDPMuxSrflx %s %s: %v", network, url, err) + return + } + + ip := xorAddr.IP + port := xorAddr.Port + + srflxConfig := CandidateServerReflexiveConfig{ + Network: network, + Address: ip.String(), + Port: port, + Component: ComponentRTP, + RelAddr: localAddr.IP.String(), + RelPort: localAddr.Port, + } + c, err := NewCandidateServerReflexive(&srflxConfig) + if err != nil { + closeConnAndLog(conn, a.log, "failed to create server reflexive candidate: %s %s %d: %v", network, ip, port, err) + return + } + + if err := a.addCandidate(ctx, c, conn); err != nil { + if closeErr := c.close(); closeErr != nil { + a.log.Warnf("Failed to close candidate: %v", closeErr) + } + a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v", err) + } + }(*urls[i], networkType.String(), udpAddr) + } + } + } +} + +func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*stun.URI, networkTypes []NetworkType) { //nolint:gocognit var wg sync.WaitGroup defer wg.Wait() @@ -344,43 +456,55 @@ func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*URL, networkT for i := range urls { wg.Add(1) - go func(url URL, network string) { + go func(url stun.URI, network string) { defer wg.Done() hostPort := fmt.Sprintf("%s:%d", url.Host, url.Port) serverAddr, err := a.net.ResolveUDPAddr(network, hostPort) if err != nil { - a.log.Warnf("failed to resolve stun host: %s: %v", hostPort, err) + a.log.Warnf("Failed to resolve STUN host: %s: %v", hostPort, err) return } - conn, err := listenUDPInPortRange(a.net, a.log, int(a.portmax), int(a.portmin), network, &net.UDPAddr{IP: nil, Port: 0}) + conn, err := listenUDPInPortRange(a.net, a.log, int(a.portMax), int(a.portMin), network, &net.UDPAddr{IP: nil, Port: 0}) if err != nil { - closeConnAndLog(conn, a.log, fmt.Sprintf("Failed to listen for %s: %v\n", serverAddr.String(), err)) + closeConnAndLog(conn, a.log, "failed to listen for %s: %v", serverAddr.String(), err) return } + // If the agent closes midway through the connection + // we end it early to prevent close delay. + cancelCtx, cancelFunc := context.WithCancel(ctx) + defer cancelFunc() + go func() { + select { + case <-cancelCtx.Done(): + return + case <-a.done: + _ = conn.Close() + } + }() - xoraddr, err := getXORMappedAddr(conn, serverAddr, stunGatherTimeout) + xorAddr, err := stunx.GetXORMappedAddr(conn, serverAddr, stunGatherTimeout) if err != nil { - closeConnAndLog(conn, a.log, fmt.Sprintf("could not get server reflexive address %s %s: %v\n", network, url, err)) + closeConnAndLog(conn, a.log, "failed to get server reflexive address %s %s: %v", network, url, err) return } - ip := xoraddr.IP - port := xoraddr.Port + ip := xorAddr.IP + port := xorAddr.Port - laddr := conn.LocalAddr().(*net.UDPAddr) + lAddr := conn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert srflxConfig := CandidateServerReflexiveConfig{ Network: network, Address: ip.String(), Port: port, Component: ComponentRTP, - RelAddr: laddr.IP.String(), - RelPort: laddr.Port, + RelAddr: lAddr.IP.String(), + RelPort: lAddr.Port, } c, err := NewCandidateServerReflexive(&srflxConfig) if err != nil { - closeConnAndLog(conn, a.log, fmt.Sprintf("Failed to create server reflexive candidate: %s %s %d: %v\n", network, ip, port, err)) + closeConnAndLog(conn, a.log, "failed to create server reflexive candidate: %s %s %d: %v", network, ip, port, err) return } @@ -388,21 +512,21 @@ func (a *Agent) gatherCandidatesSrflx(ctx context.Context, urls []*URL, networkT if closeErr := c.close(); closeErr != nil { a.log.Warnf("Failed to close candidate: %v", closeErr) } - a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v\n", err) + a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v", err) } }(*urls[i], networkType.String()) } } } -func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*URL) { //nolint:gocognit +func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*stun.URI) { //nolint:gocognit var wg sync.WaitGroup defer wg.Wait() network := NetworkTypeUDP4.String() for i := range urls { switch { - case urls[i].Scheme != SchemeTypeTURN && urls[i].Scheme != SchemeTypeTURNS: + case urls[i].Scheme != stun.SchemeTypeTURN && urls[i].Scheme != stun.SchemeTypeTURNS: continue case urls[i].Username == "": a.log.Errorf("Failed to gather relay candidates: %v", ErrUsernameEmpty) @@ -413,100 +537,124 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*URL) { //noli } wg.Add(1) - go func(url URL) { + go func(url stun.URI) { defer wg.Done() - TURNServerAddr := fmt.Sprintf("%s:%d", url.Host, url.Port) + turnServerAddr := fmt.Sprintf("%s:%d", url.Host, url.Port) var ( locConn net.PacketConn err error - RelAddr string - RelPort int + relAddr string + relPort int relayProtocol string ) switch { - case url.Proto == ProtoTypeUDP && url.Scheme == SchemeTypeTURN: + case url.Proto == stun.ProtoTypeUDP && url.Scheme == stun.SchemeTypeTURN: if locConn, err = a.net.ListenPacket(network, "0.0.0.0:0"); err != nil { - a.log.Warnf("Failed to listen %s: %v\n", network, err) + a.log.Warnf("Failed to listen %s: %v", network, err) return } - RelAddr = locConn.LocalAddr().(*net.UDPAddr).IP.String() - RelPort = locConn.LocalAddr().(*net.UDPAddr).Port + relAddr = locConn.LocalAddr().(*net.UDPAddr).IP.String() //nolint:forcetypeassert + relPort = locConn.LocalAddr().(*net.UDPAddr).Port //nolint:forcetypeassert relayProtocol = udp - case a.proxyDialer != nil && url.Proto == ProtoTypeTCP && - (url.Scheme == SchemeTypeTURN || url.Scheme == SchemeTypeTURNS): - conn, connectErr := a.proxyDialer.Dial(NetworkTypeTCP4.String(), TURNServerAddr) + case a.proxyDialer != nil && url.Proto == stun.ProtoTypeTCP && + (url.Scheme == stun.SchemeTypeTURN || url.Scheme == stun.SchemeTypeTURNS): + conn, connectErr := a.proxyDialer.Dial(NetworkTypeTCP4.String(), turnServerAddr) if connectErr != nil { - a.log.Warnf("Failed to Dial TCP Addr %s via proxy dialer: %v\n", TURNServerAddr, connectErr) + a.log.Warnf("Failed to dial TCP address %s via proxy dialer: %v", turnServerAddr, connectErr) return } - RelAddr = conn.LocalAddr().(*net.TCPAddr).IP.String() - RelPort = conn.LocalAddr().(*net.TCPAddr).Port - if url.Scheme == SchemeTypeTURN { + relAddr = conn.LocalAddr().(*net.TCPAddr).IP.String() //nolint:forcetypeassert + relPort = conn.LocalAddr().(*net.TCPAddr).Port //nolint:forcetypeassert + if url.Scheme == stun.SchemeTypeTURN { relayProtocol = tcp - } else if url.Scheme == SchemeTypeTURNS { + } else if url.Scheme == stun.SchemeTypeTURNS { relayProtocol = "tls" } locConn = turn.NewSTUNConn(conn) - case url.Proto == ProtoTypeTCP && url.Scheme == SchemeTypeTURN: - tcpAddr, connectErr := net.ResolveTCPAddr(NetworkTypeTCP4.String(), TURNServerAddr) + case url.Proto == stun.ProtoTypeTCP && url.Scheme == stun.SchemeTypeTURN: + tcpAddr, connectErr := a.net.ResolveTCPAddr(NetworkTypeTCP4.String(), turnServerAddr) if connectErr != nil { - a.log.Warnf("Failed to resolve TCP Addr %s: %v\n", TURNServerAddr, connectErr) + a.log.Warnf("Failed to resolve TCP address %s: %v", turnServerAddr, connectErr) return } - conn, connectErr := net.DialTCP(NetworkTypeTCP4.String(), nil, tcpAddr) + conn, connectErr := a.net.DialTCP(NetworkTypeTCP4.String(), nil, tcpAddr) if connectErr != nil { - a.log.Warnf("Failed to Dial TCP Addr %s: %v\n", TURNServerAddr, connectErr) + a.log.Warnf("Failed to dial TCP address %s: %v", turnServerAddr, connectErr) return } - RelAddr = conn.LocalAddr().(*net.TCPAddr).IP.String() - RelPort = conn.LocalAddr().(*net.TCPAddr).Port + relAddr = conn.LocalAddr().(*net.TCPAddr).IP.String() //nolint:forcetypeassert + relPort = conn.LocalAddr().(*net.TCPAddr).Port //nolint:forcetypeassert relayProtocol = tcp locConn = turn.NewSTUNConn(conn) - case url.Proto == ProtoTypeUDP && url.Scheme == SchemeTypeTURNS: - udpAddr, connectErr := net.ResolveUDPAddr(network, TURNServerAddr) + case url.Proto == stun.ProtoTypeUDP && url.Scheme == stun.SchemeTypeTURNS: + udpAddr, connectErr := a.net.ResolveUDPAddr(network, turnServerAddr) if connectErr != nil { - a.log.Warnf("Failed to resolve UDP Addr %s: %v\n", TURNServerAddr, connectErr) + a.log.Warnf("Failed to resolve UDP address %s: %v", turnServerAddr, connectErr) return } - conn, connectErr := dtls.Dial(network, udpAddr, &dtls.Config{ + udpConn, dialErr := a.net.DialUDP("udp", nil, udpAddr) + if dialErr != nil { + a.log.Warnf("Failed to dial DTLS address %s: %v", turnServerAddr, dialErr) + return + } + + conn, connectErr := dtls.ClientWithContext(ctx, udpConn, &dtls.Config{ ServerName: url.Host, InsecureSkipVerify: a.insecureSkipVerify, //nolint:gosec }) if connectErr != nil { - a.log.Warnf("Failed to Dial DTLS Addr %s: %v\n", TURNServerAddr, connectErr) + a.log.Warnf("Failed to create DTLS client: %v", turnServerAddr, connectErr) return } - RelAddr = conn.LocalAddr().(*net.UDPAddr).IP.String() - RelPort = conn.LocalAddr().(*net.UDPAddr).Port + relAddr = conn.LocalAddr().(*net.UDPAddr).IP.String() //nolint:forcetypeassert + relPort = conn.LocalAddr().(*net.UDPAddr).Port //nolint:forcetypeassert relayProtocol = "dtls" - locConn = &fakePacketConn{conn} - case url.Proto == ProtoTypeTCP && url.Scheme == SchemeTypeTURNS: - conn, connectErr := tls.Dial(NetworkTypeTCP4.String(), TURNServerAddr, &tls.Config{ - InsecureSkipVerify: a.insecureSkipVerify, //nolint:gosec - }) - if connectErr != nil { - a.log.Warnf("Failed to Dial TLS Addr %s: %v\n", TURNServerAddr, connectErr) + locConn = &fakenet.PacketConn{Conn: conn} + case url.Proto == stun.ProtoTypeTCP && url.Scheme == stun.SchemeTypeTURNS: + tcpAddr, resolvErr := a.net.ResolveTCPAddr(NetworkTypeTCP4.String(), turnServerAddr) + if resolvErr != nil { + a.log.Warnf("Failed to resolve relay address %s: %v", turnServerAddr, resolvErr) return } - RelAddr = conn.LocalAddr().(*net.TCPAddr).IP.String() - RelPort = conn.LocalAddr().(*net.TCPAddr).Port + + tcpConn, dialErr := a.net.DialTCP(NetworkTypeTCP4.String(), nil, tcpAddr) + if dialErr != nil { + a.log.Warnf("Failed to connect to relay: %v", dialErr) + return + } + + conn := tls.Client(tcpConn, &tls.Config{ + ServerName: url.Host, + InsecureSkipVerify: a.insecureSkipVerify, //nolint:gosec + }) + + if hsErr := conn.HandshakeContext(ctx); hsErr != nil { + if closeErr := tcpConn.Close(); closeErr != nil { + a.log.Errorf("Failed to close relay connection: %v", closeErr) + } + a.log.Warnf("Failed to connect to relay: %v", hsErr) + return + } + + relAddr = conn.LocalAddr().(*net.TCPAddr).IP.String() //nolint:forcetypeassert + relPort = conn.LocalAddr().(*net.TCPAddr).Port //nolint:forcetypeassert relayProtocol = "tls" locConn = turn.NewSTUNConn(conn) default: - a.log.Warnf("Unable to handle URL in gatherCandidatesRelay %v\n", url) + a.log.Warnf("Unable to handle URL in gatherCandidatesRelay %v", url) return } client, err := turn.NewClient(&turn.ClientConfig{ - TURNServerAddr: TURNServerAddr, + TURNServerAddr: turnServerAddr, Conn: locConn, Username: url.Username, Password: url.Password, @@ -514,31 +662,31 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*URL) { //noli Net: a.net, }) if err != nil { - closeConnAndLog(locConn, a.log, fmt.Sprintf("Failed to build new turn.Client %s %s\n", TURNServerAddr, err)) + closeConnAndLog(locConn, a.log, "failed to create new TURN client %s %s", turnServerAddr, err) return } if err = client.Listen(); err != nil { client.Close() - closeConnAndLog(locConn, a.log, fmt.Sprintf("Failed to listen on turn.Client %s %s\n", TURNServerAddr, err)) + closeConnAndLog(locConn, a.log, "failed to listen on TURN client %s %s", turnServerAddr, err) return } relayConn, err := client.Allocate() if err != nil { client.Close() - closeConnAndLog(locConn, a.log, fmt.Sprintf("Failed to allocate on turn.Client %s %s\n", TURNServerAddr, err)) + closeConnAndLog(locConn, a.log, "failed to allocate on TURN client %s %s", turnServerAddr, err) return } - raddr := relayConn.LocalAddr().(*net.UDPAddr) + rAddr := relayConn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert relayConfig := CandidateRelayConfig{ Network: network, Component: ComponentRTP, - Address: raddr.IP.String(), - Port: raddr.Port, - RelAddr: RelAddr, - RelPort: RelPort, + Address: rAddr.IP.String(), + Port: rAddr.Port, + RelAddr: relAddr, + RelPort: relPort, RelayProtocol: relayProtocol, OnClose: func() error { client.Close() @@ -555,7 +703,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*URL) { //noli relayConnClose() client.Close() - closeConnAndLog(locConn, a.log, fmt.Sprintf("Failed to create relay candidate: %s %s: %v\n", network, raddr.String(), err)) + closeConnAndLog(locConn, a.log, "failed to create relay candidate: %s %s: %v", network, rAddr.String(), err) return } @@ -565,7 +713,7 @@ func (a *Agent) gatherCandidatesRelay(ctx context.Context, urls []*URL) { //noli if closeErr := candidate.close(); closeErr != nil { a.log.Warnf("Failed to close candidate: %v", closeErr) } - a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v\n", err) + a.log.Warnf("Failed to append to localCandidates and run onCandidateHdlr: %v", err) } }(*urls[i]) } diff --git a/vendor/github.com/pion/ice/v2/ice.go b/vendor/github.com/pion/ice/v2/ice.go index d7094f6db..bd551206e 100644 --- a/vendor/github.com/pion/ice/v2/ice.go +++ b/vendor/github.com/pion/ice/v2/ice.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice // ConnectionState is an enum showing the state of a ICE Connection @@ -5,8 +8,11 @@ type ConnectionState int // List of supported States const ( + // ConnectionStateUnknown represents an unknown state + ConnectionStateUnknown ConnectionState = iota + // ConnectionStateNew ICE agent is gathering addresses - ConnectionStateNew = iota + 1 + ConnectionStateNew // ConnectionStateChecking ICE agent has been given local and remote candidates, and is attempting to find a match ConnectionStateChecking @@ -52,13 +58,16 @@ func (c ConnectionState) String() string { type GatheringState int const ( - // GatheringStateNew indicates candidate gatering is not yet started - GatheringStateNew GatheringState = iota + 1 + // GatheringStateUnknown represents an unknown state + GatheringStateUnknown GatheringState = iota - // GatheringStateGathering indicates candidate gatering is ongoing + // GatheringStateNew indicates candidate gathering is not yet started + GatheringStateNew + + // GatheringStateGathering indicates candidate gathering is ongoing GatheringStateGathering - // GatheringStateComplete indicates candidate gatering has been completed + // GatheringStateComplete indicates candidate gathering has been completed GatheringStateComplete ) diff --git a/vendor/github.com/pion/ice/v2/icecontrol.go b/vendor/github.com/pion/ice/v2/icecontrol.go index ede2e09cb..b086bd892 100644 --- a/vendor/github.com/pion/ice/v2/icecontrol.go +++ b/vendor/github.com/pion/ice/v2/icecontrol.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( diff --git a/vendor/github.com/pion/ice/v2/internal/atomic/atomic.go b/vendor/github.com/pion/ice/v2/internal/atomic/atomic.go new file mode 100644 index 000000000..f8caf5a2e --- /dev/null +++ b/vendor/github.com/pion/ice/v2/internal/atomic/atomic.go @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package atomic contains custom atomic types +package atomic + +import "sync/atomic" + +// Error is an atomic error +type Error struct { + v atomic.Value +} + +// Store updates the value of the atomic variable +func (a *Error) Store(err error) { + a.v.Store(struct{ error }{err}) +} + +// Load retrieves the current value of the atomic variable +func (a *Error) Load() error { + err, _ := a.v.Load().(struct{ error }) + return err.error +} diff --git a/vendor/github.com/pion/ice/v2/internal/fakenet/mock_conn.go b/vendor/github.com/pion/ice/v2/internal/fakenet/mock_conn.go new file mode 100644 index 000000000..cc98849d5 --- /dev/null +++ b/vendor/github.com/pion/ice/v2/internal/fakenet/mock_conn.go @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +//go:build !js +// +build !js + +package fakenet + +import ( + "net" + "time" +) + +// MockPacketConn for tests +type MockPacketConn struct{} + +func (m *MockPacketConn) ReadFrom([]byte) (n int, addr net.Addr, err error) { return 0, nil, nil } //nolint:revive +func (m *MockPacketConn) WriteTo([]byte, net.Addr) (n int, err error) { return 0, nil } //nolint:revive +func (m *MockPacketConn) Close() error { return nil } //nolint:revive +func (m *MockPacketConn) LocalAddr() net.Addr { return nil } //nolint:revive +func (m *MockPacketConn) SetDeadline(time.Time) error { return nil } //nolint:revive +func (m *MockPacketConn) SetReadDeadline(time.Time) error { return nil } //nolint:revive +func (m *MockPacketConn) SetWriteDeadline(time.Time) error { return nil } //nolint:revive diff --git a/vendor/github.com/pion/ice/v2/internal/fakenet/packet_conn.go b/vendor/github.com/pion/ice/v2/internal/fakenet/packet_conn.go new file mode 100644 index 000000000..0b9faaa84 --- /dev/null +++ b/vendor/github.com/pion/ice/v2/internal/fakenet/packet_conn.go @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package fakenet contains fake network abstractions +package fakenet + +import ( + "net" +) + +// Compile-time assertion +var _ net.PacketConn = (*PacketConn)(nil) + +// PacketConn wraps a net.Conn and emulates net.PacketConn +type PacketConn struct { + net.Conn +} + +// ReadFrom reads a packet from the connection, +func (f *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + n, err = f.Conn.Read(p) + addr = f.Conn.RemoteAddr() + return +} + +// WriteTo writes a packet with payload p to addr. +func (f *PacketConn) WriteTo(p []byte, _ net.Addr) (int, error) { + return f.Conn.Write(p) +} diff --git a/vendor/github.com/pion/ice/v2/internal/stun/stun.go b/vendor/github.com/pion/ice/v2/internal/stun/stun.go new file mode 100644 index 000000000..230cf850a --- /dev/null +++ b/vendor/github.com/pion/ice/v2/internal/stun/stun.go @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package stun contains ICE specific STUN code +package stun + +import ( + "errors" + "fmt" + "net" + "time" + + "github.com/pion/stun" +) + +var ( + errGetXorMappedAddrResponse = errors.New("failed to get XOR-MAPPED-ADDRESS response") + errMismatchUsername = errors.New("username mismatch") +) + +// GetXORMappedAddr initiates a STUN requests to serverAddr using conn, reads the response and returns +// the XORMappedAddress returned by the STUN server. +func GetXORMappedAddr(conn net.PacketConn, serverAddr net.Addr, timeout time.Duration) (*stun.XORMappedAddress, error) { + if timeout > 0 { + if err := conn.SetReadDeadline(time.Now().Add(timeout)); err != nil { + return nil, err + } + + // Reset timeout after completion + defer conn.SetReadDeadline(time.Time{}) //nolint:errcheck + } + + req, err := stun.Build(stun.BindingRequest, stun.TransactionID) + if err != nil { + return nil, err + } + + if _, err = conn.WriteTo(req.Raw, serverAddr); err != nil { + return nil, err + } + + const maxMessageSize = 1280 + buf := make([]byte, maxMessageSize) + n, _, err := conn.ReadFrom(buf) + if err != nil { + return nil, err + } + + res := &stun.Message{Raw: buf[:n]} + if err = res.Decode(); err != nil { + return nil, err + } + + var addr stun.XORMappedAddress + if err = addr.GetFrom(res); err != nil { + return nil, fmt.Errorf("%w: %v", errGetXorMappedAddrResponse, err) //nolint:errorlint + } + + return &addr, nil +} + +// AssertUsername checks that the given STUN message m has a USERNAME attribute with a given value +func AssertUsername(m *stun.Message, expectedUsername string) error { + var username stun.Username + if err := username.GetFrom(m); err != nil { + return err + } else if string(username) != expectedUsername { + return fmt.Errorf("%w expected(%x) actual(%x)", errMismatchUsername, expectedUsername, string(username)) + } + + return nil +} diff --git a/vendor/github.com/pion/ice/v2/mdns.go b/vendor/github.com/pion/ice/v2/mdns.go index 5a431d152..aa8231648 100644 --- a/vendor/github.com/pion/ice/v2/mdns.go +++ b/vendor/github.com/pion/ice/v2/mdns.go @@ -1,11 +1,13 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( - "net" - "github.com/google/uuid" "github.com/pion/logging" "github.com/pion/mdns" + "github.com/pion/transport/v2" "golang.org/x/net/ipv4" ) @@ -31,17 +33,17 @@ func generateMulticastDNSName() (string, error) { return u.String() + ".local", err } -func createMulticastDNS(mDNSMode MulticastDNSMode, mDNSName string, log logging.LeveledLogger) (*mdns.Conn, MulticastDNSMode, error) { +func createMulticastDNS(n transport.Net, mDNSMode MulticastDNSMode, mDNSName string, log logging.LeveledLogger) (*mdns.Conn, MulticastDNSMode, error) { if mDNSMode == MulticastDNSModeDisabled { return nil, mDNSMode, nil } - addr, mdnsErr := net.ResolveUDPAddr("udp4", mdns.DefaultAddress) + addr, mdnsErr := n.ResolveUDPAddr("udp4", mdns.DefaultAddress) if mdnsErr != nil { return nil, mDNSMode, mdnsErr } - l, mdnsErr := net.ListenUDP("udp4", addr) + l, mdnsErr := n.ListenUDP("udp4", addr) if mdnsErr != nil { // If ICE fails to start MulticastDNS server just warn the user and continue log.Errorf("Failed to enable mDNS, continuing in mDNS disabled mode: (%s)", mdnsErr) diff --git a/vendor/github.com/pion/ice/v2/net.go b/vendor/github.com/pion/ice/v2/net.go new file mode 100644 index 000000000..d716bc19c --- /dev/null +++ b/vendor/github.com/pion/ice/v2/net.go @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package ice + +import ( + "net" + + "github.com/pion/logging" + "github.com/pion/transport/v2" +) + +// The conditions of invalidation written below are defined in +// https://tools.ietf.org/html/rfc8445#section-5.1.1.1 +func isSupportedIPv6(ip net.IP) bool { + if len(ip) != net.IPv6len || + isZeros(ip[0:12]) || // !(IPv4-compatible IPv6) + ip[0] == 0xfe && ip[1]&0xc0 == 0xc0 || // !(IPv6 site-local unicast) + ip.IsLinkLocalUnicast() || + ip.IsLinkLocalMulticast() { + return false + } + return true +} + +func isZeros(ip net.IP) bool { + for i := 0; i < len(ip); i++ { + if ip[i] != 0 { + return false + } + } + return true +} + +func localInterfaces(n transport.Net, interfaceFilter func(string) bool, ipFilter func(net.IP) bool, networkTypes []NetworkType, includeLoopback bool) ([]net.IP, error) { //nolint:gocognit + ips := []net.IP{} + ifaces, err := n.Interfaces() + if err != nil { + return ips, err + } + + var IPv4Requested, IPv6Requested bool + for _, typ := range networkTypes { + if typ.IsIPv4() { + IPv4Requested = true + } + + if typ.IsIPv6() { + IPv6Requested = true + } + } + + for _, iface := range ifaces { + if iface.Flags&net.FlagUp == 0 { + continue // Interface down + } + if (iface.Flags&net.FlagLoopback != 0) && !includeLoopback { + continue // Loopback interface + } + + if interfaceFilter != nil && !interfaceFilter(iface.Name) { + continue + } + + addrs, err := iface.Addrs() + if err != nil { + continue + } + + for _, addr := range addrs { + var ip net.IP + switch addr := addr.(type) { + case *net.IPNet: + ip = addr.IP + case *net.IPAddr: + ip = addr.IP + } + if ip == nil || (ip.IsLoopback() && !includeLoopback) { + continue + } + + if ipv4 := ip.To4(); ipv4 == nil { + if !IPv6Requested { + continue + } else if !isSupportedIPv6(ip) { + continue + } + } else if !IPv4Requested { + continue + } + + if ipFilter != nil && !ipFilter(ip) { + continue + } + + ips = append(ips, ip) + } + } + return ips, nil +} + +func listenUDPInPortRange(n transport.Net, log logging.LeveledLogger, portMax, portMin int, network string, lAddr *net.UDPAddr) (transport.UDPConn, error) { + if (lAddr.Port != 0) || ((portMin == 0) && (portMax == 0)) { + return n.ListenUDP(network, lAddr) + } + var i, j int + i = portMin + if i == 0 { + i = 1 + } + j = portMax + if j == 0 { + j = 0xFFFF + } + if i > j { + return nil, ErrPort + } + + portStart := globalMathRandomGenerator.Intn(j-i+1) + i + portCurrent := portStart + for { + lAddr = &net.UDPAddr{IP: lAddr.IP, Port: portCurrent} + c, e := n.ListenUDP(network, lAddr) + if e == nil { + return c, e //nolint:nilerr + } + log.Debugf("Failed to listen %s: %v", lAddr.String(), e) + portCurrent++ + if portCurrent > j { + portCurrent = i + } + if portCurrent == portStart { + break + } + } + return nil, ErrPort +} diff --git a/vendor/github.com/pion/ice/v2/networktype.go b/vendor/github.com/pion/ice/v2/networktype.go index 95091018e..57df18631 100644 --- a/vendor/github.com/pion/ice/v2/networktype.go +++ b/vendor/github.com/pion/ice/v2/networktype.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( @@ -7,8 +10,12 @@ import ( ) const ( - udp = "udp" - tcp = "tcp" + udp = "udp" + tcp = "tcp" + udp4 = "udp4" + udp6 = "udp6" + tcp4 = "tcp4" + tcp6 = "tcp6" ) func supportedNetworkTypes() []NetworkType { @@ -40,13 +47,13 @@ const ( func (t NetworkType) String() string { switch t { case NetworkTypeUDP4: - return "udp4" + return udp4 case NetworkTypeUDP6: - return "udp6" + return udp6 case NetworkTypeTCP4: - return "tcp4" + return tcp4 case NetworkTypeTCP6: - return "tcp6" + return tcp6 default: return ErrUnknownType.Error() } diff --git a/vendor/github.com/pion/ice/v2/priority.go b/vendor/github.com/pion/ice/v2/priority.go index 421829938..16ac5cc73 100644 --- a/vendor/github.com/pion/ice/v2/priority.go +++ b/vendor/github.com/pion/ice/v2/priority.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( diff --git a/vendor/github.com/pion/ice/v2/rand.go b/vendor/github.com/pion/ice/v2/rand.go index 918783e02..3de1f0407 100644 --- a/vendor/github.com/pion/ice/v2/rand.go +++ b/vendor/github.com/pion/ice/v2/rand.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import "github.com/pion/randutil" diff --git a/vendor/github.com/pion/ice/v2/renovate.json b/vendor/github.com/pion/ice/v2/renovate.json index f1614058a..f1bb98c6a 100644 --- a/vendor/github.com/pion/ice/v2/renovate.json +++ b/vendor/github.com/pion/ice/v2/renovate.json @@ -1,27 +1,6 @@ { + "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:base", - ":disableDependencyDashboard" - ], - "postUpdateOptions": [ - "gomodTidy" - ], - "commitBody": "Generated by renovateBot", - "packageRules": [ - { - "matchUpdateTypes": ["minor", "patch", "pin", "digest"], - "automerge": true - }, - { - "packagePatterns": ["^golang.org/x/"], - "schedule": ["on the first day of the month"] - } - ], - "ignorePaths": [ - ".github/workflows/generate-authors.yml", - ".github/workflows/lint.yaml", - ".github/workflows/renovate-go-mod-fix.yaml", - ".github/workflows/test.yaml", - ".github/workflows/tidy-check.yaml" + "github>pion/renovate-config" ] } diff --git a/vendor/github.com/pion/ice/v2/role.go b/vendor/github.com/pion/ice/v2/role.go index 7a8bc064a..e9a7bda94 100644 --- a/vendor/github.com/pion/ice/v2/role.go +++ b/vendor/github.com/pion/ice/v2/role.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( diff --git a/vendor/github.com/pion/ice/v2/selection.go b/vendor/github.com/pion/ice/v2/selection.go index 72484148d..9f312637f 100644 --- a/vendor/github.com/pion/ice/v2/selection.go +++ b/vendor/github.com/pion/ice/v2/selection.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( @@ -40,7 +43,7 @@ func (s *controllingSelector) isNominatable(c Candidate) bool { return time.Since(s.startTime).Nanoseconds() > s.agent.relayAcceptanceMinWait.Nanoseconds() } - s.log.Errorf("isNominatable invalid candidate type %s", c.Type().String()) + s.log.Errorf("Invalid candidate type: %s", c.Type()) return false } @@ -84,7 +87,7 @@ func (s *controllingSelector) nominatePair(pair *CandidatePair) { return } - s.log.Tracef("ping STUN (nominate candidate pair) from %s to %s\n", pair.Local.String(), pair.Remote.String()) + s.log.Tracef("ping STUN (nominate candidate pair) from %s to %s", pair.Local.String(), pair.Remote.String()) s.agent.sendBindingRequest(msg, pair.Local, pair.Remote) } @@ -101,9 +104,9 @@ func (s *controllingSelector) HandleBindingRequest(m *stun.Message, local, remot if p.state == CandidatePairStateSucceeded && s.nominatedPair == nil && s.agent.getSelectedPair() == nil { bestPair := s.agent.getBestAvailableCandidatePair() if bestPair == nil { - s.log.Tracef("No best pair available\n") + s.log.Tracef("No best pair available") } else if bestPair.equal(p) && s.isNominatable(p.Local) && s.isNominatable(p.Remote) { - s.log.Tracef("The candidate (%s, %s) is the best candidate available, marking it as nominated\n", + s.log.Tracef("The candidate (%s, %s) is the best candidate available, marking it as nominated", p.Local.String(), p.Remote.String()) s.nominatedPair = p s.nominatePair(p) @@ -114,7 +117,7 @@ func (s *controllingSelector) HandleBindingRequest(m *stun.Message, local, remot func (s *controllingSelector) HandleSuccessResponse(m *stun.Message, local, remote Candidate, remoteAddr net.Addr) { ok, pendingRequest := s.agent.handleInboundBindingSuccess(m.TransactionID) if !ok { - s.log.Warnf("discard message from (%s), unknown TransactionID 0x%x", remote, m.TransactionID) + s.log.Warnf("Discard message from (%s), unknown TransactionID 0x%x", remote, m.TransactionID) return } @@ -123,7 +126,7 @@ func (s *controllingSelector) HandleSuccessResponse(m *stun.Message, local, remo // Assert that NAT is not symmetric // https://tools.ietf.org/html/rfc8445#section-7.2.5.2.1 if !addrEqual(transactionAddr, remoteAddr) { - s.log.Debugf("discard message: transaction source and destination does not match expected(%s), actual(%s)", transactionAddr, remote) + s.log.Debugf("Discard message: transaction source and destination does not match expected(%s), actual(%s)", transactionAddr, remote) return } @@ -195,7 +198,7 @@ func (s *controlledSelector) PingCandidate(local, remote Candidate) { } func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remote Candidate, remoteAddr net.Addr) { - // nolint:godox + //nolint:godox // TODO according to the standard we should specifically answer a failed nomination: // https://tools.ietf.org/html/rfc8445#section-7.3.1.5 // If the controlled agent does not accept the request from the @@ -205,7 +208,7 @@ func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remot ok, pendingRequest := s.agent.handleInboundBindingSuccess(m.TransactionID) if !ok { - s.log.Warnf("discard message from (%s), unknown TransactionID 0x%x", remote, m.TransactionID) + s.log.Warnf("Discard message from (%s), unknown TransactionID 0x%x", remote, m.TransactionID) return } @@ -214,7 +217,7 @@ func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remot // Assert that NAT is not symmetric // https://tools.ietf.org/html/rfc8445#section-7.2.5.2.1 if !addrEqual(transactionAddr, remoteAddr) { - s.log.Debugf("discard message: transaction source and destination does not match expected(%s), actual(%s)", transactionAddr, remote) + s.log.Debugf("Discard message: transaction source and destination does not match expected(%s), actual(%s)", transactionAddr, remote) return } @@ -229,13 +232,20 @@ func (s *controlledSelector) HandleSuccessResponse(m *stun.Message, local, remot p.state = CandidatePairStateSucceeded s.log.Tracef("Found valid candidate pair: %s", p) + if p.nominateOnBindingSuccess { + if selectedPair := s.agent.getSelectedPair(); selectedPair == nil || + (selectedPair != p && selectedPair.priority() <= p.priority()) { + s.agent.setSelectedPair(p) + } else if selectedPair != p { + s.log.Tracef("ignore nominate new pair %s, already nominated pair %s", p, selectedPair) + } + } } func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote Candidate) { useCandidate := m.Contains(stun.AttrUseCandidate) p := s.agent.findPair(local, remote) - if p == nil { p = s.agent.addPair(local, remote) } @@ -248,10 +258,12 @@ func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote // previously sent by this pair produced a successful response and // generated a valid pair (Section 7.2.5.3.2). The agent sets the // nominated flag value of the valid pair to true. - if selectedPair := s.agent.getSelectedPair(); selectedPair == nil { + if selectedPair := s.agent.getSelectedPair(); selectedPair == nil || + (selectedPair != p && selectedPair.priority() <= p.priority()) { s.agent.setSelectedPair(p) + } else if selectedPair != p { + s.log.Tracef("ignore nominate new pair %s, already nominated pair %s", p, selectedPair) } - s.agent.sendBindingSuccess(m, local, remote) } else { // If the received Binding request triggered a new check to be // enqueued in the triggered-check queue (Section 7.3.1.4), once the @@ -261,12 +273,12 @@ func (s *controlledSelector) HandleBindingRequest(m *stun.Message, local, remote // MUST remove the candidate pair from the valid list, set the // candidate pair state to Failed, and set the checklist state to // Failed. - s.PingCandidate(local, remote) + p.nominateOnBindingSuccess = true } - } else { - s.agent.sendBindingSuccess(m, local, remote) - s.PingCandidate(local, remote) } + + s.agent.sendBindingSuccess(m, local, remote) + s.PingCandidate(local, remote) } type liteSelector struct { @@ -276,8 +288,8 @@ type liteSelector struct { // A lite selector should not contact candidates func (s *liteSelector) ContactCandidates() { if _, ok := s.pairCandidateSelector.(*controllingSelector); ok { - // nolint:godox - // pion/ice#96 + //nolint:godox + // https://github.com/pion/ice/issues/96 // TODO: implement lite controlling agent. For now falling back to full agent. // This only happens if both peers are lite. See RFC 8445 S6.1.1 and S6.2 s.pairCandidateSelector.ContactCandidates() diff --git a/vendor/github.com/pion/ice/v2/stats.go b/vendor/github.com/pion/ice/v2/stats.go index f59d89ff9..9b83bea85 100644 --- a/vendor/github.com/pion/ice/v2/stats.go +++ b/vendor/github.com/pion/ice/v2/stats.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( @@ -104,7 +107,7 @@ type CandidatePairStats struct { // ResponsesReceived represents the total number of connectivity check responses received. ResponsesReceived uint64 - // ResponsesSent epresents the total number of connectivity check responses sent. + // ResponsesSent represents the total number of connectivity check responses sent. // Since we cannot distinguish connectivity check requests and consent requests, // all responses are counted. ResponsesSent uint64 @@ -164,7 +167,7 @@ type CandidateStats struct { // RelayProtocol is the protocol used by the endpoint to communicate with the // TURN server. This is only present for local candidates. Valid values for - // the TURN URL protocol is one of udp, tcp, or tls. + // the TURN URL protocol is one of UDP, TCP, or TLS. RelayProtocol string // Deleted is true if the candidate has been deleted/freed. For host candidates, diff --git a/vendor/github.com/pion/ice/v2/stun.go b/vendor/github.com/pion/ice/v2/stun.go deleted file mode 100644 index bef7c87e5..000000000 --- a/vendor/github.com/pion/ice/v2/stun.go +++ /dev/null @@ -1,24 +0,0 @@ -package ice - -import ( - "fmt" - - "github.com/pion/stun" -) - -func assertInboundUsername(m *stun.Message, expectedUsername string) error { - var username stun.Username - if err := username.GetFrom(m); err != nil { - return err - } - if string(username) != expectedUsername { - return fmt.Errorf("%w expected(%x) actual(%x)", errMismatchUsername, expectedUsername, string(username)) - } - - return nil -} - -func assertInboundMessageIntegrity(m *stun.Message, key []byte) error { - messageIntegrityAttr := stun.MessageIntegrity(key) - return messageIntegrityAttr.Check(m) -} diff --git a/vendor/github.com/pion/ice/v2/tcp_mux.go b/vendor/github.com/pion/ice/v2/tcp_mux.go index e5da608c9..fb4e5243e 100644 --- a/vendor/github.com/pion/ice/v2/tcp_mux.go +++ b/vendor/github.com/pion/ice/v2/tcp_mux.go @@ -1,7 +1,11 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( "encoding/binary" + "errors" "io" "net" "strings" @@ -11,37 +15,19 @@ import ( "github.com/pion/stun" ) +// ErrGetTransportAddress can't convert net.Addr to underlying type (UDPAddr or TCPAddr). +var ErrGetTransportAddress = errors.New("failed to get local transport address") + // TCPMux is allows grouping multiple TCP net.Conns and using them like UDP // net.PacketConns. The main implementation of this is TCPMuxDefault, and this -// interface exists to: -// 1. prevent SEGV panics when TCPMuxDefault is not initialized by using the -// invalidTCPMux implementation, and -// 2. allow mocking in tests. +// interface exists to allow mocking in tests. type TCPMux interface { io.Closer - GetConnByUfrag(ufrag string) (net.PacketConn, error) + GetConnByUfrag(ufrag string, isIPv6 bool, local net.IP) (net.PacketConn, error) RemoveConnByUfrag(ufrag string) } -// invalidTCPMux is an implementation of TCPMux that always returns ErrTCPMuxNotInitialized. -type invalidTCPMux struct{} - -func newInvalidTCPMux() *invalidTCPMux { - return &invalidTCPMux{} -} - -// Close implements TCPMux interface. -func (m *invalidTCPMux) Close() error { - return ErrTCPMuxNotInitialized -} - -// GetConnByUfrag implements TCPMux interface. -func (m *invalidTCPMux) GetConnByUfrag(ufrag string) (net.PacketConn, error) { - return nil, ErrTCPMuxNotInitialized -} - -// RemoveConnByUfrag implements TCPMux interface. -func (m *invalidTCPMux) RemoveConnByUfrag(ufrag string) {} +type ipAddr string // TCPMuxDefault muxes TCP net.Conns into net.PacketConns and groups them by // Ufrag. It is a default implementation of TCPMux interface. @@ -49,8 +35,8 @@ type TCPMuxDefault struct { params *TCPMuxParams closed bool - // conns is a map of all tcpPacketConns indexed by ufrag - conns map[string]*tcpPacketConn + // connsIPv4 and connsIPv6 are maps of all tcpPacketConns indexed by ufrag and local address + connsIPv4, connsIPv6 map[string]map[ipAddr]*tcpPacketConn mu sync.Mutex wg sync.WaitGroup @@ -61,6 +47,11 @@ type TCPMuxParams struct { Listener net.Listener Logger logging.LeveledLogger ReadBufferSize int + + // Maximum buffer size for write op. 0 means no write buffer, the write op will block until the whole packet is written + // if the write buffer is full, the subsequent write packet will be dropped until it has enough space. + // a default 4MB is recommended. + WriteBufferSize int } // NewTCPMuxDefault creates a new instance of TCPMuxDefault. @@ -72,7 +63,8 @@ func NewTCPMuxDefault(params TCPMuxParams) *TCPMuxDefault { m := &TCPMuxDefault{ params: ¶ms, - conns: map[string]*tcpPacketConn{}, + connsIPv4: map[string]map[ipAddr]*tcpPacketConn{}, + connsIPv6: map[string]map[ipAddr]*tcpPacketConn{}, } m.wg.Add(1) @@ -85,11 +77,11 @@ func NewTCPMuxDefault(params TCPMuxParams) *TCPMuxDefault { } func (m *TCPMuxDefault) start() { - m.params.Logger.Infof("Listening TCP on %s\n", m.params.Listener.Addr()) + m.params.Logger.Infof("Listening TCP on %s", m.params.Listener.Addr()) for { conn, err := m.params.Listener.Accept() if err != nil { - m.params.Logger.Infof("Error accepting connection: %s\n", err) + m.params.Logger.Infof("Error accepting connection: %s", err) return } @@ -109,7 +101,7 @@ func (m *TCPMuxDefault) LocalAddr() net.Addr { } // GetConnByUfrag retrieves an existing or creates a new net.PacketConn. -func (m *TCPMuxDefault) GetConnByUfrag(ufrag string) (net.PacketConn, error) { +func (m *TCPMuxDefault) GetConnByUfrag(ufrag string, isIPv6 bool, local net.IP) (net.PacketConn, error) { m.mu.Lock() defer m.mu.Unlock() @@ -117,34 +109,50 @@ func (m *TCPMuxDefault) GetConnByUfrag(ufrag string) (net.PacketConn, error) { return nil, io.ErrClosedPipe } - conn, ok := m.conns[ufrag] - - if ok { + if conn, ok := m.getConn(ufrag, isIPv6, local); ok { return conn, nil - // return nil, fmt.Errorf("duplicate ufrag %v", ufrag) } - conn = m.createConn(ufrag, m.LocalAddr()) - - return conn, nil + return m.createConn(ufrag, isIPv6, local) } -func (m *TCPMuxDefault) createConn(ufrag string, localAddr net.Addr) *tcpPacketConn { +func (m *TCPMuxDefault) createConn(ufrag string, isIPv6 bool, local net.IP) (*tcpPacketConn, error) { + addr, ok := m.LocalAddr().(*net.TCPAddr) + if !ok { + return nil, ErrGetTransportAddress + } + localAddr := *addr + localAddr.IP = local + conn := newTCPPacketConn(tcpPacketParams{ - ReadBuffer: m.params.ReadBufferSize, - LocalAddr: localAddr, - Logger: m.params.Logger, + ReadBuffer: m.params.ReadBufferSize, + WriteBuffer: m.params.WriteBufferSize, + LocalAddr: &localAddr, + Logger: m.params.Logger, }) - m.conns[ufrag] = conn + + var conns map[ipAddr]*tcpPacketConn + if isIPv6 { + if conns, ok = m.connsIPv6[ufrag]; !ok { + conns = make(map[ipAddr]*tcpPacketConn) + m.connsIPv6[ufrag] = conns + } + } else { + if conns, ok = m.connsIPv4[ufrag]; !ok { + conns = make(map[ipAddr]*tcpPacketConn) + m.connsIPv4[ufrag] = conns + } + } + conns[ipAddr(local.String())] = conn m.wg.Add(1) go func() { defer m.wg.Done() <-conn.CloseChannel() - m.RemoveConnByUfrag(ufrag) + m.removeConnByUfragAndLocalHost(ufrag, local) }() - return conn + return conn, nil } func (m *TCPMuxDefault) closeAndLogError(closer io.Closer) { @@ -172,41 +180,61 @@ func (m *TCPMuxDefault) handleConn(conn net.Conn) { copy(msg.Raw, buf) if err = msg.Decode(); err != nil { m.closeAndLogError(conn) - m.params.Logger.Warnf("Failed to handle decode ICE from %s to %s: %v\n", conn.RemoteAddr(), conn.LocalAddr(), err) + m.params.Logger.Warnf("Failed to handle decode ICE from %s to %s: %v", conn.RemoteAddr(), conn.LocalAddr(), err) return } - if m == nil || msg.Type.Method != stun.MethodBinding { // not a stun + if m == nil || msg.Type.Method != stun.MethodBinding { // Not a STUN m.closeAndLogError(conn) - m.params.Logger.Warnf("Not a STUN message from %s to %s\n", conn.RemoteAddr(), conn.LocalAddr()) + m.params.Logger.Warnf("Not a STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr()) return } for _, attr := range msg.Attributes { - m.params.Logger.Debugf("msg attr: %s\n", attr.String()) + m.params.Logger.Debugf("msg attr: %s", attr.String()) } attr, err := msg.Get(stun.AttrUsername) if err != nil { m.closeAndLogError(conn) - m.params.Logger.Warnf("No Username attribute in STUN message from %s to %s\n", conn.RemoteAddr(), conn.LocalAddr()) + m.params.Logger.Warnf("No Username attribute in STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr()) return } ufrag := strings.Split(string(attr), ":")[0] - m.params.Logger.Debugf("Ufrag: %s\n", ufrag) + m.params.Logger.Debugf("Ufrag: %s", ufrag) m.mu.Lock() defer m.mu.Unlock() - packetConn, ok := m.conns[ufrag] + host, _, err := net.SplitHostPort(conn.RemoteAddr().String()) + if err != nil { + m.closeAndLogError(conn) + m.params.Logger.Warnf("Failed to get host in STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr()) + return + } + + isIPv6 := net.ParseIP(host).To4() == nil + + localAddr, ok := conn.LocalAddr().(*net.TCPAddr) if !ok { - packetConn = m.createConn(ufrag, conn.LocalAddr()) + m.closeAndLogError(conn) + m.params.Logger.Warnf("Failed to get local tcp address in STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr()) + return + } + packetConn, ok := m.getConn(ufrag, isIPv6, localAddr.IP) + if !ok { + packetConn, err = m.createConn(ufrag, isIPv6, localAddr.IP) + if err != nil { + m.closeAndLogError(conn) + m.params.Logger.Warnf("Failed to create packetConn for STUN message from %s to %s", conn.RemoteAddr(), conn.LocalAddr()) + return + } } if err := packetConn.AddConn(conn, buf); err != nil { m.closeAndLogError(conn) - m.params.Logger.Warnf("Error adding conn to tcpPacketConn from %s to %s: %s\n", conn.RemoteAddr(), conn.LocalAddr(), err) + m.params.Logger.Warnf("Error adding conn to tcpPacketConn from %s to %s: %s", conn.RemoteAddr(), conn.LocalAddr(), err) return } } @@ -216,10 +244,19 @@ func (m *TCPMuxDefault) Close() error { m.mu.Lock() m.closed = true - for _, conn := range m.conns { - m.closeAndLogError(conn) + for _, conns := range m.connsIPv4 { + for _, conn := range conns { + m.closeAndLogError(conn) + } } - m.conns = map[string]*tcpPacketConn{} + for _, conns := range m.connsIPv6 { + for _, conn := range conns { + m.closeAndLogError(conn) + } + } + + m.connsIPv4 = map[string]map[ipAddr]*tcpPacketConn{} + m.connsIPv6 = map[string]map[ipAddr]*tcpPacketConn{} err := m.params.Listener.Close() @@ -232,13 +269,77 @@ func (m *TCPMuxDefault) Close() error { // RemoveConnByUfrag closes and removes a net.PacketConn by Ufrag. func (m *TCPMuxDefault) RemoveConnByUfrag(ufrag string) { - m.mu.Lock() - defer m.mu.Unlock() + removedConns := make([]*tcpPacketConn, 0, 4) - if conn, ok := m.conns[ufrag]; ok { - m.closeAndLogError(conn) - delete(m.conns, ufrag) + // Keep lock section small to avoid deadlock with conn lock + m.mu.Lock() + if conns, ok := m.connsIPv4[ufrag]; ok { + delete(m.connsIPv4, ufrag) + for _, conn := range conns { + removedConns = append(removedConns, conn) + } } + if conns, ok := m.connsIPv6[ufrag]; ok { + delete(m.connsIPv6, ufrag) + for _, conn := range conns { + removedConns = append(removedConns, conn) + } + } + + m.mu.Unlock() + + // Close the connections outside the critical section to avoid + // deadlocking TCP mux if (*tcpPacketConn).Close() blocks. + for _, conn := range removedConns { + m.closeAndLogError(conn) + } +} + +func (m *TCPMuxDefault) removeConnByUfragAndLocalHost(ufrag string, local net.IP) { + removedConns := make([]*tcpPacketConn, 0, 4) + + localIP := ipAddr(local.String()) + // Keep lock section small to avoid deadlock with conn lock + m.mu.Lock() + if conns, ok := m.connsIPv4[ufrag]; ok { + if conn, ok := conns[localIP]; ok { + delete(conns, localIP) + if len(conns) == 0 { + delete(m.connsIPv4, ufrag) + } + removedConns = append(removedConns, conn) + } + } + if conns, ok := m.connsIPv6[ufrag]; ok { + if conn, ok := conns[localIP]; ok { + delete(conns, localIP) + if len(conns) == 0 { + delete(m.connsIPv6, ufrag) + } + removedConns = append(removedConns, conn) + } + } + m.mu.Unlock() + + // Close the connections outside the critical section to avoid + // deadlocking TCP mux if (*tcpPacketConn).Close() blocks. + for _, conn := range removedConns { + m.closeAndLogError(conn) + } +} + +func (m *TCPMuxDefault) getConn(ufrag string, isIPv6 bool, local net.IP) (val *tcpPacketConn, ok bool) { + var conns map[ipAddr]*tcpPacketConn + if isIPv6 { + conns, ok = m.connsIPv6[ufrag] + } else { + conns, ok = m.connsIPv4[ufrag] + } + if conns != nil { + val, ok = conns[ipAddr(local.String())] + } + + return } const streamingPacketHeaderLen = 2 @@ -246,11 +347,12 @@ const streamingPacketHeaderLen = 2 // readStreamingPacket reads 1 packet from stream // read packet bytes https://tools.ietf.org/html/rfc4571#section-2 // 2-byte length header prepends each packet: -// 0 1 2 3 -// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -// ----------------------------------------------------------------- -// | LENGTH | RTP or RTCP packet ... | -// ----------------------------------------------------------------- +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// ----------------------------------------------------------------- +// | LENGTH | RTP or RTCP packet ... | +// ----------------------------------------------------------------- func readStreamingPacket(conn net.Conn, buf []byte) (int, error) { header := make([]byte, streamingPacketHeaderLen) var bytesRead, n int @@ -281,11 +383,11 @@ func readStreamingPacket(conn net.Conn, buf []byte) (int, error) { } func writeStreamingPacket(conn net.Conn, buf []byte) (int, error) { - bufferCopy := make([]byte, streamingPacketHeaderLen+len(buf)) - binary.BigEndian.PutUint16(bufferCopy, uint16(len(buf))) - copy(bufferCopy[2:], buf) + bufCopy := make([]byte, streamingPacketHeaderLen+len(buf)) + binary.BigEndian.PutUint16(bufCopy, uint16(len(buf))) + copy(bufCopy[2:], buf) - n, err := conn.Write(bufferCopy) + n, err := conn.Write(bufCopy) if err != nil { return 0, err } diff --git a/vendor/github.com/pion/ice/v2/tcp_mux_multi.go b/vendor/github.com/pion/ice/v2/tcp_mux_multi.go new file mode 100644 index 000000000..e32acbf3e --- /dev/null +++ b/vendor/github.com/pion/ice/v2/tcp_mux_multi.go @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package ice + +import "net" + +// AllConnsGetter allows multiple fixed TCP ports to be used, +// each of which is multiplexed like TCPMux. AllConnsGetter also acts as +// a TCPMux, in which case it will return a single connection for one +// of the ports. +type AllConnsGetter interface { + GetAllConns(ufrag string, isIPv6 bool, localIP net.IP) ([]net.PacketConn, error) +} + +// MultiTCPMuxDefault implements both TCPMux and AllConnsGetter, +// allowing users to pass multiple TCPMux instances to the ICE agent +// configuration. +type MultiTCPMuxDefault struct { + muxes []TCPMux +} + +// NewMultiTCPMuxDefault creates an instance of MultiTCPMuxDefault that +// uses the provided TCPMux instances. +func NewMultiTCPMuxDefault(muxes ...TCPMux) *MultiTCPMuxDefault { + return &MultiTCPMuxDefault{ + muxes: muxes, + } +} + +// GetConnByUfrag returns a PacketConn given the connection's ufrag, network and local address +// creates the connection if an existing one can't be found. This, unlike +// GetAllConns, will only return a single PacketConn from the first mux that was +// passed in to NewMultiTCPMuxDefault. +func (m *MultiTCPMuxDefault) GetConnByUfrag(ufrag string, isIPv6 bool, local net.IP) (net.PacketConn, error) { + // NOTE: We always use the first element here in order to maintain the + // behavior of using an existing connection if one exists. + if len(m.muxes) == 0 { + return nil, errNoTCPMuxAvailable + } + return m.muxes[0].GetConnByUfrag(ufrag, isIPv6, local) +} + +// RemoveConnByUfrag stops and removes the muxed packet connection +// from all underlying TCPMux instances. +func (m *MultiTCPMuxDefault) RemoveConnByUfrag(ufrag string) { + for _, mux := range m.muxes { + mux.RemoveConnByUfrag(ufrag) + } +} + +// GetAllConns returns a PacketConn for each underlying TCPMux +func (m *MultiTCPMuxDefault) GetAllConns(ufrag string, isIPv6 bool, local net.IP) ([]net.PacketConn, error) { + if len(m.muxes) == 0 { + // Make sure that we either return at least one connection or an error. + return nil, errNoTCPMuxAvailable + } + var conns []net.PacketConn + for _, mux := range m.muxes { + conn, err := mux.GetConnByUfrag(ufrag, isIPv6, local) + if err != nil { + // For now, this implementation is all or none. + return nil, err + } + if conn != nil { + conns = append(conns, conn) + } + } + return conns, nil +} + +// Close the multi mux, no further connections could be created +func (m *MultiTCPMuxDefault) Close() error { + var err error + for _, mux := range m.muxes { + if e := mux.Close(); e != nil { + err = e + } + } + return err +} diff --git a/vendor/github.com/pion/ice/v2/tcp_packet_conn.go b/vendor/github.com/pion/ice/v2/tcp_packet_conn.go index dc4eaf04f..8c51fd1cc 100644 --- a/vendor/github.com/pion/ice/v2/tcp_packet_conn.go +++ b/vendor/github.com/pion/ice/v2/tcp_packet_conn.go @@ -1,15 +1,78 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( + "errors" "fmt" "io" "net" "sync" + "sync/atomic" "time" "github.com/pion/logging" + "github.com/pion/transport/v2/packetio" ) +type bufferedConn struct { + net.Conn + buf *packetio.Buffer + logger logging.LeveledLogger + closed int32 +} + +func newBufferedConn(conn net.Conn, bufSize int, logger logging.LeveledLogger) net.Conn { + buf := packetio.NewBuffer() + if bufSize > 0 { + buf.SetLimitSize(bufSize) + } + + bc := &bufferedConn{ + Conn: conn, + buf: buf, + logger: logger, + } + + go bc.writeProcess() + return bc +} + +func (bc *bufferedConn) Write(b []byte) (int, error) { + n, err := bc.buf.Write(b) + if err != nil { + return n, err + } + return n, nil +} + +func (bc *bufferedConn) writeProcess() { + pktBuf := make([]byte, receiveMTU) + for atomic.LoadInt32(&bc.closed) == 0 { + n, err := bc.buf.Read(pktBuf) + if errors.Is(err, io.EOF) { + return + } + + if err != nil { + bc.logger.Warnf("read buffer error: %s", err) + continue + } + + if _, err := bc.Conn.Write(pktBuf[:n]); err != nil { + bc.logger.Warnf("write error: %s", err) + continue + } + } +} + +func (bc *bufferedConn) Close() error { + atomic.StoreInt32(&bc.closed, 1) + _ = bc.buf.Close() + return bc.Conn.Close() +} + type tcpPacketConn struct { params *tcpPacketParams @@ -31,9 +94,10 @@ type streamingPacket struct { } type tcpPacketParams struct { - ReadBuffer int - LocalAddr net.Addr - Logger logging.LeveledLogger + ReadBuffer int + LocalAddr net.Addr + Logger logging.LeveledLogger + WriteBuffer int } func newTCPPacketConn(params tcpPacketParams) *tcpPacketConn { @@ -50,7 +114,7 @@ func newTCPPacketConn(params tcpPacketParams) *tcpPacketConn { } func (t *tcpPacketConn) AddConn(conn net.Conn, firstPacketData []byte) error { - t.params.Logger.Infof("AddConn: %s %s", conn.RemoteAddr().Network(), conn.RemoteAddr()) + t.params.Logger.Infof("AddConn: %s remote %s to local %s", conn.RemoteAddr().Network(), conn.RemoteAddr(), conn.LocalAddr()) t.mu.Lock() defer t.mu.Unlock() @@ -65,14 +129,25 @@ func (t *tcpPacketConn) AddConn(conn net.Conn, firstPacketData []byte) error { return fmt.Errorf("%w: %s", errConnectionAddrAlreadyExist, conn.RemoteAddr().String()) } + if t.params.WriteBuffer > 0 { + conn = newBufferedConn(conn, t.params.WriteBuffer, t.params.Logger) + } t.conns[conn.RemoteAddr().String()] = conn t.wg.Add(1) go func() { - if firstPacketData != nil { - t.recvChan <- streamingPacket{firstPacketData, conn.RemoteAddr(), nil} - } defer t.wg.Done() + if firstPacketData != nil { + select { + case <-t.closedChan: + // NOTE: recvChan can fill up and never drain in edge + // cases while closing a connection, which can cause the + // packetConn to never finish closing. Bail out early + // here to prevent that. + return + case t.recvChan <- streamingPacket{firstPacketData, conn.RemoteAddr(), nil}: + } + } t.startReading(conn) }() @@ -84,9 +159,8 @@ func (t *tcpPacketConn) startReading(conn net.Conn) { for { n, err := readStreamingPacket(conn, buf) - // t.params.Logger.Infof("readStreamingPacket read %d bytes", n) if err != nil { - t.params.Logger.Infof("%w: %s\n", errReadingStreamingPacket, err) + t.params.Logger.Infof("%v: %s", errReadingStreamingPacket, err) t.handleRecv(streamingPacket{nil, conn.RemoteAddr(), err}) t.removeConn(conn) return @@ -95,7 +169,6 @@ func (t *tcpPacketConn) startReading(conn net.Conn) { data := make([]byte, n) copy(data, buf[:n]) - // t.params.Logger.Infof("Writing read streaming packet to recvChan: %d bytes", len(data)) t.handleRecv(streamingPacket{data, conn.RemoteAddr(), nil}) } } @@ -126,7 +199,7 @@ func (t *tcpPacketConn) isClosed() bool { } // WriteTo is for passive and s-o candidates. -func (t *tcpPacketConn) ReadFrom(b []byte) (n int, raddr net.Addr, err error) { +func (t *tcpPacketConn) ReadFrom(b []byte) (n int, rAddr net.Addr, err error) { pkt, ok := <-t.recvChan if !ok { @@ -147,27 +220,18 @@ func (t *tcpPacketConn) ReadFrom(b []byte) (n int, raddr net.Addr, err error) { } // WriteTo is for active and s-o candidates. -func (t *tcpPacketConn) WriteTo(buf []byte, raddr net.Addr) (n int, err error) { +func (t *tcpPacketConn) WriteTo(buf []byte, rAddr net.Addr) (n int, err error) { t.mu.Lock() - defer t.mu.Unlock() + conn, ok := t.conns[rAddr.String()] + t.mu.Unlock() - conn, ok := t.conns[raddr.String()] if !ok { return 0, io.ErrClosedPipe - // conn, err := net.DialTCP(tcp, nil, raddr.(*net.TCPAddr)) - - // if err != nil { - // t.params.Logger.Tracef("DialTCP error: %s", err) - // return 0, err - // } - - // go t.startReading(conn) - // t.conns[raddr.String()] = conn } n, err = writeStreamingPacket(conn, buf) if err != nil { - t.params.Logger.Tracef("%w %s\n", errWriting, raddr) + t.params.Logger.Tracef("%w %s", errWriting, rAddr) return n, err } @@ -177,7 +241,7 @@ func (t *tcpPacketConn) WriteTo(buf []byte, raddr net.Addr) (n int, err error) { func (t *tcpPacketConn) closeAndLogError(closer io.Closer) { err := closer.Close() if err != nil { - t.params.Logger.Warnf("%w: %s", errClosingConnection, err) + t.params.Logger.Warnf("%v: %s", errClosingConnection, err) } } @@ -219,15 +283,15 @@ func (t *tcpPacketConn) LocalAddr() net.Addr { return t.params.LocalAddr } -func (t *tcpPacketConn) SetDeadline(tm time.Time) error { +func (t *tcpPacketConn) SetDeadline(time.Time) error { return nil } -func (t *tcpPacketConn) SetReadDeadline(tm time.Time) error { +func (t *tcpPacketConn) SetReadDeadline(time.Time) error { return nil } -func (t *tcpPacketConn) SetWriteDeadline(tm time.Time) error { +func (t *tcpPacketConn) SetWriteDeadline(time.Time) error { return nil } diff --git a/vendor/github.com/pion/ice/v2/tcptype.go b/vendor/github.com/pion/ice/v2/tcptype.go index 6700fe5ff..e500e57f5 100644 --- a/vendor/github.com/pion/ice/v2/tcptype.go +++ b/vendor/github.com/pion/ice/v2/tcptype.go @@ -1,9 +1,12 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import "strings" // TCPType is the type of ICE TCP candidate as described in -// ttps://tools.ietf.org/html/rfc6544#section-4.5 +// https://tools.ietf.org/html/rfc6544#section-4.5 type TCPType int const ( diff --git a/vendor/github.com/pion/ice/v2/test_utils.go b/vendor/github.com/pion/ice/v2/test_utils.go new file mode 100644 index 000000000..235fda315 --- /dev/null +++ b/vendor/github.com/pion/ice/v2/test_utils.go @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +//go:build !js +// +build !js + +package ice + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func newHostRemote(t *testing.T) *CandidateHost { + remoteHostConfig := &CandidateHostConfig{ + Network: "udp", + Address: "1.2.3.5", + Port: 12350, + Component: 1, + } + hostRemote, err := NewCandidateHost(remoteHostConfig) + require.NoError(t, err) + return hostRemote +} + +func newPrflxRemote(t *testing.T) *CandidatePeerReflexive { + prflxConfig := &CandidatePeerReflexiveConfig{ + Network: "udp", + Address: "10.10.10.2", + Port: 19217, + Component: 1, + RelAddr: "4.3.2.1", + RelPort: 43211, + } + prflxRemote, err := NewCandidatePeerReflexive(prflxConfig) + require.NoError(t, err) + return prflxRemote +} + +func newSrflxRemote(t *testing.T) *CandidateServerReflexive { + srflxConfig := &CandidateServerReflexiveConfig{ + Network: "udp", + Address: "10.10.10.2", + Port: 19218, + Component: 1, + RelAddr: "4.3.2.1", + RelPort: 43212, + } + srflxRemote, err := NewCandidateServerReflexive(srflxConfig) + require.NoError(t, err) + return srflxRemote +} + +func newRelayRemote(t *testing.T) *CandidateRelay { + relayConfig := &CandidateRelayConfig{ + Network: "udp", + Address: "1.2.3.4", + Port: 12340, + Component: 1, + RelAddr: "4.3.2.1", + RelPort: 43210, + } + relayRemote, err := NewCandidateRelay(relayConfig) + require.NoError(t, err) + return relayRemote +} + +func newHostLocal(t *testing.T) *CandidateHost { + localHostConfig := &CandidateHostConfig{ + Network: "udp", + Address: "192.168.1.1", + Port: 19216, + Component: 1, + } + hostLocal, err := NewCandidateHost(localHostConfig) + require.NoError(t, err) + return hostLocal +} diff --git a/vendor/github.com/pion/ice/v2/transport.go b/vendor/github.com/pion/ice/v2/transport.go index 6d9aaffad..cd37a9b26 100644 --- a/vendor/github.com/pion/ice/v2/transport.go +++ b/vendor/github.com/pion/ice/v2/transport.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( @@ -44,12 +47,12 @@ func (a *Agent) connect(ctx context.Context, isControlling bool, remoteUfrag, re if err != nil { return nil, err } - err = a.startConnectivityChecks(isControlling, remoteUfrag, remotePwd) + err = a.startConnectivityChecks(isControlling, remoteUfrag, remotePwd) //nolint:contextcheck if err != nil { return nil, err } - // block until pair selected + // Block until pair selected select { case <-a.done: return nil, a.getErr() @@ -70,7 +73,7 @@ func (c *Conn) Read(p []byte) (int, error) { return 0, err } - n, err := c.agent.buffer.Read(p) + n, err := c.agent.buf.Read(p) atomic.AddUint64(&c.bytesReceived, uint64(n)) return n, err } @@ -130,16 +133,16 @@ func (c *Conn) RemoteAddr() net.Addr { } // SetDeadline is a stub -func (c *Conn) SetDeadline(t time.Time) error { +func (c *Conn) SetDeadline(time.Time) error { return nil } // SetReadDeadline is a stub -func (c *Conn) SetReadDeadline(t time.Time) error { +func (c *Conn) SetReadDeadline(time.Time) error { return nil } // SetWriteDeadline is a stub -func (c *Conn) SetWriteDeadline(t time.Time) error { +func (c *Conn) SetWriteDeadline(time.Time) error { return nil } diff --git a/vendor/github.com/pion/ice/v2/udp_mux.go b/vendor/github.com/pion/ice/v2/udp_mux.go index 0bbe3b5cc..405bb7b1a 100644 --- a/vendor/github.com/pion/ice/v2/udp_mux.go +++ b/vendor/github.com/pion/ice/v2/udp_mux.go @@ -1,6 +1,10 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( + "errors" "io" "net" "os" @@ -9,13 +13,16 @@ import ( "github.com/pion/logging" "github.com/pion/stun" + "github.com/pion/transport/v2" + "github.com/pion/transport/v2/stdnet" ) // UDPMux allows multiple connections to go over a single UDP port type UDPMux interface { io.Closer - GetConn(ufrag string) (net.PacketConn, error) + GetConn(ufrag string, addr net.Addr) (net.PacketConn, error) RemoveConnByUfrag(ufrag string) + GetListenAddresses() []net.Addr } // UDPMuxDefault is an implementation of the interface @@ -25,16 +32,19 @@ type UDPMuxDefault struct { closedChan chan struct{} closeOnce sync.Once - // conns is a map of all udpMuxedConn indexed by ufrag|network|candidateType - conns map[string]*udpMuxedConn + // connsIPv4 and connsIPv6 are maps of all udpMuxedConn indexed by ufrag|network|candidateType + connsIPv4, connsIPv6 map[string]*udpMuxedConn addressMapMu sync.RWMutex addressMap map[string]*udpMuxedConn - // buffer pool to recycle buffers for net.UDPAddr encodes/decodes + // Buffer pool to recycle buffers for net.UDPAddr encodes/decodes pool *sync.Pool mu sync.Mutex + + // For UDP connection listen at unspecified address + localAddrsForUnspecified []net.Addr } const maxAddrSize = 512 @@ -43,6 +53,11 @@ const maxAddrSize = 512 type UDPMuxParams struct { Logger logging.LeveledLogger UDPConn net.PacketConn + + // Required for gathering local addresses + // in case a un UDPConn is passed which does not + // bind to a specific local address. + Net transport.Net } // NewUDPMuxDefault creates an implementation of UDPMux @@ -51,17 +66,57 @@ func NewUDPMuxDefault(params UDPMuxParams) *UDPMuxDefault { params.Logger = logging.NewDefaultLoggerFactory().NewLogger("ice") } + var localAddrsForUnspecified []net.Addr + if addr, ok := params.UDPConn.LocalAddr().(*net.UDPAddr); !ok { + params.Logger.Errorf("LocalAddr is not a net.UDPAddr, got %T", params.UDPConn.LocalAddr()) + } else if ok && addr.IP.IsUnspecified() { + // For unspecified addresses, the correct behavior is to return errListenUnspecified, but + // it will break the applications that are already using unspecified UDP connection + // with UDPMuxDefault, so print a warn log and create a local address list for mux. + params.Logger.Warn("UDPMuxDefault should not listening on unspecified address, use NewMultiUDPMuxFromPort instead") + var networks []NetworkType + switch { + case addr.IP.To4() != nil: + networks = []NetworkType{NetworkTypeUDP4} + + case addr.IP.To16() != nil: + networks = []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6} + + default: + params.Logger.Errorf("LocalAddr expected IPV4 or IPV6, got %T", params.UDPConn.LocalAddr()) + } + if len(networks) > 0 { + if params.Net == nil { + var err error + if params.Net, err = stdnet.NewNet(); err != nil { + params.Logger.Errorf("failed to get create network: %v", err) + } + } + + ips, err := localInterfaces(params.Net, nil, nil, networks, true) + if err == nil { + for _, ip := range ips { + localAddrsForUnspecified = append(localAddrsForUnspecified, &net.UDPAddr{IP: ip, Port: addr.Port}) + } + } else { + params.Logger.Errorf("failed to get local interfaces for unspecified addr: %v", err) + } + } + } + m := &UDPMuxDefault{ addressMap: map[string]*udpMuxedConn{}, params: params, - conns: make(map[string]*udpMuxedConn), + connsIPv4: make(map[string]*udpMuxedConn), + connsIPv6: make(map[string]*udpMuxedConn), closedChan: make(chan struct{}, 1), pool: &sync.Pool{ New: func() interface{} { - // big enough buffer to fit both packet and address + // Big enough buffer to fit both packet and address return newBufferHolder(receiveMTU + maxAddrSize) }, }, + localAddrsForUnspecified: localAddrsForUnspecified, } go m.connWorker() @@ -74,9 +129,27 @@ func (m *UDPMuxDefault) LocalAddr() net.Addr { return m.params.UDPConn.LocalAddr() } -// GetConn returns a PacketConn given the connection's ufrag and network +// GetListenAddresses returns the list of addresses that this mux is listening on +func (m *UDPMuxDefault) GetListenAddresses() []net.Addr { + if len(m.localAddrsForUnspecified) > 0 { + return m.localAddrsForUnspecified + } + + return []net.Addr{m.LocalAddr()} +} + +// GetConn returns a PacketConn given the connection's ufrag and network address // creates the connection if an existing one can't be found -func (m *UDPMuxDefault) GetConn(ufrag string) (net.PacketConn, error) { +func (m *UDPMuxDefault) GetConn(ufrag string, addr net.Addr) (net.PacketConn, error) { + // don't check addr for mux using unspecified address + if len(m.localAddrsForUnspecified) == 0 && m.params.UDPConn.LocalAddr().String() != addr.String() { + return nil, errInvalidAddress + } + + var isIPv6 bool + if udpAddr, _ := addr.(*net.UDPAddr); udpAddr != nil && udpAddr.IP.To4() == nil { + isIPv6 = true + } m.mu.Lock() defer m.mu.Unlock() @@ -84,37 +157,46 @@ func (m *UDPMuxDefault) GetConn(ufrag string) (net.PacketConn, error) { return nil, io.ErrClosedPipe } - if c, ok := m.conns[ufrag]; ok { - return c, nil + if conn, ok := m.getConn(ufrag, isIPv6); ok { + return conn, nil } c := m.createMuxedConn(ufrag) go func() { <-c.CloseChannel() - m.removeConn(ufrag) + m.RemoveConnByUfrag(ufrag) }() - m.conns[ufrag] = c + + if isIPv6 { + m.connsIPv6[ufrag] = c + } else { + m.connsIPv4[ufrag] = c + } + return c, nil } // RemoveConnByUfrag stops and removes the muxed packet connection func (m *UDPMuxDefault) RemoveConnByUfrag(ufrag string) { - m.mu.Lock() - removedConns := make([]*udpMuxedConn, 0) - for key := range m.conns { - if key != ufrag { - continue - } + removedConns := make([]*udpMuxedConn, 0, 2) - c := m.conns[key] - delete(m.conns, key) - if c != nil { - removedConns = append(removedConns, c) - } + // Keep lock section small to avoid deadlock with conn lock + m.mu.Lock() + if c, ok := m.connsIPv4[ufrag]; ok { + delete(m.connsIPv4, ufrag) + removedConns = append(removedConns, c) + } + if c, ok := m.connsIPv6[ufrag]; ok { + delete(m.connsIPv6, ufrag) + removedConns = append(removedConns, c) } - // keep lock section small to avoid deadlock with conn lock m.mu.Unlock() + if len(removedConns) == 0 { + // No need to lock if no connection was found + return + } + m.addressMapMu.Lock() defer m.addressMapMu.Unlock() @@ -143,37 +225,25 @@ func (m *UDPMuxDefault) Close() error { m.mu.Lock() defer m.mu.Unlock() - for _, c := range m.conns { + for _, c := range m.connsIPv4 { _ = c.Close() } - m.conns = make(map[string]*udpMuxedConn) + for _, c := range m.connsIPv6 { + _ = c.Close() + } + + m.connsIPv4 = make(map[string]*udpMuxedConn) + m.connsIPv6 = make(map[string]*udpMuxedConn) + close(m.closedChan) + + _ = m.params.UDPConn.Close() }) return err } -func (m *UDPMuxDefault) removeConn(key string) { - m.mu.Lock() - c := m.conns[key] - delete(m.conns, key) - // keep lock section small to avoid deadlock with conn lock - m.mu.Unlock() - - if c == nil { - return - } - - m.addressMapMu.Lock() - defer m.addressMapMu.Unlock() - - addresses := c.getAddresses() - for _, addr := range addresses { - delete(m.addressMap, addr) - } -} - -func (m *UDPMuxDefault) writeTo(buf []byte, raddr net.Addr) (n int, err error) { - return m.params.UDPConn.WriteTo(buf, raddr) +func (m *UDPMuxDefault) writeTo(buf []byte, rAddr net.Addr) (n int, err error) { + return m.params.UDPConn.WriteTo(buf, rAddr) } func (m *UDPMuxDefault) registerConnForAddress(conn *udpMuxedConn, addr string) { @@ -219,7 +289,7 @@ func (m *UDPMuxDefault) connWorker() { } else if err != nil { if os.IsTimeout(err) { continue - } else if err != io.EOF { + } else if !errors.Is(err, io.EOF) { logger.Errorf("could not read udp packet: %v", err) } @@ -244,20 +314,21 @@ func (m *UDPMuxDefault) connWorker() { } if err = msg.Decode(); err != nil { - m.params.Logger.Warnf("Failed to handle decode ICE from %s: %v\n", addr.String(), err) + m.params.Logger.Warnf("Failed to handle decode ICE from %s: %v", addr.String(), err) continue } attr, stunAttrErr := msg.Get(stun.AttrUsername) if stunAttrErr != nil { - m.params.Logger.Warnf("No Username attribute in STUN message from %s\n", addr.String()) + m.params.Logger.Warnf("No Username attribute in STUN message from %s", addr.String()) continue } ufrag := strings.Split(string(attr), ":")[0] + isIPv6 := udpAddr.IP.To4() == nil m.mu.Lock() - destinationConn = m.conns[ufrag] + destinationConn, _ = m.getConn(ufrag, isIPv6) m.mu.Unlock() } @@ -272,12 +343,21 @@ func (m *UDPMuxDefault) connWorker() { } } +func (m *UDPMuxDefault) getConn(ufrag string, isIPv6 bool) (val *udpMuxedConn, ok bool) { + if isIPv6 { + val, ok = m.connsIPv6[ufrag] + } else { + val, ok = m.connsIPv4[ufrag] + } + return +} + type bufferHolder struct { - buffer []byte + buf []byte } func newBufferHolder(size int) *bufferHolder { return &bufferHolder{ - buffer: make([]byte, size), + buf: make([]byte, size), } } diff --git a/vendor/github.com/pion/ice/v2/udp_mux_multi.go b/vendor/github.com/pion/ice/v2/udp_mux_multi.go new file mode 100644 index 000000000..158cbc37f --- /dev/null +++ b/vendor/github.com/pion/ice/v2/udp_mux_multi.go @@ -0,0 +1,228 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package ice + +import ( + "fmt" + "net" + + "github.com/pion/logging" + "github.com/pion/transport/v2" + "github.com/pion/transport/v2/stdnet" +) + +// MultiUDPMuxDefault implements both UDPMux and AllConnsGetter, +// allowing users to pass multiple UDPMux instances to the ICE agent +// configuration. +type MultiUDPMuxDefault struct { + muxes []UDPMux + localAddrToMux map[string]UDPMux +} + +// NewMultiUDPMuxDefault creates an instance of MultiUDPMuxDefault that +// uses the provided UDPMux instances. +func NewMultiUDPMuxDefault(muxes ...UDPMux) *MultiUDPMuxDefault { + addrToMux := make(map[string]UDPMux) + for _, mux := range muxes { + for _, addr := range mux.GetListenAddresses() { + addrToMux[addr.String()] = mux + } + } + return &MultiUDPMuxDefault{ + muxes: muxes, + localAddrToMux: addrToMux, + } +} + +// GetConn returns a PacketConn given the connection's ufrag and network +// creates the connection if an existing one can't be found. +func (m *MultiUDPMuxDefault) GetConn(ufrag string, addr net.Addr) (net.PacketConn, error) { + mux, ok := m.localAddrToMux[addr.String()] + if !ok { + return nil, errNoUDPMuxAvailable + } + return mux.GetConn(ufrag, addr) +} + +// RemoveConnByUfrag stops and removes the muxed packet connection +// from all underlying UDPMux instances. +func (m *MultiUDPMuxDefault) RemoveConnByUfrag(ufrag string) { + for _, mux := range m.muxes { + mux.RemoveConnByUfrag(ufrag) + } +} + +// Close the multi mux, no further connections could be created +func (m *MultiUDPMuxDefault) Close() error { + var err error + for _, mux := range m.muxes { + if e := mux.Close(); e != nil { + err = e + } + } + return err +} + +// GetListenAddresses returns the list of addresses that this mux is listening on +func (m *MultiUDPMuxDefault) GetListenAddresses() []net.Addr { + addrs := make([]net.Addr, 0, len(m.localAddrToMux)) + for _, mux := range m.muxes { + addrs = append(addrs, mux.GetListenAddresses()...) + } + return addrs +} + +// NewMultiUDPMuxFromPort creates an instance of MultiUDPMuxDefault that +// listen all interfaces on the provided port. +func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMuxDefault, error) { + params := multiUDPMuxFromPortParam{ + networks: []NetworkType{NetworkTypeUDP4, NetworkTypeUDP6}, + } + for _, opt := range opts { + opt.apply(¶ms) + } + + if params.net == nil { + var err error + if params.net, err = stdnet.NewNet(); err != nil { + return nil, fmt.Errorf("failed to get create network: %w", err) + } + } + + ips, err := localInterfaces(params.net, params.ifFilter, params.ipFilter, params.networks, params.includeLoopback) + if err != nil { + return nil, err + } + + conns := make([]net.PacketConn, 0, len(ips)) + for _, ip := range ips { + conn, listenErr := params.net.ListenUDP("udp", &net.UDPAddr{IP: ip, Port: port}) + if listenErr != nil { + err = listenErr + break + } + if params.readBufferSize > 0 { + _ = conn.SetReadBuffer(params.readBufferSize) + } + if params.writeBufferSize > 0 { + _ = conn.SetWriteBuffer(params.writeBufferSize) + } + conns = append(conns, conn) + } + + if err != nil { + for _, conn := range conns { + _ = conn.Close() + } + return nil, err + } + + muxes := make([]UDPMux, 0, len(conns)) + for _, conn := range conns { + mux := NewUDPMuxDefault(UDPMuxParams{ + Logger: params.logger, + UDPConn: conn, + Net: params.net, + }) + muxes = append(muxes, mux) + } + + return NewMultiUDPMuxDefault(muxes...), nil +} + +// UDPMuxFromPortOption provide options for NewMultiUDPMuxFromPort +type UDPMuxFromPortOption interface { + apply(*multiUDPMuxFromPortParam) +} + +type multiUDPMuxFromPortParam struct { + ifFilter func(string) bool + ipFilter func(ip net.IP) bool + networks []NetworkType + readBufferSize int + writeBufferSize int + logger logging.LeveledLogger + includeLoopback bool + net transport.Net +} + +type udpMuxFromPortOption struct { + f func(*multiUDPMuxFromPortParam) +} + +func (o *udpMuxFromPortOption) apply(p *multiUDPMuxFromPortParam) { + o.f(p) +} + +// UDPMuxFromPortWithInterfaceFilter set the filter to filter out interfaces that should not be used +func UDPMuxFromPortWithInterfaceFilter(f func(string) bool) UDPMuxFromPortOption { + return &udpMuxFromPortOption{ + f: func(p *multiUDPMuxFromPortParam) { + p.ifFilter = f + }, + } +} + +// UDPMuxFromPortWithIPFilter set the filter to filter out IP addresses that should not be used +func UDPMuxFromPortWithIPFilter(f func(ip net.IP) bool) UDPMuxFromPortOption { + return &udpMuxFromPortOption{ + f: func(p *multiUDPMuxFromPortParam) { + p.ipFilter = f + }, + } +} + +// UDPMuxFromPortWithNetworks set the networks that should be used. default is both IPv4 and IPv6 +func UDPMuxFromPortWithNetworks(networks ...NetworkType) UDPMuxFromPortOption { + return &udpMuxFromPortOption{ + f: func(p *multiUDPMuxFromPortParam) { + p.networks = networks + }, + } +} + +// UDPMuxFromPortWithReadBufferSize set the UDP connection read buffer size +func UDPMuxFromPortWithReadBufferSize(size int) UDPMuxFromPortOption { + return &udpMuxFromPortOption{ + f: func(p *multiUDPMuxFromPortParam) { + p.readBufferSize = size + }, + } +} + +// UDPMuxFromPortWithWriteBufferSize set the UDP connection write buffer size +func UDPMuxFromPortWithWriteBufferSize(size int) UDPMuxFromPortOption { + return &udpMuxFromPortOption{ + f: func(p *multiUDPMuxFromPortParam) { + p.writeBufferSize = size + }, + } +} + +// UDPMuxFromPortWithLogger set the logger for the created UDPMux +func UDPMuxFromPortWithLogger(logger logging.LeveledLogger) UDPMuxFromPortOption { + return &udpMuxFromPortOption{ + f: func(p *multiUDPMuxFromPortParam) { + p.logger = logger + }, + } +} + +// UDPMuxFromPortWithLoopback set loopback interface should be included +func UDPMuxFromPortWithLoopback() UDPMuxFromPortOption { + return &udpMuxFromPortOption{ + f: func(p *multiUDPMuxFromPortParam) { + p.includeLoopback = true + }, + } +} + +// UDPMuxFromPortWithNet sets the network transport to use. +func UDPMuxFromPortWithNet(n transport.Net) UDPMuxFromPortOption { + return &udpMuxFromPortOption{ + f: func(p *multiUDPMuxFromPortParam) { + p.net = n + }, + } +} diff --git a/vendor/github.com/pion/ice/v2/udp_mux_universal.go b/vendor/github.com/pion/ice/v2/udp_mux_universal.go new file mode 100644 index 000000000..07b6a70ea --- /dev/null +++ b/vendor/github.com/pion/ice/v2/udp_mux_universal.go @@ -0,0 +1,271 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package ice + +import ( + "fmt" + "net" + "time" + + "github.com/pion/logging" + "github.com/pion/stun" + "github.com/pion/transport/v2" +) + +// UniversalUDPMux allows multiple connections to go over a single UDP port for +// host, server reflexive and relayed candidates. +// Actual connection muxing is happening in the UDPMux. +type UniversalUDPMux interface { + UDPMux + GetXORMappedAddr(stunAddr net.Addr, deadline time.Duration) (*stun.XORMappedAddress, error) + GetRelayedAddr(turnAddr net.Addr, deadline time.Duration) (*net.Addr, error) + GetConnForURL(ufrag string, url string, addr net.Addr) (net.PacketConn, error) +} + +// UniversalUDPMuxDefault handles STUN and TURN servers packets by wrapping the original UDPConn overriding ReadFrom. +// It the passes packets to the UDPMux that does the actual connection muxing. +type UniversalUDPMuxDefault struct { + *UDPMuxDefault + params UniversalUDPMuxParams + + // Since we have a shared socket, for srflx candidates it makes sense to have a shared mapped address across all the agents + // stun.XORMappedAddress indexed by the STUN server addr + xorMappedMap map[string]*xorMapped +} + +// UniversalUDPMuxParams are parameters for UniversalUDPMux server reflexive. +type UniversalUDPMuxParams struct { + Logger logging.LeveledLogger + UDPConn net.PacketConn + XORMappedAddrCacheTTL time.Duration + Net transport.Net +} + +// NewUniversalUDPMuxDefault creates an implementation of UniversalUDPMux embedding UDPMux +func NewUniversalUDPMuxDefault(params UniversalUDPMuxParams) *UniversalUDPMuxDefault { + if params.Logger == nil { + params.Logger = logging.NewDefaultLoggerFactory().NewLogger("ice") + } + if params.XORMappedAddrCacheTTL == 0 { + params.XORMappedAddrCacheTTL = time.Second * 25 + } + + m := &UniversalUDPMuxDefault{ + params: params, + xorMappedMap: make(map[string]*xorMapped), + } + + // Wrap UDP connection, process server reflexive messages + // before they are passed to the UDPMux connection handler (connWorker) + m.params.UDPConn = &udpConn{ + PacketConn: params.UDPConn, + mux: m, + logger: params.Logger, + } + + // Embed UDPMux + udpMuxParams := UDPMuxParams{ + Logger: params.Logger, + UDPConn: m.params.UDPConn, + Net: m.params.Net, + } + m.UDPMuxDefault = NewUDPMuxDefault(udpMuxParams) + + return m +} + +// udpConn is a wrapper around UDPMux conn that overrides ReadFrom and handles STUN/TURN packets +type udpConn struct { + net.PacketConn + mux *UniversalUDPMuxDefault + logger logging.LeveledLogger +} + +// GetRelayedAddr creates relayed connection to the given TURN service and returns the relayed addr. +// Not implemented yet. +func (m *UniversalUDPMuxDefault) GetRelayedAddr(net.Addr, time.Duration) (*net.Addr, error) { + return nil, errNotImplemented +} + +// GetConnForURL add uniques to the muxed connection by concatenating ufrag and URL (e.g. STUN URL) to be able to support multiple STUN/TURN servers +// and return a unique connection per server. +func (m *UniversalUDPMuxDefault) GetConnForURL(ufrag string, url string, addr net.Addr) (net.PacketConn, error) { + return m.UDPMuxDefault.GetConn(fmt.Sprintf("%s%s", ufrag, url), addr) +} + +// ReadFrom is called by UDPMux connWorker and handles packets coming from the STUN server discovering a mapped address. +// It passes processed packets further to the UDPMux (maybe this is not really necessary). +func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + n, addr, err = c.PacketConn.ReadFrom(p) + if err != nil { + return + } + + if stun.IsMessage(p[:n]) { + msg := &stun.Message{ + Raw: append([]byte{}, p[:n]...), + } + + if err = msg.Decode(); err != nil { + c.logger.Warnf("Failed to handle decode ICE from %s: %v", addr.String(), err) + err = nil + return + } + + udpAddr, ok := addr.(*net.UDPAddr) + if !ok { + // Message about this err will be logged in the UDPMux + return + } + + if c.mux.isXORMappedResponse(msg, udpAddr.String()) { + err = c.mux.handleXORMappedResponse(udpAddr, msg) + if err != nil { + c.logger.Debugf("%w: %v", errGetXorMappedAddrResponse, err) + err = nil + } + return + } + } + return n, addr, err +} + +// isXORMappedResponse indicates whether the message is a XORMappedAddress and is coming from the known STUN server. +func (m *UniversalUDPMuxDefault) isXORMappedResponse(msg *stun.Message, stunAddr string) bool { + m.mu.Lock() + defer m.mu.Unlock() + // Check first if it is a STUN server address because remote peer can also send similar messages but as a BindingSuccess + _, ok := m.xorMappedMap[stunAddr] + _, err := msg.Get(stun.AttrXORMappedAddress) + return err == nil && ok +} + +// handleXORMappedResponse parses response from the STUN server, extracts XORMappedAddress attribute +// and set the mapped address for the server +func (m *UniversalUDPMuxDefault) handleXORMappedResponse(stunAddr *net.UDPAddr, msg *stun.Message) error { + m.mu.Lock() + defer m.mu.Unlock() + + mappedAddr, ok := m.xorMappedMap[stunAddr.String()] + if !ok { + return errNoXorAddrMapping + } + + var addr stun.XORMappedAddress + if err := addr.GetFrom(msg); err != nil { + return err + } + + m.xorMappedMap[stunAddr.String()] = mappedAddr + mappedAddr.SetAddr(&addr) + + return nil +} + +// GetXORMappedAddr returns *stun.XORMappedAddress if already present for a given STUN server. +// Makes a STUN binding request to discover mapped address otherwise. +// Blocks until the stun.XORMappedAddress has been discovered or deadline. +// Method is safe for concurrent use. +func (m *UniversalUDPMuxDefault) GetXORMappedAddr(serverAddr net.Addr, deadline time.Duration) (*stun.XORMappedAddress, error) { + m.mu.Lock() + mappedAddr, ok := m.xorMappedMap[serverAddr.String()] + // If we already have a mapping for this STUN server (address already received) + // and if it is not too old we return it without making a new request to STUN server + if ok { + if mappedAddr.expired() { + mappedAddr.closeWaiters() + delete(m.xorMappedMap, serverAddr.String()) + ok = false + } else if mappedAddr.pending() { + ok = false + } + } + m.mu.Unlock() + if ok { + return mappedAddr.addr, nil + } + + // Otherwise, make a STUN request to discover the address + // or wait for already sent request to complete + waitAddrReceived, err := m.sendSTUN(serverAddr) + if err != nil { + return nil, fmt.Errorf("%w: %s", errSendSTUNPacket, err) //nolint:errorlint + } + + // Block until response was handled by the connWorker routine and XORMappedAddress was updated + select { + case <-waitAddrReceived: + // When channel closed, addr was obtained + m.mu.Lock() + mappedAddr := *m.xorMappedMap[serverAddr.String()] + m.mu.Unlock() + if mappedAddr.addr == nil { + return nil, errNoXorAddrMapping + } + return mappedAddr.addr, nil + case <-time.After(deadline): + return nil, errXORMappedAddrTimeout + } +} + +// sendSTUN sends a STUN request via UDP conn. +// +// The returned channel is closed when the STUN response has been received. +// Method is safe for concurrent use. +func (m *UniversalUDPMuxDefault) sendSTUN(serverAddr net.Addr) (chan struct{}, error) { + m.mu.Lock() + defer m.mu.Unlock() + + // If record present in the map, we already sent a STUN request, + // just wait when waitAddrReceived will be closed + addrMap, ok := m.xorMappedMap[serverAddr.String()] + if !ok { + addrMap = &xorMapped{ + expiresAt: time.Now().Add(m.params.XORMappedAddrCacheTTL), + waitAddrReceived: make(chan struct{}), + } + m.xorMappedMap[serverAddr.String()] = addrMap + } + + req, err := stun.Build(stun.BindingRequest, stun.TransactionID) + if err != nil { + return nil, err + } + + if _, err = m.params.UDPConn.WriteTo(req.Raw, serverAddr); err != nil { + return nil, err + } + + return addrMap.waitAddrReceived, nil +} + +type xorMapped struct { + addr *stun.XORMappedAddress + waitAddrReceived chan struct{} + expiresAt time.Time +} + +func (a *xorMapped) closeWaiters() { + select { + case <-a.waitAddrReceived: + // Notify was close, ok, that means we received duplicate response just exit + break + default: + // Notify tha twe have a new addr + close(a.waitAddrReceived) + } +} + +func (a *xorMapped) pending() bool { + return a.addr == nil +} + +func (a *xorMapped) expired() bool { + return a.expiresAt.Before(time.Now()) +} + +func (a *xorMapped) SetAddr(addr *stun.XORMappedAddress) { + a.addr = addr + a.closeWaiters() +} diff --git a/vendor/github.com/pion/ice/v2/udp_muxed_conn.go b/vendor/github.com/pion/ice/v2/udp_muxed_conn.go index 35169af35..09e4b3a8e 100644 --- a/vendor/github.com/pion/ice/v2/udp_muxed_conn.go +++ b/vendor/github.com/pion/ice/v2/udp_muxed_conn.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import ( @@ -8,7 +11,7 @@ import ( "time" "github.com/pion/logging" - "github.com/pion/transport/packetio" + "github.com/pion/transport/v2/packetio" ) type udpMuxedConnParams struct { @@ -22,11 +25,11 @@ type udpMuxedConnParams struct { // udpMuxedConn represents a logical packet conn for a single remote as identified by ufrag type udpMuxedConn struct { params *udpMuxedConnParams - // remote addresses that we have sent to on this conn + // Remote addresses that we have sent to on this conn addresses []string - // channel holding incoming packets - buffer *packetio.Buffer + // Channel holding incoming packets + buf *packetio.Buffer closedChan chan struct{} closeOnce sync.Once mu sync.Mutex @@ -35,70 +38,70 @@ type udpMuxedConn struct { func newUDPMuxedConn(params *udpMuxedConnParams) *udpMuxedConn { p := &udpMuxedConn{ params: params, - buffer: packetio.NewBuffer(), + buf: packetio.NewBuffer(), closedChan: make(chan struct{}), } return p } -func (c *udpMuxedConn) ReadFrom(b []byte) (n int, raddr net.Addr, err error) { - buf := c.params.AddrPool.Get().(*bufferHolder) +func (c *udpMuxedConn) ReadFrom(b []byte) (n int, rAddr net.Addr, err error) { + buf := c.params.AddrPool.Get().(*bufferHolder) //nolint:forcetypeassert defer c.params.AddrPool.Put(buf) - // read address - total, err := c.buffer.Read(buf.buffer) + // Read address + total, err := c.buf.Read(buf.buf) if err != nil { return 0, nil, err } - dataLen := int(binary.LittleEndian.Uint16(buf.buffer[:2])) + dataLen := int(binary.LittleEndian.Uint16(buf.buf[:2])) if dataLen > total || dataLen > len(b) { return 0, nil, io.ErrShortBuffer } - // read data and then address + // Read data and then address offset := 2 - copy(b, buf.buffer[offset:offset+dataLen]) + copy(b, buf.buf[offset:offset+dataLen]) offset += dataLen - // read address len & decode address - addrLen := int(binary.LittleEndian.Uint16(buf.buffer[offset : offset+2])) + // Read address len & decode address + addrLen := int(binary.LittleEndian.Uint16(buf.buf[offset : offset+2])) offset += 2 - if raddr, err = decodeUDPAddr(buf.buffer[offset : offset+addrLen]); err != nil { + if rAddr, err = decodeUDPAddr(buf.buf[offset : offset+addrLen]); err != nil { return 0, nil, err } - return dataLen, raddr, nil + return dataLen, rAddr, nil } -func (c *udpMuxedConn) WriteTo(buf []byte, raddr net.Addr) (n int, err error) { +func (c *udpMuxedConn) WriteTo(buf []byte, rAddr net.Addr) (n int, err error) { if c.isClosed() { return 0, io.ErrClosedPipe } - // each time we write to a new address, we'll register it with the mux - addr := raddr.String() + // Each time we write to a new address, we'll register it with the mux + addr := rAddr.String() if !c.containsAddress(addr) { c.addAddress(addr) } - return c.params.Mux.writeTo(buf, raddr) + return c.params.Mux.writeTo(buf, rAddr) } func (c *udpMuxedConn) LocalAddr() net.Addr { return c.params.LocalAddr } -func (c *udpMuxedConn) SetDeadline(tm time.Time) error { +func (c *udpMuxedConn) SetDeadline(time.Time) error { return nil } -func (c *udpMuxedConn) SetReadDeadline(tm time.Time) error { +func (c *udpMuxedConn) SetReadDeadline(time.Time) error { return nil } -func (c *udpMuxedConn) SetWriteDeadline(tm time.Time) error { +func (c *udpMuxedConn) SetWriteDeadline(time.Time) error { return nil } @@ -109,12 +112,9 @@ func (c *udpMuxedConn) CloseChannel() <-chan struct{} { func (c *udpMuxedConn) Close() error { var err error c.closeOnce.Do(func() { - err = c.buffer.Close() + err = c.buf.Close() close(c.closedChan) }) - c.mu.Lock() - defer c.mu.Unlock() - c.addresses = nil return err } @@ -140,7 +140,7 @@ func (c *udpMuxedConn) addAddress(addr string) { c.addresses = append(c.addresses, addr) c.mu.Unlock() - // map it on mux + // Map it on mux c.params.Mux.registerConnForAddress(c, addr) } @@ -170,51 +170,51 @@ func (c *udpMuxedConn) containsAddress(addr string) bool { } func (c *udpMuxedConn) writePacket(data []byte, addr *net.UDPAddr) error { - // write two packets, address and data - buf := c.params.AddrPool.Get().(*bufferHolder) + // Write two packets, address and data + buf := c.params.AddrPool.Get().(*bufferHolder) //nolint:forcetypeassert defer c.params.AddrPool.Put(buf) - // format of buffer | data len | data bytes | addr len | addr bytes | - if len(buf.buffer) < len(data)+maxAddrSize { + // Format of buffer | data len | data bytes | addr len | addr bytes | + if len(buf.buf) < len(data)+maxAddrSize { return io.ErrShortBuffer } - // data len - binary.LittleEndian.PutUint16(buf.buffer, uint16(len(data))) + // Data length + binary.LittleEndian.PutUint16(buf.buf, uint16(len(data))) offset := 2 - // data - copy(buf.buffer[offset:], data) + // Data + copy(buf.buf[offset:], data) offset += len(data) - // write address first, leaving room for its length - n, err := encodeUDPAddr(addr, buf.buffer[offset+2:]) + // Write address first, leaving room for its length + n, err := encodeUDPAddr(addr, buf.buf[offset+2:]) if err != nil { - return nil + return err } total := offset + n + 2 - // address len - binary.LittleEndian.PutUint16(buf.buffer[offset:], uint16(n)) + // Address len + binary.LittleEndian.PutUint16(buf.buf[offset:], uint16(n)) - if _, err := c.buffer.Write(buf.buffer[:total]); err != nil { + if _, err := c.buf.Write(buf.buf[:total]); err != nil { return err } return nil } func encodeUDPAddr(addr *net.UDPAddr, buf []byte) (int, error) { - ipdata, err := addr.IP.MarshalText() + ipData, err := addr.IP.MarshalText() if err != nil { return 0, err } - total := 2 + len(ipdata) + 2 + len(addr.Zone) + total := 2 + len(ipData) + 2 + len(addr.Zone) if total > len(buf) { return 0, io.ErrShortBuffer } - binary.LittleEndian.PutUint16(buf, uint16(len(ipdata))) + binary.LittleEndian.PutUint16(buf, uint16(len(ipData))) offset := 2 - n := copy(buf[offset:], ipdata) + n := copy(buf[offset:], ipData) offset += n binary.LittleEndian.PutUint16(buf[offset:], uint16(addr.Port)) offset += 2 @@ -228,7 +228,7 @@ func decodeUDPAddr(buf []byte) (*net.UDPAddr, error) { offset := 0 ipLen := int(binary.LittleEndian.Uint16(buf[:2])) offset += 2 - // basic bounds checking + // Basic bounds checking if ipLen+offset > len(buf) { return nil, io.ErrShortBuffer } diff --git a/vendor/github.com/pion/ice/v2/url.go b/vendor/github.com/pion/ice/v2/url.go index 390591e3d..b2b9f8bd3 100644 --- a/vendor/github.com/pion/ice/v2/url.go +++ b/vendor/github.com/pion/ice/v2/url.go @@ -1,225 +1,82 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice -import ( - "net" - "net/url" - "strconv" +import "github.com/pion/stun" + +type ( + // URL represents a STUN (rfc7064) or TURN (rfc7065) URI + // + // Deprecated: Please use pion/stun.URI + URL = stun.URI + + // ProtoType indicates the transport protocol type that is used in the ice.URL + // structure. + // + // Deprecated: TPlease use pion/stun.ProtoType + ProtoType = stun.ProtoType + + // SchemeType indicates the type of server used in the ice.URL structure. + // + // Deprecated: Please use pion/stun.SchemeType + SchemeType = stun.SchemeType ) -// SchemeType indicates the type of server used in the ice.URL structure. -type SchemeType int - -// Unknown defines default public constant to use for "enum" like struct -// comparisons when no value was defined. -const Unknown = iota - const ( // SchemeTypeSTUN indicates the URL represents a STUN server. - SchemeTypeSTUN SchemeType = iota + 1 + // + // Deprecated: Please use pion/stun.SchemeTypeSTUN + SchemeTypeSTUN = stun.SchemeTypeSTUN // SchemeTypeSTUNS indicates the URL represents a STUNS (secure) server. - SchemeTypeSTUNS + // + // Deprecated: Please use pion/stun.SchemeTypeSTUNS + SchemeTypeSTUNS = stun.SchemeTypeSTUNS // SchemeTypeTURN indicates the URL represents a TURN server. - SchemeTypeTURN + // + // Deprecated: Please use pion/stun.SchemeTypeTURN + SchemeTypeTURN = stun.SchemeTypeTURN // SchemeTypeTURNS indicates the URL represents a TURNS (secure) server. - SchemeTypeTURNS + // + // Deprecated: Please use pion/stun.SchemeTypeTURNS + SchemeTypeTURNS = stun.SchemeTypeTURNS ) -// NewSchemeType defines a procedure for creating a new SchemeType from a raw -// string naming the scheme type. -func NewSchemeType(raw string) SchemeType { - switch raw { - case "stun": - return SchemeTypeSTUN - case "stuns": - return SchemeTypeSTUNS - case "turn": - return SchemeTypeTURN - case "turns": - return SchemeTypeTURNS - default: - return SchemeType(Unknown) - } -} - -func (t SchemeType) String() string { - switch t { - case SchemeTypeSTUN: - return "stun" - case SchemeTypeSTUNS: - return "stuns" - case SchemeTypeTURN: - return "turn" - case SchemeTypeTURNS: - return "turns" - default: - return ErrUnknownType.Error() - } -} - -// ProtoType indicates the transport protocol type that is used in the ice.URL -// structure. -type ProtoType int - const ( // ProtoTypeUDP indicates the URL uses a UDP transport. - ProtoTypeUDP ProtoType = iota + 1 + // + // Deprecated: Please use pion/stun.ProtoTypeUDP + ProtoTypeUDP = stun.ProtoTypeUDP // ProtoTypeTCP indicates the URL uses a TCP transport. - ProtoTypeTCP + // + // Deprecated: Please use pion/stun.ProtoTypeTCP + ProtoTypeTCP = stun.ProtoTypeTCP ) -// NewProtoType defines a procedure for creating a new ProtoType from a raw -// string naming the transport protocol type. -func NewProtoType(raw string) ProtoType { - switch raw { - case "udp": - return ProtoTypeUDP - case "tcp": - return ProtoTypeTCP - default: - return ProtoType(Unknown) - } -} - -func (t ProtoType) String() string { - switch t { - case ProtoTypeUDP: - return "udp" - case ProtoTypeTCP: - return "tcp" - default: - return ErrUnknownType.Error() - } -} - -// URL represents a STUN (rfc7064) or TURN (rfc7065) URL -type URL struct { - Scheme SchemeType - Host string - Port int - Username string - Password string - Proto ProtoType -} +// Unknown represents and unknown ProtoType or SchemeType +// +// Deprecated: Please use pion/stun.SchemeTypeUnknown or pion/stun.ProtoTypeUnknown +const Unknown = 0 // ParseURL parses a STUN or TURN urls following the ABNF syntax described in // https://tools.ietf.org/html/rfc7064 and https://tools.ietf.org/html/rfc7065 // respectively. -func ParseURL(raw string) (*URL, error) { //nolint:gocognit - rawParts, err := url.Parse(raw) - if err != nil { - return nil, err - } +// +// Deprecated: Please use pion/stun.ParseURI +var ParseURL = stun.ParseURI //nolint:gochecknoglobals - var u URL - u.Scheme = NewSchemeType(rawParts.Scheme) - if u.Scheme == SchemeType(Unknown) { - return nil, ErrSchemeType - } +// NewSchemeType defines a procedure for creating a new SchemeType from a raw +// string naming the scheme type. +// +// Deprecated: Please use pion/stun.NewSchemeType +var NewSchemeType = stun.NewSchemeType //nolint:gochecknoglobals - var rawPort string - if u.Host, rawPort, err = net.SplitHostPort(rawParts.Opaque); err != nil { - if e, ok := err.(*net.AddrError); ok { - if e.Err == "missing port in address" { - nextRawURL := u.Scheme.String() + ":" + rawParts.Opaque - switch { - case u.Scheme == SchemeTypeSTUN || u.Scheme == SchemeTypeTURN: - nextRawURL += ":3478" - if rawParts.RawQuery != "" { - nextRawURL += "?" + rawParts.RawQuery - } - return ParseURL(nextRawURL) - case u.Scheme == SchemeTypeSTUNS || u.Scheme == SchemeTypeTURNS: - nextRawURL += ":5349" - if rawParts.RawQuery != "" { - nextRawURL += "?" + rawParts.RawQuery - } - return ParseURL(nextRawURL) - } - } - } - return nil, err - } - - if u.Host == "" { - return nil, ErrHost - } - - if u.Port, err = strconv.Atoi(rawPort); err != nil { - return nil, ErrPort - } - - switch u.Scheme { - case SchemeTypeSTUN: - qArgs, err := url.ParseQuery(rawParts.RawQuery) - if err != nil || len(qArgs) > 0 { - return nil, ErrSTUNQuery - } - u.Proto = ProtoTypeUDP - case SchemeTypeSTUNS: - qArgs, err := url.ParseQuery(rawParts.RawQuery) - if err != nil || len(qArgs) > 0 { - return nil, ErrSTUNQuery - } - u.Proto = ProtoTypeTCP - case SchemeTypeTURN: - proto, err := parseProto(rawParts.RawQuery) - if err != nil { - return nil, err - } - - u.Proto = proto - if u.Proto == ProtoType(Unknown) { - u.Proto = ProtoTypeUDP - } - case SchemeTypeTURNS: - proto, err := parseProto(rawParts.RawQuery) - if err != nil { - return nil, err - } - - u.Proto = proto - if u.Proto == ProtoType(Unknown) { - u.Proto = ProtoTypeTCP - } - } - - return &u, nil -} - -func parseProto(raw string) (ProtoType, error) { - qArgs, err := url.ParseQuery(raw) - if err != nil || len(qArgs) > 1 { - return ProtoType(Unknown), ErrInvalidQuery - } - - var proto ProtoType - if rawProto := qArgs.Get("transport"); rawProto != "" { - if proto = NewProtoType(rawProto); proto == ProtoType(0) { - return ProtoType(Unknown), ErrProtoType - } - return proto, nil - } - - if len(qArgs) > 0 { - return ProtoType(Unknown), ErrInvalidQuery - } - - return proto, nil -} - -func (u URL) String() string { - rawURL := u.Scheme.String() + ":" + net.JoinHostPort(u.Host, strconv.Itoa(u.Port)) - if u.Scheme == SchemeTypeTURN || u.Scheme == SchemeTypeTURNS { - rawURL += "?transport=" + u.Proto.String() - } - return rawURL -} - -// IsSecure returns whether the this URL's scheme describes secure scheme or not. -func (u URL) IsSecure() bool { - return u.Scheme == SchemeTypeSTUNS || u.Scheme == SchemeTypeTURNS -} +// NewProtoType defines a procedure for creating a new ProtoType from a raw +// string naming the transport protocol type. +// +// Deprecated: Please use pion/stun.NewProtoType +var NewProtoType = stun.NewProtoType //nolint:gochecknoglobals diff --git a/vendor/github.com/pion/ice/v2/usecandidate.go b/vendor/github.com/pion/ice/v2/usecandidate.go index f168c08eb..6fc7ed50c 100644 --- a/vendor/github.com/pion/ice/v2/usecandidate.go +++ b/vendor/github.com/pion/ice/v2/usecandidate.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package ice import "github.com/pion/stun" diff --git a/vendor/github.com/pion/ice/v2/util.go b/vendor/github.com/pion/ice/v2/util.go deleted file mode 100644 index 7eb13c803..000000000 --- a/vendor/github.com/pion/ice/v2/util.go +++ /dev/null @@ -1,233 +0,0 @@ -package ice - -import ( - "fmt" - "net" - "sync/atomic" - "time" - - "github.com/pion/logging" - "github.com/pion/stun" - "github.com/pion/transport/vnet" -) - -type atomicError struct{ v atomic.Value } - -func (a *atomicError) Store(err error) { - a.v.Store(struct{ error }{err}) -} - -func (a *atomicError) Load() error { - err, _ := a.v.Load().(struct{ error }) - return err.error -} - -// The conditions of invalidation written below are defined in -// https://tools.ietf.org/html/rfc8445#section-5.1.1.1 -func isSupportedIPv6(ip net.IP) bool { - if len(ip) != net.IPv6len || - isZeros(ip[0:12]) || // !(IPv4-compatible IPv6) - ip[0] == 0xfe && ip[1]&0xc0 == 0xc0 || // !(IPv6 site-local unicast) - ip.IsLinkLocalUnicast() || - ip.IsLinkLocalMulticast() { - return false - } - return true -} - -func isZeros(ip net.IP) bool { - for i := 0; i < len(ip); i++ { - if ip[i] != 0 { - return false - } - } - return true -} - -func parseAddr(in net.Addr) (net.IP, int, NetworkType, bool) { - switch addr := in.(type) { - case *net.UDPAddr: - return addr.IP, addr.Port, NetworkTypeUDP4, true - case *net.TCPAddr: - return addr.IP, addr.Port, NetworkTypeTCP4, true - } - return nil, 0, 0, false -} - -func createAddr(network NetworkType, ip net.IP, port int) net.Addr { - switch { - case network.IsTCP(): - return &net.TCPAddr{IP: ip, Port: port} - default: - return &net.UDPAddr{IP: ip, Port: port} - } -} - -func addrEqual(a, b net.Addr) bool { - aIP, aPort, aType, aOk := parseAddr(a) - if !aOk { - return false - } - - bIP, bPort, bType, bOk := parseAddr(b) - if !bOk { - return false - } - - return aType == bType && aIP.Equal(bIP) && aPort == bPort -} - -// getXORMappedAddr initiates a stun requests to serverAddr using conn, reads the response and returns -// the XORMappedAddress returned by the stun server. -// -// Adapted from stun v0.2. -func getXORMappedAddr(conn net.PacketConn, serverAddr net.Addr, deadline time.Duration) (*stun.XORMappedAddress, error) { - if deadline > 0 { - if err := conn.SetReadDeadline(time.Now().Add(deadline)); err != nil { - return nil, err - } - } - defer func() { - if deadline > 0 { - _ = conn.SetReadDeadline(time.Time{}) - } - }() - resp, err := stunRequest( - func(p []byte) (int, error) { - n, _, errr := conn.ReadFrom(p) - return n, errr - }, - func(b []byte) (int, error) { - return conn.WriteTo(b, serverAddr) - }, - ) - if err != nil { - return nil, err - } - var addr stun.XORMappedAddress - if err = addr.GetFrom(resp); err != nil { - return nil, fmt.Errorf("%w: %v", errGetXorMappedAddrResponse, err) - } - return &addr, nil -} - -func stunRequest(read func([]byte) (int, error), write func([]byte) (int, error)) (*stun.Message, error) { - req, err := stun.Build(stun.BindingRequest, stun.TransactionID) - if err != nil { - return nil, err - } - if _, err = write(req.Raw); err != nil { - return nil, err - } - const maxMessageSize = 1280 - bs := make([]byte, maxMessageSize) - n, err := read(bs) - if err != nil { - return nil, err - } - res := &stun.Message{Raw: bs[:n]} - if err := res.Decode(); err != nil { - return nil, err - } - return res, nil -} - -func localInterfaces(vnet *vnet.Net, interfaceFilter func(string) bool, networkTypes []NetworkType) ([]net.IP, error) { //nolint:gocognit - ips := []net.IP{} - ifaces, err := vnet.Interfaces() - if err != nil { - return ips, err - } - - var IPv4Requested, IPv6Requested bool - for _, typ := range networkTypes { - if typ.IsIPv4() { - IPv4Requested = true - } - - if typ.IsIPv6() { - IPv6Requested = true - } - } - - for _, iface := range ifaces { - if iface.Flags&net.FlagUp == 0 { - continue // interface down - } - if iface.Flags&net.FlagLoopback != 0 { - continue // loopback interface - } - - if interfaceFilter != nil && !interfaceFilter(iface.Name) { - continue - } - - addrs, err := iface.Addrs() - if err != nil { - continue - } - - for _, addr := range addrs { - var ip net.IP - switch addr := addr.(type) { - case *net.IPNet: - ip = addr.IP - case *net.IPAddr: - ip = addr.IP - } - if ip == nil || ip.IsLoopback() { - continue - } - - if ipv4 := ip.To4(); ipv4 == nil { - if !IPv6Requested { - continue - } else if !isSupportedIPv6(ip) { - continue - } - } else if !IPv4Requested { - continue - } - - ips = append(ips, ip) - } - } - return ips, nil -} - -func listenUDPInPortRange(vnet *vnet.Net, log logging.LeveledLogger, portMax, portMin int, network string, laddr *net.UDPAddr) (vnet.UDPPacketConn, error) { - if (laddr.Port != 0) || ((portMin == 0) && (portMax == 0)) { - return vnet.ListenUDP(network, laddr) - } - var i, j int - i = portMin - if i == 0 { - i = 1 - } - j = portMax - if j == 0 { - j = 0xFFFF - } - if i > j { - return nil, ErrPort - } - - portStart := globalMathRandomGenerator.Intn(j-i+1) + i - portCurrent := portStart - for { - laddr = &net.UDPAddr{IP: laddr.IP, Port: portCurrent} - c, e := vnet.ListenUDP(network, laddr) - if e == nil { - return c, e - } - log.Debugf("failed to listen %s: %v", laddr.String(), e) - portCurrent++ - if portCurrent > j { - portCurrent = i - } - if portCurrent == portStart { - break - } - } - return nil, ErrPort -} diff --git a/vendor/github.com/pion/interceptor/.gitignore b/vendor/github.com/pion/interceptor/.gitignore index 83db74ba5..6e2f206a9 100644 --- a/vendor/github.com/pion/interceptor/.gitignore +++ b/vendor/github.com/pion/interceptor/.gitignore @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + ### JetBrains IDE ### ##################### .idea/ @@ -22,3 +25,4 @@ cover.out *.wasm examples/sfu-ws/cert.pem examples/sfu-ws/key.pem +wasm_exec.js diff --git a/vendor/github.com/pion/interceptor/.golangci.yml b/vendor/github.com/pion/interceptor/.golangci.yml index d6162c970..4e3eddf42 100644 --- a/vendor/github.com/pion/interceptor/.golangci.yml +++ b/vendor/github.com/pion/interceptor/.golangci.yml @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + linters-settings: govet: check-shadowing: true @@ -10,19 +13,34 @@ linters-settings: modules: - github.com/pkg/errors: recommendations: - - errors + - errors + forbidigo: + forbid: + - ^fmt.Print(f|ln)?$ + - ^log.(Panic|Fatal|Print)(f|ln)?$ + - ^os.Exit$ + - ^panic$ + - ^print(ln)?$ linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully - - deadcode # Finds unused code + - contextcheck # check the function whether use a non-inherited context + - decorder # check declaration order and count of types, constants, variables and functions - depguard # Go linter that checks if package imports are in a list of acceptable packages - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection + - durationcheck # check for two durations multiplied together - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. - exhaustive # check exhaustiveness of enum switch statements - exportloopref # checks for pointers to enclosing loop variables + - forbidigo # Forbids identifiers + - forcetypeassert # finds forced type assertions - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - gochecknoinits # Checks that no init functions are present in Go code @@ -35,40 +53,59 @@ linters: - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - gosimple # Linter for Go source code that specializes in simplifying a code - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - grouper # An analyzer to analyze expression groups. + - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used - misspell # Finds commonly misspelled English words in comments - nakedret # Finds naked returns in functions greater than a specified function length + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. - noctx # noctx finds sending http request without context.Context - - scopelint # Scopelint checks for unpinned variables in go programs + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # golint replacement, finds style mistakes - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - - structcheck # Finds unused struct fields - stylecheck # Stylecheck is a replacement for golint + - tagliatelle # Checks the struct tags. + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types - - varcheck # Finds unused global variables and constants + - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: + - containedctx # containedctx is a linter that detects struct contained context.Context field + - cyclop # checks function and package cyclomatic complexity + - exhaustivestruct # Checks if all struct's fields are initialized - funlen # Tool for detection of long functions - gocyclo # Computes and checks the cyclomatic complexity of functions - godot # Check if comments end in a period - gomnd # An analyzer to detect magic numbers. + - ifshort # Checks that your code uses short syntax for if-statements whenever possible + - ireturn # Accept Interfaces, Return Concrete Types - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - nestif # Reports deeply nested if statements - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - nolintlint # Reports ill-formed or insufficient nolint directives + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - prealloc # Finds slice declarations that could potentially be preallocated + - promlinter # Check Prometheus metrics naming via promlint - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. - testpackage # linter that makes you use a separate _test package + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers + - varnamelen # checks that the length of a variable's name matches its scope + - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! issues: @@ -78,12 +115,23 @@ issues: - path: _test\.go linters: - gocognit + - forbidigo # Allow complex main function in examples - path: examples text: "of func `main` is high" linters: - gocognit + + # Allow forbidden identifiers in examples + - path: examples + linters: + - forbidigo + + # Allow forbidden identifiers in CLI commands + - path: cmd + linters: + - forbidigo run: skip-dirs-use-default: false diff --git a/vendor/github.com/pion/interceptor/.goreleaser.yml b/vendor/github.com/pion/interceptor/.goreleaser.yml new file mode 100644 index 000000000..30093e9d6 --- /dev/null +++ b/vendor/github.com/pion/interceptor/.goreleaser.yml @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + +builds: +- skip: true diff --git a/vendor/github.com/pion/interceptor/AUTHORS.txt b/vendor/github.com/pion/interceptor/AUTHORS.txt index ea5d0e561..44a421ef8 100644 --- a/vendor/github.com/pion/interceptor/AUTHORS.txt +++ b/vendor/github.com/pion/interceptor/AUTHORS.txt @@ -2,15 +2,29 @@ # we would love to have you https://github.com/pion/webrtc/wiki/Contributing # # This file is auto generated, using git to list all individuals contributors. -# see `.github/generate-authors.sh` for the scripting +# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting +Aaron Boushley Adam Kiss adamroach +Aditya Kumar aler9 <46489434+aler9@users.noreply.github.com> Antoine Baché Atsushi Watanabe +Bobby Peck boks1971 David Zhao Jonathan Müller Kevin Caffrey +Maksim Nesterov Mathis Engelbart +Miroslav +Miroslav Šedivý +Quentin Renard +Rayleigh Li Sean DuBois +Steffen Vogel +XLPolar +ziminghua <565209960@qq.com> + +# List of contributors not appearing in Git history + diff --git a/vendor/github.com/pion/interceptor/LICENSE b/vendor/github.com/pion/interceptor/LICENSE index ab602974d..491caf6b0 100644 --- a/vendor/github.com/pion/interceptor/LICENSE +++ b/vendor/github.com/pion/interceptor/LICENSE @@ -1,21 +1,9 @@ MIT License -Copyright (c) 2018 +Copyright (c) 2023 The Pion community -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: +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 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. +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/pion/interceptor/README.md b/vendor/github.com/pion/interceptor/README.md index 8c67a46df..dcc856f1e 100644 --- a/vendor/github.com/pion/interceptor/README.md +++ b/vendor/github.com/pion/interceptor/README.md @@ -8,7 +8,8 @@ Pion Interceptor Slack Widget
- GoDoc + GitHub Workflow Status + Go Reference Coverage Status Go Report Card License: MIT @@ -27,19 +28,20 @@ by anyone. With the following tenets in mind. * Encourage modification. Add your own interceptors without forking. Mixing with the ones we provide. * Empower learning. This code base should be useful to read and learn even if you aren't using Pion. -#### Current Interceptors +### Current Interceptors * [NACK Generator/Responder](https://github.com/pion/interceptor/tree/master/pkg/nack) * [Sender and Receiver Reports](https://github.com/pion/interceptor/tree/master/pkg/report) * [Transport Wide Congestion Control Feedback](https://github.com/pion/interceptor/tree/master/pkg/twcc) * [Packet Dump](https://github.com/pion/interceptor/tree/master/pkg/packetdump) +* [Google Congestion Control](https://github.com/pion/interceptor/tree/master/pkg/gcc) +* [Stats](https://github.com/pion/interceptor/tree/master/pkg/stats) A [webrtc-stats](https://www.w3.org/TR/webrtc-stats/) compliant statistics generation +* [Interval PLI](https://github.com/pion/interceptor/tree/master/pkg/intervalpli) Generate PLI on a interval. Useful when no decoder is available. -#### Planned Interceptors +### Planned Interceptors * Bandwidth Estimation - [NADA](https://tools.ietf.org/html/rfc8698) - - [Google Congestion Control](https://tools.ietf.org/html/draft-ietf-rmcat-gcc-02) * JitterBuffer, re-order packets and wait for arrival * [FlexFec](https://tools.ietf.org/html/draft-ietf-payload-flexible-fec-scheme-20) -* [webrtc-stats](https://www.w3.org/TR/webrtc-stats/) compliant statistics generation * [RTCP Feedback for Congestion Control](https://datatracker.ietf.org/doc/html/rfc8888) the standardized alternative to TWCC. ### Interceptor Public API @@ -64,15 +66,19 @@ sequentially as the packet moves through them. The [examples](https://github.com/pion/interceptor/blob/master/examples) directory provides some basic examples. If you need more please file an issue! You should also look in [pion/webrtc](https://github.com/pion/webrtc) for real world examples. +### Roadmap +The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones. + ### Community -Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion). +Pion has an active community on the [Slack](https://pion.ly/slack). + +Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news. We are always looking to support **your projects**. Please reach out if you have something to build! - If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly) ### Contributing -Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible: +Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible: [AUTHORS.txt](./AUTHORS.txt) ### License MIT License - see [LICENSE](LICENSE) for full text diff --git a/vendor/github.com/pion/interceptor/attributes.go b/vendor/github.com/pion/interceptor/attributes.go index 603315da3..d7936d526 100644 --- a/vendor/github.com/pion/interceptor/attributes.go +++ b/vendor/github.com/pion/interceptor/attributes.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package interceptor import ( diff --git a/vendor/github.com/pion/interceptor/chain.go b/vendor/github.com/pion/interceptor/chain.go index d53c30714..267f36651 100644 --- a/vendor/github.com/pion/interceptor/chain.go +++ b/vendor/github.com/pion/interceptor/chain.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package interceptor // Chain is an interceptor that runs all child interceptors in order. diff --git a/vendor/github.com/pion/interceptor/codecov.yml b/vendor/github.com/pion/interceptor/codecov.yml index 085200a48..263e4d45c 100644 --- a/vendor/github.com/pion/interceptor/codecov.yml +++ b/vendor/github.com/pion/interceptor/codecov.yml @@ -3,6 +3,8 @@ # # It is automatically copied from https://github.com/pion/.goassets repository. # +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT coverage: status: diff --git a/vendor/github.com/pion/interceptor/errors.go b/vendor/github.com/pion/interceptor/errors.go index 45e1252c9..3dafee3ef 100644 --- a/vendor/github.com/pion/interceptor/errors.go +++ b/vendor/github.com/pion/interceptor/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package interceptor import ( @@ -18,7 +21,7 @@ func flattenErrs(errs []error) error { return multiError(errs2) } -type multiError []error +type multiError []error //nolint func (me multiError) Error() string { var errstrings []string @@ -41,7 +44,7 @@ func (me multiError) Is(err error) bool { if errors.Is(e, err) { return true } - if me2, ok := e.(multiError); ok { + if me2, ok := e.(multiError); ok { //nolint if me2.Is(err) { return true } diff --git a/vendor/github.com/pion/interceptor/interceptor.go b/vendor/github.com/pion/interceptor/interceptor.go index a143fdd03..c6ba53242 100644 --- a/vendor/github.com/pion/interceptor/interceptor.go +++ b/vendor/github.com/pion/interceptor/interceptor.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package interceptor contains the Interceptor interface, with some useful interceptors that should be safe to use // in most cases. package interceptor @@ -17,7 +20,6 @@ type Factory interface { // Interceptor can be used to add functionality to you PeerConnections by modifying any incoming/outgoing rtp/rtcp // packets, or sending your own packets as needed. type Interceptor interface { - // BindRTCPReader lets you modify any incoming RTCP packets. It is called once per sender/receiver, however this might // change in the future. The returned method will be called once per packet batch. BindRTCPReader(reader RTCPReader) RTCPReader diff --git a/vendor/github.com/pion/interceptor/internal/ntp/ntp.go b/vendor/github.com/pion/interceptor/internal/ntp/ntp.go new file mode 100644 index 000000000..69f98d6c9 --- /dev/null +++ b/vendor/github.com/pion/interceptor/internal/ntp/ntp.go @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package ntp provides conversion methods between time.Time and NTP timestamps +// stored in uint64 +package ntp + +import ( + "time" +) + +// ToNTP converts a time.Time oboject to an uint64 NTP timestamp +func ToNTP(t time.Time) uint64 { + // seconds since 1st January 1900 + s := (float64(t.UnixNano()) / 1000000000) + 2208988800 + + // higher 32 bits are the integer part, lower 32 bits are the fractional part + integerPart := uint32(s) + fractionalPart := uint32((s - float64(integerPart)) * 0xFFFFFFFF) + return uint64(integerPart)<<32 | uint64(fractionalPart) +} + +// ToTime converts a uint64 NTP timestamps to a time.Time object +func ToTime(t uint64) time.Time { + seconds := (t & 0xFFFFFFFF00000000) >> 32 + fractional := float64(t&0x00000000FFFFFFFF) / float64(0xFFFFFFFF) + d := time.Duration(seconds)*time.Second + time.Duration(fractional*1e9)*time.Nanosecond + + return time.Unix(0, 0).Add(-2208988800 * time.Second).Add(d) +} diff --git a/vendor/github.com/pion/interceptor/noop.go b/vendor/github.com/pion/interceptor/noop.go index 2dc4e8e2e..b0fc2a69a 100644 --- a/vendor/github.com/pion/interceptor/noop.go +++ b/vendor/github.com/pion/interceptor/noop.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package interceptor // NoOp is an Interceptor that does not modify any packets. It can embedded in other interceptors, so it's diff --git a/vendor/github.com/pion/interceptor/pkg/nack/errors.go b/vendor/github.com/pion/interceptor/pkg/nack/errors.go index 66ed2b31f..b47ec39c2 100644 --- a/vendor/github.com/pion/interceptor/pkg/nack/errors.go +++ b/vendor/github.com/pion/interceptor/pkg/nack/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package nack import "errors" @@ -5,4 +8,8 @@ import "errors" // ErrInvalidSize is returned by newReceiveLog/newSendBuffer, when an incorrect buffer size is supplied. var ErrInvalidSize = errors.New("invalid buffer size") -var errPacketReleased = errors.New("could not retain packet, already released") +var ( + errPacketReleased = errors.New("could not retain packet, already released") + errFailedToCastHeaderPool = errors.New("could not access header pool, failed cast") + errFailedToCastPayloadPool = errors.New("could not access payload pool, failed cast") +) diff --git a/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go b/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go index f55f4d7f3..faf533ba4 100644 --- a/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go +++ b/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package nack import ( @@ -16,7 +19,7 @@ type GeneratorInterceptorFactory struct { } // NewInterceptor constructs a new ReceiverInterceptor -func (g *GeneratorInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) { +func (g *GeneratorInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) { i := &GeneratorInterceptor{ size: 512, skipLastN: 0, @@ -108,8 +111,8 @@ func (n *GeneratorInterceptor) BindRemoteStream(info *interceptor.StreamInfo, re }) } -// UnbindLocalStream is called when the Stream is removed. It can be used to clean up any data related to that track. -func (n *GeneratorInterceptor) UnbindLocalStream(info *interceptor.StreamInfo) { +// UnbindRemoteStream is called when the Stream is removed. It can be used to clean up any data related to that track. +func (n *GeneratorInterceptor) UnbindRemoteStream(info *interceptor.StreamInfo) { n.receiveLogsMu.Lock() delete(n.receiveLogs, info.SSRC) n.receiveLogsMu.Unlock() diff --git a/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go b/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go index 092f5db92..e4f46f7c9 100644 --- a/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go +++ b/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package nack import ( diff --git a/vendor/github.com/pion/interceptor/pkg/nack/nack.go b/vendor/github.com/pion/interceptor/pkg/nack/nack.go index a658e7f39..b7589ebd9 100644 --- a/vendor/github.com/pion/interceptor/pkg/nack/nack.go +++ b/vendor/github.com/pion/interceptor/pkg/nack/nack.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package nack provides interceptors to implement sending and receiving negative acknowledgements package nack diff --git a/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go b/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go index 8107f59a2..6a19996e7 100644 --- a/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go +++ b/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package nack import ( @@ -127,8 +130,9 @@ func (s *receiveLog) getReceived(seq uint16) bool { func (s *receiveLog) fixLastConsecutive() { i := s.lastConsecutive + 1 - for ; i != s.end+1 && s.getReceived(i); i++ { + for ; i != s.end+1 && s.getReceived(i); i++ { //nolint:revive // find all consecutive packets } + s.lastConsecutive = i - 1 } diff --git a/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go b/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go index 9b49f1130..8f74952dd 100644 --- a/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go +++ b/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package nack import ( @@ -14,13 +17,16 @@ type ResponderInterceptorFactory struct { opts []ResponderOption } +type packetFactory interface { + NewPacket(header *rtp.Header, payload []byte) (*retainablePacket, error) +} + // NewInterceptor constructs a new ResponderInterceptor -func (r *ResponderInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) { +func (r *ResponderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) { i := &ResponderInterceptor{ - size: 8192, - log: logging.NewDefaultLoggerFactory().NewLogger("nack_responder"), - streams: map[uint32]*localStream{}, - packetMan: newPacketManager(), + size: 1024, + log: logging.NewDefaultLoggerFactory().NewLogger("nack_responder"), + streams: map[uint32]*localStream{}, } for _, opt := range r.opts { @@ -29,6 +35,10 @@ func (r *ResponderInterceptorFactory) NewInterceptor(id string) (interceptor.Int } } + if i.packetFactory == nil { + i.packetFactory = newPacketManager() + } + if _, err := newSendBuffer(i.size); err != nil { return nil, err } @@ -39,9 +49,9 @@ func (r *ResponderInterceptorFactory) NewInterceptor(id string) (interceptor.Int // ResponderInterceptor responds to nack feedback messages type ResponderInterceptor struct { interceptor.NoOp - size uint16 - log logging.LeveledLogger - packetMan *packetManager + size uint16 + log logging.LeveledLogger + packetFactory packetFactory streams map[uint32]*localStream streamsMu sync.Mutex @@ -100,7 +110,7 @@ func (n *ResponderInterceptor) BindLocalStream(info *interceptor.StreamInfo, wri n.streamsMu.Unlock() return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) { - pkt, err := n.packetMan.NewPacket(header, payload) + pkt, err := n.packetFactory.NewPacket(header, payload) if err != nil { return 0, err } diff --git a/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go b/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go index 7ad52c8ad..0aaa7577f 100644 --- a/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go +++ b/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package nack import "github.com/pion/logging" @@ -21,3 +24,12 @@ func ResponderLog(log logging.LeveledLogger) ResponderOption { return nil } } + +// DisableCopy bypasses copy of underlying packets. It should be used when +// you are not re-using underlying buffers of packets that have been written +func DisableCopy() ResponderOption { + return func(s *ResponderInterceptor) error { + s.packetFactory = &noOpPacketFactory{} + return nil + } +} diff --git a/vendor/github.com/pion/interceptor/pkg/nack/retainable_packet.go b/vendor/github.com/pion/interceptor/pkg/nack/retainable_packet.go index 89e9cb625..31e9d832f 100644 --- a/vendor/github.com/pion/interceptor/pkg/nack/retainable_packet.go +++ b/vendor/github.com/pion/interceptor/pkg/nack/retainable_packet.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package nack import ( @@ -41,11 +44,20 @@ func (m *packetManager) NewPacket(header *rtp.Header, payload []byte) (*retainab count: 1, } - p.header = m.headerPool.Get().(*rtp.Header) + var ok bool + p.header, ok = m.headerPool.Get().(*rtp.Header) + if !ok { + return nil, errFailedToCastHeaderPool + } + *p.header = header.Clone() if payload != nil { - p.buffer = m.payloadPool.Get().(*[]byte) + p.buffer, ok = m.payloadPool.Get().(*[]byte) + if !ok { + return nil, errFailedToCastPayloadPool + } + size := copy(*p.buffer, payload) p.payload = (*p.buffer)[:size] } @@ -60,6 +72,21 @@ func (m *packetManager) releasePacket(header *rtp.Header, payload *[]byte) { } } +type noOpPacketFactory struct{} + +func (f *noOpPacketFactory) NewPacket(header *rtp.Header, payload []byte) (*retainablePacket, error) { + return &retainablePacket{ + onRelease: f.releasePacket, + count: 1, + header: header, + payload: payload, + }, nil +} + +func (f *noOpPacketFactory) releasePacket(_ *rtp.Header, _ *[]byte) { + // no-op +} + type retainablePacket struct { onRelease func(*rtp.Header, *[]byte) diff --git a/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go b/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go index 8e3607584..e8e816f61 100644 --- a/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go +++ b/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package nack import ( diff --git a/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go b/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go index cb0e7e1ef..c126f9abc 100644 --- a/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go +++ b/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package report import ( @@ -15,7 +18,7 @@ type ReceiverInterceptorFactory struct { } // NewInterceptor constructs a new ReceiverInterceptor -func (r *ReceiverInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) { +func (r *ReceiverInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) { i := &ReceiverInterceptor{ interval: 1 * time.Second, now: time.Now, @@ -98,13 +101,9 @@ func (r *ReceiverInterceptor) loop(rtcpWriter interceptor.RTCPWriter) { case <-ticker.C: now := r.now() r.streams.Range(func(key, value interface{}) bool { - stream := value.(*receiverStream) - - var pkts []rtcp.Packet - - pkts = append(pkts, stream.generateReport(now)) - - if _, err := rtcpWriter.Write(pkts, interceptor.Attributes{}); err != nil { + if stream, ok := value.(*receiverStream); !ok { + r.log.Warnf("failed to cast ReceiverInterceptor stream") + } else if _, err := rtcpWriter.Write([]rtcp.Packet{stream.generateReport(now)}, interceptor.Attributes{}); err != nil { r.log.Warnf("failed sending: %+v", err) } @@ -172,8 +171,11 @@ func (r *ReceiverInterceptor) BindRTCPReader(reader interceptor.RTCPReader) inte continue } - stream := value.(*receiverStream) - stream.processSenderReport(r.now(), sr) + if stream, ok := value.(*receiverStream); !ok { + r.log.Warnf("failed to cast ReceiverInterceptor stream") + } else { + stream.processSenderReport(r.now(), sr) + } } } diff --git a/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go b/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go index 0467dc5de..337a34142 100644 --- a/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go +++ b/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package report import ( diff --git a/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go b/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go index c5722611e..b899bb1b2 100644 --- a/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go +++ b/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package report import ( diff --git a/vendor/github.com/pion/interceptor/pkg/report/report.go b/vendor/github.com/pion/interceptor/pkg/report/report.go index 0a3034ce6..e4d2e64a4 100644 --- a/vendor/github.com/pion/interceptor/pkg/report/report.go +++ b/vendor/github.com/pion/interceptor/pkg/report/report.go @@ -1,2 +1,5 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package report provides interceptors to implement sending sender and receiver reports. package report diff --git a/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go b/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go index 0c18e45a8..02d1c0001 100644 --- a/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go +++ b/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package report import ( @@ -16,7 +19,7 @@ type SenderInterceptorFactory struct { } // NewInterceptor constructs a new SenderInterceptor -func (s *SenderInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) { +func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) { i := &SenderInterceptor{ interval: 1 * time.Second, now: time.Now, @@ -99,21 +102,9 @@ func (s *SenderInterceptor) loop(rtcpWriter interceptor.RTCPWriter) { case <-ticker.C: now := s.now() s.streams.Range(func(key, value interface{}) bool { - ssrc := key.(uint32) - stream := value.(*senderStream) - - stream.m.Lock() - defer stream.m.Unlock() - - sr := &rtcp.SenderReport{ - SSRC: ssrc, - NTPTime: ntpTime(now), - RTPTime: stream.lastRTPTimeRTP + uint32(now.Sub(stream.lastRTPTimeTime).Seconds()*stream.clockRate), - PacketCount: stream.packetCount, - OctetCount: stream.octetCount, - } - - if _, err := rtcpWriter.Write([]rtcp.Packet{sr}, interceptor.Attributes{}); err != nil { + if stream, ok := value.(*senderStream); !ok { + s.log.Warnf("failed to cast SenderInterceptor stream") + } else if _, err := rtcpWriter.Write([]rtcp.Packet{stream.generateReport(now)}, interceptor.Attributes{}); err != nil { s.log.Warnf("failed sending: %+v", err) } @@ -129,7 +120,7 @@ func (s *SenderInterceptor) loop(rtcpWriter interceptor.RTCPWriter) { // BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method // will be called once per rtp packet. func (s *SenderInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer interceptor.RTPWriter) interceptor.RTPWriter { - stream := newSenderStream(info.ClockRate) + stream := newSenderStream(info.SSRC, info.ClockRate) s.streams.Store(info.SSRC, stream) return interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, a interceptor.Attributes) (int, error) { @@ -138,13 +129,3 @@ func (s *SenderInterceptor) BindLocalStream(info *interceptor.StreamInfo, writer return writer.Write(header, payload, a) }) } - -func ntpTime(t time.Time) uint64 { - // seconds since 1st January 1900 - s := (float64(t.UnixNano()) / 1000000000) + 2208988800 - - // higher 32 bits are the integer part, lower 32 bits are the fractional part - integerPart := uint32(s) - fractionalPart := uint32((s - float64(integerPart)) * 0xFFFFFFFF) - return uint64(integerPart)<<32 | uint64(fractionalPart) -} diff --git a/vendor/github.com/pion/interceptor/pkg/report/sender_option.go b/vendor/github.com/pion/interceptor/pkg/report/sender_option.go index 4cb161a37..6932e1c0a 100644 --- a/vendor/github.com/pion/interceptor/pkg/report/sender_option.go +++ b/vendor/github.com/pion/interceptor/pkg/report/sender_option.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package report import ( diff --git a/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go b/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go index 851d70e58..29d14da41 100644 --- a/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go +++ b/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go @@ -1,13 +1,19 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package report import ( "sync" "time" + "github.com/pion/interceptor/internal/ntp" + "github.com/pion/rtcp" "github.com/pion/rtp" ) type senderStream struct { + ssrc uint32 clockRate float64 m sync.Mutex @@ -18,8 +24,9 @@ type senderStream struct { octetCount uint32 } -func newSenderStream(clockRate uint32) *senderStream { +func newSenderStream(ssrc uint32, clockRate uint32) *senderStream { return &senderStream{ + ssrc: ssrc, clockRate: float64(clockRate), } } @@ -35,3 +42,16 @@ func (stream *senderStream) processRTP(now time.Time, header *rtp.Header, payloa stream.packetCount++ stream.octetCount += uint32(len(payload)) } + +func (stream *senderStream) generateReport(now time.Time) *rtcp.SenderReport { + stream.m.Lock() + defer stream.m.Unlock() + + return &rtcp.SenderReport{ + SSRC: stream.ssrc, + NTPTime: ntp.ToNTP(now), + RTPTime: stream.lastRTPTimeRTP + uint32(now.Sub(stream.lastRTPTimeTime).Seconds()*stream.clockRate), + PacketCount: stream.packetCount, + OctetCount: stream.octetCount, + } +} diff --git a/vendor/github.com/pion/interceptor/pkg/twcc/header_extension_interceptor.go b/vendor/github.com/pion/interceptor/pkg/twcc/header_extension_interceptor.go index fa3fb810c..791b14592 100644 --- a/vendor/github.com/pion/interceptor/pkg/twcc/header_extension_interceptor.go +++ b/vendor/github.com/pion/interceptor/pkg/twcc/header_extension_interceptor.go @@ -1,17 +1,23 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package twcc import ( + "errors" "sync/atomic" "github.com/pion/interceptor" "github.com/pion/rtp" ) +var errHeaderIsNil = errors.New("header is nil") + // HeaderExtensionInterceptorFactory is a interceptor.Factory for a HeaderExtensionInterceptor type HeaderExtensionInterceptorFactory struct{} // NewInterceptor constructs a new HeaderExtensionInterceptor -func (h *HeaderExtensionInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) { +func (h *HeaderExtensionInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) { return &HeaderExtensionInterceptor{}, nil } @@ -48,6 +54,9 @@ func (h *HeaderExtensionInterceptor) BindLocalStream(info *interceptor.StreamInf if err != nil { return 0, err } + if header == nil { + return 0, errHeaderIsNil + } err = header.SetExtension(hdrExtID, tcc) if err != nil { return 0, err diff --git a/vendor/github.com/pion/interceptor/pkg/twcc/sender_interceptor.go b/vendor/github.com/pion/interceptor/pkg/twcc/sender_interceptor.go index cf8688ebf..8706e4518 100644 --- a/vendor/github.com/pion/interceptor/pkg/twcc/sender_interceptor.go +++ b/vendor/github.com/pion/interceptor/pkg/twcc/sender_interceptor.go @@ -1,6 +1,10 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package twcc import ( + "errors" "math/rand" "sync" "time" @@ -15,8 +19,10 @@ type SenderInterceptorFactory struct { opts []Option } +var errClosed = errors.New("interceptor is closed") + // NewInterceptor constructs a new SenderInterceptor -func (s *SenderInterceptorFactory) NewInterceptor(id string) (interceptor.Interceptor, error) { +func (s *SenderInterceptorFactory) NewInterceptor(_ string) (interceptor.Interceptor, error) { i := &SenderInterceptor{ log: logging.NewDefaultLoggerFactory().NewLogger("twcc_sender_interceptor"), packetChan: make(chan packet), @@ -129,12 +135,17 @@ func (s *SenderInterceptor) BindRemoteStream(info *interceptor.StreamInfo, reade return 0, nil, err } - s.packetChan <- packet{ + p := packet{ hdr: header, sequenceNumber: tccExt.TransportSequence, arrivalTime: time.Since(s.startTime).Microseconds(), ssrc: info.SSRC, } + select { + case <-s.close: + return 0, nil, errClosed + case s.packetChan <- p: + } } return i, attr, nil @@ -166,6 +177,13 @@ func (s *SenderInterceptor) isClosed() bool { func (s *SenderInterceptor) loop(w interceptor.RTCPWriter) { defer s.wg.Done() + select { + case <-s.close: + return + case p := <-s.packetChan: + s.recorder.Record(p.ssrc, p.sequenceNumber, p.arrivalTime) + } + ticker := time.NewTicker(s.interval) for { select { diff --git a/vendor/github.com/pion/interceptor/pkg/twcc/twcc.go b/vendor/github.com/pion/interceptor/pkg/twcc/twcc.go index 0e43f69ab..235f1f11c 100644 --- a/vendor/github.com/pion/interceptor/pkg/twcc/twcc.go +++ b/vendor/github.com/pion/interceptor/pkg/twcc/twcc.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package twcc provides interceptors to implement transport wide congestion control. package twcc @@ -70,13 +73,12 @@ func insertSorted(list []pktInfo, element pktInfo) []pktInfo { // BuildFeedbackPacket creates a new RTCP packet containing a TWCC feedback report. func (r *Recorder) BuildFeedbackPacket() []rtcp.Packet { - feedback := newFeedback(r.senderSSRC, r.mediaSSRC, r.fbPktCnt) - r.fbPktCnt++ if len(r.receivedPackets) < 2 { - r.receivedPackets = []pktInfo{} - return []rtcp.Packet{feedback.getRTCP()} + return nil } + feedback := newFeedback(r.senderSSRC, r.mediaSSRC, r.fbPktCnt) + r.fbPktCnt++ feedback.setBase(uint16(r.receivedPackets[0].sequenceNumber&0xffff), r.receivedPackets[0].arrivalTime) var pkts []rtcp.Packet diff --git a/vendor/github.com/pion/interceptor/registry.go b/vendor/github.com/pion/interceptor/registry.go index 1347193d4..e36ef6bfb 100644 --- a/vendor/github.com/pion/interceptor/registry.go +++ b/vendor/github.com/pion/interceptor/registry.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package interceptor // Registry is a collector for interceptors. diff --git a/vendor/github.com/pion/interceptor/renovate.json b/vendor/github.com/pion/interceptor/renovate.json index f1614058a..f1bb98c6a 100644 --- a/vendor/github.com/pion/interceptor/renovate.json +++ b/vendor/github.com/pion/interceptor/renovate.json @@ -1,27 +1,6 @@ { + "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:base", - ":disableDependencyDashboard" - ], - "postUpdateOptions": [ - "gomodTidy" - ], - "commitBody": "Generated by renovateBot", - "packageRules": [ - { - "matchUpdateTypes": ["minor", "patch", "pin", "digest"], - "automerge": true - }, - { - "packagePatterns": ["^golang.org/x/"], - "schedule": ["on the first day of the month"] - } - ], - "ignorePaths": [ - ".github/workflows/generate-authors.yml", - ".github/workflows/lint.yaml", - ".github/workflows/renovate-go-mod-fix.yaml", - ".github/workflows/test.yaml", - ".github/workflows/tidy-check.yaml" + "github>pion/renovate-config" ] } diff --git a/vendor/github.com/pion/interceptor/streaminfo.go b/vendor/github.com/pion/interceptor/streaminfo.go index 956fa5306..4108159c5 100644 --- a/vendor/github.com/pion/interceptor/streaminfo.go +++ b/vendor/github.com/pion/interceptor/streaminfo.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package interceptor // RTPHeaderExtension represents a negotiated RFC5285 RTP header extension. diff --git a/vendor/github.com/pion/mdns/.gitignore b/vendor/github.com/pion/mdns/.gitignore index 83db74ba5..f977e7485 100644 --- a/vendor/github.com/pion/mdns/.gitignore +++ b/vendor/github.com/pion/mdns/.gitignore @@ -22,3 +22,4 @@ cover.out *.wasm examples/sfu-ws/cert.pem examples/sfu-ws/key.pem +wasm_exec.js diff --git a/vendor/github.com/pion/mdns/.golangci.yml b/vendor/github.com/pion/mdns/.golangci.yml index d6162c970..48696f16b 100644 --- a/vendor/github.com/pion/mdns/.golangci.yml +++ b/vendor/github.com/pion/mdns/.golangci.yml @@ -15,14 +15,21 @@ linters-settings: linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully - - deadcode # Finds unused code + - contextcheck # check the function whether use a non-inherited context + - decorder # check declaration order and count of types, constants, variables and functions - depguard # Go linter that checks if package imports are in a list of acceptable packages - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection + - durationcheck # check for two durations multiplied together - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. - exhaustive # check exhaustiveness of enum switch statements - exportloopref # checks for pointers to enclosing loop variables + - forcetypeassert # finds forced type assertions - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - gochecknoinits # Checks that no init functions are present in Go code @@ -35,40 +42,60 @@ linters: - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - gosimple # Linter for Go source code that specializes in simplifying a code - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - grouper # An analyzer to analyze expression groups. + - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used - misspell # Finds commonly misspelled English words in comments - nakedret # Finds naked returns in functions greater than a specified function length + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. - noctx # noctx finds sending http request without context.Context - - scopelint # Scopelint checks for unpinned variables in go programs + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # golint replacement, finds style mistakes - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - - structcheck # Finds unused struct fields - stylecheck # Stylecheck is a replacement for golint + - tagliatelle # Checks the struct tags. + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types - - varcheck # Finds unused global variables and constants + - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: + - containedctx # containedctx is a linter that detects struct contained context.Context field + - cyclop # checks function and package cyclomatic complexity + - exhaustivestruct # Checks if all struct's fields are initialized + - forbidigo # Forbids identifiers - funlen # Tool for detection of long functions - gocyclo # Computes and checks the cyclomatic complexity of functions - godot # Check if comments end in a period - gomnd # An analyzer to detect magic numbers. + - ifshort # Checks that your code uses short syntax for if-statements whenever possible + - ireturn # Accept Interfaces, Return Concrete Types - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - nestif # Reports deeply nested if statements - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - nolintlint # Reports ill-formed or insufficient nolint directives + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - prealloc # Finds slice declarations that could potentially be preallocated + - promlinter # Check Prometheus metrics naming via promlint - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. - testpackage # linter that makes you use a separate _test package + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers + - varnamelen # checks that the length of a variable's name matches its scope + - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! issues: diff --git a/vendor/github.com/pion/mdns/.goreleaser.yml b/vendor/github.com/pion/mdns/.goreleaser.yml new file mode 100644 index 000000000..2caa5fbd3 --- /dev/null +++ b/vendor/github.com/pion/mdns/.goreleaser.yml @@ -0,0 +1,2 @@ +builds: +- skip: true diff --git a/vendor/github.com/pion/mdns/AUTHORS.txt b/vendor/github.com/pion/mdns/AUTHORS.txt new file mode 100644 index 000000000..48ceb8211 --- /dev/null +++ b/vendor/github.com/pion/mdns/AUTHORS.txt @@ -0,0 +1,17 @@ +# Thank you to everyone that made Pion possible. If you are interested in contributing +# we would love to have you https://github.com/pion/webrtc/wiki/Contributing +# +# This file is auto generated, using git to list all individuals contributors. +# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting +Atsushi Watanabe +Bjørn Remseth +Doug Cone +Hugo Arregui +Javier Peletier +Jonas van den Berg <24623262+vonas@users.noreply.github.com> +Konstantin Itskov +Sean DuBois +Sean DuBois + +# List of contributors not appearing in Git history + diff --git a/vendor/github.com/pion/mdns/README.md b/vendor/github.com/pion/mdns/README.md index 634699e02..b2d7d9c73 100644 --- a/vendor/github.com/pion/mdns/README.md +++ b/vendor/github.com/pion/mdns/README.md @@ -21,7 +21,7 @@ Go mDNS implementation. The original user is Pion WebRTC, but we would love to s ### Running Server For a mDNS server that responds to queries for `pion-test.local` ```sh -go run examples/listen/main.go +go run examples/server/main.go ``` @@ -55,11 +55,5 @@ If you need commercial support or don't want to use public methods you can conta ### Contributing Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible: -* [Sean DuBois](https://github.com/Sean-Der) - *Original Author* -* [Konstantin Itskov](https://github.com/trivigy) - Contributor -* [Hugo Arregui](https://github.com/hugoArregui) -* [Atsushi Watanabe](https://github.com/at-wat) -* [Doug Cone](https://github.com/nullvariable) - ### License MIT License - see [LICENSE](LICENSE) for full text diff --git a/vendor/github.com/pion/mdns/conn.go b/vendor/github.com/pion/mdns/conn.go index 2c169e2a6..31037e252 100644 --- a/vendor/github.com/pion/mdns/conn.go +++ b/vendor/github.com/pion/mdns/conn.go @@ -5,6 +5,7 @@ import ( "errors" "math/big" "net" + "runtime" "sync" "time" @@ -24,6 +25,7 @@ type Conn struct { queryInterval time.Duration localNames []string queries []query + ifaces []net.Interface closed chan interface{} } @@ -39,13 +41,14 @@ type queryResult struct { } const ( - inboundBufferSize = 512 defaultQueryInterval = time.Second destinationAddress = "224.0.0.251:5353" maxMessageRecords = 3 responseTTL = 120 ) +var errNoPositiveMTUFound = errors.New("no positive MTU found") + // Server establishes a mDNS connection over an existing conn func Server(conn *ipv4.PacketConn, config *Config) (*Conn, error) { if config == nil { @@ -57,11 +60,24 @@ func Server(conn *ipv4.PacketConn, config *Config) (*Conn, error) { return nil, err } + inboundBufferSize := 0 joinErrCount := 0 - for i := range ifaces { + ifacesToUse := make([]net.Interface, 0, len(ifaces)) + for i, ifc := range ifaces { if err = conn.JoinGroup(&ifaces[i], &net.UDPAddr{IP: net.IPv4(224, 0, 0, 251)}); err != nil { joinErrCount++ + continue } + + ifcCopy := ifc + ifacesToUse = append(ifacesToUse, ifcCopy) + if ifaces[i].MTU > inboundBufferSize { + inboundBufferSize = ifaces[i].MTU + } + } + + if inboundBufferSize == 0 { + return nil, errNoPositiveMTUFound } if joinErrCount >= len(ifaces) { return nil, errJoiningMulticastGroup @@ -88,6 +104,7 @@ func Server(conn *ipv4.PacketConn, config *Config) (*Conn, error) { socket: conn, dstAddr: dstAddr, localNames: localNames, + ifaces: ifacesToUse, log: loggerFactory.NewLogger("mdns"), closed: make(chan interface{}), } @@ -95,7 +112,11 @@ func Server(conn *ipv4.PacketConn, config *Config) (*Conn, error) { c.queryInterval = config.QueryInterval } - go c.start() + // https://www.rfc-editor.org/rfc/rfc6762.html#section-17 + // Multicast DNS messages carried by UDP may be up to the IP MTU of the + // physical interface, less the space required for the IP header (20 + // bytes for IPv4; 40 bytes for IPv6) and the UDP header (8 bytes). + go c.start(inboundBufferSize - 20 - 8) return c, nil } @@ -167,7 +188,11 @@ func interfaceForRemote(remote string) (net.IP, error) { return nil, err } - localAddr := conn.LocalAddr().(*net.UDPAddr) + localAddr, ok := conn.LocalAddr().(*net.UDPAddr) + if !ok { + return nil, errFailedCast + } + if err := conn.Close(); err != nil { return nil, err } @@ -199,9 +224,24 @@ func (c *Conn) sendQuestion(name string) { return } - if _, err := c.socket.WriteTo(rawQuery, nil, c.dstAddr); err != nil { - c.log.Warnf("Failed to send mDNS packet %v", err) - return + c.writeToSocket(rawQuery) +} + +const isWindows = runtime.GOOS == "windows" + +func (c *Conn) writeToSocket(b []byte) { + var wcm ipv4.ControlMessage + for i := range c.ifaces { + if isWindows { + if err := c.socket.SetMulticastInterface(&c.ifaces[i]); err != nil { + c.log.Warnf("Failed to set multicast interface for %d: %v", i, err) + } + } else { + wcm.IfIndex = c.ifaces[i].Index + } + if _, err := c.socket.WriteTo(b, &wcm, c.dstAddr); err != nil { + c.log.Warnf("Failed to send mDNS packet on interface %d: %v", i, err) + } } } @@ -238,13 +278,10 @@ func (c *Conn) sendAnswer(name string, dst net.IP) { return } - if _, err := c.socket.WriteTo(rawAnswer, nil, c.dstAddr); err != nil { - c.log.Warnf("Failed to send mDNS packet %v", err) - return - } + c.writeToSocket(rawAnswer) } -func (c *Conn) start() { //nolint gocognit +func (c *Conn) start(inboundBufferSize int) { //nolint gocognit defer func() { c.mu.Lock() defer c.mu.Unlock() @@ -257,7 +294,11 @@ func (c *Conn) start() { //nolint gocognit for { n, _, src, err := c.socket.ReadFrom(b) if err != nil { - return + if errors.Is(err, net.ErrClosed) { + return + } + c.log.Warnf("Failed to ReadFrom %q %v", src, err) + continue } func() { @@ -307,7 +348,15 @@ func (c *Conn) start() { //nolint gocognit for i := len(c.queries) - 1; i >= 0; i-- { if c.queries[i].nameWithSuffix == a.Name.String() { - c.queries[i].queryResultChan <- queryResult{a, src} + ip, err := ipFromAnswerHeader(a, p) + if err != nil { + c.log.Warnf("Failed to parse mDNS answer %v", err) + return + } + + c.queries[i].queryResultChan <- queryResult{a, &net.IPAddr{ + IP: ip, + }} c.queries = append(c.queries[:i], c.queries[i+1:]...) } } @@ -315,3 +364,21 @@ func (c *Conn) start() { //nolint gocognit }() } } + +func ipFromAnswerHeader(a dnsmessage.ResourceHeader, p dnsmessage.Parser) (ip []byte, err error) { + if a.Type == dnsmessage.TypeA { + resource, err := p.AResource() + if err != nil { + return nil, err + } + ip = net.IP(resource.A[:]) + } else { + resource, err := p.AAAAResource() + if err != nil { + return nil, err + } + ip = resource.AAAA[:] + } + + return +} diff --git a/vendor/github.com/pion/mdns/errors.go b/vendor/github.com/pion/mdns/errors.go index 2f8dc928b..0a1b61240 100644 --- a/vendor/github.com/pion/mdns/errors.go +++ b/vendor/github.com/pion/mdns/errors.go @@ -7,4 +7,5 @@ var ( errConnectionClosed = errors.New("mDNS: connection is closed") errContextElapsed = errors.New("mDNS: context has elapsed") errNilConfig = errors.New("mDNS: config must not be nil") + errFailedCast = errors.New("mDNS: failed to cast listener to UDPAddr") ) diff --git a/vendor/github.com/pion/mdns/renovate.json b/vendor/github.com/pion/mdns/renovate.json index 4400fd9b2..f1bb98c6a 100644 --- a/vendor/github.com/pion/mdns/renovate.json +++ b/vendor/github.com/pion/mdns/renovate.json @@ -1,15 +1,6 @@ { + "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:base" - ], - "postUpdateOptions": [ - "gomodTidy" - ], - "commitBody": "Generated by renovateBot", - "packageRules": [ - { - "packagePatterns": ["^golang.org/x/"], - "schedule": ["on the first day of the month"] - } + "github>pion/renovate-config" ] } diff --git a/vendor/github.com/pion/rtcp/.gitignore b/vendor/github.com/pion/rtcp/.gitignore index 83db74ba5..f977e7485 100644 --- a/vendor/github.com/pion/rtcp/.gitignore +++ b/vendor/github.com/pion/rtcp/.gitignore @@ -22,3 +22,4 @@ cover.out *.wasm examples/sfu-ws/cert.pem examples/sfu-ws/key.pem +wasm_exec.js diff --git a/vendor/github.com/pion/rtcp/.golangci.yml b/vendor/github.com/pion/rtcp/.golangci.yml index d6162c970..d7a88eca3 100644 --- a/vendor/github.com/pion/rtcp/.golangci.yml +++ b/vendor/github.com/pion/rtcp/.golangci.yml @@ -15,14 +15,22 @@ linters-settings: linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully + - contextcheck # check the function whether use a non-inherited context - deadcode # Finds unused code + - decorder # check declaration order and count of types, constants, variables and functions - depguard # Go linter that checks if package imports are in a list of acceptable packages - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection + - durationcheck # check for two durations multiplied together - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. - exhaustive # check exhaustiveness of enum switch statements - exportloopref # checks for pointers to enclosing loop variables + - forcetypeassert # finds forced type assertions - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - gochecknoinits # Checks that no init functions are present in Go code @@ -35,40 +43,62 @@ linters: - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - gosimple # Linter for Go source code that specializes in simplifying a code - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - grouper # An analyzer to analyze expression groups. + - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used - misspell # Finds commonly misspelled English words in comments - nakedret # Finds naked returns in functions greater than a specified function length + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. - noctx # noctx finds sending http request without context.Context - - scopelint # Scopelint checks for unpinned variables in go programs + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # golint replacement, finds style mistakes - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - structcheck # Finds unused struct fields - stylecheck # Stylecheck is a replacement for golint + - tagliatelle # Checks the struct tags. + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types - varcheck # Finds unused global variables and constants + - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: + - containedctx # containedctx is a linter that detects struct contained context.Context field + - cyclop # checks function and package cyclomatic complexity + - exhaustivestruct # Checks if all struct's fields are initialized + - forbidigo # Forbids identifiers - funlen # Tool for detection of long functions - gocyclo # Computes and checks the cyclomatic complexity of functions - godot # Check if comments end in a period - gomnd # An analyzer to detect magic numbers. + - ifshort # Checks that your code uses short syntax for if-statements whenever possible + - ireturn # Accept Interfaces, Return Concrete Types - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - nestif # Reports deeply nested if statements - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - nolintlint # Reports ill-formed or insufficient nolint directives + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - prealloc # Finds slice declarations that could potentially be preallocated + - promlinter # Check Prometheus metrics naming via promlint - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. - testpackage # linter that makes you use a separate _test package + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers + - varnamelen # checks that the length of a variable's name matches its scope + - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! issues: diff --git a/vendor/github.com/pion/rtcp/AUTHORS.txt b/vendor/github.com/pion/rtcp/AUTHORS.txt index 783fc31ef..5d5a53cc2 100644 --- a/vendor/github.com/pion/rtcp/AUTHORS.txt +++ b/vendor/github.com/pion/rtcp/AUTHORS.txt @@ -15,6 +15,7 @@ Juliusz Chroboczek Kevin Wang lllf Luke Curley +Mathis Engelbart Max Hawkins Sean DuBois Sean DuBois diff --git a/vendor/github.com/pion/rtcp/fuzz.go b/vendor/github.com/pion/rtcp/fuzz.go index 2ea4fb1fc..b8118224a 100644 --- a/vendor/github.com/pion/rtcp/fuzz.go +++ b/vendor/github.com/pion/rtcp/fuzz.go @@ -1,3 +1,4 @@ +//go:build gofuzz // +build gofuzz package rtcp diff --git a/vendor/github.com/pion/rtcp/header.go b/vendor/github.com/pion/rtcp/header.go index 5bc34abe5..43f6abc06 100644 --- a/vendor/github.com/pion/rtcp/header.go +++ b/vendor/github.com/pion/rtcp/header.go @@ -27,6 +27,7 @@ const ( FormatFIR uint8 = 4 FormatTLN uint8 = 1 FormatRRR uint8 = 5 + FormatCCFB uint8 = 11 FormatREMB uint8 = 15 // https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01#page-5 diff --git a/vendor/github.com/pion/rtcp/packet.go b/vendor/github.com/pion/rtcp/packet.go index 0200a79c0..885d52cde 100644 --- a/vendor/github.com/pion/rtcp/packet.go +++ b/vendor/github.com/pion/rtcp/packet.go @@ -87,6 +87,8 @@ func unmarshal(rawData []byte) (packet Packet, bytesprocessed int, err error) { packet = new(RapidResynchronizationRequest) case FormatTCC: packet = new(TransportLayerCC) + case FormatCCFB: + packet = new(CCFeedbackReport) default: packet = new(RawPacket) } diff --git a/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go b/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go index b876e9c88..00c7e8626 100644 --- a/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go +++ b/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go @@ -14,6 +14,10 @@ type RapidResynchronizationRequest struct { MediaSSRC uint32 } +// RapidResynchronisationRequest is provided as RFC 6051 spells resynchronization with an s. +// We provide both names to be consistent with other RFCs which spell resynchronization with a z. +type RapidResynchronisationRequest = RapidResynchronizationRequest + const ( rrrLength = 2 rrrHeaderLength = ssrcLength * 2 diff --git a/vendor/github.com/pion/rtcp/rfc8888.go b/vendor/github.com/pion/rtcp/rfc8888.go new file mode 100644 index 000000000..8527fc8e7 --- /dev/null +++ b/vendor/github.com/pion/rtcp/rfc8888.go @@ -0,0 +1,325 @@ +package rtcp + +import ( + "encoding/binary" + "errors" + "fmt" +) + +// https://www.rfc-editor.org/rfc/rfc8888.html#name-rtcp-congestion-control-fee +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| FMT=11 | PT = 205 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of RTCP packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of 1st RTP Stream | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | begin_seq | num_reports | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |R|ECN| Arrival time offset | ... . +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// . . +// . . +// . . +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of nth RTP Stream | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | begin_seq | num_reports | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |R|ECN| Arrival time offset | ... | +// . . +// . . +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Report Timestamp (32 bits) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +var ( + errReportBlockLength = errors.New("feedback report blocks must be at least 8 bytes") + errIncorrectNumReports = errors.New("feedback report block contains less reports than num_reports") + errMetricBlockLength = errors.New("feedback report metric blocks must be exactly 2 bytes") +) + +// ECN represents the two ECN bits +type ECN uint8 + +const ( + //nolint:misspell + // ECNNonECT signals Non ECN-Capable Transport, Non-ECT + ECNNonECT ECN = iota // 00 + + //nolint:misspell + // ECNECT1 signals ECN Capable Transport, ECT(0) + ECNECT1 // 01 + + //nolint:misspell + // ECNECT0 signals ECN Capable Transport, ECT(1) + ECNECT0 // 10 + + // ECNCE signals ECN Congestion Encountered, CE + ECNCE // 11 +) + +const ( + reportTimestampLength = 4 + reportBlockOffset = 8 +) + +// CCFeedbackReport is a Congestion Control Feedback Report as defined in +// https://www.rfc-editor.org/rfc/rfc8888.html#name-rtcp-congestion-control-fee +type CCFeedbackReport struct { + // SSRC of sender + SenderSSRC uint32 + + // Report Blocks + ReportBlocks []CCFeedbackReportBlock + + // Basetime + ReportTimestamp uint32 +} + +// DestinationSSRC returns an array of SSRC values that this packet refers to. +func (b CCFeedbackReport) DestinationSSRC() []uint32 { + ssrcs := make([]uint32, len(b.ReportBlocks)) + for i, block := range b.ReportBlocks { + ssrcs[i] = block.MediaSSRC + } + return ssrcs +} + +// Len returns the length of the report in bytes +func (b *CCFeedbackReport) Len() uint16 { + n := uint16(0) + for _, block := range b.ReportBlocks { + n += block.len() + } + return reportBlockOffset + n + reportTimestampLength +} + +// Header returns the Header associated with this packet. +func (b *CCFeedbackReport) Header() Header { + return Header{ + Padding: false, + Count: FormatCCFB, + Type: TypeTransportSpecificFeedback, + Length: b.Len()/4 - 1, + } +} + +// Marshal encodes the Congestion Control Feedback Report in binary +func (b CCFeedbackReport) Marshal() ([]byte, error) { + header := b.Header() + headerBuf, err := header.Marshal() + if err != nil { + return nil, err + } + length := 4 * (header.Length + 1) + buf := make([]byte, length) + copy(buf[:headerLength], headerBuf) + binary.BigEndian.PutUint32(buf[headerLength:], b.SenderSSRC) + offset := uint16(reportBlockOffset) + for _, block := range b.ReportBlocks { + b, err := block.marshal() + if err != nil { + return nil, err + } + copy(buf[offset:], b) + offset += block.len() + } + + binary.BigEndian.PutUint32(buf[offset:], b.ReportTimestamp) + return buf, nil +} + +func (b CCFeedbackReport) String() string { + out := fmt.Sprintf("CCFB:\n\tHeader %v\n", b.Header()) + out += fmt.Sprintf("CCFB:\n\tSender SSRC %d\n", b.SenderSSRC) + out += fmt.Sprintf("\tReport Timestamp %d\n", b.ReportTimestamp) + out += "\tFeedback Reports \n" + for _, report := range b.ReportBlocks { + out += fmt.Sprintf("%v ", report) + } + out += "\n" + return out +} + +// Unmarshal decodes the Congestion Control Feedback Report from binary +func (b *CCFeedbackReport) Unmarshal(rawPacket []byte) error { + if len(rawPacket) < headerLength+ssrcLength+reportTimestampLength { + return errPacketTooShort + } + + var h Header + if err := h.Unmarshal(rawPacket); err != nil { + return err + } + if h.Type != TypeTransportSpecificFeedback { + return errWrongType + } + + b.SenderSSRC = binary.BigEndian.Uint32(rawPacket[headerLength:]) + + reportTimestampOffset := uint16(len(rawPacket) - reportTimestampLength) + b.ReportTimestamp = binary.BigEndian.Uint32(rawPacket[reportTimestampOffset:]) + + offset := uint16(reportBlockOffset) + b.ReportBlocks = []CCFeedbackReportBlock{} + for offset < reportTimestampOffset { + var block CCFeedbackReportBlock + if err := block.unmarshal(rawPacket[offset:]); err != nil { + return err + } + b.ReportBlocks = append(b.ReportBlocks, block) + offset += block.len() + } + + return nil +} + +const ( + ssrcOffset = 0 + beginSequenceOffset = 4 + numReportsOffset = 6 + reportsOffset = 8 + + maxMetricBlocks = 16384 +) + +// CCFeedbackReportBlock is a Feedback Report Block +type CCFeedbackReportBlock struct { + // SSRC of the RTP stream on which this block is reporting + MediaSSRC uint32 + BeginSequence uint16 + MetricBlocks []CCFeedbackMetricBlock +} + +// len returns the length of the report block in bytes +func (b *CCFeedbackReportBlock) len() uint16 { + n := len(b.MetricBlocks) + if n%2 != 0 { + n++ + } + return reportsOffset + 2*uint16(n) +} + +func (b CCFeedbackReportBlock) String() string { + out := fmt.Sprintf("\tReport Block Media SSRC %d\n", b.MediaSSRC) + out += fmt.Sprintf("\tReport Begin Sequence Nr %d\n", b.BeginSequence) + out += fmt.Sprintf("\tReport length %d\n\t", len(b.MetricBlocks)) + for i, block := range b.MetricBlocks { + out += fmt.Sprintf("{nr: %d, rx: %v, ts: %v} ", b.BeginSequence+uint16(i), block.Received, block.ArrivalTimeOffset) + } + out += "\n" + return out +} + +// marshal encodes the Congestion Control Feedback Report Block in binary +func (b CCFeedbackReportBlock) marshal() ([]byte, error) { + if len(b.MetricBlocks) > maxMetricBlocks { + return nil, errTooManyReports + } + + buf := make([]byte, b.len()) + binary.BigEndian.PutUint32(buf[ssrcOffset:], b.MediaSSRC) + binary.BigEndian.PutUint16(buf[beginSequenceOffset:], b.BeginSequence) + + length := uint16(len(b.MetricBlocks)) + if length > 0 { + length-- + } + + binary.BigEndian.PutUint16(buf[numReportsOffset:], length) + + for i, block := range b.MetricBlocks { + b, err := block.marshal() + if err != nil { + return nil, err + } + copy(buf[reportsOffset+i*2:], b) + } + + return buf, nil +} + +// Unmarshal decodes the Congestion Control Feedback Report Block from binary +func (b *CCFeedbackReportBlock) unmarshal(rawPacket []byte) error { + if len(rawPacket) < reportsOffset { + return errReportBlockLength + } + b.MediaSSRC = binary.BigEndian.Uint32(rawPacket[:beginSequenceOffset]) + b.BeginSequence = binary.BigEndian.Uint16(rawPacket[beginSequenceOffset:numReportsOffset]) + numReportsField := binary.BigEndian.Uint16(rawPacket[numReportsOffset:]) + if numReportsField == 0 { + return nil + } + endSequence := b.BeginSequence + numReportsField + numReports := endSequence - b.BeginSequence + 1 + + if len(rawPacket) < int(reportsOffset+numReports*2) { + return errIncorrectNumReports + } + b.MetricBlocks = make([]CCFeedbackMetricBlock, numReports) + for i := uint16(0); i < numReports; i++ { + var mb CCFeedbackMetricBlock + offset := reportsOffset + 2*i + if err := mb.unmarshal(rawPacket[offset : offset+2]); err != nil { + return err + } + b.MetricBlocks[i] = mb + } + return nil +} + +const ( + metricBlockLength = 2 +) + +// CCFeedbackMetricBlock is a Feedback Metric Block +type CCFeedbackMetricBlock struct { + Received bool + ECN ECN + + // Offset in 1/1024 seconds before Report Timestamp + ArrivalTimeOffset uint16 +} + +// Marshal encodes the Congestion Control Feedback Metric Block in binary +func (b CCFeedbackMetricBlock) marshal() ([]byte, error) { + buf := make([]byte, 2) + r := uint16(0) + if b.Received { + r = 1 + } + dst, err := setNBitsOfUint16(0, 1, 0, r) + if err != nil { + return nil, err + } + dst, err = setNBitsOfUint16(dst, 2, 1, uint16(b.ECN)) + if err != nil { + return nil, err + } + dst, err = setNBitsOfUint16(dst, 13, 3, b.ArrivalTimeOffset) + if err != nil { + return nil, err + } + + binary.BigEndian.PutUint16(buf, dst) + return buf, nil +} + +// Unmarshal decodes the Congestion Control Feedback Metric Block from binary +func (b *CCFeedbackMetricBlock) unmarshal(rawPacket []byte) error { + if len(rawPacket) != metricBlockLength { + return errMetricBlockLength + } + b.Received = rawPacket[0]&0x80 != 0 + if !b.Received { + b.ECN = ECNNonECT + b.ArrivalTimeOffset = 0 + return nil + } + b.ECN = ECN(rawPacket[0] >> 5 & 0x03) + b.ArrivalTimeOffset = binary.BigEndian.Uint16(rawPacket) & 0x1FFF + return nil +} diff --git a/vendor/github.com/pion/rtcp/source_description.go b/vendor/github.com/pion/rtcp/source_description.go index d62e57a87..c4483c301 100644 --- a/vendor/github.com/pion/rtcp/source_description.go +++ b/vendor/github.com/pion/rtcp/source_description.go @@ -61,6 +61,19 @@ type SourceDescription struct { Chunks []SourceDescriptionChunk } +// NewCNAMESourceDescription creates a new SourceDescription with a single CNAME item. +func NewCNAMESourceDescription(ssrc uint32, cname string) *SourceDescription { + return &SourceDescription{ + Chunks: []SourceDescriptionChunk{{ + Source: ssrc, + Items: []SourceDescriptionItem{{ + Type: SDESCNAME, + Text: cname, + }}, + }}, + } +} + // Marshal encodes the SourceDescription in binary func (s SourceDescription) Marshal() ([]byte, error) { /* @@ -242,16 +255,16 @@ func (s *SourceDescriptionChunk) Unmarshal(rawPacket []byte) error { } func (s SourceDescriptionChunk) len() int { - len := sdesSourceLen + chunkLen := sdesSourceLen for _, it := range s.Items { - len += it.len() + chunkLen += it.len() } - len += sdesTypeLen // for terminating null octet + chunkLen += sdesTypeLen // for terminating null octet // align to 32-bit boundary - len += getPadding(len) + chunkLen += getPadding(chunkLen) - return len + return chunkLen } // A SourceDescriptionItem is a part of a SourceDescription that describes a stream. diff --git a/vendor/github.com/pion/rtcp/util.go b/vendor/github.com/pion/rtcp/util.go index 5702d35f8..95e8f1a83 100644 --- a/vendor/github.com/pion/rtcp/util.go +++ b/vendor/github.com/pion/rtcp/util.go @@ -1,11 +1,11 @@ package rtcp // getPadding Returns the padding required to make the length a multiple of 4 -func getPadding(len int) int { - if len%4 == 0 { +func getPadding(packetLen int) int { + if packetLen%4 == 0 { return 0 } - return 4 - (len % 4) + return 4 - (packetLen % 4) } // setNBitsOfUint16 will truncate the value to size, left-shift to startIndex position and set diff --git a/vendor/github.com/pion/rtp/.gitignore b/vendor/github.com/pion/rtp/.gitignore index 83db74ba5..f977e7485 100644 --- a/vendor/github.com/pion/rtp/.gitignore +++ b/vendor/github.com/pion/rtp/.gitignore @@ -22,3 +22,4 @@ cover.out *.wasm examples/sfu-ws/cert.pem examples/sfu-ws/key.pem +wasm_exec.js diff --git a/vendor/github.com/pion/rtp/AUTHORS.txt b/vendor/github.com/pion/rtp/AUTHORS.txt index 52d7bd90a..a7bc7a02b 100644 --- a/vendor/github.com/pion/rtp/AUTHORS.txt +++ b/vendor/github.com/pion/rtp/AUTHORS.txt @@ -27,8 +27,10 @@ Michael Uti Raphael Derosso Pereira Rob Lofthouse Robin Raymond +Sean DuBois Sean DuBois Sean DuBois Simone Gotti Tarrence van As +wangzixiang Woodrow Douglass diff --git a/vendor/github.com/pion/rtp/abssendtimeextension.go b/vendor/github.com/pion/rtp/abssendtimeextension.go index fc9731d0b..f0c6de375 100644 --- a/vendor/github.com/pion/rtp/abssendtimeextension.go +++ b/vendor/github.com/pion/rtp/abssendtimeextension.go @@ -15,7 +15,7 @@ type AbsSendTimeExtension struct { } // Marshal serializes the members to buffer. -func (t *AbsSendTimeExtension) Marshal() ([]byte, error) { +func (t AbsSendTimeExtension) Marshal() ([]byte, error) { return []byte{ byte(t.Timestamp & 0xFF0000 >> 16), byte(t.Timestamp & 0xFF00 >> 8), diff --git a/vendor/github.com/pion/rtp/audiolevelextension.go b/vendor/github.com/pion/rtp/audiolevelextension.go index f8701e153..ca44f2870 100644 --- a/vendor/github.com/pion/rtp/audiolevelextension.go +++ b/vendor/github.com/pion/rtp/audiolevelextension.go @@ -36,7 +36,7 @@ type AudioLevelExtension struct { } // Marshal serializes the members to buffer -func (a *AudioLevelExtension) Marshal() ([]byte, error) { +func (a AudioLevelExtension) Marshal() ([]byte, error) { if a.Level > 127 { return nil, errAudioLevelOverflow } diff --git a/vendor/github.com/pion/rtp/codecs/av1_packet.go b/vendor/github.com/pion/rtp/codecs/av1_packet.go new file mode 100644 index 000000000..120a904c1 --- /dev/null +++ b/vendor/github.com/pion/rtp/codecs/av1_packet.go @@ -0,0 +1,158 @@ +package codecs + +import ( + "github.com/pion/rtp/pkg/obu" +) + +const ( + zMask = byte(0b10000000) + zBitshift = 7 + + yMask = byte(0b01000000) + yBitshift = 6 + + wMask = byte(0b00110000) + wBitshift = 4 + + nMask = byte(0b00001000) + nBitshift = 3 + + av1PayloaderHeadersize = 1 +) + +// AV1Payloader payloads AV1 packets +type AV1Payloader struct{} + +// Payload fragments a AV1 packet across one or more byte arrays +// See AV1Packet for description of AV1 Payload Header +func (p *AV1Payloader) Payload(mtu uint16, payload []byte) (payloads [][]byte) { + maxFragmentSize := int(mtu) - av1PayloaderHeadersize - 2 + payloadDataRemaining := len(payload) + payloadDataIndex := 0 + + // Make sure the fragment/payload size is correct + if min(maxFragmentSize, payloadDataRemaining) <= 0 { + return payloads + } + + for payloadDataRemaining > 0 { + currentFragmentSize := min(maxFragmentSize, payloadDataRemaining) + leb128Size := 1 + if currentFragmentSize >= 127 { + leb128Size = 2 + } + + out := make([]byte, av1PayloaderHeadersize+leb128Size+currentFragmentSize) + leb128Value := obu.EncodeLEB128(uint(currentFragmentSize)) + if leb128Size == 1 { + out[1] = byte(leb128Value) + } else { + out[1] = byte(leb128Value >> 8) + out[2] = byte(leb128Value) + } + + copy(out[av1PayloaderHeadersize+leb128Size:], payload[payloadDataIndex:payloadDataIndex+currentFragmentSize]) + payloads = append(payloads, out) + + payloadDataRemaining -= currentFragmentSize + payloadDataIndex += currentFragmentSize + + if len(payloads) > 1 { + out[0] ^= zMask + } + if payloadDataRemaining != 0 { + out[0] ^= yMask + } + } + + return payloads +} + +// AV1Packet represents a depacketized AV1 RTP Packet +// +// 0 1 2 3 4 5 6 7 +// +-+-+-+-+-+-+-+-+ +// |Z|Y| W |N|-|-|-| +// +-+-+-+-+-+-+-+-+ +// +// https://aomediacodec.github.io/av1-rtp-spec/#44-av1-aggregation-header +type AV1Packet struct { + // Z: MUST be set to 1 if the first OBU element is an + // OBU fragment that is a continuation of an OBU fragment + // from the previous packet, and MUST be set to 0 otherwise. + Z bool + + // Y: MUST be set to 1 if the last OBU element is an OBU fragment + // that will continue in the next packet, and MUST be set to 0 otherwise. + Y bool + + // W: two bit field that describes the number of OBU elements in the packet. + // This field MUST be set equal to 0 or equal to the number of OBU elements + // contained in the packet. If set to 0, each OBU element MUST be preceded by + // a length field. If not set to 0 (i.e., W = 1, 2 or 3) the last OBU element + // MUST NOT be preceded by a length field. Instead, the length of the last OBU + // element contained in the packet can be calculated as follows: + // Length of the last OBU element = + // length of the RTP payload + // - length of aggregation header + // - length of previous OBU elements including length fields + W byte + + // N: MUST be set to 1 if the packet is the first packet of a coded video sequence, and MUST be set to 0 otherwise. + N bool + + // Each AV1 RTP Packet is a collection of OBU Elements. Each OBU Element may be a full OBU, or just a fragment of one. + // AV1Frame provides the tools to construct a collection of OBUs from a collection of OBU Elements + OBUElements [][]byte +} + +// Unmarshal parses the passed byte slice and stores the result in the AV1Packet this method is called upon +func (p *AV1Packet) Unmarshal(payload []byte) ([]byte, error) { + if payload == nil { + return nil, errNilPacket + } else if len(payload) < 2 { + return nil, errShortPacket + } + + p.Z = ((payload[0] & zMask) >> zBitshift) != 0 + p.Y = ((payload[0] & yMask) >> yBitshift) != 0 + p.N = ((payload[0] & nMask) >> nBitshift) != 0 + p.W = (payload[0] & wMask) >> wBitshift + + if p.Z && p.N { + return nil, errIsKeyframeAndFragment + } + + currentIndex := uint(1) + p.OBUElements = [][]byte{} + + var ( + obuElementLength, bytesRead uint + err error + ) + for i := 1; ; i++ { + if currentIndex == uint(len(payload)) { + break + } + + // If W bit is set the last OBU Element will have no length header + if byte(i) == p.W { + bytesRead = 0 + obuElementLength = uint(len(payload)) - currentIndex + } else { + obuElementLength, bytesRead, err = obu.ReadLeb128(payload[currentIndex:]) + if err != nil { + return nil, err + } + } + + currentIndex += bytesRead + if uint(len(payload)) < currentIndex+obuElementLength { + return nil, errShortPacket + } + p.OBUElements = append(p.OBUElements, payload[currentIndex:currentIndex+obuElementLength]) + currentIndex += obuElementLength + } + + return payload[1:], nil +} diff --git a/vendor/github.com/pion/rtp/codecs/error.go b/vendor/github.com/pion/rtp/codecs/error.go index 38ee9076b..7f72e7b8e 100644 --- a/vendor/github.com/pion/rtp/codecs/error.go +++ b/vendor/github.com/pion/rtp/codecs/error.go @@ -8,4 +8,7 @@ var ( errTooManyPDiff = errors.New("too many PDiff") errTooManySpatialLayers = errors.New("too many spatial layers") errUnhandledNALUType = errors.New("NALU Type is unhandled") + + // AV1 Errors + errIsKeyframeAndFragment = errors.New("bits Z and N are set. Not possible to have OBU be tail fragment and be keyframe") ) diff --git a/vendor/github.com/pion/rtp/codecs/g711_packet.go b/vendor/github.com/pion/rtp/codecs/g711_packet.go index 2348a79f7..7ab68b2c3 100644 --- a/vendor/github.com/pion/rtp/codecs/g711_packet.go +++ b/vendor/github.com/pion/rtp/codecs/g711_packet.go @@ -6,7 +6,7 @@ type G711Payloader struct{} // Payload fragments an G711 packet across one or more byte arrays func (p *G711Payloader) Payload(mtu uint16, payload []byte) [][]byte { var out [][]byte - if payload == nil || mtu <= 0 { + if payload == nil || mtu == 0 { return out } diff --git a/vendor/github.com/pion/rtp/codecs/h265_packet.go b/vendor/github.com/pion/rtp/codecs/h265_packet.go index a4ae84334..6f0490dc6 100644 --- a/vendor/github.com/pion/rtp/codecs/h265_packet.go +++ b/vendor/github.com/pion/rtp/codecs/h265_packet.go @@ -1,6 +1,7 @@ package codecs import ( + "encoding/binary" "errors" "fmt" ) @@ -727,6 +728,8 @@ var ( type H265Packet struct { packet isH265Packet mightNeedDONL bool + + videoDepacketizer } // WithDONL can be called to specify whether or not DONL might be parsed. @@ -801,3 +804,16 @@ func (p *H265Packet) Unmarshal(payload []byte) ([]byte, error) { func (p *H265Packet) Packet() isH265Packet { return p.packet } + +// IsPartitionHead checks if this is the head of a packetized nalu stream. +func (*H265Packet) IsPartitionHead(payload []byte) bool { + if len(payload) < 3 { + return false + } + + if H265NALUHeader(binary.BigEndian.Uint16(payload[0:2])).Type() == h265NaluFragmentationUnitType { + return H265FragmentationUnitHeader(payload[2]).S() + } + + return true +} diff --git a/vendor/github.com/pion/rtp/header_extension.go b/vendor/github.com/pion/rtp/header_extension.go new file mode 100644 index 000000000..c143ac119 --- /dev/null +++ b/vendor/github.com/pion/rtp/header_extension.go @@ -0,0 +1,350 @@ +package rtp + +import ( + "encoding/binary" + "fmt" + "io" +) + +const ( + headerExtensionProfileOneByte = 0xBEDE + headerExtensionProfileTwoByte = 0x1000 + headerExtensionIDReserved = 0xF +) + +// HeaderExtension represents an RTP extension header. +type HeaderExtension interface { + Set(id uint8, payload []byte) error + GetIDs() []uint8 + Get(id uint8) []byte + Del(id uint8) error + + Unmarshal(buf []byte) (int, error) + Marshal() ([]byte, error) + MarshalTo(buf []byte) (int, error) + MarshalSize() int +} + +// OneByteHeaderExtension is an RFC8285 one-byte header extension. +type OneByteHeaderExtension struct { + payload []byte +} + +// Set sets the extension payload for the specified ID. +func (e *OneByteHeaderExtension) Set(id uint8, buf []byte) error { + if id < 1 || id > 14 { + return fmt.Errorf("%w actual(%d)", errRFC8285OneByteHeaderIDRange, id) + } + if len(buf) > 16 { + return fmt.Errorf("%w actual(%d)", errRFC8285OneByteHeaderSize, len(buf)) + } + + for n := 4; n < len(e.payload); { + if e.payload[n] == 0x00 { // padding + n++ + continue + } + + extid := e.payload[n] >> 4 + len := int(e.payload[n]&^0xF0 + 1) + n++ + + if extid == id { + e.payload = append(e.payload[:n+1], append(buf, e.payload[n+1+len:]...)...) + return nil + } + n += len + } + e.payload = append(e.payload, (id<<4 | uint8(len(buf)-1))) + e.payload = append(e.payload, buf...) + binary.BigEndian.PutUint16(e.payload[2:4], binary.BigEndian.Uint16(e.payload[2:4])+1) + return nil +} + +// GetIDs returns the available IDs. +func (e *OneByteHeaderExtension) GetIDs() []uint8 { + ids := make([]uint8, 0, binary.BigEndian.Uint16(e.payload[2:4])) + for n := 4; n < len(e.payload); { + if e.payload[n] == 0x00 { // padding + n++ + continue + } + + extid := e.payload[n] >> 4 + len := int(e.payload[n]&^0xF0 + 1) + n++ + + if extid == headerExtensionIDReserved { + break + } + + ids = append(ids, extid) + n += len + } + return ids +} + +// Get returns the payload of the extension with the given ID. +func (e *OneByteHeaderExtension) Get(id uint8) []byte { + for n := 4; n < len(e.payload); { + if e.payload[n] == 0x00 { // padding + n++ + continue + } + + extid := e.payload[n] >> 4 + len := int(e.payload[n]&^0xF0 + 1) + n++ + + if extid == id { + return e.payload[n : n+len] + } + n += len + } + return nil +} + +// Del deletes the extension with the specified ID. +func (e *OneByteHeaderExtension) Del(id uint8) error { + for n := 4; n < len(e.payload); { + if e.payload[n] == 0x00 { // padding + n++ + continue + } + + extid := e.payload[n] >> 4 + len := int(e.payload[n]&^0xF0 + 1) + + if extid == id { + e.payload = append(e.payload[:n], e.payload[n+1+len:]...) + return nil + } + n += len + 1 + } + return errHeaderExtensionNotFound +} + +// Unmarshal parses the extension payload. +func (e *OneByteHeaderExtension) Unmarshal(buf []byte) (int, error) { + profile := binary.BigEndian.Uint16(buf[0:2]) + if profile != headerExtensionProfileOneByte { + return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2]) + } + e.payload = buf + return len(buf), nil +} + +// Marshal returns the extension payload. +func (e OneByteHeaderExtension) Marshal() ([]byte, error) { + return e.payload, nil +} + +// MarshalTo writes the extension payload to the given buffer. +func (e OneByteHeaderExtension) MarshalTo(buf []byte) (int, error) { + size := e.MarshalSize() + if size > len(buf) { + return 0, io.ErrShortBuffer + } + return copy(buf, e.payload), nil +} + +// MarshalSize returns the size of the extension payload. +func (e OneByteHeaderExtension) MarshalSize() int { + return len(e.payload) +} + +// TwoByteHeaderExtension is an RFC8285 two-byte header extension. +type TwoByteHeaderExtension struct { + payload []byte +} + +// Set sets the extension payload for the specified ID. +func (e *TwoByteHeaderExtension) Set(id uint8, buf []byte) error { + if id < 1 || id > 255 { + return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderIDRange, id) + } + if len(buf) > 255 { + return fmt.Errorf("%w actual(%d)", errRFC8285TwoByteHeaderSize, len(buf)) + } + + for n := 4; n < len(e.payload); { + if e.payload[n] == 0x00 { // padding + n++ + continue + } + + extid := e.payload[n] + n++ + + len := int(e.payload[n]) + n++ + + if extid == id { + e.payload = append(e.payload[:n+2], append(buf, e.payload[n+2+len:]...)...) + return nil + } + n += len + } + e.payload = append(e.payload, id, uint8(len(buf))) + e.payload = append(e.payload, buf...) + binary.BigEndian.PutUint16(e.payload[2:4], binary.BigEndian.Uint16(e.payload[2:4])+1) + return nil +} + +// GetIDs returns the available IDs. +func (e *TwoByteHeaderExtension) GetIDs() []uint8 { + ids := make([]uint8, 0, binary.BigEndian.Uint16(e.payload[2:4])) + for n := 4; n < len(e.payload); { + if e.payload[n] == 0x00 { // padding + n++ + continue + } + + extid := e.payload[n] + n++ + + len := int(e.payload[n]) + n++ + + ids = append(ids, extid) + n += len + } + return ids +} + +// Get returns the payload of the extension with the given ID. +func (e *TwoByteHeaderExtension) Get(id uint8) []byte { + for n := 4; n < len(e.payload); { + if e.payload[n] == 0x00 { // padding + n++ + continue + } + + extid := e.payload[n] + n++ + + len := int(e.payload[n]) + n++ + + if extid == id { + return e.payload[n : n+len] + } + n += len + } + return nil +} + +// Del deletes the extension with the specified ID. +func (e *TwoByteHeaderExtension) Del(id uint8) error { + for n := 4; n < len(e.payload); { + if e.payload[n] == 0x00 { // padding + n++ + continue + } + + extid := e.payload[n] + + len := int(e.payload[n+1]) + + if extid == id { + e.payload = append(e.payload[:n], e.payload[n+2+len:]...) + return nil + } + n += len + 2 + } + return errHeaderExtensionNotFound +} + +// Unmarshal parses the extension payload. +func (e *TwoByteHeaderExtension) Unmarshal(buf []byte) (int, error) { + profile := binary.BigEndian.Uint16(buf[0:2]) + if profile != headerExtensionProfileTwoByte { + return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2]) + } + e.payload = buf + return len(buf), nil +} + +// Marshal returns the extension payload. +func (e TwoByteHeaderExtension) Marshal() ([]byte, error) { + return e.payload, nil +} + +// MarshalTo marshals the extension to the given buffer. +func (e TwoByteHeaderExtension) MarshalTo(buf []byte) (int, error) { + size := e.MarshalSize() + if size > len(buf) { + return 0, io.ErrShortBuffer + } + return copy(buf, e.payload), nil +} + +// MarshalSize returns the size of the extension payload. +func (e TwoByteHeaderExtension) MarshalSize() int { + return len(e.payload) +} + +// RawExtension represents an RFC3550 header extension. +type RawExtension struct { + payload []byte +} + +// Set sets the extension payload for the specified ID. +func (e *RawExtension) Set(id uint8, payload []byte) error { + if id != 0 { + return fmt.Errorf("%w actual(%d)", errRFC3550HeaderIDRange, id) + } + e.payload = payload + return nil +} + +// GetIDs returns the available IDs. +func (e *RawExtension) GetIDs() []uint8 { + return []uint8{0} +} + +// Get returns the payload of the extension with the given ID. +func (e *RawExtension) Get(id uint8) []byte { + if id == 0 { + return e.payload + } + return nil +} + +// Del deletes the extension with the specified ID. +func (e *RawExtension) Del(id uint8) error { + if id == 0 { + e.payload = nil + return nil + } + return fmt.Errorf("%w actual(%d)", errRFC3550HeaderIDRange, id) +} + +// Unmarshal parses the extension from the given buffer. +func (e *RawExtension) Unmarshal(buf []byte) (int, error) { + profile := binary.BigEndian.Uint16(buf[0:2]) + if profile == headerExtensionProfileOneByte || profile == headerExtensionProfileTwoByte { + return 0, fmt.Errorf("%w actual(%x)", errHeaderExtensionNotFound, buf[0:2]) + } + e.payload = buf + return len(buf), nil +} + +// Marshal returns the raw extension payload. +func (e RawExtension) Marshal() ([]byte, error) { + return e.payload, nil +} + +// MarshalTo marshals the extension to the given buffer. +func (e RawExtension) MarshalTo(buf []byte) (int, error) { + size := e.MarshalSize() + if size > len(buf) { + return 0, io.ErrShortBuffer + } + return copy(buf, e.payload), nil +} + +// MarshalSize returns the size of the extension when marshaled. +func (e RawExtension) MarshalSize() int { + return len(e.payload) +} diff --git a/vendor/github.com/pion/rtp/packet.go b/vendor/github.com/pion/rtp/packet.go index c1d20f387..b3ae12400 100644 --- a/vendor/github.com/pion/rtp/packet.go +++ b/vendor/github.com/pion/rtp/packet.go @@ -30,7 +30,8 @@ type Header struct { // Packet represents an RTP Packet type Packet struct { Header - Payload []byte + Payload []byte + PaddingSize byte } const ( @@ -212,7 +213,8 @@ func (p *Packet) Unmarshal(buf []byte) error { } end := len(buf) if p.Header.Padding { - end -= int(buf[end-1]) + p.PaddingSize = buf[end-1] + end -= int(p.PaddingSize) } if end < n { return errTooSmall @@ -222,7 +224,7 @@ func (p *Packet) Unmarshal(buf []byte) error { } // Marshal serializes the header into bytes. -func (h *Header) Marshal() (buf []byte, err error) { +func (h Header) Marshal() (buf []byte, err error) { buf = make([]byte, h.MarshalSize()) n, err := h.MarshalTo(buf) @@ -233,7 +235,7 @@ func (h *Header) Marshal() (buf []byte, err error) { } // MarshalTo serializes the header and writes to the buffer. -func (h *Header) MarshalTo(buf []byte) (n int, err error) { +func (h Header) MarshalTo(buf []byte) (n int, err error) { /* * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 @@ -330,7 +332,7 @@ func (h *Header) MarshalTo(buf []byte) (n int, err error) { } // MarshalSize returns the size of the header once marshaled. -func (h *Header) MarshalSize() int { +func (h Header) MarshalSize() int { // NOTE: Be careful to match the MarshalTo() method. size := 12 + (len(h.CSRC) * csrcLength) @@ -455,7 +457,7 @@ func (h *Header) DelExtension(id uint8) error { } // Marshal serializes the packet into bytes. -func (p *Packet) Marshal() (buf []byte, err error) { +func (p Packet) Marshal() (buf []byte, err error) { buf = make([]byte, p.MarshalSize()) n, err := p.MarshalTo(buf) @@ -467,35 +469,40 @@ func (p *Packet) Marshal() (buf []byte, err error) { } // MarshalTo serializes the packet and writes to the buffer. -func (p *Packet) MarshalTo(buf []byte) (n int, err error) { +func (p Packet) MarshalTo(buf []byte) (n int, err error) { + p.Header.Padding = p.PaddingSize != 0 n, err = p.Header.MarshalTo(buf) if err != nil { return 0, err } // Make sure the buffer is large enough to hold the packet. - if n+len(p.Payload) > len(buf) { + if n+len(p.Payload)+int(p.PaddingSize) > len(buf) { return 0, io.ErrShortBuffer } m := copy(buf[n:], p.Payload) + if p.Header.Padding { + buf[n+m+int(p.PaddingSize-1)] = p.PaddingSize + } - return n + m, nil + return n + m + int(p.PaddingSize), nil } // MarshalSize returns the size of the packet once marshaled. -func (p *Packet) MarshalSize() int { - return p.Header.MarshalSize() + len(p.Payload) +func (p Packet) MarshalSize() int { + return p.Header.MarshalSize() + len(p.Payload) + int(p.PaddingSize) } // Clone returns a deep copy of p. -func (p *Packet) Clone() *Packet { +func (p Packet) Clone() *Packet { clone := &Packet{} clone.Header = p.Header.Clone() if p.Payload != nil { clone.Payload = make([]byte, len(p.Payload)) copy(clone.Payload, p.Payload) } + clone.PaddingSize = p.PaddingSize return clone } diff --git a/vendor/github.com/pion/rtp/pkg/obu/leb128.go b/vendor/github.com/pion/rtp/pkg/obu/leb128.go new file mode 100644 index 000000000..988a8f442 --- /dev/null +++ b/vendor/github.com/pion/rtp/pkg/obu/leb128.go @@ -0,0 +1,66 @@ +// Package obu implements tools for working with the "Open Bitstream Unit" +package obu + +import "errors" + +const ( + sevenLsbBitmask = uint(0b01111111) + msbBitmask = uint(0b10000000) +) + +// ErrFailedToReadLEB128 indicates that a buffer ended before a LEB128 value could be successfully read +var ErrFailedToReadLEB128 = errors.New("payload ended before LEB128 was finished") + +// EncodeLEB128 encodes a uint as LEB128 +func EncodeLEB128(in uint) (out uint) { + for { + // Copy seven bits from in and discard + // what we have copied from in + out |= (in & sevenLsbBitmask) + in >>= 7 + + // If we have more bits to encode set MSB + // otherwise we are done + if in != 0 { + out |= msbBitmask + out <<= 8 + } else { + return out + } + } +} + +func decodeLEB128(in uint) (out uint) { + for { + // Take 7 LSB from in + out |= (in & sevenLsbBitmask) + + // Discard the MSB + in >>= 8 + if in == 0 { + return out + } + + out <<= 7 + } +} + +// ReadLeb128 scans an buffer and decodes a Leb128 value. +// If the end of the buffer is reached and all MSB are set +// an error is returned +func ReadLeb128(in []byte) (uint, uint, error) { + var encodedLength uint + + for i := range in { + encodedLength |= uint(in[i]) + + if in[i]&byte(msbBitmask) == 0 { + return decodeLEB128(encodedLength), uint(i + 1), nil + } + + // Make more room for next read + encodedLength <<= 8 + } + + return 0, 0, ErrFailedToReadLEB128 +} diff --git a/vendor/github.com/pion/rtp/renovate.json b/vendor/github.com/pion/rtp/renovate.json index 08c1e39d6..f1614058a 100644 --- a/vendor/github.com/pion/rtp/renovate.json +++ b/vendor/github.com/pion/rtp/renovate.json @@ -1,6 +1,7 @@ { "extends": [ - "config:base" + "config:base", + ":disableDependencyDashboard" ], "postUpdateOptions": [ "gomodTidy" diff --git a/vendor/github.com/pion/rtp/transportccextension.go b/vendor/github.com/pion/rtp/transportccextension.go index f9ffe4eb1..236af056a 100644 --- a/vendor/github.com/pion/rtp/transportccextension.go +++ b/vendor/github.com/pion/rtp/transportccextension.go @@ -23,7 +23,7 @@ type TransportCCExtension struct { } // Marshal serializes the members to buffer -func (t *TransportCCExtension) Marshal() ([]byte, error) { +func (t TransportCCExtension) Marshal() ([]byte, error) { buf := make([]byte, transportCCExtensionSize) binary.BigEndian.PutUint16(buf[0:2], t.TransportSequence) return buf, nil diff --git a/vendor/github.com/pion/sctp/.gitignore b/vendor/github.com/pion/sctp/.gitignore index 83db74ba5..f977e7485 100644 --- a/vendor/github.com/pion/sctp/.gitignore +++ b/vendor/github.com/pion/sctp/.gitignore @@ -22,3 +22,4 @@ cover.out *.wasm examples/sfu-ws/cert.pem examples/sfu-ws/key.pem +wasm_exec.js diff --git a/vendor/github.com/pion/sctp/.golangci.yml b/vendor/github.com/pion/sctp/.golangci.yml index d6162c970..48696f16b 100644 --- a/vendor/github.com/pion/sctp/.golangci.yml +++ b/vendor/github.com/pion/sctp/.golangci.yml @@ -15,14 +15,21 @@ linters-settings: linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully - - deadcode # Finds unused code + - contextcheck # check the function whether use a non-inherited context + - decorder # check declaration order and count of types, constants, variables and functions - depguard # Go linter that checks if package imports are in a list of acceptable packages - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection + - durationcheck # check for two durations multiplied together - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. - exhaustive # check exhaustiveness of enum switch statements - exportloopref # checks for pointers to enclosing loop variables + - forcetypeassert # finds forced type assertions - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - gochecknoinits # Checks that no init functions are present in Go code @@ -35,40 +42,60 @@ linters: - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - gosimple # Linter for Go source code that specializes in simplifying a code - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - grouper # An analyzer to analyze expression groups. + - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used - misspell # Finds commonly misspelled English words in comments - nakedret # Finds naked returns in functions greater than a specified function length + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. - noctx # noctx finds sending http request without context.Context - - scopelint # Scopelint checks for unpinned variables in go programs + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # golint replacement, finds style mistakes - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - - structcheck # Finds unused struct fields - stylecheck # Stylecheck is a replacement for golint + - tagliatelle # Checks the struct tags. + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types - - varcheck # Finds unused global variables and constants + - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: + - containedctx # containedctx is a linter that detects struct contained context.Context field + - cyclop # checks function and package cyclomatic complexity + - exhaustivestruct # Checks if all struct's fields are initialized + - forbidigo # Forbids identifiers - funlen # Tool for detection of long functions - gocyclo # Computes and checks the cyclomatic complexity of functions - godot # Check if comments end in a period - gomnd # An analyzer to detect magic numbers. + - ifshort # Checks that your code uses short syntax for if-statements whenever possible + - ireturn # Accept Interfaces, Return Concrete Types - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - nestif # Reports deeply nested if statements - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - nolintlint # Reports ill-formed or insufficient nolint directives + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - prealloc # Finds slice declarations that could potentially be preallocated + - promlinter # Check Prometheus metrics naming via promlint - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. - testpackage # linter that makes you use a separate _test package + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers + - varnamelen # checks that the length of a variable's name matches its scope + - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! issues: diff --git a/vendor/github.com/pion/sctp/.goreleaser.yml b/vendor/github.com/pion/sctp/.goreleaser.yml new file mode 100644 index 000000000..2caa5fbd3 --- /dev/null +++ b/vendor/github.com/pion/sctp/.goreleaser.yml @@ -0,0 +1,2 @@ +builds: +- skip: true diff --git a/vendor/github.com/pion/sctp/AUTHORS.txt b/vendor/github.com/pion/sctp/AUTHORS.txt index 91314c3f8..ed95135c8 100644 --- a/vendor/github.com/pion/sctp/AUTHORS.txt +++ b/vendor/github.com/pion/sctp/AUTHORS.txt @@ -2,23 +2,32 @@ # we would love to have you https://github.com/pion/webrtc/wiki/Contributing # # This file is auto generated, using git to list all individuals contributors. -# see `.github/generate-authors.sh` for the scripting +# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting Aaron France +Adrian Cable Atsushi Watanabe backkem Cecylia Bocovich chenkaiC4 +Eric Daniels Hugo Arregui Hugo Arregui Jerko Steiner +Jerry Tao John Bradley Konstantin Itskov Lukas Herman Luke Curley Michael MacDonald ronan +Sam Lancia Sean DuBois Sean DuBois +Steffen Vogel Teddy +Will Forcey Yutaka Takeda ZHENK + +# List of contributors not appearing in Git history + diff --git a/vendor/github.com/pion/sctp/DESIGN.md b/vendor/github.com/pion/sctp/DESIGN.md deleted file mode 100644 index 02ac16113..000000000 --- a/vendor/github.com/pion/sctp/DESIGN.md +++ /dev/null @@ -1,20 +0,0 @@ -

- Design -

- -### Portable -Pion SCTP is written in Go and extremely portable. Anywhere Golang runs, Pion SCTP should work as well! Instead of dealing with complicated -cross-compiling of multiple libraries, you now can run anywhere with one `go build` - -### Simple API -The API is based on an io.ReadWriteCloser. - -### Readable -If code comes from an RFC we try to make sure everything is commented with a link to the spec. -This makes learning and debugging easier, this library was written to also serve as a guide for others. - -### Tested -Every commit is tested via travis-ci Go provides fantastic facilities for testing, and more will be added as time goes on. - -### Shared libraries -Every pion product is built using shared libraries, allowing others to review and reuse our libraries. diff --git a/vendor/github.com/pion/sctp/README.md b/vendor/github.com/pion/sctp/README.md index f1815cdd7..3ca234452 100644 --- a/vendor/github.com/pion/sctp/README.md +++ b/vendor/github.com/pion/sctp/README.md @@ -6,32 +6,29 @@

A Go implementation of SCTP

Pion SCTP - Slack Widget
- Build Status - GoDoc + GitHub Workflow Status + Go Reference Coverage Status Go Report Card - License: MIT


-See [DESIGN.md](DESIGN.md) for an overview of features and future goals. - ### Roadmap The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones. ### Community -Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion). +Pion has an active community on the [Slack](https://pion.ly/slack). + +Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news. We are always looking to support **your projects**. Please reach out if you have something to build! - If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly) ### Contributing -Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible: +Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible: [AUTHORS.txt](./AUTHORS.txt) ### License MIT License - see [LICENSE](LICENSE) for full text diff --git a/vendor/github.com/pion/sctp/association.go b/vendor/github.com/pion/sctp/association.go index caf6d4eb1..fb7fd38db 100644 --- a/vendor/github.com/pion/sctp/association.go +++ b/vendor/github.com/pion/sctp/association.go @@ -17,28 +17,30 @@ import ( ) // Use global random generator to properly seed by crypto grade random. +var globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals + +// Association errors var ( - globalMathRandomGenerator = randutil.NewMathRandomGenerator() // nolint:gochecknoglobals - errChunk = errors.New("abort chunk, with following errors") - errShutdownNonEstablished = errors.New("shutdown called in non-established state") - errAssociationClosedBeforeConn = errors.New("association closed before connecting") - errSilentlyDiscard = errors.New("silently discard") - errInitNotStoredToSend = errors.New("the init not stored to send") - errCookieEchoNotStoredToSend = errors.New("cookieEcho not stored to send") - errSCTPPacketSourcePortZero = errors.New("sctp packet must not have a source port of 0") - errSCTPPacketDestinationPortZero = errors.New("sctp packet must not have a destination port of 0") - errInitChunkBundled = errors.New("init chunk must not be bundled with any other chunk") - errInitChunkVerifyTagNotZero = errors.New("init chunk expects a verification tag of 0 on the packet when out-of-the-blue") - errHandleInitState = errors.New("todo: handle Init when in state") - errInitAckNoCookie = errors.New("no cookie in InitAck") - errInflightQueueTSNPop = errors.New("unable to be popped from inflight queue TSN") - errTSNRequestNotExist = errors.New("requested non-existent TSN") - errResetPacketInStateNotExist = errors.New("sending reset packet in non-established state") - errParamterType = errors.New("unexpected parameter type") - errPayloadDataStateNotExist = errors.New("sending payload data in non-established state") - errChunkTypeUnhandled = errors.New("unhandled chunk type") - errHandshakeInitAck = errors.New("handshake failed (INIT ACK)") - errHandshakeCookieEcho = errors.New("handshake failed (COOKIE ECHO)") + ErrChunk = errors.New("abort chunk, with following errors") + ErrShutdownNonEstablished = errors.New("shutdown called in non-established state") + ErrAssociationClosedBeforeConn = errors.New("association closed before connecting") + ErrSilentlyDiscard = errors.New("silently discard") + ErrInitNotStoredToSend = errors.New("the init not stored to send") + ErrCookieEchoNotStoredToSend = errors.New("cookieEcho not stored to send") + ErrSCTPPacketSourcePortZero = errors.New("sctp packet must not have a source port of 0") + ErrSCTPPacketDestinationPortZero = errors.New("sctp packet must not have a destination port of 0") + ErrInitChunkBundled = errors.New("init chunk must not be bundled with any other chunk") + ErrInitChunkVerifyTagNotZero = errors.New("init chunk expects a verification tag of 0 on the packet when out-of-the-blue") + ErrHandleInitState = errors.New("todo: handle Init when in state") + ErrInitAckNoCookie = errors.New("no cookie in InitAck") + ErrInflightQueueTSNPop = errors.New("unable to be popped from inflight queue TSN") + ErrTSNRequestNotExist = errors.New("requested non-existent TSN") + ErrResetPacketInStateNotExist = errors.New("sending reset packet in non-established state") + ErrParamterType = errors.New("unexpected parameter type") + ErrPayloadDataStateNotExist = errors.New("sending payload data in non-established state") + ErrChunkTypeUnhandled = errors.New("unhandled chunk type") + ErrHandshakeInitAck = errors.New("handshake failed (INIT ACK)") + ErrHandshakeCookieEcho = errors.New("handshake failed (COOKIE ECHO)") ) const ( @@ -115,21 +117,17 @@ func getAssociationStateString(a uint32) string { // Association represents an SCTP association // 13.2. Parameters Necessary per Association (i.e., the TCB) -// Peer : Tag value to be sent in every packet and is received -// Verification: in the INIT or INIT ACK chunk. -// Tag : // -// My : Tag expected in every inbound packet and sent in the -// Verification: INIT or INIT ACK chunk. +// Peer : Tag value to be sent in every packet and is received +// Verification: in the INIT or INIT ACK chunk. +// Tag : +// State : A state variable indicating what state the association +// : is in, i.e., COOKIE-WAIT, COOKIE-ECHOED, ESTABLISHED, +// : SHUTDOWN-PENDING, SHUTDOWN-SENT, SHUTDOWN-RECEIVED, +// : SHUTDOWN-ACK-SENT. // -// Tag : -// State : A state variable indicating what state the association -// : is in, i.e., COOKIE-WAIT, COOKIE-ECHOED, ESTABLISHED, -// : SHUTDOWN-PENDING, SHUTDOWN-SENT, SHUTDOWN-RECEIVED, -// : SHUTDOWN-ACK-SENT. -// -// Note: No "CLOSED" state is illustrated since if a -// association is "CLOSED" its TCB SHOULD be removed. +// Note: No "CLOSED" state is illustrated since if a +// association is "CLOSED" its TCB SHOULD be removed. type Association struct { bytesReceived uint64 bytesSent uint64 @@ -152,6 +150,9 @@ type Association struct { willSendShutdownAck bool willSendShutdownComplete bool + willSendAbort bool + willSendAbortCause errorCause + // Reconfig myNextRSN uint32 reconfigs map[uint32]*chunkReconfig @@ -168,7 +169,8 @@ type Association struct { pendingQueue *pendingQueue controlQueue *controlQueue mtu uint32 - maxPayloadSize uint32 // max DATA chunk payload size + maxPayloadSize uint32 // max DATA chunk payload size + srtt atomic.Value // type float64 cumulativeTSNAckPoint uint32 advancedPeerTSNAckPoint uint32 useForwardTSN bool @@ -243,7 +245,7 @@ func Server(config Config) (*Association, error) { } return a, nil case <-a.readLoopCloseCh: - return nil, errAssociationClosedBeforeConn + return nil, ErrAssociationClosedBeforeConn } } @@ -259,7 +261,7 @@ func Client(config Config) (*Association, error) { } return a, nil case <-a.readLoopCloseCh: - return nil, errAssociationClosedBeforeConn + return nil, ErrAssociationClosedBeforeConn } } @@ -307,7 +309,7 @@ func createAssociation(config Config) *Association { handshakeCompletedCh: make(chan error), cumulativeTSNAckPoint: tsn - 1, advancedPeerTSNAckPoint: tsn - 1, - silentError: errSilentlyDiscard, + silentError: ErrSilentlyDiscard, stats: &associationStats{}, log: config.LoggerFactory.NewLogger("sctp"), } @@ -322,6 +324,7 @@ func createAssociation(config Config) *Association { a.log.Tracef("[%s] updated cwnd=%d ssthresh=%d inflight=%d (INI)", a.name, a.cwnd, a.ssthresh, a.inflightQueue.getNumBytes()) + a.srtt.Store(float64(0)) a.t1Init = newRTXTimer(timerT1Init, a, maxInitRetrans) a.t1Cookie = newRTXTimer(timerT1Cookie, a, maxInitRetrans) a.t2Shutdown = newRTXTimer(timerT2Shutdown, a, noMaxRetrans) // retransmit forever @@ -363,7 +366,7 @@ func (a *Association) init(isClient bool) { func (a *Association) sendInit() error { a.log.Debugf("[%s] sending INIT", a.name) if a.storedInit == nil { - return errInitNotStoredToSend + return ErrInitNotStoredToSend } outbound := &packet{} @@ -384,7 +387,7 @@ func (a *Association) sendInit() error { // caller must hold a.lock func (a *Association) sendCookieEcho() error { if a.storedCookieEcho == nil { - return errCookieEchoNotStoredToSend + return ErrCookieEchoNotStoredToSend } a.log.Debugf("[%s] sending COOKIE-ECHO", a.name) @@ -410,7 +413,7 @@ func (a *Association) Shutdown(ctx context.Context) error { state := a.getState() if state != established { - return fmt.Errorf("%w: shutdown %s", errShutdownNonEstablished, a.name) + return fmt.Errorf("%w: shutdown %s", ErrShutdownNonEstablished, a.name) } // Attempt a graceful shutdown. @@ -469,6 +472,26 @@ func (a *Association) close() error { return err } +// Abort sends the abort packet with user initiated abort and immediately +// closes the connection. +func (a *Association) Abort(reason string) { + a.log.Debugf("[%s] aborting association: %s", a.name, reason) + + a.lock.Lock() + + a.willSendAbort = true + a.willSendAbortCause = &errorCauseUserInitiatedAbort{ + upperLayerAbortReason: []byte(reason), + } + + a.lock.Unlock() + + a.awakeWriteLoop() + + // Wait for readLoop to end + <-a.readLoopCloseCh +} + func (a *Association) closeAllTimers() { // Close all retransmission & ack timers a.t1Init.close() @@ -538,7 +561,7 @@ loop: for _, raw := range rawPackets { _, err := a.netConn.Write(raw) if err != nil { - if err != io.EOF { + if !errors.Is(err, io.EOF) { a.log.Warnf("[%s] failed to write packets on netConn: %v", a.name, err) } a.log.Debugf("[%s] writeLoop ended", a.name) @@ -752,7 +775,7 @@ func (a *Association) gatherOutboundSackPackets(rawPackets [][]byte) [][]byte { if a.ackState == ackStateImmediate { a.ackState = ackStateIdle sack := a.createSelectiveAckChunk() - a.log.Debugf("[%s] sending SACK: %s", a.name, sack.String()) + a.log.Debugf("[%s] sending SACK: %s", a.name, sack) raw, err := a.createPacket([]chunk{sack}).marshal() if err != nil { a.log.Warnf("[%s] failed to serialize a SACK packet", a.name) @@ -829,12 +852,39 @@ func (a *Association) gatherOutboundShutdownPackets(rawPackets [][]byte) ([][]by return rawPackets, ok } +func (a *Association) gatherAbortPacket() ([]byte, error) { + cause := a.willSendAbortCause + + a.willSendAbort = false + a.willSendAbortCause = nil + + abort := &chunkAbort{} + + if cause != nil { + abort.errorCauses = []errorCause{cause} + } + + raw, err := a.createPacket([]chunk{abort}).marshal() + + return raw, err +} + // gatherOutbound gathers outgoing packets. The returned bool value set to // false means the association should be closed down after the final send. func (a *Association) gatherOutbound() ([][]byte, bool) { a.lock.Lock() defer a.lock.Unlock() + if a.willSendAbort { + pkt, err := a.gatherAbortPacket() + if err != nil { + a.log.Warnf("[%s] failed to serialize an abort packet", a.name) + return nil, false + } + + return [][]byte{pkt}, false + } + rawPackets := [][]byte{} if a.controlQueue.size() > 0 { @@ -880,7 +930,7 @@ func checkPacket(p *packet) error { // identify the association to which this packet belongs. The port // number 0 MUST NOT be used. if p.sourcePort == 0 { - return errSCTPPacketSourcePortZero + return ErrSCTPPacketSourcePortZero } // This is the SCTP port number to which this packet is destined. @@ -888,7 +938,7 @@ func checkPacket(p *packet) error { // SCTP packet to the correct receiving endpoint/application. The // port number 0 MUST NOT be used. if p.destinationPort == 0 { - return errSCTPPacketDestinationPortZero + return ErrSCTPPacketDestinationPortZero } // Check values on the packet that are specific to a particular chunk type @@ -899,13 +949,13 @@ func checkPacket(p *packet) error { // They MUST be the only chunks present in the SCTP packets that carry // them. if len(p.chunks) != 1 { - return errInitChunkBundled + return ErrInitChunkBundled } // A packet containing an INIT chunk MUST have a zero Verification // Tag. if p.verificationTag != 0 { - return errInitChunkVerifyTagNotZero + return ErrInitChunkVerifyTagNotZero } } } @@ -961,6 +1011,26 @@ func (a *Association) BytesReceived() uint64 { return atomic.LoadUint64(&a.bytesReceived) } +// MTU returns the association's current MTU +func (a *Association) MTU() uint32 { + return atomic.LoadUint32(&a.mtu) +} + +// CWND returns the association's current congestion window (cwnd) +func (a *Association) CWND() uint32 { + return atomic.LoadUint32(&a.cwnd) +} + +// RWND returns the association's current receiver window (rwnd) +func (a *Association) RWND() uint32 { + return atomic.LoadUint32(&a.rwnd) +} + +// SRTT returns the latest smoothed round-trip time (srrt) +func (a *Association) SRTT() float64 { + return a.srtt.Load().(float64) //nolint:forcetypeassert +} + func setSupportedExtensions(init *chunkInitCommon) { // nolint:godox // TODO RFC5061 https://tools.ietf.org/html/rfc6525#section-5.2 @@ -987,7 +1057,7 @@ func (a *Association) handleInit(p *packet, i *chunkInit) ([]*packet, error) { if state != closed && state != cookieWait && state != cookieEchoed { // 5.2.2. Unexpected INIT in States Other than CLOSED, COOKIE-ECHOED, // COOKIE-WAIT, and SHUTDOWN-ACK-SENT - return nil, fmt.Errorf("%w: %s", errHandleInitState, getAssociationStateString(state)) + return nil, fmt.Errorf("%w: %s", ErrHandleInitState, getAssociationStateString(state)) } // Should we be setting any of these permanently until we've ACKed further? @@ -1008,14 +1078,14 @@ func (a *Association) handleInit(p *packet, i *chunkInit) ([]*packet, error) { case *paramSupportedExtensions: for _, t := range v.ChunkTypes { if t == ctForwardTSN { - a.log.Debugf("[%s] use ForwardTSN (on init)\n", a.name) + a.log.Debugf("[%s] use ForwardTSN (on init)", a.name) a.useForwardTSN = true } } } } if !a.useForwardTSN { - a.log.Warnf("[%s] not using ForwardTSN (on init)\n", a.name) + a.log.Warnf("[%s] not using ForwardTSN (on init)", a.name) } outbound := &packet{} @@ -1093,17 +1163,17 @@ func (a *Association) handleInitAck(p *packet, i *chunkInitAck) error { case *paramSupportedExtensions: for _, t := range v.ChunkTypes { if t == ctForwardTSN { - a.log.Debugf("[%s] use ForwardTSN (on initAck)\n", a.name) + a.log.Debugf("[%s] use ForwardTSN (on initAck)", a.name) a.useForwardTSN = true } } } } if !a.useForwardTSN { - a.log.Warnf("[%s] not using ForwardTSN (on initAck)\n", a.name) + a.log.Warnf("[%s] not using ForwardTSN (on initAck)", a.name) } if cookieParam == nil { - return errInitAckNoCookie + return ErrInitAckNoCookie } a.storedCookieEcho = &chunkCookieEcho{} @@ -1365,7 +1435,7 @@ func (a *Association) processSelectiveAck(d *chunkSelectiveAck) (map[uint16]int, for i := a.cumulativeTSNAckPoint + 1; sna32LTE(i, d.cumulativeTSNAck); i++ { c, ok := a.inflightQueue.pop(i) if !ok { - return nil, 0, fmt.Errorf("%w: %v", errInflightQueueTSNPop, i) + return nil, 0, fmt.Errorf("%w: %v", ErrInflightQueueTSNPop, i) } if !c.acked { @@ -1401,6 +1471,7 @@ func (a *Association) processSelectiveAck(d *chunkSelectiveAck) (map[uint16]int, a.minTSN2MeasureRTT = a.myNextTSN rtt := time.Since(c.since).Seconds() * 1000.0 srtt := a.rtoMgr.setNewRTT(rtt) + a.srtt.Store(srtt) a.log.Tracef("[%s] SACK: measured-rtt=%f srtt=%f new-rto=%f", a.name, rtt, srtt, a.rtoMgr.getRTO()) } @@ -1420,7 +1491,7 @@ func (a *Association) processSelectiveAck(d *chunkSelectiveAck) (map[uint16]int, tsn := d.cumulativeTSNAck + uint32(i) c, ok := a.inflightQueue.get(tsn) if !ok { - return nil, 0, fmt.Errorf("%w: %v", errTSNRequestNotExist, tsn) + return nil, 0, fmt.Errorf("%w: %v", ErrTSNRequestNotExist, tsn) } if !c.acked { @@ -1439,6 +1510,7 @@ func (a *Association) processSelectiveAck(d *chunkSelectiveAck) (map[uint16]int, a.minTSN2MeasureRTT = a.myNextTSN rtt := time.Since(c.since).Seconds() * 1000.0 srtt := a.rtoMgr.setNewRTT(rtt) + a.srtt.Store(srtt) a.log.Tracef("[%s] SACK: measured-rtt=%f srtt=%f new-rto=%f", a.name, rtt, srtt, a.rtoMgr.getRTO()) } @@ -1536,7 +1608,7 @@ func (a *Association) processFastRetransmission(cumTSNAckPoint, htna uint32, cum for tsn := cumTSNAckPoint + 1; sna32LT(tsn, maxTSN); tsn++ { c, ok := a.inflightQueue.get(tsn) if !ok { - return fmt.Errorf("%w: %v", errTSNRequestNotExist, tsn) + return fmt.Errorf("%w: %v", ErrTSNRequestNotExist, tsn) } if !c.acked && !c.abandoned() && c.missIndicator < 3 { c.missIndicator++ @@ -1747,6 +1819,17 @@ func (a *Association) handleShutdownComplete(_ *chunkShutdownComplete) error { return nil } +func (a *Association) handleAbort(c *chunkAbort) error { + var errStr string + for _, e := range c.errorCauses { + errStr += fmt.Sprintf("(%s)", e) + } + + _ = a.close() + + return fmt.Errorf("[%s] %w: %s", a.name, ErrChunk, errStr) +} + // createForwardTSN generates ForwardTSN chunk. // This method will be be called if useForwardTSN is set to false. // The caller should hold the lock. @@ -1849,7 +1932,7 @@ func (a *Association) handleForwardTSN(c *chunkForwardTSN) []*packet { // send a SACK to its peer (the sender of the FORWARD TSN) since such a // duplicate may indicate the previous SACK was lost in the network. - a.log.Tracef("[%s] should send ack? newCumTSN=%d peerLastTSN=%d\n", + a.log.Tracef("[%s] should send ack? newCumTSN=%d peerLastTSN=%d", a.name, c.newCumulativeTSN, a.peerLastTSN) if sna32LTE(c.newCumulativeTSN, a.peerLastTSN) { a.log.Tracef("[%s] sending ack on Forward TSN", a.name) @@ -1902,7 +1985,7 @@ func (a *Association) sendResetRequest(streamIdentifier uint16) error { state := a.getState() if state != established { - return fmt.Errorf("%w: state=%s", errResetPacketInStateNotExist, + return fmt.Errorf("%w: state=%s", ErrResetPacketInStateNotExist, getAssociationStateString(state)) } @@ -1924,21 +2007,23 @@ func (a *Association) sendResetRequest(streamIdentifier uint16) error { func (a *Association) handleReconfigParam(raw param) (*packet, error) { switch p := raw.(type) { case *paramOutgoingResetRequest: + a.log.Tracef("[%s] handleReconfigParam (OutgoingResetRequest)", a.name) a.reconfigRequests[p.reconfigRequestSequenceNumber] = p resp := a.resetStreamsIfAny(p) if resp != nil { return resp, nil } - return nil, nil + return nil, nil //nolint:nilnil case *paramReconfigResponse: + a.log.Tracef("[%s] handleReconfigParam (ReconfigResponse)", a.name) delete(a.reconfigs, p.reconfigResponseSequenceNumber) if len(a.reconfigs) == 0 { a.tReconfig.stop() } - return nil, nil + return nil, nil //nolint:nilnil default: - return nil, fmt.Errorf("%w: %t", errParamterType, p) + return nil, fmt.Errorf("%w: %t", ErrParamterType, p) } } @@ -1953,7 +2038,11 @@ func (a *Association) resetStreamsIfAny(p *paramOutgoingResetRequest) *packet { if !ok { continue } - a.unregisterStream(s, io.EOF) + a.lock.Unlock() + s.onInboundStreamReset() + a.lock.Lock() + a.log.Debugf("[%s] deleting stream %d", a.name, id) + delete(a.streams, s.streamIdentifier) } delete(a.reconfigRequests, p.reconfigRequestSequenceNumber) } else { @@ -2095,7 +2184,7 @@ func (a *Association) sendPayloadData(chunks []*chunkPayloadData) error { state := a.getState() if state != established { - return fmt.Errorf("%w: state=%s", errPayloadDataStateNotExist, + return fmt.Errorf("%w: state=%s", ErrPayloadDataStateNotExist, getAssociationStateString(state)) } @@ -2251,6 +2340,8 @@ func (a *Association) handleChunk(p *packet, c chunk) error { return nil } + isAbort := false + switch c := c.(type) { case *chunkInit: packets, err = a.handleInit(p, c) @@ -2259,11 +2350,8 @@ func (a *Association) handleChunk(p *packet, c chunk) error { err = a.handleInitAck(p, c) case *chunkAbort: - var errStr string - for _, e := range c.errorCauses { - errStr += fmt.Sprintf("(%s)", e) - } - return fmt.Errorf("[%s] %w: %s", a.name, errChunk, errStr) + isAbort = true + err = a.handleAbort(c) case *chunkError: var errStr string @@ -2301,11 +2389,15 @@ func (a *Association) handleChunk(p *packet, c chunk) error { err = a.handleShutdownComplete(c) default: - err = errChunkTypeUnhandled + err = ErrChunkTypeUnhandled } // Log and return, the only condition that is fatal is a ABORT chunk if err != nil { + if isAbort { + return err + } + a.log.Errorf("Failed to handle chunk: %v", err) return nil } @@ -2425,13 +2517,13 @@ func (a *Association) onRetransmissionFailure(id int) { if id == timerT1Init { a.log.Errorf("[%s] retransmission failure: T1-init", a.name) - a.handshakeCompletedCh <- errHandshakeInitAck + a.handshakeCompletedCh <- ErrHandshakeInitAck return } if id == timerT1Cookie { a.log.Errorf("[%s] retransmission failure: T1-cookie", a.name) - a.handshakeCompletedCh <- errHandshakeCookieEcho + a.handshakeCompletedCh <- ErrHandshakeCookieEcho return } diff --git a/vendor/github.com/pion/sctp/chunk_abort.go b/vendor/github.com/pion/sctp/chunk_abort.go index 9288412ef..eab64a4f4 100644 --- a/vendor/github.com/pion/sctp/chunk_abort.go +++ b/vendor/github.com/pion/sctp/chunk_abort.go @@ -16,24 +16,25 @@ SHUTDOWN COMPLETE) MAY be bundled with an ABORT, but they MUST be placed before the ABORT in the SCTP packet or they will be ignored by the receiver. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Type = 6 |Reserved |T| Length | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | -| zero or more Error Causes | -| | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type = 6 |Reserved |T| Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | zero or more Error Causes | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ type chunkAbort struct { chunkHeader errorCauses []errorCause } +// Abort chunk errors var ( - errChunkTypeNotAbort = errors.New("ChunkType is not of type ABORT") - errBuildAbortChunkFailed = errors.New("failed build Abort Chunk") + ErrChunkTypeNotAbort = errors.New("ChunkType is not of type ABORT") + ErrBuildAbortChunkFailed = errors.New("failed build Abort Chunk") ) func (a *chunkAbort) unmarshal(raw []byte) error { @@ -42,7 +43,7 @@ func (a *chunkAbort) unmarshal(raw []byte) error { } if a.typ != ctAbort { - return fmt.Errorf("%w: actually is %s", errChunkTypeNotAbort, a.typ.String()) + return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotAbort, a.typ.String()) } offset := chunkHeaderSize @@ -53,7 +54,7 @@ func (a *chunkAbort) unmarshal(raw []byte) error { e, err := buildErrorCause(raw[offset:]) if err != nil { - return fmt.Errorf("%w: %v", errBuildAbortChunkFailed, err) + return fmt.Errorf("%w: %v", ErrBuildAbortChunkFailed, err) //nolint:errorlint } offset += int(e.length()) diff --git a/vendor/github.com/pion/sctp/chunk_cookie_ack.go b/vendor/github.com/pion/sctp/chunk_cookie_ack.go index 742529cbd..5d63b1c48 100644 --- a/vendor/github.com/pion/sctp/chunk_cookie_ack.go +++ b/vendor/github.com/pion/sctp/chunk_cookie_ack.go @@ -8,17 +8,20 @@ import ( /* chunkCookieAck represents an SCTP Chunk of type chunkCookieAck - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Type = 11 |Chunk Flags | Length = 4 | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type = 11 |Chunk Flags | Length = 4 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ type chunkCookieAck struct { chunkHeader } -var errChunkTypeNotCookieAck = errors.New("ChunkType is not of type COOKIEACK") +// Cookie ack chunk errors +var ( + ErrChunkTypeNotCookieAck = errors.New("ChunkType is not of type COOKIEACK") +) func (c *chunkCookieAck) unmarshal(raw []byte) error { if err := c.chunkHeader.unmarshal(raw); err != nil { @@ -26,7 +29,7 @@ func (c *chunkCookieAck) unmarshal(raw []byte) error { } if c.typ != ctCookieAck { - return fmt.Errorf("%w: actually is %s", errChunkTypeNotCookieAck, c.typ.String()) + return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotCookieAck, c.typ.String()) } return nil diff --git a/vendor/github.com/pion/sctp/chunk_cookie_echo.go b/vendor/github.com/pion/sctp/chunk_cookie_echo.go index 3aaced3de..8eb956647 100644 --- a/vendor/github.com/pion/sctp/chunk_cookie_echo.go +++ b/vendor/github.com/pion/sctp/chunk_cookie_echo.go @@ -8,21 +8,24 @@ import ( /* CookieEcho represents an SCTP Chunk of type CookieEcho - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Type = 10 |Chunk Flags | Length | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Cookie | -| | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type = 10 |Chunk Flags | Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Cookie | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ type chunkCookieEcho struct { chunkHeader cookie []byte } -var errChunkTypeNotCookieEcho = errors.New("ChunkType is not of type COOKIEECHO") +// Cookie echo chunk errors +var ( + ErrChunkTypeNotCookieEcho = errors.New("ChunkType is not of type COOKIEECHO") +) func (c *chunkCookieEcho) unmarshal(raw []byte) error { if err := c.chunkHeader.unmarshal(raw); err != nil { @@ -30,7 +33,7 @@ func (c *chunkCookieEcho) unmarshal(raw []byte) error { } if c.typ != ctCookieEcho { - return fmt.Errorf("%w: actually is %s", errChunkTypeNotCookieEcho, c.typ.String()) + return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotCookieEcho, c.typ.String()) } c.cookie = c.raw diff --git a/vendor/github.com/pion/sctp/chunk_error.go b/vendor/github.com/pion/sctp/chunk_error.go index d58d752c4..d2a2b2971 100644 --- a/vendor/github.com/pion/sctp/chunk_error.go +++ b/vendor/github.com/pion/sctp/chunk_error.go @@ -6,41 +6,42 @@ import ( ) /* - Operation Error (ERROR) (9) +Operation Error (ERROR) (9) - An endpoint sends this chunk to its peer endpoint to notify it of - certain error conditions. It contains one or more error causes. An - Operation Error is not considered fatal in and of itself, but may be - used with an ERROR chunk to report a fatal condition. It has the - following parameters: +An endpoint sends this chunk to its peer endpoint to notify it of +certain error conditions. It contains one or more error causes. An +Operation Error is not considered fatal in and of itself, but may be +used with an ERROR chunk to report a fatal condition. It has the +following parameters: - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type = 9 | Chunk Flags | Length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - \ \ - / one or more Error Causes / - \ \ - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type = 9 | Chunk Flags | Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + \ \ + / one or more Error Causes / + \ \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - Chunk Flags: 8 bits +Chunk Flags: 8 bits - Set to 0 on transmit and ignored on receipt. + Set to 0 on transmit and ignored on receipt. - Length: 16 bits (unsigned integer) +Length: 16 bits (unsigned integer) - Set to the size of the chunk in bytes, including the chunk header - and all the Error Cause fields present. + Set to the size of the chunk in bytes, including the chunk header + and all the Error Cause fields present. */ type chunkError struct { chunkHeader errorCauses []errorCause } +// Error chunk errors var ( - errChunkTypeNotCtError = errors.New("ChunkType is not of type ctError") - errBuildErrorChunkFailed = errors.New("failed build Error Chunk") + ErrChunkTypeNotCtError = errors.New("ChunkType is not of type ctError") + ErrBuildErrorChunkFailed = errors.New("failed build Error Chunk") ) func (a *chunkError) unmarshal(raw []byte) error { @@ -49,7 +50,7 @@ func (a *chunkError) unmarshal(raw []byte) error { } if a.typ != ctError { - return fmt.Errorf("%w, actually is %s", errChunkTypeNotCtError, a.typ.String()) + return fmt.Errorf("%w, actually is %s", ErrChunkTypeNotCtError, a.typ.String()) } offset := chunkHeaderSize @@ -60,7 +61,7 @@ func (a *chunkError) unmarshal(raw []byte) error { e, err := buildErrorCause(raw[offset:]) if err != nil { - return fmt.Errorf("%w: %v", errBuildErrorChunkFailed, err) + return fmt.Errorf("%w: %v", ErrBuildErrorChunkFailed, err) //nolint:errorlint } offset += int(e.length()) diff --git a/vendor/github.com/pion/sctp/chunk_forward_tsn.go b/vendor/github.com/pion/sctp/chunk_forward_tsn.go index 8f04d93f2..70b5079f0 100644 --- a/vendor/github.com/pion/sctp/chunk_forward_tsn.go +++ b/vendor/github.com/pion/sctp/chunk_forward_tsn.go @@ -43,9 +43,10 @@ const ( forwardTSNStreamLength = 4 ) +// Forward TSN chunk errors var ( - errMarshalStreamFailed = errors.New("failed to marshal stream") - errChunkTooShort = errors.New("chunk too short") + ErrMarshalStreamFailed = errors.New("failed to marshal stream") + ErrChunkTooShort = errors.New("chunk too short") ) func (c *chunkForwardTSN) unmarshal(raw []byte) error { @@ -54,7 +55,7 @@ func (c *chunkForwardTSN) unmarshal(raw []byte) error { } if len(c.raw) < newCumulativeTSNLength { - return errChunkTooShort + return ErrChunkTooShort } c.newCumulativeTSN = binary.BigEndian.Uint32(c.raw[0:]) @@ -65,7 +66,7 @@ func (c *chunkForwardTSN) unmarshal(raw []byte) error { s := chunkForwardTSNStream{} if err := s.unmarshal(c.raw[offset:]); err != nil { - return fmt.Errorf("%w: %v", errMarshalStreamFailed, err) + return fmt.Errorf("%w: %v", ErrMarshalStreamFailed, err) //nolint:errorlint } c.streams = append(c.streams, s) @@ -84,7 +85,7 @@ func (c *chunkForwardTSN) marshal() ([]byte, error) { for _, s := range c.streams { b, err := s.marshal() if err != nil { - return nil, fmt.Errorf("%w: %v", errMarshalStreamFailed, err) + return nil, fmt.Errorf("%w: %v", ErrMarshalStreamFailed, err) //nolint:errorlint } out = append(out, b...) } @@ -129,7 +130,7 @@ func (s *chunkForwardTSNStream) length() int { func (s *chunkForwardTSNStream) unmarshal(raw []byte) error { if len(raw) < forwardTSNStreamLength { - return errChunkTooShort + return ErrChunkTooShort } s.identifier = binary.BigEndian.Uint16(raw[0:]) s.sequence = binary.BigEndian.Uint16(raw[2:]) diff --git a/vendor/github.com/pion/sctp/chunk_heartbeat.go b/vendor/github.com/pion/sctp/chunk_heartbeat.go index 7db97cdf2..3cfcd5ada 100644 --- a/vendor/github.com/pion/sctp/chunk_heartbeat.go +++ b/vendor/github.com/pion/sctp/chunk_heartbeat.go @@ -15,16 +15,15 @@ the present association. The parameter field contains the Heartbeat Information, which is a variable-length opaque data structure understood only by the sender. - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Type = 4 | Chunk Flags | Heartbeat Length | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | -| Heartbeat Information TLV (Variable-Length) | -| | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type = 4 | Chunk Flags | Heartbeat Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | Heartbeat Information TLV (Variable-Length) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Defined as a variable-length parameter using the format described in Section 3.2.1, i.e.: @@ -38,36 +37,37 @@ type chunkHeartbeat struct { params []param } +// Heartbeat chunk errors var ( - errChunkTypeNotHeartbeat = errors.New("ChunkType is not of type HEARTBEAT") - errHeartbeatNotLongEnoughInfo = errors.New("heartbeat is not long enough to contain Heartbeat Info") - errParseParamTypeFailed = errors.New("failed to parse param type") - errHeartbeatParam = errors.New("heartbeat should only have HEARTBEAT param") - errHeartbeatChunkUnmarshal = errors.New("failed unmarshalling param in Heartbeat Chunk") + ErrChunkTypeNotHeartbeat = errors.New("ChunkType is not of type HEARTBEAT") + ErrHeartbeatNotLongEnoughInfo = errors.New("heartbeat is not long enough to contain Heartbeat Info") + ErrParseParamTypeFailed = errors.New("failed to parse param type") + ErrHeartbeatParam = errors.New("heartbeat should only have HEARTBEAT param") + ErrHeartbeatChunkUnmarshal = errors.New("failed unmarshalling param in Heartbeat Chunk") ) func (h *chunkHeartbeat) unmarshal(raw []byte) error { if err := h.chunkHeader.unmarshal(raw); err != nil { return err } else if h.typ != ctHeartbeat { - return fmt.Errorf("%w: actually is %s", errChunkTypeNotHeartbeat, h.typ.String()) + return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotHeartbeat, h.typ.String()) } if len(raw) <= chunkHeaderSize { - return fmt.Errorf("%w: %d", errHeartbeatNotLongEnoughInfo, len(raw)) + return fmt.Errorf("%w: %d", ErrHeartbeatNotLongEnoughInfo, len(raw)) } pType, err := parseParamType(raw[chunkHeaderSize:]) if err != nil { - return fmt.Errorf("%w: %v", errParseParamTypeFailed, err) + return fmt.Errorf("%w: %v", ErrParseParamTypeFailed, err) //nolint:errorlint } if pType != heartbeatInfo { - return fmt.Errorf("%w: instead have %s", errHeartbeatParam, pType.String()) + return fmt.Errorf("%w: instead have %s", ErrHeartbeatParam, pType.String()) } p, err := buildParam(pType, raw[chunkHeaderSize:]) if err != nil { - return fmt.Errorf("%w: %v", errHeartbeatChunkUnmarshal, err) + return fmt.Errorf("%w: %v", ErrHeartbeatChunkUnmarshal, err) //nolint:errorlint } h.params = append(h.params, p) @@ -75,7 +75,7 @@ func (h *chunkHeartbeat) unmarshal(raw []byte) error { } func (h *chunkHeartbeat) Marshal() ([]byte, error) { - return nil, errUnimplemented + return nil, ErrUnimplemented } func (h *chunkHeartbeat) check() (abort bool, err error) { diff --git a/vendor/github.com/pion/sctp/chunk_heartbeat_ack.go b/vendor/github.com/pion/sctp/chunk_heartbeat_ack.go index feb822c20..3d21b593d 100644 --- a/vendor/github.com/pion/sctp/chunk_heartbeat_ack.go +++ b/vendor/github.com/pion/sctp/chunk_heartbeat_ack.go @@ -15,16 +15,15 @@ HEARTBEAT chunk to which this ack is responding. The parameter field contains a variable-length opaque data structure. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Type = 5 | Chunk Flags | Heartbeat Ack Length | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | -| Heartbeat Information TLV (Variable-Length) | -| | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type = 5 | Chunk Flags | Heartbeat Ack Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | Heartbeat Information TLV (Variable-Length) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Defined as a variable-length parameter using the format described in Section 3.2.1, i.e.: @@ -38,34 +37,35 @@ type chunkHeartbeatAck struct { params []param } +// Heartbeat ack chunk errors var ( - errUnimplemented = errors.New("unimplemented") - errHeartbeatAckParams = errors.New("heartbeat Ack must have one param") - errHeartbeatAckNotHeartbeatInfo = errors.New("heartbeat Ack must have one param, and it should be a HeartbeatInfo") - errHeartbeatAckMarshalParam = errors.New("unable to marshal parameter for Heartbeat Ack") + ErrUnimplemented = errors.New("unimplemented") + ErrHeartbeatAckParams = errors.New("heartbeat Ack must have one param") + ErrHeartbeatAckNotHeartbeatInfo = errors.New("heartbeat Ack must have one param, and it should be a HeartbeatInfo") + ErrHeartbeatAckMarshalParam = errors.New("unable to marshal parameter for Heartbeat Ack") ) -func (h *chunkHeartbeatAck) unmarshal(raw []byte) error { - return errUnimplemented +func (h *chunkHeartbeatAck) unmarshal([]byte) error { + return ErrUnimplemented } func (h *chunkHeartbeatAck) marshal() ([]byte, error) { if len(h.params) != 1 { - return nil, errHeartbeatAckParams + return nil, ErrHeartbeatAckParams } switch h.params[0].(type) { case *paramHeartbeatInfo: // ParamHeartbeatInfo is valid default: - return nil, errHeartbeatAckNotHeartbeatInfo + return nil, ErrHeartbeatAckNotHeartbeatInfo } out := make([]byte, 0) for idx, p := range h.params { pp, err := p.marshal() if err != nil { - return nil, fmt.Errorf("%w: %v", errHeartbeatAckMarshalParam, err) + return nil, fmt.Errorf("%w: %v", ErrHeartbeatAckMarshalParam, err) //nolint:errorlint } out = append(out, pp...) diff --git a/vendor/github.com/pion/sctp/chunk_init.go b/vendor/github.com/pion/sctp/chunk_init.go index 8a2a1cdaa..8eab506b9 100644 --- a/vendor/github.com/pion/sctp/chunk_init.go +++ b/vendor/github.com/pion/sctp/chunk_init.go @@ -10,30 +10,31 @@ Init represents an SCTP Chunk of type INIT See chunkInitCommon for the fixed headers -Variable Parameters Status Type Value -------------------------------------------------------------- -IPv4 IP (Note 1) Optional 5 -IPv6 IP (Note 1) Optional 6 -Cookie Preservative Optional 9 -Reserved for ECN Capable (Note 2) Optional 32768 (0x8000) -Host Name IP (Note 3) Optional 11 -Supported IP Types (Note 4) Optional 12 + Variable Parameters Status Type Value + ------------------------------------------------------------- + IPv4 IP (Note 1) Optional 5 + IPv6 IP (Note 1) Optional 6 + Cookie Preservative Optional 9 + Reserved for ECN Capable (Note 2) Optional 32768 (0x8000) + Host Name IP (Note 3) Optional 11 + Supported IP Types (Note 4) Optional 12 */ type chunkInit struct { chunkHeader chunkInitCommon } +// Init chunk errors var ( - errChunkTypeNotTypeInit = errors.New("ChunkType is not of type INIT") - errChunkValueNotLongEnough = errors.New("chunk Value isn't long enough for mandatory parameters exp") - errChunkTypeInitFlagZero = errors.New("ChunkType of type INIT flags must be all 0") - errChunkTypeInitUnmarshalFailed = errors.New("failed to unmarshal INIT body") - errChunkTypeInitMarshalFailed = errors.New("failed marshaling INIT common data") - errChunkTypeInitInitateTagZero = errors.New("ChunkType of type INIT ACK InitiateTag must not be 0") - errInitInboundStreamRequestZero = errors.New("INIT ACK inbound stream request must be > 0") - errInitOutboundStreamRequestZero = errors.New("INIT ACK outbound stream request must be > 0") - errInitAdvertisedReceiver1500 = errors.New("INIT ACK Advertised Receiver Window Credit (a_rwnd) must be >= 1500") + ErrChunkTypeNotTypeInit = errors.New("ChunkType is not of type INIT") + ErrChunkValueNotLongEnough = errors.New("chunk Value isn't long enough for mandatory parameters exp") + ErrChunkTypeInitFlagZero = errors.New("ChunkType of type INIT flags must be all 0") + ErrChunkTypeInitUnmarshalFailed = errors.New("failed to unmarshal INIT body") + ErrChunkTypeInitMarshalFailed = errors.New("failed marshaling INIT common data") + ErrChunkTypeInitInitateTagZero = errors.New("ChunkType of type INIT ACK InitiateTag must not be 0") + ErrInitInboundStreamRequestZero = errors.New("INIT ACK inbound stream request must be > 0") + ErrInitOutboundStreamRequestZero = errors.New("INIT ACK outbound stream request must be > 0") + ErrInitAdvertisedReceiver1500 = errors.New("INIT ACK Advertised Receiver Window Credit (a_rwnd) must be >= 1500") ) func (i *chunkInit) unmarshal(raw []byte) error { @@ -42,20 +43,20 @@ func (i *chunkInit) unmarshal(raw []byte) error { } if i.typ != ctInit { - return fmt.Errorf("%w: actually is %s", errChunkTypeNotTypeInit, i.typ.String()) + return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotTypeInit, i.typ.String()) } else if len(i.raw) < initChunkMinLength { - return fmt.Errorf("%w: %d actual: %d", errChunkValueNotLongEnough, initChunkMinLength, len(i.raw)) + return fmt.Errorf("%w: %d actual: %d", ErrChunkValueNotLongEnough, initChunkMinLength, len(i.raw)) } // The Chunk Flags field in INIT is reserved, and all bits in it should // be set to 0 by the sender and ignored by the receiver. The sequence // of parameters within an INIT can be processed in any order. if i.flags != 0 { - return errChunkTypeInitFlagZero + return ErrChunkTypeInitFlagZero } if err := i.chunkInitCommon.unmarshal(i.raw); err != nil { - return fmt.Errorf("%w: %v", errChunkTypeInitUnmarshalFailed, err) + return fmt.Errorf("%w: %v", ErrChunkTypeInitUnmarshalFailed, err) //nolint:errorlint } return nil @@ -64,7 +65,7 @@ func (i *chunkInit) unmarshal(raw []byte) error { func (i *chunkInit) marshal() ([]byte, error) { initShared, err := i.chunkInitCommon.marshal() if err != nil { - return nil, fmt.Errorf("%w: %v", errChunkTypeInitMarshalFailed, err) + return nil, fmt.Errorf("%w: %v", ErrChunkTypeInitMarshalFailed, err) //nolint:errorlint } i.chunkHeader.typ = ctInit @@ -86,7 +87,7 @@ func (i *chunkInit) check() (abort bool, err error) { // association by transmitting an ABORT. if i.initiateTag == 0 { abort = true - return abort, errChunkTypeInitInitateTagZero + return abort, ErrChunkTypeInitInitateTagZero } // Defines the maximum number of streams the sender of this INIT @@ -101,7 +102,7 @@ func (i *chunkInit) check() (abort bool, err error) { // the association. if i.numInboundStreams == 0 { abort = true - return abort, errInitInboundStreamRequestZero + return abort, ErrInitInboundStreamRequestZero } // Defines the number of outbound streams the sender of this INIT @@ -113,7 +114,7 @@ func (i *chunkInit) check() (abort bool, err error) { if i.numOutboundStreams == 0 { abort = true - return abort, errInitOutboundStreamRequestZero + return abort, ErrInitOutboundStreamRequestZero } // An SCTP receiver MUST be able to receive a minimum of 1500 bytes in @@ -122,7 +123,7 @@ func (i *chunkInit) check() (abort bool, err error) { // ACK. if i.advertisedReceiverWindowCredit < 1500 { abort = true - return abort, errInitAdvertisedReceiver1500 + return abort, ErrInitAdvertisedReceiver1500 } return false, nil diff --git a/vendor/github.com/pion/sctp/chunk_init_ack.go b/vendor/github.com/pion/sctp/chunk_init_ack.go index 02a3344b0..c34f1d6b6 100644 --- a/vendor/github.com/pion/sctp/chunk_init_ack.go +++ b/vendor/github.com/pion/sctp/chunk_init_ack.go @@ -10,30 +10,31 @@ chunkInitAck represents an SCTP Chunk of type INIT ACK See chunkInitCommon for the fixed headers -Variable Parameters Status Type Value -------------------------------------------------------------- -State Cookie Mandatory 7 -IPv4 IP (Note 1) Optional 5 -IPv6 IP (Note 1) Optional 6 -Unrecognized Parameter Optional 8 -Reserved for ECN Capable (Note 2) Optional 32768 (0x8000) -Host Name IP (Note 3) Optional 11 + Variable Parameters Status Type Value + ------------------------------------------------------------- + State Cookie Mandatory 7 + IPv4 IP (Note 1) Optional 5 + IPv6 IP (Note 1) Optional 6 + Unrecognized Parameter Optional 8 + Reserved for ECN Capable (Note 2) Optional 32768 (0x8000) + Host Name IP (Note 3) Optional 11 */ type chunkInitAck struct { chunkHeader chunkInitCommon } +// Init ack chunk errors var ( - errChunkTypeNotInitAck = errors.New("ChunkType is not of type INIT ACK") - errChunkNotLongEnoughForParams = errors.New("chunk Value isn't long enough for mandatory parameters exp") - errChunkTypeInitAckFlagZero = errors.New("ChunkType of type INIT ACK flags must be all 0") - errInitAckUnmarshalFailed = errors.New("failed to unmarshal INIT body") - errInitCommonDataMarshalFailed = errors.New("failed marshaling INIT common data") - errChunkTypeInitAckInitateTagZero = errors.New("ChunkType of type INIT ACK InitiateTag must not be 0") - errInitAckInboundStreamRequestZero = errors.New("INIT ACK inbound stream request must be > 0") - errInitAckOutboundStreamRequestZero = errors.New("INIT ACK outbound stream request must be > 0") - errInitAckAdvertisedReceiver1500 = errors.New("INIT ACK Advertised Receiver Window Credit (a_rwnd) must be >= 1500") + ErrChunkTypeNotInitAck = errors.New("ChunkType is not of type INIT ACK") + ErrChunkNotLongEnoughForParams = errors.New("chunk Value isn't long enough for mandatory parameters exp") + ErrChunkTypeInitAckFlagZero = errors.New("ChunkType of type INIT ACK flags must be all 0") + ErrInitAckUnmarshalFailed = errors.New("failed to unmarshal INIT body") + ErrInitCommonDataMarshalFailed = errors.New("failed marshaling INIT common data") + ErrChunkTypeInitAckInitateTagZero = errors.New("ChunkType of type INIT ACK InitiateTag must not be 0") + ErrInitAckInboundStreamRequestZero = errors.New("INIT ACK inbound stream request must be > 0") + ErrInitAckOutboundStreamRequestZero = errors.New("INIT ACK outbound stream request must be > 0") + ErrInitAckAdvertisedReceiver1500 = errors.New("INIT ACK Advertised Receiver Window Credit (a_rwnd) must be >= 1500") ) func (i *chunkInitAck) unmarshal(raw []byte) error { @@ -42,20 +43,20 @@ func (i *chunkInitAck) unmarshal(raw []byte) error { } if i.typ != ctInitAck { - return fmt.Errorf("%w: actually is %s", errChunkTypeNotInitAck, i.typ.String()) + return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotInitAck, i.typ.String()) } else if len(i.raw) < initChunkMinLength { - return fmt.Errorf("%w: %d actual: %d", errChunkNotLongEnoughForParams, initChunkMinLength, len(i.raw)) + return fmt.Errorf("%w: %d actual: %d", ErrChunkNotLongEnoughForParams, initChunkMinLength, len(i.raw)) } // The Chunk Flags field in INIT is reserved, and all bits in it should // be set to 0 by the sender and ignored by the receiver. The sequence // of parameters within an INIT can be processed in any order. if i.flags != 0 { - return errChunkTypeInitAckFlagZero + return ErrChunkTypeInitAckFlagZero } if err := i.chunkInitCommon.unmarshal(i.raw); err != nil { - return fmt.Errorf("%w: %v", errInitAckUnmarshalFailed, err) + return fmt.Errorf("%w: %v", ErrInitAckUnmarshalFailed, err) //nolint:errorlint } return nil @@ -64,7 +65,7 @@ func (i *chunkInitAck) unmarshal(raw []byte) error { func (i *chunkInitAck) marshal() ([]byte, error) { initShared, err := i.chunkInitCommon.marshal() if err != nil { - return nil, fmt.Errorf("%w: %v", errInitCommonDataMarshalFailed, err) + return nil, fmt.Errorf("%w: %v", ErrInitCommonDataMarshalFailed, err) //nolint:errorlint } i.chunkHeader.typ = ctInitAck @@ -87,7 +88,7 @@ func (i *chunkInitAck) check() (abort bool, err error) { // purpose. if i.initiateTag == 0 { abort = true - return abort, errChunkTypeInitAckInitateTagZero + return abort, ErrChunkTypeInitAckInitateTagZero } // Defines the maximum number of streams the sender of this INIT ACK @@ -102,7 +103,7 @@ func (i *chunkInitAck) check() (abort bool, err error) { // destroy the association discarding its TCB. if i.numInboundStreams == 0 { abort = true - return abort, errInitAckInboundStreamRequestZero + return abort, ErrInitAckInboundStreamRequestZero } // Defines the number of outbound streams the sender of this INIT ACK @@ -115,7 +116,7 @@ func (i *chunkInitAck) check() (abort bool, err error) { if i.numOutboundStreams == 0 { abort = true - return abort, errInitAckOutboundStreamRequestZero + return abort, ErrInitAckOutboundStreamRequestZero } // An SCTP receiver MUST be able to receive a minimum of 1500 bytes in @@ -124,7 +125,7 @@ func (i *chunkInitAck) check() (abort bool, err error) { // ACK. if i.advertisedReceiverWindowCredit < 1500 { abort = true - return abort, errInitAckAdvertisedReceiver1500 + return abort, ErrInitAckAdvertisedReceiver1500 } return false, nil diff --git a/vendor/github.com/pion/sctp/chunk_init_common.go b/vendor/github.com/pion/sctp/chunk_init_common.go index 14535cab1..c2ccfadc0 100644 --- a/vendor/github.com/pion/sctp/chunk_init_common.go +++ b/vendor/github.com/pion/sctp/chunk_init_common.go @@ -53,10 +53,11 @@ const ( initOptionalVarHeaderLength = 4 ) +// Init chunk errors var ( - errInitChunkParseParamTypeFailed = errors.New("failed to parse param type") - errInitChunkUnmarshalParam = errors.New("failed unmarshalling param in Init Chunk") - errInitAckMarshalParam = errors.New("unable to marshal parameter for INIT/INITACK") + ErrInitChunkParseParamTypeFailed = errors.New("failed to parse param type") + ErrInitChunkUnmarshalParam = errors.New("failed unmarshalling param in Init Chunk") + ErrInitAckMarshalParam = errors.New("unable to marshal parameter for INIT/INITACK") ) func (i *chunkInitCommon) unmarshal(raw []byte) error { @@ -89,11 +90,11 @@ func (i *chunkInitCommon) unmarshal(raw []byte) error { if remaining > initOptionalVarHeaderLength { pType, err := parseParamType(raw[offset:]) if err != nil { - return fmt.Errorf("%w: %v", errInitChunkParseParamTypeFailed, err) + return fmt.Errorf("%w: %v", ErrInitChunkParseParamTypeFailed, err) //nolint:errorlint } p, err := buildParam(pType, raw[offset:]) if err != nil { - return fmt.Errorf("%w: %v", errInitChunkUnmarshalParam, err) + return fmt.Errorf("%w: %v", ErrInitChunkUnmarshalParam, err) //nolint:errorlint } i.params = append(i.params, p) padding := getPadding(p.length()) @@ -117,7 +118,7 @@ func (i *chunkInitCommon) marshal() ([]byte, error) { for idx, p := range i.params { pp, err := p.marshal() if err != nil { - return nil, fmt.Errorf("%w: %v", errInitAckMarshalParam, err) + return nil, fmt.Errorf("%w: %v", ErrInitAckMarshalParam, err) //nolint:errorlint } out = append(out, pp...) diff --git a/vendor/github.com/pion/sctp/chunk_payload_data.go b/vendor/github.com/pion/sctp/chunk_payload_data.go index eecc7c8c8..50554ac45 100644 --- a/vendor/github.com/pion/sctp/chunk_payload_data.go +++ b/vendor/github.com/pion/sctp/chunk_payload_data.go @@ -10,38 +10,38 @@ import ( /* chunkPayloadData represents an SCTP Chunk of type DATA - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Type = 0 | Reserved|U|B|E| Length | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| TSN | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Stream Identifier S | Stream Sequence Number n | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Payload Protocol Identifier | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | -| User Data (seq n of Stream S) | -| | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type = 0 | Reserved|U|B|E| Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TSN | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Stream Identifier S | Stream Sequence Number n | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Payload Protocol Identifier | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | User Data (seq n of Stream S) | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ An unfragmented user message shall have both the B and E bits set to '1'. Setting both B and E bits to '0' indicates a middle fragment of a multi-fragment user message, as summarized in the following table: - B E Description -============================================================ -| 1 0 | First piece of a fragmented user message | -+----------------------------------------------------------+ -| 0 0 | Middle piece of a fragmented user message | -+----------------------------------------------------------+ -| 0 1 | Last piece of a fragmented user message | -+----------------------------------------------------------+ -| 1 1 | Unfragmented message | -============================================================ -| Table 1: Fragment Description Flags | -============================================================ + + B E Description + ============================================================ + | 1 0 | First piece of a fragmented user message | + +----------------------------------------------------------+ + | 0 0 | Middle piece of a fragmented user message | + +----------------------------------------------------------+ + | 0 1 | Last piece of a fragmented user message | + +----------------------------------------------------------+ + | 1 1 | Unfragmented message | + ============================================================ + | Table 1: Fragment Description Flags | + ============================================================ */ type chunkPayloadData struct { chunkHeader @@ -97,7 +97,10 @@ const ( PayloadTypeWebRTCBinaryEmpty PayloadProtocolIdentifier = 57 ) -var errChunkPayloadSmall = errors.New("packet is smaller than the header size") +// Data chunk errors +var ( + ErrChunkPayloadSmall = errors.New("packet is smaller than the header size") +) func (p PayloadProtocolIdentifier) String() string { switch p { @@ -127,7 +130,7 @@ func (p *chunkPayloadData) unmarshal(raw []byte) error { p.endingFragment = p.flags&payloadDataEndingFragmentBitmask != 0 if len(raw) < payloadDataHeaderSize { - return errChunkPayloadSmall + return ErrChunkPayloadSmall } p.tsn = binary.BigEndian.Uint32(p.raw[0:]) p.streamIdentifier = binary.BigEndian.Uint16(p.raw[4:]) diff --git a/vendor/github.com/pion/sctp/chunk_reconfig.go b/vendor/github.com/pion/sctp/chunk_reconfig.go index c827a40a4..9b018b213 100644 --- a/vendor/github.com/pion/sctp/chunk_reconfig.go +++ b/vendor/github.com/pion/sctp/chunk_reconfig.go @@ -28,10 +28,11 @@ type chunkReconfig struct { paramB param } +// Reconfigure chunk errors var ( - errChunkParseParamTypeFailed = errors.New("failed to parse param type") - errChunkMarshalParamAReconfigFailed = errors.New("unable to marshal parameter A for reconfig") - errChunkMarshalParamBReconfigFailed = errors.New("unable to marshal parameter B for reconfig") + ErrChunkParseParamTypeFailed = errors.New("failed to parse param type") + ErrChunkMarshalParamAReconfigFailed = errors.New("unable to marshal parameter A for reconfig") + ErrChunkMarshalParamBReconfigFailed = errors.New("unable to marshal parameter B for reconfig") ) func (c *chunkReconfig) unmarshal(raw []byte) error { @@ -40,7 +41,7 @@ func (c *chunkReconfig) unmarshal(raw []byte) error { } pType, err := parseParamType(c.raw) if err != nil { - return fmt.Errorf("%w: %v", errChunkParseParamTypeFailed, err) + return fmt.Errorf("%w: %v", ErrChunkParseParamTypeFailed, err) //nolint:errorlint } a, err := buildParam(pType, c.raw) if err != nil { @@ -53,7 +54,7 @@ func (c *chunkReconfig) unmarshal(raw []byte) error { if len(c.raw) > offset { pType, err := parseParamType(c.raw[offset:]) if err != nil { - return fmt.Errorf("%w: %v", errChunkParseParamTypeFailed, err) + return fmt.Errorf("%w: %v", ErrChunkParseParamTypeFailed, err) //nolint:errorlint } b, err := buildParam(pType, c.raw[offset:]) if err != nil { @@ -68,7 +69,7 @@ func (c *chunkReconfig) unmarshal(raw []byte) error { func (c *chunkReconfig) marshal() ([]byte, error) { out, err := c.paramA.marshal() if err != nil { - return nil, fmt.Errorf("%w: %v", errChunkMarshalParamAReconfigFailed, err) + return nil, fmt.Errorf("%w: %v", ErrChunkMarshalParamAReconfigFailed, err) //nolint:errorlint } if c.paramB != nil { // Pad param A @@ -76,7 +77,7 @@ func (c *chunkReconfig) marshal() ([]byte, error) { outB, err := c.paramB.marshal() if err != nil { - return nil, fmt.Errorf("%w: %v", errChunkMarshalParamBReconfigFailed, err) + return nil, fmt.Errorf("%w: %v", ErrChunkMarshalParamBReconfigFailed, err) //nolint:errorlint } out = append(out, outB...) diff --git a/vendor/github.com/pion/sctp/chunk_selective_ack.go b/vendor/github.com/pion/sctp/chunk_selective_ack.go index 00f83dba5..28223a920 100644 --- a/vendor/github.com/pion/sctp/chunk_selective_ack.go +++ b/vendor/github.com/pion/sctp/chunk_selective_ack.go @@ -46,10 +46,11 @@ type gapAckBlock struct { end uint16 } +// Selective ack chunk errors var ( - errChunkTypeNotSack = errors.New("ChunkType is not of type SACK") - errSackSizeNotLargeEnoughInfo = errors.New("SACK Chunk size is not large enough to contain header") - errSackSizeNotMatchPredicted = errors.New("SACK Chunk size does not match predicted amount from header values") + ErrChunkTypeNotSack = errors.New("ChunkType is not of type SACK") + ErrSackSizeNotLargeEnoughInfo = errors.New("SACK Chunk size is not large enough to contain header") + ErrSackSizeNotMatchPredicted = errors.New("SACK Chunk size does not match predicted amount from header values") ) // String makes gapAckBlock printable @@ -75,11 +76,11 @@ func (s *chunkSelectiveAck) unmarshal(raw []byte) error { } if s.typ != ctSack { - return fmt.Errorf("%w: actually is %s", errChunkTypeNotSack, s.typ.String()) + return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotSack, s.typ.String()) } if len(s.raw) < selectiveAckHeaderSize { - return fmt.Errorf("%w: %v remaining, needs %v bytes", errSackSizeNotLargeEnoughInfo, + return fmt.Errorf("%w: %v remaining, needs %v bytes", ErrSackSizeNotLargeEnoughInfo, len(s.raw), selectiveAckHeaderSize) } @@ -89,7 +90,7 @@ func (s *chunkSelectiveAck) unmarshal(raw []byte) error { s.duplicateTSN = make([]uint32, binary.BigEndian.Uint16(s.raw[10:])) if len(s.raw) != selectiveAckHeaderSize+(4*len(s.gapAckBlocks)+(4*len(s.duplicateTSN))) { - return errSackSizeNotMatchPredicted + return ErrSackSizeNotMatchPredicted } offset := selectiveAckHeaderSize diff --git a/vendor/github.com/pion/sctp/chunk_shutdown.go b/vendor/github.com/pion/sctp/chunk_shutdown.go index be0371a88..bcd9fc155 100644 --- a/vendor/github.com/pion/sctp/chunk_shutdown.go +++ b/vendor/github.com/pion/sctp/chunk_shutdown.go @@ -26,9 +26,10 @@ const ( cumulativeTSNAckLength = 4 ) +// Shutdown chunk errors var ( - errInvalidChunkSize = errors.New("invalid chunk size") - errChunkTypeNotShutdown = errors.New("ChunkType is not of type SHUTDOWN") + ErrInvalidChunkSize = errors.New("invalid chunk size") + ErrChunkTypeNotShutdown = errors.New("ChunkType is not of type SHUTDOWN") ) func (c *chunkShutdown) unmarshal(raw []byte) error { @@ -37,11 +38,11 @@ func (c *chunkShutdown) unmarshal(raw []byte) error { } if c.typ != ctShutdown { - return fmt.Errorf("%w: actually is %s", errChunkTypeNotShutdown, c.typ.String()) + return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotShutdown, c.typ.String()) } if len(c.raw) != cumulativeTSNAckLength { - return errInvalidChunkSize + return ErrInvalidChunkSize } c.cumulativeTSNAck = binary.BigEndian.Uint32(c.raw[0:]) diff --git a/vendor/github.com/pion/sctp/chunk_shutdown_ack.go b/vendor/github.com/pion/sctp/chunk_shutdown_ack.go index f575dbbd6..a8bb8d0c2 100644 --- a/vendor/github.com/pion/sctp/chunk_shutdown_ack.go +++ b/vendor/github.com/pion/sctp/chunk_shutdown_ack.go @@ -18,7 +18,10 @@ type chunkShutdownAck struct { chunkHeader } -var errChunkTypeNotShutdownAck = errors.New("ChunkType is not of type SHUTDOWN-ACK") +// Shutdown ack chunk errors +var ( + ErrChunkTypeNotShutdownAck = errors.New("ChunkType is not of type SHUTDOWN-ACK") +) func (c *chunkShutdownAck) unmarshal(raw []byte) error { if err := c.chunkHeader.unmarshal(raw); err != nil { @@ -26,7 +29,7 @@ func (c *chunkShutdownAck) unmarshal(raw []byte) error { } if c.typ != ctShutdownAck { - return fmt.Errorf("%w: actually is %s", errChunkTypeNotShutdownAck, c.typ.String()) + return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotShutdownAck, c.typ.String()) } return nil diff --git a/vendor/github.com/pion/sctp/chunk_shutdown_complete.go b/vendor/github.com/pion/sctp/chunk_shutdown_complete.go index 99883f346..4de045d62 100644 --- a/vendor/github.com/pion/sctp/chunk_shutdown_complete.go +++ b/vendor/github.com/pion/sctp/chunk_shutdown_complete.go @@ -18,7 +18,10 @@ type chunkShutdownComplete struct { chunkHeader } -var errChunkTypeNotShutdownComplete = errors.New("ChunkType is not of type SHUTDOWN-COMPLETE") +// Shutdown complete chunk errors +var ( + ErrChunkTypeNotShutdownComplete = errors.New("ChunkType is not of type SHUTDOWN-COMPLETE") +) func (c *chunkShutdownComplete) unmarshal(raw []byte) error { if err := c.chunkHeader.unmarshal(raw); err != nil { @@ -26,7 +29,7 @@ func (c *chunkShutdownComplete) unmarshal(raw []byte) error { } if c.typ != ctShutdownComplete { - return fmt.Errorf("%w: actually is %s", errChunkTypeNotShutdownComplete, c.typ.String()) + return fmt.Errorf("%w: actually is %s", ErrChunkTypeNotShutdownComplete, c.typ.String()) } return nil diff --git a/vendor/github.com/pion/sctp/chunkheader.go b/vendor/github.com/pion/sctp/chunkheader.go index be2a12264..6e9a5e9d6 100644 --- a/vendor/github.com/pion/sctp/chunkheader.go +++ b/vendor/github.com/pion/sctp/chunkheader.go @@ -13,15 +13,15 @@ transmitted in the SCTP packet. Each chunk is formatted with a Chunk Type field, a chunk-specific Flag field, a Chunk Length field, and a Value field. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Chunk Type | Chunk Flags | Chunk Length | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | -| Chunk Value | -| | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Chunk Type | Chunk Flags | Chunk Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + | Chunk Value | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ type chunkHeader struct { typ chunkType @@ -33,15 +33,16 @@ const ( chunkHeaderSize = 4 ) +// SCTP chunk header errors var ( - errChunkHeaderTooSmall = errors.New("raw is too small for a SCTP chunk") - errChunkHeaderNotEnoughSpace = errors.New("not enough data left in SCTP packet to satisfy requested length") - errChunkHeaderPaddingNonZero = errors.New("chunk padding is non-zero at offset") + ErrChunkHeaderTooSmall = errors.New("raw is too small for a SCTP chunk") + ErrChunkHeaderNotEnoughSpace = errors.New("not enough data left in SCTP packet to satisfy requested length") + ErrChunkHeaderPaddingNonZero = errors.New("chunk padding is non-zero at offset") ) func (c *chunkHeader) unmarshal(raw []byte) error { if len(raw) < chunkHeaderSize { - return fmt.Errorf("%w: raw only %d bytes, %d is the minimum length", errChunkHeaderTooSmall, len(raw), chunkHeaderSize) + return fmt.Errorf("%w: raw only %d bytes, %d is the minimum length", ErrChunkHeaderTooSmall, len(raw), chunkHeaderSize) } c.typ = chunkType(raw[0]) @@ -53,7 +54,7 @@ func (c *chunkHeader) unmarshal(raw []byte) error { lengthAfterValue := len(raw) - (chunkHeaderSize + valueLength) if lengthAfterValue < 0 { - return fmt.Errorf("%w: remain %d req %d ", errChunkHeaderNotEnoughSpace, valueLength, len(raw)-chunkHeaderSize) + return fmt.Errorf("%w: remain %d req %d ", ErrChunkHeaderNotEnoughSpace, valueLength, len(raw)-chunkHeaderSize) } else if lengthAfterValue < 4 { // https://tools.ietf.org/html/rfc4960#section-3.2 // The Chunk Length field does not count any chunk padding. @@ -67,7 +68,7 @@ func (c *chunkHeader) unmarshal(raw []byte) error { for i := lengthAfterValue; i > 0; i-- { paddingOffset := chunkHeaderSize + valueLength + (i - 1) if raw[paddingOffset] != 0 { - return fmt.Errorf("%w: %d ", errChunkHeaderPaddingNonZero, paddingOffset) + return fmt.Errorf("%w: %d ", ErrChunkHeaderPaddingNonZero, paddingOffset) } } } diff --git a/vendor/github.com/pion/sctp/error_cause.go b/vendor/github.com/pion/sctp/error_cause.go index db9a56f05..9e5e68d85 100644 --- a/vendor/github.com/pion/sctp/error_cause.go +++ b/vendor/github.com/pion/sctp/error_cause.go @@ -18,7 +18,10 @@ type errorCause interface { errorCauseCode() errorCauseCode } -var errBuildErrorCaseHandle = errors.New("BuildErrorCause does not handle") +// Error and abort chunk errors +var ( + ErrBuildErrorCaseHandle = errors.New("BuildErrorCause does not handle") +) // buildErrorCause delegates the building of a error cause from raw bytes to the correct structure func buildErrorCause(raw []byte) (errorCause, error) { @@ -32,13 +35,16 @@ func buildErrorCause(raw []byte) (errorCause, error) { e = &errorCauseUnrecognizedChunkType{} case protocolViolation: e = &errorCauseProtocolViolation{} + case userInitiatedAbort: + e = &errorCauseUserInitiatedAbort{} default: - return nil, fmt.Errorf("%w: %s", errBuildErrorCaseHandle, c.String()) + return nil, fmt.Errorf("%w: %s", ErrBuildErrorCaseHandle, c.String()) } if err := e.unmarshal(raw); err != nil { return nil, err } + return e, nil } diff --git a/vendor/github.com/pion/sctp/error_cause_protocol_violation.go b/vendor/github.com/pion/sctp/error_cause_protocol_violation.go index 3379ea24b..5434e0e3e 100644 --- a/vendor/github.com/pion/sctp/error_cause_protocol_violation.go +++ b/vendor/github.com/pion/sctp/error_cause_protocol_violation.go @@ -6,28 +6,31 @@ import ( ) /* - This error cause MAY be included in ABORT chunks that are sent - because an SCTP endpoint detects a protocol violation of the peer - that is not covered by the error causes described in Section 3.3.10.1 - to Section 3.3.10.12. An implementation MAY provide additional - information specifying what kind of protocol violation has been - detected. +This error cause MAY be included in ABORT chunks that are sent +because an SCTP endpoint detects a protocol violation of the peer +that is not covered by the error causes described in Section 3.3.10.1 +to Section 3.3.10.12. An implementation MAY provide additional +information specifying what kind of protocol violation has been +detected. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Cause Code=13 | Cause Length=Variable | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - / Additional Information / - \ \ - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Cause Code=13 | Cause Length=Variable | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + / Additional Information / + \ \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ type errorCauseProtocolViolation struct { errorCauseHeader additionalInformation []byte } -var errProtocolViolationUnmarshal = errors.New("unable to unmarshal Protocol Violation error") +// Abort chunk errors +var ( + ErrProtocolViolationUnmarshal = errors.New("unable to unmarshal Protocol Violation error") +) func (e *errorCauseProtocolViolation) marshal() ([]byte, error) { e.raw = e.additionalInformation @@ -37,7 +40,7 @@ func (e *errorCauseProtocolViolation) marshal() ([]byte, error) { func (e *errorCauseProtocolViolation) unmarshal(raw []byte) error { err := e.errorCauseHeader.unmarshal(raw) if err != nil { - return fmt.Errorf("%w: %v", errProtocolViolationUnmarshal, err) + return fmt.Errorf("%w: %v", ErrProtocolViolationUnmarshal, err) //nolint:errorlint } e.additionalInformation = e.raw diff --git a/vendor/github.com/pion/sctp/error_cause_user_initiated_abort.go b/vendor/github.com/pion/sctp/error_cause_user_initiated_abort.go new file mode 100644 index 000000000..1dee93733 --- /dev/null +++ b/vendor/github.com/pion/sctp/error_cause_user_initiated_abort.go @@ -0,0 +1,46 @@ +package sctp + +import ( + "fmt" +) + +/* +This error cause MAY be included in ABORT chunks that are sent +because of an upper-layer request. The upper layer can specify an +Upper Layer Abort Reason that is transported by SCTP transparently +and MAY be delivered to the upper-layer protocol at the peer. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Cause Code=12 | Cause Length=Variable | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + / Upper Layer Abort Reason / + \ \ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ +type errorCauseUserInitiatedAbort struct { + errorCauseHeader + upperLayerAbortReason []byte +} + +func (e *errorCauseUserInitiatedAbort) marshal() ([]byte, error) { + e.code = userInitiatedAbort + e.errorCauseHeader.raw = e.upperLayerAbortReason + return e.errorCauseHeader.marshal() +} + +func (e *errorCauseUserInitiatedAbort) unmarshal(raw []byte) error { + err := e.errorCauseHeader.unmarshal(raw) + if err != nil { + return err + } + + e.upperLayerAbortReason = e.errorCauseHeader.raw + return nil +} + +// String makes errorCauseUserInitiatedAbort printable +func (e *errorCauseUserInitiatedAbort) String() string { + return fmt.Sprintf("%s: %s", e.errorCauseHeader.String(), e.upperLayerAbortReason) +} diff --git a/vendor/github.com/pion/sctp/packet.go b/vendor/github.com/pion/sctp/packet.go index f1765adb0..7bc22c82f 100644 --- a/vendor/github.com/pion/sctp/packet.go +++ b/vendor/github.com/pion/sctp/packet.go @@ -19,34 +19,29 @@ Packet represents an SCTP packet, defined in https://tools.ietf.org/html/rfc4960 An SCTP packet is composed of a common header and chunks. A chunk contains either control information or user data. + SCTP Packet Format + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Common Header | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Chunk #1 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Chunk #n | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - SCTP Packet Format - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Common Header | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Chunk #1 | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| ... | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Chunk #n | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - - SCTP Common Header Format - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Source Value Number | Destination Value Number | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Verification Tag | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Checksum | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - + SCTP Common Header Format + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Source Value Number | Destination Value Number | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Verification Tag | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Checksum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ type packet struct { sourcePort uint16 @@ -59,16 +54,17 @@ const ( packetHeaderSize = 12 ) +// SCTP packet errors var ( - errPacketRawTooSmall = errors.New("raw is smaller than the minimum length for a SCTP packet") - errParseSCTPChunkNotEnoughData = errors.New("unable to parse SCTP chunk, not enough data for complete header") - errUnmarshalUnknownChunkType = errors.New("failed to unmarshal, contains unknown chunk type") - errChecksumMismatch = errors.New("checksum mismatch theirs") + ErrPacketRawTooSmall = errors.New("raw is smaller than the minimum length for a SCTP packet") + ErrParseSCTPChunkNotEnoughData = errors.New("unable to parse SCTP chunk, not enough data for complete header") + ErrUnmarshalUnknownChunkType = errors.New("failed to unmarshal, contains unknown chunk type") + ErrChecksumMismatch = errors.New("checksum mismatch theirs") ) func (p *packet) unmarshal(raw []byte) error { if len(raw) < packetHeaderSize { - return fmt.Errorf("%w: raw only %d bytes, %d is the minimum length", errPacketRawTooSmall, len(raw), packetHeaderSize) + return fmt.Errorf("%w: raw only %d bytes, %d is the minimum length", ErrPacketRawTooSmall, len(raw), packetHeaderSize) } p.sourcePort = binary.BigEndian.Uint16(raw[0:]) @@ -81,7 +77,7 @@ func (p *packet) unmarshal(raw []byte) error { if offset == len(raw) { break } else if offset+chunkHeaderSize > len(raw) { - return fmt.Errorf("%w: offset %d remaining %d", errParseSCTPChunkNotEnoughData, offset, len(raw)) + return fmt.Errorf("%w: offset %d remaining %d", ErrParseSCTPChunkNotEnoughData, offset, len(raw)) } var c chunk @@ -115,7 +111,7 @@ func (p *packet) unmarshal(raw []byte) error { case ctShutdownComplete: c = &chunkShutdownComplete{} default: - return fmt.Errorf("%w: %s", errUnmarshalUnknownChunkType, chunkType(raw[offset]).String()) + return fmt.Errorf("%w: %s", ErrUnmarshalUnknownChunkType, chunkType(raw[offset]).String()) } if err := c.unmarshal(raw[offset:]); err != nil { @@ -129,7 +125,7 @@ func (p *packet) unmarshal(raw []byte) error { theirChecksum := binary.LittleEndian.Uint32(raw[8:]) ourChecksum := generatePacketChecksum(raw) if theirChecksum != ourChecksum { - return fmt.Errorf("%w: %d ours: %d", errChecksumMismatch, theirChecksum, ourChecksum) + return fmt.Errorf("%w: %d ours: %d", ErrChecksumMismatch, theirChecksum, ourChecksum) } return nil } diff --git a/vendor/github.com/pion/sctp/param.go b/vendor/github.com/pion/sctp/param.go index b7887c5bd..a23935341 100644 --- a/vendor/github.com/pion/sctp/param.go +++ b/vendor/github.com/pion/sctp/param.go @@ -10,7 +10,8 @@ type param interface { length() int } -var errParamTypeUnhandled = errors.New("unhandled ParamType") +// ErrParamTypeUnhandled is returned if unknown parameter type is specified. +var ErrParamTypeUnhandled = errors.New("unhandled ParamType") func buildParam(t paramType, rawParam []byte) (param, error) { switch t { @@ -35,6 +36,6 @@ func buildParam(t paramType, rawParam []byte) (param, error) { case reconfigResp: return (¶mReconfigResponse{}).unmarshal(rawParam) default: - return nil, fmt.Errorf("%w: %v", errParamTypeUnhandled, t) + return nil, fmt.Errorf("%w: %v", ErrParamTypeUnhandled, t) } } diff --git a/vendor/github.com/pion/sctp/param_outgoing_reset_request.go b/vendor/github.com/pion/sctp/param_outgoing_reset_request.go index ceae17892..8ec0bf6cc 100644 --- a/vendor/github.com/pion/sctp/param_outgoing_reset_request.go +++ b/vendor/github.com/pion/sctp/param_outgoing_reset_request.go @@ -52,7 +52,10 @@ type paramOutgoingResetRequest struct { streamIdentifiers []uint16 } -var errSSNResetRequestParamTooShort = errors.New("outgoing SSN reset request parameter too short") +// Outgoing reset request parameter errors +var ( + ErrSSNResetRequestParamTooShort = errors.New("outgoing SSN reset request parameter too short") +) func (r *paramOutgoingResetRequest) marshal() ([]byte, error) { r.typ = outSSNResetReq @@ -72,7 +75,7 @@ func (r *paramOutgoingResetRequest) unmarshal(raw []byte) (param, error) { return nil, err } if len(r.raw) < paramOutgoingResetRequestStreamIdentifiersOffset { - return nil, errSSNResetRequestParamTooShort + return nil, ErrSSNResetRequestParamTooShort } r.reconfigRequestSequenceNumber = binary.BigEndian.Uint32(r.raw) r.reconfigResponseSequenceNumber = binary.BigEndian.Uint32(r.raw[4:]) diff --git a/vendor/github.com/pion/sctp/param_reconfig_response.go b/vendor/github.com/pion/sctp/param_reconfig_response.go index d9eab5512..e3bfab5a4 100644 --- a/vendor/github.com/pion/sctp/param_reconfig_response.go +++ b/vendor/github.com/pion/sctp/param_reconfig_response.go @@ -45,7 +45,10 @@ const ( reconfigResultInProgress reconfigResult = 6 ) -var errReconfigRespParamTooShort = errors.New("reconfig response parameter too short") +// Reconfiguration response errors +var ( + ErrReconfigRespParamTooShort = errors.New("reconfig response parameter too short") +) func (t reconfigResult) String() string { switch t { @@ -83,7 +86,7 @@ func (r *paramReconfigResponse) unmarshal(raw []byte) (param, error) { return nil, err } if len(r.raw) < 8 { - return nil, errReconfigRespParamTooShort + return nil, ErrReconfigRespParamTooShort } r.reconfigResponseSequenceNumber = binary.BigEndian.Uint32(r.raw) r.result = reconfigResult(binary.BigEndian.Uint32(r.raw[4:])) diff --git a/vendor/github.com/pion/sctp/param_requested_hmac_algorithm.go b/vendor/github.com/pion/sctp/param_requested_hmac_algorithm.go index 895f3d75a..d546af88a 100644 --- a/vendor/github.com/pion/sctp/param_requested_hmac_algorithm.go +++ b/vendor/github.com/pion/sctp/param_requested_hmac_algorithm.go @@ -15,7 +15,8 @@ const ( hmacSHA256 hmacAlgorithm = 3 ) -var errInvalidAlgorithmType = errors.New("invalid algorithm type") +// ErrInvalidAlgorithmType is returned if unknown auth algorithm is specified. +var ErrInvalidAlgorithmType = errors.New("invalid algorithm type") func (c hmacAlgorithm) String() string { switch c { @@ -64,7 +65,7 @@ func (r *paramRequestedHMACAlgorithm) unmarshal(raw []byte) (param, error) { case hmacSHA256: r.availableAlgorithms = append(r.availableAlgorithms, a) default: - return nil, fmt.Errorf("%w: %v", errInvalidAlgorithmType, a) + return nil, fmt.Errorf("%w: %v", ErrInvalidAlgorithmType, a) } i += 2 diff --git a/vendor/github.com/pion/sctp/paramheader.go b/vendor/github.com/pion/sctp/paramheader.go index f814d461f..f3cbf74ca 100644 --- a/vendor/github.com/pion/sctp/paramheader.go +++ b/vendor/github.com/pion/sctp/paramheader.go @@ -17,11 +17,12 @@ const ( paramHeaderLength = 4 ) +// Parameter header parse errors var ( - errParamHeaderTooShort = errors.New("param header too short") - errParamHeaderSelfReportedLengthShorter = errors.New("param self reported length is shorter than header length") - errParamHeaderSelfReportedLengthLonger = errors.New("param self reported length is longer than header length") - errParamHeaderParseFailed = errors.New("failed to parse param type") + ErrParamHeaderTooShort = errors.New("param header too short") + ErrParamHeaderSelfReportedLengthShorter = errors.New("param self reported length is shorter than header length") + ErrParamHeaderSelfReportedLengthLonger = errors.New("param self reported length is longer than header length") + ErrParamHeaderParseFailed = errors.New("failed to parse param type") ) func (p *paramHeader) marshal() ([]byte, error) { @@ -37,20 +38,20 @@ func (p *paramHeader) marshal() ([]byte, error) { func (p *paramHeader) unmarshal(raw []byte) error { if len(raw) < paramHeaderLength { - return errParamHeaderTooShort + return ErrParamHeaderTooShort } paramLengthPlusHeader := binary.BigEndian.Uint16(raw[2:]) if int(paramLengthPlusHeader) < paramHeaderLength { - return fmt.Errorf("%w: param self reported length (%d) shorter than header length (%d)", errParamHeaderSelfReportedLengthShorter, int(paramLengthPlusHeader), paramHeaderLength) + return fmt.Errorf("%w: param self reported length (%d) shorter than header length (%d)", ErrParamHeaderSelfReportedLengthShorter, int(paramLengthPlusHeader), paramHeaderLength) } if len(raw) < int(paramLengthPlusHeader) { - return fmt.Errorf("%w: param length (%d) shorter than its self reported length (%d)", errParamHeaderSelfReportedLengthLonger, len(raw), int(paramLengthPlusHeader)) + return fmt.Errorf("%w: param length (%d) shorter than its self reported length (%d)", ErrParamHeaderSelfReportedLengthLonger, len(raw), int(paramLengthPlusHeader)) } typ, err := parseParamType(raw[0:]) if err != nil { - return fmt.Errorf("%w: %v", errParamHeaderParseFailed, err) + return fmt.Errorf("%w: %v", ErrParamHeaderParseFailed, err) //nolint:errorlint } p.typ = typ p.raw = raw[paramHeaderLength:paramLengthPlusHeader] diff --git a/vendor/github.com/pion/sctp/paramtype.go b/vendor/github.com/pion/sctp/paramtype.go index 9fe2cf14c..2db73ddec 100644 --- a/vendor/github.com/pion/sctp/paramtype.go +++ b/vendor/github.com/pion/sctp/paramtype.go @@ -39,11 +39,14 @@ const ( adaptLayerInd paramType = 49158 // Adaptation Layer Indication (0xC006) [RFC5061] ) -var errParamPacketTooShort = errors.New("packet to short") +// Parameter packet errors +var ( + ErrParamPacketTooShort = errors.New("packet to short") +) func parseParamType(raw []byte) (paramType, error) { if len(raw) < 2 { - return paramType(0), errParamPacketTooShort + return paramType(0), ErrParamPacketTooShort } return paramType(binary.BigEndian.Uint16(raw)), nil } diff --git a/vendor/github.com/pion/sctp/pending_queue.go b/vendor/github.com/pion/sctp/pending_queue.go index a6e1a7a55..8082cf52f 100644 --- a/vendor/github.com/pion/sctp/pending_queue.go +++ b/vendor/github.com/pion/sctp/pending_queue.go @@ -48,10 +48,11 @@ type pendingQueue struct { unorderedIsSelected bool } +// Pending queue errors var ( - errUnexpectedChuckPoppedUnordered = errors.New("unexpected chunk popped (unordered)") - errUnexpectedChuckPoppedOrdered = errors.New("unexpected chunk popped (ordered)") - errUnexpectedQState = errors.New("unexpected q state (should've been selected)") + ErrUnexpectedChuckPoppedUnordered = errors.New("unexpected chunk popped (unordered)") + ErrUnexpectedChuckPoppedOrdered = errors.New("unexpected chunk popped (ordered)") + ErrUnexpectedQState = errors.New("unexpected q state (should've been selected)") ) func newPendingQueue() *pendingQueue { @@ -90,12 +91,12 @@ func (q *pendingQueue) pop(c *chunkPayloadData) error { if q.unorderedIsSelected { popped = q.unorderedQueue.pop() if popped != c { - return errUnexpectedChuckPoppedUnordered + return ErrUnexpectedChuckPoppedUnordered } } else { popped = q.orderedQueue.pop() if popped != c { - return errUnexpectedChuckPoppedOrdered + return ErrUnexpectedChuckPoppedOrdered } } if popped.endingFragment { @@ -103,12 +104,12 @@ func (q *pendingQueue) pop(c *chunkPayloadData) error { } } else { if !c.beginningFragment { - return errUnexpectedQState + return ErrUnexpectedQState } if c.unordered { popped := q.unorderedQueue.pop() if popped != c { - return errUnexpectedChuckPoppedUnordered + return ErrUnexpectedChuckPoppedUnordered } if !popped.endingFragment { q.selected = true @@ -117,7 +118,7 @@ func (q *pendingQueue) pop(c *chunkPayloadData) error { } else { popped := q.orderedQueue.pop() if popped != c { - return errUnexpectedChuckPoppedOrdered + return ErrUnexpectedChuckPoppedOrdered } if !popped.endingFragment { q.selected = true diff --git a/vendor/github.com/pion/sctp/renovate.json b/vendor/github.com/pion/sctp/renovate.json index f1614058a..f1bb98c6a 100644 --- a/vendor/github.com/pion/sctp/renovate.json +++ b/vendor/github.com/pion/sctp/renovate.json @@ -1,27 +1,6 @@ { + "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:base", - ":disableDependencyDashboard" - ], - "postUpdateOptions": [ - "gomodTidy" - ], - "commitBody": "Generated by renovateBot", - "packageRules": [ - { - "matchUpdateTypes": ["minor", "patch", "pin", "digest"], - "automerge": true - }, - { - "packagePatterns": ["^golang.org/x/"], - "schedule": ["on the first day of the month"] - } - ], - "ignorePaths": [ - ".github/workflows/generate-authors.yml", - ".github/workflows/lint.yaml", - ".github/workflows/renovate-go-mod-fix.yaml", - ".github/workflows/test.yaml", - ".github/workflows/tidy-check.yaml" + "github>pion/renovate-config" ] } diff --git a/vendor/github.com/pion/sctp/stream.go b/vendor/github.com/pion/sctp/stream.go index 58041b1f4..43038ee1e 100644 --- a/vendor/github.com/pion/sctp/stream.go +++ b/vendor/github.com/pion/sctp/stream.go @@ -5,8 +5,10 @@ import ( "fmt" "io" "math" + "os" "sync" "sync/atomic" + "time" "github.com/pion/logging" ) @@ -20,9 +22,34 @@ const ( ReliabilityTypeTimed byte = 2 ) +// StreamState is an enum for SCTP Stream state field +// This field identifies the state of stream. +type StreamState int + +// StreamState enums +const ( + StreamStateOpen StreamState = iota // Stream object starts with StreamStateOpen + StreamStateClosing // Outgoing stream is being reset + StreamStateClosed // Stream has been closed +) + +func (ss StreamState) String() string { + switch ss { + case StreamStateOpen: + return "open" + case StreamStateClosing: + return "closing" + case StreamStateClosed: + return "closed" + } + return "unknown" +} + +// SCTP stream errors var ( - errOutboundPacketTooLarge = errors.New("outbound packet larger than maximum message size") - errStreamClosed = errors.New("Stream closed") + ErrOutboundPacketTooLarge = errors.New("outbound packet larger than maximum message size") + ErrStreamClosed = errors.New("stream closed") + ErrReadDeadlineExceeded = fmt.Errorf("read deadline exceeded: %w", os.ErrDeadlineExceeded) ) // Stream represents an SCTP stream @@ -35,13 +62,14 @@ type Stream struct { sequenceNumber uint16 readNotifier *sync.Cond readErr error - writeErr error + readTimeoutCancel chan struct{} unordered bool reliabilityType byte reliabilityValue uint32 bufferedAmount uint64 bufferedAmountLow uint64 onBufferedAmountLow func() + state StreamState log logging.LeveledLogger name string } @@ -92,6 +120,14 @@ func (s *Stream) ReadSCTP(p []byte) (int, PayloadProtocolIdentifier, error) { s.lock.Lock() defer s.lock.Unlock() + defer func() { + // close readTimeoutCancel if the current read timeout routine is no longer effective + if s.readTimeoutCancel != nil && s.readErr != nil { + close(s.readTimeoutCancel) + s.readTimeoutCancel = nil + } + }() + for { n, ppi, err := s.reassemblyQueue.read(p) if err == nil { @@ -109,6 +145,47 @@ func (s *Stream) ReadSCTP(p []byte) (int, PayloadProtocolIdentifier, error) { } } +// SetReadDeadline sets the read deadline in an identical way to net.Conn +func (s *Stream) SetReadDeadline(deadline time.Time) error { + s.lock.Lock() + defer s.lock.Unlock() + + if s.readTimeoutCancel != nil { + close(s.readTimeoutCancel) + s.readTimeoutCancel = nil + } + + if s.readErr != nil { + if !errors.Is(s.readErr, ErrReadDeadlineExceeded) { + return nil + } + s.readErr = nil + } + + if !deadline.IsZero() { + s.readTimeoutCancel = make(chan struct{}) + + go func(readTimeoutCancel chan struct{}) { + t := time.NewTimer(time.Until(deadline)) + select { + case <-readTimeoutCancel: + t.Stop() + return + case <-t.C: + s.lock.Lock() + if s.readErr == nil { + s.readErr = ErrReadDeadlineExceeded + } + s.readTimeoutCancel = nil + s.lock.Unlock() + + s.readNotifier.Signal() + } + }(s.readTimeoutCancel) + } + return nil +} + func (s *Stream) handleData(pd *chunkPayloadData) { s.lock.Lock() defer s.lock.Unlock() @@ -178,32 +255,23 @@ func (s *Stream) Write(p []byte) (n int, err error) { } // WriteSCTP writes len(p) bytes from p to the DTLS connection -func (s *Stream) WriteSCTP(p []byte, ppi PayloadProtocolIdentifier) (n int, err error) { +func (s *Stream) WriteSCTP(p []byte, ppi PayloadProtocolIdentifier) (int, error) { maxMessageSize := s.association.MaxMessageSize() if len(p) > int(maxMessageSize) { - return 0, fmt.Errorf("%w: %v", errOutboundPacketTooLarge, math.MaxUint16) + return 0, fmt.Errorf("%w: %v", ErrOutboundPacketTooLarge, math.MaxUint16) } - switch s.association.getState() { - case shutdownSent, shutdownAckSent, shutdownPending, shutdownReceived: - s.lock.Lock() - if s.writeErr == nil { - s.writeErr = errStreamClosed - } - s.lock.Unlock() - default: - } - - s.lock.RLock() - err = s.writeErr - s.lock.RUnlock() - if err != nil { - return 0, err + if s.State() != StreamStateOpen { + return 0, ErrStreamClosed } chunks := s.packetize(p, ppi) - - return len(p), s.association.sendPayloadData(chunks) + n := len(p) + err := s.association.sendPayloadData(chunks) + if err != nil { + return n, ErrStreamClosed + } + return n, nil } func (s *Stream) packetize(raw []byte, ppi PayloadProtocolIdentifier) []*chunkPayloadData { @@ -267,26 +335,23 @@ func (s *Stream) packetize(raw []byte, ppi PayloadProtocolIdentifier) []*chunkPa // Close closes the write-direction of the stream. // Future calls to Write are not permitted after calling Close. func (s *Stream) Close() error { - if sid, isOpen := func() (uint16, bool) { + if sid, resetOutbound := func() (uint16, bool) { s.lock.Lock() defer s.lock.Unlock() - isOpen := true - if s.writeErr == nil { - s.writeErr = errStreamClosed - } else { - isOpen = false - } + s.log.Debugf("[%s] Close: state=%s", s.name, s.state.String()) - if s.readErr == nil { - s.readErr = io.EOF - } else { - isOpen = false + if s.state == StreamStateOpen { + if s.readErr == nil { + s.state = StreamStateClosing + } else { + s.state = StreamStateClosed + } + s.log.Debugf("[%s] state change: open => %s", s.name, s.state.String()) + return s.streamIdentifier, true } - s.readNotifier.Broadcast() // broadcast regardless - - return s.streamIdentifier, isOpen - }(); isOpen { + return s.streamIdentifier, false + }(); resetOutbound { // Reset the outgoing stream // https://tools.ietf.org/html/rfc6525 return s.association.sendResetRequest(sid) @@ -365,3 +430,35 @@ func (s *Stream) getNumBytesInReassemblyQueue() int { // No lock is required as it reads the size with atomic load function. return s.reassemblyQueue.getNumBytes() } + +func (s *Stream) onInboundStreamReset() { + s.lock.Lock() + defer s.lock.Unlock() + + s.log.Debugf("[%s] onInboundStreamReset: state=%s", s.name, s.state.String()) + + // No more inbound data to read. Unblock the read with io.EOF. + // This should cause DCEP layer (datachannel package) to call Close() which + // will reset outgoing stream also. + + // See RFC 8831 section 6.7: + // if one side decides to close the data channel, it resets the corresponding + // outgoing stream. When the peer sees that an incoming stream was + // reset, it also resets its corresponding outgoing stream. Once this + // is completed, the data channel is closed. + + s.readErr = io.EOF + s.readNotifier.Broadcast() + + if s.state == StreamStateClosing { + s.log.Debugf("[%s] state change: closing => closed", s.name) + s.state = StreamStateClosed + } +} + +// State return the stream state. +func (s *Stream) State() StreamState { + s.lock.RLock() + defer s.lock.RUnlock() + return s.state +} diff --git a/vendor/github.com/pion/sctp/util.go b/vendor/github.com/pion/sctp/util.go index e2e54ab0c..9302e7126 100644 --- a/vendor/github.com/pion/sctp/util.go +++ b/vendor/github.com/pion/sctp/util.go @@ -4,8 +4,8 @@ const ( paddingMultiple = 4 ) -func getPadding(len int) int { - return (paddingMultiple - (len % paddingMultiple)) % paddingMultiple +func getPadding(l int) int { + return (paddingMultiple - (l % paddingMultiple)) % paddingMultiple } func padByte(in []byte, cnt int) []byte { diff --git a/vendor/github.com/pion/sdp/v3/.gitignore b/vendor/github.com/pion/sdp/v3/.gitignore index 83db74ba5..f977e7485 100644 --- a/vendor/github.com/pion/sdp/v3/.gitignore +++ b/vendor/github.com/pion/sdp/v3/.gitignore @@ -22,3 +22,4 @@ cover.out *.wasm examples/sfu-ws/cert.pem examples/sfu-ws/key.pem +wasm_exec.js diff --git a/vendor/github.com/pion/sdp/v3/.golangci.yml b/vendor/github.com/pion/sdp/v3/.golangci.yml index d6162c970..d7a88eca3 100644 --- a/vendor/github.com/pion/sdp/v3/.golangci.yml +++ b/vendor/github.com/pion/sdp/v3/.golangci.yml @@ -15,14 +15,22 @@ linters-settings: linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully + - contextcheck # check the function whether use a non-inherited context - deadcode # Finds unused code + - decorder # check declaration order and count of types, constants, variables and functions - depguard # Go linter that checks if package imports are in a list of acceptable packages - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection + - durationcheck # check for two durations multiplied together - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. - exhaustive # check exhaustiveness of enum switch statements - exportloopref # checks for pointers to enclosing loop variables + - forcetypeassert # finds forced type assertions - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - gochecknoinits # Checks that no init functions are present in Go code @@ -35,40 +43,62 @@ linters: - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - gosimple # Linter for Go source code that specializes in simplifying a code - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - grouper # An analyzer to analyze expression groups. + - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used - misspell # Finds commonly misspelled English words in comments - nakedret # Finds naked returns in functions greater than a specified function length + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. - noctx # noctx finds sending http request without context.Context - - scopelint # Scopelint checks for unpinned variables in go programs + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # golint replacement, finds style mistakes - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - structcheck # Finds unused struct fields - stylecheck # Stylecheck is a replacement for golint + - tagliatelle # Checks the struct tags. + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types - varcheck # Finds unused global variables and constants + - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: + - containedctx # containedctx is a linter that detects struct contained context.Context field + - cyclop # checks function and package cyclomatic complexity + - exhaustivestruct # Checks if all struct's fields are initialized + - forbidigo # Forbids identifiers - funlen # Tool for detection of long functions - gocyclo # Computes and checks the cyclomatic complexity of functions - godot # Check if comments end in a period - gomnd # An analyzer to detect magic numbers. + - ifshort # Checks that your code uses short syntax for if-statements whenever possible + - ireturn # Accept Interfaces, Return Concrete Types - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - nestif # Reports deeply nested if statements - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - nolintlint # Reports ill-formed or insufficient nolint directives + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - prealloc # Finds slice declarations that could potentially be preallocated + - promlinter # Check Prometheus metrics naming via promlint - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. - testpackage # linter that makes you use a separate _test package + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers + - varnamelen # checks that the length of a variable's name matches its scope + - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! issues: diff --git a/vendor/github.com/pion/sdp/v3/AUTHORS.txt b/vendor/github.com/pion/sdp/v3/AUTHORS.txt new file mode 100644 index 000000000..038e59911 --- /dev/null +++ b/vendor/github.com/pion/sdp/v3/AUTHORS.txt @@ -0,0 +1,32 @@ +# Thank you to everyone that made Pion possible. If you are interested in contributing +# we would love to have you https://github.com/pion/webrtc/wiki/Contributing +# +# This file is auto generated, using git to list all individuals contributors. +# see `.github/generate-authors.sh` for the scripting +adwpc +Atsushi Watanabe +backkem +Brendan Abolivier +chenkaiC4 +cnderrauber +Daniele Sluijters +Graham King +Guilherme +Hugo Arregui +Jason +Jerko Steiner +John Bradley +Konstantin Itskov +korymiller1489 +Luke S +Max Hawkins +Maxim Oransky +mchlrhw <4028654+mchlrhw@users.noreply.github.com> +Michael MacDonald +Mustafa Navruz +Roman Romanenko +Sean DuBois +Sean DuBois +tarrencev +Woodrow Douglass +ZHENK diff --git a/vendor/github.com/pion/sdp/v3/README.md b/vendor/github.com/pion/sdp/v3/README.md index e7d7ff692..c29c97f78 100644 --- a/vendor/github.com/pion/sdp/v3/README.md +++ b/vendor/github.com/pion/sdp/v3/README.md @@ -17,7 +17,6 @@


-See [DESIGN.md](DESIGN.md) for an overview of features and future goals. ### Roadmap The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones. @@ -32,28 +31,5 @@ If you need commercial support or don't want to use public methods you can conta ### Contributing Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible: -* [John Bradley](https://github.com/kc5nra) - *Original Author* -* [Sean DuBois](https://github.com/Sean-Der) - *Original Author* -* [Michiel De Backker](https://github.com/backkem) - *Public API, Initialization* -* [Konstantin Itskov](https://github.com/trivigy) - *Fix documentation* -* [chenkaiC4](https://github.com/chenkaiC4) - *Fix GolangCI Linter* -* [Woodrow Douglass](https://github.com/wdouglass) *RTCP, RTP improvements, G.722 support, Bugfixes* -* [Michael MacDonald](https://github.com/mjmac) -* [Max Hawkins](https://github.com/maxhawkins) -* [mchlrhw](https://github.com/mchlrhw) -* [Hugo Arregui](https://github.com/hugoArregui) -* [Guilherme Souza](https://github.com/gqgs) -* [adwpc](https://github.com/adwpc) - *extmap add transport-cc* -* [Atsushi Watanabe](https://github.com/at-wat) -* [Luke S](https://github.com/encounter) -* [Jerko Steiner](https://github.com/jeremija) -* [Roman Romanenko](https://github.com/r-novel) -* [Jason Brady](https://github.com/jbrady42) -* [Kory Miller](https://github.com/jbrady42/korymiller1489) -* [ZHENK](https://github.com/scorpionknifes) -* [Tarrence van As](https://github.com/tarrencev) -* [Maxim Oransky](https://github.com/sdfsdhgjkbmnmxc) -* [Graham King](https://github.com/grahamking/) - ### License MIT License - see [LICENSE](LICENSE) for full text diff --git a/vendor/github.com/pion/sdp/v3/base_lexer.go b/vendor/github.com/pion/sdp/v3/base_lexer.go index 45fe7994b..f1f2ccd1a 100644 --- a/vendor/github.com/pion/sdp/v3/base_lexer.go +++ b/vendor/github.com/pion/sdp/v3/base_lexer.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io" + "strconv" ) var errDocumentStart = errors.New("already on document start") @@ -17,8 +18,7 @@ func (e syntaxError) Error() string { if e.i < 0 { e.i = 0 } - head, middle, tail := e.s[:e.i], e.s[e.i:e.i+1], e.s[e.i+1:] - return fmt.Sprintf("%s --> %s <-- %s", head, middle, tail) + return fmt.Sprintf("sdp: syntax error at pos %d: %s", e.i, strconv.QuoteToASCII(e.s[e.i:e.i+1])) } type baseLexer struct { @@ -50,7 +50,7 @@ func (l *baseLexer) readByte() (byte, error) { func (l *baseLexer) nextLine() error { for { ch, err := l.readByte() - if err == io.EOF { + if errors.Is(err, io.EOF) { return nil } else if err != nil { return err @@ -64,7 +64,7 @@ func (l *baseLexer) nextLine() error { func (l *baseLexer) readWhitespace() error { for { ch, err := l.readByte() - if err == io.EOF { + if errors.Is(err, io.EOF) { return nil } else if err != nil { return err @@ -78,7 +78,7 @@ func (l *baseLexer) readWhitespace() error { func (l *baseLexer) readUint64Field() (i uint64, err error) { for { ch, err := l.readByte() - if err == io.EOF && i > 0 { + if errors.Is(err, io.EOF) && i > 0 { break } else if err != nil { return i, err @@ -130,11 +130,11 @@ func (l *baseLexer) readUint64Field() (i uint64, err error) { // Returns next field on this line or empty string if no more fields on line func (l *baseLexer) readField() (string, error) { start := l.pos - stop := start + var stop int for { stop = l.pos ch, err := l.readByte() - if err == io.EOF && stop > start { + if errors.Is(err, io.EOF) && stop > start { break } else if err != nil { return "", err diff --git a/vendor/github.com/pion/sdp/v3/jsep.go b/vendor/github.com/pion/sdp/v3/jsep.go index efdef0301..de07d8f30 100644 --- a/vendor/github.com/pion/sdp/v3/jsep.go +++ b/vendor/github.com/pion/sdp/v3/jsep.go @@ -9,24 +9,25 @@ import ( // Constants for SDP attributes used in JSEP const ( - AttrKeyCandidate = "candidate" - AttrKeyEndOfCandidates = "end-of-candidates" - AttrKeyIdentity = "identity" - AttrKeyGroup = "group" - AttrKeySSRC = "ssrc" - AttrKeySSRCGroup = "ssrc-group" - AttrKeyMsid = "msid" - AttrKeyMsidSemantic = "msid-semantic" - AttrKeyConnectionSetup = "setup" - AttrKeyMID = "mid" - AttrKeyICELite = "ice-lite" - AttrKeyRTCPMux = "rtcp-mux" - AttrKeyRTCPRsize = "rtcp-rsize" - AttrKeyInactive = "inactive" - AttrKeyRecvOnly = "recvonly" - AttrKeySendOnly = "sendonly" - AttrKeySendRecv = "sendrecv" - AttrKeyExtMap = "extmap" + AttrKeyCandidate = "candidate" + AttrKeyEndOfCandidates = "end-of-candidates" + AttrKeyIdentity = "identity" + AttrKeyGroup = "group" + AttrKeySSRC = "ssrc" + AttrKeySSRCGroup = "ssrc-group" + AttrKeyMsid = "msid" + AttrKeyMsidSemantic = "msid-semantic" + AttrKeyConnectionSetup = "setup" + AttrKeyMID = "mid" + AttrKeyICELite = "ice-lite" + AttrKeyRTCPMux = "rtcp-mux" + AttrKeyRTCPRsize = "rtcp-rsize" + AttrKeyInactive = "inactive" + AttrKeyRecvOnly = "recvonly" + AttrKeySendOnly = "sendonly" + AttrKeySendRecv = "sendrecv" + AttrKeyExtMap = "extmap" + AttrKeyExtMapAllowMixed = "extmap-allow-mixed" ) // Constants for semantic tokens used in JSEP diff --git a/vendor/github.com/pion/sdp/v3/renovate.json b/vendor/github.com/pion/sdp/v3/renovate.json index 4400fd9b2..f1614058a 100644 --- a/vendor/github.com/pion/sdp/v3/renovate.json +++ b/vendor/github.com/pion/sdp/v3/renovate.json @@ -1,15 +1,27 @@ { "extends": [ - "config:base" + "config:base", + ":disableDependencyDashboard" ], "postUpdateOptions": [ "gomodTidy" ], "commitBody": "Generated by renovateBot", "packageRules": [ + { + "matchUpdateTypes": ["minor", "patch", "pin", "digest"], + "automerge": true + }, { "packagePatterns": ["^golang.org/x/"], "schedule": ["on the first day of the month"] } + ], + "ignorePaths": [ + ".github/workflows/generate-authors.yml", + ".github/workflows/lint.yaml", + ".github/workflows/renovate-go-mod-fix.yaml", + ".github/workflows/test.yaml", + ".github/workflows/tidy-check.yaml" ] } diff --git a/vendor/github.com/pion/sdp/v3/unmarshal.go b/vendor/github.com/pion/sdp/v3/unmarshal.go index 92e0dcf48..59fcbe74c 100644 --- a/vendor/github.com/pion/sdp/v3/unmarshal.go +++ b/vendor/github.com/pion/sdp/v3/unmarshal.go @@ -568,9 +568,11 @@ func unmarshalBandwidth(value string) (*Bandwidth, error) { experimental := strings.HasPrefix(parts[0], "X-") if experimental { parts[0] = strings.TrimPrefix(parts[0], "X-") - } else if !anyOf(parts[0], "CT", "AS") { + } else if !anyOf(parts[0], "CT", "AS", "TIAS", "RS", "RR") { // Set according to currently registered with IANA // https://tools.ietf.org/html/rfc4566#section-5.8 + // https://tools.ietf.org/html/rfc3890#section-6.2 + // https://tools.ietf.org/html/rfc3556#section-2 return nil, fmt.Errorf("%w `%v`", errSDPInvalidValue, parts[0]) } @@ -767,8 +769,9 @@ func unmarshalMediaDescription(l *lexer) (stateFn, error) { // Set according to currently registered with IANA // https://tools.ietf.org/html/rfc4566#section-5.14 + // https://tools.ietf.org/html/rfc4975#section-8.1 for _, proto := range strings.Split(field, "/") { - if !anyOf(proto, "UDP", "RTP", "AVP", "SAVP", "SAVPF", "TLS", "DTLS", "SCTP", "AVPF") { + if !anyOf(proto, "UDP", "RTP", "AVP", "SAVP", "SAVPF", "TLS", "DTLS", "SCTP", "AVPF", "TCP", "MSRP") { return nil, fmt.Errorf("%w `%v`", errSDPInvalidNumericValue, field) } newMediaDesc.MediaName.Protos = append(newMediaDesc.MediaName.Protos, proto) diff --git a/vendor/github.com/pion/sdp/v3/util.go b/vendor/github.com/pion/sdp/v3/util.go index 74f47ed19..84cba7f7d 100644 --- a/vendor/github.com/pion/sdp/v3/util.go +++ b/vendor/github.com/pion/sdp/v3/util.go @@ -98,7 +98,7 @@ func parseRtpmap(rtpmap string) (Codec, error) { return codec, parsingFailed } - ptInt, err := strconv.Atoi(ptSplit[1]) + ptInt, err := strconv.ParseUint(ptSplit[1], 10, 8) if err != nil { return codec, parsingFailed } @@ -109,7 +109,7 @@ func parseRtpmap(rtpmap string) (Codec, error) { codec.Name = split[0] parts := len(split) if parts > 1 { - rate, err := strconv.Atoi(split[1]) + rate, err := strconv.ParseUint(split[1], 10, 32) if err != nil { return codec, parsingFailed } @@ -139,7 +139,7 @@ func parseFmtp(fmtp string) (Codec, error) { return codec, parsingFailed } - ptInt, err := strconv.Atoi(split[1]) + ptInt, err := strconv.ParseUint(split[1], 10, 8) if err != nil { return codec, parsingFailed } @@ -165,7 +165,7 @@ func parseRtcpFb(rtcpFb string) (Codec, error) { return codec, parsingFailed } - ptInt, err := strconv.Atoi(ptSplit[1]) + ptInt, err := strconv.ParseUint(ptSplit[1], 10, 8) if err != nil { return codec, parsingFailed } @@ -304,8 +304,8 @@ type keyToState func(key string) stateFn func (l *lexer) handleType(fn keyToState) (stateFn, error) { key, err := l.readType() - if err == io.EOF && key == "" { - return nil, nil + if errors.Is(err, io.EOF) && key == "" { + return nil, nil //nolint:nilnil } else if err != nil { return nil, err } diff --git a/vendor/github.com/pion/srtp/v2/.gitignore b/vendor/github.com/pion/srtp/v2/.gitignore index 83db74ba5..6e2f206a9 100644 --- a/vendor/github.com/pion/srtp/v2/.gitignore +++ b/vendor/github.com/pion/srtp/v2/.gitignore @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + ### JetBrains IDE ### ##################### .idea/ @@ -22,3 +25,4 @@ cover.out *.wasm examples/sfu-ws/cert.pem examples/sfu-ws/key.pem +wasm_exec.js diff --git a/vendor/github.com/pion/srtp/v2/.golangci.yml b/vendor/github.com/pion/srtp/v2/.golangci.yml index d6162c970..4e3eddf42 100644 --- a/vendor/github.com/pion/srtp/v2/.golangci.yml +++ b/vendor/github.com/pion/srtp/v2/.golangci.yml @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + linters-settings: govet: check-shadowing: true @@ -10,19 +13,34 @@ linters-settings: modules: - github.com/pkg/errors: recommendations: - - errors + - errors + forbidigo: + forbid: + - ^fmt.Print(f|ln)?$ + - ^log.(Panic|Fatal|Print)(f|ln)?$ + - ^os.Exit$ + - ^panic$ + - ^print(ln)?$ linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully - - deadcode # Finds unused code + - contextcheck # check the function whether use a non-inherited context + - decorder # check declaration order and count of types, constants, variables and functions - depguard # Go linter that checks if package imports are in a list of acceptable packages - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection + - durationcheck # check for two durations multiplied together - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. - exhaustive # check exhaustiveness of enum switch statements - exportloopref # checks for pointers to enclosing loop variables + - forbidigo # Forbids identifiers + - forcetypeassert # finds forced type assertions - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - gochecknoinits # Checks that no init functions are present in Go code @@ -35,40 +53,59 @@ linters: - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - gosimple # Linter for Go source code that specializes in simplifying a code - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - grouper # An analyzer to analyze expression groups. + - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used - misspell # Finds commonly misspelled English words in comments - nakedret # Finds naked returns in functions greater than a specified function length + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. - noctx # noctx finds sending http request without context.Context - - scopelint # Scopelint checks for unpinned variables in go programs + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # golint replacement, finds style mistakes - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - - structcheck # Finds unused struct fields - stylecheck # Stylecheck is a replacement for golint + - tagliatelle # Checks the struct tags. + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types - - varcheck # Finds unused global variables and constants + - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: + - containedctx # containedctx is a linter that detects struct contained context.Context field + - cyclop # checks function and package cyclomatic complexity + - exhaustivestruct # Checks if all struct's fields are initialized - funlen # Tool for detection of long functions - gocyclo # Computes and checks the cyclomatic complexity of functions - godot # Check if comments end in a period - gomnd # An analyzer to detect magic numbers. + - ifshort # Checks that your code uses short syntax for if-statements whenever possible + - ireturn # Accept Interfaces, Return Concrete Types - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - nestif # Reports deeply nested if statements - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - nolintlint # Reports ill-formed or insufficient nolint directives + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - prealloc # Finds slice declarations that could potentially be preallocated + - promlinter # Check Prometheus metrics naming via promlint - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. - testpackage # linter that makes you use a separate _test package + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers + - varnamelen # checks that the length of a variable's name matches its scope + - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! issues: @@ -78,12 +115,23 @@ issues: - path: _test\.go linters: - gocognit + - forbidigo # Allow complex main function in examples - path: examples text: "of func `main` is high" linters: - gocognit + + # Allow forbidden identifiers in examples + - path: examples + linters: + - forbidigo + + # Allow forbidden identifiers in CLI commands + - path: cmd + linters: + - forbidigo run: skip-dirs-use-default: false diff --git a/vendor/github.com/pion/srtp/v2/.goreleaser.yml b/vendor/github.com/pion/srtp/v2/.goreleaser.yml new file mode 100644 index 000000000..30093e9d6 --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/.goreleaser.yml @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + +builds: +- skip: true diff --git a/vendor/github.com/pion/srtp/v2/AUTHORS.txt b/vendor/github.com/pion/srtp/v2/AUTHORS.txt index 03ce53e92..daca101e1 100644 --- a/vendor/github.com/pion/srtp/v2/AUTHORS.txt +++ b/vendor/github.com/pion/srtp/v2/AUTHORS.txt @@ -2,7 +2,11 @@ # we would love to have you https://github.com/pion/webrtc/wiki/Contributing # # This file is auto generated, using git to list all individuals contributors. -# see `.github/generate-authors.sh` for the scripting +# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting +adamroach +Adrian Cable +Agniva De Sarker +Antoine Baché Atsushi Watanabe backkem chenkaiC4 @@ -17,8 +21,16 @@ Max Hawkins mission-liao Novel Corpse OrlandoCo +Patryk +Patryk Rogalski Sean DuBois Sean DuBois +SeongGyu Park +Steffen +Steffen Vogel Tobias Fridén Woodrow Douglass Yutaka Takeda + +# List of contributors not appearing in Git history + diff --git a/vendor/github.com/pion/srtp/v2/DESIGN.md b/vendor/github.com/pion/srtp/v2/DESIGN.md deleted file mode 100644 index 8742540df..000000000 --- a/vendor/github.com/pion/srtp/v2/DESIGN.md +++ /dev/null @@ -1,20 +0,0 @@ -

- Design -

- -### Portable -Pion SRTP is written in Go and extremely portable. Anywhere Golang runs, Pion SRTP should work as well! Instead of dealing with complicated -cross-compiling of multiple libraries, you now can run anywhere with one `go build` - -### Simple API -The API is based on an io.ReadWriteCloser. - -### Readable -If code comes from an RFC we try to make sure everything is commented with a link to the spec. -This makes learning and debugging easier, this library was written to also serve as a guide for others. - -### Tested -Every commit is tested via travis-ci Go provides fantastic facilities for testing, and more will be added as time goes on. - -### Shared libraries -Every pion product is built using shared libraries, allowing others to review and reuse our libraries. diff --git a/vendor/github.com/pion/srtp/v2/LICENSE b/vendor/github.com/pion/srtp/v2/LICENSE index ab602974d..491caf6b0 100644 --- a/vendor/github.com/pion/srtp/v2/LICENSE +++ b/vendor/github.com/pion/srtp/v2/LICENSE @@ -1,21 +1,9 @@ MIT License -Copyright (c) 2018 +Copyright (c) 2023 The Pion community -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: +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 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. +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/pion/srtp/v2/README.md b/vendor/github.com/pion/srtp/v2/README.md index 55685b767..d0e94e7a9 100644 --- a/vendor/github.com/pion/srtp/v2/README.md +++ b/vendor/github.com/pion/srtp/v2/README.md @@ -9,28 +9,27 @@ Sourcegraph Widget Slack Widget
- Build Status - GoDoc + GitHub Workflow Status + Go Reference Coverage Status Go Report Card License: MIT


-See [DESIGN.md](DESIGN.md) for an overview of features and future goals. - ### Roadmap The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones. ### Community -Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion). +Pion has an active community on the [Slack](https://pion.ly/slack). + +Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news. We are always looking to support **your projects**. Please reach out if you have something to build! - If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly) ### Contributing -Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible: +Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible: [AUTHORS.txt](./AUTHORS.txt) ### License MIT License - see [LICENSE](LICENSE) for full text diff --git a/vendor/github.com/pion/srtp/v2/codecov.yml b/vendor/github.com/pion/srtp/v2/codecov.yml index 085200a48..263e4d45c 100644 --- a/vendor/github.com/pion/srtp/v2/codecov.yml +++ b/vendor/github.com/pion/srtp/v2/codecov.yml @@ -3,6 +3,8 @@ # # It is automatically copied from https://github.com/pion/.goassets repository. # +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT coverage: status: diff --git a/vendor/github.com/pion/srtp/v2/context.go b/vendor/github.com/pion/srtp/v2/context.go index 3e778814a..27da02cfd 100644 --- a/vendor/github.com/pion/srtp/v2/context.go +++ b/vendor/github.com/pion/srtp/v2/context.go @@ -1,9 +1,12 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import ( "fmt" - "github.com/pion/transport/replaydetector" + "github.com/pion/transport/v2/replaydetector" ) const ( @@ -16,6 +19,7 @@ const ( labelSRTCPSalt = 0x05 maxSequenceNumber = 65535 + maxROC = (1 << 32) - 1 seqNumMedian = 1 << 15 seqNumMax = 1 << 16 @@ -26,8 +30,8 @@ const ( // Encrypt/Decrypt state for a single SRTP SSRC type srtpSSRCState struct { ssrc uint32 - index uint64 rolloverHasProcessed bool + index uint64 replayDetector replaydetector.ReplayDetector } @@ -41,6 +45,9 @@ type srtcpSSRCState struct { // Context represents a SRTP cryptographic context. // Context can only be used for one-way operations. // it must either used ONLY for encryption or ONLY for decryption. +// Note that Context does not provide any concurrency protection: +// access to a Context from multiple goroutines requires external +// synchronization. type Context struct { cipher srtpCipher @@ -57,8 +64,7 @@ type Context struct { // Passing multiple options which set the same parameter let the last one valid. // Following example create SRTP Context with replay protection with window size of 256. // -// decCtx, err := srtp.CreateContext(key, salt, profile, srtp.SRTPReplayProtection(256)) -// +// decCtx, err := srtp.CreateContext(key, salt, profile, srtp.SRTPReplayProtection(256)) func CreateContext(masterKey, masterSalt []byte, profile ProtectionProfile, opts ...ContextOption) (c *Context, err error) { keyLen, err := profile.keyLen() if err != nil { @@ -82,10 +88,10 @@ func CreateContext(masterKey, masterSalt []byte, profile ProtectionProfile, opts } switch profile { - case ProtectionProfileAeadAes128Gcm: - c.cipher, err = newSrtpCipherAeadAesGcm(masterKey, masterSalt) - case ProtectionProfileAes128CmHmacSha1_80: - c.cipher, err = newSrtpCipherAesCmHmacSha1(masterKey, masterSalt) + case ProtectionProfileAeadAes128Gcm, ProtectionProfileAeadAes256Gcm: + c.cipher, err = newSrtpCipherAeadAesGcm(profile, masterKey, masterSalt) + case ProtectionProfileAes128CmHmacSha1_32, ProtectionProfileAes128CmHmacSha1_80: + c.cipher, err = newSrtpCipherAesCmHmacSha1(profile, masterKey, masterSalt) default: return nil, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, profile) } @@ -109,13 +115,13 @@ func CreateContext(masterKey, masterSalt []byte, profile ProtectionProfile, opts } // https://tools.ietf.org/html/rfc3550#appendix-A.1 -func (s *srtpSSRCState) nextRolloverCount(sequenceNumber uint16) (uint32, func()) { +func (s *srtpSSRCState) nextRolloverCount(sequenceNumber uint16) (roc uint32, diff int32, overflow bool) { seq := int32(sequenceNumber) localRoc := uint32(s.index >> 16) localSeq := int32(s.index & (seqNumMax - 1)) guessRoc := localRoc - var difference int32 = 0 + var difference int32 if s.rolloverHasProcessed { // When localROC is equal to 0, and entering seq-localSeq > seqNumMedian @@ -144,15 +150,17 @@ func (s *srtpSSRCState) nextRolloverCount(sequenceNumber uint16) (uint32, func() } } - return guessRoc, func() { - if !s.rolloverHasProcessed { - s.index |= uint64(sequenceNumber) - s.rolloverHasProcessed = true - return - } - if difference > 0 { - s.index += uint64(difference) - } + return guessRoc, difference, (guessRoc == 0 && localRoc == maxROC) +} + +func (s *srtpSSRCState) updateRolloverCount(sequenceNumber uint16, difference int32) { + if !s.rolloverHasProcessed { + s.index |= uint64(sequenceNumber) + s.rolloverHasProcessed = true + return + } + if difference > 0 { + s.index += uint64(difference) } } @@ -196,7 +204,8 @@ func (c *Context) ROC(ssrc uint32) (uint32, bool) { // SetROC sets SRTP rollover counter value of specified SSRC. func (c *Context) SetROC(ssrc uint32, roc uint32) { s := c.getSRTPSSRCState(ssrc) - s.index = uint64(roc<<16) | (s.index & (seqNumMax - 1)) + s.index = uint64(roc) << 16 + s.rolloverHasProcessed = false } // Index returns SRTCP index value of specified SSRC. diff --git a/vendor/github.com/pion/srtp/v2/crypto.go b/vendor/github.com/pion/srtp/v2/crypto.go new file mode 100644 index 000000000..9696e8f2c --- /dev/null +++ b/vendor/github.com/pion/srtp/v2/crypto.go @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package srtp + +import ( + "crypto/cipher" + + "github.com/pion/transport/v2/utils/xor" +) + +// incrementCTR increments a big-endian integer of arbitrary size. +func incrementCTR(ctr []byte) { + for i := len(ctr) - 1; i >= 0; i-- { + ctr[i]++ + if ctr[i] != 0 { + break + } + } +} + +// xorBytesCTR performs CTR encryption and decryption. +// It is equivalent to cipher.NewCTR followed by XORKeyStream. +func xorBytesCTR(block cipher.Block, iv []byte, dst, src []byte) error { + if len(iv) != block.BlockSize() { + return errBadIVLength + } + + ctr := make([]byte, len(iv)) + copy(ctr, iv) + bs := block.BlockSize() + stream := make([]byte, bs) + + i := 0 + for i < len(src) { + block.Encrypt(stream, ctr) + incrementCTR(ctr) + n := xor.XorBytes(dst[i:], src[i:], stream) + if n == 0 { + break + } + i += n + } + return nil +} diff --git a/vendor/github.com/pion/srtp/v2/errors.go b/vendor/github.com/pion/srtp/v2/errors.go index a70262145..5b1751d30 100644 --- a/vendor/github.com/pion/srtp/v2/errors.go +++ b/vendor/github.com/pion/srtp/v2/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import ( @@ -18,6 +21,8 @@ var ( errTooShortRTCP = errors.New("packet is too short to be rtcp packet") errPayloadDiffers = errors.New("payload differs") errStartedChannelUsedIncorrectly = errors.New("started channel used incorrectly, should only be closed") + errBadIVLength = errors.New("bad iv length in xorBytesCTR") + errExceededMaxPackets = errors.New("exceeded the maximum number of packets") errStreamNotInited = errors.New("stream has not been inited, unable to close") errStreamAlreadyClosed = errors.New("stream is already closed") @@ -25,16 +30,16 @@ var ( errFailedTypeAssertion = errors.New("failed to cast child") ) -type errorDuplicated struct { +type duplicatedError struct { Proto string // srtp or srtcp SSRC uint32 Index uint32 // sequence number or index } -func (e *errorDuplicated) Error() string { +func (e *duplicatedError) Error() string { return fmt.Sprintf("%s ssrc=%d index=%d: %v", e.Proto, e.SSRC, e.Index, errDuplicated) } -func (e *errorDuplicated) Unwrap() error { +func (e *duplicatedError) Unwrap() error { return errDuplicated } diff --git a/vendor/github.com/pion/srtp/v2/key_derivation.go b/vendor/github.com/pion/srtp/v2/key_derivation.go index 5bbf3aaad..05f0f29fa 100644 --- a/vendor/github.com/pion/srtp/v2/key_derivation.go +++ b/vendor/github.com/pion/srtp/v2/key_derivation.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import ( @@ -19,7 +22,7 @@ func aesCmKeyDerivation(label byte, masterKey, masterSalt []byte, indexOverKdr i nMasterKey := len(masterKey) nMasterSalt := len(masterSalt) - prfIn := make([]byte, nMasterKey) + prfIn := make([]byte, 16) copy(prfIn[:nMasterSalt], masterSalt) prfIn[7] ^= label @@ -32,8 +35,8 @@ func aesCmKeyDerivation(label byte, masterKey, masterSalt []byte, indexOverKdr i out := make([]byte, ((outLen+nMasterKey)/nMasterKey)*nMasterKey) var i uint16 - for n := 0; n < outLen; n += nMasterKey { - binary.BigEndian.PutUint16(prfIn[nMasterKey-2:], i) + for n := 0; n < outLen; n += block.BlockSize() { + binary.BigEndian.PutUint16(prfIn[len(prfIn)-2:], i) block.Encrypt(out[n:n+nMasterKey], prfIn) i++ } @@ -48,16 +51,19 @@ func aesCmKeyDerivation(label byte, masterKey, masterSalt []byte, indexOverKdr i // - passing through 65,535 // i = 2^16 * ROC + SEQ // IV = (salt*2 ^ 16) | (ssrc*2 ^ 64) | (i*2 ^ 16) -func generateCounter(sequenceNumber uint16, rolloverCounter uint32, ssrc uint32, sessionSalt []byte) []byte { - counter := make([]byte, 16) +func generateCounter(sequenceNumber uint16, rolloverCounter uint32, ssrc uint32, sessionSalt []byte) (counter [16]byte) { + copy(counter[:], sessionSalt) - binary.BigEndian.PutUint32(counter[4:], ssrc) - binary.BigEndian.PutUint32(counter[8:], rolloverCounter) - binary.BigEndian.PutUint32(counter[12:], uint32(sequenceNumber)<<16) - - for i := range sessionSalt { - counter[i] ^= sessionSalt[i] - } + counter[4] ^= byte(ssrc >> 24) + counter[5] ^= byte(ssrc >> 16) + counter[6] ^= byte(ssrc >> 8) + counter[7] ^= byte(ssrc) + counter[8] ^= byte(rolloverCounter >> 24) + counter[9] ^= byte(rolloverCounter >> 16) + counter[10] ^= byte(rolloverCounter >> 8) + counter[11] ^= byte(rolloverCounter) + counter[12] ^= byte(sequenceNumber >> 8) + counter[13] ^= byte(sequenceNumber) return counter } diff --git a/vendor/github.com/pion/srtp/v2/keying.go b/vendor/github.com/pion/srtp/v2/keying.go index 82fd4d900..c5977c398 100644 --- a/vendor/github.com/pion/srtp/v2/keying.go +++ b/vendor/github.com/pion/srtp/v2/keying.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp const labelExtractorDtlsSrtp = "EXTRACTOR-dtls_srtp" diff --git a/vendor/github.com/pion/srtp/v2/option.go b/vendor/github.com/pion/srtp/v2/option.go index d6159f13f..7e2618e38 100644 --- a/vendor/github.com/pion/srtp/v2/option.go +++ b/vendor/github.com/pion/srtp/v2/option.go @@ -1,17 +1,20 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import ( - "github.com/pion/transport/replaydetector" + "github.com/pion/transport/v2/replaydetector" ) // ContextOption represents option of Context using the functional options pattern. type ContextOption func(*Context) error // SRTPReplayProtection sets SRTP replay protection window size. -func SRTPReplayProtection(windowSize uint) ContextOption { // nolint:golint +func SRTPReplayProtection(windowSize uint) ContextOption { // nolint:revive return func(c *Context) error { c.newSRTPReplayDetector = func() replaydetector.ReplayDetector { - return replaydetector.WithWrap(windowSize, maxSequenceNumber) + return replaydetector.New(windowSize, maxROC<<16|maxSequenceNumber) } return nil } @@ -21,14 +24,14 @@ func SRTPReplayProtection(windowSize uint) ContextOption { // nolint:golint func SRTCPReplayProtection(windowSize uint) ContextOption { return func(c *Context) error { c.newSRTCPReplayDetector = func() replaydetector.ReplayDetector { - return replaydetector.WithWrap(windowSize, maxSRTCPIndex) + return replaydetector.New(windowSize, maxSRTCPIndex) } return nil } } // SRTPNoReplayProtection disables SRTP replay protection. -func SRTPNoReplayProtection() ContextOption { // nolint:golint +func SRTPNoReplayProtection() ContextOption { // nolint:revive return func(c *Context) error { c.newSRTPReplayDetector = func() replaydetector.ReplayDetector { return &nopReplayDetector{} diff --git a/vendor/github.com/pion/srtp/v2/protection_profile.go b/vendor/github.com/pion/srtp/v2/protection_profile.go index 94476ad54..c9b0fce3a 100644 --- a/vendor/github.com/pion/srtp/v2/protection_profile.go +++ b/vendor/github.com/pion/srtp/v2/protection_profile.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import "fmt" @@ -6,17 +9,20 @@ import "fmt" type ProtectionProfile uint16 // Supported protection profiles +// See https://www.iana.org/assignments/srtp-protection/srtp-protection.xhtml const ( ProtectionProfileAes128CmHmacSha1_80 ProtectionProfile = 0x0001 + ProtectionProfileAes128CmHmacSha1_32 ProtectionProfile = 0x0002 ProtectionProfileAeadAes128Gcm ProtectionProfile = 0x0007 + ProtectionProfileAeadAes256Gcm ProtectionProfile = 0x0008 ) func (p ProtectionProfile) keyLen() (int, error) { switch p { - case ProtectionProfileAes128CmHmacSha1_80: - fallthrough - case ProtectionProfileAeadAes128Gcm: + case ProtectionProfileAes128CmHmacSha1_32, ProtectionProfileAes128CmHmacSha1_80, ProtectionProfileAeadAes128Gcm: return 16, nil + case ProtectionProfileAeadAes256Gcm: + return 32, nil default: return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p) } @@ -24,21 +30,34 @@ func (p ProtectionProfile) keyLen() (int, error) { func (p ProtectionProfile) saltLen() (int, error) { switch p { - case ProtectionProfileAes128CmHmacSha1_80: + case ProtectionProfileAes128CmHmacSha1_32, ProtectionProfileAes128CmHmacSha1_80: return 14, nil - case ProtectionProfileAeadAes128Gcm: + case ProtectionProfileAeadAes128Gcm, ProtectionProfileAeadAes256Gcm: return 12, nil default: return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p) } } -func (p ProtectionProfile) authTagLen() (int, error) { +func (p ProtectionProfile) rtpAuthTagLen() (int, error) { switch p { case ProtectionProfileAes128CmHmacSha1_80: - return (&srtpCipherAesCmHmacSha1{}).authTagLen(), nil - case ProtectionProfileAeadAes128Gcm: - return (&srtpCipherAeadAesGcm{}).authTagLen(), nil + return 10, nil + case ProtectionProfileAes128CmHmacSha1_32: + return 4, nil + case ProtectionProfileAeadAes128Gcm, ProtectionProfileAeadAes256Gcm: + return 0, nil + default: + return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p) + } +} + +func (p ProtectionProfile) rtcpAuthTagLen() (int, error) { + switch p { + case ProtectionProfileAes128CmHmacSha1_32, ProtectionProfileAes128CmHmacSha1_80: + return 10, nil + case ProtectionProfileAeadAes128Gcm, ProtectionProfileAeadAes256Gcm: + return 0, nil default: return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p) } @@ -46,10 +65,10 @@ func (p ProtectionProfile) authTagLen() (int, error) { func (p ProtectionProfile) aeadAuthTagLen() (int, error) { switch p { - case ProtectionProfileAes128CmHmacSha1_80: - return (&srtpCipherAesCmHmacSha1{}).aeadAuthTagLen(), nil - case ProtectionProfileAeadAes128Gcm: - return (&srtpCipherAeadAesGcm{}).aeadAuthTagLen(), nil + case ProtectionProfileAes128CmHmacSha1_32, ProtectionProfileAes128CmHmacSha1_80: + return 0, nil + case ProtectionProfileAeadAes128Gcm, ProtectionProfileAeadAes256Gcm: + return 16, nil default: return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p) } @@ -57,9 +76,9 @@ func (p ProtectionProfile) aeadAuthTagLen() (int, error) { func (p ProtectionProfile) authKeyLen() (int, error) { switch p { - case ProtectionProfileAes128CmHmacSha1_80: + case ProtectionProfileAes128CmHmacSha1_32, ProtectionProfileAes128CmHmacSha1_80: return 20, nil - case ProtectionProfileAeadAes128Gcm: + case ProtectionProfileAeadAes128Gcm, ProtectionProfileAeadAes256Gcm: return 0, nil default: return 0, fmt.Errorf("%w: %#v", errNoSuchSRTPProfile, p) diff --git a/vendor/github.com/pion/srtp/v2/renovate.json b/vendor/github.com/pion/srtp/v2/renovate.json index 08c1e39d6..f1bb98c6a 100644 --- a/vendor/github.com/pion/srtp/v2/renovate.json +++ b/vendor/github.com/pion/srtp/v2/renovate.json @@ -1,26 +1,6 @@ { + "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:base" - ], - "postUpdateOptions": [ - "gomodTidy" - ], - "commitBody": "Generated by renovateBot", - "packageRules": [ - { - "matchUpdateTypes": ["minor", "patch", "pin", "digest"], - "automerge": true - }, - { - "packagePatterns": ["^golang.org/x/"], - "schedule": ["on the first day of the month"] - } - ], - "ignorePaths": [ - ".github/workflows/generate-authors.yml", - ".github/workflows/lint.yaml", - ".github/workflows/renovate-go-mod-fix.yaml", - ".github/workflows/test.yaml", - ".github/workflows/tidy-check.yaml" + "github>pion/renovate-config" ] } diff --git a/vendor/github.com/pion/srtp/v2/session.go b/vendor/github.com/pion/srtp/v2/session.go index 95520f782..2e1f4fe3f 100644 --- a/vendor/github.com/pion/srtp/v2/session.go +++ b/vendor/github.com/pion/srtp/v2/session.go @@ -1,12 +1,17 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import ( + "errors" "io" "net" "sync" + "time" "github.com/pion/logging" - "github.com/pion/transport/packetio" + "github.com/pion/transport/v2/packetio" ) type streamSession interface { @@ -20,7 +25,8 @@ type session struct { localContext, remoteContext *Context localOptions, remoteOptions []ContextOption - newStream chan readStream + newStream chan readStream + acceptStreamTimeout time.Time started chan interface{} closed chan interface{} @@ -40,10 +46,11 @@ type session struct { // or directly pass the keys themselves. // After a Config is passed to a session it must not be modified. type Config struct { - Keys SessionKeys - Profile ProtectionProfile - BufferFactory func(packetType packetio.BufferPacketType, ssrc uint32) io.ReadWriteCloser - LoggerFactory logging.LoggerFactory + Keys SessionKeys + Profile ProtectionProfile + BufferFactory func(packetType packetio.BufferPacketType, ssrc uint32) io.ReadWriteCloser + LoggerFactory logging.LoggerFactory + AcceptStreamTimeout time.Time // List of local/remote context options. // ReplayProtection is enabled on remote context by default. @@ -117,6 +124,10 @@ func (s *session) start(localMasterKey, localMasterSalt, remoteMasterKey, remote return err } + if err = s.nextConn.SetReadDeadline(s.acceptStreamTimeout); err != nil { + return err + } + go func() { defer func() { close(s.newStream) @@ -132,7 +143,7 @@ func (s *session) start(localMasterKey, localMasterSalt, remoteMasterKey, remote var i int i, err = s.nextConn.Read(b) if err != nil { - if err != io.EOF { + if !errors.Is(err, io.EOF) { s.log.Error(err.Error()) } return diff --git a/vendor/github.com/pion/srtp/v2/session_srtcp.go b/vendor/github.com/pion/srtp/v2/session_srtcp.go index a5fb656cc..13f1a9589 100644 --- a/vendor/github.com/pion/srtp/v2/session_srtcp.go +++ b/vendor/github.com/pion/srtp/v2/session_srtcp.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import ( @@ -46,15 +49,16 @@ func NewSessionSRTCP(conn net.Conn, config *Config) (*SessionSRTCP, error) { //n s := &SessionSRTCP{ session: session{ - nextConn: conn, - localOptions: localOpts, - remoteOptions: remoteOpts, - readStreams: map[uint32]readStream{}, - newStream: make(chan readStream), - started: make(chan interface{}), - closed: make(chan interface{}), - bufferFactory: config.BufferFactory, - log: loggerFactory.NewLogger("srtp"), + nextConn: conn, + localOptions: localOpts, + remoteOptions: remoteOpts, + readStreams: map[uint32]readStream{}, + newStream: make(chan readStream), + acceptStreamTimeout: config.AcceptStreamTimeout, + started: make(chan interface{}), + closed: make(chan interface{}), + bufferFactory: config.BufferFactory, + log: loggerFactory.NewLogger("srtp"), }, } s.writeStream = &WriteStreamSRTCP{s} @@ -114,8 +118,11 @@ func (s *SessionSRTCP) write(buf []byte) (int, error) { return 0, errStartedChannelUsedIncorrectly } + ibuf := bufferpool.Get() + defer bufferpool.Put(ibuf) + s.session.localContextMutex.Lock() - encrypted, err := s.localContext.EncryptRTCP(nil, buf, nil) + encrypted, err := s.localContext.EncryptRTCP(ibuf.([]byte), buf, nil) s.session.localContextMutex.Unlock() if err != nil { @@ -162,6 +169,9 @@ func (s *SessionSRTCP) decrypt(buf []byte) error { if r == nil { return nil // Session has been closed } else if isNew { + if !s.session.acceptStreamTimeout.IsZero() { + _ = s.session.nextConn.SetReadDeadline(time.Time{}) + } s.session.newStream <- r // Notify AcceptStream } diff --git a/vendor/github.com/pion/srtp/v2/session_srtp.go b/vendor/github.com/pion/srtp/v2/session_srtp.go index 071c465ed..e07cbe216 100644 --- a/vendor/github.com/pion/srtp/v2/session_srtp.go +++ b/vendor/github.com/pion/srtp/v2/session_srtp.go @@ -1,7 +1,11 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import ( "net" + "sync" "time" "github.com/pion/logging" @@ -46,15 +50,16 @@ func NewSessionSRTP(conn net.Conn, config *Config) (*SessionSRTP, error) { //nol s := &SessionSRTP{ session: session{ - nextConn: conn, - localOptions: localOpts, - remoteOptions: remoteOpts, - readStreams: map[uint32]readStream{}, - newStream: make(chan readStream), - started: make(chan interface{}), - closed: make(chan interface{}), - bufferFactory: config.BufferFactory, - log: loggerFactory.NewLogger("srtp"), + nextConn: conn, + localOptions: localOpts, + remoteOptions: remoteOpts, + readStreams: map[uint32]readStream{}, + newStream: make(chan readStream), + acceptStreamTimeout: config.AcceptStreamTimeout, + started: make(chan interface{}), + closed: make(chan interface{}), + bufferFactory: config.BufferFactory, + log: loggerFactory.NewLogger("srtp"), }, } s.writeStream = &WriteStreamSRTP{s} @@ -111,21 +116,41 @@ func (s *SessionSRTP) Close() error { func (s *SessionSRTP) write(b []byte) (int, error) { packet := &rtp.Packet{} - err := packet.Unmarshal(b) - if err != nil { - return 0, nil + if err := packet.Unmarshal(b); err != nil { + return 0, err } return s.writeRTP(&packet.Header, packet.Payload) } +// bufferpool is a global pool of buffers used for encrypted packets in +// writeRTP below. Since it's global, buffers can be shared between +// different sessions, which amortizes the cost of allocating the pool. +// +// 1472 is the maximum Ethernet UDP payload. We give ourselves 20 bytes +// of slack for any authentication tags, which is more than enough for +// either CTR or GCM. If the buffer is too small, no harm, it will just +// get expanded by growBuffer. +var bufferpool = sync.Pool{ // nolint:gochecknoglobals + New: func() interface{} { + return make([]byte, 1492) + }, +} + func (s *SessionSRTP) writeRTP(header *rtp.Header, payload []byte) (int, error) { if _, ok := <-s.session.started; ok { return 0, errStartedChannelUsedIncorrectly } + // encryptRTP will either return our buffer, or, if it is too + // small, allocate a new buffer itself. In either case, it is + // safe to put the buffer back into the pool, but only after + // nextConn.Write has returned. + ibuf := bufferpool.Get() + defer bufferpool.Put(ibuf) + s.session.localContextMutex.Lock() - encrypted, err := s.localContext.encryptRTP(nil, header, payload) + encrypted, err := s.localContext.encryptRTP(ibuf.([]byte), header, payload) s.session.localContextMutex.Unlock() if err != nil { @@ -150,6 +175,9 @@ func (s *SessionSRTP) decrypt(buf []byte) error { if r == nil { return nil // Session has been closed } else if isNew { + if !s.session.acceptStreamTimeout.IsZero() { + _ = s.session.nextConn.SetReadDeadline(time.Time{}) + } s.session.newStream <- r // Notify AcceptStream } diff --git a/vendor/github.com/pion/srtp/v2/srtcp.go b/vendor/github.com/pion/srtp/v2/srtcp.go index dbf512549..7fd0746bd 100644 --- a/vendor/github.com/pion/srtp/v2/srtcp.go +++ b/vendor/github.com/pion/srtp/v2/srtcp.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import ( @@ -11,9 +14,18 @@ const maxSRTCPIndex = 0x7FFFFFFF func (c *Context) decryptRTCP(dst, encrypted []byte) ([]byte, error) { out := allocateIfMismatch(dst, encrypted) - tailOffset := len(encrypted) - (c.cipher.authTagLen() + srtcpIndexSize) - if tailOffset < 0 { + authTagLen, err := c.cipher.rtcpAuthTagLen() + if err != nil { + return nil, err + } + aeadAuthTagLen, err := c.cipher.aeadAuthTagLen() + if err != nil { + return nil, err + } + tailOffset := len(encrypted) - (authTagLen + srtcpIndexSize) + + if tailOffset < aeadAuthTagLen { return nil, fmt.Errorf("%w: %d", errTooShortRTCP, len(encrypted)) } else if isEncrypted := encrypted[tailOffset] >> 7; isEncrypted == 0 { return out, nil @@ -25,10 +37,10 @@ func (c *Context) decryptRTCP(dst, encrypted []byte) ([]byte, error) { s := c.getSRTCPSSRCState(ssrc) markAsValid, ok := s.replayDetector.Check(uint64(index)) if !ok { - return nil, &errorDuplicated{Proto: "srtcp", SSRC: ssrc, Index: index} + return nil, &duplicatedError{Proto: "srtcp", SSRC: ssrc, Index: index} } - out, err := c.cipher.decryptRTCP(out, encrypted, index, ssrc) + out, err = c.cipher.decryptRTCP(out, encrypted, index, ssrc) if err != nil { return nil, err } @@ -54,11 +66,16 @@ func (c *Context) encryptRTCP(dst, decrypted []byte) ([]byte, error) { ssrc := binary.BigEndian.Uint32(decrypted[4:]) s := c.getSRTCPSSRCState(ssrc) + if s.srtcpIndex >= maxSRTCPIndex { + // ... when 2^48 SRTP packets or 2^31 SRTCP packets have been secured with the same key + // (whichever occurs before), the key management MUST be called to provide new master key(s) + // (previously stored and used keys MUST NOT be used again), or the session MUST be terminated. + // https://www.rfc-editor.org/rfc/rfc3711#section-9.2 + return nil, errExceededMaxPackets + } + // We roll over early because MSB is used for marking as encrypted s.srtcpIndex++ - if s.srtcpIndex > maxSRTCPIndex { - s.srtcpIndex = 0 - } return c.cipher.encryptRTCP(dst, decrypted, s.srtcpIndex, ssrc) } diff --git a/vendor/github.com/pion/srtp/v2/srtp.go b/vendor/github.com/pion/srtp/v2/srtp.go index 26283722d..42c71be01 100644 --- a/vendor/github.com/pion/srtp/v2/srtp.go +++ b/vendor/github.com/pion/srtp/v2/srtp.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package srtp implements Secure Real-time Transport Protocol package srtp @@ -8,23 +11,29 @@ import ( func (c *Context) decryptRTP(dst, ciphertext []byte, header *rtp.Header, headerLen int) ([]byte, error) { s := c.getSRTPSSRCState(header.SSRC) - markAsValid, ok := s.replayDetector.Check(uint64(header.SequenceNumber)) + roc, diff, _ := s.nextRolloverCount(header.SequenceNumber) + markAsValid, ok := s.replayDetector.Check( + (uint64(roc) << 16) | uint64(header.SequenceNumber), + ) if !ok { - return nil, &errorDuplicated{ + return nil, &duplicatedError{ Proto: "srtp", SSRC: header.SSRC, Index: uint32(header.SequenceNumber), } } - dst = growBufferSize(dst, len(ciphertext)-c.cipher.authTagLen()) - roc, updateROC := s.nextRolloverCount(header.SequenceNumber) + authTagLen, err := c.cipher.rtpAuthTagLen() + if err != nil { + return nil, err + } + dst = growBufferSize(dst, len(ciphertext)-authTagLen) - dst, err := c.cipher.decryptRTP(dst, ciphertext, header, headerLen, roc) + dst, err = c.cipher.decryptRTP(dst, ciphertext, header, headerLen, roc) if err != nil { return nil, err } markAsValid() - updateROC() + s.updateRolloverCount(header.SequenceNumber, diff) return dst, nil } @@ -63,8 +72,15 @@ func (c *Context) EncryptRTP(dst []byte, plaintext []byte, header *rtp.Header) ( // Similar to above but faster because it can avoid unmarshaling the header and marshaling the payload. func (c *Context) encryptRTP(dst []byte, header *rtp.Header, payload []byte) (ciphertext []byte, err error) { s := c.getSRTPSSRCState(header.SSRC) - roc, updateROC := s.nextRolloverCount(header.SequenceNumber) - updateROC() + roc, diff, ovf := s.nextRolloverCount(header.SequenceNumber) + if ovf { + // ... when 2^48 SRTP packets or 2^31 SRTCP packets have been secured with the same key + // (whichever occurs before), the key management MUST be called to provide new master key(s) + // (previously stored and used keys MUST NOT be used again), or the session MUST be terminated. + // https://www.rfc-editor.org/rfc/rfc3711#section-9.2 + return nil, errExceededMaxPackets + } + s.updateRolloverCount(header.SequenceNumber, diff) return c.cipher.encryptRTP(dst, header, payload, roc) } diff --git a/vendor/github.com/pion/srtp/v2/srtp_cipher.go b/vendor/github.com/pion/srtp/v2/srtp_cipher.go index 2cdf32587..db501472f 100644 --- a/vendor/github.com/pion/srtp/v2/srtp_cipher.go +++ b/vendor/github.com/pion/srtp/v2/srtp_cipher.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import "github.com/pion/rtp" @@ -7,10 +10,11 @@ import "github.com/pion/rtp" type srtpCipher interface { // authTagLen returns auth key length of the cipher. // See the note below. - authTagLen() int + rtpAuthTagLen() (int, error) + rtcpAuthTagLen() (int, error) // aeadAuthTagLen returns AEAD auth key length of the cipher. // See the note below. - aeadAuthTagLen() int + aeadAuthTagLen() (int, error) getRTCPIndex([]byte) uint32 encryptRTP([]byte, *rtp.Header, []byte, uint32) ([]byte, error) diff --git a/vendor/github.com/pion/srtp/v2/srtp_cipher_aead_aes_gcm.go b/vendor/github.com/pion/srtp/v2/srtp_cipher_aead_aes_gcm.go index ea819bf57..90643d92d 100644 --- a/vendor/github.com/pion/srtp/v2/srtp_cipher_aead_aes_gcm.go +++ b/vendor/github.com/pion/srtp/v2/srtp_cipher_aead_aes_gcm.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import ( @@ -13,13 +16,15 @@ const ( ) type srtpCipherAeadAesGcm struct { + ProtectionProfile + srtpCipher, srtcpCipher cipher.AEAD srtpSessionSalt, srtcpSessionSalt []byte } -func newSrtpCipherAeadAesGcm(masterKey, masterSalt []byte) (*srtpCipherAeadAesGcm, error) { - s := &srtpCipherAeadAesGcm{} +func newSrtpCipherAeadAesGcm(profile ProtectionProfile, masterKey, masterSalt []byte) (*srtpCipherAeadAesGcm, error) { + s := &srtpCipherAeadAesGcm{ProtectionProfile: profile} srtpSessionKey, err := aesCmKeyDerivation(labelSRTPEncryption, masterKey, masterSalt, 0, len(masterKey)) if err != nil { @@ -60,33 +65,31 @@ func newSrtpCipherAeadAesGcm(masterKey, masterSalt []byte) (*srtpCipherAeadAesGc return s, nil } -func (s *srtpCipherAeadAesGcm) authTagLen() int { - return 0 -} - -func (s *srtpCipherAeadAesGcm) aeadAuthTagLen() int { - return 16 -} - func (s *srtpCipherAeadAesGcm) encryptRTP(dst []byte, header *rtp.Header, payload []byte, roc uint32) (ciphertext []byte, err error) { // Grow the given buffer to fit the output. - dst = growBufferSize(dst, header.MarshalSize()+len(payload)+s.aeadAuthTagLen()) + authTagLen, err := s.aeadAuthTagLen() + if err != nil { + return nil, err + } + dst = growBufferSize(dst, header.MarshalSize()+len(payload)+authTagLen) - hdr, err := header.Marshal() + n, err := header.MarshalTo(dst) if err != nil { return nil, err } iv := s.rtpInitializationVector(header, roc) - nHdr := len(hdr) - s.srtpCipher.Seal(dst[nHdr:nHdr], iv, payload, hdr) - copy(dst[:nHdr], hdr) + s.srtpCipher.Seal(dst[n:n], iv[:], payload, dst[:n]) return dst, nil } func (s *srtpCipherAeadAesGcm) decryptRTP(dst, ciphertext []byte, header *rtp.Header, headerLen int, roc uint32) ([]byte, error) { // Grow the given buffer to fit the output. - nDst := len(ciphertext) - s.aeadAuthTagLen() + authTagLen, err := s.aeadAuthTagLen() + if err != nil { + return nil, err + } + nDst := len(ciphertext) - authTagLen if nDst < 0 { // Size of ciphertext is shorter than AEAD auth tag len. return nil, errFailedToVerifyAuthTag @@ -96,7 +99,7 @@ func (s *srtpCipherAeadAesGcm) decryptRTP(dst, ciphertext []byte, header *rtp.He iv := s.rtpInitializationVector(header, roc) if _, err := s.srtpCipher.Open( - dst[headerLen:headerLen], iv, ciphertext[headerLen:], ciphertext[:headerLen], + dst[headerLen:headerLen], iv[:], ciphertext[headerLen:], ciphertext[:headerLen], ); err != nil { return nil, err } @@ -106,14 +109,18 @@ func (s *srtpCipherAeadAesGcm) decryptRTP(dst, ciphertext []byte, header *rtp.He } func (s *srtpCipherAeadAesGcm) encryptRTCP(dst, decrypted []byte, srtcpIndex uint32, ssrc uint32) ([]byte, error) { - aadPos := len(decrypted) + s.aeadAuthTagLen() + authTagLen, err := s.aeadAuthTagLen() + if err != nil { + return nil, err + } + aadPos := len(decrypted) + authTagLen // Grow the given buffer to fit the output. dst = growBufferSize(dst, aadPos+srtcpIndexSize) iv := s.rtcpInitializationVector(srtcpIndex, ssrc) aad := s.rtcpAdditionalAuthenticatedData(decrypted, srtcpIndex) - s.srtcpCipher.Seal(dst[8:8], iv, decrypted[8:], aad) + s.srtcpCipher.Seal(dst[8:8], iv[:], decrypted[8:], aad[:]) copy(dst[:8], decrypted[:8]) copy(dst[aadPos:aadPos+4], aad[8:12]) @@ -123,7 +130,11 @@ func (s *srtpCipherAeadAesGcm) encryptRTCP(dst, decrypted []byte, srtcpIndex uin func (s *srtpCipherAeadAesGcm) decryptRTCP(dst, encrypted []byte, srtcpIndex, ssrc uint32) ([]byte, error) { aadPos := len(encrypted) - srtcpIndexSize // Grow the given buffer to fit the output. - nDst := aadPos - s.aeadAuthTagLen() + authTagLen, err := s.aeadAuthTagLen() + if err != nil { + return nil, err + } + nDst := aadPos - authTagLen if nDst < 0 { // Size of ciphertext is shorter than AEAD auth tag len. return nil, errFailedToVerifyAuthTag @@ -133,7 +144,7 @@ func (s *srtpCipherAeadAesGcm) decryptRTCP(dst, encrypted []byte, srtcpIndex, ss iv := s.rtcpInitializationVector(srtcpIndex, ssrc) aad := s.rtcpAdditionalAuthenticatedData(encrypted, srtcpIndex) - if _, err := s.srtcpCipher.Open(dst[8:8], iv, encrypted[8:aadPos], aad); err != nil { + if _, err := s.srtcpCipher.Open(dst[8:8], iv[:], encrypted[8:aadPos], aad[:]); err != nil { return nil, err } @@ -147,8 +158,8 @@ func (s *srtpCipherAeadAesGcm) decryptRTCP(dst, encrypted []byte, srtcpIndex, ss // value is then XORed to the 12-octet salt to form the 12-octet IV. // // https://tools.ietf.org/html/rfc7714#section-8.1 -func (s *srtpCipherAeadAesGcm) rtpInitializationVector(header *rtp.Header, roc uint32) []byte { - iv := make([]byte, 12) +func (s *srtpCipherAeadAesGcm) rtpInitializationVector(header *rtp.Header, roc uint32) [12]byte { + var iv [12]byte binary.BigEndian.PutUint32(iv[2:], header.SSRC) binary.BigEndian.PutUint32(iv[6:], roc) binary.BigEndian.PutUint16(iv[10:], header.SequenceNumber) @@ -166,8 +177,8 @@ func (s *srtpCipherAeadAesGcm) rtpInitializationVector(header *rtp.Header, roc u // form the 12-octet IV. // // https://tools.ietf.org/html/rfc7714#section-9.1 -func (s *srtpCipherAeadAesGcm) rtcpInitializationVector(srtcpIndex uint32, ssrc uint32) []byte { - iv := make([]byte, 12) +func (s *srtpCipherAeadAesGcm) rtcpInitializationVector(srtcpIndex uint32, ssrc uint32) [12]byte { + var iv [12]byte binary.BigEndian.PutUint32(iv[2:], ssrc) binary.BigEndian.PutUint32(iv[8:], srtcpIndex) @@ -183,10 +194,10 @@ func (s *srtpCipherAeadAesGcm) rtcpInitializationVector(srtcpIndex uint32, ssrc // "ESRTCP word" // // https://tools.ietf.org/html/rfc7714#section-17 -func (s *srtpCipherAeadAesGcm) rtcpAdditionalAuthenticatedData(rtcpPacket []byte, srtcpIndex uint32) []byte { - aad := make([]byte, 12) +func (s *srtpCipherAeadAesGcm) rtcpAdditionalAuthenticatedData(rtcpPacket []byte, srtcpIndex uint32) [12]byte { + var aad [12]byte - copy(aad, rtcpPacket[:8]) + copy(aad[:], rtcpPacket[:8]) binary.BigEndian.PutUint32(aad[8:], srtcpIndex) aad[8] |= rtcpEncryptionFlag diff --git a/vendor/github.com/pion/srtp/v2/srtp_cipher_aes_cm_hmac_sha1.go b/vendor/github.com/pion/srtp/v2/srtp_cipher_aes_cm_hmac_sha1.go index 85d7ac4ad..d56e6afb1 100644 --- a/vendor/github.com/pion/srtp/v2/srtp_cipher_aes_cm_hmac_sha1.go +++ b/vendor/github.com/pion/srtp/v2/srtp_cipher_aes_cm_hmac_sha1.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import ( //nolint:gci @@ -13,6 +16,8 @@ import ( //nolint:gci ) type srtpCipherAesCmHmacSha1 struct { + ProtectionProfile + srtpSessionSalt []byte srtpSessionAuth hash.Hash srtpBlock cipher.Block @@ -22,8 +27,8 @@ type srtpCipherAesCmHmacSha1 struct { srtcpBlock cipher.Block } -func newSrtpCipherAesCmHmacSha1(masterKey, masterSalt []byte) (*srtpCipherAesCmHmacSha1, error) { - s := &srtpCipherAesCmHmacSha1{} +func newSrtpCipherAesCmHmacSha1(profile ProtectionProfile, masterKey, masterSalt []byte) (*srtpCipherAesCmHmacSha1, error) { + s := &srtpCipherAesCmHmacSha1{ProtectionProfile: profile} srtpSessionKey, err := aesCmKeyDerivation(labelSRTPEncryption, masterKey, masterSalt, 0, len(masterKey)) if err != nil { return nil, err @@ -44,7 +49,7 @@ func newSrtpCipherAesCmHmacSha1(masterKey, masterSalt []byte) (*srtpCipherAesCmH return nil, err } - authKeyLen, err := ProtectionProfileAes128CmHmacSha1_80.authKeyLen() + authKeyLen, err := profile.authKeyLen() if err != nil { return nil, err } @@ -64,17 +69,13 @@ func newSrtpCipherAesCmHmacSha1(masterKey, masterSalt []byte) (*srtpCipherAesCmH return s, nil } -func (s *srtpCipherAesCmHmacSha1) authTagLen() int { - return 10 -} - -func (s *srtpCipherAesCmHmacSha1) aeadAuthTagLen() int { - return 0 -} - func (s *srtpCipherAesCmHmacSha1) encryptRTP(dst []byte, header *rtp.Header, payload []byte, roc uint32) (ciphertext []byte, err error) { // Grow the given buffer to fit the output. - dst = growBufferSize(dst, header.MarshalSize()+len(payload)+s.authTagLen()) + authTagLen, err := s.rtpAuthTagLen() + if err != nil { + return nil, err + } + dst = growBufferSize(dst, header.MarshalSize()+len(payload)+authTagLen) // Copy the header unencrypted. n, err := header.MarshalTo(dst) @@ -84,8 +85,9 @@ func (s *srtpCipherAesCmHmacSha1) encryptRTP(dst []byte, header *rtp.Header, pay // Encrypt the payload counter := generateCounter(header.SequenceNumber, roc, header.SSRC, s.srtpSessionSalt) - stream := cipher.NewCTR(s.srtpBlock, counter) - stream.XORKeyStream(dst[n:], payload) + if err = xorBytesCTR(s.srtpBlock, counter[:], dst[n:], payload); err != nil { + return nil, err + } n += len(payload) // Generate the auth tag. @@ -102,8 +104,12 @@ func (s *srtpCipherAesCmHmacSha1) encryptRTP(dst []byte, header *rtp.Header, pay func (s *srtpCipherAesCmHmacSha1) decryptRTP(dst, ciphertext []byte, header *rtp.Header, headerLen int, roc uint32) ([]byte, error) { // Split the auth tag and the cipher text into two parts. - actualTag := ciphertext[len(ciphertext)-s.authTagLen():] - ciphertext = ciphertext[:len(ciphertext)-s.authTagLen()] + authTagLen, err := s.rtpAuthTagLen() + if err != nil { + return nil, err + } + actualTag := ciphertext[len(ciphertext)-authTagLen:] + ciphertext = ciphertext[:len(ciphertext)-authTagLen] // Generate the auth tag we expect to see from the ciphertext. expectedTag, err := s.generateSrtpAuthTag(ciphertext, roc) @@ -122,17 +128,20 @@ func (s *srtpCipherAesCmHmacSha1) decryptRTP(dst, ciphertext []byte, header *rtp // Decrypt the ciphertext for the payload. counter := generateCounter(header.SequenceNumber, roc, header.SSRC, s.srtpSessionSalt) - stream := cipher.NewCTR(s.srtpBlock, counter) - stream.XORKeyStream(dst[headerLen:], ciphertext[headerLen:]) - return dst, nil + err = xorBytesCTR( + s.srtpBlock, counter[:], dst[headerLen:], ciphertext[headerLen:], + ) + return dst, err } func (s *srtpCipherAesCmHmacSha1) encryptRTCP(dst, decrypted []byte, srtcpIndex uint32, ssrc uint32) ([]byte, error) { dst = allocateIfMismatch(dst, decrypted) // Encrypt everything after header - stream := cipher.NewCTR(s.srtcpBlock, generateCounter(uint16(srtcpIndex&0xffff), srtcpIndex>>16, ssrc, s.srtcpSessionSalt)) - stream.XORKeyStream(dst[8:], dst[8:]) + counter := generateCounter(uint16(srtcpIndex&0xffff), srtcpIndex>>16, ssrc, s.srtcpSessionSalt) + if err := xorBytesCTR(s.srtcpBlock, counter[:], dst[8:], dst[8:]); err != nil { + return nil, err + } // Add SRTCP Index and set Encryption bit dst = append(dst, make([]byte, 4)...) @@ -147,23 +156,27 @@ func (s *srtpCipherAesCmHmacSha1) encryptRTCP(dst, decrypted []byte, srtcpIndex } func (s *srtpCipherAesCmHmacSha1) decryptRTCP(out, encrypted []byte, index, ssrc uint32) ([]byte, error) { - tailOffset := len(encrypted) - (s.authTagLen() + srtcpIndexSize) + authTagLen, err := s.rtcpAuthTagLen() + if err != nil { + return nil, err + } + tailOffset := len(encrypted) - (authTagLen + srtcpIndexSize) out = out[0:tailOffset] - expectedTag, err := s.generateSrtcpAuthTag(encrypted[:len(encrypted)-s.authTagLen()]) + expectedTag, err := s.generateSrtcpAuthTag(encrypted[:len(encrypted)-authTagLen]) if err != nil { return nil, err } - actualTag := encrypted[len(encrypted)-s.authTagLen():] + actualTag := encrypted[len(encrypted)-authTagLen:] if subtle.ConstantTimeCompare(actualTag, expectedTag) != 1 { return nil, errFailedToVerifyAuthTag } - stream := cipher.NewCTR(s.srtcpBlock, generateCounter(uint16(index&0xffff), index>>16, ssrc, s.srtcpSessionSalt)) - stream.XORKeyStream(out[8:], out[8:]) + counter := generateCounter(uint16(index&0xffff), index>>16, ssrc, s.srtcpSessionSalt) + err = xorBytesCTR(s.srtcpBlock, counter[:], out[8:], out[8:]) - return out, nil + return out, err } func (s *srtpCipherAesCmHmacSha1) generateSrtpAuthTag(buf []byte, roc uint32) ([]byte, error) { @@ -196,8 +209,12 @@ func (s *srtpCipherAesCmHmacSha1) generateSrtpAuthTag(buf []byte, roc uint32) ([ return nil, err } - // Truncate the hash to the first 10 bytes. - return s.srtpSessionAuth.Sum(nil)[0:s.authTagLen()], nil + // Truncate the hash to the size indicated by the profile + authTagLen, err := s.rtpAuthTagLen() + if err != nil { + return nil, err + } + return s.srtpSessionAuth.Sum(nil)[0:authTagLen], nil } func (s *srtpCipherAesCmHmacSha1) generateSrtcpAuthTag(buf []byte) ([]byte, error) { @@ -217,12 +234,17 @@ func (s *srtpCipherAesCmHmacSha1) generateSrtcpAuthTag(buf []byte) ([]byte, erro if _, err := s.srtcpSessionAuth.Write(buf); err != nil { return nil, err } + authTagLen, err := s.rtcpAuthTagLen() + if err != nil { + return nil, err + } - return s.srtcpSessionAuth.Sum(nil)[0:s.authTagLen()], nil + return s.srtcpSessionAuth.Sum(nil)[0:authTagLen], nil } func (s *srtpCipherAesCmHmacSha1) getRTCPIndex(in []byte) uint32 { - tailOffset := len(in) - (s.authTagLen() + srtcpIndexSize) + authTagLen, _ := s.rtcpAuthTagLen() + tailOffset := len(in) - (authTagLen + srtcpIndexSize) srtcpIndexBuffer := in[tailOffset : tailOffset+srtcpIndexSize] return binary.BigEndian.Uint32(srtcpIndexBuffer) &^ (1 << 31) } diff --git a/vendor/github.com/pion/srtp/v2/stream.go b/vendor/github.com/pion/srtp/v2/stream.go index 7b7a0cf9a..5f9c58a75 100644 --- a/vendor/github.com/pion/srtp/v2/stream.go +++ b/vendor/github.com/pion/srtp/v2/stream.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp type readStream interface { diff --git a/vendor/github.com/pion/srtp/v2/stream_srtcp.go b/vendor/github.com/pion/srtp/v2/stream_srtcp.go index e335937ff..08d36dca5 100644 --- a/vendor/github.com/pion/srtp/v2/stream_srtcp.go +++ b/vendor/github.com/pion/srtp/v2/stream_srtcp.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import ( @@ -7,7 +10,7 @@ import ( "time" "github.com/pion/rtcp" - "github.com/pion/transport/packetio" + "github.com/pion/transport/v2/packetio" ) // Limit the buffer size to 100KB @@ -17,11 +20,11 @@ const srtcpBufferSize = 100 * 1000 type ReadStreamSRTCP struct { mu sync.Mutex - isInited bool isClosed chan bool - session *SessionSRTCP - ssrc uint32 + session *SessionSRTCP + ssrc uint32 + isInited bool buffer io.ReadWriteCloser } diff --git a/vendor/github.com/pion/srtp/v2/stream_srtp.go b/vendor/github.com/pion/srtp/v2/stream_srtp.go index 8b57c7c6f..858970082 100644 --- a/vendor/github.com/pion/srtp/v2/stream_srtp.go +++ b/vendor/github.com/pion/srtp/v2/stream_srtp.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import ( @@ -7,7 +10,7 @@ import ( "time" "github.com/pion/rtp" - "github.com/pion/transport/packetio" + "github.com/pion/transport/v2/packetio" ) // Limit the buffer size to 1MB @@ -17,11 +20,11 @@ const srtpBufferSize = 1000 * 1000 type ReadStreamSRTP struct { mu sync.Mutex - isInited bool isClosed chan bool - session *SessionSRTP - ssrc uint32 + session *SessionSRTP + ssrc uint32 + isInited bool buffer io.ReadWriteCloser } diff --git a/vendor/github.com/pion/srtp/v2/util.go b/vendor/github.com/pion/srtp/v2/util.go index 1ae34a62a..792175d96 100644 --- a/vendor/github.com/pion/srtp/v2/util.go +++ b/vendor/github.com/pion/srtp/v2/util.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package srtp import "bytes" diff --git a/vendor/github.com/pion/stun/.codecov.yml b/vendor/github.com/pion/stun/.codecov.yml deleted file mode 100644 index 7fa67fec8..000000000 --- a/vendor/github.com/pion/stun/.codecov.yml +++ /dev/null @@ -1,9 +0,0 @@ -coverage: - status: - patch: off - project: - default: - # basic - target: 98 - threshold: null - base: auto diff --git a/vendor/github.com/pion/stun/.gitignore b/vendor/github.com/pion/stun/.gitignore index b9420d943..6e2f206a9 100644 --- a/vendor/github.com/pion/stun/.gitignore +++ b/vendor/github.com/pion/stun/.gitignore @@ -1,17 +1,28 @@ -*-fuzz.zip -.idea -benchmark.*.write -*.test -*.out +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + +### JetBrains IDE ### +##################### +.idea/ + +### Emacs Temporary Files ### +############################# +*~ + +### Folders ### +############### +bin/ +vendor/ +node_modules/ + +### Files ### +############# +*.ivf +*.ogg +tags +cover.out *.sw[poe] -bench.go-* -PACKAGES -cmd/stun-cli/stun-cli -cmd/stun-decode/stun-decode -cmd/stun-bench/stun-bench -cmd/stun-nat-behaviour/stun-nat-behaviour - -coverage.txt - -e2e/dump.pcap -e2e/log-*.txt +*.wasm +examples/sfu-ws/cert.pem +examples/sfu-ws/key.pem +wasm_exec.js diff --git a/vendor/github.com/pion/stun/.golangci.yml b/vendor/github.com/pion/stun/.golangci.yml index 40ca69c0d..4e3eddf42 100644 --- a/vendor/github.com/pion/stun/.golangci.yml +++ b/vendor/github.com/pion/stun/.golangci.yml @@ -1,93 +1,137 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + linters-settings: govet: check-shadowing: true - golint: - min-confidence: 0 - gocyclo: - min-complexity: 15 - maligned: - suggest-new: true - dupl: - threshold: 100 - goconst: - min-len: 2 - min-occurrences: 2 misspell: locale: US - lll: - line-length: 140 - goimports: - local-prefixes: github.com/pion - gocritic: - enabled-tags: - - performance - - style - - experimental - disabled-checks: - - commentedOutCode - - sloppyReassign - -issues: - exclude: - - "`assertHMACSize` - `blocksize` always receives `64`" - exclude-rules: - - text: "string ``" - linters: - - goconst - - # Exclude some linters from running on tests files. - - path: _test\.go - linters: - - gocyclo - - errcheck - - dupl - - gosec - - goconst - - # Ease some gocritic warnings on test files. - - path: _test\.go - text: "(unnamedResult|exitAfterDefer|unlambda)" - linters: - - gocritic - - # Exclude known linters from partially hard-vendored code, - # which is impossible to exclude via "nolint" comments. - - path: internal/hmac/ - text: "weak cryptographic primitive" - linters: - - gosec - - path: internal/hmac/ - text: "Write\\` is not checked" - linters: - - errcheck - - # Ease linting on benchmarking code. - - path: cmd/stun-bench/ - linters: - - gosec - - errcheck - - unparam - - - path: ^cmd/ - linters: - - gocyclo - - path: ^cmd/ - text: "(unnamedResult|exitAfterDefer)" - linters: - - gocritic + exhaustive: + default-signifies-exhaustive: true + gomodguard: + blocked: + modules: + - github.com/pkg/errors: + recommendations: + - errors + forbidigo: + forbid: + - ^fmt.Print(f|ln)?$ + - ^log.(Panic|Fatal|Print)(f|ln)?$ + - ^os.Exit$ + - ^panic$ + - ^print(ln)?$ linters: - enable-all: true + enable: + - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bidichk # Checks for dangerous unicode character sequences + - bodyclose # checks whether HTTP response body is closed successfully + - contextcheck # check the function whether use a non-inherited context + - decorder # check declaration order and count of types, constants, variables and functions + - depguard # Go linter that checks if package imports are in a list of acceptable packages + - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) + - dupl # Tool for code clone detection + - durationcheck # check for two durations multiplied together + - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. + - exhaustive # check exhaustiveness of enum switch statements + - exportloopref # checks for pointers to enclosing loop variables + - forbidigo # Forbids identifiers + - forcetypeassert # finds forced type assertions + - gci # Gci control golang package import order and make it always deterministic. + - gochecknoglobals # Checks that no globals are present in Go code + - gochecknoinits # Checks that no init functions are present in Go code + - gocognit # Computes and checks the cognitive complexity of functions + - goconst # Finds repeated strings that could be replaced by a constant + - gocritic # The most opinionated Go source code linter + - godox # Tool for detection of FIXME, TODO and other comment keywords + - goerr113 # Golang linter to check the errors handling expressions + - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification + - gofumpt # Gofumpt checks whether code was gofumpt-ed. + - goheader # Checks is file header matches to pattern + - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. + - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. + - goprintffuncname # Checks that printf-like functions are named with `f` at the end + - gosec # Inspects source code for security problems + - gosimple # Linter for Go source code that specializes in simplifying a code + - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - grouper # An analyzer to analyze expression groups. + - importas # Enforces consistent import aliases + - ineffassign # Detects when assignments to existing variables are not used + - misspell # Finds commonly misspelled English words in comments + - nakedret # Finds naked returns in functions greater than a specified function length + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. + - noctx # noctx finds sending http request without context.Context + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # golint replacement, finds style mistakes + - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks + - stylecheck # Stylecheck is a replacement for golint + - tagliatelle # Checks the struct tags. + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes + - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code + - unconvert # Remove unnecessary type conversions + - unparam # Reports unused function parameters + - unused # Checks Go code for unused constants, variables, functions and types + - wastedassign # wastedassign finds wasted assignment statements + - whitespace # Tool for detection of leading and trailing whitespace disable: - - funlen - - gochecknoglobals - - godox - - prealloc - - scopelint + - containedctx # containedctx is a linter that detects struct contained context.Context field + - cyclop # checks function and package cyclomatic complexity + - exhaustivestruct # Checks if all struct's fields are initialized + - funlen # Tool for detection of long functions + - gocyclo # Computes and checks the cyclomatic complexity of functions + - godot # Check if comments end in a period + - gomnd # An analyzer to detect magic numbers. + - ifshort # Checks that your code uses short syntax for if-statements whenever possible + - ireturn # Accept Interfaces, Return Concrete Types + - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length + - maligned # Tool to detect Go structs that would take less memory if their fields were sorted + - nestif # Reports deeply nested if statements + - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity + - nolintlint # Reports ill-formed or insufficient nolint directives + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test + - prealloc # Finds slice declarations that could potentially be preallocated + - promlinter # Check Prometheus metrics naming via promlint + - rowserrcheck # checks whether Err of rows is checked successfully + - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. + - testpackage # linter that makes you use a separate _test package + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers + - varnamelen # checks that the length of a variable's name matches its scope + - wrapcheck # Checks that errors returned from external packages are wrapped + - wsl # Whitespace Linter - Forces you to use empty lines! + +issues: + exclude-use-default: false + exclude-rules: + # Allow complex tests, better to be self contained + - path: _test\.go + linters: + - gocognit + - forbidigo + + # Allow complex main function in examples + - path: examples + text: "of func `main` is high" + linters: + - gocognit + + # Allow forbidden identifiers in examples + - path: examples + linters: + - forbidigo + + # Allow forbidden identifiers in CLI commands + - path: cmd + linters: + - forbidigo run: - skip-dirs: - - e2e - - fuzz - - testdata - - api + skip-dirs-use-default: false diff --git a/vendor/github.com/pion/stun/.goreleaser.yml b/vendor/github.com/pion/stun/.goreleaser.yml index d72bde60c..30093e9d6 100644 --- a/vendor/github.com/pion/stun/.goreleaser.yml +++ b/vendor/github.com/pion/stun/.goreleaser.yml @@ -1,39 +1,5 @@ -before: - hooks: - - go mod tidy - -archives: -- replacements: - darwin: Darwin - linux: Linux - windows: Windows - 386: i386 - amd64: x86_64 - -checksum: - name_template: 'checksums.txt' - -snapshot: - name_template: "{{ .Tag }}-next" - -changelog: - sort: asc - filters: - exclude: - - '^docs:' - - '^test:' +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT builds: - - binary: stun-not-behavior - id: stun-not-behavior - goos: - - darwin - - windows - - linux - - freebsd - goarch: - - amd64 - - 386 - env: - - CGO_ENABLED=0 - main: ./cmd/stun-nat-behavior +- skip: true diff --git a/vendor/github.com/pion/stun/.travis.yml b/vendor/github.com/pion/stun/.travis.yml deleted file mode 100644 index e464e84ad..000000000 --- a/vendor/github.com/pion/stun/.travis.yml +++ /dev/null @@ -1,135 +0,0 @@ -# -# DO NOT EDIT THIS FILE DIRECTLY -# -# It is automatically copied from https://github.com/pion/.goassets repository. -# If this repository should have package specific CI config, -# remove the repository name from .goassets/.github/workflows/assets-sync.yml. -# - -dist: bionic -language: go - - -branches: - only: - - master - -env: - global: - - GO111MODULE=on - - GOLANGCI_LINT_VERSION=1.19.1 - -cache: - directories: - - ${HOME}/.cache/go-build - - ${GOPATH}/pkg/mod - npm: true - yarn: true - -_lint_job: &lint_job - env: CACHE_NAME=lint - before_install: - - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi - install: skip - before_script: - - | - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \ - | bash -s - -b $GOPATH/bin v${GOLANGCI_LINT_VERSION} - script: - - bash .github/assert-contributors.sh - - bash .github/lint-disallowed-functions-in-library.sh - - bash .github/lint-commit-message.sh - - bash .github/lint-filename.sh - - golangci-lint run ./... -_test_job: &test_job - env: CACHE_NAME=test - before_install: - - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi - - go mod download - install: - - go build ./... - script: - - testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)} - - coverpkgs=$(echo "${testpkgs}" | paste -s -d ',') - - | - go test \ - -coverpkg=${coverpkgs} -coverprofile=cover.out -covermode=atomic \ - ${TEST_EXTRA_ARGS:-} \ - -v -race ${testpkgs} - - if [ -n "${TEST_HOOK}" ]; then ${TEST_HOOK}; fi - after_success: - - travis_retry bash <(curl -s https://codecov.io/bash) -c -F go -_test_i386_job: &test_i386_job - env: CACHE_NAME=test386 - services: docker - before_install: - - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi - script: - - testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)} - - | - docker run \ - -u $(id -u):$(id -g) \ - -e "GO111MODULE=on" \ - -e "CGO_ENABLED=0" \ - -v ${PWD}:/go/src/github.com/pion/$(basename ${PWD}) \ - -v ${HOME}/gopath/pkg/mod:/go/pkg/mod \ - -v ${HOME}/.cache/go-build:/.cache/go-build \ - -w /go/src/github.com/pion/$(basename ${PWD}) \ - -it i386/golang:${GO_VERSION}-alpine \ - /usr/local/go/bin/go test \ - ${TEST_EXTRA_ARGS:-} \ - -v ${testpkgs} -_test_wasm_job: &test_wasm_job - env: CACHE_NAME=wasm - language: node_js - node_js: 12 - before_install: - - if [ -f .github/.ci.conf ]; then . .github/.ci.conf; fi - - if ${SKIP_WASM_TEST:-false}; then exit 0; fi - install: - # Manually download and install Go instead of using gimme. - # It looks like gimme Go causes some errors on go-test for Wasm. - - curl -sSfL https://dl.google.com/go/go${GO_VERSION}.linux-amd64.tar.gz | tar -C ~ -xzf - - - export GOROOT=${HOME}/go - - export PATH=${GOROOT}/bin:${PATH} - - yarn install - - export GO_JS_WASM_EXEC=${GO_JS_WASM_EXEC:-${GOROOT}/misc/wasm/go_js_wasm_exec} - script: - - testpkgs=${TEST_PACKAGES:-$(go list ./... | grep -v examples)} - - coverpkgs=$(echo "${testpkgs}" | paste -s -d ',') - - | - GOOS=js GOARCH=wasm go test \ - -coverpkg=${coverpkgs} -coverprofile=cover.out -covermode=atomic \ - -exec="${GO_JS_WASM_EXEC}" \ - -v ${testpkgs} - after_success: - - travis_retry bash <(curl -s https://codecov.io/bash) -c -F wasm - -jobs: - include: - - <<: *lint_job - name: Lint 1.14 - go: 1.14 - - <<: *test_job - name: Test 1.13 - go: 1.13 - - <<: *test_job - name: Test 1.14 - go: 1.14 - - <<: *test_i386_job - name: Test i386 1.13 - env: GO_VERSION=1.13 - go: 1.14 # version for host environment used to go list - - <<: *test_i386_job - name: Test i386 1.14 - env: GO_VERSION=1.14 - go: 1.14 # version for host environment used to go list - - <<: *test_wasm_job - name: Test WASM 1.13 - env: GO_VERSION=1.13 - - <<: *test_wasm_job - name: Test WASM 1.14 - env: GO_VERSION=1.14 - -notifications: - email: false diff --git a/vendor/github.com/pion/stun/AUTHORS b/vendor/github.com/pion/stun/AUTHORS deleted file mode 100644 index 717af8f60..000000000 --- a/vendor/github.com/pion/stun/AUTHORS +++ /dev/null @@ -1,10 +0,0 @@ -Sean DuBois -Raphael Randschau -Aleksandr Razumov -Aliaksandr Valialkin -Michiel De Backker -Y.Horie -songjiayang -The gortc project -The IETF Trust -The Go Authors diff --git a/vendor/github.com/pion/stun/AUTHORS.txt b/vendor/github.com/pion/stun/AUTHORS.txt new file mode 100644 index 000000000..3c3d9e00f --- /dev/null +++ b/vendor/github.com/pion/stun/AUTHORS.txt @@ -0,0 +1,40 @@ +# Thank you to everyone that made Pion possible. If you are interested in contributing +# we would love to have you https://github.com/pion/webrtc/wiki/Contributing +# +# This file is auto generated, using git to list all individuals contributors. +# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting +Adam Kiss +Aleksandr Razumov +Aleksandr Razumov +Atsushi Watanabe +backkem +Cecylia Bocovich +Christian Muehlhaeuser +David-dp- +ernado +ernado +fossabot +Frank Dietrich +Hugo Arregui +Jerry Tao +jinleileiking +John Bradley +Juliusz Chroboczek +Maanas Royy +Moises Marangoni +Raphael Randschau +Sean DuBois +Sean DuBois +Sean DuBois +songjiayang +Steffen Vogel +Vladislav Yarmak +Will LE +Y.Horie +Yutaka Takeda +ZHENK + +# List of contributors not appearing in Git history +Aliaksandr Valialkin +The IETF Trust +The gortc project diff --git a/vendor/github.com/pion/stun/Dockerfile b/vendor/github.com/pion/stun/Dockerfile deleted file mode 100644 index 429239a38..000000000 --- a/vendor/github.com/pion/stun/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM golang:1.14 - -COPY . /go/src/github.com/pion/stun - -RUN go test github.com/pion/stun diff --git a/vendor/github.com/pion/stun/LICENSE.md b/vendor/github.com/pion/stun/LICENSE similarity index 93% rename from vendor/github.com/pion/stun/LICENSE.md rename to vendor/github.com/pion/stun/LICENSE index 5cc9cbdc5..491caf6b0 100644 --- a/vendor/github.com/pion/stun/LICENSE.md +++ b/vendor/github.com/pion/stun/LICENSE @@ -1,4 +1,6 @@ -Copyright 2018 Pion LLC +MIT License + +Copyright (c) 2023 The Pion community 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: diff --git a/vendor/github.com/pion/stun/Makefile b/vendor/github.com/pion/stun/Makefile index 43de8d003..ebfcd3394 100644 --- a/vendor/github.com/pion/stun/Makefile +++ b/vendor/github.com/pion/stun/Makefile @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + VERSION := $(shell git describe --tags | sed -e 's/^v//g' | awk -F "-" '{print $$1}') ITERATION := $(shell git describe --tags --long | awk -F "-" '{print $$2}') GO_VERSION=$(shell gobuild -v) @@ -9,22 +12,6 @@ bench: go test -bench . bench-record: $(GO) test -bench . > "benchmarks/stun-go-$(GO_VERSION).txt" -fuzz-prepare-msg: - go-fuzz-build -func FuzzMessage -o stun-msg-fuzz.zip github.com/pion/stun -fuzz-prepare-typ: - go-fuzz-build -func FuzzType -o stun-typ-fuzz.zip github.com/pion/stun -fuzz-prepare-setters: - go-fuzz-build -func FuzzSetters -o stun-setters-fuzz.zip github.com/pion/stun -fuzz-msg: - go-fuzz -bin=./stun-msg-fuzz.zip -workdir=fuzz/stun-msg -fuzz-typ: - go-fuzz -bin=./stun-typ-fuzz.zip -workdir=fuzz/stun-typ -fuzz-setters: - go-fuzz -bin=./stun-setters-fuzz.zip -workdir=fuzz/stun-setters -fuzz-test: - go test -tags gofuzz -run TestFuzz -v . -fuzz-reset-setters: - rm -f -v -r stun-setters-fuzz.zip fuzz/stun-setters lint: @golangci-lint run ./... @echo "ok" @@ -39,23 +26,14 @@ bench-compare: go test -bench . > bench.go-16 go-tip test -bench . > bench.go-tip @benchcmp bench.go-16 bench.go-tip -install-fuzz: - go get -u github.com/dvyukov/go-fuzz/go-fuzz-build - go get github.com/dvyukov/go-fuzz/go-fuzz install: go get gortc.io/api go get -u github.com/golangci/golangci-lint/cmd/golangci-lint -docker-build: - docker build -t pion/stun . test-integration: @cd e2e && bash ./test.sh -prepush: assert test lint test-integration +prepush: test lint test-integration check-api: @cd api && bash ./check.sh -assert: - bash .github/assert-contributors.sh - bash .github/lint-disallowed-functions-in-library.sh - bash .github/lint-commit-message.sh test: @./go.test.sh clean: diff --git a/vendor/github.com/pion/stun/README.md b/vendor/github.com/pion/stun/README.md index 6dd50d0f3..fe1b28ffb 100644 --- a/vendor/github.com/pion/stun/README.md +++ b/vendor/github.com/pion/stun/README.md @@ -6,52 +6,21 @@

A Go implementation of STUN

Pion stun - Slack Widget
- Build Status - GoDoc + GitHub Workflow Status + Go Reference Coverage Status Go Report Card - License: MIT


-### Roadmap -The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones. - -### Community -Pion has an active community on the [Golang Slack](https://invite.slack.golangbridge.org/). Sign up and join the **#pion** channel for discussions and support. You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion). - -We are always looking to support **your projects**. Please reach out if you have something to build! - -If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly) - -### Contributing -Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible: - -* [Sean DuBois](https://github.com/Sean-Der) - *Original Author* -* [Raphael Randschau](https://github.com/nicolai86) - *STUN client* -* [Michiel De Backker](https://github.com/backkem) - *Minor fixes* -* [Y.Horie](https://github.com/u5surf) - *Fix lint issues* -* [Aleksandr Razumov](https://github.com/ernado) - *The v0.3 version* -* [songjiayang](https://github.com/songjiayang) -* [Adam Kiss](https://github.com/masterada) -* [Moises Marangoni](https://github.com/Moisesbr) -* [Yutaka Takeda](https://github.com/enobufs) -* [Hugo Arregui](https://github.com/hugoArregui) -* [Maanas Royy](https://github.com/maanas) -* [Atsushi Watanabe](https://github.com/at-wat) -* [Cecylia Bocovich](https://github.com/cohosh) -* [Christian Muehlhaeuser](https://github.com/muesli) - -# STUN -Package stun implements Session Traversal Utilities for NAT (STUN) [[RFC5389](https://tools.ietf.org/html/rfc5389)] +Package `stun` implements Session Traversal Utilities for NAT (STUN) ([RFC 5389][rfc5389]) protocol and [client](https://pkg.go.dev/github.com/pion/stun#Client) with no external dependencies and zero allocations in hot paths. Client [supports](https://pkg.go.dev/github.com/pion/stun#WithRTO) automatic request retransmissions. -# Example +### Example You can get your current IP address from any STUN server by sending binding request. See more idiomatic example at `cmd/stun-client`. ```go @@ -64,8 +33,14 @@ import ( ) func main() { + // Parse a STUN URI + u, err := stun.ParseURI("stun:stun.l.google.com:19302") + if err != nil { + panic(err) + } + // Creating a "connection" to STUN server. - c, err := stun.Dial("udp", "stun.l.google.com:19302") + c, err := stun.DialURI(u, &stun.DialConfig{}) if err != nil { panic(err) } @@ -88,30 +63,43 @@ func main() { } ``` -## Supported RFCs -- [x] [RFC 5389](https://tools.ietf.org/html/rfc5389) — Session Traversal Utilities for NAT -- [x] [RFC 5769](https://tools.ietf.org/html/rfc5769) — Test Vectors for STUN -- [x] [RFC 6062](https://tools.ietf.org/html/rfc6062) — TURN extensions for TCP allocations -- [x] [RFC 7064](https://tools.ietf.org/html/rfc7064) — STUN URI -- [x] (TLS-over-)TCP client support -- [ ] [ALTERNATE-SERVER](https://tools.ietf.org/html/rfc5389#section-11) support [#48](https://github.com/pion/stun/issues/48) -- [ ] [RFC 5780](https://tools.ietf.org/html/rfc5780) — NAT Behavior Discovery Using STUN [#49](https://github.com/pion/stun/issues/49) +### RFCs +#### Implemented +- **RFC 5389**: [Session Traversal Utilities for NAT (STUN)][rfc5389] +- **RFC 5769**: [Test Vectors for Session Traversal Utilities for NAT (STUN)][rfc5769] +- **RFC 6062**: [Traversal Using Relays around NAT (TURN) Extensions for TCP Allocations][rfc6062] +- **RFC 7064**: [URI Scheme for the Session Traversal Utilities for NAT (STUN) Protocol][rfc7064] +- **RFC 7065**: [Traversal Using Relays around NAT (TURN) Uniform Resource Identifiers][rfc7065] +- **RFC 5780**: [NAT Behavior Discovery Using Session Traversal Utilities for NAT (STUN)][rfc5780] via [cmd/stun-nat-behaviour](cmd/stun-nat-behaviour) +- (TLS-over-)TCP client support -# Stability +#### Planned +- **RFC 5389**: [ALTERNATE-SERVER](https://tools.ietf.org/html/rfc5389#section-11) support [#48](https://github.com/pion/stun/issues/48) + +#### Compatability notes + +[RFC 5389][rfc5389] obsoletes [RFC 3489][rfc3489], so implementation was ignored by purpose, however, +[RFC 3489][rfc3489] can be easily implemented as separate package. + +[rfc3489]: https://tools.ietf.org/html/rfc3489 +[rfc5389]: https://tools.ietf.org/html/rfc5389 +[rfc5769]: https://tools.ietf.org/html/rfc5769 +[rfc5780]: https://tools.ietf.org/html/rfc5780 +[rfc6062]: https://tools.ietf.org/html/rfc6062 +[rfc7064]: https://tools.ietf.org/html/rfc7064 +[rfc7065]: https://tools.ietf.org/html/rfc7065 + +### Stability Package is currently stable, no backward incompatible changes are expected with exception of critical bugs or security fixes. Additional attributes are unlikely to be implemented in scope of stun package, the only exception is constants for attribute or message types. -# RFC 3489 notes -RFC 5389 obsoletes RFC 3489, so implementation was ignored by purpose, however, -RFC 3489 can be easily implemented as separate package. - -# Requirements +### Requirements Go 1.12 is currently supported and tested in CI. -# Testing +### Testing Client behavior is tested and verified in many ways: * End-To-End with long-term credentials * **coturn**: The coturn [server](https://github.com/coturn/coturn/wiki/turnserver) (linux) @@ -123,62 +111,76 @@ See [TeamCity project](https://tc.gortc.io/project.html?projectId=stun&guest=1) for more information. Also the Wireshark `.pcap` files are available for e2e test in artifacts for build. -# Benchmarks - +### Benchmarks Intel(R) Core(TM) i7-8700K: ``` -version: 1.16.5 +version: 1.22.2 goos: linux goarch: amd64 pkg: github.com/pion/stun PASS benchmark iter time/iter throughput bytes alloc allocs --------- ---- --------- ---------- ----------- ------ -BenchmarkMappedAddress_AddTo-12 30000000 36.40 ns/op 0 B/op 0 allocs/op -BenchmarkAlternateServer_AddTo-12 50000000 36.70 ns/op 0 B/op 0 allocs/op -BenchmarkAgent_GC-12 500000 2552.00 ns/op 0 B/op 0 allocs/op -BenchmarkAgent_Process-12 50000000 38.00 ns/op 0 B/op 0 allocs/op -BenchmarkMessage_GetNotFound-12 200000000 6.90 ns/op 0 B/op 0 allocs/op -BenchmarkMessage_Get-12 200000000 7.61 ns/op 0 B/op 0 allocs/op -BenchmarkClient_Do-12 2000000 1072.00 ns/op 0 B/op 0 allocs/op -BenchmarkErrorCode_AddTo-12 20000000 67.00 ns/op 0 B/op 0 allocs/op -BenchmarkErrorCodeAttribute_AddTo-12 30000000 52.20 ns/op 0 B/op 0 allocs/op -BenchmarkErrorCodeAttribute_GetFrom-12 100000000 12.00 ns/op 0 B/op 0 allocs/op -BenchmarkFingerprint_AddTo-12 20000000 102.00 ns/op 430.08 MB/s 0 B/op 0 allocs/op -BenchmarkFingerprint_Check-12 30000000 54.80 ns/op 948.38 MB/s 0 B/op 0 allocs/op -BenchmarkBuildOverhead/Build-12 5000000 333.00 ns/op 0 B/op 0 allocs/op -BenchmarkBuildOverhead/BuildNonPointer-12 3000000 536.00 ns/op 100 B/op 4 allocs/op -BenchmarkBuildOverhead/Raw-12 10000000 181.00 ns/op 0 B/op 0 allocs/op -BenchmarkMessageIntegrity_AddTo-12 1000000 1053.00 ns/op 18.98 MB/s 0 B/op 0 allocs/op -BenchmarkMessageIntegrity_Check-12 1000000 1135.00 ns/op 28.17 MB/s 0 B/op 0 allocs/op -BenchmarkMessage_Write-12 100000000 27.70 ns/op 1011.09 MB/s 0 B/op 0 allocs/op -BenchmarkMessageType_Value-12 2000000000 0.49 ns/op 0 B/op 0 allocs/op -BenchmarkMessage_WriteTo-12 100000000 12.80 ns/op 0 B/op 0 allocs/op -BenchmarkMessage_ReadFrom-12 50000000 25.00 ns/op 801.19 MB/s 0 B/op 0 allocs/op -BenchmarkMessage_ReadBytes-12 100000000 18.00 ns/op 1113.03 MB/s 0 B/op 0 allocs/op -BenchmarkIsMessage-12 2000000000 1.08 ns/op 18535.57 MB/s 0 B/op 0 allocs/op -BenchmarkMessage_NewTransactionID-12 2000000 673.00 ns/op 0 B/op 0 allocs/op -BenchmarkMessageFull-12 5000000 316.00 ns/op 0 B/op 0 allocs/op -BenchmarkMessageFullHardcore-12 20000000 88.90 ns/op 0 B/op 0 allocs/op -BenchmarkMessage_WriteHeader-12 200000000 8.18 ns/op 0 B/op 0 allocs/op -BenchmarkMessage_CloneTo-12 30000000 37.90 ns/op 1795.32 MB/s 0 B/op 0 allocs/op -BenchmarkMessage_AddTo-12 300000000 4.77 ns/op 0 B/op 0 allocs/op -BenchmarkDecode-12 100000000 22.00 ns/op 0 B/op 0 allocs/op -BenchmarkUsername_AddTo-12 50000000 23.20 ns/op 0 B/op 0 allocs/op -BenchmarkUsername_GetFrom-12 100000000 17.90 ns/op 0 B/op 0 allocs/op -BenchmarkNonce_AddTo-12 50000000 34.40 ns/op 0 B/op 0 allocs/op -BenchmarkNonce_AddTo_BadLength-12 200000000 8.29 ns/op 0 B/op 0 allocs/op -BenchmarkNonce_GetFrom-12 100000000 17.50 ns/op 0 B/op 0 allocs/op -BenchmarkUnknownAttributes/AddTo-12 30000000 48.10 ns/op 0 B/op 0 allocs/op -BenchmarkUnknownAttributes/GetFrom-12 100000000 20.90 ns/op 0 B/op 0 allocs/op -BenchmarkXOR-12 50000000 25.80 ns/op 39652.86 MB/s 0 B/op 0 allocs/op -BenchmarkXORSafe-12 3000000 515.00 ns/op 1988.04 MB/s 0 B/op 0 allocs/op -BenchmarkXORFast-12 20000000 73.40 ns/op 13959.30 MB/s 0 B/op 0 allocs/op -BenchmarkXORMappedAddress_AddTo-12 20000000 56.70 ns/op 0 B/op 0 allocs/op -BenchmarkXORMappedAddress_GetFrom-12 50000000 37.40 ns/op 0 B/op 0 allocs/op -ok github.com/pion/stun 76.868s +BenchmarkMappedAddress_AddTo-12 32489450 38.30 ns/op 0 B/op 0 allocs/op +BenchmarkAlternateServer_AddTo-12 31230991 39.00 ns/op 0 B/op 0 allocs/op +BenchmarkAgent_GC-12 431390 2918.00 ns/op 0 B/op 0 allocs/op +BenchmarkAgent_Process-12 35901940 36.20 ns/op 0 B/op 0 allocs/op +BenchmarkMessage_GetNotFound-12 242004358 5.19 ns/op 0 B/op 0 allocs/op +BenchmarkMessage_Get-12 230520343 5.21 ns/op 0 B/op 0 allocs/op +BenchmarkClient_Do-12 1282231 943.00 ns/op 0 B/op 0 allocs/op +BenchmarkErrorCode_AddTo-12 16318916 75.50 ns/op 0 B/op 0 allocs/op +BenchmarkErrorCodeAttribute_AddTo-12 21584140 54.80 ns/op 0 B/op 0 allocs/op +BenchmarkErrorCodeAttribute_GetFrom-12 100000000 11.10 ns/op 0 B/op 0 allocs/op +BenchmarkFingerprint_AddTo-12 19368768 64.00 ns/op 687.81 MB/s 0 B/op 0 allocs/op +BenchmarkFingerprint_Check-12 24167007 49.10 ns/op 1057.99 MB/s 0 B/op 0 allocs/op +BenchmarkBuildOverhead/Build-12 5486252 224.00 ns/op 0 B/op 0 allocs/op +BenchmarkBuildOverhead/BuildNonPointer-12 2496544 517.00 ns/op 100 B/op 4 allocs/op +BenchmarkBuildOverhead/Raw-12 6652118 181.00 ns/op 0 B/op 0 allocs/op +BenchmarkMessage_ForEach-12 28254212 35.90 ns/op 0 B/op 0 allocs/op +BenchmarkMessageIntegrity_AddTo-12 1000000 1179.00 ns/op 16.96 MB/s 0 B/op 0 allocs/op +BenchmarkMessageIntegrity_Check-12 975954 1219.00 ns/op 26.24 MB/s 0 B/op 0 allocs/op +BenchmarkMessage_Write-12 41040598 30.40 ns/op 922.13 MB/s 0 B/op 0 allocs/op +BenchmarkMessageType_Value-12 1000000000 0.53 ns/op 0 B/op 0 allocs/op +BenchmarkMessage_WriteTo-12 94942935 11.30 ns/op 0 B/op 0 allocs/op +BenchmarkMessage_ReadFrom-12 43437718 29.30 ns/op 682.87 MB/s 0 B/op 0 allocs/op +BenchmarkMessage_ReadBytes-12 74693397 15.90 ns/op 1257.42 MB/s 0 B/op 0 allocs/op +BenchmarkIsMessage-12 1000000000 1.20 ns/op 16653.64 MB/s 0 B/op 0 allocs/op +BenchmarkMessage_NewTransactionID-12 521121 2450.00 ns/op 0 B/op 0 allocs/op +BenchmarkMessageFull-12 5389495 221.00 ns/op 0 B/op 0 allocs/op +BenchmarkMessageFullHardcore-12 12715876 94.40 ns/op 0 B/op 0 allocs/op +BenchmarkMessage_WriteHeader-12 100000000 11.60 ns/op 0 B/op 0 allocs/op +BenchmarkMessage_CloneTo-12 30199020 41.80 ns/op 1626.66 MB/s 0 B/op 0 allocs/op +BenchmarkMessage_AddTo-12 415257625 2.97 ns/op 0 B/op 0 allocs/op +BenchmarkDecode-12 49573747 23.60 ns/op 0 B/op 0 allocs/op +BenchmarkUsername_AddTo-12 56282674 22.50 ns/op 0 B/op 0 allocs/op +BenchmarkUsername_GetFrom-12 100000000 10.10 ns/op 0 B/op 0 allocs/op +BenchmarkNonce_AddTo-12 39419097 35.80 ns/op 0 B/op 0 allocs/op +BenchmarkNonce_AddTo_BadLength-12 196291666 6.04 ns/op 0 B/op 0 allocs/op +BenchmarkNonce_GetFrom-12 120857732 9.93 ns/op 0 B/op 0 allocs/op +BenchmarkUnknownAttributes/AddTo-12 28881430 37.20 ns/op 0 B/op 0 allocs/op +BenchmarkUnknownAttributes/GetFrom-12 64907534 19.80 ns/op 0 B/op 0 allocs/op +BenchmarkXOR-12 32868506 32.20 ns/op 31836.66 MB/s +BenchmarkXORSafe-12 5185776 234.00 ns/op 4378.74 MB/s +BenchmarkXORFast-12 30975679 32.50 ns/op 31525.28 MB/s +BenchmarkXORMappedAddress_AddTo-12 21518028 54.50 ns/op 0 B/op 0 allocs/op +BenchmarkXORMappedAddress_GetFrom-12 35597667 34.40 ns/op 0 B/op 0 allocs/op +ok github.com/pion/stun 60.973s ``` +### Roadmap +The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones. + +### Community +Pion has an active community on the [Slack](https://pion.ly/slack). + +Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news. + +We are always looking to support **your projects**. Please reach out if you have something to build! +If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly) + +### Contributing +Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible: [AUTHORS.txt](./AUTHORS.txt) + ### License MIT License - see [LICENSE](LICENSE) for full text diff --git a/vendor/github.com/pion/stun/addr.go b/vendor/github.com/pion/stun/addr.go index c4d9653d5..d15e2bbd2 100644 --- a/vendor/github.com/pion/stun/addr.go +++ b/vendor/github.com/pion/stun/addr.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package stun import ( @@ -26,6 +29,14 @@ type AlternateServer struct { Port int } +// ResponseOrigin represents RESPONSE-ORIGIN attribute. +// +// RFC 5780 Section 7.3 +type ResponseOrigin struct { + IP net.IP + Port int +} + // OtherAddress represents OTHER-ADDRESS attribute. // // RFC 5780 Section 7.4 @@ -37,20 +48,21 @@ type OtherAddress struct { // AddTo adds ALTERNATE-SERVER attribute to message. func (s *AlternateServer) AddTo(m *Message) error { a := (*MappedAddress)(s) - return a.addAs(m, AttrAlternateServer) + return a.AddToAs(m, AttrAlternateServer) } // GetFrom decodes ALTERNATE-SERVER from message. func (s *AlternateServer) GetFrom(m *Message) error { a := (*MappedAddress)(s) - return a.getAs(m, AttrAlternateServer) + return a.GetFromAs(m, AttrAlternateServer) } func (a MappedAddress) String() string { return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port)) } -func (a *MappedAddress) getAs(m *Message, t AttrType) error { +// GetFromAs decodes MAPPED-ADDRESS value in message m as an attribute of type t. +func (a *MappedAddress) GetFromAs(m *Message, t AttrType) error { v, err := m.Get(t) if err != nil { return err @@ -84,7 +96,8 @@ func (a *MappedAddress) getAs(m *Message, t AttrType) error { return nil } -func (a *MappedAddress) addAs(m *Message, t AttrType) error { +// AddToAs adds MAPPED-ADDRESS value to m as t attribute. +func (a *MappedAddress) AddToAs(m *Message, t AttrType) error { var ( family = familyIPv4 ip = a.IP @@ -109,26 +122,42 @@ func (a *MappedAddress) addAs(m *Message, t AttrType) error { // AddTo adds MAPPED-ADDRESS to message. func (a *MappedAddress) AddTo(m *Message) error { - return a.addAs(m, AttrMappedAddress) + return a.AddToAs(m, AttrMappedAddress) } // GetFrom decodes MAPPED-ADDRESS from message. func (a *MappedAddress) GetFrom(m *Message) error { - return a.getAs(m, AttrMappedAddress) + return a.GetFromAs(m, AttrMappedAddress) } // AddTo adds OTHER-ADDRESS attribute to message. func (o *OtherAddress) AddTo(m *Message) error { a := (*MappedAddress)(o) - return a.addAs(m, AttrOtherAddress) + return a.AddToAs(m, AttrOtherAddress) } // GetFrom decodes OTHER-ADDRESS from message. func (o *OtherAddress) GetFrom(m *Message) error { a := (*MappedAddress)(o) - return a.getAs(m, AttrOtherAddress) + return a.GetFromAs(m, AttrOtherAddress) } func (o OtherAddress) String() string { return net.JoinHostPort(o.IP.String(), strconv.Itoa(o.Port)) } + +// AddTo adds RESPONSE-ORIGIN attribute to message. +func (o *ResponseOrigin) AddTo(m *Message) error { + a := (*MappedAddress)(o) + return a.AddToAs(m, AttrResponseOrigin) +} + +// GetFrom decodes RESPONSE-ORIGIN from message. +func (o *ResponseOrigin) GetFrom(m *Message) error { + a := (*MappedAddress)(o) + return a.GetFromAs(m, AttrResponseOrigin) +} + +func (o ResponseOrigin) String() string { + return net.JoinHostPort(o.IP.String(), strconv.Itoa(o.Port)) +} diff --git a/vendor/github.com/pion/stun/agent.go b/vendor/github.com/pion/stun/agent.go index 6a8a47352..f03efa3c0 100644 --- a/vendor/github.com/pion/stun/agent.go +++ b/vendor/github.com/pion/stun/agent.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package stun import ( @@ -7,13 +10,15 @@ import ( ) // NoopHandler just discards any event. -var NoopHandler Handler = func(e Event) {} +func NoopHandler() Handler { + return func(e Event) {} +} // NewAgent initializes and returns new Agent with provided handler. // If h is nil, the NoopHandler will be used. func NewAgent(h Handler) *Agent { if h == nil { - h = NoopHandler + h = NoopHandler() } a := &Agent{ transactions: make(map[transactionID]agentTransaction), diff --git a/vendor/github.com/pion/stun/appveyor.yml b/vendor/github.com/pion/stun/appveyor.yml deleted file mode 100644 index 664099d5a..000000000 --- a/vendor/github.com/pion/stun/appveyor.yml +++ /dev/null @@ -1,22 +0,0 @@ -version: "{build}" - -platform: x64 - -branches: - only: - - master - -skip_tags: true - -clone_folder: c:\gopath\src\github.com\pion\stun - -environment: - GOPATH: c:\gopath - GOVERSION: 1.12 - -install: - - go version - - go get -v -t . - -build_script: - - go test -v . diff --git a/vendor/github.com/pion/stun/attributes.go b/vendor/github.com/pion/stun/attributes.go index 7238234a7..8a1aa214c 100644 --- a/vendor/github.com/pion/stun/attributes.go +++ b/vendor/github.com/pion/stun/attributes.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package stun import ( @@ -76,8 +79,20 @@ const ( // Attributes from RFC 5780 NAT Behavior Discovery const ( - AttrOtherAddress AttrType = 0x802C // OTHER-ADDRESS - AttrChangeRequest AttrType = 0x0003 // CHANGE-REQUEST + AttrChangeRequest AttrType = 0x0003 // CHANGE-REQUEST + AttrPadding AttrType = 0x0026 // PADDING + AttrResponsePort AttrType = 0x0027 // RESPONSE-PORT + AttrCacheTimeout AttrType = 0x8027 // CACHE-TIMEOUT + AttrResponseOrigin AttrType = 0x802b // RESPONSE-ORIGIN + AttrOtherAddress AttrType = 0x802C // OTHER-ADDRESS +) + +// Attributes from RFC 3489, removed by RFC 5389, +// +// but still used by RFC5389-implementing software like Vovida.org, reTURNServer, etc. +const ( + AttrSourceAddress AttrType = 0x0004 // SOURCE-ADDRESS + AttrChangedAddress AttrType = 0x0005 // CHANGED-ADDRESS ) // Attributes from RFC 6062 TURN Extensions for TCP Allocations. @@ -95,45 +110,58 @@ const ( AttrOrigin AttrType = 0x802F ) +// Attributes from RFC 8489 STUN. +const ( + AttrMessageIntegritySHA256 AttrType = 0x001C // MESSAGE-INTEGRITY-SHA256 + AttrPasswordAlgorithm AttrType = 0x001D // PASSWORD-ALGORITHM + AttrUserhash AttrType = 0x001E // USERHASH + AttrPasswordAlgorithms AttrType = 0x8002 // PASSWORD-ALGORITHMS + AttrAlternateDomain AttrType = 0x8003 // ALTERNATE-DOMAIN +) + // Value returns uint16 representation of attribute type. func (t AttrType) Value() uint16 { return uint16(t) } -var attrNames = map[AttrType]string{ - AttrMappedAddress: "MAPPED-ADDRESS", - AttrUsername: "USERNAME", - AttrErrorCode: "ERROR-CODE", - AttrMessageIntegrity: "MESSAGE-INTEGRITY", - AttrUnknownAttributes: "UNKNOWN-ATTRIBUTES", - AttrRealm: "REALM", - AttrNonce: "NONCE", - AttrXORMappedAddress: "XOR-MAPPED-ADDRESS", - AttrSoftware: "SOFTWARE", - AttrAlternateServer: "ALTERNATE-SERVER", - AttrOtherAddress: "OTHER-ADDRESS", - AttrChangeRequest: "CHANGE-REQUEST", - AttrFingerprint: "FINGERPRINT", - AttrPriority: "PRIORITY", - AttrUseCandidate: "USE-CANDIDATE", - AttrICEControlled: "ICE-CONTROLLED", - AttrICEControlling: "ICE-CONTROLLING", - AttrChannelNumber: "CHANNEL-NUMBER", - AttrLifetime: "LIFETIME", - AttrXORPeerAddress: "XOR-PEER-ADDRESS", - AttrData: "DATA", - AttrXORRelayedAddress: "XOR-RELAYED-ADDRESS", - AttrEvenPort: "EVEN-PORT", - AttrRequestedTransport: "REQUESTED-TRANSPORT", - AttrDontFragment: "DONT-FRAGMENT", - AttrReservationToken: "RESERVATION-TOKEN", - AttrConnectionID: "CONNECTION-ID", - AttrRequestedAddressFamily: "REQUESTED-ADDRESS-FAMILY", - AttrOrigin: "ORIGIN", +func attrNames() map[AttrType]string { + return map[AttrType]string{ + AttrMappedAddress: "MAPPED-ADDRESS", + AttrUsername: "USERNAME", + AttrErrorCode: "ERROR-CODE", + AttrMessageIntegrity: "MESSAGE-INTEGRITY", + AttrUnknownAttributes: "UNKNOWN-ATTRIBUTES", + AttrRealm: "REALM", + AttrNonce: "NONCE", + AttrXORMappedAddress: "XOR-MAPPED-ADDRESS", + AttrSoftware: "SOFTWARE", + AttrAlternateServer: "ALTERNATE-SERVER", + AttrFingerprint: "FINGERPRINT", + AttrPriority: "PRIORITY", + AttrUseCandidate: "USE-CANDIDATE", + AttrICEControlled: "ICE-CONTROLLED", + AttrICEControlling: "ICE-CONTROLLING", + AttrChannelNumber: "CHANNEL-NUMBER", + AttrLifetime: "LIFETIME", + AttrXORPeerAddress: "XOR-PEER-ADDRESS", + AttrData: "DATA", + AttrXORRelayedAddress: "XOR-RELAYED-ADDRESS", + AttrEvenPort: "EVEN-PORT", + AttrRequestedTransport: "REQUESTED-TRANSPORT", + AttrDontFragment: "DONT-FRAGMENT", + AttrReservationToken: "RESERVATION-TOKEN", + AttrConnectionID: "CONNECTION-ID", + AttrRequestedAddressFamily: "REQUESTED-ADDRESS-FAMILY", + AttrMessageIntegritySHA256: "MESSAGE-INTEGRITY-SHA256", + AttrPasswordAlgorithm: "PASSWORD-ALGORITHM", + AttrUserhash: "USERHASH", + AttrPasswordAlgorithms: "PASSWORD-ALGORITHMS", + AttrAlternateDomain: "ALTERNATE-DOMAIN", + } } func (t AttrType) String() string { - s, ok := attrNames[t] + s, ok := attrNames()[t] if !ok { // Just return hex representation of unknown attribute type. return fmt.Sprintf("0x%x", uint16(t)) @@ -219,8 +247,8 @@ func nearestPaddedValueLength(l int) int { // type value, it also translates it to the new value to enable backward // compatibility. (See: https://github.com/pion/stun/issues/21) func compatAttrType(val uint16) AttrType { - if val == 0x8020 { - return AttrXORMappedAddress // new: 0x0020 + if val == 0x8020 { // draft-ietf-behave-rfc3489bis-02, MS-TURN + return AttrXORMappedAddress // new: 0x0020 (from draft-ietf-behave-rfc3489bis-03 on) } return AttrType(val) } diff --git a/vendor/github.com/pion/stun/attributes_debug.go b/vendor/github.com/pion/stun/attributes_debug.go index 7bf09af7c..836d79f13 100644 --- a/vendor/github.com/pion/stun/attributes_debug.go +++ b/vendor/github.com/pion/stun/attributes_debug.go @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +//go:build debug // +build debug package stun diff --git a/vendor/github.com/pion/stun/checks.go b/vendor/github.com/pion/stun/checks.go index a7609973a..6b678a06b 100644 --- a/vendor/github.com/pion/stun/checks.go +++ b/vendor/github.com/pion/stun/checks.go @@ -1,8 +1,16 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +//go:build !debug // +build !debug package stun -import "github.com/pion/stun/internal/hmac" +import ( + "errors" + + "github.com/pion/stun/internal/hmac" +) // CheckSize returns ErrAttrSizeInvalid if got is not equal to expected. func CheckSize(_ AttrType, got, expected int) error { @@ -28,7 +36,7 @@ func checkFingerprint(got, expected uint32) error { // IsAttrSizeInvalid returns true if error means that attribute size is invalid. func IsAttrSizeInvalid(err error) bool { - return err == ErrAttributeSizeInvalid + return errors.Is(err, ErrAttributeSizeInvalid) } // CheckOverflow returns ErrAttributeSizeOverflow if got is bigger that max. @@ -41,5 +49,5 @@ func CheckOverflow(_ AttrType, got, max int) error { // IsAttrSizeOverflow returns true if error means that attribute size is too big. func IsAttrSizeOverflow(err error) bool { - return err == ErrAttributeSizeOverflow + return errors.Is(err, ErrAttributeSizeOverflow) } diff --git a/vendor/github.com/pion/stun/checks_debug.go b/vendor/github.com/pion/stun/checks_debug.go index 955f555b0..0b5c67c83 100644 --- a/vendor/github.com/pion/stun/checks_debug.go +++ b/vendor/github.com/pion/stun/checks_debug.go @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +//go:build debug // +build debug package stun diff --git a/vendor/github.com/pion/stun/client.go b/vendor/github.com/pion/stun/client.go index 62a0b6eb0..5d02e51dc 100644 --- a/vendor/github.com/pion/stun/client.go +++ b/vendor/github.com/pion/stun/client.go @@ -1,17 +1,29 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package stun import ( + "crypto/tls" "errors" "fmt" "io" "log" "net" "runtime" + "strconv" "sync" "sync/atomic" "time" + + "github.com/pion/dtls/v2" + "github.com/pion/transport/v2" + "github.com/pion/transport/v2/stdnet" ) +// ErrUnsupportedURI is an error thrown if the user passes an unsupported STUN or TURN URI +var ErrUnsupportedURI = fmt.Errorf("invalid schema or transport") + // Dial connects to the address on the named network and then // initializes Client on that connection, returning error if any. func Dial(network, address string) (*Client, error) { @@ -22,6 +34,77 @@ func Dial(network, address string) (*Client, error) { return NewClient(conn) } +// DialConfig is used to pass configuration to DialURI() +type DialConfig struct { + DTLSConfig dtls.Config + TLSConfig tls.Config + + Net transport.Net +} + +// DialURI connect to the STUN/TURN URI and then +// initializes Client on that connection, returning error if any. +func DialURI(uri *URI, cfg *DialConfig) (*Client, error) { + var conn Connection + var err error + + nw := cfg.Net + if nw == nil { + nw, err = stdnet.NewNet() + if err != nil { + return nil, fmt.Errorf("failed to create net: %w", err) + } + } + + addr := net.JoinHostPort(uri.Host, strconv.Itoa(uri.Port)) + + switch { + case uri.Scheme == SchemeTypeSTUN: + if conn, err = nw.Dial("udp", addr); err != nil { + return nil, fmt.Errorf("failed to listen: %w", err) + } + + case uri.Scheme == SchemeTypeTURN: + network := "udp" //nolint:goconst + if uri.Proto == ProtoTypeTCP { + network = "tcp" //nolint:goconst + } + + if conn, err = nw.Dial(network, addr); err != nil { + return nil, fmt.Errorf("failed to dial: %w", err) + } + + case uri.Scheme == SchemeTypeTURNS && uri.Proto == ProtoTypeUDP: + dtlsCfg := cfg.DTLSConfig // Copy + dtlsCfg.ServerName = uri.Host + + udpConn, err := nw.Dial("udp", addr) + if err != nil { + return nil, fmt.Errorf("failed to dial: %w", err) + } + + if conn, err = dtls.Client(udpConn, &dtlsCfg); err != nil { + return nil, fmt.Errorf("failed to connect to '%s': %w", addr, err) + } + + case (uri.Scheme == SchemeTypeTURNS || uri.Scheme == SchemeTypeSTUNS) && uri.Proto == ProtoTypeTCP: + tlsCfg := cfg.TLSConfig //nolint:govet + tlsCfg.ServerName = uri.Host + + tcpConn, err := nw.Dial("tcp", addr) + if err != nil { + return nil, fmt.Errorf("failed to dial: %w", err) + } + + conn = tls.Client(tcpConn, &tlsCfg) + + default: + return nil, ErrUnsupportedURI + } + + return NewClient(conn) +} + // ErrNoConnection means that ClientOptions.Connection is nil. var ErrNoConnection = errors.New("no connection provided") @@ -79,8 +162,10 @@ func WithCollector(coll Collector) ClientOption { // WithNoConnClose prevents client from closing underlying connection when // the Close() method is called. -var WithNoConnClose ClientOption = func(c *Client) { - c.closeConn = false +func WithNoConnClose() ClientOption { + return func(c *Client) { + c.closeConn = false + } } // WithNoRetransmit disables retransmissions and sets RTO to @@ -116,7 +201,7 @@ func NewClient(conn Connection, options ...ClientOption) (*Client, error) { c := &Client{ close: make(chan struct{}), c: conn, - clock: systemClock, + clock: systemClock(), rto: int64(defaultRTO), rtoRate: defaultTimeoutRate, t: make(map[transactionID]*clientTransaction, 100), @@ -157,7 +242,7 @@ func clientFinalizer(c *Client) { return } err := c.Close() - if err == ErrClientClosed { + if errors.Is(err, ErrClientClosed) { return } if err == nil { @@ -225,7 +310,7 @@ func (t *clientTransaction) handle(e Event) { } } -var clientTransactionPool = &sync.Pool{ +var clientTransactionPool = &sync.Pool{ //nolint:gochecknoglobals New: func() interface{} { return &clientTransaction{ raw: make([]byte, 1500), @@ -234,7 +319,7 @@ var clientTransactionPool = &sync.Pool{ } func acquireClientTransaction() *clientTransaction { - return clientTransactionPool.Get().(*clientTransaction) + return clientTransactionPool.Get().(*clientTransaction) //nolint:forcetypeassert } func putClientTransaction(t *clientTransaction) { @@ -275,7 +360,9 @@ type systemClockService struct{} func (systemClockService) Now() time.Time { return time.Now() } -var systemClock = systemClockService{} +func systemClock() systemClockService { + return systemClockService{} +} // SetRTO sets current RTO value. func (c *Client) SetRTO(rto time.Duration) { @@ -284,6 +371,8 @@ func (c *Client) SetRTO(rto time.Duration) { // StopErr occurs when Client fails to stop transaction while // processing error. +// +//nolint:errname type StopErr struct { Err error // value returned by Stop() Cause error // error that caused Stop() call @@ -294,6 +383,8 @@ func (e StopErr) Error() string { } // CloseErr indicates client close failure. +// +//nolint:errname type CloseErr struct { AgentErr error ConnectionErr error @@ -301,7 +392,7 @@ type CloseErr struct { func sprintErr(err error) string { if err == nil { - return "" + return "" //nolint:goconst } return err.Error() } @@ -322,7 +413,7 @@ func (c *Client) readUntilClosed() { } _, err := m.ReadFrom(c.c) if err == nil { - if pErr := c.a.Process(m); pErr == ErrAgentClosed { + if pErr := c.a.Process(m); errors.Is(pErr, ErrAgentClosed) { return } } @@ -330,10 +421,10 @@ func (c *Client) readUntilClosed() { } func closedOrPanic(err error) { - if err == nil || err == ErrAgentClosed { + if err == nil || errors.Is(err, ErrAgentClosed) { return } - panic(err) // nolint + panic(err) //nolint } type tickerCollector struct { @@ -425,7 +516,7 @@ type callbackWaitHandler struct { func (s *callbackWaitHandler) HandleEvent(e Event) { s.cond.L.Lock() if s.callback == nil { - panic("s.callback is nil") // nolint + panic("s.callback is nil") //nolint } s.callback(e) s.processed = true @@ -445,7 +536,7 @@ func (s *callbackWaitHandler) wait() { func (s *callbackWaitHandler) setCallback(f func(event Event)) { if f == nil { - panic("f is nil") // nolint + panic("f is nil") //nolint } s.cond.L.Lock() s.callback = f @@ -455,7 +546,7 @@ func (s *callbackWaitHandler) setCallback(f func(event Event)) { s.cond.L.Unlock() } -var callbackWaitHandlerPool = sync.Pool{ +var callbackWaitHandlerPool = sync.Pool{ //nolint:gochecknoglobals New: func() interface{} { return &callbackWaitHandler{ cond: sync.NewCond(new(sync.Mutex)), @@ -485,7 +576,7 @@ func (c *Client) Do(m *Message, f func(Event)) error { if f == nil { return c.Indicate(m) } - h := callbackWaitHandlerPool.Get().(*callbackWaitHandler) + h := callbackWaitHandlerPool.Get().(*callbackWaitHandler) //nolint:forcetypeassert h.setCallback(f) defer func() { callbackWaitHandlerPool.Put(h) @@ -509,7 +600,7 @@ type buffer struct { buf []byte } -var bufferPool = &sync.Pool{ +var bufferPool = &sync.Pool{ //nolint:gochecknoglobals New: func() interface{} { return &buffer{buf: make([]byte, 2048)} }, @@ -527,7 +618,7 @@ func (c *Client) handleAgentCallback(e Event) { } c.mux.Unlock() if !found { - if c.handler != nil && e.Error != ErrTransactionStopped { + if c.handler != nil && !errors.Is(e.Error, ErrTransactionStopped) { c.handler(e) } // Ignoring. @@ -541,7 +632,7 @@ func (c *Client) handleAgentCallback(e Event) { } // Doing re-transmission. t.attempt++ - b := bufferPool.Get().(*buffer) + b := bufferPool.Get().(*buffer) //nolint:forcetypeassert b.buf = b.buf[:copy(b.buf[:cap(b.buf)], t.raw)] defer bufferPool.Put(b) var ( diff --git a/vendor/github.com/pion/udp/codecov.yml b/vendor/github.com/pion/stun/codecov.yml similarity index 78% rename from vendor/github.com/pion/udp/codecov.yml rename to vendor/github.com/pion/stun/codecov.yml index 085200a48..263e4d45c 100644 --- a/vendor/github.com/pion/udp/codecov.yml +++ b/vendor/github.com/pion/stun/codecov.yml @@ -3,6 +3,8 @@ # # It is automatically copied from https://github.com/pion/.goassets repository. # +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT coverage: status: diff --git a/vendor/github.com/pion/stun/errorcode.go b/vendor/github.com/pion/stun/errorcode.go index 8095048cd..c852eed69 100644 --- a/vendor/github.com/pion/stun/errorcode.go +++ b/vendor/github.com/pion/stun/errorcode.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package stun import ( @@ -29,7 +32,7 @@ const ( // AddTo adds ERROR-CODE to m. func (c ErrorCodeAttribute) AddTo(m *Message) error { - value := make([]byte, 0, errorCodeReasonMaxB) + value := make([]byte, 0, errorCodeReasonStart+errorCodeReasonMaxB) if err := CheckOverflow(AttrErrorCode, len(c.Reason)+errorCodeReasonStart, errorCodeReasonMaxB+errorCodeReasonStart, @@ -131,6 +134,7 @@ const ( CodePeerAddrFamilyMismatch ErrorCode = 443 // Peer Address Family Mismatch ) +//nolint:gochecknoglobals var errorReasons = map[ErrorCode][]byte{ CodeTryAlternate: []byte("Try Alternate"), CodeBadRequest: []byte("Bad Request"), diff --git a/vendor/github.com/pion/stun/errors.go b/vendor/github.com/pion/stun/errors.go index 029c9e426..d5f59edd6 100644 --- a/vendor/github.com/pion/stun/errors.go +++ b/vendor/github.com/pion/stun/errors.go @@ -1,8 +1,13 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package stun import "errors" // DecodeErr records an error and place when it is occurred. +// +//nolint:errname type DecodeErr struct { Place DecodeErrPlace Message string @@ -50,7 +55,6 @@ func newDecodeErr(parent, children, message string) *DecodeErr { } } -// TODO(ar): rewrite errors to be more precise. func newAttrDecodeErr(children, message string) *DecodeErr { return newDecodeErr("attribute", children, message) } diff --git a/vendor/github.com/pion/stun/fingerprint.go b/vendor/github.com/pion/stun/fingerprint.go index aef80a206..b4126d267 100644 --- a/vendor/github.com/pion/stun/fingerprint.go +++ b/vendor/github.com/pion/stun/fingerprint.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package stun import ( @@ -17,9 +20,9 @@ var ErrFingerprintMismatch = errors.New("fingerprint check failed") // // Example: // -// m := New() -// Fingerprint.AddTo(m) -var Fingerprint FingerprintAttr +// m := New() +// Fingerprint.AddTo(m) +var Fingerprint FingerprintAttr //nolint:gochecknoglobals const ( fingerprintXORValue uint32 = 0x5354554e //nolint:staticcheck diff --git a/vendor/github.com/pion/stun/fingerprint_debug.go b/vendor/github.com/pion/stun/fingerprint_debug.go index 6da074cd8..0e3471db8 100644 --- a/vendor/github.com/pion/stun/fingerprint_debug.go +++ b/vendor/github.com/pion/stun/fingerprint_debug.go @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +//go:build debug // +build debug package stun diff --git a/vendor/github.com/pion/stun/fuzz.go b/vendor/github.com/pion/stun/fuzz.go deleted file mode 100644 index debfa8d1c..000000000 --- a/vendor/github.com/pion/stun/fuzz.go +++ /dev/null @@ -1,140 +0,0 @@ -// +build gofuzz - -package stun - -import ( - "encoding/binary" - "fmt" -) - -var ( - m = New() -) - -// FuzzMessage is go-fuzz endpoint for message. -func FuzzMessage(data []byte) int { - m.Reset() - // fuzzer dont know about cookies - binary.BigEndian.PutUint32(data[4:8], magicCookie) - // trying to read data as message - if _, err := m.Write(data); err != nil { - return 0 - } - m2 := New() - if _, err := m2.Write(m.Raw); err != nil { - panic(err) // nolint - } - if m2.TransactionID != m.TransactionID { - panic("transaction ID mismatch") // nolint - } - if m2.Type != m.Type { - panic("type missmatch") // nolint - } - if len(m2.Attributes) != len(m.Attributes) { - panic("attributes length missmatch") // nolint - } - return 1 -} - -// FuzzType is go-fuzz endpoint for message type. -func FuzzType(data []byte) int { - t := MessageType{} - vt, _ := binary.Uvarint(data) - v := uint16(vt) & 0x1fff // first 3 bits are empty - t.ReadValue(v) - v2 := t.Value() - if v != v2 { - panic("v != v2") // nolint - } - t2 := MessageType{} - t2.ReadValue(v2) - if t2 != t { - panic("t2 != t") // nolint - } - return 0 -} - -type attr interface { - Getter - Setter -} - -type attrs []struct { - g attr - t AttrType -} - -func (a attrs) pick(v byte) struct { - g attr - t AttrType -} { - idx := int(v) % len(a) - return a[idx] -} - -func FuzzSetters(data []byte) int { - var ( - m1 = &Message{ - Raw: make([]byte, 0, 2048), - } - m2 = &Message{ - Raw: make([]byte, 0, 2048), - } - m3 = &Message{ - Raw: make([]byte, 0, 2048), - } - ) - attributes := attrs{ - {new(Realm), AttrRealm}, - {new(XORMappedAddress), AttrXORMappedAddress}, - {new(Nonce), AttrNonce}, - {new(Software), AttrSoftware}, - {new(AlternateServer), AttrAlternateServer}, - {new(ErrorCodeAttribute), AttrErrorCode}, - {new(UnknownAttributes), AttrUnknownAttributes}, - {new(Username), AttrUsername}, - {new(MappedAddress), AttrMappedAddress}, - {new(Realm), AttrRealm}, - } - var firstByte = byte(0) - if len(data) > 0 { - firstByte = data[0] - } - a := attributes.pick(firstByte) - value := data - if len(data) > 1 { - value = value[1:] - } - m1.WriteHeader() - m1.Add(a.t, value) - err := a.g.GetFrom(m1) - if err == ErrAttributeNotFound { - fmt.Println("unexpected 404") // nolint - panic(err) // nolint - } - if err != nil { - return 1 - } - m2.WriteHeader() - if err = a.g.AddTo(m2); err != nil { - // We allow decoding some text attributes - // when their length is too big, but - // not encoding. - if !IsAttrSizeOverflow(err) { - panic(err) // nolint - } - return 1 - } - m3.WriteHeader() - v, err := m2.Get(a.t) - if err != nil { - panic(err) // nolint - } - m3.Add(a.t, v) - - if !m2.Equal(m3) { - fmt.Println(m2, "not equal", m3) // nolint - panic("not equal") // nolint - } - return 1 -} diff --git a/vendor/github.com/pion/stun/go.test.sh b/vendor/github.com/pion/stun/go.test.sh deleted file mode 100644 index 25234e4b3..000000000 --- a/vendor/github.com/pion/stun/go.test.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -set -e -touch coverage.txt - -# test fuzz inputs -go test -tags gofuzz -run TestFuzz -v . - -# quick-test without -race -go test ./... - -# test with "debug" tag -go test -tags debug ./... - -# test concurrency -go test -race -cpu=1,2,4 -run TestClient_DoConcurrent - -for d in $(go list ./... | grep -v vendor); do - go test -race -coverprofile=profile.out -covermode=atomic "$d" - if [[ -f profile.out ]]; then - cat profile.out >> coverage.txt - rm profile.out - fi -done diff --git a/vendor/github.com/pion/stun/helpers.go b/vendor/github.com/pion/stun/helpers.go index 158a51dd4..d4056503c 100644 --- a/vendor/github.com/pion/stun/helpers.go +++ b/vendor/github.com/pion/stun/helpers.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package stun // Interfaces that are implemented by message attributes, shorthands for them, @@ -21,15 +24,16 @@ type ( // first error. To prevent allocations, pass pointers to values. // // Example: -// var ( -// t = BindingRequest -// username = NewUsername("username") -// nonce = NewNonce("nonce") -// realm = NewRealm("example.org") -// ) -// m := new(Message) -// m.Build(t, username, nonce, realm) // 4 allocations -// m.Build(&t, &username, &nonce, &realm) // 0 allocations +// +// var ( +// t = BindingRequest +// username = NewUsername("username") +// nonce = NewNonce("nonce") +// realm = NewRealm("example.org") +// ) +// m := new(Message) +// m.Build(t, username, nonce, realm) // 4 allocations +// m.Build(&t, &username, &nonce, &realm) // 0 allocations // // See BenchmarkBuildOverhead. func (m *Message) Build(setters ...Setter) error { @@ -67,7 +71,7 @@ func (m *Message) Parse(getters ...Getter) error { func MustBuild(setters ...Setter) *Message { m, err := Build(setters...) if err != nil { - panic(err) // nolint + panic(err) //nolint } return m } diff --git a/vendor/github.com/pion/stun/integrity.go b/vendor/github.com/pion/stun/integrity.go index 39b0d5073..0fee0b075 100644 --- a/vendor/github.com/pion/stun/integrity.go +++ b/vendor/github.com/pion/stun/integrity.go @@ -1,8 +1,11 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package stun -import ( - "crypto/md5" // #nosec - "crypto/sha1" // #nosec +import ( //nolint:gci + "crypto/md5" //nolint:gosec + "crypto/sha1" //nolint:gosec "errors" "fmt" "strings" @@ -17,8 +20,7 @@ const credentialsSep = ":" // credentials. Password, username, and realm must be SASL-prepared. func NewLongTermIntegrity(username, realm, password string) MessageIntegrity { k := strings.Join([]string{username, realm, password}, credentialsSep) - // #nosec - h := md5.New() + h := md5.New() //nolint:gosec fmt.Fprint(h, k) return MessageIntegrity(h.Sum(nil)) } diff --git a/vendor/github.com/pion/stun/integrity_debug.go b/vendor/github.com/pion/stun/integrity_debug.go index 6b8a30307..27fd0e275 100644 --- a/vendor/github.com/pion/stun/integrity_debug.go +++ b/vendor/github.com/pion/stun/integrity_debug.go @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +//go:build debug // +build debug package stun diff --git a/vendor/github.com/pion/stun/internal/hmac/hmac.go b/vendor/github.com/pion/stun/internal/hmac/hmac.go index 801ece67a..b4db5c996 100644 --- a/vendor/github.com/pion/stun/internal/hmac/hmac.go +++ b/vendor/github.com/pion/stun/internal/hmac/hmac.go @@ -1,6 +1,5 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. +// SPDX-FileCopyrightText: 2009 The Go Authors. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause /* Package hmac implements the Keyed-Hash Message Authentication Code (HMAC) as @@ -34,19 +33,37 @@ import ( // opad = 0x5c byte repeated for key length // hmac = H([key ^ opad] H([key ^ ipad] text)) +// Marshalable is the combination of encoding.BinaryMarshaler and +// encoding.BinaryUnmarshaler. Their method definitions are repeated here to +// avoid a dependency on the encoding package. +type marshalable interface { + MarshalBinary() ([]byte, error) + UnmarshalBinary([]byte) error +} + type hmac struct { - size int - blocksize int opad, ipad []byte outer, inner hash.Hash + + // If marshaled is true, then opad and ipad do not contain a padded + // copy of the key, but rather the marshaled state of outer/inner after + // opad/ipad has been fed into it. + marshaled bool } func (h *hmac) Sum(in []byte) []byte { origLen := len(in) in = h.inner.Sum(in) - h.outer.Reset() - h.outer.Write(h.opad) - h.outer.Write(in[origLen:]) + + if h.marshaled { + if err := h.outer.(marshalable).UnmarshalBinary(h.opad); err != nil { //nolint:forcetypeassert + panic(err) //nolint + } + } else { + h.outer.Reset() + h.outer.Write(h.opad) //nolint:errcheck,gosec + } + h.outer.Write(in[origLen:]) //nolint:errcheck,gosec return h.outer.Sum(in[:origLen]) } @@ -54,13 +71,51 @@ func (h *hmac) Write(p []byte) (n int, err error) { return h.inner.Write(p) } -func (h *hmac) Size() int { return h.size } - -func (h *hmac) BlockSize() int { return h.blocksize } +func (h *hmac) Size() int { return h.outer.Size() } +func (h *hmac) BlockSize() int { return h.inner.BlockSize() } func (h *hmac) Reset() { + if h.marshaled { + if err := h.inner.(marshalable).UnmarshalBinary(h.ipad); err != nil { //nolint:forcetypeassert + panic(err) //nolint + } + return + } + h.inner.Reset() - h.inner.Write(h.ipad) + h.inner.Write(h.ipad) //nolint:errcheck,gosec + + // If the underlying hash is marshalable, we can save some time by + // saving a copy of the hash state now, and restoring it on future + // calls to Reset and Sum instead of writing ipad/opad every time. + // + // If either hash is unmarshalable for whatever reason, + // it's safe to bail out here. + marshalableInner, innerOK := h.inner.(marshalable) + if !innerOK { + return + } + marshalableOuter, outerOK := h.outer.(marshalable) + if !outerOK { + return + } + + imarshal, err := marshalableInner.MarshalBinary() + if err != nil { + return + } + + h.outer.Reset() + h.outer.Write(h.opad) //nolint:errcheck,gosec + omarshal, err := marshalableOuter.MarshalBinary() + if err != nil { + return + } + + // Marshaling succeeded; save the marshaled state for later + h.ipad = imarshal + h.opad = omarshal + h.marshaled = true } // New returns a new HMAC hash using the given hash.Hash type and key. @@ -71,13 +126,12 @@ func New(h func() hash.Hash, key []byte) hash.Hash { hm := new(hmac) hm.outer = h() hm.inner = h() - hm.size = hm.inner.Size() - hm.blocksize = hm.inner.BlockSize() - hm.ipad = make([]byte, hm.blocksize) - hm.opad = make([]byte, hm.blocksize) - if len(key) > hm.blocksize { + blocksize := hm.inner.BlockSize() + hm.ipad = make([]byte, blocksize) + hm.opad = make([]byte, blocksize) + if len(key) > blocksize { // If key is too big, hash it. - hm.outer.Write(key) + hm.outer.Write(key) //nolint:errcheck,gosec key = hm.outer.Sum(nil) } copy(hm.ipad, key) @@ -88,7 +142,8 @@ func New(h func() hash.Hash, key []byte) hash.Hash { for i := range hm.opad { hm.opad[i] ^= 0x5c } - hm.inner.Write(hm.ipad) + hm.inner.Write(hm.ipad) //nolint:errcheck,gosec + return hm } diff --git a/vendor/github.com/pion/stun/internal/hmac/pool.go b/vendor/github.com/pion/stun/internal/hmac/pool.go index 4db61e540..d2ac14afa 100644 --- a/vendor/github.com/pion/stun/internal/hmac/pool.go +++ b/vendor/github.com/pion/stun/internal/hmac/pool.go @@ -1,29 +1,27 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package hmac -import ( - "crypto/sha1" +import ( //nolint:gci + "crypto/sha1" //nolint:gosec "crypto/sha256" "hash" "sync" ) -// setZeroes sets all bytes from b to zeroes. -// -// See https://github.com/golang/go/issues/5373 -func setZeroes(b []byte) { - for i := range b { - b[i] = 0 - } -} - func (h *hmac) resetTo(key []byte) { h.outer.Reset() h.inner.Reset() - setZeroes(h.ipad) - setZeroes(h.opad) - if len(key) > h.blocksize { + blocksize := h.inner.BlockSize() + + // Reset size and zero of ipad and opad. + h.ipad = append(h.ipad[:0], make([]byte, blocksize)...) + h.opad = append(h.opad[:0], make([]byte, blocksize)...) + + if len(key) > blocksize { // If key is too big, hash it. - h.outer.Write(key) + h.outer.Write(key) //nolint:errcheck,gosec key = h.outer.Sum(nil) } copy(h.ipad, key) @@ -34,10 +32,12 @@ func (h *hmac) resetTo(key []byte) { for i := range h.opad { h.opad[i] ^= 0x5c } - h.inner.Write(h.ipad) + h.inner.Write(h.ipad) //nolint:errcheck,gosec + + h.marshaled = false } -var hmacSHA1Pool = &sync.Pool{ +var hmacSHA1Pool = &sync.Pool{ //nolint:gochecknoglobals New: func() interface{} { h := New(sha1.New, make([]byte, sha1.BlockSize)) return h @@ -46,7 +46,7 @@ var hmacSHA1Pool = &sync.Pool{ // AcquireSHA1 returns new HMAC from pool. func AcquireSHA1(key []byte) hash.Hash { - h := hmacSHA1Pool.Get().(*hmac) + h := hmacSHA1Pool.Get().(*hmac) //nolint:forcetypeassert assertHMACSize(h, sha1.Size, sha1.BlockSize) h.resetTo(key) return h @@ -54,12 +54,12 @@ func AcquireSHA1(key []byte) hash.Hash { // PutSHA1 puts h to pool. func PutSHA1(h hash.Hash) { - hm := h.(*hmac) + hm := h.(*hmac) //nolint:forcetypeassert assertHMACSize(hm, sha1.Size, sha1.BlockSize) hmacSHA1Pool.Put(hm) } -var hmacSHA256Pool = &sync.Pool{ +var hmacSHA256Pool = &sync.Pool{ //nolint:gochecknoglobals New: func() interface{} { h := New(sha256.New, make([]byte, sha256.BlockSize)) return h @@ -68,7 +68,7 @@ var hmacSHA256Pool = &sync.Pool{ // AcquireSHA256 returns new HMAC from SHA256 pool. func AcquireSHA256(key []byte) hash.Hash { - h := hmacSHA256Pool.Get().(*hmac) + h := hmacSHA256Pool.Get().(*hmac) //nolint:forcetypeassert assertHMACSize(h, sha256.Size, sha256.BlockSize) h.resetTo(key) return h @@ -76,7 +76,7 @@ func AcquireSHA256(key []byte) hash.Hash { // PutSHA256 puts h to SHA256 pool. func PutSHA256(h hash.Hash) { - hm := h.(*hmac) + hm := h.(*hmac) //nolint:forcetypeassert assertHMACSize(hm, sha256.Size, sha256.BlockSize) hmacSHA256Pool.Put(hm) } @@ -85,8 +85,8 @@ func PutSHA256(h hash.Hash) { // // Put and Acquire functions are internal functions to project, so // checking it via such assert is optimal. -func assertHMACSize(h *hmac, size, blocksize int) { - if h.size != size || h.blocksize != blocksize { - panic("BUG: hmac size invalid") // nolint +func assertHMACSize(h *hmac, size, blocksize int) { //nolint:unparam + if h.Size() != size || h.BlockSize() != blocksize { + panic("BUG: hmac size invalid") //nolint } } diff --git a/vendor/github.com/pion/stun/internal/hmac/vendor.sh b/vendor/github.com/pion/stun/internal/hmac/vendor.sh index 83a2b32d3..190d2b9b7 100644 --- a/vendor/github.com/pion/stun/internal/hmac/vendor.sh +++ b/vendor/github.com/pion/stun/internal/hmac/vendor.sh @@ -1,4 +1,7 @@ -#!/bin/bash +#!/bin/env bash + +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + cp -v $GOROOT/src/crypto/hmac/{hmac,hmac_test}.go . git diff {hmac,hmac_test}.go - diff --git a/vendor/github.com/pion/stun/message.go b/vendor/github.com/pion/stun/message.go index 381923530..6a828d68b 100644 --- a/vendor/github.com/pion/stun/message.go +++ b/vendor/github.com/pion/stun/message.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package stun import ( @@ -63,7 +66,7 @@ func Decode(data []byte, m *Message) error { // buffering to enable zero-allocation encoding and decoding, // so there are some usage constraints: // -// Message, its fields, results of m.Get or any attribute a.GetFrom +// Message, its fields, results of m.Get or any attribute a.GetFrom // are valid only until Message.Raw is not modified. type Message struct { Type MessageType @@ -73,6 +76,32 @@ type Message struct { Raw []byte } +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (m Message) MarshalBinary() (data []byte, err error) { + // We can't return m.Raw, allocation is expected by implicit interface + // contract induced by other implementations. + b := make([]byte, len(m.Raw)) + copy(b, m.Raw) + return b, nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +func (m *Message) UnmarshalBinary(data []byte) error { + // We can't retain data, copy is expected by interface contract. + m.Raw = append(m.Raw[:0], data...) + return m.Decode() +} + +// GobEncode implements the gob.GobEncoder interface. +func (m Message) GobEncode() ([]byte, error) { + return m.MarshalBinary() +} + +// GobDecode implements the gob.GobDecoder interface. +func (m *Message) GobDecode(data []byte) error { + return m.UnmarshalBinary(data) +} + // AddTo sets b.TransactionID to m.TransactionID. // // Implements Setter to aid in crafting responses. @@ -94,7 +123,11 @@ func (m *Message) NewTransactionID() error { func (m *Message) String() string { tID := base64.StdEncoding.EncodeToString(m.TransactionID[:]) - return fmt.Sprintf("%s l=%d attrs=%d id=%s", m.Type, m.Length, len(m.Attributes), tID) + aInfo := "" + for k, a := range m.Attributes { + aInfo += fmt.Sprintf("attr%d=%s ", k, a.Type) + } + return fmt.Sprintf("%s l=%d attrs=%d id=%s, %s", m.Type, m.Length, len(m.Attributes), tID, aInfo) } // Reset resets Message, attributes and underlying buffer length. @@ -384,7 +417,6 @@ func (m *Message) Write(tBuf []byte) (int, error) { // CloneTo clones m to b securing any further m mutations. func (m *Message) CloneTo(b *Message) error { - // TODO(ar): implement low-level copy. b.Raw = append(b.Raw[:0], m.Raw...) return b.Decode() } @@ -403,11 +435,11 @@ const ( // Common STUN message types. var ( // Binding request message type. - BindingRequest = NewType(MethodBinding, ClassRequest) + BindingRequest = NewType(MethodBinding, ClassRequest) //nolint:gochecknoglobals // Binding success response message type - BindingSuccess = NewType(MethodBinding, ClassSuccessResponse) + BindingSuccess = NewType(MethodBinding, ClassSuccessResponse) //nolint:gochecknoglobals // Binding error response message type. - BindingError = NewType(MethodBinding, ClassErrorResponse) + BindingError = NewType(MethodBinding, ClassErrorResponse) //nolint:gochecknoglobals ) func (c MessageClass) String() string { @@ -421,7 +453,7 @@ func (c MessageClass) String() string { case ClassErrorResponse: return "error response" default: - panic("unknown message class") // nolint: never happens unless wrongly casted + panic("unknown message class") //nolint } } @@ -446,23 +478,25 @@ const ( MethodConnectionAttempt Method = 0x000c ) -var methodName = map[Method]string{ - MethodBinding: "Binding", - MethodAllocate: "Allocate", - MethodRefresh: "Refresh", - MethodSend: "Send", - MethodData: "Data", - MethodCreatePermission: "CreatePermission", - MethodChannelBind: "ChannelBind", +func methodName() map[Method]string { + return map[Method]string{ + MethodBinding: "Binding", + MethodAllocate: "Allocate", + MethodRefresh: "Refresh", + MethodSend: "Send", + MethodData: "Data", + MethodCreatePermission: "CreatePermission", + MethodChannelBind: "ChannelBind", - // RFC 6062. - MethodConnect: "Connect", - MethodConnectionBind: "ConnectionBind", - MethodConnectionAttempt: "ConnectionAttempt", + // RFC 6062. + MethodConnect: "Connect", + MethodConnectionBind: "ConnectionBind", + MethodConnectionAttempt: "ConnectionAttempt", + } } func (m Method) String() string { - s, ok := methodName[m] + s, ok := methodName()[m] if !ok { // Falling back to hex representation. s = fmt.Sprintf("0x%x", uint16(m)) diff --git a/vendor/github.com/pion/stun/renovate.json b/vendor/github.com/pion/stun/renovate.json index 4400fd9b2..f1bb98c6a 100644 --- a/vendor/github.com/pion/stun/renovate.json +++ b/vendor/github.com/pion/stun/renovate.json @@ -1,15 +1,6 @@ { + "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:base" - ], - "postUpdateOptions": [ - "gomodTidy" - ], - "commitBody": "Generated by renovateBot", - "packageRules": [ - { - "packagePatterns": ["^golang.org/x/"], - "schedule": ["on the first day of the month"] - } + "github>pion/renovate-config" ] } diff --git a/vendor/github.com/pion/stun/stun.go b/vendor/github.com/pion/stun/stun.go index 9f804d270..3a7954fd0 100644 --- a/vendor/github.com/pion/stun/stun.go +++ b/vendor/github.com/pion/stun/stun.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package stun implements Session Traversal Utilities for NAT (STUN) RFC 5389. // // The stun package is intended to use by package that implements extension @@ -17,12 +20,12 @@ import ( ) // bin is shorthand to binary.BigEndian. -var bin = binary.BigEndian +var bin = binary.BigEndian //nolint:gochecknoglobals func readFullOrPanic(r io.Reader, v []byte) int { n, err := io.ReadFull(r, v) if err != nil { - panic(err) // nolint + panic(err) //nolint } return n } @@ -30,7 +33,7 @@ func readFullOrPanic(r io.Reader, v []byte) int { func writeOrPanic(w io.Writer, v []byte) int { n, err := w.Write(v) if err != nil { - panic(err) // nolint + panic(err) //nolint } return n } @@ -48,4 +51,4 @@ func (transactionIDSetter) AddTo(m *Message) error { } // TransactionID is Setter for m.TransactionID. -var TransactionID Setter = transactionIDSetter{} +var TransactionID Setter = transactionIDSetter{} //nolint:gochecknoglobals diff --git a/vendor/github.com/pion/stun/textattrs.go b/vendor/github.com/pion/stun/textattrs.go index efdfbd957..a98915a6b 100644 --- a/vendor/github.com/pion/stun/textattrs.go +++ b/vendor/github.com/pion/stun/textattrs.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package stun // NewUsername returns Username with provided value. diff --git a/vendor/github.com/pion/stun/uattrs.go b/vendor/github.com/pion/stun/uattrs.go index 238d32d10..8b85d8a7a 100644 --- a/vendor/github.com/pion/stun/uattrs.go +++ b/vendor/github.com/pion/stun/uattrs.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package stun import "errors" diff --git a/vendor/github.com/pion/stun/uri.go b/vendor/github.com/pion/stun/uri.go new file mode 100644 index 000000000..b9dab691f --- /dev/null +++ b/vendor/github.com/pion/stun/uri.go @@ -0,0 +1,257 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package stun + +import ( + "errors" + "net" + "net/url" + "strconv" +) + +var ( + // ErrUnknownType indicates an error with Unknown info. + ErrUnknownType = errors.New("Unknown") + + // ErrSchemeType indicates the scheme type could not be parsed. + ErrSchemeType = errors.New("unknown scheme type") + + // ErrSTUNQuery indicates query arguments are provided in a STUN URL. + ErrSTUNQuery = errors.New("queries not supported in stun address") + + // ErrInvalidQuery indicates an malformed query is provided. + ErrInvalidQuery = errors.New("invalid query") + + // ErrHost indicates malformed hostname is provided. + ErrHost = errors.New("invalid hostname") + + // ErrPort indicates malformed port is provided. + ErrPort = errors.New("invalid port") + + // ErrProtoType indicates an unsupported transport type was provided. + ErrProtoType = errors.New("invalid transport protocol type") +) + +// SchemeType indicates the type of server used in the ice.URL structure. +type SchemeType int + +const ( + // SchemeTypeUnknown indicates an unknown or unsupported scheme. + SchemeTypeUnknown SchemeType = iota + + // SchemeTypeSTUN indicates the URL represents a STUN server. + SchemeTypeSTUN + + // SchemeTypeSTUNS indicates the URL represents a STUNS (secure) server. + SchemeTypeSTUNS + + // SchemeTypeTURN indicates the URL represents a TURN server. + SchemeTypeTURN + + // SchemeTypeTURNS indicates the URL represents a TURNS (secure) server. + SchemeTypeTURNS +) + +// NewSchemeType defines a procedure for creating a new SchemeType from a raw +// string naming the scheme type. +func NewSchemeType(raw string) SchemeType { + switch raw { + case "stun": + return SchemeTypeSTUN + case "stuns": + return SchemeTypeSTUNS + case "turn": + return SchemeTypeTURN + case "turns": + return SchemeTypeTURNS + default: + return SchemeTypeUnknown + } +} + +func (t SchemeType) String() string { + switch t { + case SchemeTypeSTUN: + return "stun" + case SchemeTypeSTUNS: + return "stuns" + case SchemeTypeTURN: + return "turn" + case SchemeTypeTURNS: + return "turns" + default: + return ErrUnknownType.Error() + } +} + +// ProtoType indicates the transport protocol type that is used in the ice.URL +// structure. +type ProtoType int + +const ( + // ProtoTypeUnknown indicates an unknown or unsupported protocol. + ProtoTypeUnknown ProtoType = iota + + // ProtoTypeUDP indicates the URL uses a UDP transport. + ProtoTypeUDP + + // ProtoTypeTCP indicates the URL uses a TCP transport. + ProtoTypeTCP +) + +// NewProtoType defines a procedure for creating a new ProtoType from a raw +// string naming the transport protocol type. +func NewProtoType(raw string) ProtoType { + switch raw { + case "udp": + return ProtoTypeUDP + case "tcp": + return ProtoTypeTCP + default: + return ProtoTypeUnknown + } +} + +func (t ProtoType) String() string { + switch t { + case ProtoTypeUDP: + return "udp" + case ProtoTypeTCP: + return "tcp" + default: + return ErrUnknownType.Error() + } +} + +// URI represents a STUN (rfc7064) or TURN (rfc7065) URI +type URI struct { + Scheme SchemeType + Host string + Port int + Username string + Password string + Proto ProtoType +} + +// ParseURI parses a STUN or TURN urls following the ABNF syntax described in +// https://tools.ietf.org/html/rfc7064 and https://tools.ietf.org/html/rfc7065 +// respectively. +func ParseURI(raw string) (*URI, error) { //nolint:gocognit + rawParts, err := url.Parse(raw) + if err != nil { + return nil, err + } + + var u URI + u.Scheme = NewSchemeType(rawParts.Scheme) + if u.Scheme == SchemeTypeUnknown { + return nil, ErrSchemeType + } + + var rawPort string + if u.Host, rawPort, err = net.SplitHostPort(rawParts.Opaque); err != nil { + var e *net.AddrError + if errors.As(err, &e) { + if e.Err == "missing port in address" { + nextRawURL := u.Scheme.String() + ":" + rawParts.Opaque + switch { + case u.Scheme == SchemeTypeSTUN || u.Scheme == SchemeTypeTURN: + nextRawURL += ":3478" + if rawParts.RawQuery != "" { + nextRawURL += "?" + rawParts.RawQuery + } + return ParseURI(nextRawURL) + case u.Scheme == SchemeTypeSTUNS || u.Scheme == SchemeTypeTURNS: + nextRawURL += ":5349" + if rawParts.RawQuery != "" { + nextRawURL += "?" + rawParts.RawQuery + } + return ParseURI(nextRawURL) + } + } + } + return nil, err + } + + if u.Host == "" { + return nil, ErrHost + } + + if u.Port, err = strconv.Atoi(rawPort); err != nil { + return nil, ErrPort + } + + switch u.Scheme { + case SchemeTypeSTUN: + qArgs, err := url.ParseQuery(rawParts.RawQuery) + if err != nil || len(qArgs) > 0 { + return nil, ErrSTUNQuery + } + u.Proto = ProtoTypeUDP + case SchemeTypeSTUNS: + qArgs, err := url.ParseQuery(rawParts.RawQuery) + if err != nil || len(qArgs) > 0 { + return nil, ErrSTUNQuery + } + u.Proto = ProtoTypeTCP + case SchemeTypeTURN: + proto, err := parseProto(rawParts.RawQuery) + if err != nil { + return nil, err + } + + u.Proto = proto + if u.Proto == ProtoTypeUnknown { + u.Proto = ProtoTypeUDP + } + case SchemeTypeTURNS: + proto, err := parseProto(rawParts.RawQuery) + if err != nil { + return nil, err + } + + u.Proto = proto + if u.Proto == ProtoTypeUnknown { + u.Proto = ProtoTypeTCP + } + + case SchemeTypeUnknown: + } + + return &u, nil +} + +func parseProto(raw string) (ProtoType, error) { + qArgs, err := url.ParseQuery(raw) + if err != nil || len(qArgs) > 1 { + return ProtoTypeUnknown, ErrInvalidQuery + } + + var proto ProtoType + if rawProto := qArgs.Get("transport"); rawProto != "" { + if proto = NewProtoType(rawProto); proto == ProtoType(0) { + return ProtoTypeUnknown, ErrProtoType + } + return proto, nil + } + + if len(qArgs) > 0 { + return ProtoTypeUnknown, ErrInvalidQuery + } + + return proto, nil +} + +func (u URI) String() string { + rawURL := u.Scheme.String() + ":" + net.JoinHostPort(u.Host, strconv.Itoa(u.Port)) + if u.Scheme == SchemeTypeTURN || u.Scheme == SchemeTypeTURNS { + rawURL += "?transport=" + u.Proto.String() + } + return rawURL +} + +// IsSecure returns whether the this URL's scheme describes secure scheme or not. +func (u URI) IsSecure() bool { + return u.Scheme == SchemeTypeSTUNS || u.Scheme == SchemeTypeTURNS +} diff --git a/vendor/github.com/pion/stun/xor.go b/vendor/github.com/pion/stun/xor.go deleted file mode 100644 index 34365eb26..000000000 --- a/vendor/github.com/pion/stun/xor.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package stun - -import ( - "runtime" - "unsafe" -) - -// #nosec -const wordSize = int(unsafe.Sizeof(uintptr(0))) - -var supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" - -// fastXORBytes xors in bulk. It only works on architectures that -// support unaligned read/writes. -// -// #nosec -func fastXORBytes(dst, a, b []byte) int { - n := len(a) - if len(b) < n { - n = len(b) - } - - w := n / wordSize - if w > 0 { - dw := *(*[]uintptr)(unsafe.Pointer(&dst)) - aw := *(*[]uintptr)(unsafe.Pointer(&a)) - bw := *(*[]uintptr)(unsafe.Pointer(&b)) - for i := 0; i < w; i++ { - dw[i] = aw[i] ^ bw[i] - } - } - - for i := n - n%wordSize; i < n; i++ { - dst[i] = a[i] ^ b[i] - } - - return n -} - -func safeXORBytes(dst, a, b []byte) int { - n := len(a) - if len(b) < n { - n = len(b) - } - for i := 0; i < n; i++ { - dst[i] = a[i] ^ b[i] - } - return n -} - -// xorBytes xors the bytes in a and b. The destination is assumed to have enough -// space. Returns the number of bytes xor'd. -func xorBytes(dst, a, b []byte) int { - if supportsUnaligned { - return fastXORBytes(dst, a, b) - } - return safeXORBytes(dst, a, b) -} diff --git a/vendor/github.com/pion/stun/xoraddr.go b/vendor/github.com/pion/stun/xoraddr.go index 23f7777c0..fc423be86 100644 --- a/vendor/github.com/pion/stun/xoraddr.go +++ b/vendor/github.com/pion/stun/xoraddr.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package stun import ( @@ -6,6 +9,8 @@ import ( "io" "net" "strconv" + + "github.com/pion/transport/v2/utils/xor" ) const ( @@ -66,7 +71,7 @@ func (a XORMappedAddress) AddToAs(m *Message, t AttrType) error { bin.PutUint32(xorValue[0:4], magicCookie) bin.PutUint16(value[0:2], family) bin.PutUint16(value[2:4], uint16(a.Port^magicCookie>>16)) - xorBytes(value[4:4+len(ip)], ip, xorValue) + xor.XorBytes(value[4:4+len(ip)], ip, xorValue) m.Add(t, value[:4+len(ip)]) return nil } @@ -115,7 +120,7 @@ func (a *XORMappedAddress) GetFromAs(m *Message, t AttrType) error { xorValue := make([]byte, 4+TransactionIDSize) bin.PutUint32(xorValue[0:4], magicCookie) copy(xorValue[4:], m.TransactionID[:]) - xorBytes(a.IP, v[4:], xorValue) + xor.XorBytes(a.IP, v[4:], xorValue) return nil } @@ -126,20 +131,20 @@ func (a *XORMappedAddress) GetFromAs(m *Message, t AttrType) error { // // Example: // -// expectedIP := net.ParseIP("213.141.156.236") -// expectedIP.String() // 213.141.156.236, 16 bytes, first 12 of them are zeroes -// expectedPort := 21254 -// addr := &XORMappedAddress{ -// IP: expectedIP, -// Port: expectedPort, -// } -// // addr were added to message that is decoded as newMessage -// // ... +// expectedIP := net.ParseIP("213.141.156.236") +// expectedIP.String() // 213.141.156.236, 16 bytes, first 12 of them are zeroes +// expectedPort := 21254 +// addr := &XORMappedAddress{ +// IP: expectedIP, +// Port: expectedPort, +// } +// // addr were added to message that is decoded as newMessage +// // ... // -// addr.GetFrom(newMessage) -// addr.IP.String() // 213.141.156.236, net.IPv4Len -// expectedIP.String() // d58d:9cec::ffff:d58d:9cec, 16 bytes, first 4 are IPv4 -// // now we have len(expectedIP) = 16 and len(addr.IP) = 4. +// addr.GetFrom(newMessage) +// addr.IP.String() // 213.141.156.236, net.IPv4Len +// expectedIP.String() // d58d:9cec::ffff:d58d:9cec, 16 bytes, first 4 are IPv4 +// // now we have len(expectedIP) = 16 and len(addr.IP) = 4. func (a *XORMappedAddress) GetFrom(m *Message) error { return a.GetFromAs(m, AttrXORMappedAddress) } diff --git a/vendor/github.com/pion/transport/packetio/hardlimit.go b/vendor/github.com/pion/transport/packetio/hardlimit.go deleted file mode 100644 index 5ddacc733..000000000 --- a/vendor/github.com/pion/transport/packetio/hardlimit.go +++ /dev/null @@ -1,5 +0,0 @@ -// +build packetioSizeHardlimit - -package packetio - -const sizeHardlimit = true diff --git a/vendor/github.com/pion/transport/packetio/no_hardlimit.go b/vendor/github.com/pion/transport/packetio/no_hardlimit.go deleted file mode 100644 index 55ea30865..000000000 --- a/vendor/github.com/pion/transport/packetio/no_hardlimit.go +++ /dev/null @@ -1,5 +0,0 @@ -// +build !packetioSizeHardlimit - -package packetio - -const sizeHardlimit = false diff --git a/vendor/github.com/pion/udp/.gitignore b/vendor/github.com/pion/transport/v2/.gitignore similarity index 72% rename from vendor/github.com/pion/udp/.gitignore rename to vendor/github.com/pion/transport/v2/.gitignore index 83db74ba5..6e2f206a9 100644 --- a/vendor/github.com/pion/udp/.gitignore +++ b/vendor/github.com/pion/transport/v2/.gitignore @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + ### JetBrains IDE ### ##################### .idea/ @@ -22,3 +25,4 @@ cover.out *.wasm examples/sfu-ws/cert.pem examples/sfu-ws/key.pem +wasm_exec.js diff --git a/vendor/github.com/pion/udp/.golangci.yml b/vendor/github.com/pion/transport/v2/.golangci.yml similarity index 59% rename from vendor/github.com/pion/udp/.golangci.yml rename to vendor/github.com/pion/transport/v2/.golangci.yml index d6162c970..4e3eddf42 100644 --- a/vendor/github.com/pion/udp/.golangci.yml +++ b/vendor/github.com/pion/transport/v2/.golangci.yml @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + linters-settings: govet: check-shadowing: true @@ -10,19 +13,34 @@ linters-settings: modules: - github.com/pkg/errors: recommendations: - - errors + - errors + forbidigo: + forbid: + - ^fmt.Print(f|ln)?$ + - ^log.(Panic|Fatal|Print)(f|ln)?$ + - ^os.Exit$ + - ^panic$ + - ^print(ln)?$ linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully - - deadcode # Finds unused code + - contextcheck # check the function whether use a non-inherited context + - decorder # check declaration order and count of types, constants, variables and functions - depguard # Go linter that checks if package imports are in a list of acceptable packages - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection + - durationcheck # check for two durations multiplied together - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. - exhaustive # check exhaustiveness of enum switch statements - exportloopref # checks for pointers to enclosing loop variables + - forbidigo # Forbids identifiers + - forcetypeassert # finds forced type assertions - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - gochecknoinits # Checks that no init functions are present in Go code @@ -35,40 +53,59 @@ linters: - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - gosimple # Linter for Go source code that specializes in simplifying a code - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - grouper # An analyzer to analyze expression groups. + - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used - misspell # Finds commonly misspelled English words in comments - nakedret # Finds naked returns in functions greater than a specified function length + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. - noctx # noctx finds sending http request without context.Context - - scopelint # Scopelint checks for unpinned variables in go programs + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # golint replacement, finds style mistakes - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - - structcheck # Finds unused struct fields - stylecheck # Stylecheck is a replacement for golint + - tagliatelle # Checks the struct tags. + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types - - varcheck # Finds unused global variables and constants + - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: + - containedctx # containedctx is a linter that detects struct contained context.Context field + - cyclop # checks function and package cyclomatic complexity + - exhaustivestruct # Checks if all struct's fields are initialized - funlen # Tool for detection of long functions - gocyclo # Computes and checks the cyclomatic complexity of functions - godot # Check if comments end in a period - gomnd # An analyzer to detect magic numbers. + - ifshort # Checks that your code uses short syntax for if-statements whenever possible + - ireturn # Accept Interfaces, Return Concrete Types - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - nestif # Reports deeply nested if statements - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - nolintlint # Reports ill-formed or insufficient nolint directives + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - prealloc # Finds slice declarations that could potentially be preallocated + - promlinter # Check Prometheus metrics naming via promlint - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. - testpackage # linter that makes you use a separate _test package + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers + - varnamelen # checks that the length of a variable's name matches its scope + - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! issues: @@ -78,12 +115,23 @@ issues: - path: _test\.go linters: - gocognit + - forbidigo # Allow complex main function in examples - path: examples text: "of func `main` is high" linters: - gocognit + + # Allow forbidden identifiers in examples + - path: examples + linters: + - forbidigo + + # Allow forbidden identifiers in CLI commands + - path: cmd + linters: + - forbidigo run: skip-dirs-use-default: false diff --git a/vendor/github.com/pion/transport/v2/.goreleaser.yml b/vendor/github.com/pion/transport/v2/.goreleaser.yml new file mode 100644 index 000000000..30093e9d6 --- /dev/null +++ b/vendor/github.com/pion/transport/v2/.goreleaser.yml @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + +builds: +- skip: true diff --git a/vendor/github.com/pion/transport/AUTHORS.txt b/vendor/github.com/pion/transport/v2/AUTHORS.txt similarity index 66% rename from vendor/github.com/pion/transport/AUTHORS.txt rename to vendor/github.com/pion/transport/v2/AUTHORS.txt index f2d46d70b..35bbec396 100644 --- a/vendor/github.com/pion/transport/AUTHORS.txt +++ b/vendor/github.com/pion/transport/v2/AUTHORS.txt @@ -2,18 +2,27 @@ # we would love to have you https://github.com/pion/webrtc/wiki/Contributing # # This file is auto generated, using git to list all individuals contributors. -# see `.github/generate-authors.sh` for the scripting +# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting +Adrian Cable Atsushi Watanabe backkem +cnderrauber Hugo Arregui +Jeremiah Millay Jozef Kralik Juliusz Chroboczek Luke Curley Mathis Engelbart OrlandoCo +Sean DuBois +Sean DuBois Sean DuBois Sean DuBois +Steffen Vogel Winlin Woodrow Douglass Yutaka Takeda ZHENK + +# List of contributors not appearing in Git history + diff --git a/vendor/github.com/pion/transport/v2/LICENSE b/vendor/github.com/pion/transport/v2/LICENSE new file mode 100644 index 000000000..491caf6b0 --- /dev/null +++ b/vendor/github.com/pion/transport/v2/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2023 The Pion community + +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/pion/transport/v2/README.md b/vendor/github.com/pion/transport/v2/README.md new file mode 100644 index 000000000..b604b104d --- /dev/null +++ b/vendor/github.com/pion/transport/v2/README.md @@ -0,0 +1,34 @@ +

+
+ Pion Transport +
+

+

Transport testing for Pion

+

+ Pion transport + Slack Widget +
+ GitHub Workflow Status + Go Reference + Coverage Status + Go Report Card + License: MIT +

+
+ +### Roadmap +The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones. + +### Community +Pion has an active community on the [Slack](https://pion.ly/slack). + +Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news. + +We are always looking to support **your projects**. Please reach out if you have something to build! +If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly) + +### Contributing +Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible: [AUTHORS.txt](./AUTHORS.txt) + +### License +MIT License - see [LICENSE](LICENSE) for full text diff --git a/vendor/github.com/pion/transport/v2/codecov.yml b/vendor/github.com/pion/transport/v2/codecov.yml new file mode 100644 index 000000000..263e4d45c --- /dev/null +++ b/vendor/github.com/pion/transport/v2/codecov.yml @@ -0,0 +1,22 @@ +# +# DO NOT EDIT THIS FILE +# +# It is automatically copied from https://github.com/pion/.goassets repository. +# +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + +coverage: + status: + project: + default: + # Allow decreasing 2% of total coverage to avoid noise. + threshold: 2% + patch: + default: + target: 70% + only_pulls: true + +ignore: + - "examples/*" + - "examples/**/*" diff --git a/vendor/github.com/pion/transport/connctx/connctx.go b/vendor/github.com/pion/transport/v2/connctx/connctx.go similarity index 91% rename from vendor/github.com/pion/transport/connctx/connctx.go rename to vendor/github.com/pion/transport/v2/connctx/connctx.go index 19fe48498..0bdd59509 100644 --- a/vendor/github.com/pion/transport/connctx/connctx.go +++ b/vendor/github.com/pion/transport/v2/connctx/connctx.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package connctx wraps net.Conn using context.Context. package connctx @@ -97,8 +100,8 @@ func (c *connCtx) ReadContext(ctx context.Context, b []byte) (int, error) { if e := ctx.Err(); e != nil && n == 0 { err = e } - if err2 := errSetDeadline.Load(); err == nil && err2 != nil { - err = err2.(error) + if err2, ok := errSetDeadline.Load().(error); ok && err == nil && err2 != nil { + err = err2 } return n, err } @@ -141,8 +144,8 @@ func (c *connCtx) WriteContext(ctx context.Context, b []byte) (int, error) { if e := ctx.Err(); e != nil && n == 0 { err = e } - if err2 := errSetDeadline.Load(); err == nil && err2 != nil { - err = err2.(error) + if err2, ok := errSetDeadline.Load().(error); ok && err == nil && err2 != nil { + err = err2 } return n, err } diff --git a/vendor/github.com/pion/transport/connctx/pipe.go b/vendor/github.com/pion/transport/v2/connctx/pipe.go similarity index 60% rename from vendor/github.com/pion/transport/connctx/pipe.go rename to vendor/github.com/pion/transport/v2/connctx/pipe.go index e2f040928..96b802e43 100644 --- a/vendor/github.com/pion/transport/connctx/pipe.go +++ b/vendor/github.com/pion/transport/v2/connctx/pipe.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package connctx import ( diff --git a/vendor/github.com/pion/transport/deadline/deadline.go b/vendor/github.com/pion/transport/v2/deadline/deadline.go similarity index 90% rename from vendor/github.com/pion/transport/deadline/deadline.go rename to vendor/github.com/pion/transport/v2/deadline/deadline.go index 6f9738670..abd39f06d 100644 --- a/vendor/github.com/pion/transport/deadline/deadline.go +++ b/vendor/github.com/pion/transport/v2/deadline/deadline.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package deadline provides deadline timer used to implement // net.Conn compatible connection package deadline @@ -59,11 +62,15 @@ func (d *Deadline) Set(t time.Time) { exceeded := d.exceeded stopped := d.stopped go func() { + timer := time.NewTimer(dur) select { - case <-time.After(dur): + case <-timer.C: close(exceeded) stopped <- false case <-d.stop: + if !timer.Stop() { + <-timer.C + } stopped <- true } }() diff --git a/vendor/github.com/pion/transport/v2/net.go b/vendor/github.com/pion/transport/v2/net.go new file mode 100644 index 000000000..86d3468f7 --- /dev/null +++ b/vendor/github.com/pion/transport/v2/net.go @@ -0,0 +1,418 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package transport implements various networking related +// functions used throughout the Pion modules. +package transport + +import ( + "errors" + "io" + "net" + "time" +) + +var ( + // ErrNoAddressAssigned ... + ErrNoAddressAssigned = errors.New("no address assigned") + // ErrNotSupported ... + ErrNotSupported = errors.New("not supported yey") + // ErrInterfaceNotFound ... + ErrInterfaceNotFound = errors.New("interface not found") + // ErrNotUDPAddress ... + ErrNotUDPAddress = errors.New("not a UDP address") +) + +// Net is an interface providing common networking functions which are +// similar to the functions provided by standard net package. +type Net interface { + // ListenPacket announces on the local network address. + // + // The network must be "udp", "udp4", "udp6", "unixgram", or an IP + // transport. The IP transports are "ip", "ip4", or "ip6" followed by + // a colon and a literal protocol number or a protocol name, as in + // "ip:1" or "ip:icmp". + // + // For UDP and IP networks, if the host in the address parameter is + // empty or a literal unspecified IP address, ListenPacket listens on + // all available IP addresses of the local system except multicast IP + // addresses. + // To only use IPv4, use network "udp4" or "ip4:proto". + // The address can use a host name, but this is not recommended, + // because it will create a listener for at most one of the host's IP + // addresses. + // If the port in the address parameter is empty or "0", as in + // "127.0.0.1:" or "[::1]:0", a port number is automatically chosen. + // The LocalAddr method of PacketConn can be used to discover the + // chosen port. + // + // See func Dial for a description of the network and address + // parameters. + // + // ListenPacket uses context.Background internally; to specify the context, use + // ListenConfig.ListenPacket. + ListenPacket(network string, address string) (net.PacketConn, error) + + // ListenUDP acts like ListenPacket for UDP networks. + // + // The network must be a UDP network name; see func Dial for details. + // + // If the IP field of laddr is nil or an unspecified IP address, + // ListenUDP listens on all available IP addresses of the local system + // except multicast IP addresses. + // If the Port field of laddr is 0, a port number is automatically + // chosen. + ListenUDP(network string, locAddr *net.UDPAddr) (UDPConn, error) + + // ListenTCP acts like Listen for TCP networks. + // + // The network must be a TCP network name; see func Dial for details. + // + // If the IP field of laddr is nil or an unspecified IP address, + // ListenTCP listens on all available unicast and anycast IP addresses + // of the local system. + // If the Port field of laddr is 0, a port number is automatically + // chosen. + ListenTCP(network string, laddr *net.TCPAddr) (TCPListener, error) + + // Dial connects to the address on the named network. + // + // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), + // "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4" + // (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and + // "unixpacket". + // + // For TCP and UDP networks, the address has the form "host:port". + // The host must be a literal IP address, or a host name that can be + // resolved to IP addresses. + // The port must be a literal port number or a service name. + // If the host is a literal IPv6 address it must be enclosed in square + // brackets, as in "[2001:db8::1]:80" or "[fe80::1%zone]:80". + // The zone specifies the scope of the literal IPv6 address as defined + // in RFC 4007. + // The functions JoinHostPort and SplitHostPort manipulate a pair of + // host and port in this form. + // When using TCP, and the host resolves to multiple IP addresses, + // Dial will try each IP address in order until one succeeds. + // + // Examples: + // + // Dial("tcp", "golang.org:http") + // Dial("tcp", "192.0.2.1:http") + // Dial("tcp", "198.51.100.1:80") + // Dial("udp", "[2001:db8::1]:domain") + // Dial("udp", "[fe80::1%lo0]:53") + // Dial("tcp", ":80") + // + // For IP networks, the network must be "ip", "ip4" or "ip6" followed + // by a colon and a literal protocol number or a protocol name, and + // the address has the form "host". The host must be a literal IP + // address or a literal IPv6 address with zone. + // It depends on each operating system how the operating system + // behaves with a non-well known protocol number such as "0" or "255". + // + // Examples: + // + // Dial("ip4:1", "192.0.2.1") + // Dial("ip6:ipv6-icmp", "2001:db8::1") + // Dial("ip6:58", "fe80::1%lo0") + // + // For TCP, UDP and IP networks, if the host is empty or a literal + // unspecified IP address, as in ":80", "0.0.0.0:80" or "[::]:80" for + // TCP and UDP, "", "0.0.0.0" or "::" for IP, the local system is + // assumed. + // + // For Unix networks, the address must be a file system path. + Dial(network, address string) (net.Conn, error) + + // DialUDP acts like Dial for UDP networks. + // + // The network must be a UDP network name; see func Dial for details. + // + // If laddr is nil, a local address is automatically chosen. + // If the IP field of raddr is nil or an unspecified IP address, the + // local system is assumed. + DialUDP(network string, laddr, raddr *net.UDPAddr) (UDPConn, error) + + // DialTCP acts like Dial for TCP networks. + // + // The network must be a TCP network name; see func Dial for details. + // + // If laddr is nil, a local address is automatically chosen. + // If the IP field of raddr is nil or an unspecified IP address, the + // local system is assumed. + DialTCP(network string, laddr, raddr *net.TCPAddr) (TCPConn, error) + + // ResolveIPAddr returns an address of IP end point. + // + // The network must be an IP network name. + // + // If the host in the address parameter is not a literal IP address, + // ResolveIPAddr resolves the address to an address of IP end point. + // Otherwise, it parses the address as a literal IP address. + // The address parameter can use a host name, but this is not + // recommended, because it will return at most one of the host name's + // IP addresses. + // + // See func Dial for a description of the network and address + // parameters. + ResolveIPAddr(network, address string) (*net.IPAddr, error) + + // ResolveUDPAddr returns an address of UDP end point. + // + // The network must be a UDP network name. + // + // If the host in the address parameter is not a literal IP address or + // the port is not a literal port number, ResolveUDPAddr resolves the + // address to an address of UDP end point. + // Otherwise, it parses the address as a pair of literal IP address + // and port number. + // The address parameter can use a host name, but this is not + // recommended, because it will return at most one of the host name's + // IP addresses. + // + // See func Dial for a description of the network and address + // parameters. + ResolveUDPAddr(network, address string) (*net.UDPAddr, error) + + // ResolveTCPAddr returns an address of TCP end point. + // + // The network must be a TCP network name. + // + // If the host in the address parameter is not a literal IP address or + // the port is not a literal port number, ResolveTCPAddr resolves the + // address to an address of TCP end point. + // Otherwise, it parses the address as a pair of literal IP address + // and port number. + // The address parameter can use a host name, but this is not + // recommended, because it will return at most one of the host name's + // IP addresses. + // + // See func Dial for a description of the network and address + // parameters. + ResolveTCPAddr(network, address string) (*net.TCPAddr, error) + + // Interfaces returns a list of the system's network interfaces. + Interfaces() ([]*Interface, error) + + // InterfaceByIndex returns the interface specified by index. + // + // On Solaris, it returns one of the logical network interfaces + // sharing the logical data link; for more precision use + // InterfaceByName. + InterfaceByIndex(index int) (*Interface, error) + + // InterfaceByName returns the interface specified by name. + InterfaceByName(name string) (*Interface, error) + + // The following functions are extensions to Go's standard net package + + CreateDialer(dialer *net.Dialer) Dialer +} + +// Dialer is identical to net.Dialer excepts that its methods +// (Dial, DialContext) are overridden to use the Net interface. +// Use vnet.CreateDialer() to create an instance of this Dialer. +type Dialer interface { + Dial(network, address string) (net.Conn, error) +} + +// UDPConn is packet-oriented connection for UDP. +type UDPConn interface { + // Close closes the connection. + // Any blocked Read or Write operations will be unblocked and return errors. + Close() error + + // LocalAddr returns the local network address, if known. + LocalAddr() net.Addr + + // RemoteAddr returns the remote network address, if known. + RemoteAddr() net.Addr + + // SetDeadline sets the read and write deadlines associated + // with the connection. It is equivalent to calling both + // SetReadDeadline and SetWriteDeadline. + // + // A deadline is an absolute time after which I/O operations + // fail instead of blocking. The deadline applies to all future + // and pending I/O, not just the immediately following call to + // Read or Write. After a deadline has been exceeded, the + // connection can be refreshed by setting a deadline in the future. + // + // If the deadline is exceeded a call to Read or Write or to other + // I/O methods will return an error that wraps os.ErrDeadlineExceeded. + // This can be tested using errors.Is(err, os.ErrDeadlineExceeded). + // The error's Timeout method will return true, but note that there + // are other possible errors for which the Timeout method will + // return true even if the deadline has not been exceeded. + // + // An idle timeout can be implemented by repeatedly extending + // the deadline after successful Read or Write calls. + // + // A zero value for t means I/O operations will not time out. + SetDeadline(t time.Time) error + + // SetReadDeadline sets the deadline for future Read calls + // and any currently-blocked Read call. + // A zero value for t means Read will not time out. + SetReadDeadline(t time.Time) error + + // SetWriteDeadline sets the deadline for future Write calls + // and any currently-blocked Write call. + // Even if write times out, it may return n > 0, indicating that + // some of the data was successfully written. + // A zero value for t means Write will not time out. + SetWriteDeadline(t time.Time) error + + // SetReadBuffer sets the size of the operating system's + // receive buffer associated with the connection. + SetReadBuffer(bytes int) error + + // SetWriteBuffer sets the size of the operating system's + // transmit buffer associated with the connection. + SetWriteBuffer(bytes int) error + + // Read reads data from the connection. + // Read can be made to time out and return an error after a fixed + // time limit; see SetDeadline and SetReadDeadline. + Read(b []byte) (n int, err error) + + // ReadFrom reads a packet from the connection, + // copying the payload into p. It returns the number of + // bytes copied into p and the return address that + // was on the packet. + // It returns the number of bytes read (0 <= n <= len(p)) + // and any error encountered. Callers should always process + // the n > 0 bytes returned before considering the error err. + // ReadFrom can be made to time out and return an error after a + // fixed time limit; see SetDeadline and SetReadDeadline. + ReadFrom(p []byte) (n int, addr net.Addr, err error) + + // ReadFromUDP acts like ReadFrom but returns a UDPAddr. + ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) + + // ReadMsgUDP reads a message from c, copying the payload into b and + // the associated out-of-band data into oob. It returns the number of + // bytes copied into b, the number of bytes copied into oob, the flags + // that were set on the message and the source address of the message. + // + // The packages golang.org/x/net/ipv4 and golang.org/x/net/ipv6 can be + // used to manipulate IP-level socket options in oob. + ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) + + // Write writes data to the connection. + // Write can be made to time out and return an error after a fixed + // time limit; see SetDeadline and SetWriteDeadline. + Write(b []byte) (n int, err error) + + // WriteTo writes a packet with payload p to addr. + // WriteTo can be made to time out and return an Error after a + // fixed time limit; see SetDeadline and SetWriteDeadline. + // On packet-oriented connections, write timeouts are rare. + WriteTo(p []byte, addr net.Addr) (n int, err error) + + // WriteToUDP acts like WriteTo but takes a UDPAddr. + WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) + + // WriteMsgUDP writes a message to addr via c if c isn't connected, or + // to c's remote address if c is connected (in which case addr must be + // nil). The payload is copied from b and the associated out-of-band + // data is copied from oob. It returns the number of payload and + // out-of-band bytes written. + // + // The packages golang.org/x/net/ipv4 and golang.org/x/net/ipv6 can be + // used to manipulate IP-level socket options in oob. + WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) +} + +// TCPConn is an interface for TCP network connections. +type TCPConn interface { + net.Conn + + // CloseRead shuts down the reading side of the TCP connection. + // Most callers should just use Close. + CloseRead() error + + // CloseWrite shuts down the writing side of the TCP connection. + // Most callers should just use Close. + CloseWrite() error + + // ReadFrom implements the io.ReaderFrom ReadFrom method. + ReadFrom(r io.Reader) (int64, error) + + // SetLinger sets the behavior of Close on a connection which still + // has data waiting to be sent or to be acknowledged. + // + // If sec < 0 (the default), the operating system finishes sending the + // data in the background. + // + // If sec == 0, the operating system discards any unsent or + // unacknowledged data. + // + // If sec > 0, the data is sent in the background as with sec < 0. On + // some operating systems after sec seconds have elapsed any remaining + // unsent data may be discarded. + SetLinger(sec int) error + + // SetKeepAlive sets whether the operating system should send + // keep-alive messages on the connection. + SetKeepAlive(keepalive bool) error + + // SetKeepAlivePeriod sets period between keep-alives. + SetKeepAlivePeriod(d time.Duration) error + + // SetNoDelay controls whether the operating system should delay + // packet transmission in hopes of sending fewer packets (Nagle's + // algorithm). The default is true (no delay), meaning that data is + // sent as soon as possible after a Write. + SetNoDelay(noDelay bool) error + + // SetWriteBuffer sets the size of the operating system's + // transmit buffer associated with the connection. + SetWriteBuffer(bytes int) error + + // SetReadBuffer sets the size of the operating system's + // receive buffer associated with the connection. + SetReadBuffer(bytes int) error +} + +// TCPListener is a TCP network listener. Clients should typically +// use variables of type Listener instead of assuming TCP. +type TCPListener interface { + net.Listener + + // AcceptTCP accepts the next incoming call and returns the new + // connection. + AcceptTCP() (TCPConn, error) + + // SetDeadline sets the deadline associated with the listener. + // A zero time value disables the deadline. + SetDeadline(t time.Time) error +} + +// Interface wraps a standard net.Interfaces and its assigned addresses +type Interface struct { + net.Interface + addrs []net.Addr +} + +// NewInterface creates a new interface based of a standard net.Interface +func NewInterface(ifc net.Interface) *Interface { + return &Interface{ + Interface: ifc, + addrs: nil, + } +} + +// AddAddress adds a new address to the interface +func (ifc *Interface) AddAddress(addr net.Addr) { + ifc.addrs = append(ifc.addrs, addr) +} + +// Addrs returns a slice of configured addresses on the interface +func (ifc *Interface) Addrs() ([]net.Addr, error) { + if len(ifc.addrs) == 0 { + return nil, ErrNoAddressAssigned + } + return ifc.addrs, nil +} diff --git a/vendor/github.com/pion/transport/packetio/buffer.go b/vendor/github.com/pion/transport/v2/packetio/buffer.go similarity index 85% rename from vendor/github.com/pion/transport/packetio/buffer.go rename to vendor/github.com/pion/transport/v2/packetio/buffer.go index 97d86f8c8..2d46d796a 100644 --- a/vendor/github.com/pion/transport/packetio/buffer.go +++ b/vendor/github.com/pion/transport/v2/packetio/buffer.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package packetio provides packet buffer package packetio @@ -7,7 +10,7 @@ import ( "sync" "time" - "github.com/pion/transport/deadline" + "github.com/pion/transport/v2/deadline" ) var errPacketTooBig = errors.New("packet too big") @@ -35,8 +38,7 @@ type Buffer struct { data []byte head, tail int - notify chan struct{} - subs bool + notify chan struct{} // non-nil when we have blocked readers closed bool count int @@ -54,7 +56,6 @@ const ( // NewBuffer creates a new Buffer. func NewBuffer() *Buffer { return &Buffer{ - notify: make(chan struct{}), readDeadline: deadline.New(), } } @@ -77,42 +78,42 @@ func (b *Buffer) available(size int) bool { // grow increases the size of the buffer. If it returns nil, then the // buffer has been grown. It returns ErrFull if hits a limit. func (b *Buffer) grow() error { - var newsize int + var newSize int if len(b.data) < cutoffSize { - newsize = 2 * len(b.data) + newSize = 2 * len(b.data) } else { - newsize = 5 * len(b.data) / 4 + newSize = 5 * len(b.data) / 4 } - if newsize < minSize { - newsize = minSize + if newSize < minSize { + newSize = minSize } - if (b.limitSize <= 0 || sizeHardlimit) && newsize > maxSize { - newsize = maxSize + if (b.limitSize <= 0 || sizeHardLimit) && newSize > maxSize { + newSize = maxSize } // one byte slack - if b.limitSize > 0 && newsize > b.limitSize+1 { - newsize = b.limitSize + 1 + if b.limitSize > 0 && newSize > b.limitSize+1 { + newSize = b.limitSize + 1 } - if newsize <= len(b.data) { + if newSize <= len(b.data) { return ErrFull } - newdata := make([]byte, newsize) + newData := make([]byte, newSize) var n int if b.head <= b.tail { // data was contiguous - n = copy(newdata, b.data[b.head:b.tail]) + n = copy(newData, b.data[b.head:b.tail]) } else { - // data was discontiguous - n = copy(newdata, b.data[b.head:]) - n += copy(newdata[n:], b.data[:b.tail]) + // data was discontinuous + n = copy(newData, b.data[b.head:]) + n += copy(newData[n:], b.data[:b.tail]) } b.head = 0 b.tail = n - b.data = newdata + b.data = newData return nil } @@ -149,13 +150,11 @@ func (b *Buffer) Write(packet []byte) (int, error) { } var notify chan struct{} - - if b.subs { - // readers are waiting. Prepare to notify, but only + if b.notify != nil { + // Prepare to notify readers, but only // actually do it after we release the lock. notify = b.notify - b.notify = make(chan struct{}) - b.subs = false + b.notify = nil } // store the length of the packet @@ -192,7 +191,7 @@ func (b *Buffer) Write(packet []byte) (int, error) { // Blocks until data is available or the buffer is closed. // Returns io.ErrShortBuffer is the packet is too small to copy the Write. // Returns io.EOF if the buffer is closed. -func (b *Buffer) Read(packet []byte) (n int, err error) { +func (b *Buffer) Read(packet []byte) (n int, err error) { //nolint:gocognit // Return immediately if the deadline is already exceeded. select { case <-b.readDeadline.Done(): @@ -259,8 +258,10 @@ func (b *Buffer) Read(packet []byte) (n int, err error) { return 0, io.EOF } + if b.notify == nil { + b.notify = make(chan struct{}) + } notify := b.notify - b.subs = true b.mutex.Unlock() select { @@ -282,11 +283,14 @@ func (b *Buffer) Close() (err error) { } notify := b.notify + b.notify = nil b.closed = true b.mutex.Unlock() - close(notify) + if notify != nil { + close(notify) + } return nil } @@ -329,9 +333,9 @@ func (b *Buffer) size() int { // Causes Write to return ErrFull when this limit is reached. // A zero value means 4MB since v0.11.0. // -// User can set packetioSizeHardlimit build tag to enable 4MB hardlimit. -// When packetioSizeHardlimit build tag is set, SetLimitSize exceeding -// the hardlimit will be silently discarded. +// User can set packetioSizeHardLimit build tag to enable 4MB hard limit. +// When packetioSizeHardLimit build tag is set, SetLimitSize exceeding +// the hard limit will be silently discarded. func (b *Buffer) SetLimitSize(limit int) { b.mutex.Lock() defer b.mutex.Unlock() diff --git a/vendor/github.com/pion/transport/packetio/errors.go b/vendor/github.com/pion/transport/v2/packetio/errors.go similarity index 82% rename from vendor/github.com/pion/transport/packetio/errors.go rename to vendor/github.com/pion/transport/v2/packetio/errors.go index 06f1b9d98..4974a10b5 100644 --- a/vendor/github.com/pion/transport/packetio/errors.go +++ b/vendor/github.com/pion/transport/v2/packetio/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package packetio import ( diff --git a/vendor/github.com/pion/transport/v2/packetio/hardlimit.go b/vendor/github.com/pion/transport/v2/packetio/hardlimit.go new file mode 100644 index 000000000..8058e47fa --- /dev/null +++ b/vendor/github.com/pion/transport/v2/packetio/hardlimit.go @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +//go:build packetioSizeHardlimit +// +build packetioSizeHardlimit + +package packetio + +const sizeHardLimit = true diff --git a/vendor/github.com/pion/transport/v2/packetio/no_hardlimit.go b/vendor/github.com/pion/transport/v2/packetio/no_hardlimit.go new file mode 100644 index 000000000..a59e25957 --- /dev/null +++ b/vendor/github.com/pion/transport/v2/packetio/no_hardlimit.go @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +//go:build !packetioSizeHardlimit +// +build !packetioSizeHardlimit + +package packetio + +const sizeHardLimit = false diff --git a/vendor/github.com/pion/transport/v2/renovate.json b/vendor/github.com/pion/transport/v2/renovate.json new file mode 100644 index 000000000..f1bb98c6a --- /dev/null +++ b/vendor/github.com/pion/transport/v2/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "github>pion/renovate-config" + ] +} diff --git a/vendor/github.com/pion/transport/replaydetector/fixedbig.go b/vendor/github.com/pion/transport/v2/replaydetector/fixedbig.go similarity index 93% rename from vendor/github.com/pion/transport/replaydetector/fixedbig.go rename to vendor/github.com/pion/transport/v2/replaydetector/fixedbig.go index a571a1aad..80cb6b305 100644 --- a/vendor/github.com/pion/transport/replaydetector/fixedbig.go +++ b/vendor/github.com/pion/transport/v2/replaydetector/fixedbig.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package replaydetector import ( diff --git a/vendor/github.com/pion/transport/replaydetector/replaydetector.go b/vendor/github.com/pion/transport/v2/replaydetector/replaydetector.go similarity index 94% rename from vendor/github.com/pion/transport/replaydetector/replaydetector.go rename to vendor/github.com/pion/transport/v2/replaydetector/replaydetector.go index d9420022b..4358d8f3b 100644 --- a/vendor/github.com/pion/transport/replaydetector/replaydetector.go +++ b/vendor/github.com/pion/transport/v2/replaydetector/replaydetector.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package replaydetector provides packet replay detection algorithm. package replaydetector @@ -55,7 +58,7 @@ func (d *slidingWindowDetector) Check(seq uint64) (accept func(), ok bool) { } // WithWrap creates ReplayDetector allowing sequence wrapping. -// This is suitable for short bitwidth counter like SRTP and SRTCP. +// This is suitable for short bit width counter like SRTP and SRTCP. func WithWrap(windowSize uint, maxSeq uint64) ReplayDetector { return &wrappedSlidingWindowDetector{ maxSeq: maxSeq, diff --git a/vendor/github.com/pion/transport/v2/stdnet/net.go b/vendor/github.com/pion/transport/v2/stdnet/net.go new file mode 100644 index 000000000..fa4753b51 --- /dev/null +++ b/vendor/github.com/pion/transport/v2/stdnet/net.go @@ -0,0 +1,167 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package stdnet implements the transport.Net interface +// using methods from Go's standard net package. +package stdnet + +import ( + "fmt" + "net" + + "github.com/pion/transport/v2" +) + +const ( + lo0String = "lo0String" + udpString = "udp" +) + +// Net is an implementation of the net.Net interface +// based on functions of the standard net package. +type Net struct { + interfaces []*transport.Interface +} + +// NewNet creates a new StdNet instance. +func NewNet() (*Net, error) { + n := &Net{} + + return n, n.UpdateInterfaces() +} + +// Compile-time assertion +var _ transport.Net = &Net{} + +// UpdateInterfaces updates the internal list of network interfaces +// and associated addresses. +func (n *Net) UpdateInterfaces() error { + ifs := []*transport.Interface{} + + oifs, err := net.Interfaces() + if err != nil { + return err + } + + for _, oif := range oifs { + ifc := transport.NewInterface(oif) + + addrs, err := oif.Addrs() + if err != nil { + return err + } + + for _, addr := range addrs { + ifc.AddAddress(addr) + } + + ifs = append(ifs, ifc) + } + + n.interfaces = ifs + + return nil +} + +// Interfaces returns a slice of interfaces which are available on the +// system +func (n *Net) Interfaces() ([]*transport.Interface, error) { + return n.interfaces, nil +} + +// InterfaceByIndex returns the interface specified by index. +// +// On Solaris, it returns one of the logical network interfaces +// sharing the logical data link; for more precision use +// InterfaceByName. +func (n *Net) InterfaceByIndex(index int) (*transport.Interface, error) { + for _, ifc := range n.interfaces { + if ifc.Index == index { + return ifc, nil + } + } + + return nil, fmt.Errorf("%w: index=%d", transport.ErrInterfaceNotFound, index) +} + +// InterfaceByName returns the interface specified by name. +func (n *Net) InterfaceByName(name string) (*transport.Interface, error) { + for _, ifc := range n.interfaces { + if ifc.Name == name { + return ifc, nil + } + } + + return nil, fmt.Errorf("%w: %s", transport.ErrInterfaceNotFound, name) +} + +// ListenPacket announces on the local network address. +func (n *Net) ListenPacket(network string, address string) (net.PacketConn, error) { + return net.ListenPacket(network, address) +} + +// ListenUDP acts like ListenPacket for UDP networks. +func (n *Net) ListenUDP(network string, locAddr *net.UDPAddr) (transport.UDPConn, error) { + return net.ListenUDP(network, locAddr) +} + +// Dial connects to the address on the named network. +func (n *Net) Dial(network, address string) (net.Conn, error) { + return net.Dial(network, address) +} + +// DialUDP acts like Dial for UDP networks. +func (n *Net) DialUDP(network string, laddr, raddr *net.UDPAddr) (transport.UDPConn, error) { + return net.DialUDP(network, laddr, raddr) +} + +// ResolveIPAddr returns an address of IP end point. +func (n *Net) ResolveIPAddr(network, address string) (*net.IPAddr, error) { + return net.ResolveIPAddr(network, address) +} + +// ResolveUDPAddr returns an address of UDP end point. +func (n *Net) ResolveUDPAddr(network, address string) (*net.UDPAddr, error) { + return net.ResolveUDPAddr(network, address) +} + +// ResolveTCPAddr returns an address of TCP end point. +func (n *Net) ResolveTCPAddr(network, address string) (*net.TCPAddr, error) { + return net.ResolveTCPAddr(network, address) +} + +// DialTCP acts like Dial for TCP networks. +func (n *Net) DialTCP(network string, laddr, raddr *net.TCPAddr) (transport.TCPConn, error) { + return net.DialTCP(network, laddr, raddr) +} + +// ListenTCP acts like Listen for TCP networks. +func (n *Net) ListenTCP(network string, laddr *net.TCPAddr) (transport.TCPListener, error) { + l, err := net.ListenTCP(network, laddr) + if err != nil { + return nil, err + } + + return tcpListener{l}, nil +} + +type tcpListener struct { + *net.TCPListener +} + +func (l tcpListener) AcceptTCP() (transport.TCPConn, error) { + return l.TCPListener.AcceptTCP() +} + +type stdDialer struct { + *net.Dialer +} + +func (d stdDialer) Dial(network, address string) (net.Conn, error) { + return d.Dialer.Dial(network, address) +} + +// CreateDialer creates an instance of vnet.Dialer +func (n *Net) CreateDialer(d *net.Dialer) transport.Dialer { + return stdDialer{d} +} diff --git a/vendor/github.com/pion/udp/conn.go b/vendor/github.com/pion/transport/v2/udp/conn.go similarity index 83% rename from vendor/github.com/pion/udp/conn.go rename to vendor/github.com/pion/transport/v2/udp/conn.go index a845d4af5..2b57174a3 100644 --- a/vendor/github.com/pion/udp/conn.go +++ b/vendor/github.com/pion/transport/v2/udp/conn.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package udp provides a connection-oriented listener over a UDP PacketConn package udp @@ -9,8 +12,8 @@ import ( "sync/atomic" "time" - "github.com/pion/transport/deadline" - "github.com/pion/transport/packetio" + "github.com/pion/transport/v2/deadline" + "github.com/pion/transport/v2/packetio" ) const ( @@ -22,6 +25,7 @@ const ( var ( ErrClosedListener = errors.New("udp: listener closed") ErrListenQueueExceeded = errors.New("udp: listen queue exceeded") + ErrReadBufferFailed = errors.New("udp: failed to get read buffer from pool") ) // listener augments a connection-oriented Listener over a UDP PacketConn @@ -37,10 +41,13 @@ type listener struct { connLock sync.Mutex conns map[string]*Conn - connWG sync.WaitGroup + connWG *sync.WaitGroup readWG sync.WaitGroup errClose atomic.Value // error + + readDoneCh chan struct{} + errRead atomic.Value // error } // Accept waits for and returns the next connection to the listener. @@ -50,6 +57,10 @@ func (l *listener) Accept() (net.Conn, error) { l.connWG.Add(1) return c, nil + case <-l.readDoneCh: + err, _ := l.errRead.Load().(error) + return nil, err + case <-l.doneCh: return nil, ErrClosedListener } @@ -65,7 +76,7 @@ func (l *listener) Close() error { l.connLock.Lock() // Close unaccepted connections - L_CLOSE: + lclose: for { select { case c := <-l.acceptCh: @@ -73,7 +84,7 @@ func (l *listener) Close() error { delete(l.conns, c.rAddr.String()) default: - break L_CLOSE + break lclose } } nConns := len(l.conns) @@ -138,6 +149,8 @@ func (lc *ListenConfig) Listen(network string, laddr *net.UDPAddr) (net.Listener return &buf }, }, + connWG: &sync.WaitGroup{}, + readDoneCh: make(chan struct{}), } l.accepting.Store(true) @@ -162,24 +175,32 @@ func Listen(network string, laddr *net.UDPAddr) (net.Listener, error) { } // readLoop has to tasks: -// 1. Dispatching incoming packets to the correct Conn. -// It can therefore not be ended until all Conns are closed. -// 2. Creating a new Conn when receiving from a new remote. +// 1. Dispatching incoming packets to the correct Conn. +// It can therefore not be ended until all Conns are closed. +// 2. Creating a new Conn when receiving from a new remote. func (l *listener) readLoop() { defer l.readWG.Done() + defer close(l.readDoneCh) + + buf, ok := l.readBufferPool.Get().(*[]byte) + if !ok { + l.errRead.Store(ErrReadBufferFailed) + return + } + defer l.readBufferPool.Put(buf) for { - buf := *(l.readBufferPool.Get().(*[]byte)) - n, raddr, err := l.pConn.ReadFrom(buf) + n, raddr, err := l.pConn.ReadFrom(*buf) if err != nil { + l.errRead.Store(err) return } - conn, ok, err := l.getConn(raddr, buf[:n]) + conn, ok, err := l.getConn(raddr, (*buf)[:n]) if err != nil { continue } if ok { - _, _ = conn.buffer.Write(buf[:n]) + _, _ = conn.buffer.Write((*buf)[:n]) } } } @@ -189,7 +210,7 @@ func (l *listener) getConn(raddr net.Addr, buf []byte) (*Conn, bool, error) { defer l.connLock.Unlock() conn, ok := l.conns[raddr.String()] if !ok { - if !l.accepting.Load().(bool) { + if isAccepting, ok := l.accepting.Load().(bool); !isAccepting || !ok { return nil, false, ErrClosedListener } if l.acceptFilter != nil { @@ -258,7 +279,7 @@ func (c *Conn) Close() error { nConns := len(c.listener.conns) c.listener.connLock.Unlock() - if nConns == 0 && !c.listener.accepting.Load().(bool) { + if isAccepting, ok := c.listener.accepting.Load().(bool); nConns == 0 && !isAccepting && ok { // Wait if this is the final connection c.listener.readWG.Wait() if errClose, ok := c.listener.errClose.Load().(error); ok { @@ -267,6 +288,10 @@ func (c *Conn) Close() error { } else { err = nil } + + if errBuf := c.buffer.Close(); errBuf != nil && err == nil { + err = errBuf + } }) return err diff --git a/vendor/github.com/pion/transport/v2/utils/xor/xor_amd64.go b/vendor/github.com/pion/transport/v2/utils/xor/xor_amd64.go new file mode 100644 index 000000000..ded8e0d35 --- /dev/null +++ b/vendor/github.com/pion/transport/v2/utils/xor/xor_amd64.go @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2018 The Go Authors. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause + +//go:build !gccgo +// +build !gccgo + +// Package xor provides utility functions used by other Pion +// packages. AMD64 arch. +package xor + +// XorBytes xors the bytes in a and b. The destination should have enough +// space, otherwise xorBytes will panic. Returns the number of bytes xor'd. +// +//revive:disable-next-line +func XorBytes(dst, a, b []byte) int { + n := len(a) + if len(b) < n { + n = len(b) + } + if n == 0 { + return 0 + } + _ = dst[n-1] + xorBytesSSE2(&dst[0], &a[0], &b[0], n) // amd64 must have SSE2 + return n +} + +//go:noescape +func xorBytesSSE2(dst, a, b *byte, n int) diff --git a/vendor/github.com/pion/transport/v2/utils/xor/xor_amd64.s b/vendor/github.com/pion/transport/v2/utils/xor/xor_amd64.s new file mode 100644 index 000000000..f66ac95a2 --- /dev/null +++ b/vendor/github.com/pion/transport/v2/utils/xor/xor_amd64.s @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2018 The Go Authors. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause + +// go:build !gccgo +// +build !gccgo + +#include "textflag.h" + +// func xorBytesSSE2(dst, a, b *byte, n int) +TEXT ·xorBytesSSE2(SB), NOSPLIT, $0 + MOVQ dst+0(FP), BX + MOVQ a+8(FP), SI + MOVQ b+16(FP), CX + MOVQ n+24(FP), DX + TESTQ $15, DX // AND 15 & len, if not zero jump to not_aligned. + JNZ not_aligned + +aligned: + MOVQ $0, AX // position in slices + +loop16b: + MOVOU (SI)(AX*1), X0 // XOR 16byte forwards. + MOVOU (CX)(AX*1), X1 + PXOR X1, X0 + MOVOU X0, (BX)(AX*1) + ADDQ $16, AX + CMPQ DX, AX + JNE loop16b + RET + +loop_1b: + SUBQ $1, DX // XOR 1byte backwards. + MOVB (SI)(DX*1), DI + MOVB (CX)(DX*1), AX + XORB AX, DI + MOVB DI, (BX)(DX*1) + TESTQ $7, DX // AND 7 & len, if not zero jump to loop_1b. + JNZ loop_1b + CMPQ DX, $0 // if len is 0, ret. + JE ret + TESTQ $15, DX // AND 15 & len, if zero jump to aligned. + JZ aligned + +not_aligned: + TESTQ $7, DX // AND $7 & len, if not zero jump to loop_1b. + JNE loop_1b + SUBQ $8, DX // XOR 8bytes backwards. + MOVQ (SI)(DX*1), DI + MOVQ (CX)(DX*1), AX + XORQ AX, DI + MOVQ DI, (BX)(DX*1) + CMPQ DX, $16 // if len is greater or equal 16 here, it must be aligned. + JGE aligned + +ret: + RET diff --git a/vendor/github.com/pion/transport/v2/utils/xor/xor_arm.go b/vendor/github.com/pion/transport/v2/utils/xor/xor_arm.go new file mode 100644 index 000000000..25d6b72d8 --- /dev/null +++ b/vendor/github.com/pion/transport/v2/utils/xor/xor_arm.go @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2022 The Pion community +// SPDX-License-Identifier: MIT + +//go:build !gccgo +// +build !gccgo + +// Package xor provides utility functions used by other Pion +// packages. ARM arch. +package xor + +import ( + "unsafe" + + "golang.org/x/sys/cpu" +) + +const wordSize = int(unsafe.Sizeof(uintptr(0))) // nolint:gosec +var hasNEON = cpu.ARM.HasNEON // nolint:gochecknoglobals + +func isAligned(a *byte) bool { + return uintptr(unsafe.Pointer(a))%uintptr(wordSize) == 0 +} + +// XorBytes xors the bytes in a and b. The destination should have enough +// space, otherwise xorBytes will panic. Returns the number of bytes xor'd. +// +//revive:disable-next-line +func XorBytes(dst, a, b []byte) int { + n := len(a) + if len(b) < n { + n = len(b) + } + if n == 0 { + return 0 + } + // make sure dst has enough space + _ = dst[n-1] + + if hasNEON { + xorBytesNEON32(&dst[0], &a[0], &b[0], n) + } else if isAligned(&dst[0]) && isAligned(&a[0]) && isAligned(&b[0]) { + xorBytesARM32(&dst[0], &a[0], &b[0], n) + } else { + safeXORBytes(dst, a, b, n) + } + return n +} + +// n needs to be smaller or equal than the length of a and b. +func safeXORBytes(dst, a, b []byte, n int) { + for i := 0; i < n; i++ { + dst[i] = a[i] ^ b[i] + } +} + +//go:noescape +func xorBytesARM32(dst, a, b *byte, n int) + +//go:noescape +func xorBytesNEON32(dst, a, b *byte, n int) diff --git a/vendor/github.com/pion/transport/v2/utils/xor/xor_arm.s b/vendor/github.com/pion/transport/v2/utils/xor/xor_arm.s new file mode 100644 index 000000000..5e52a2d64 --- /dev/null +++ b/vendor/github.com/pion/transport/v2/utils/xor/xor_arm.s @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: 2022 The Pion community +// SPDX-License-Identifier: MIT + +// go:build !gccgo +// +build !gccgo + +#include "textflag.h" + +// func xorBytesARM32(dst, a, b *byte, n int) +TEXT ·xorBytesARM32(SB), NOSPLIT|NOFRAME, $0 + MOVW dst+0(FP), R0 + MOVW a+4(FP), R1 + MOVW b+8(FP), R2 + MOVW n+12(FP), R3 + CMP $4, R3 + BLT less_than4 + +loop_4: + MOVW.P 4(R1), R4 + MOVW.P 4(R2), R5 + EOR R4, R5, R5 + MOVW.P R5, 4(R0) + + SUB $4, R3 + CMP $4, R3 + BGE loop_4 + +less_than4: + CMP $2, R3 + BLT less_than2 + MOVH.P 2(R1), R4 + MOVH.P 2(R2), R5 + EOR R4, R5, R5 + MOVH.P R5, 2(R0) + + SUB $2, R3 + +less_than2: + CMP $0, R3 + BEQ end + MOVB (R1), R4 + MOVB (R2), R5 + EOR R4, R5, R5 + MOVB R5, (R0) +end: + RET + +// func xorBytesNEON32(dst, a, b *byte, n int) +TEXT ·xorBytesNEON32(SB), NOSPLIT|NOFRAME, $0 + MOVW dst+0(FP), R0 + MOVW a+4(FP), R1 + MOVW b+8(FP), R2 + MOVW n+12(FP), R3 + CMP $32, R3 + BLT less_than32 + +loop_32: + WORD $0xF421020D // vld1.u8 {q0, q1}, [r1]! + WORD $0xF422420D // vld1.u8 {q2, q3}, [r2]! + WORD $0xF3004154 // veor q2, q0, q2 + WORD $0xF3026156 // veor q3, q1, q3 + WORD $0xF400420D // vst1.u8 {q2, q3}, [r0]! + + SUB $32, R3 + CMP $32, R3 + BGE loop_32 + +less_than32: + CMP $16, R3 + BLT less_than16 + WORD $0xF4210A0D // vld1.u8 q0, [r1]! + WORD $0xF4222A0D // vld1.u8 q1, [r2]! + WORD $0xF3002152 // veor q1, q0, q1 + WORD $0xF4002A0D // vst1.u8 {q1}, [r0]! + + SUB $16, R3 + +less_than16: + CMP $8, R3 + BLT less_than8 + WORD $0xF421070D // vld1.u8 d0, [r1]! + WORD $0xF422170D // vld1.u8 d1, [r2]! + WORD $0xF3001111 // veor d1, d0, d1 + WORD $0xF400170D // vst1.u8 {d1}, [r0]! + + SUB $8, R3 + +less_than8: + CMP $4, R3 + BLT less_than4 + MOVW.P 4(R1), R4 + MOVW.P 4(R2), R5 + EOR R4, R5, R5 + MOVW.P R5, 4(R0) + + SUB $4, R3 + +less_than4: + CMP $2, R3 + BLT less_than2 + MOVH.P 2(R1), R4 + MOVH.P 2(R2), R5 + EOR R4, R5, R5 + MOVH.P R5, 2(R0) + + SUB $2, R3 + +less_than2: + CMP $0, R3 + BEQ end + MOVB (R1), R4 + MOVB (R2), R5 + EOR R4, R5, R5 + MOVB R5, (R0) +end: + RET diff --git a/vendor/github.com/pion/transport/v2/utils/xor/xor_arm64.go b/vendor/github.com/pion/transport/v2/utils/xor/xor_arm64.go new file mode 100644 index 000000000..7002ab7c9 --- /dev/null +++ b/vendor/github.com/pion/transport/v2/utils/xor/xor_arm64.go @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2020 The Go Authors. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause + +//go:build !gccgo +// +build !gccgo + +// Package xor provides utility functions used by other Pion +// packages. ARM64 arch. +package xor + +// XorBytes xors the bytes in a and b. The destination should have enough +// space, otherwise xorBytes will panic. Returns the number of bytes xor'd. +// +//revive:disable-next-line +func XorBytes(dst, a, b []byte) int { + n := len(a) + if len(b) < n { + n = len(b) + } + if n == 0 { + return 0 + } + // make sure dst has enough space + _ = dst[n-1] + + xorBytesARM64(&dst[0], &a[0], &b[0], n) + return n +} + +//go:noescape +func xorBytesARM64(dst, a, b *byte, n int) diff --git a/vendor/github.com/pion/transport/v2/utils/xor/xor_arm64.s b/vendor/github.com/pion/transport/v2/utils/xor/xor_arm64.s new file mode 100644 index 000000000..0b82d0992 --- /dev/null +++ b/vendor/github.com/pion/transport/v2/utils/xor/xor_arm64.s @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2020 The Go Authors. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause + +//go:build !gccgo +// +build !gccgo + +#include "textflag.h" + +// func xorBytesARM64(dst, a, b *byte, n int) +TEXT ·xorBytesARM64(SB), NOSPLIT|NOFRAME, $0 + MOVD dst+0(FP), R0 + MOVD a+8(FP), R1 + MOVD b+16(FP), R2 + MOVD n+24(FP), R3 + CMP $64, R3 + BLT tail +loop_64: + VLD1.P 64(R1), [V0.B16, V1.B16, V2.B16, V3.B16] + VLD1.P 64(R2), [V4.B16, V5.B16, V6.B16, V7.B16] + VEOR V0.B16, V4.B16, V4.B16 + VEOR V1.B16, V5.B16, V5.B16 + VEOR V2.B16, V6.B16, V6.B16 + VEOR V3.B16, V7.B16, V7.B16 + VST1.P [V4.B16, V5.B16, V6.B16, V7.B16], 64(R0) + SUBS $64, R3 + CMP $64, R3 + BGE loop_64 +tail: + // quick end + CBZ R3, end + TBZ $5, R3, less_than32 + VLD1.P 32(R1), [V0.B16, V1.B16] + VLD1.P 32(R2), [V2.B16, V3.B16] + VEOR V0.B16, V2.B16, V2.B16 + VEOR V1.B16, V3.B16, V3.B16 + VST1.P [V2.B16, V3.B16], 32(R0) +less_than32: + TBZ $4, R3, less_than16 + LDP.P 16(R1), (R11, R12) + LDP.P 16(R2), (R13, R14) + EOR R11, R13, R13 + EOR R12, R14, R14 + STP.P (R13, R14), 16(R0) +less_than16: + TBZ $3, R3, less_than8 + MOVD.P 8(R1), R11 + MOVD.P 8(R2), R12 + EOR R11, R12, R12 + MOVD.P R12, 8(R0) +less_than8: + TBZ $2, R3, less_than4 + MOVWU.P 4(R1), R13 + MOVWU.P 4(R2), R14 + EORW R13, R14, R14 + MOVWU.P R14, 4(R0) +less_than4: + TBZ $1, R3, less_than2 + MOVHU.P 2(R1), R15 + MOVHU.P 2(R2), R16 + EORW R15, R16, R16 + MOVHU.P R16, 2(R0) +less_than2: + TBZ $0, R3, end + MOVBU (R1), R17 + MOVBU (R2), R19 + EORW R17, R19, R19 + MOVBU R19, (R0) +end: + RET diff --git a/vendor/github.com/pion/transport/v2/utils/xor/xor_generic.go b/vendor/github.com/pion/transport/v2/utils/xor/xor_generic.go new file mode 100644 index 000000000..967fed33a --- /dev/null +++ b/vendor/github.com/pion/transport/v2/utils/xor/xor_generic.go @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: 2013 The Go Authors. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2022 The Pion community +// SPDX-License-Identifier: MIT + +//go:build (!amd64 && !ppc64 && !ppc64le && !arm64 && !arm) || gccgo +// +build !amd64,!ppc64,!ppc64le,!arm64,!arm gccgo + +// Package xor provides utility functions used by other Pion +// packages. Generic arch. +package xor + +import ( + "runtime" + "unsafe" +) + +const ( + wordSize = int(unsafe.Sizeof(uintptr(0))) // nolint:gosec + supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x" // nolint:gochecknoglobals +) + +func isAligned(a *byte) bool { + return uintptr(unsafe.Pointer(a))%uintptr(wordSize) == 0 +} + +// XorBytes xors the bytes in a and b. The destination should have enough +// space, otherwise xorBytes will panic. Returns the number of bytes xor'd. +// +//revive:disable-next-line +func XorBytes(dst, a, b []byte) int { + n := len(a) + if len(b) < n { + n = len(b) + } + if n == 0 { + return 0 + } + + switch { + case supportsUnaligned: + fastXORBytes(dst, a, b, n) + case isAligned(&dst[0]) && isAligned(&a[0]) && isAligned(&b[0]): + fastXORBytes(dst, a, b, n) + default: + safeXORBytes(dst, a, b, n) + } + return n +} + +// fastXORBytes xors in bulk. It only works on architectures that +// support unaligned read/writes. +// n needs to be smaller or equal than the length of a and b. +func fastXORBytes(dst, a, b []byte, n int) { + // Assert dst has enough space + _ = dst[n-1] + + w := n / wordSize + if w > 0 { + dw := *(*[]uintptr)(unsafe.Pointer(&dst)) // nolint:gosec + aw := *(*[]uintptr)(unsafe.Pointer(&a)) // nolint:gosec + bw := *(*[]uintptr)(unsafe.Pointer(&b)) // nolint:gosec + for i := 0; i < w; i++ { + dw[i] = aw[i] ^ bw[i] + } + } + + for i := (n - n%wordSize); i < n; i++ { + dst[i] = a[i] ^ b[i] + } +} + +// n needs to be smaller or equal than the length of a and b. +func safeXORBytes(dst, a, b []byte, n int) { + for i := 0; i < n; i++ { + dst[i] = a[i] ^ b[i] + } +} diff --git a/vendor/github.com/pion/transport/v2/utils/xor/xor_ppc64x.go b/vendor/github.com/pion/transport/v2/utils/xor/xor_ppc64x.go new file mode 100644 index 000000000..bcc5926c4 --- /dev/null +++ b/vendor/github.com/pion/transport/v2/utils/xor/xor_ppc64x.go @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2018 The Go Authors. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause + +//go:build (ppc64 && !gccgo) || (ppc64le && !gccgo) +// +build ppc64,!gccgo ppc64le,!gccgo + +// Package xor provides utility functions used by other Pion +// packages. PPC64 arch. +package xor + +// XorBytes xors the bytes in a and b. The destination should have enough +// space, otherwise xorBytes will panic. Returns the number of bytes xor'd. +// +//revive:disable-next-line +func XorBytes(dst, a, b []byte) int { + n := len(a) + if len(b) < n { + n = len(b) + } + if n == 0 { + return 0 + } + _ = dst[n-1] + xorBytesVSX(&dst[0], &a[0], &b[0], n) + return n +} + +//go:noescape +func xorBytesVSX(dst, a, b *byte, n int) diff --git a/vendor/github.com/pion/transport/v2/utils/xor/xor_ppc64x.s b/vendor/github.com/pion/transport/v2/utils/xor/xor_ppc64x.s new file mode 100644 index 000000000..22763535f --- /dev/null +++ b/vendor/github.com/pion/transport/v2/utils/xor/xor_ppc64x.s @@ -0,0 +1,87 @@ +// SPDX-FileCopyrightText: 2018 The Go Authors. All rights reserved. +// SPDX-License-Identifier: BSD-3-Clause + +//go:build (ppc64 && !gccgo) || (ppc64le && !gccgo) +//+build ppc64,!gccgo ppc64le,!gccgo + +#include "textflag.h" + +// func xorBytesVSX(dst, a, b *byte, n int) +TEXT ·xorBytesVSX(SB), NOSPLIT, $0 + MOVD dst+0(FP), R3 // R3 = dst + MOVD a+8(FP), R4 // R4 = a + MOVD b+16(FP), R5 // R5 = b + MOVD n+24(FP), R6 // R6 = n + + CMPU R6, $32, CR7 // Check if n ≥ 32 bytes + MOVD R0, R8 // R8 = index + CMPU R6, $8, CR6 // Check if 8 ≤ n < 32 bytes + BLT CR6, small // Smaller than 8 + BLT CR7, xor16 // Case for 16 ≤ n < 32 bytes + + // Case for n ≥ 32 bytes +preloop32: + SRD $5, R6, R7 // Setup loop counter + MOVD R7, CTR + MOVD $16, R10 + ANDCC $31, R6, R9 // Check for tailing bytes for later +loop32: + LXVD2X (R4)(R8), VS32 // VS32 = a[i,...,i+15] + LXVD2X (R4)(R10), VS34 + LXVD2X (R5)(R8), VS33 // VS33 = b[i,...,i+15] + LXVD2X (R5)(R10), VS35 + XXLXOR VS32, VS33, VS32 // VS34 = a[] ^ b[] + XXLXOR VS34, VS35, VS34 + STXVD2X VS32, (R3)(R8) // Store to dst + STXVD2X VS34, (R3)(R10) + ADD $32, R8 // Update index + ADD $32, R10 + BC 16, 0, loop32 // bdnz loop16 + + BEQ CR0, done + + MOVD R9, R6 + CMP R6, $8 + BLT small +xor16: + CMP R6, $16 + BLT xor8 + LXVD2X (R4)(R8), VS32 + LXVD2X (R5)(R8), VS33 + XXLXOR VS32, VS33, VS32 + STXVD2X VS32, (R3)(R8) + ADD $16, R8 + ADD $-16, R6 + CMP R6, $8 + BLT small +xor8: + // Case for 8 ≤ n < 16 bytes + MOVD (R4)(R8), R14 // R14 = a[i,...,i+7] + MOVD (R5)(R8), R15 // R15 = b[i,...,i+7] + XOR R14, R15, R16 // R16 = a[] ^ b[] + SUB $8, R6 // n = n - 8 + MOVD R16, (R3)(R8) // Store to dst + ADD $8, R8 + + // Check if we're finished + CMP R6, R0 + BGT small + RET + + // Case for n < 8 bytes and tailing bytes from the + // previous cases. +small: + CMP R6, R0 + BEQ done + MOVD R6, CTR // Setup loop counter + +loop: + MOVBZ (R4)(R8), R14 // R14 = a[i] + MOVBZ (R5)(R8), R15 // R15 = b[i] + XOR R14, R15, R16 // R16 = a[i] ^ b[i] + MOVB R16, (R3)(R8) // Store to dst + ADD $1, R8 + BC 16, 0, loop // bdnz loop + +done: + RET diff --git a/vendor/github.com/pion/transport/v2/vnet/.gitignore b/vendor/github.com/pion/transport/v2/vnet/.gitignore new file mode 100644 index 000000000..f2eef3e93 --- /dev/null +++ b/vendor/github.com/pion/transport/v2/vnet/.gitignore @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + +*.sw[poe] diff --git a/vendor/github.com/pion/transport/vnet/README.md b/vendor/github.com/pion/transport/v2/vnet/README.md similarity index 72% rename from vendor/github.com/pion/transport/vnet/README.md rename to vendor/github.com/pion/transport/v2/vnet/README.md index b502f9f8e..bd0af2506 100644 --- a/vendor/github.com/pion/transport/vnet/README.md +++ b/vendor/github.com/pion/transport/v2/vnet/README.md @@ -38,8 +38,8 @@ A virtual network layer for pion. : +-------+ : ...................................... Note: - o: NIC (Netork Interface Controller) - [1]: Net implments NIC interface. + o: NIC (Network Interface Controller) + [1]: Net implements NIC interface. [2]: Root router has no NAT. All child routers have a NAT always. [3]: Router implements NIC interface for accesses from the parent router. @@ -61,12 +61,12 @@ Net provides 3 interfaces: +---------+ 1 * +-----------+ 1 * +-----------+ 1 * +------+ ..| :Router |----+------>o--| :Net |<>------|:Interface |<>------|:Addr | +---------+ | NIC +-----------+ +-----------+ +------+ - | <> (vnet.Interface) (net.Addr) + | <> (transport.Interface) (net.Addr) | | * +-----------+ 1 * +-----------+ 1 * +------+ +------>o--| :Router |<>------|:Interface |<>------|:Addr | NIC +-----------+ +-----------+ +------+ - <> (vnet.Interface) (net.Addr) + <> (transport.Interface) (net.Addr) ``` > The instance of `Net` will be the one passed around the project. @@ -76,8 +76,8 @@ Net provides 3 interfaces: ## Implementation ### Design Policy -* Each pion package should have config object which has `Net` (of type vnet.Net) property. (just like how - we distribute `LoggerFactory` throughout the pion project. +* Each pion package should have config object which has `Net` (of type `transport.Net`) property. + - Just like how we distribute `LoggerFactory` throughout the pion project. * DNS => a simple dictionary (global)? * Each Net has routing capability (a goroutine) * Use interface provided net package as much as possible @@ -86,10 +86,8 @@ Net provides 3 interfaces: - Easy to control / monitor (stats, etc) * Root router has no NAT (== Internet / WAN) * Non-root router has a NAT always -* When a Net is instantiated, it will automatically add `lo0` and `eth0` interface, and `lo0` will -have one IP address, 127.0.0.1. (this is not used in pion/ice, however) -* When a Net is added to a router, the router automatically assign an IP address for `eth0` -interface. +* When a Net is instantiated, it will automatically add `lo0` and `eth0` interface, and `lo0` will have one IP address, 127.0.0.1. (this is not used in pion/ice, however) +* When a Net is added to a router, the router automatically assign an IP address for `eth0` interface. - For simplicity * User data won't fragment, but optionally drop chunk larger than MTU * IPv6 is not supported @@ -98,13 +96,14 @@ interface. 1. Create a root router (WAN) 1. Create child routers and add to its parent (forms a tree, don't create a loop!) 1. Add instances of Net to each routers -1. Call Stop(), or Stop(), on the top router, which propages all other routers +1. Call Stop(), or Stop(), on the top router, which propagates all other routers #### Example: WAN with one endpoint (vnet) ```go import ( "net" + "github.com/pion/transport" "github.com/pion/transport/vnet" "github.com/pion/logging" ) @@ -141,7 +140,7 @@ if err = wan.Start(); err != nil { // // Stop the router. -// This will stop all internal goroutines in the router tree. +// This will stop all internal Go routines in the router tree. // (No need to call Stop() on child routers) if err = wan.Stop(); err != nil { // handle error @@ -158,17 +157,17 @@ instance (`nw` in the above example) like this: ```go type AgentConfig struct { : - Net: *vnet.Net, + Net: transport.Net, } type Agent struct { : - net: *vnet.Net, + net: transport.Net, } func NetAgent(config *AgentConfig) *Agent { if config.Net == nil { - config.Net = vnet.NewNet(nil) // defaults to native operation + config.Net = vnet.NewNet() } return &Agent { @@ -189,26 +188,25 @@ func (a *Agent) listenUDP(...) error { } ``` - ### Compatibility and Support Status -|`net`
(built-in)|`vnet`|Note| -|---|---|---| -|net.Interfaces()|a.net.Interfaces()|| -|net.InterfaceByName()|a.net.InterfaceByName()|| -|net.ResolveUDPAddr()|a.net.ResolveUDPAddr()|| -|net.ListenPacket()|a.net.ListenPacket()|| -|net.ListenUDP()|a.net.ListenUDP()|(ListenPacket() is recommended)| -|net.Listen()|a.net.Listen()|(TODO)| -|net.ListenTCP()|(not supported)|(Listen() would be recommended)| -|net.Dial()|a.net.Dial()|| -|net.DialUDP()|a.net.DialUDP()|| -|net.DialTCP()|(not supported)|| -|net.Interface|vnet.Interface|| -|net.PacketConn|(use it as-is)|| -|net.UDPConn|vnet.UDPConn|Use vnet.UDPPacketConn in your code| -|net.TCPConn|vnet.TCPConn|(TODO)|Use net.Conn in your code| -|net.Dialer|vnet.Dialer|Use a.net.CreateDialer() to create it.
The use of vnet.Dialer is currently experimental.| +|`net`
(built-in) |`vnet` |Note | +|:--- |:--- |:--- | +| net.Interfaces() | a.net.Interfaces() | | +| net.InterfaceByName() | a.net.InterfaceByName() | | +| net.ResolveUDPAddr() | a.net.ResolveUDPAddr() | | +| net.ListenPacket() | a.net.ListenPacket() | | +| net.ListenUDP() | a.net.ListenUDP() | ListenPacket() is recommended | +| net.Listen() | a.net.Listen() | TODO) | +| net.ListenTCP() | (not supported) | Listen() would be recommended | +| net.Dial() | a.net.Dial() | | +| net.DialUDP() | a.net.DialUDP() | | +| net.DialTCP() | (not supported) | | +| net.Interface | transport.Interface | | +| net.PacketConn | (use it as-is) | | +| net.UDPConn | transport.UDPConn | | +| net.TCPConn | transport.TCPConn | TODO: Use net.Conn in your code | +| net.Dialer | transport.Dialer | Use a.net.CreateDialer() to create it.
The use of vnet.Dialer is currently experimental. | > `a.net` is an instance of Net class, and types are defined under the package name `vnet` @@ -221,7 +219,7 @@ func (a *Agent) listenUDP(...) error { * Support of IPv6 * Write a bunch of examples for building virtual networks. * Add network impairment features (on Router) - - Introduce lantecy / jitter + - Introduce latency / jitter - Packet filtering handler (allow selectively drop packets, etc.) * Add statistics data retrieval - Total number of packets forward by each router @@ -230,10 +228,4 @@ func (a *Agent) listenUDP(...) error { ## References * [Comparing Simulated Packet Loss and RealWorld Network Congestion](https://www.riverbed.com/document/fpo/WhitePaper-Riverbed-SimulatedPacketLoss.pdf) - -### Code experiments -* [CIDR and IPMask](https://play.golang.org/p/B7OBhkZqjmj) -* [Test with net.IP](https://play.golang.org/p/AgXd23wKY4W) -* [ListenPacket](https://play.golang.org/p/d4vasbnRimQ) -* [isDottedIP()](https://play.golang.org/p/t4aZ47TgJfO) -* [SplitHostPort](https://play.golang.org/p/JtvurlcMbhn) +* [wireguard-go using GVisor's netstack](https://github.com/WireGuard/wireguard-go/tree/master/tun/netstack) \ No newline at end of file diff --git a/vendor/github.com/pion/transport/vnet/chunk.go b/vendor/github.com/pion/transport/v2/vnet/chunk.go similarity index 96% rename from vendor/github.com/pion/transport/vnet/chunk.go rename to vendor/github.com/pion/transport/v2/vnet/chunk.go index 7a87a2fd7..9f59c9ced 100644 --- a/vendor/github.com/pion/transport/vnet/chunk.go +++ b/vendor/github.com/pion/transport/v2/vnet/chunk.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package vnet import ( @@ -155,7 +158,7 @@ func (c *chunkUDP) Clone() Chunk { } func (c *chunkUDP) Network() string { - return udpString + return udp } func (c *chunkUDP) String() string { @@ -170,7 +173,7 @@ func (c *chunkUDP) String() string { } func (c *chunkUDP) setSourceAddr(address string) error { - addr, err := net.ResolveUDPAddr(udpString, address) + addr, err := net.ResolveUDPAddr(udp, address) if err != nil { return err } @@ -180,7 +183,7 @@ func (c *chunkUDP) setSourceAddr(address string) error { } func (c *chunkUDP) setDestinationAddr(address string) error { - addr, err := net.ResolveUDPAddr(udpString, address) + addr, err := net.ResolveUDPAddr(udp, address) if err != nil { return err } diff --git a/vendor/github.com/pion/transport/vnet/chunk_queue.go b/vendor/github.com/pion/transport/v2/vnet/chunk_queue.go similarity index 91% rename from vendor/github.com/pion/transport/vnet/chunk_queue.go rename to vendor/github.com/pion/transport/v2/vnet/chunk_queue.go index c424307ac..2393254a6 100644 --- a/vendor/github.com/pion/transport/vnet/chunk_queue.go +++ b/vendor/github.com/pion/transport/v2/vnet/chunk_queue.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package vnet import ( diff --git a/vendor/github.com/pion/transport/vnet/conn.go b/vendor/github.com/pion/transport/v2/vnet/conn.go similarity index 73% rename from vendor/github.com/pion/transport/vnet/conn.go rename to vendor/github.com/pion/transport/v2/vnet/conn.go index f4b8b9290..8f6f34269 100644 --- a/vendor/github.com/pion/transport/vnet/conn.go +++ b/vendor/github.com/pion/transport/v2/vnet/conn.go @@ -1,12 +1,18 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package vnet import ( "errors" + "fmt" "io" "math" "net" "sync" "time" + + "github.com/pion/transport/v2" ) const ( @@ -22,14 +28,6 @@ var ( errNoRemAddr = errors.New("no remAddr defined") ) -// UDPPacketConn is packet-oriented connection for UDP. -type UDPPacketConn interface { - net.PacketConn - Read(b []byte) (int, error) - RemoteAddr() net.Addr - Write(b []byte) (int, error) -} - // vNet implements this type connObserver interface { write(c Chunk) error @@ -38,7 +36,7 @@ type connObserver interface { } // UDPConn is the implementation of the Conn and PacketConn interfaces for UDP network connections. -// comatible with net.PacketConn and net.Conn +// compatible with net.PacketConn and net.Conn type UDPConn struct { locAddr *net.UDPAddr // read-only remAddr *net.UDPAddr // read-only @@ -49,6 +47,8 @@ type UDPConn struct { readTimer *time.Timer // thread-safe } +var _ transport.UDPConn = &UDPConn{} + func newUDPConn(locAddr, remAddr *net.UDPAddr, obs connObserver) (*UDPConn, error) { if obs == nil { return nil, errObsCannotBeNil @@ -63,6 +63,84 @@ func newUDPConn(locAddr, remAddr *net.UDPAddr, obs connObserver) (*UDPConn, erro }, nil } +// Close closes the connection. +// Any blocked ReadFrom or WriteTo operations will be unblocked and return errors. +func (c *UDPConn) Close() error { + c.mu.Lock() + defer c.mu.Unlock() + + if c.closed { + return errAlreadyClosed + } + c.closed = true + close(c.readCh) + + c.obs.onClosed(c.locAddr) + return nil +} + +// LocalAddr returns the local network address. +func (c *UDPConn) LocalAddr() net.Addr { + return c.locAddr +} + +// RemoteAddr returns the remote network address. +func (c *UDPConn) RemoteAddr() net.Addr { + return c.remAddr +} + +// SetDeadline sets the read and write deadlines associated +// with the connection. It is equivalent to calling both +// SetReadDeadline and SetWriteDeadline. +// +// A deadline is an absolute time after which I/O operations +// fail with a timeout (see type Error) instead of +// blocking. The deadline applies to all future and pending +// I/O, not just the immediately following call to ReadFrom or +// WriteTo. After a deadline has been exceeded, the connection +// can be refreshed by setting a deadline in the future. +// +// An idle timeout can be implemented by repeatedly extending +// the deadline after successful ReadFrom or WriteTo calls. +// +// A zero value for t means I/O operations will not time out. +func (c *UDPConn) SetDeadline(t time.Time) error { + return c.SetReadDeadline(t) +} + +// SetReadDeadline sets the deadline for future ReadFrom calls +// and any currently-blocked ReadFrom call. +// A zero value for t means ReadFrom will not time out. +func (c *UDPConn) SetReadDeadline(t time.Time) error { + var d time.Duration + var noDeadline time.Time + if t == noDeadline { + d = time.Duration(math.MaxInt64) + } else { + d = time.Until(t) + } + c.readTimer.Reset(d) + return nil +} + +// SetWriteDeadline sets the deadline for future WriteTo calls +// and any currently-blocked WriteTo call. +// Even if write times out, it may return n > 0, indicating that +// some of the data was successfully written. +// A zero value for t means WriteTo will not time out. +func (c *UDPConn) SetWriteDeadline(time.Time) error { + // Write never blocks. + return nil +} + +// Read reads data from the connection. +// Read can be made to time out and return an Error with Timeout() == true +// after a fixed time limit; see SetDeadline and SetReadDeadline. +func (c *UDPConn) Read(b []byte) (int, error) { + n, _, err := c.ReadFrom(b) + return n, err +} + // ReadFrom reads a packet from the connection, // copying the payload into p. It returns the number of // bytes copied into p and the return address that @@ -113,6 +191,40 @@ loop: } } +// ReadFromUDP acts like ReadFrom but returns a UDPAddr. +func (c *UDPConn) ReadFromUDP(b []byte) (int, *net.UDPAddr, error) { + n, addr, err := c.ReadFrom(b) + + udpAddr, ok := addr.(*net.UDPAddr) + if !ok { + return -1, nil, fmt.Errorf("%w: %s", transport.ErrNotUDPAddress, addr) + } + + return n, udpAddr, err +} + +// ReadMsgUDP reads a message from c, copying the payload into b and +// the associated out-of-band data into oob. It returns the number of +// bytes copied into b, the number of bytes copied into oob, the flags +// that were set on the message and the source address of the message. +// +// The packages golang.org/x/net/ipv4 and golang.org/x/net/ipv6 can be +// used to manipulate IP-level socket options in oob. +func (c *UDPConn) ReadMsgUDP([]byte, []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) { + return -1, -1, -1, nil, transport.ErrNotSupported +} + +// Write writes data to the connection. +// Write can be made to time out and return an Error with Timeout() == true +// after a fixed time limit; see SetDeadline and SetWriteDeadline. +func (c *UDPConn) Write(b []byte) (int, error) { + if c.remAddr == nil { + return 0, errNoRemAddr + } + + return c.WriteTo(b, c.remAddr) +} + // WriteTo writes a packet with payload p to addr. // WriteTo can be made to time out and return // an Error with Timeout() == true after a fixed time limit; @@ -142,93 +254,33 @@ func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { return len(p), nil } -// Close closes the connection. -// Any blocked ReadFrom or WriteTo operations will be unblocked and return errors. -func (c *UDPConn) Close() error { - c.mu.Lock() - defer c.mu.Unlock() - - if c.closed { - return errAlreadyClosed - } - c.closed = true - close(c.readCh) - - c.obs.onClosed(c.locAddr) - return nil +// WriteToUDP acts like WriteTo but takes a UDPAddr. +func (c *UDPConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) { + return c.WriteTo(b, addr) } -// LocalAddr returns the local network address. -func (c *UDPConn) LocalAddr() net.Addr { - return c.locAddr -} - -// SetDeadline sets the read and write deadlines associated -// with the connection. It is equivalent to calling both -// SetReadDeadline and SetWriteDeadline. +// WriteMsgUDP writes a message to addr via c if c isn't connected, or +// to c's remote address if c is connected (in which case addr must be +// nil). The payload is copied from b and the associated out-of-band +// data is copied from oob. It returns the number of payload and +// out-of-band bytes written. // -// A deadline is an absolute time after which I/O operations -// fail with a timeout (see type Error) instead of -// blocking. The deadline applies to all future and pending -// I/O, not just the immediately following call to ReadFrom or -// WriteTo. After a deadline has been exceeded, the connection -// can be refreshed by setting a deadline in the future. -// -// An idle timeout can be implemented by repeatedly extending -// the deadline after successful ReadFrom or WriteTo calls. -// -// A zero value for t means I/O operations will not time out. -func (c *UDPConn) SetDeadline(t time.Time) error { - return c.SetReadDeadline(t) +// The packages golang.org/x/net/ipv4 and golang.org/x/net/ipv6 can be +// used to manipulate IP-level socket options in oob. +func (c *UDPConn) WriteMsgUDP([]byte, []byte, *net.UDPAddr) (n, oobn int, err error) { + return -1, -1, transport.ErrNotSupported } -// SetReadDeadline sets the deadline for future ReadFrom calls -// and any currently-blocked ReadFrom call. -// A zero value for t means ReadFrom will not time out. -func (c *UDPConn) SetReadDeadline(t time.Time) error { - var d time.Duration - var noDeadline time.Time - if t == noDeadline { - d = time.Duration(math.MaxInt64) - } else { - d = time.Until(t) - } - c.readTimer.Reset(d) - return nil +// SetReadBuffer sets the size of the operating system's +// receive buffer associated with the connection. +func (c *UDPConn) SetReadBuffer(int) error { + return transport.ErrNotSupported } -// SetWriteDeadline sets the deadline for future WriteTo calls -// and any currently-blocked WriteTo call. -// Even if write times out, it may return n > 0, indicating that -// some of the data was successfully written. -// A zero value for t means WriteTo will not time out. -func (c *UDPConn) SetWriteDeadline(t time.Time) error { - // Write never blocks. - return nil -} - -// Read reads data from the connection. -// Read can be made to time out and return an Error with Timeout() == true -// after a fixed time limit; see SetDeadline and SetReadDeadline. -func (c *UDPConn) Read(b []byte) (int, error) { - n, _, err := c.ReadFrom(b) - return n, err -} - -// RemoteAddr returns the remote network address. -func (c *UDPConn) RemoteAddr() net.Addr { - return c.remAddr -} - -// Write writes data to the connection. -// Write can be made to time out and return an Error with Timeout() == true -// after a fixed time limit; see SetDeadline and SetWriteDeadline. -func (c *UDPConn) Write(b []byte) (int, error) { - if c.remAddr == nil { - return 0, errNoRemAddr - } - - return c.WriteTo(b, c.remAddr) +// SetWriteBuffer sets the size of the operating system's +// transmit buffer associated with the connection. +func (c *UDPConn) SetWriteBuffer(int) error { + return transport.ErrNotSupported } func (c *UDPConn) onInboundChunk(chunk Chunk) { diff --git a/vendor/github.com/pion/transport/vnet/conn_map.go b/vendor/github.com/pion/transport/v2/vnet/conn_map.go similarity index 83% rename from vendor/github.com/pion/transport/vnet/conn_map.go rename to vendor/github.com/pion/transport/v2/vnet/conn_map.go index d52818d27..da22483ef 100644 --- a/vendor/github.com/pion/transport/vnet/conn_map.go +++ b/vendor/github.com/pion/transport/v2/vnet/conn_map.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package vnet import ( @@ -27,7 +30,7 @@ func (m *udpConnMap) insert(conn *UDPConn) error { m.mutex.Lock() defer m.mutex.Unlock() - udpAddr := conn.LocalAddr().(*net.UDPAddr) + udpAddr := conn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert // check if the port has a listener conns, ok := m.portMap[udpAddr.Port] @@ -37,7 +40,7 @@ func (m *udpConnMap) insert(conn *UDPConn) error { } for _, conn := range conns { - laddr := conn.LocalAddr().(*net.UDPAddr) + laddr := conn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert if laddr.IP.IsUnspecified() || laddr.IP.Equal(udpAddr.IP) { return errAddressAlreadyInUse } @@ -56,7 +59,7 @@ func (m *udpConnMap) find(addr net.Addr) (*UDPConn, bool) { m.mutex.Lock() // could be RLock, but we have delete() op defer m.mutex.Unlock() - udpAddr := addr.(*net.UDPAddr) + udpAddr := addr.(*net.UDPAddr) //nolint:forcetypeassert if conns, ok := m.portMap[udpAddr.Port]; ok { if udpAddr.IP.IsUnspecified() { @@ -70,7 +73,7 @@ func (m *udpConnMap) find(addr net.Addr) (*UDPConn, bool) { } for _, conn := range conns { - laddr := conn.LocalAddr().(*net.UDPAddr) + laddr := conn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert if laddr.IP.IsUnspecified() || laddr.IP.Equal(udpAddr.IP) { return conn, ok } @@ -84,7 +87,7 @@ func (m *udpConnMap) delete(addr net.Addr) error { m.mutex.Lock() defer m.mutex.Unlock() - udpAddr := addr.(*net.UDPAddr) + udpAddr := addr.(*net.UDPAddr) //nolint:forcetypeassert conns, ok := m.portMap[udpAddr.Port] if !ok { @@ -100,7 +103,7 @@ func (m *udpConnMap) delete(addr net.Addr) error { newConns := []*UDPConn{} for _, conn := range conns { - laddr := conn.LocalAddr().(*net.UDPAddr) + laddr := conn.LocalAddr().(*net.UDPAddr) //nolint:forcetypeassert if laddr.IP.IsUnspecified() { // This can't happen! return errCannotRemoveUnspecifiedIP diff --git a/vendor/github.com/pion/transport/vnet/delay_filter.go b/vendor/github.com/pion/transport/v2/vnet/delay_filter.go similarity index 90% rename from vendor/github.com/pion/transport/vnet/delay_filter.go rename to vendor/github.com/pion/transport/v2/vnet/delay_filter.go index 327f40618..119e34eac 100644 --- a/vendor/github.com/pion/transport/vnet/delay_filter.go +++ b/vendor/github.com/pion/transport/v2/vnet/delay_filter.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package vnet import ( @@ -47,7 +50,7 @@ func (f *DelayFilter) Run(ctx context.Context) { case <-ctx.Done(): return case <-f.push: - next := f.queue.peek().(timedChunk) + next := f.queue.peek().(timedChunk) //nolint:forcetypeassert if !timer.Stop() { <-timer.C } diff --git a/vendor/github.com/pion/transport/vnet/errors.go b/vendor/github.com/pion/transport/v2/vnet/errors.go similarity index 71% rename from vendor/github.com/pion/transport/vnet/errors.go rename to vendor/github.com/pion/transport/v2/vnet/errors.go index d0e9394f5..22c7c2d36 100644 --- a/vendor/github.com/pion/transport/vnet/errors.go +++ b/vendor/github.com/pion/transport/v2/vnet/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package vnet type timeoutError struct { diff --git a/vendor/github.com/pion/transport/vnet/loss_filter.go b/vendor/github.com/pion/transport/v2/vnet/loss_filter.go similarity index 86% rename from vendor/github.com/pion/transport/vnet/loss_filter.go rename to vendor/github.com/pion/transport/v2/vnet/loss_filter.go index b5a23f405..4b2d75f38 100644 --- a/vendor/github.com/pion/transport/vnet/loss_filter.go +++ b/vendor/github.com/pion/transport/v2/vnet/loss_filter.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package vnet import ( diff --git a/vendor/github.com/pion/transport/vnet/nat.go b/vendor/github.com/pion/transport/v2/vnet/nat.go similarity index 93% rename from vendor/github.com/pion/transport/vnet/nat.go rename to vendor/github.com/pion/transport/v2/vnet/nat.go index 4ece5faad..a4be36ecd 100644 --- a/vendor/github.com/pion/transport/vnet/nat.go +++ b/vendor/github.com/pion/transport/v2/vnet/nat.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package vnet import ( @@ -22,8 +25,9 @@ var ( // EndpointDependencyType defines a type of behavioral dependendency on the // remote endpoint's IP address or port number. This is used for the two // kinds of behaviors: -// - Port mapping behavior -// - Filtering behavior +// - Port mapping behavior +// - Filtering behavior +// // See: https://tools.ietf.org/html/rfc4787 type EndpointDependencyType uint8 @@ -58,7 +62,7 @@ type NATType struct { Mode NATMode MappingBehavior EndpointDependencyType FilteringBehavior EndpointDependencyType - Hairpining bool // Not implemented yet + Hairpinning bool // Not implemented yet PortPreservation bool // Not implemented yet MappingLifeTime time.Duration } @@ -151,14 +155,14 @@ func (n *networkAddressTranslator) translateOutbound(from Chunk) (Chunk, error) to := from.Clone() - if from.Network() == udpString { + if from.Network() == udp { if n.natType.Mode == NATModeNAT1To1 { // 1:1 NAT behavior - srcAddr := from.SourceAddr().(*net.UDPAddr) + srcAddr := from.SourceAddr().(*net.UDPAddr) //nolint:forcetypeassert srcIP := n.getPairedMappedIP(srcAddr.IP) if srcIP == nil { n.log.Debugf("[%s] drop outbound chunk %s with not route", n.name, from.String()) - return nil, nil // silently discard + return nil, nil // nolint:nilnil } srcPort := srcAddr.Port if err := to.setSourceAddr(fmt.Sprintf("%s:%d", srcIP.String(), srcPort)); err != nil { @@ -206,16 +210,16 @@ func (n *networkAddressTranslator) translateOutbound(from Chunk) (Chunk, error) iKey := fmt.Sprintf("udp:%s", m.mapped) - n.log.Debugf("[%s] created a new NAT binding oKey=%s iKey=%s\n", + n.log.Debugf("[%s] created a new NAT binding oKey=%s iKey=%s", n.name, oKey, iKey) m.filters[filterKey] = struct{}{} - n.log.Debugf("[%s] permit access from %s to %s\n", n.name, filterKey, m.mapped) + n.log.Debugf("[%s] permit access from %s to %s", n.name, filterKey, m.mapped) n.inboundMap[iKey] = m } else if _, ok := m.filters[filterKey]; !ok { - n.log.Debugf("[%s] permit access from %s to %s\n", n.name, filterKey, m.mapped) + n.log.Debugf("[%s] permit access from %s to %s", n.name, filterKey, m.mapped) m.filters[filterKey] = struct{}{} } @@ -238,15 +242,15 @@ func (n *networkAddressTranslator) translateInbound(from Chunk) (Chunk, error) { to := from.Clone() - if from.Network() == udpString { + if from.Network() == udp { if n.natType.Mode == NATModeNAT1To1 { // 1:1 NAT behavior - dstAddr := from.DestinationAddr().(*net.UDPAddr) + dstAddr := from.DestinationAddr().(*net.UDPAddr) //nolint:forcetypeassert dstIP := n.getPairedLocalIP(dstAddr.IP) if dstIP == nil { return nil, fmt.Errorf("drop %s as %w", from.String(), errNoAssociatedLocalAddress) } - dstPort := from.DestinationAddr().(*net.UDPAddr).Port + dstPort := from.DestinationAddr().(*net.UDPAddr).Port //nolint:forcetypeassert if err := to.setDestinationAddr(fmt.Sprintf("%s:%d", dstIP, dstPort)); err != nil { return nil, err } diff --git a/vendor/github.com/pion/transport/vnet/net.go b/vendor/github.com/pion/transport/v2/vnet/net.go similarity index 62% rename from vendor/github.com/pion/transport/vnet/net.go rename to vendor/github.com/pion/transport/v2/vnet/net.go index 4dc6a2a06..5d0938e64 100644 --- a/vendor/github.com/pion/transport/vnet/net.go +++ b/vendor/github.com/pion/transport/v2/vnet/net.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package vnet import ( @@ -9,27 +12,28 @@ import ( "strconv" "strings" "sync" + + "github.com/pion/transport/v2" ) const ( lo0String = "lo0String" - udpString = "udp" + udp = "udp" + udp4 = "udp4" ) var ( macAddrCounter uint64 = 0xBEEFED910200 //nolint:gochecknoglobals errNoInterface = errors.New("no interface is available") - errNotFound = errors.New("not found") errUnexpectedNetwork = errors.New("unexpected network") errCantAssignRequestedAddr = errors.New("can't assign requested address") errUnknownNetwork = errors.New("unknown network") errNoRouterLinked = errors.New("no router linked") errInvalidPortNumber = errors.New("invalid port number") errUnexpectedTypeSwitchFailure = errors.New("unexpected type-switch failure") - errBindFailerFor = errors.New("bind failed for") + errBindFailedFor = errors.New("bind failed for") errEndPortLessThanStart = errors.New("end port is less than the start") errPortSpaceExhausted = errors.New("port space exhausted") - errVNetDisabled = errors.New("vnet is not enabled") ) func newMACAddress() net.HardwareAddr { @@ -39,15 +43,20 @@ func newMACAddress() net.HardwareAddr { return b[2:] } -type vNet struct { - interfaces []*Interface // read-only - staticIPs []net.IP // read-only - router *Router // read-only - udpConns *udpConnMap // read-only +// Net represents a local network stack equivalent to a set of layers from NIC +// up to the transport (UDP / TCP) layer. +type Net struct { + interfaces []*transport.Interface // read-only + staticIPs []net.IP // read-only + router *Router // read-only + udpConns *udpConnMap // read-only mutex sync.RWMutex } -func (v *vNet) _getInterfaces() ([]*Interface, error) { +// Compile-time assertion +var _ transport.Net = &Net{} + +func (v *Net) _getInterfaces() ([]*transport.Interface, error) { if len(v.interfaces) == 0 { return nil, errNoInterface } @@ -55,7 +64,8 @@ func (v *vNet) _getInterfaces() ([]*Interface, error) { return v.interfaces, nil } -func (v *vNet) getInterfaces() ([]*Interface, error) { +// Interfaces returns a list of the system's network interfaces. +func (v *Net) Interfaces() ([]*transport.Interface, error) { v.mutex.RLock() defer v.mutex.RUnlock() @@ -63,7 +73,7 @@ func (v *vNet) getInterfaces() ([]*Interface, error) { } // caller must hold the mutex (read) -func (v *vNet) _getInterface(ifName string) (*Interface, error) { +func (v *Net) _getInterface(ifName string) (*transport.Interface, error) { ifs, err := v._getInterfaces() if err != nil { return nil, err @@ -74,18 +84,38 @@ func (v *vNet) _getInterface(ifName string) (*Interface, error) { } } - return nil, fmt.Errorf("interface %s %w", ifName, errNotFound) + return nil, fmt.Errorf("%w: %s", transport.ErrInterfaceNotFound, ifName) } -func (v *vNet) getInterface(ifName string) (*Interface, error) { +func (v *Net) getInterface(ifName string) (*transport.Interface, error) { v.mutex.RLock() defer v.mutex.RUnlock() return v._getInterface(ifName) } +// InterfaceByIndex returns the interface specified by index. +// +// On Solaris, it returns one of the logical network interfaces +// sharing the logical data link; for more precision use +// InterfaceByName. +func (v *Net) InterfaceByIndex(index int) (*transport.Interface, error) { + for _, ifc := range v.interfaces { + if ifc.Index == index { + return ifc, nil + } + } + + return nil, fmt.Errorf("%w: index=%d", transport.ErrInterfaceNotFound, index) +} + +// InterfaceByName returns the interface specified by name. +func (v *Net) InterfaceByName(ifName string) (*transport.Interface, error) { + return v.getInterface(ifName) +} + // caller must hold the mutex -func (v *vNet) getAllIPAddrs(ipv6 bool) []net.IP { +func (v *Net) getAllIPAddrs(ipv6 bool) []net.IP { ips := []net.IP{} for _, ifc := range v.interfaces { @@ -115,7 +145,7 @@ func (v *vNet) getAllIPAddrs(ipv6 bool) []net.IP { return ips } -func (v *vNet) setRouter(r *Router) error { +func (v *Net) setRouter(r *Router) error { v.mutex.Lock() defer v.mutex.Unlock() @@ -123,11 +153,11 @@ func (v *vNet) setRouter(r *Router) error { return nil } -func (v *vNet) onInboundChunk(c Chunk) { +func (v *Net) onInboundChunk(c Chunk) { v.mutex.Lock() defer v.mutex.Unlock() - if c.Network() == udpString { + if c.Network() == udp { if conn, ok := v.udpConns.find(c.DestinationAddr()); ok { conn.onInboundChunk(c) } @@ -135,9 +165,9 @@ func (v *vNet) onInboundChunk(c Chunk) { } // caller must hold the mutex -func (v *vNet) _dialUDP(network string, locAddr, remAddr *net.UDPAddr) (UDPPacketConn, error) { +func (v *Net) _dialUDP(network string, locAddr, remAddr *net.UDPAddr) (transport.UDPConn, error) { // validate network - if network != udpString && network != "udp4" { + if network != udp && network != udp4 { return nil, fmt.Errorf("%w: %s", errUnexpectedNetwork, network) } @@ -193,11 +223,12 @@ func (v *vNet) _dialUDP(network string, locAddr, remAddr *net.UDPAddr) (UDPPacke return conn, nil } -func (v *vNet) listenPacket(network string, address string) (UDPPacketConn, error) { +// ListenPacket announces on the local network address. +func (v *Net) ListenPacket(network string, address string) (net.PacketConn, error) { v.mutex.Lock() defer v.mutex.Unlock() - locAddr, err := v.resolveUDPAddr(network, address) + locAddr, err := v.ResolveUDPAddr(network, address) if err != nil { return nil, err } @@ -205,25 +236,28 @@ func (v *vNet) listenPacket(network string, address string) (UDPPacketConn, erro return v._dialUDP(network, locAddr, nil) } -func (v *vNet) listenUDP(network string, locAddr *net.UDPAddr) (UDPPacketConn, error) { +// ListenUDP acts like ListenPacket for UDP networks. +func (v *Net) ListenUDP(network string, locAddr *net.UDPAddr) (transport.UDPConn, error) { v.mutex.Lock() defer v.mutex.Unlock() return v._dialUDP(network, locAddr, nil) } -func (v *vNet) dialUDP(network string, locAddr, remAddr *net.UDPAddr) (UDPPacketConn, error) { +// DialUDP acts like Dial for UDP networks. +func (v *Net) DialUDP(network string, locAddr, remAddr *net.UDPAddr) (transport.UDPConn, error) { v.mutex.Lock() defer v.mutex.Unlock() return v._dialUDP(network, locAddr, remAddr) } -func (v *vNet) dial(network string, address string) (UDPPacketConn, error) { +// Dial connects to the address on the named network. +func (v *Net) Dial(network string, address string) (net.Conn, error) { v.mutex.Lock() defer v.mutex.Unlock() - remAddr, err := v.resolveUDPAddr(network, address) + remAddr, err := v.ResolveUDPAddr(network, address) if err != nil { return nil, err } @@ -236,8 +270,37 @@ func (v *vNet) dial(network string, address string) (UDPPacketConn, error) { return v._dialUDP(network, locAddr, remAddr) } -func (v *vNet) resolveUDPAddr(network, address string) (*net.UDPAddr, error) { - if network != udpString && network != "udp4" { +// ResolveIPAddr returns an address of IP end point. +func (v *Net) ResolveIPAddr(_, address string) (*net.IPAddr, error) { + var err error + + // Check if host is a domain name + ip := net.ParseIP(address) + if ip == nil { + address = strings.ToLower(address) + if address == "localhost" { + ip = net.IPv4(127, 0, 0, 1) + } else { + // host is a domain name. resolve IP address by the name + if v.router == nil { + return nil, errNoRouterLinked + } + + ip, err = v.router.resolver.lookUp(address) + if err != nil { + return nil, err + } + } + } + + return &net.IPAddr{ + IP: ip, + }, nil +} + +// ResolveUDPAddr returns an address of UDP end point. +func (v *Net) ResolveUDPAddr(network, address string) (*net.UDPAddr, error) { + if network != udp && network != udp4 { return nil, fmt.Errorf("%w %s", errUnknownNetwork, network) } @@ -246,23 +309,9 @@ func (v *vNet) resolveUDPAddr(network, address string) (*net.UDPAddr, error) { return nil, err } - // Check if host is a domain name - ip := net.ParseIP(host) - if ip == nil { - host = strings.ToLower(host) - if host == "localhost" { - ip = net.IPv4(127, 0, 0, 1) - } else { - // host is a domain name. resolve IP address by the name - if v.router == nil { - return nil, errNoRouterLinked - } - - ip, err = v.router.resolver.lookUp(host) - if err != nil { - return nil, err - } - } + ipAddress, err := v.ResolveIPAddr("ip", host) + if err != nil { + return nil, err } port, err := strconv.Atoi(sPort) @@ -271,15 +320,46 @@ func (v *vNet) resolveUDPAddr(network, address string) (*net.UDPAddr, error) { } udpAddr := &net.UDPAddr{ - IP: ip, + IP: ipAddress.IP, + Zone: ipAddress.Zone, Port: port, } return udpAddr, nil } -func (v *vNet) write(c Chunk) error { - if c.Network() == udpString { +// ResolveTCPAddr returns an address of TCP end point. +func (v *Net) ResolveTCPAddr(network, address string) (*net.TCPAddr, error) { + if network != udp && network != "udp4" { + return nil, fmt.Errorf("%w %s", errUnknownNetwork, network) + } + + host, sPort, err := net.SplitHostPort(address) + if err != nil { + return nil, err + } + + ipAddr, err := v.ResolveIPAddr("ip", host) + if err != nil { + return nil, err + } + + port, err := strconv.Atoi(sPort) + if err != nil { + return nil, errInvalidPortNumber + } + + udpAddr := &net.TCPAddr{ + IP: ipAddr.IP, + Zone: ipAddr.Zone, + Port: port, + } + + return udpAddr, nil +} + +func (v *Net) write(c Chunk) error { + if c.Network() == udp { if udp, ok := c.(*chunkUDP); ok { if c.getDestinationIP().IsLoopback() { if conn, ok := v.udpConns.find(udp.DestinationAddr()); ok { @@ -300,8 +380,8 @@ func (v *vNet) write(c Chunk) error { return nil } -func (v *vNet) onClosed(addr net.Addr) { - if addr.Network() == udpString { +func (v *Net) onClosed(addr net.Addr) { + if addr.Network() == udp { //nolint:errcheck v.udpConns.delete(addr) // #nosec } @@ -311,7 +391,7 @@ func (v *vNet) onClosed(addr net.Addr) { // is any IP address ("0.0.0.0" or "::"). If locIP is a non-any addr, // this method simply returns locIP. // caller must hold the mutex -func (v *vNet) determineSourceIP(locIP, dstIP net.IP) net.IP { +func (v *Net) determineSourceIP(locIP, dstIP net.IP) net.IP { if locIP != nil && !locIP.IsUnspecified() { return locIP } @@ -343,7 +423,7 @@ func (v *vNet) determineSourceIP(locIP, dstIP net.IP) net.IP { } for _, addr := range addrs { - ip := addr.(*net.IPNet).IP + ip := addr.(*net.IPNet).IP //nolint:forcetypeassert if findIPv4 { if ip.To4() != nil { srcIP = ip @@ -362,7 +442,7 @@ func (v *vNet) determineSourceIP(locIP, dstIP net.IP) net.IP { } // caller must hold the mutex -func (v *vNet) hasIPAddr(ip net.IP) bool { //nolint:gocognit +func (v *Net) hasIPAddr(ip net.IP) bool { //nolint:gocognit for _, ifc := range v.interfaces { if addrs, err := ifc.Addrs(); err == nil { for _, addr := range addrs { @@ -397,7 +477,7 @@ func (v *vNet) hasIPAddr(ip net.IP) bool { //nolint:gocognit } // caller must hold the mutex -func (v *vNet) allocateLocalAddr(ip net.IP, port int) error { +func (v *Net) allocateLocalAddr(ip net.IP, port int) error { // gather local IP addresses to bind var ips []net.IP if ip.IsUnspecified() { @@ -407,7 +487,7 @@ func (v *vNet) allocateLocalAddr(ip net.IP, port int) error { } if len(ips) == 0 { - return fmt.Errorf("%w %s", errBindFailerFor, ip.String()) + return fmt.Errorf("%w %s", errBindFailedFor, ip.String()) } // check if all these transport addresses are not in use @@ -419,7 +499,7 @@ func (v *vNet) allocateLocalAddr(ip net.IP, port int) error { if _, ok := v.udpConns.find(addr); ok { return &net.OpError{ Op: "bind", - Net: udpString, + Net: udp, Addr: addr, Err: fmt.Errorf("bind: %w", errAddressAlreadyInUse), } @@ -430,7 +510,7 @@ func (v *vNet) allocateLocalAddr(ip net.IP, port int) error { } // caller must hold the mutex -func (v *vNet) assignPort(ip net.IP, start, end int) (int, error) { +func (v *Net) assignPort(ip net.IP, start, end int) (int, error) { // choose randomly from the range between start and end (inclusive) if end < start { return -1, errEndPortLessThanStart @@ -450,6 +530,10 @@ func (v *vNet) assignPort(ip net.IP, start, end int) (int, error) { return -1, errPortSpaceExhausted } +func (v *Net) getStaticIPs() []net.IP { + return v.staticIPs +} + // NetConfig is a bag of configuration parameters passed to NewNet(). type NetConfig struct { // StaticIPs is an array of static IP addresses to be assigned for this Net. @@ -461,51 +545,25 @@ type NetConfig struct { StaticIP string } -// Net represents a local network stack euivalent to a set of layers from NIC -// up to the transport (UDP / TCP) layer. -type Net struct { - v *vNet - ifs []*Interface -} - -// NewNet creates an instance of Net. -// If config is nil, the virtual network is disabled. (uses corresponding -// net.Xxxx() operations. +// NewNet creates an instance of a virtual network. +// // By design, it always have lo0 and eth0 interfaces. // The lo0 has the address 127.0.0.1 assigned by default. // IP address for eth0 will be assigned when this Net is added to a router. -func NewNet(config *NetConfig) *Net { - if config == nil { - ifs := []*Interface{} - if orgIfs, err := net.Interfaces(); err == nil { - for _, orgIfc := range orgIfs { - ifc := NewInterface(orgIfc) - if addrs, err := orgIfc.Addrs(); err == nil { - for _, addr := range addrs { - ifc.AddAddr(addr) - } - } - - ifs = append(ifs, ifc) - } - } - - return &Net{ifs: ifs} - } - - lo0 := NewInterface(net.Interface{ +func NewNet(config *NetConfig) (*Net, error) { + lo0 := transport.NewInterface(net.Interface{ Index: 1, MTU: 16384, Name: lo0String, HardwareAddr: nil, Flags: net.FlagUp | net.FlagLoopback | net.FlagMulticast, }) - lo0.AddAddr(&net.IPNet{ + lo0.AddAddress(&net.IPNet{ IP: net.ParseIP("127.0.0.1"), Mask: net.CIDRMask(8, 32), }) - eth0 := NewInterface(net.Interface{ + eth0 := transport.NewInterface(net.Interface{ Index: 2, MTU: 1500, Name: "eth0", @@ -525,153 +583,36 @@ func NewNet(config *NetConfig) *Net { } } - v := &vNet{ - interfaces: []*Interface{lo0, eth0}, + return &Net{ + interfaces: []*transport.Interface{lo0, eth0}, staticIPs: staticIPs, udpConns: newUDPConnMap(), - } - - return &Net{ - v: v, - } + }, nil } -// Interfaces returns a list of the system's network interfaces. -func (n *Net) Interfaces() ([]*Interface, error) { - if n.v == nil { - return n.ifs, nil - } - - return n.v.getInterfaces() +// DialTCP acts like Dial for TCP networks. +func (v *Net) DialTCP(string, *net.TCPAddr, *net.TCPAddr) (transport.TCPConn, error) { + return nil, transport.ErrNotSupported } -// InterfaceByName returns the interface specified by name. -func (n *Net) InterfaceByName(name string) (*Interface, error) { - if n.v == nil { - for _, ifc := range n.ifs { - if ifc.Name == name { - return ifc, nil - } - } - - return nil, fmt.Errorf("interface %s %w", name, errNotFound) - } - - return n.v.getInterface(name) -} - -// ListenPacket announces on the local network address. -func (n *Net) ListenPacket(network string, address string) (net.PacketConn, error) { - if n.v == nil { - return net.ListenPacket(network, address) - } - - return n.v.listenPacket(network, address) -} - -// ListenUDP acts like ListenPacket for UDP networks. -func (n *Net) ListenUDP(network string, locAddr *net.UDPAddr) (UDPPacketConn, error) { - if n.v == nil { - return net.ListenUDP(network, locAddr) - } - - return n.v.listenUDP(network, locAddr) -} - -// Dial connects to the address on the named network. -func (n *Net) Dial(network, address string) (net.Conn, error) { - if n.v == nil { - return net.Dial(network, address) - } - - return n.v.dial(network, address) +// ListenTCP acts like Listen for TCP networks. +func (v *Net) ListenTCP(string, *net.TCPAddr) (transport.TCPListener, error) { + return nil, transport.ErrNotSupported } // CreateDialer creates an instance of vnet.Dialer -func (n *Net) CreateDialer(dialer *net.Dialer) Dialer { - if n.v == nil { - return &vDialer{ - dialer: dialer, - } - } - - return &vDialer{ - dialer: dialer, - v: n.v, +func (v *Net) CreateDialer(d *net.Dialer) transport.Dialer { + return &dialer{ + dialer: d, + net: v, } } -// DialUDP acts like Dial for UDP networks. -func (n *Net) DialUDP(network string, laddr, raddr *net.UDPAddr) (UDPPacketConn, error) { - if n.v == nil { - return net.DialUDP(network, laddr, raddr) - } - - return n.v.dialUDP(network, laddr, raddr) -} - -// ResolveUDPAddr returns an address of UDP end point. -func (n *Net) ResolveUDPAddr(network, address string) (*net.UDPAddr, error) { - if n.v == nil { - return net.ResolveUDPAddr(network, address) - } - - return n.v.resolveUDPAddr(network, address) -} - -func (n *Net) getInterface(ifName string) (*Interface, error) { - if n.v == nil { - return nil, errVNetDisabled - } - - return n.v.getInterface(ifName) -} - -func (n *Net) setRouter(r *Router) error { - if n.v == nil { - return errVNetDisabled - } - - return n.v.setRouter(r) -} - -func (n *Net) onInboundChunk(c Chunk) { - if n.v == nil { - return - } - - n.v.onInboundChunk(c) -} - -func (n *Net) getStaticIPs() []net.IP { - if n.v == nil { - return nil - } - - return n.v.staticIPs -} - -// IsVirtual tests if the virtual network is enabled. -func (n *Net) IsVirtual() bool { - return n.v != nil -} - -// Dialer is identical to net.Dialer excepts that its methods -// (Dial, DialContext) are overridden to use virtual network. -// Use vnet.CreateDialer() to create an instance of this Dialer. -type Dialer interface { - Dial(network, address string) (net.Conn, error) -} - -type vDialer struct { +type dialer struct { dialer *net.Dialer - v *vNet + net *Net } -func (d *vDialer) Dial(network, address string) (net.Conn, error) { - if d.v == nil { - return d.dialer.Dial(network, address) - } - - return d.v.dial(network, address) +func (d *dialer) Dial(network, address string) (net.Conn, error) { + return d.net.Dial(network, address) } diff --git a/vendor/github.com/pion/transport/vnet/resolver.go b/vendor/github.com/pion/transport/v2/vnet/resolver.go similarity index 87% rename from vendor/github.com/pion/transport/vnet/resolver.go rename to vendor/github.com/pion/transport/v2/vnet/resolver.go index e5166e3cd..a391cc7f0 100644 --- a/vendor/github.com/pion/transport/vnet/resolver.go +++ b/vendor/github.com/pion/transport/v2/vnet/resolver.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package vnet import ( @@ -11,7 +14,7 @@ import ( var ( errHostnameEmpty = errors.New("host name must not be empty") - errFailedtoParseIPAddr = errors.New("failed to parse IP address") + errFailedToParseIPAddr = errors.New("failed to parse IP address") ) type resolverConfig struct { @@ -53,7 +56,7 @@ func (r *resolver) addHost(name string, ipAddr string) error { } ip := net.ParseIP(ipAddr) if ip == nil { - return fmt.Errorf("%w \"%s\"", errFailedtoParseIPAddr, ipAddr) + return fmt.Errorf("%w \"%s\"", errFailedToParseIPAddr, ipAddr) } r.hosts[name] = ip return nil diff --git a/vendor/github.com/pion/transport/vnet/router.go b/vendor/github.com/pion/transport/v2/vnet/router.go similarity index 93% rename from vendor/github.com/pion/transport/vnet/router.go rename to vendor/github.com/pion/transport/v2/vnet/router.go index 9e44f9edd..cd4b88dc4 100644 --- a/vendor/github.com/pion/transport/vnet/router.go +++ b/vendor/github.com/pion/transport/v2/vnet/router.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package vnet import ( @@ -11,6 +14,7 @@ import ( "time" "github.com/pion/logging" + "github.com/pion/transport/v2" ) const ( @@ -63,9 +67,9 @@ type RouterConfig struct { LoggerFactory logging.LoggerFactory } -// NIC is a nework inerface controller that interfaces Router +// NIC is a network interface controller that interfaces Router type NIC interface { - getInterface(ifName string) (*Interface, error) + getInterface(ifName string) (*transport.Interface, error) onInboundChunk(c Chunk) getStaticIPs() []net.IP setRouter(r *Router) error @@ -78,7 +82,7 @@ type ChunkFilter func(c Chunk) bool // Router ... type Router struct { name string // read-only - interfaces []*Interface // read-only + interfaces []*transport.Interface // read-only ipv4Net *net.IPNet // read-only staticIPs []net.IP // read-only staticLocalIPs map[string]net.IP // read-only, @@ -116,17 +120,17 @@ func NewRouter(config *RouterConfig) (*Router, error) { } // set up network interface, lo0 - lo0 := NewInterface(net.Interface{ + lo0 := transport.NewInterface(net.Interface{ Index: 1, MTU: 16384, Name: lo0String, HardwareAddr: nil, Flags: net.FlagUp | net.FlagLoopback | net.FlagMulticast, }) - lo0.AddAddr(&net.IPAddr{IP: net.ParseIP("127.0.0.1"), Zone: ""}) + lo0.AddAddress(&net.IPAddr{IP: net.ParseIP("127.0.0.1"), Zone: ""}) // set up network interface, eth0 - eth0 := NewInterface(net.Interface{ + eth0 := transport.NewInterface(net.Interface{ Index: 2, MTU: 1500, Name: "eth0", @@ -177,7 +181,7 @@ func NewRouter(config *RouterConfig) (*Router, error) { return &Router{ name: name, - interfaces: []*Interface{lo0, eth0}, + interfaces: []*transport.Interface{lo0, eth0}, ipv4Net: ipv4Net, staticIPs: staticIPs, staticLocalIPs: staticLocalIPs, @@ -194,7 +198,7 @@ func NewRouter(config *RouterConfig) (*Router, error) { } // caller must hold the mutex -func (r *Router) getInterfaces() ([]*Interface, error) { +func (r *Router) getInterfaces() ([]*transport.Interface, error) { if len(r.interfaces) == 0 { return nil, fmt.Errorf("%w is available", errNoInterface) } @@ -202,7 +206,7 @@ func (r *Router) getInterfaces() ([]*Interface, error) { return r.interfaces, nil } -func (r *Router) getInterface(ifName string) (*Interface, error) { +func (r *Router) getInterface(ifName string) (*transport.Interface, error) { r.mutex.RLock() defer r.mutex.RUnlock() @@ -216,7 +220,7 @@ func (r *Router) getInterface(ifName string) (*Interface, error) { } } - return nil, fmt.Errorf("interface %s %w", ifName, errNotFound) + return nil, fmt.Errorf("%w: %s", transport.ErrInterfaceNotFound, ifName) } // Start ... @@ -316,7 +320,7 @@ func (r *Router) addNIC(nic NIC) error { return fmt.Errorf("%w: %s", errStaticIPisBeyondSubnet, r.ipv4Net.String()) } - ifc.AddAddr(&net.IPNet{ + ifc.AddAddress(&net.IPNet{ IP: ip, Mask: r.ipv4Net.Mask, }) @@ -324,14 +328,10 @@ func (r *Router) addNIC(nic NIC) error { r.nics[ip.String()] = nic } - if err = nic.setRouter(r); err != nil { - return err - } - - return nil + return nic.setRouter(r) } -// AddRouter adds a chile Router. +// AddRouter adds a child Router. func (r *Router) AddRouter(router *Router) error { r.mutex.Lock() defer r.mutex.Unlock() @@ -482,7 +482,7 @@ func (r *Router) processChunks() (time.Duration, error) { dstIP := c.getDestinationIP() - // check if the desination is in our subnet + // check if the destination is in our subnet if r.ipv4Net.Contains(dstIP) { // search for the destination NIC var nic NIC @@ -520,7 +520,7 @@ func (r *Router) processChunks() (time.Duration, error) { //nolint:godox /* FIXME: this implementation would introduce a duplicate packet! - if r.nat.natType.Hairpining { + if r.nat.natType.Hairpinning { hairpinned, err := r.nat.translateInbound(toParent) if err != nil { r.log.Warnf("[%s] %s", r.name, err.Error()) @@ -553,14 +553,15 @@ func (r *Router) setRouter(parent *Router) error { return err } - if len(ifc.addrs) == 0 { + addrs, _ := ifc.Addrs() + if len(addrs) == 0 { return errNoIPAddrEth0 } mappedIPs := []net.IP{} localIPs := []net.IP{} - for _, ifcAddr := range ifc.addrs { + for _, ifcAddr := range addrs { var ip net.IP switch addr := ifcAddr.(type) { case *net.IPNet: @@ -586,7 +587,7 @@ func (r *Router) setRouter(parent *Router) error { r.natType = &NATType{ MappingBehavior: EndpointIndependent, FilteringBehavior: EndpointAddrPortDependent, - Hairpining: false, + Hairpinning: false, PortPreservation: false, MappingLifeTime: 30 * time.Second, } diff --git a/vendor/github.com/pion/transport/vnet/tbf.go b/vendor/github.com/pion/transport/v2/vnet/tbf.go similarity index 68% rename from vendor/github.com/pion/transport/vnet/tbf.go rename to vendor/github.com/pion/transport/v2/vnet/tbf.go index 0bb0f740d..e6188e31c 100644 --- a/vendor/github.com/pion/transport/vnet/tbf.go +++ b/vendor/github.com/pion/transport/v2/vnet/tbf.go @@ -1,8 +1,14 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package vnet import ( + "math" "sync" "time" + + "github.com/pion/logging" ) const ( @@ -17,17 +23,20 @@ const ( // TokenBucketFilter implements a token bucket rate limit algorithm. type TokenBucketFilter struct { NIC - currentTokensInBucket int + currentTokensInBucket float64 c chan Chunk queue *chunkQueue queueSize int // in bytes - mutex sync.Mutex - rate int - maxBurst int + mutex sync.Mutex + rate int + maxBurst int + minRefillDuration time.Duration wg sync.WaitGroup done chan struct{} + + log logging.LeveledLogger } // TBFOption is the option type to configure a TokenBucketFilter @@ -43,7 +52,7 @@ func TBFQueueSizeInBytes(bytes int) TBFOption { } } -// TBFRate sets the bitrate of a TokenBucketFilter +// TBFRate sets the bit rate of a TokenBucketFilter func TBFRate(rate int) TBFOption { return func(t *TokenBucketFilter) TBFOption { t.mutex.Lock() @@ -84,9 +93,11 @@ func NewTokenBucketFilter(n NIC, opts ...TBFOption) (*TokenBucketFilter, error) queueSize: 50000, mutex: sync.Mutex{}, rate: 1 * MBit, - maxBurst: 2 * KBit, + maxBurst: 8 * KBit, + minRefillDuration: 100 * time.Millisecond, wg: sync.WaitGroup{}, done: make(chan struct{}), + log: logging.NewDefaultLoggerFactory().NewLogger("tbf"), } tbf.Set(opts...) tbf.queue = newChunkQueue(0, tbf.queueSize) @@ -101,40 +112,47 @@ func (t *TokenBucketFilter) onInboundChunk(c Chunk) { func (t *TokenBucketFilter) run() { defer t.wg.Done() - ticker := time.NewTicker(1 * time.Millisecond) + + t.refillTokens(t.minRefillDuration) + lastRefill := time.Now() for { select { case <-t.done: - ticker.Stop() t.drainQueue() return - case <-ticker.C: - t.mutex.Lock() - if t.currentTokensInBucket < t.maxBurst { - // add (bitrate * S) / 1000 converted to bytes (divide by 8) S - // is the update interval in milliseconds - t.currentTokensInBucket += (t.rate / 1000) / 8 - } - t.mutex.Unlock() - t.drainQueue() case chunk := <-t.c: + if time.Since(lastRefill) > t.minRefillDuration { + t.refillTokens(time.Since(lastRefill)) + lastRefill = time.Now() + } t.queue.push(chunk) t.drainQueue() } } } +func (t *TokenBucketFilter) refillTokens(dt time.Duration) { + m := 1000.0 / float64(dt.Milliseconds()) + add := (float64(t.rate) / m) / 8.0 + t.mutex.Lock() + defer t.mutex.Unlock() + t.currentTokensInBucket = math.Min(float64(t.maxBurst), t.currentTokensInBucket+add) + t.log.Tracef("add=(%v / %v) / 8 = %v, currentTokensInBucket=%v, maxBurst=%v", t.rate, m, add, t.currentTokensInBucket, t.maxBurst) +} + func (t *TokenBucketFilter) drainQueue() { for { next := t.queue.peek() if next == nil { break } - tokens := len(next.UserData()) + tokens := float64(len(next.UserData())) if t.currentTokensInBucket < tokens { + t.log.Tracef("currentTokensInBucket=%v, tokens=%v, stop drain", t.currentTokensInBucket, tokens) break } + t.log.Tracef("currentTokensInBucket=%v, tokens=%v, pop chunk", t.currentTokensInBucket, tokens) t.queue.pop() t.NIC.onInboundChunk(next) t.currentTokensInBucket -= tokens diff --git a/vendor/github.com/pion/transport/vnet/udpproxy.go b/vendor/github.com/pion/transport/v2/vnet/udpproxy.go similarity index 73% rename from vendor/github.com/pion/transport/vnet/udpproxy.go rename to vendor/github.com/pion/transport/v2/vnet/udpproxy.go index caba86e0e..c5b4f9965 100644 --- a/vendor/github.com/pion/transport/vnet/udpproxy.go +++ b/vendor/github.com/pion/transport/v2/vnet/udpproxy.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package vnet import ( @@ -10,19 +13,20 @@ import ( // UDPProxy is a proxy between real server(net.UDPConn) and vnet.UDPConn. // // High level design: -// .............................................. -// : Virtual Network (vnet) : -// : : -// +-------+ * 1 +----+ +--------+ : -// | :App |------------>|:Net|--o<-----|:Router | ............................. -// +-------+ +----+ | | : UDPProxy : -// : | | +----+ +---------+ +---------+ +--------+ -// : | |--->o--|:Net|-->o-| vnet. |-->o-| net. |--->-| :Real | -// : | | +----+ | UDPConn | | UDPConn | | Server | -// : | | : +---------+ +---------+ +--------+ -// : | | ............................: -// : +--------+ : -// ............................................... +// +// .............................................. +// : Virtual Network (vnet) : +// : : +// +-------+ * 1 +----+ +--------+ : +// | :App |------------>|:Net|--o<-----|:Router | ............................. +// +-------+ +----+ | | : UDPProxy : +// : | | +----+ +---------+ +---------+ +--------+ +// : | |--->o--|:Net|-->o-| vnet. |-->o-| net. |--->-| :Real | +// : | | +----+ | UDPConn | | UDPConn | | Server | +// : | | : +---------+ +---------+ +--------+ +// : | | ............................: +// : +--------+ : +// ............................................... type UDPProxy struct { // The router bind to. router *Router @@ -52,7 +56,7 @@ func NewProxy(router *Router) (*UDPProxy, error) { // Close the proxy, stop all workers. func (v *UDPProxy) Close() error { v.workers.Range(func(key, value interface{}) bool { - _ = value.(*aUDPProxyWorker).Close() + _ = value.(*aUDPProxyWorker).Close() //nolint:forcetypeassert return true }) return nil @@ -107,12 +111,16 @@ func (v *aUDPProxyWorker) Close() error { return nil } -func (v *aUDPProxyWorker) Proxy(ctx context.Context, client *Net, serverAddr *net.UDPAddr) error { // nolint:gocognit +func (v *aUDPProxyWorker) Proxy(ctx context.Context, _ *Net, serverAddr *net.UDPAddr) error { // nolint:gocognit // Create vnet for real server by serverAddr. - nw := NewNet(&NetConfig{ + nw, err := NewNet(&NetConfig{ StaticIP: serverAddr.IP.String(), }) - if err := v.router.AddNet(nw); err != nil { + if err != nil { + return err + } + + if err = v.router.AddNet(nw); err != nil { return err } @@ -134,7 +142,7 @@ func (v *aUDPProxyWorker) Proxy(ctx context.Context, client *Net, serverAddr *ne // Exists binding. if value, ok := v.endpoints.Load(addr.String()); ok { // Exists endpoint, reuse it. - return value.(*net.UDPConn), nil + return value.(*net.UDPConn), nil //nolint:forcetypeassert } // The real server we proxy to, for utest to mock it. diff --git a/vendor/github.com/pion/transport/vnet/udpproxy_direct.go b/vendor/github.com/pion/transport/v2/vnet/udpproxy_direct.go similarity index 75% rename from vendor/github.com/pion/transport/vnet/udpproxy_direct.go rename to vendor/github.com/pion/transport/v2/vnet/udpproxy_direct.go index 9ab460e71..bb2aedfd0 100644 --- a/vendor/github.com/pion/transport/vnet/udpproxy_direct.go +++ b/vendor/github.com/pion/transport/v2/vnet/udpproxy_direct.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package vnet import ( @@ -20,7 +23,7 @@ func (v *UDPProxy) Deliver(sourceAddr, destAddr net.Addr, b []byte) (nn int, err return } -func (v *aUDPProxyWorker) Deliver(sourceAddr, destAddr net.Addr, b []byte) (nn int, err error) { +func (v *aUDPProxyWorker) Deliver(sourceAddr, _ net.Addr, b []byte) (nn int, err error) { addr, ok := sourceAddr.(*net.UDPAddr) if !ok { return 0, fmt.Errorf("invalid addr %v", sourceAddr) // nolint:goerr113 @@ -29,12 +32,13 @@ func (v *aUDPProxyWorker) Deliver(sourceAddr, destAddr net.Addr, b []byte) (nn i // nolint:godox // TODO: Support deliver packet from real server to vnet. // If packet is from vnet, proxy to real server. var realSocket *net.UDPConn - if value, ok := v.endpoints.Load(addr.String()); !ok { + value, ok := v.endpoints.Load(addr.String()) + if !ok { return 0, nil - } else { // nolint:golint - realSocket = value.(*net.UDPConn) } + realSocket = value.(*net.UDPConn) // nolint:forcetypeassert + // Send to real server. if _, err := realSocket.Write(b); err != nil { return 0, err diff --git a/vendor/github.com/pion/transport/v2/vnet/vnet.go b/vendor/github.com/pion/transport/v2/vnet/vnet.go new file mode 100644 index 000000000..1d244f074 --- /dev/null +++ b/vendor/github.com/pion/transport/v2/vnet/vnet.go @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package vnet provides a virtual network layer for pion +package vnet diff --git a/vendor/github.com/pion/transport/vnet/.gitignore b/vendor/github.com/pion/transport/vnet/.gitignore deleted file mode 100644 index d39fb86a3..000000000 --- a/vendor/github.com/pion/transport/vnet/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.sw[poe] diff --git a/vendor/github.com/pion/transport/vnet/interface.go b/vendor/github.com/pion/transport/vnet/interface.go deleted file mode 100644 index ec80c0b78..000000000 --- a/vendor/github.com/pion/transport/vnet/interface.go +++ /dev/null @@ -1,40 +0,0 @@ -package vnet - -import ( - "errors" - "net" -) - -var errNoAddressAssigned = errors.New("no address assigned") - -// See: https://play.golang.org/p/nBO9KGYEziv - -// InterfaceBase ... -type InterfaceBase net.Interface - -// Interface ... -type Interface struct { - InterfaceBase - addrs []net.Addr -} - -// NewInterface ... -func NewInterface(ifc net.Interface) *Interface { - return &Interface{ - InterfaceBase: InterfaceBase(ifc), - addrs: nil, - } -} - -// AddAddr ... -func (ifc *Interface) AddAddr(addr net.Addr) { - ifc.addrs = append(ifc.addrs, addr) -} - -// Addrs ... -func (ifc *Interface) Addrs() ([]net.Addr, error) { - if len(ifc.addrs) == 0 { - return nil, errNoAddressAssigned - } - return ifc.addrs, nil -} diff --git a/vendor/github.com/pion/transport/vnet/vnet.go b/vendor/github.com/pion/transport/vnet/vnet.go deleted file mode 100644 index bfe0f0f27..000000000 --- a/vendor/github.com/pion/transport/vnet/vnet.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package vnet provides a virtual network layer for pion -package vnet diff --git a/vendor/github.com/pion/turn/v2/.gitignore b/vendor/github.com/pion/turn/v2/.gitignore index 83db74ba5..f977e7485 100644 --- a/vendor/github.com/pion/turn/v2/.gitignore +++ b/vendor/github.com/pion/turn/v2/.gitignore @@ -22,3 +22,4 @@ cover.out *.wasm examples/sfu-ws/cert.pem examples/sfu-ws/key.pem +wasm_exec.js diff --git a/vendor/github.com/pion/turn/v2/.golangci.yml b/vendor/github.com/pion/turn/v2/.golangci.yml index d6162c970..48696f16b 100644 --- a/vendor/github.com/pion/turn/v2/.golangci.yml +++ b/vendor/github.com/pion/turn/v2/.golangci.yml @@ -15,14 +15,21 @@ linters-settings: linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully - - deadcode # Finds unused code + - contextcheck # check the function whether use a non-inherited context + - decorder # check declaration order and count of types, constants, variables and functions - depguard # Go linter that checks if package imports are in a list of acceptable packages - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection + - durationcheck # check for two durations multiplied together - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. - exhaustive # check exhaustiveness of enum switch statements - exportloopref # checks for pointers to enclosing loop variables + - forcetypeassert # finds forced type assertions - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - gochecknoinits # Checks that no init functions are present in Go code @@ -35,40 +42,60 @@ linters: - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - gosimple # Linter for Go source code that specializes in simplifying a code - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - grouper # An analyzer to analyze expression groups. + - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used - misspell # Finds commonly misspelled English words in comments - nakedret # Finds naked returns in functions greater than a specified function length + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. - noctx # noctx finds sending http request without context.Context - - scopelint # Scopelint checks for unpinned variables in go programs + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # golint replacement, finds style mistakes - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - - structcheck # Finds unused struct fields - stylecheck # Stylecheck is a replacement for golint + - tagliatelle # Checks the struct tags. + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types - - varcheck # Finds unused global variables and constants + - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: + - containedctx # containedctx is a linter that detects struct contained context.Context field + - cyclop # checks function and package cyclomatic complexity + - exhaustivestruct # Checks if all struct's fields are initialized + - forbidigo # Forbids identifiers - funlen # Tool for detection of long functions - gocyclo # Computes and checks the cyclomatic complexity of functions - godot # Check if comments end in a period - gomnd # An analyzer to detect magic numbers. + - ifshort # Checks that your code uses short syntax for if-statements whenever possible + - ireturn # Accept Interfaces, Return Concrete Types - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - nestif # Reports deeply nested if statements - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - nolintlint # Reports ill-formed or insufficient nolint directives + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - prealloc # Finds slice declarations that could potentially be preallocated + - promlinter # Check Prometheus metrics naming via promlint - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. - testpackage # linter that makes you use a separate _test package + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers + - varnamelen # checks that the length of a variable's name matches its scope + - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! issues: diff --git a/vendor/github.com/pion/turn/v2/.goreleaser.yml b/vendor/github.com/pion/turn/v2/.goreleaser.yml index c7f2efdc4..2caa5fbd3 100644 --- a/vendor/github.com/pion/turn/v2/.goreleaser.yml +++ b/vendor/github.com/pion/turn/v2/.goreleaser.yml @@ -1,109 +1,2 @@ -before: - hooks: - - go mod tidy - -archives: -- replacements: - darwin: Darwin - linux: Linux - windows: Windows - 386: i386 - amd64: x86_64 - -checksum: - name_template: 'checksums.txt' - -snapshot: - name_template: "{{ .Tag }}-next" - -changelog: - sort: asc - filters: - exclude: - - '^docs:' - - '^test:' - builds: - - binary: turn-client-tcp - id: turn-client-tcp - goos: - - darwin - - windows - - linux - - freebsd - goarch: - - amd64 - - 386 - env: - - CGO_ENABLED=0 - main: ./examples/turn-client/tcp - - - binary: turn-client-udp - id: turn-client-udp - goos: - - darwin - - windows - - linux - - freebsd - goarch: - - amd64 - - 386 - env: - - CGO_ENABLED=0 - main: ./examples/turn-client/udp - - - binary: turn-server-add-software-attribute - id: turn-server-add-software-attribute - goos: - - darwin - - windows - - linux - - freebsd - goarch: - - amd64 - - 386 - env: - - CGO_ENABLED=0 - main: ./examples/turn-server/add-software-attribute - - - binary: turn-server-log - id: turn-server-log - goos: - - darwin - - windows - - linux - - freebsd - goarch: - - amd64 - - 386 - env: - - CGO_ENABLED=0 - main: ./examples/turn-server/log - - - binary: turn-server-simple - id: turn-server-simple - goos: - - darwin - - windows - - linux - - freebsd - goarch: - - amd64 - - 386 - env: - - CGO_ENABLED=0 - main: ./examples/turn-server/simple/ - - - binary: turn-server-tcp - id: turn-server-tcp - goos: - - darwin - - windows - - linux - - freebsd - goarch: - - amd64 - - 386 - env: - - CGO_ENABLED=0 - main: ./examples/turn-server/tcp/ +- skip: true diff --git a/vendor/github.com/pion/turn/v2/AUTHORS.txt b/vendor/github.com/pion/turn/v2/AUTHORS.txt index 8515b5c5b..3b38a5c1f 100644 --- a/vendor/github.com/pion/turn/v2/AUTHORS.txt +++ b/vendor/github.com/pion/turn/v2/AUTHORS.txt @@ -2,14 +2,17 @@ # we would love to have you https://github.com/pion/webrtc/wiki/Contributing # # This file is auto generated, using git to list all individuals contributors. -# see `.github/generate-authors.sh` for the scripting +# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting Aaron France Aleksandr Razumov andrefsp +Antonio Sorrentino Atsushi Watanabe backkem +Caleb Phillips cnderrauber David Colburn +Gabor Retvari Herman Banken Hugo Arregui Igor German @@ -25,10 +28,16 @@ Mészáros Mihály nindolabs <6729798+nindolabs@users.noreply.github.com> Onwuka Gideon Robert Eperjesi +Sean DuBois Sean DuBois Sean DuBois Sean DuBois songjiayang +Steffen Vogel +ted Tom Clift Yusuke Nakamura Yutaka Takeda + +# List of contributors not appearing in Git history + diff --git a/vendor/github.com/pion/turn/v2/FAQ.md b/vendor/github.com/pion/turn/v2/FAQ.md new file mode 100644 index 000000000..a8ffd6491 --- /dev/null +++ b/vendor/github.com/pion/turn/v2/FAQ.md @@ -0,0 +1,17 @@ + + +## FAQ + +Q: Will pion/turn also act as a STUN server? + +A: Yes. + +Q: How do I implement token-based authentication? + +A: Replace the username with a token in the [AuthHandler](https://github.com/pion/turn/blob/6d0ff435910870eb9024b18321b93b61844fcfec/examples/turn-server/simple/main.go#L49). +The password sent by the client can be any non-empty string, as long as it matches that used by the [GenerateAuthKey](https://github.com/pion/turn/blob/6d0ff435910870eb9024b18321b93b61844fcfec/examples/turn-server/simple/main.go#L41) +function. + +Q: Will WebRTC prioritize using STUN over TURN? + +A: Yes. \ No newline at end of file diff --git a/vendor/github.com/pion/turn/v2/README.md b/vendor/github.com/pion/turn/v2/README.md index 2fa806a5a..b8901fd30 100644 --- a/vendor/github.com/pion/turn/v2/README.md +++ b/vendor/github.com/pion/turn/v2/README.md @@ -13,8 +13,7 @@ Build Status GoDoc Coverage Status - Go Report Card - Codacy Badge + Go Report Card License: MIT


@@ -64,7 +63,7 @@ Pion has an active community on the [Golang Slack](https://pion.ly/slack). Sign We are always looking to support **your projects**. Please reach out if you have something to build! ### Contributing -Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible: +Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible. ### License MIT License - see [LICENSE.md](LICENSE.md) for full text diff --git a/vendor/github.com/pion/turn/v2/client.go b/vendor/github.com/pion/turn/v2/client.go index b04e7d186..5e675fd36 100644 --- a/vendor/github.com/pion/turn/v2/client.go +++ b/vendor/github.com/pion/turn/v2/client.go @@ -10,7 +10,9 @@ import ( "github.com/pion/logging" "github.com/pion/stun" - "github.com/pion/transport/vnet" + "github.com/pion/transport/v2" + "github.com/pion/transport/v2/stdnet" + "github.com/pion/transport/v2/vnet" "github.com/pion/turn/v2/internal/client" "github.com/pion/turn/v2/internal/proto" ) @@ -34,7 +36,7 @@ const ( // ClientConfig is a bag of config parameters for Client. type ClientConfig struct { STUNServerAddr string // STUN server address (e.g. "stun.abc.com:3478") - TURNServerAddr string // TURN server addrees (e.g. "turn.abc.com:3478") + TURNServerAddr string // TURN server address (e.g. "turn.abc.com:3478") Username string Password string Realm string @@ -42,7 +44,7 @@ type ClientConfig struct { RTO time.Duration Conn net.PacketConn // Listening socket (net.PacketConn) LoggerFactory logging.LoggerFactory - Net *vnet.Net + Net transport.Net } // Client is a STUN server client @@ -50,8 +52,8 @@ type Client struct { conn net.PacketConn // read-only stunServ net.Addr // read-only turnServ net.Addr // read-only - stunServStr string // read-only, used for dmuxing - turnServStr string // read-only, used for dmuxing + stunServStr string // read-only, used for de-multiplexing + turnServStr string // read-only, used for de-multiplexing username stun.Username // read-only password string // read-only realm stun.Realm // read-only @@ -62,7 +64,7 @@ type Client struct { relayedConn *client.UDPConn // protected by mutex *** allocTryLock client.TryLock // thread-safe listenTryLock client.TryLock // thread-safe - net *vnet.Net // read-only + net transport.Net // read-only mutex sync.RWMutex // thread-safe mutexTrMap sync.Mutex // thread-safe log logging.LeveledLogger // read-only @@ -81,15 +83,18 @@ func NewClient(config *ClientConfig) (*Client, error) { return nil, errNilConn } + var err error if config.Net == nil { - config.Net = vnet.NewNet(nil) // defaults to native operation - } else if config.Net.IsVirtual() { - log.Warn("vnet is enabled") + config.Net, err = stdnet.NewNet() // defaults to native operation + if err != nil { + return nil, err + } + } else if _, ok := config.Net.(*vnet.Net); ok { + log.Warn("Virtual network is enabled") } var stunServ, turnServ net.Addr var stunServStr, turnServStr string - var err error if len(config.STUNServerAddr) > 0 { log.Debugf("resolving %s", config.STUNServerAddr) stunServ, err = config.Net.ResolveUDPAddr("udp4", config.STUNServerAddr) @@ -333,9 +338,16 @@ func (c *Client) Allocate() (net.PacketConn, error) { return relayedConn, nil } +// CreatePermission Issues a CreatePermission request for the supplied addresses +// as described in https://datatracker.ietf.org/doc/html/rfc5766#section-9 +func (c *Client) CreatePermission(addrs ...net.Addr) error { + return c.relayedUDPConn().CreatePermissions(addrs...) +} + // PerformTransaction performs STUN transaction func (c *Client) PerformTransaction(msg *stun.Message, to net.Addr, ignoreResult bool) (client.TransactionResult, - error) { + error, +) { trKey := b64.StdEncoding.EncodeToString(msg.TransactionID[:]) raw := make([]byte, len(msg.Raw)) @@ -371,16 +383,16 @@ func (c *Client) PerformTransaction(msg *stun.Message, to net.Addr, ignoreResult return res, nil } -// OnDeallocated is called when deallocation of relay address has been complete. +// OnDeallocated is called when de-allocation of relay address has been complete. // (Called by UDPConn) func (c *Client) OnDeallocated(relayedAddr net.Addr) { c.setRelayedUDPConn(nil) } // HandleInbound handles data received. -// This method handles incoming packet demultiplex it by the source address +// This method handles incoming packet de-multiplex it by the source address // and the types of the message. -// This return a booleen (handled or not) and if there was an error. +// This return a boolean (handled or not) and if there was an error. // Caller should check if the packet was handled by this client or not. // If not handled, it is assumed that the packet is application data. // If an error is returned, the caller should discard the packet regardless. @@ -413,7 +425,7 @@ func (c *Client) HandleInbound(data []byte, from net.Addr) (bool, error) { return true, errNonSTUNMessage default: // assume, this is an application data - c.log.Tracef("non-STUN/TURN packect, unhandled") + c.log.Tracef("non-STUN/TURN packet, unhandled") } return false, nil @@ -529,7 +541,7 @@ func (c *Client) onRtxTimeout(trKey string, nRtx int) { } if nRtx == maxRtxCount { - // all retransmisstions failed + // all retransmissions failed c.trMap.Delete(trKey) if !tr.WriteResult(client.TransactionResult{ Err: fmt.Errorf("%w %s", errAllRetransmissionsFailed, trKey), diff --git a/vendor/github.com/pion/turn/v2/internal/allocation/allocation.go b/vendor/github.com/pion/turn/v2/internal/allocation/allocation.go index 2b346f198..7a3ce1ddf 100644 --- a/vendor/github.com/pion/turn/v2/internal/allocation/allocation.go +++ b/vendor/github.com/pion/turn/v2/internal/allocation/allocation.go @@ -4,6 +4,7 @@ package allocation import ( "net" "sync" + "sync/atomic" "time" "github.com/pion/logging" @@ -12,6 +13,11 @@ import ( "github.com/pion/turn/v2/internal/proto" ) +type allocationResponse struct { + transactionID [stun.TransactionIDSize]byte + responseAttrs []stun.Setter +} + // Allocation is tied to a FiveTuple and relays traffic // use CreateAllocation and GetAllocation to operate type Allocation struct { @@ -27,6 +33,12 @@ type Allocation struct { lifetimeTimer *time.Timer closed chan interface{} log logging.LeveledLogger + + // some clients (Firefox or others using resiprocate's nICE lib) may retry allocation + // with same 5 tuple when received 413, for compatible with these clients, + // cache for response lost and client retry to implement 'stateless stack approach' + // https://datatracker.ietf.org/doc/html/rfc5766#section-6.2 + responseCache atomic.Value // *allocationResponse } func addr2IPFingerprint(addr net.Addr) string { @@ -36,7 +48,7 @@ func addr2IPFingerprint(addr net.Addr) string { case *net.TCPAddr: // Do we really need this case? return a.IP.String() } - return "" // shoud never happen + return "" // should never happen } // NewAllocation creates a new instance of NewAllocation. @@ -164,6 +176,22 @@ func (a *Allocation) Refresh(lifetime time.Duration) { } } +// SetResponseCache cache allocation response for retransmit allocation request +func (a *Allocation) SetResponseCache(transactionID [stun.TransactionIDSize]byte, attrs []stun.Setter) { + a.responseCache.Store(&allocationResponse{ + transactionID: transactionID, + responseAttrs: attrs, + }) +} + +// GetResponseCache return response cache for retransmit allocation request +func (a *Allocation) GetResponseCache() (id [stun.TransactionIDSize]byte, attrs []stun.Setter) { + if res, ok := a.responseCache.Load().(*allocationResponse); ok && res != nil { + id, attrs = res.transactionID, res.responseAttrs + } + return +} + // Close closes the allocation func (a *Allocation) Close() error { select { @@ -238,13 +266,19 @@ func (a *Allocation) packetHandler(m *Manager) { a.log.Errorf("Failed to send ChannelData from allocation %v %v", srcAddr, err) } } else if p := a.GetPermission(srcAddr); p != nil { - udpAddr := srcAddr.(*net.UDPAddr) + udpAddr, ok := srcAddr.(*net.UDPAddr) + if !ok { + a.log.Errorf("Failed to send DataIndication from allocation %v %v", srcAddr, err) + return + } + peerAddressAttr := proto.PeerAddress{IP: udpAddr.IP, Port: udpAddr.Port} dataAttr := proto.Data(buffer[:n]) msg, err := stun.Build(stun.TransactionID, stun.NewType(stun.MethodData, stun.ClassIndication), peerAddressAttr, dataAttr) if err != nil { a.log.Errorf("Failed to send DataIndication from allocation %v %v", srcAddr, err) + return } a.log.Debugf("relaying message from %s to client at %s", srcAddr.String(), diff --git a/vendor/github.com/pion/turn/v2/internal/allocation/allocation_manager.go b/vendor/github.com/pion/turn/v2/internal/allocation/allocation_manager.go index 2fd79725c..5e924ac64 100644 --- a/vendor/github.com/pion/turn/v2/internal/allocation/allocation_manager.go +++ b/vendor/github.com/pion/turn/v2/internal/allocation/allocation_manager.go @@ -14,6 +14,7 @@ type ManagerConfig struct { LeveledLogger logging.LeveledLogger AllocatePacketConn func(network string, requestedPort int) (net.PacketConn, net.Addr, error) AllocateConn func(network string, requestedPort int) (net.Conn, net.Addr, error) + PermissionHandler func(sourceAddr net.Addr, peerIP net.IP) bool } type reservation struct { @@ -31,6 +32,7 @@ type Manager struct { allocatePacketConn func(network string, requestedPort int) (net.PacketConn, net.Addr, error) allocateConn func(network string, requestedPort int) (net.Conn, net.Addr, error) + permissionHandler func(sourceAddr net.Addr, peerIP net.IP) bool } // NewManager creates a new instance of Manager. @@ -49,6 +51,7 @@ func NewManager(config ManagerConfig) (*Manager, error) { allocations: make(map[string]*Allocation, 64), allocatePacketConn: config.AllocatePacketConn, allocateConn: config.AllocateConn, + permissionHandler: config.PermissionHandler, }, nil } @@ -195,3 +198,18 @@ func (m *Manager) GetRandomEvenPort() (int, error) { } return 0, errFailedToAllocateEvenPort } + +// GrantPermission handles permission requests by calling the permission handler callback +// associated with the TURN server listener socket +func (m *Manager) GrantPermission(sourceAddr net.Addr, peerIP net.IP) error { + // no permission handler: open + if m.permissionHandler == nil { + return nil + } + + if m.permissionHandler(sourceAddr, peerIP) { + return nil + } + + return errAdminProhibited +} diff --git a/vendor/github.com/pion/turn/v2/internal/allocation/errors.go b/vendor/github.com/pion/turn/v2/internal/allocation/errors.go index b92b78d8b..b9677e11a 100644 --- a/vendor/github.com/pion/turn/v2/internal/allocation/errors.go +++ b/vendor/github.com/pion/turn/v2/internal/allocation/errors.go @@ -15,4 +15,5 @@ var ( errDupeFiveTuple = errors.New("allocation attempt created with duplicate FiveTuple") errFailedToCastUDPAddr = errors.New("failed to cast net.Addr to *net.UDPAddr") errFailedToAllocateEvenPort = errors.New("failed to allocate an even port") + errAdminProhibited = errors.New("permission request administratively prohibited") ) diff --git a/vendor/github.com/pion/turn/v2/internal/client/binding.go b/vendor/github.com/pion/turn/v2/internal/client/binding.go index ee52053cb..f4d6fa2b4 100644 --- a/vendor/github.com/pion/turn/v2/internal/client/binding.go +++ b/vendor/github.com/pion/turn/v2/internal/client/binding.go @@ -7,9 +7,10 @@ import ( "time" ) -// Chanel number: -// 0x4000 through 0x7FFF: These values are the allowed channel -// numbers (16,383 possible values). +// Channel number: +// +// 0x4000 through 0x7FFF: These values are the allowed channel +// numbers (16,383 possible values). const ( minChannelNumber uint16 = 0x4000 maxChannelNumber uint16 = 0x7fff diff --git a/vendor/github.com/pion/turn/v2/internal/client/conn.go b/vendor/github.com/pion/turn/v2/internal/client/conn.go index 8a2b1ae75..8aeb742c3 100644 --- a/vendor/github.com/pion/turn/v2/internal/client/conn.go +++ b/vendor/github.com/pion/turn/v2/internal/client/conn.go @@ -153,6 +153,21 @@ func (c *UDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } } +func (c *UDPConn) createPermission(perm *permission, addr net.Addr) error { + perm.mutex.Lock() + defer perm.mutex.Unlock() + + if perm.state() == permStateIdle { + // punch a hole! (this would block a bit..) + if err := c.CreatePermissions(addr); err != nil { + c.permMap.delete(addr) + return err + } + perm.setState(permStatePermitted) + } + return nil +} + // WriteTo writes a packet with payload p to addr. // WriteTo can be made to time out and return // an Error with Timeout() == true after a fixed time limit; @@ -172,30 +187,15 @@ func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) { //nolint: goco c.permMap.insert(addr, perm) } - // This func-block would block, per destination IP (, or perm), until - // the perm state becomes "requested". Purpose of this is to guarantee - // the order of packets (within the same perm). - // Note that CreatePermission transaction may not be complete before - // all the data transmission. This is done assuming that the request - // will be mostly likely successful and we can tolerate some loss of - // UDP packet (or reorder), inorder to minimize the latency in most cases. - createPermission := func() error { - perm.mutex.Lock() - defer perm.mutex.Unlock() - - if perm.state() == permStateIdle { - // punch a hole! (this would block a bit..) - if err = c.createPermissions(addr); err != nil { - c.permMap.delete(addr) - return err - } - perm.setState(permStatePermitted) - } - return nil - } - for i := 0; i < maxRetryAttempts; i++ { - if err = createPermission(); !errors.Is(err, errTryAgain) { + // c.createPermission() would block, per destination IP (, or perm), + // until the perm state becomes "requested". Purpose of this is to + // guarantee the order of packets (within the same perm). + // Note that CreatePermission transaction may not be complete before + // all the data transmission. This is done assuming that the request + // will be most likely successful and we can tolerate some loss of + // UDP packet (or reorder), inorder to minimize the latency in most cases. + if err = c.createPermission(perm, addr); !errors.Is(err, errTryAgain) { break } } @@ -277,7 +277,11 @@ func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) { //nolint: goco }() // send via ChannelData - return c.sendChannelData(p, b.number) + _, err = c.sendChannelData(p, b.number) + if err != nil { + return 0, err + } + return len(p), nil } // Close closes the connection. @@ -359,7 +363,9 @@ func addr2PeerAddress(addr net.Addr) proto.PeerAddress { return peerAddr } -func (c *UDPConn) createPermissions(addrs ...net.Addr) error { +// CreatePermissions Issues a CreatePermission request for the supplied addresses +// as described in https://datatracker.ietf.org/doc/html/rfc5766#section-9 +func (c *UDPConn) CreatePermissions(addrs ...net.Addr) error { setters := []stun.Setter{ stun.TransactionID, stun.NewType(stun.MethodCreatePermission, stun.ClassRequest), @@ -496,7 +502,7 @@ func (c *UDPConn) refreshPermissions() error { c.log.Debug("no permission to refresh") return nil } - if err := c.createPermissions(addrs...); err != nil { + if err := c.CreatePermissions(addrs...); err != nil { if errors.Is(err, errTryAgain) { return errTryAgain } @@ -549,7 +555,11 @@ func (c *UDPConn) sendChannelData(data []byte, chNum uint16) (int, error) { Number: proto.ChannelNumber(chNum), } chData.Encode() - return c.obs.WriteTo(chData.Raw, c.obs.TURNServerAddr()) + _, err := c.obs.WriteTo(chData.Raw, c.obs.TURNServerAddr()) + if err != nil { + return 0, err + } + return len(data), nil } func (c *UDPConn) onRefreshTimers(id int) { diff --git a/vendor/github.com/pion/turn/v2/internal/client/errors.go b/vendor/github.com/pion/turn/v2/internal/client/errors.go index 2d4bd30c4..7fc816fd0 100644 --- a/vendor/github.com/pion/turn/v2/internal/client/errors.go +++ b/vendor/github.com/pion/turn/v2/internal/client/errors.go @@ -5,7 +5,7 @@ import ( ) var ( - errFakeErr = errors.New("fake error") + errFake = errors.New("fake error") errTryAgain = errors.New("try again") errClosed = errors.New("use of closed network connection") errUDPAddrCast = errors.New("addr is not a net.UDPAddr") diff --git a/vendor/github.com/pion/turn/v2/internal/client/transaction.go b/vendor/github.com/pion/turn/v2/internal/client/transaction.go index 610a4d4d2..54024b447 100644 --- a/vendor/github.com/pion/turn/v2/internal/client/transaction.go +++ b/vendor/github.com/pion/turn/v2/internal/client/transaction.go @@ -139,7 +139,7 @@ func NewTransactionMap() *TransactionMap { } } -// Insert inserts a trasaction to the map +// Insert inserts a transaction to the map func (m *TransactionMap) Insert(key string, tr *Transaction) bool { m.mutex.Lock() defer m.mutex.Unlock() diff --git a/vendor/github.com/pion/turn/v2/internal/client/trylock.go b/vendor/github.com/pion/turn/v2/internal/client/trylock.go index 48e25a054..2d11a2d3c 100644 --- a/vendor/github.com/pion/turn/v2/internal/client/trylock.go +++ b/vendor/github.com/pion/turn/v2/internal/client/trylock.go @@ -10,7 +10,7 @@ type TryLock struct { } // Lock tries to lock the try-lock. If successful, it returns true. -// Otherwise, it returns false immedidately. +// Otherwise, it returns false immediately. func (c *TryLock) Lock() error { if !atomic.CompareAndSwapInt32(&c.n, 0, 1) { return errDoubleLock diff --git a/vendor/github.com/pion/turn/v2/internal/ipnet/util.go b/vendor/github.com/pion/turn/v2/internal/ipnet/util.go index 9df7f5695..24256b0a0 100644 --- a/vendor/github.com/pion/turn/v2/internal/ipnet/util.go +++ b/vendor/github.com/pion/turn/v2/internal/ipnet/util.go @@ -24,7 +24,7 @@ func AddrIPPort(a net.Addr) (net.IP, int, error) { } // AddrEqual asserts that two net.Addrs are equal -// Currently only supprots UDP but will be extended in the future to support others +// Currently only supports UDP but will be extended in the future to support others func AddrEqual(a, b net.Addr) bool { aUDP, ok := a.(*net.UDPAddr) if !ok { diff --git a/vendor/github.com/pion/turn/v2/internal/proto/chandata.go b/vendor/github.com/pion/turn/v2/internal/proto/chandata.go index fb1295b1a..6f023e088 100644 --- a/vendor/github.com/pion/turn/v2/internal/proto/chandata.go +++ b/vendor/github.com/pion/turn/v2/internal/proto/chandata.go @@ -11,7 +11,7 @@ import ( // // See RFC 5766 Section 11.4 type ChannelData struct { - Data []byte // can be subslice of Raw + Data []byte // can be sub slice of Raw Length int // ignored while encoding, len(Data) is used Number ChannelNumber Raw []byte diff --git a/vendor/github.com/pion/turn/v2/internal/proto/connection_id.go b/vendor/github.com/pion/turn/v2/internal/proto/connection_id.go new file mode 100644 index 000000000..4984b474b --- /dev/null +++ b/vendor/github.com/pion/turn/v2/internal/proto/connection_id.go @@ -0,0 +1,39 @@ +package proto + +import ( + "encoding/binary" + + "github.com/pion/stun" +) + +// ConnectionID represents CONNECTION-ID attribute. +// +// The CONNECTION-ID attribute uniquely identifies a peer data +// connection. It is a 32-bit unsigned integral value. +// +// RFC 6062 Section 6.2.1 +type ConnectionID uint32 + +const connectionIDSize = 4 // uint32: 4 bytes, 32 bits + +// AddTo adds CONNECTION-ID to message. +func (c ConnectionID) AddTo(m *stun.Message) error { + v := make([]byte, lifetimeSize) + binary.BigEndian.PutUint32(v, uint32(c)) + m.Add(stun.AttrConnectionID, v) + return nil +} + +// GetFrom decodes CONNECTION-ID from message. +func (c *ConnectionID) GetFrom(m *stun.Message) error { + v, err := m.Get(stun.AttrConnectionID) + if err != nil { + return err + } + if err = stun.CheckSize(stun.AttrConnectionID, len(v), connectionIDSize); err != nil { + return err + } + _ = v[connectionIDSize-1] // asserting length + *(*uint32)(c) = binary.BigEndian.Uint32(v) + return nil +} diff --git a/vendor/github.com/pion/turn/v2/internal/proto/dontfrag.go b/vendor/github.com/pion/turn/v2/internal/proto/dontfrag.go index eb4d8caf8..ac52b2b51 100644 --- a/vendor/github.com/pion/turn/v2/internal/proto/dontfrag.go +++ b/vendor/github.com/pion/turn/v2/internal/proto/dontfrag.go @@ -1,18 +1,45 @@ package proto -import "github.com/pion/stun" +import ( + "github.com/pion/stun" +) -// DontFragmentAttr represents DONT-FRAGMENT attribute. -type DontFragmentAttr struct{} +// DontFragmentAttr is a deprecated alias for DontFragment +// Deprecated: Please use DontFragment +type DontFragmentAttr = DontFragment + +// DontFragment represents DONT-FRAGMENT attribute. +// +// This attribute is used by the client to request that the server set +// the DF (Don't Fragment) bit in the IP header when relaying the +// application data onward to the peer. This attribute has no value +// part and thus the attribute length field is 0. +// +// RFC 5766 Section 14.8 +type DontFragment struct{} + +const dontFragmentSize = 0 // AddTo adds DONT-FRAGMENT attribute to message. -func (DontFragmentAttr) AddTo(m *stun.Message) error { +func (DontFragment) AddTo(m *stun.Message) error { m.Add(stun.AttrDontFragment, nil) return nil } +// GetFrom decodes DONT-FRAGMENT from message. +func (d *DontFragment) GetFrom(m *stun.Message) error { + v, err := m.Get(stun.AttrDontFragment) + if err != nil { + return err + } + if err = stun.CheckSize(stun.AttrDontFragment, len(v), dontFragmentSize); err != nil { + return err + } + return nil +} + // IsSet returns true if DONT-FRAGMENT attribute is set. -func (DontFragmentAttr) IsSet(m *stun.Message) bool { +func (DontFragment) IsSet(m *stun.Message) bool { _, err := m.Get(stun.AttrDontFragment) return err == nil } diff --git a/vendor/github.com/pion/turn/v2/internal/proto/fuzz.go b/vendor/github.com/pion/turn/v2/internal/proto/fuzz.go deleted file mode 100644 index 1a171fb74..000000000 --- a/vendor/github.com/pion/turn/v2/internal/proto/fuzz.go +++ /dev/null @@ -1,111 +0,0 @@ -// +build gofuzz - -package proto - -import ( - "fmt" - - "github.com/pion/stun" -) - -type attr interface { - stun.Getter - stun.Setter -} - -type attrs []struct { - g attr - t stun.AttrType -} - -func (a attrs) pick(v byte) struct { - g attr - t stun.AttrType -} { - idx := int(v) % len(a) - return a[idx] -} - -func FuzzSetters(data []byte) int { - var ( - m1 = &stun.Message{ - Raw: make([]byte, 0, 2048), - } - m2 = &stun.Message{ - Raw: make([]byte, 0, 2048), - } - m3 = &stun.Message{ - Raw: make([]byte, 0, 2048), - } - ) - attributes := attrs{ - {new(RequestedTransport), stun.AttrRequestedTransport}, - {new(RelayedAddress), stun.AttrXORRelayedAddress}, - {new(ChannelNumber), stun.AttrChannelNumber}, - {new(Data), stun.AttrData}, - {new(EvenPort), stun.AttrEvenPort}, - {new(Lifetime), stun.AttrLifetime}, - {new(ReservationToken), stun.AttrReservationToken}, - } - var firstByte = byte(0) - if len(data) > 0 { - firstByte = data[0] - } - a := attributes.pick(firstByte) - value := data - if len(data) > 1 { - value = value[1:] - } - m1.WriteHeader() - m1.Add(a.t, value) - err := a.g.GetFrom(m1) - if err == stun.ErrAttributeNotFound { - fmt.Println("unexpected 404") // nolint - panic(err) // nolint - } - if err != nil { - return 1 - } - m2.WriteHeader() - if err := a.g.AddTo(m2); err != nil { - fmt.Println("failed to add attribute to m2") // nolint - panic(err) // nolint - } - m3.WriteHeader() - v, err := m2.Get(a.t) - if err != nil { - panic(err) // nolint - } - m3.Add(a.t, v) - - if !m2.Equal(m3) { - fmt.Println(m2, "not equal", m3) // nolint - panic("not equal") // nolint - } - return 1 -} - -var d = &ChannelData{} - -func FuzzChannelData(data []byte) int { - d.Reset() - if b := bin.Uint16(data[0:4]); b > 20000 { - bin.PutUint16(data[0:4], MinChannelNumber-1) - } else if b > 40000 { - bin.PutUint16(data[0:4], MinChannelNumber+(MaxChannelNumber-MinChannelNumber)%b) - } - d.Raw = append(d.Raw, data...) - if d.Decode() != nil { - return 0 - } - d2 := &ChannelData{} - d.Encode() - if !d.Number.Valid() { - return 1 - } - d2.Raw = d.Raw - if err := d2.Decode(); err != nil { - panic(err) //nolint - } - return 1 -} diff --git a/vendor/github.com/pion/turn/v2/internal/proto/reqtrans.go b/vendor/github.com/pion/turn/v2/internal/proto/reqtrans.go index a4e48639f..cc73a4713 100644 --- a/vendor/github.com/pion/turn/v2/internal/proto/reqtrans.go +++ b/vendor/github.com/pion/turn/v2/internal/proto/reqtrans.go @@ -27,7 +27,7 @@ func (p Protocol) String() string { // // This attribute is used by the client to request a specific transport // protocol for the allocated transport address. RFC 5766 only allows the use of -// codepoint 17 (User Datagram Protocol). +// code point 17 (User Datagram Protocol). // // RFC 5766 Section 14.7 type RequestedTransport struct { diff --git a/vendor/github.com/pion/turn/v2/internal/server/turn.go b/vendor/github.com/pion/turn/v2/internal/server/turn.go index cbac09c2d..4e7d25db0 100644 --- a/vendor/github.com/pion/turn/v2/internal/server/turn.go +++ b/vendor/github.com/pion/turn/v2/internal/server/turn.go @@ -33,14 +33,20 @@ func handleAllocateRequest(r Request, m *stun.Message) error { reservationToken := "" badRequestMsg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest}) - insufficentCapacityMsg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeInsufficientCapacity}) + insufficientCapacityMsg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeInsufficientCapacity}) // 2. The server checks if the 5-tuple is currently in use by an // existing allocation. If yes, the server rejects the request with // a 437 (Allocation Mismatch) error. if alloc := r.AllocationManager.GetAllocation(fiveTuple); alloc != nil { - msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeAllocMismatch}) - return buildAndSendErr(r.Conn, r.SrcAddr, errRelayAlreadyAllocatedForFiveTuple, msg...) + id, attrs := alloc.GetResponseCache() + if id != m.TransactionID { + msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeAllocMismatch}) + return buildAndSendErr(r.Conn, r.SrcAddr, errRelayAlreadyAllocatedForFiveTuple, msg...) + } + // a retry allocation + msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassSuccessResponse), append(attrs, messageIntegrity)...) + return buildAndSend(r.Conn, r.SrcAddr, msg...) } // 3. The server checks if the request contains a REQUESTED-TRANSPORT @@ -91,10 +97,10 @@ func handleAllocateRequest(r Request, m *stun.Message) error { // error. var evenPort proto.EvenPort if err = evenPort.GetFrom(m); err == nil { - randomPort := 0 + var randomPort int randomPort, err = r.AllocationManager.GetRandomEvenPort() if err != nil { - return buildAndSendErr(r.Conn, r.SrcAddr, err, insufficentCapacityMsg...) + return buildAndSendErr(r.Conn, r.SrcAddr, err, insufficientCapacityMsg...) } requestedPort = randomPort reservationToken = randSeq(8) @@ -118,7 +124,7 @@ func handleAllocateRequest(r Request, m *stun.Message) error { requestedPort, lifetimeDuration) if err != nil { - return buildAndSendErr(r.Conn, r.SrcAddr, err, insufficentCapacityMsg...) + return buildAndSendErr(r.Conn, r.SrcAddr, err, insufficientCapacityMsg...) } // Once the allocation is created, the server replies with a success @@ -162,6 +168,7 @@ func handleAllocateRequest(r Request, m *stun.Message) error { } msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassSuccessResponse), append(responseAttrs, messageIntegrity)...) + a.SetResponseCache(m.TransactionID, responseAttrs) return buildAndSend(r.Conn, r.SrcAddr, msg...) } @@ -224,8 +231,15 @@ func handleCreatePermissionRequest(r Request, m *stun.Message) error { return err } + if err := r.AllocationManager.GrantPermission(r.SrcAddr, peerAddress.IP); err != nil { + r.Log.Infof("permission denied for client %s to peer %s", r.SrcAddr.String(), + peerAddress.IP.String()) + return err + } + r.Log.Debugf("adding permission for %s", fmt.Sprintf("%s:%d", peerAddress.IP.String(), peerAddress.Port)) + a.AddPermission(allocation.NewPermission( &net.UDPAddr{ IP: peerAddress.IP, @@ -309,6 +323,16 @@ func handleChannelBindRequest(r Request, m *stun.Message) error { return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...) } + if err = r.AllocationManager.GrantPermission(r.SrcAddr, peerAddr.IP); err != nil { + r.Log.Infof("permission denied for client %s to peer %s", r.SrcAddr.String(), + peerAddr.IP.String()) + + unauthorizedRequestMsg := buildMsg(m.TransactionID, + stun.NewType(stun.MethodChannelBind, stun.ClassErrorResponse), + &stun.ErrorCodeAttribute{Code: stun.CodeUnauthorized}) + return buildAndSendErr(r.Conn, r.SrcAddr, err, unauthorizedRequestMsg...) + } + r.Log.Debugf("binding channel %d to %s", channel, fmt.Sprintf("%s:%d", peerAddr.IP.String(), peerAddr.Port)) diff --git a/vendor/github.com/pion/turn/v2/internal/server/util.go b/vendor/github.com/pion/turn/v2/internal/server/util.go index c6baffbd1..c9a339213 100644 --- a/vendor/github.com/pion/turn/v2/internal/server/util.go +++ b/vendor/github.com/pion/turn/v2/internal/server/util.go @@ -95,8 +95,13 @@ func authenticateRequest(r Request, m *stun.Message, callingMethod stun.Method) } // Assert Nonce exists and is not expired - nonceCreationTime, ok := r.Nonces.Load(string(*nonceAttr)) - if !ok || time.Since(nonceCreationTime.(time.Time)) >= nonceLifetime { + nonceCreationTime, nonceFound := r.Nonces.Load(string(*nonceAttr)) + if !nonceFound { + r.Nonces.Delete(nonceAttr) + return respondWithNonce(stun.CodeStaleNonce) + } + + if timeValue, ok := nonceCreationTime.(time.Time); !ok || time.Since(timeValue) >= nonceLifetime { r.Nonces.Delete(nonceAttr) return respondWithNonce(stun.CodeStaleNonce) } diff --git a/vendor/github.com/pion/turn/v2/lt_cred.go b/vendor/github.com/pion/turn/v2/lt_cred.go index f40f40c28..d4e9ab565 100644 --- a/vendor/github.com/pion/turn/v2/lt_cred.go +++ b/vendor/github.com/pion/turn/v2/lt_cred.go @@ -36,7 +36,7 @@ func NewLongTermAuthHandler(sharedSecret string, l logging.LeveledLogger) AuthHa l = logging.NewDefaultLoggerFactory().NewLogger("turn") } return func(username, realm string, srcAddr net.Addr) (key []byte, ok bool) { - l.Tracef("Authentication username=%q realm=%q srcAddr=%v\n", username, realm, srcAddr) + l.Tracef("Authentication username=%q realm=%q srcAddr=%v", username, realm, srcAddr) t, err := strconv.Atoi(username) if err != nil { l.Errorf("Invalid time-windowed username %q", username) diff --git a/vendor/github.com/pion/turn/v2/relay_address_generator_none.go b/vendor/github.com/pion/turn/v2/relay_address_generator_none.go index 6fab06105..f453d0994 100644 --- a/vendor/github.com/pion/turn/v2/relay_address_generator_none.go +++ b/vendor/github.com/pion/turn/v2/relay_address_generator_none.go @@ -1,10 +1,12 @@ package turn import ( + "fmt" "net" "strconv" - "github.com/pion/transport/vnet" + "github.com/pion/transport/v2" + "github.com/pion/transport/v2/stdnet" ) // RelayAddressGeneratorNone returns the listener with no modifications @@ -12,13 +14,17 @@ type RelayAddressGeneratorNone struct { // Address is passed to Listen/ListenPacket when creating the Relay Address string - Net *vnet.Net + Net transport.Net } -// Validate is caled on server startup and confirms the RelayAddressGenerator is properly configured +// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured func (r *RelayAddressGeneratorNone) Validate() error { if r.Net == nil { - r.Net = vnet.NewNet(nil) + var err error + r.Net, err = stdnet.NewNet() + if err != nil { + return fmt.Errorf("failed to create network: %w", err) + } } switch { diff --git a/vendor/github.com/pion/turn/v2/relay_address_generator_range.go b/vendor/github.com/pion/turn/v2/relay_address_generator_range.go index 9f95429e2..48a0f2bbc 100644 --- a/vendor/github.com/pion/turn/v2/relay_address_generator_range.go +++ b/vendor/github.com/pion/turn/v2/relay_address_generator_range.go @@ -5,7 +5,8 @@ import ( "net" "github.com/pion/randutil" - "github.com/pion/transport/vnet" + "github.com/pion/transport/v2" + "github.com/pion/transport/v2/stdnet" ) // RelayAddressGeneratorPortRange can be used to only allocate connections inside a defined port range. @@ -28,13 +29,17 @@ type RelayAddressGeneratorPortRange struct { // Address is passed to Listen/ListenPacket when creating the Relay Address string - Net *vnet.Net + Net transport.Net } // Validate is called on server startup and confirms the RelayAddressGenerator is properly configured func (r *RelayAddressGeneratorPortRange) Validate() error { if r.Net == nil { - r.Net = vnet.NewNet(nil) + var err error + r.Net, err = stdnet.NewNet() + if err != nil { + return fmt.Errorf("failed to create network: %w", err) + } } if r.Rand == nil { @@ -66,7 +71,11 @@ func (r *RelayAddressGeneratorPortRange) AllocatePacketConn(network string, requ if err != nil { return nil, nil, err } - relayAddr := conn.LocalAddr().(*net.UDPAddr) + relayAddr, ok := conn.LocalAddr().(*net.UDPAddr) + if !ok { + return nil, nil, errNilConn + } + relayAddr.IP = r.RelayAddress return conn, relayAddr, nil } @@ -78,7 +87,11 @@ func (r *RelayAddressGeneratorPortRange) AllocatePacketConn(network string, requ continue } - relayAddr := conn.LocalAddr().(*net.UDPAddr) + relayAddr, ok := conn.LocalAddr().(*net.UDPAddr) + if !ok { + return nil, nil, errNilConn + } + relayAddr.IP = r.RelayAddress return conn, relayAddr, nil } diff --git a/vendor/github.com/pion/turn/v2/relay_address_generator_static.go b/vendor/github.com/pion/turn/v2/relay_address_generator_static.go index ae921e7f2..ba2be9fd3 100644 --- a/vendor/github.com/pion/turn/v2/relay_address_generator_static.go +++ b/vendor/github.com/pion/turn/v2/relay_address_generator_static.go @@ -1,10 +1,12 @@ package turn import ( + "fmt" "net" "strconv" - "github.com/pion/transport/vnet" + "github.com/pion/transport/v2" + "github.com/pion/transport/v2/stdnet" ) // RelayAddressGeneratorStatic can be used to return static IP address each time a relay is created. @@ -16,13 +18,17 @@ type RelayAddressGeneratorStatic struct { // Address is passed to Listen/ListenPacket when creating the Relay Address string - Net *vnet.Net + Net transport.Net } -// Validate is caled on server startup and confirms the RelayAddressGenerator is properly configured +// Validate is called on server startup and confirms the RelayAddressGenerator is properly configured func (r *RelayAddressGeneratorStatic) Validate() error { if r.Net == nil { - r.Net = vnet.NewNet(nil) + var err error + r.Net, err = stdnet.NewNet() + if err != nil { + return fmt.Errorf("failed to create network: %w", err) + } } switch { @@ -43,7 +49,11 @@ func (r *RelayAddressGeneratorStatic) AllocatePacketConn(network string, request } // Replace actual listening IP with the user requested one of RelayAddressGeneratorStatic - relayAddr := conn.LocalAddr().(*net.UDPAddr) + relayAddr, ok := conn.LocalAddr().(*net.UDPAddr) + if !ok { + return nil, nil, errNilConn + } + relayAddr.IP = r.RelayAddress return conn, relayAddr, nil diff --git a/vendor/github.com/pion/turn/v2/renovate.json b/vendor/github.com/pion/turn/v2/renovate.json index f1614058a..f1bb98c6a 100644 --- a/vendor/github.com/pion/turn/v2/renovate.json +++ b/vendor/github.com/pion/turn/v2/renovate.json @@ -1,27 +1,6 @@ { + "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:base", - ":disableDependencyDashboard" - ], - "postUpdateOptions": [ - "gomodTidy" - ], - "commitBody": "Generated by renovateBot", - "packageRules": [ - { - "matchUpdateTypes": ["minor", "patch", "pin", "digest"], - "automerge": true - }, - { - "packagePatterns": ["^golang.org/x/"], - "schedule": ["on the first day of the month"] - } - ], - "ignorePaths": [ - ".github/workflows/generate-authors.yml", - ".github/workflows/lint.yaml", - ".github/workflows/renovate-go-mod-fix.yaml", - ".github/workflows/test.yaml", - ".github/workflows/tidy-check.yaml" + "github>pion/renovate-config" ] } diff --git a/vendor/github.com/pion/turn/v2/server.go b/vendor/github.com/pion/turn/v2/server.go index fc36aef87..3f57f9db0 100644 --- a/vendor/github.com/pion/turn/v2/server.go +++ b/vendor/github.com/pion/turn/v2/server.go @@ -28,11 +28,12 @@ type Server struct { packetConnConfigs []PacketConnConfig listenerConfigs []ListenerConfig allocationManagers []*allocation.Manager - - inboundMTU int + inboundMTU int } // NewServer creates the Pion TURN server +// +//nolint:gocognit func NewServer(config ServerConfig) (*Server, error) { if err := config.validate(); err != nil { return nil, err @@ -55,7 +56,6 @@ func NewServer(config ServerConfig) (*Server, error) { channelBindTimeout: config.ChannelBindTimeout, packetConnConfigs: config.PacketConnConfigs, listenerConfigs: config.ListenerConfigs, - allocationManagers: make([]*allocation.Manager, len(config.PacketConnConfigs)+len(config.ListenerConfigs)), nonces: &sync.Map{}, inboundMTU: mtu, } @@ -64,56 +64,22 @@ func NewServer(config ServerConfig) (*Server, error) { s.channelBindTimeout = proto.DefaultLifetime } - for i := range s.packetConnConfigs { - go func(i int, p PacketConnConfig) { - allocationManager, err := allocation.NewManager(allocation.ManagerConfig{ - AllocatePacketConn: p.RelayAddressGenerator.AllocatePacketConn, - AllocateConn: p.RelayAddressGenerator.AllocateConn, - LeveledLogger: s.log, - }) - if err != nil { - s.log.Errorf("exit read loop on error: %s", err.Error()) - return - } - s.allocationManagers[i] = allocationManager - defer func() { - if err := allocationManager.Close(); err != nil { - s.log.Errorf("Failed to close AllocationManager: %s", err.Error()) - } - }() + for _, cfg := range s.packetConnConfigs { + am, err := s.createAllocationManager(cfg.RelayAddressGenerator, cfg.PermissionHandler) + if err != nil { + return nil, fmt.Errorf("failed to create AllocationManager: %w", err) + } - s.readLoop(p.PacketConn, allocationManager) - }(i, s.packetConnConfigs[i]) + go s.readPacketConn(cfg, am) } - for i, listener := range s.listenerConfigs { - go func(i int, l ListenerConfig) { - allocationManager, err := allocation.NewManager(allocation.ManagerConfig{ - AllocatePacketConn: l.RelayAddressGenerator.AllocatePacketConn, - AllocateConn: l.RelayAddressGenerator.AllocateConn, - LeveledLogger: s.log, - }) - if err != nil { - s.log.Errorf("exit read loop on error: %s", err.Error()) - return - } - s.allocationManagers[i] = allocationManager - defer func() { - if err := allocationManager.Close(); err != nil { - s.log.Errorf("Failed to close AllocationManager: %s", err.Error()) - } - }() + for _, cfg := range s.listenerConfigs { + am, err := s.createAllocationManager(cfg.RelayAddressGenerator, cfg.PermissionHandler) + if err != nil { + return nil, fmt.Errorf("failed to create AllocationManager: %w", err) + } - for { - conn, err := l.Listener.Accept() - if err != nil { - s.log.Debugf("exit accept loop on error: %s", err.Error()) - return - } - - go s.readLoop(NewSTUNConn(conn), allocationManager) - } - }(i+len(s.packetConnConfigs), listener) + go s.readListener(cfg, am) } return s, nil @@ -121,27 +87,25 @@ func NewServer(config ServerConfig) (*Server, error) { // AllocationCount returns the number of active allocations. It can be used to drain the server before closing func (s *Server) AllocationCount() int { - allocations := 0 - for _, manager := range s.allocationManagers { - if manager != nil { - allocations += manager.AllocationCount() - } + allocs := 0 + for _, am := range s.allocationManagers { + allocs += am.AllocationCount() } - return allocations + return allocs } // Close stops the TURN Server. It cleans up any associated state and closes all connections it is managing func (s *Server) Close() error { var errors []error - for _, p := range s.packetConnConfigs { - if err := p.PacketConn.Close(); err != nil { + for _, cfg := range s.packetConnConfigs { + if err := cfg.PacketConn.Close(); err != nil { errors = append(errors, err) } } - for _, l := range s.listenerConfigs { - if err := l.Listener.Close(); err != nil { + for _, cfg := range s.listenerConfigs { + if err := cfg.Listener.Close(); err != nil { errors = append(errors, err) } } @@ -152,12 +116,58 @@ func (s *Server) Close() error { err := errFailedToClose for _, e := range errors { - err = fmt.Errorf("%s; Close error (%v) ", err.Error(), e) //nolint:goerr113 + err = fmt.Errorf("%s; close error (%w) ", err, e) } return err } +func (s *Server) readPacketConn(p PacketConnConfig, am *allocation.Manager) { + s.readLoop(p.PacketConn, am) + + if err := am.Close(); err != nil { + s.log.Errorf("Failed to close AllocationManager: %s", err) + } +} + +func (s *Server) readListener(l ListenerConfig, am *allocation.Manager) { + defer func() { + if err := am.Close(); err != nil { + s.log.Errorf("Failed to close AllocationManager: %s", err) + } + }() + + for { + conn, err := l.Listener.Accept() + if err != nil { + s.log.Debugf("Failed to accept: %s", err) + return + } + + go s.readLoop(NewSTUNConn(conn), am) + } +} + +func (s *Server) createAllocationManager(addrGenerator RelayAddressGenerator, handler PermissionHandler) (*allocation.Manager, error) { + if handler == nil { + handler = DefaultPermissionHandler + } + + am, err := allocation.NewManager(allocation.ManagerConfig{ + AllocatePacketConn: addrGenerator.AllocatePacketConn, + AllocateConn: addrGenerator.AllocateConn, + PermissionHandler: handler, + LeveledLogger: s.log, + }) + if err != nil { + return am, err + } + + s.allocationManagers = append(s.allocationManagers, am) + + return am, err +} + func (s *Server) readLoop(p net.PacketConn, allocationManager *allocation.Manager) { buf := make([]byte, s.inboundMTU) for { diff --git a/vendor/github.com/pion/turn/v2/server_config.go b/vendor/github.com/pion/turn/v2/server_config.go index a506abe3a..5487900db 100644 --- a/vendor/github.com/pion/turn/v2/server_config.go +++ b/vendor/github.com/pion/turn/v2/server_config.go @@ -23,6 +23,19 @@ type RelayAddressGenerator interface { AllocateConn(network string, requestedPort int) (net.Conn, net.Addr, error) } +// PermissionHandler is a callback to filter incoming CreatePermission and ChannelBindRequest +// requests based on the client IP address and port and the peer IP address the client intends to +// connect to. If the client is behind a NAT then the filter acts on the server reflexive +// ("mapped") address instead of the real client IP address and port. Note that TURN permissions +// are per-allocation and per-peer-IP-address, to mimic the address-restricted filtering mechanism +// of NATs that comply with [RFC4787], see https://tools.ietf.org/html/rfc5766#section-2.3. +type PermissionHandler func(clientAddr net.Addr, peerIP net.IP) (ok bool) + +// DefaultPermissionHandler is convince function that grants permission to all peers +func DefaultPermissionHandler(clientAddr net.Addr, peerIP net.IP) (ok bool) { + return true +} + // PacketConnConfig is a single net.PacketConn to listen/write on. This will be used for UDP listeners type PacketConnConfig struct { PacketConn net.PacketConn @@ -30,6 +43,11 @@ type PacketConnConfig struct { // When an allocation is generated the RelayAddressGenerator // creates the net.PacketConn and returns the IP/Port it is available at RelayAddressGenerator RelayAddressGenerator + + // PermissionHandler is a callback to filter peer addresses. Can be set as nil, in which + // case the DefaultPermissionHandler is automatically instantiated to admit all peer + // connections + PermissionHandler PermissionHandler } func (c *PacketConnConfig) validate() error { @@ -50,6 +68,11 @@ type ListenerConfig struct { // When an allocation is generated the RelayAddressGenerator // creates the net.PacketConn and returns the IP/Port it is available at RelayAddressGenerator RelayAddressGenerator + + // PermissionHandler is a callback to filter peer addresses. Can be set as nil, in which + // case the DefaultPermissionHandler is automatically instantiated to admit all peer + // connections + PermissionHandler PermissionHandler } func (c *ListenerConfig) validate() error { @@ -67,7 +90,7 @@ func (c *ListenerConfig) validate() error { // AuthHandler is a callback used to handle incoming auth requests, allowing users to customize Pion TURN with custom behavior type AuthHandler func(username, realm string, srcAddr net.Addr) (key []byte, ok bool) -// GenerateAuthKey is a convince function to easily generate keys in the format used by AuthHandler +// GenerateAuthKey is a convenience function to easily generate keys in the format used by AuthHandler func GenerateAuthKey(username, realm, password string) []byte { // #nosec h := md5.New() diff --git a/vendor/github.com/pion/udp/LICENSE b/vendor/github.com/pion/udp/LICENSE deleted file mode 100644 index 81f990d60..000000000 --- a/vendor/github.com/pion/udp/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 - -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/pion/udp/README.md b/vendor/github.com/pion/udp/README.md deleted file mode 100644 index 63e7bcb2f..000000000 --- a/vendor/github.com/pion/udp/README.md +++ /dev/null @@ -1,41 +0,0 @@ -

-
- Pion UDP -
-

-

A connection-oriented listener over a UDP PacketConn

-

- Pion UDP - - Slack Widget -
- Build Status - GoDoc - Coverage Status - Go Report Card - - License: MIT -

-
- -### Roadmap -This package is used in the [DTLS](https://github.com/pion/dtls) and [SCTP](https://github.com/pion/sctp) transport to provide a connection-oriented listener over a UDP. - -### Community -Pion has an active community on the [Golang Slack](https://pion.ly/slack/). You can also use [Pion mailing list](https://groups.google.com/forum/#!forum/pion). - -We are always looking to support **your projects**. Please reach out if you have something to build! - -If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly) - -### Contributing -Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible: - -* [Sean DuBois](https://github.com/Sean-Der) - *Original Author* -* [Michiel De Backker](https://github.com/backkem) - *Original Author* -* [Atsushi Watanabe](https://github.com/at-wat) - *Original Author* -* [ZHENK](https://github.com/scorpionknifes) -* [Daniel Beseda](https://github.com/besedad) - -### License -MIT License - see [LICENSE](LICENSE) for full text diff --git a/vendor/github.com/pion/udp/renovate.json b/vendor/github.com/pion/udp/renovate.json deleted file mode 100644 index 4400fd9b2..000000000 --- a/vendor/github.com/pion/udp/renovate.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "extends": [ - "config:base" - ], - "postUpdateOptions": [ - "gomodTidy" - ], - "commitBody": "Generated by renovateBot", - "packageRules": [ - { - "packagePatterns": ["^golang.org/x/"], - "schedule": ["on the first day of the month"] - } - ] -} diff --git a/vendor/github.com/pion/webrtc/v3/.codacy.yaml b/vendor/github.com/pion/webrtc/v3/.codacy.yaml index a8c225b74..aa0b11b39 100644 --- a/vendor/github.com/pion/webrtc/v3/.codacy.yaml +++ b/vendor/github.com/pion/webrtc/v3/.codacy.yaml @@ -1,3 +1,6 @@ --- +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + exclude_paths: - examples/examples.json diff --git a/vendor/github.com/pion/webrtc/v3/.gitignore b/vendor/github.com/pion/webrtc/v3/.gitignore index 83db74ba5..6e2f206a9 100644 --- a/vendor/github.com/pion/webrtc/v3/.gitignore +++ b/vendor/github.com/pion/webrtc/v3/.gitignore @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + ### JetBrains IDE ### ##################### .idea/ @@ -22,3 +25,4 @@ cover.out *.wasm examples/sfu-ws/cert.pem examples/sfu-ws/key.pem +wasm_exec.js diff --git a/vendor/github.com/pion/webrtc/v3/.golangci.yml b/vendor/github.com/pion/webrtc/v3/.golangci.yml index d6162c970..4e3eddf42 100644 --- a/vendor/github.com/pion/webrtc/v3/.golangci.yml +++ b/vendor/github.com/pion/webrtc/v3/.golangci.yml @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + linters-settings: govet: check-shadowing: true @@ -10,19 +13,34 @@ linters-settings: modules: - github.com/pkg/errors: recommendations: - - errors + - errors + forbidigo: + forbid: + - ^fmt.Print(f|ln)?$ + - ^log.(Panic|Fatal|Print)(f|ln)?$ + - ^os.Exit$ + - ^panic$ + - ^print(ln)?$ linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers + - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully - - deadcode # Finds unused code + - contextcheck # check the function whether use a non-inherited context + - decorder # check declaration order and count of types, constants, variables and functions - depguard # Go linter that checks if package imports are in a list of acceptable packages - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection + - durationcheck # check for two durations multiplied together - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. + - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. - exhaustive # check exhaustiveness of enum switch statements - exportloopref # checks for pointers to enclosing loop variables + - forbidigo # Forbids identifiers + - forcetypeassert # finds forced type assertions - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - gochecknoinits # Checks that no init functions are present in Go code @@ -35,40 +53,59 @@ linters: - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - - golint # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - gosimple # Linter for Go source code that specializes in simplifying a code - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - grouper # An analyzer to analyze expression groups. + - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used - misspell # Finds commonly misspelled English words in comments - nakedret # Finds naked returns in functions greater than a specified function length + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. - noctx # noctx finds sending http request without context.Context - - scopelint # Scopelint checks for unpinned variables in go programs + - predeclared # find code that shadows one of Go's predeclared identifiers + - revive # golint replacement, finds style mistakes - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - - structcheck # Finds unused struct fields - stylecheck # Stylecheck is a replacement for golint + - tagliatelle # Checks the struct tags. + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types - - varcheck # Finds unused global variables and constants + - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: + - containedctx # containedctx is a linter that detects struct contained context.Context field + - cyclop # checks function and package cyclomatic complexity + - exhaustivestruct # Checks if all struct's fields are initialized - funlen # Tool for detection of long functions - gocyclo # Computes and checks the cyclomatic complexity of functions - godot # Check if comments end in a period - gomnd # An analyzer to detect magic numbers. + - ifshort # Checks that your code uses short syntax for if-statements whenever possible + - ireturn # Accept Interfaces, Return Concrete Types - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - nestif # Reports deeply nested if statements - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - nolintlint # Reports ill-formed or insufficient nolint directives + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - prealloc # Finds slice declarations that could potentially be preallocated + - promlinter # Check Prometheus metrics naming via promlint - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. - testpackage # linter that makes you use a separate _test package + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers + - varnamelen # checks that the length of a variable's name matches its scope + - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! issues: @@ -78,12 +115,23 @@ issues: - path: _test\.go linters: - gocognit + - forbidigo # Allow complex main function in examples - path: examples text: "of func `main` is high" linters: - gocognit + + # Allow forbidden identifiers in examples + - path: examples + linters: + - forbidigo + + # Allow forbidden identifiers in CLI commands + - path: cmd + linters: + - forbidigo run: skip-dirs-use-default: false diff --git a/vendor/github.com/pion/webrtc/v3/.goreleaser.yml b/vendor/github.com/pion/webrtc/v3/.goreleaser.yml new file mode 100644 index 000000000..30093e9d6 --- /dev/null +++ b/vendor/github.com/pion/webrtc/v3/.goreleaser.yml @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT + +builds: +- skip: true diff --git a/vendor/github.com/pion/webrtc/v3/AUTHORS.txt b/vendor/github.com/pion/webrtc/v3/AUTHORS.txt index 0ef018685..4ebd9a22f 100644 --- a/vendor/github.com/pion/webrtc/v3/AUTHORS.txt +++ b/vendor/github.com/pion/webrtc/v3/AUTHORS.txt @@ -2,10 +2,13 @@ # we would love to have you https://github.com/pion/webrtc/wiki/Contributing # # This file is auto generated, using git to list all individuals contributors. -# see `.github/generate-authors.sh` for the scripting +# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting a-wing <1@233.email> +Aaron Boushley Aaron France Adam Kiss +Aditya Kumar +Adrian Cable adwpc aggresss akil @@ -13,11 +16,13 @@ Aleksandr Razumov aler9 <46489434+aler9@users.noreply.github.com> Alex Browne Alex Harford +Alexey Khit AlexWoo(武杰) Ali Error Andrew N. Shalaev Antoine Baché Antoine Baché +Anton Artur Shellunts Assad Obaid Ato Araki @@ -39,13 +44,17 @@ Cedric Fung cgojin Chad Retz chenkaiC4 +Chinmay Kousik Chris Hiszpanski Christopher Fry Clayton McCray cnderrauber cyannuk +Daniele Sluijters David Hamilton David Zhao +David Zhao +david.s Dean Sheather decanus <7621705+decanus@users.noreply.github.com> Denis @@ -54,8 +63,12 @@ donotanswer earle Egon Elbre Eric Daniels +Eric Fontaine feixiao +Forest Johnson frank +funvit +Gabor Pongracz Gareth Hayes Guilherme Hanjun Kim @@ -72,12 +85,16 @@ Jake B Jamie Good Jason Jeff Tchang +jeremija Jerko Steiner +Jerry Tao jinleileiking John Berthels John Bradley +John Selbie JooYoung Jorropo +Josh Bleecher Snyder juberti Juliusz Chroboczek Justin Okamoto @@ -90,11 +107,14 @@ krishna chiatanya Kuzmin Vladimir lawl Len +Leslie Wang +lisa yan Lukas Herman Luke Luke Curley Luke S Magnus Wahlstrand +Manish Markus Tzoe Marouane <6729798+nindolabs@users.noreply.github.com> Marouane @@ -108,6 +128,8 @@ Michiel De Backker <38858977+backkem@users.noreply.github.com> Mike Coleman Mindgamesnl mission-liao +mohammadne +mr-shitij <21.shitijagrawal@gmail.com> mxmCherry Nam V. Do Nick Mykins @@ -121,9 +143,11 @@ opennota OrlandoCo Pascal Benoit pascal-ace <47424881+pascal-ace@users.noreply.github.com> +Patrice Ferlet Patrick Lange Patryk Rogalski Pieere Pi +Pouget-Abadie q191201771 <191201771@qq.com> Quentin Renard Rafael Viscarra @@ -131,6 +155,7 @@ rahulnakre Raphael Randschau Raphael Randschau Reese <3253971+figadore@users.noreply.github.com> +rob rob-deutsch Robert Eperjesi Robin Raymond @@ -140,18 +165,24 @@ ronan Ryan Shumate salmān aljammāz Sam Lancia +Sean DuBois Sean DuBois Sean DuBois Sean DuBois Sean DuBois Sean Knight Sebastian Waisbrot +Sidney San Martín Simon Eisenmann simonacca-fotokite <47634061+simonacca-fotokite@users.noreply.github.com> Simone Gotti Slugalisk +Somers Matthews soolaugust spaceCh1mp +Steffen Vogel +stephanrotolante +streamer45 Suhas Gaddam Suzuki Takeo sylba2050 @@ -160,13 +191,17 @@ tarrencev Thomas Miller Tobias Fridén Tomek +treyhakanson +Tristan Matthews Twometer Vicken Simonian wattanakorn495 Will Forcey Will Watson +WofWca Woodrow Douglass xsbchen +Yoon SeungYong Yuki Igarashi yusuke Yutaka Takeda @@ -174,3 +209,6 @@ ZHENK zigazeljko Štefan Uram 박종훈 + +# List of contributors not appearing in Git history + diff --git a/vendor/github.com/pion/webrtc/v3/LICENSE b/vendor/github.com/pion/webrtc/v3/LICENSE index ab602974d..491caf6b0 100644 --- a/vendor/github.com/pion/webrtc/v3/LICENSE +++ b/vendor/github.com/pion/webrtc/v3/LICENSE @@ -1,21 +1,9 @@ MIT License -Copyright (c) 2018 +Copyright (c) 2023 The Pion community -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: +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 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. +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/pion/webrtc/v3/README.md b/vendor/github.com/pion/webrtc/v3/README.md index da1b3d67f..80dd8c123 100644 --- a/vendor/github.com/pion/webrtc/v3/README.md +++ b/vendor/github.com/pion/webrtc/v3/README.md @@ -6,17 +6,16 @@

A pure Go implementation of the WebRTC API

- Pion webrtc + Pion WebRTC Sourcegraph Widget Slack Widget Twitter Widget
- Build Status - PkgGoDev + GitHub Workflow Status + Go Reference Coverage Status - Go Report Card - Codacy Badge + Go Report Card License: MIT


@@ -88,7 +87,7 @@ This book is vendor agnostic and will not have any Pion specific information. * [NACK](https://github.com/pion/interceptor/pull/4) * [Sender/Receiver Reports](https://github.com/pion/interceptor/tree/master/pkg/report) * [Transport Wide Congestion Control Feedback](https://github.com/pion/interceptor/tree/master/pkg/twcc) -* Bandwidth Estimation is actively being implemented, see [pion/interceptor#25](https://github.com/pion/interceptor/issues/25) +* [Bandwidth Estimation](https://github.com/pion/webrtc/tree/master/examples/bandwidth-estimation-from-disk) #### Security * TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 and TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA for DTLS v1.2 @@ -107,12 +106,14 @@ This book is vendor agnostic and will not have any Pion specific information. * **Time to run entire test suite** - 25.60s user 9.40s system 45% cpu 1:16.69 total * Tools to measure performance [provided](https://github.com/pion/rtsp-bench) - ### Roadmap The library is in active development, please refer to the [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones. We also maintain a list of [Big Ideas](https://github.com/pion/webrtc/wiki/Big-Ideas) these are things we want to build but don't have a clear plan or the resources yet. If you are looking to get involved this is a great place to get started! We would also love to hear your ideas! Even if you can't implement it yourself, it could inspire others. +### Sponsoring +Work on Pion's congestion control and bandwidth estimation was funded through the [User-Operated Internet](https://nlnet.nl/useroperated/) fund, a fund established by [NLnet](https://nlnet.nl/) made possible by financial support from the [PKT Community](https://pkt.cash/)/[The Network Steward](https://pkt.cash/network-steward) and stichting [Technology Commons Trust](https://technologycommons.org/). + ### Community Pion has an active community on the [Slack](https://pion.ly/slack). @@ -122,7 +123,7 @@ We are always looking to support **your projects**. Please reach out if you have If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly) ### Contributing -Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contributing)** to join the group of amazing people making this project possible: +Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible: [AUTHORS.txt](./AUTHORS.txt) ### License MIT License - see [LICENSE](LICENSE) for full text diff --git a/vendor/github.com/pion/webrtc/v3/api.go b/vendor/github.com/pion/webrtc/v3/api.go index 85424df4d..716be1f38 100644 --- a/vendor/github.com/pion/webrtc/v3/api.go +++ b/vendor/github.com/pion/webrtc/v3/api.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js @@ -43,6 +46,8 @@ func NewAPI(options ...func(*API)) *API { // WithMediaEngine allows providing a MediaEngine to the API. // Settings can be changed after passing the engine to an API. +// When a PeerConnection is created the MediaEngine is copied +// and no more changes can be made. func WithMediaEngine(m *MediaEngine) func(a *API) { return func(a *API) { a.mediaEngine = m diff --git a/vendor/github.com/pion/webrtc/v3/api_js.go b/vendor/github.com/pion/webrtc/v3/api_js.go index 3d81ed7b1..fe94bff1f 100644 --- a/vendor/github.com/pion/webrtc/v3/api_js.go +++ b/vendor/github.com/pion/webrtc/v3/api_js.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build js && wasm // +build js,wasm diff --git a/vendor/github.com/pion/webrtc/v3/atomicbool.go b/vendor/github.com/pion/webrtc/v3/atomicbool.go index 1d4bf55ac..cc6cdc1e8 100644 --- a/vendor/github.com/pion/webrtc/v3/atomicbool.go +++ b/vendor/github.com/pion/webrtc/v3/atomicbool.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import "sync/atomic" @@ -20,7 +23,7 @@ func (b *atomicBool) get() bool { } func (b *atomicBool) swap(value bool) bool { - var i int32 = 0 + var i int32 if value { i = 1 } diff --git a/vendor/github.com/pion/webrtc/v3/bundlepolicy.go b/vendor/github.com/pion/webrtc/v3/bundlepolicy.go index 6d39a2773..ea6dad5ae 100644 --- a/vendor/github.com/pion/webrtc/v3/bundlepolicy.go +++ b/vendor/github.com/pion/webrtc/v3/bundlepolicy.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/certificate.go b/vendor/github.com/pion/webrtc/v3/certificate.go index 99e359741..1d5631bc4 100644 --- a/vendor/github.com/pion/webrtc/v3/certificate.go +++ b/vendor/github.com/pion/webrtc/v3/certificate.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js @@ -105,10 +108,12 @@ func (c Certificate) GetFingerprints() ([]DTLSFingerprint, error) { for _, algo := range fingerprintAlgorithms { name, err := fingerprint.StringFromHash(algo) if err != nil { + // nolint return nil, fmt.Errorf("%w: %v", ErrFailedToGenerateCertificateFingerprint, err) } value, err := fingerprint.Fingerprint(c.x509Cert, algo) if err != nil { + // nolint return nil, fmt.Errorf("%w: %v", ErrFailedToGenerateCertificateFingerprint, err) } res[i] = DTLSFingerprint{ diff --git a/vendor/github.com/pion/webrtc/v3/codecov.yml b/vendor/github.com/pion/webrtc/v3/codecov.yml index 085200a48..263e4d45c 100644 --- a/vendor/github.com/pion/webrtc/v3/codecov.yml +++ b/vendor/github.com/pion/webrtc/v3/codecov.yml @@ -3,6 +3,8 @@ # # It is automatically copied from https://github.com/pion/.goassets repository. # +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT coverage: status: diff --git a/vendor/github.com/pion/webrtc/v3/configuration.go b/vendor/github.com/pion/webrtc/v3/configuration.go index 608c5ab7e..90be318e9 100644 --- a/vendor/github.com/pion/webrtc/v3/configuration.go +++ b/vendor/github.com/pion/webrtc/v3/configuration.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js diff --git a/vendor/github.com/pion/webrtc/v3/configuration_common.go b/vendor/github.com/pion/webrtc/v3/configuration_common.go index 92fc22831..a3acdf5b8 100644 --- a/vendor/github.com/pion/webrtc/v3/configuration_common.go +++ b/vendor/github.com/pion/webrtc/v3/configuration_common.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import "strings" diff --git a/vendor/github.com/pion/webrtc/v3/configuration_js.go b/vendor/github.com/pion/webrtc/v3/configuration_js.go index 2ba4d268e..097085f9b 100644 --- a/vendor/github.com/pion/webrtc/v3/configuration_js.go +++ b/vendor/github.com/pion/webrtc/v3/configuration_js.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build js && wasm // +build js,wasm diff --git a/vendor/github.com/pion/webrtc/v3/constants.go b/vendor/github.com/pion/webrtc/v3/constants.go index 825601ddb..94ba78494 100644 --- a/vendor/github.com/pion/webrtc/v3/constants.go +++ b/vendor/github.com/pion/webrtc/v3/constants.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import "github.com/pion/dtls/v2" @@ -36,5 +39,5 @@ const ( ) func defaultSrtpProtectionProfiles() []dtls.SRTPProtectionProfile { - return []dtls.SRTPProtectionProfile{dtls.SRTP_AEAD_AES_128_GCM, dtls.SRTP_AES128_CM_HMAC_SHA1_80} + return []dtls.SRTPProtectionProfile{dtls.SRTP_AEAD_AES_256_GCM, dtls.SRTP_AEAD_AES_128_GCM, dtls.SRTP_AES128_CM_HMAC_SHA1_80} } diff --git a/vendor/github.com/pion/webrtc/v3/datachannel.go b/vendor/github.com/pion/webrtc/v3/datachannel.go index 4af5ac940..9c9154c8c 100644 --- a/vendor/github.com/pion/webrtc/v3/datachannel.go +++ b/vendor/github.com/pion/webrtc/v3/datachannel.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js @@ -49,6 +52,8 @@ type DataChannel struct { onMessageHandler func(DataChannelMessage) openHandlerOnce sync.Once onOpenHandler func() + dialHandlerOnce sync.Once + onDialHandler func() onCloseHandler func() onBufferedAmountLow func() onErrorHandler func(error) @@ -65,7 +70,7 @@ type DataChannel struct { // This constructor is part of the ORTC API. It is not // meant to be used together with the basic WebRTC API. func (api *API) NewDataChannel(transport *SCTPTransport, params *DataChannelParameters) (*DataChannel, error) { - d, err := api.newDataChannel(params, api.settingEngine.LoggerFactory.NewLogger("ortc")) + d, err := api.newDataChannel(params, nil, api.settingEngine.LoggerFactory.NewLogger("ortc")) if err != nil { return nil, err } @@ -80,13 +85,14 @@ func (api *API) NewDataChannel(transport *SCTPTransport, params *DataChannelPara // newDataChannel is an internal constructor for the data channel used to // create the DataChannel object before the networking is set up. -func (api *API) newDataChannel(params *DataChannelParameters, log logging.LeveledLogger) (*DataChannel, error) { +func (api *API) newDataChannel(params *DataChannelParameters, sctpTransport *SCTPTransport, log logging.LeveledLogger) (*DataChannel, error) { // https://w3c.github.io/webrtc-pc/#peer-to-peer-data-api (Step #5) if len(params.Label) > 65535 { return nil, &rtcerr.TypeError{Err: ErrStringSizeLimit} } d := &DataChannel{ + sctpTransport: sctpTransport, statsID: fmt.Sprintf("DataChannel-%d", time.Now().UnixNano()), label: params.Label, protocol: params.Protocol, @@ -175,6 +181,7 @@ func (d *DataChannel) open(sctpTransport *SCTPTransport) error { dc.OnBufferedAmountLow(d.onBufferedAmountLow) d.mu.Unlock() + d.onDial() d.handleOpen(dc, false, d.negotiated) return nil } @@ -228,6 +235,30 @@ func (d *DataChannel) onOpen() { } } +// OnDial sets an event handler which is invoked when the +// peer has been dialed, but before said peer has responsed +func (d *DataChannel) OnDial(f func()) { + d.mu.Lock() + d.dialHandlerOnce = sync.Once{} + d.onDialHandler = f + d.mu.Unlock() + + if d.ReadyState() == DataChannelStateOpen { + // If the data channel is already open, call the handler immediately. + go d.dialHandlerOnce.Do(f) + } +} + +func (d *DataChannel) onDial() { + d.mu.RLock() + handler := d.onDialHandler + d.mu.RUnlock() + + if handler != nil { + go d.dialHandlerOnce.Do(handler) + } +} + // OnClose sets an event handler which is invoked when // the underlying data transport has been closed. func (d *DataChannel) OnClose(f func()) { @@ -280,6 +311,9 @@ func (d *DataChannel) handleOpen(dc *datachannel.DataChannel, isRemote, isAlread // * remote datachannels should fire OnOpened. This isn't spec compliant, but we can't break behavior yet // * already negotiated datachannels should fire OnOpened if d.api.settingEngine.detach.DataChannels || isRemote || isAlreadyNegotiated { + // bufferedAmountLowThreshold and onBufferedAmountLow might be set earlier + d.dataChannel.SetBufferedAmountLowThreshold(d.bufferedAmountLowThreshold) + d.dataChannel.OnBufferedAmountLow(d.onBufferedAmountLow) d.onOpen() } else { dc.OnOpen(func() { @@ -321,12 +355,12 @@ var rlBufPool = sync.Pool{New: func() interface{} { func (d *DataChannel) readLoop() { for { - buffer := rlBufPool.Get().([]byte) + buffer := rlBufPool.Get().([]byte) //nolint:forcetypeassert n, isString, err := d.dataChannel.ReadDataChannel(buffer) if err != nil { rlBufPool.Put(buffer) // nolint:staticcheck d.setReadyState(DataChannelStateClosed) - if err != io.EOF { + if !errors.Is(err, io.EOF) { d.onError(err) } d.onClose() @@ -488,8 +522,8 @@ func (d *DataChannel) ID() *uint16 { // ReadyState represents the state of the DataChannel object. func (d *DataChannel) ReadyState() DataChannelState { - if v := d.readyState.Load(); v != nil { - return v.(DataChannelState) + if v, ok := d.readyState.Load().(DataChannelState); ok { + return v } return DataChannelState(0) } diff --git a/vendor/github.com/pion/webrtc/v3/datachannel_js.go b/vendor/github.com/pion/webrtc/v3/datachannel_js.go index 55214b551..a34ab6efd 100644 --- a/vendor/github.com/pion/webrtc/v3/datachannel_js.go +++ b/vendor/github.com/pion/webrtc/v3/datachannel_js.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build js && wasm // +build js,wasm diff --git a/vendor/github.com/pion/webrtc/v3/datachannel_js_detach.go b/vendor/github.com/pion/webrtc/v3/datachannel_js_detach.go index 43186c5db..691e9d4c7 100644 --- a/vendor/github.com/pion/webrtc/v3/datachannel_js_detach.go +++ b/vendor/github.com/pion/webrtc/v3/datachannel_js_detach.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build js && wasm // +build js,wasm diff --git a/vendor/github.com/pion/webrtc/v3/datachannelinit.go b/vendor/github.com/pion/webrtc/v3/datachannelinit.go index a4320e463..b1775b7c5 100644 --- a/vendor/github.com/pion/webrtc/v3/datachannelinit.go +++ b/vendor/github.com/pion/webrtc/v3/datachannelinit.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // DataChannelInit can be used to configure properties of the underlying diff --git a/vendor/github.com/pion/webrtc/v3/datachannelmessage.go b/vendor/github.com/pion/webrtc/v3/datachannelmessage.go index 1e3c63b36..ba12199f4 100644 --- a/vendor/github.com/pion/webrtc/v3/datachannelmessage.go +++ b/vendor/github.com/pion/webrtc/v3/datachannelmessage.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // DataChannelMessage represents a message received from the diff --git a/vendor/github.com/pion/webrtc/v3/datachannelparameters.go b/vendor/github.com/pion/webrtc/v3/datachannelparameters.go index d67a63b09..9b4f7efcc 100644 --- a/vendor/github.com/pion/webrtc/v3/datachannelparameters.go +++ b/vendor/github.com/pion/webrtc/v3/datachannelparameters.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // DataChannelParameters describes the configuration of the DataChannel. diff --git a/vendor/github.com/pion/webrtc/v3/datachannelstate.go b/vendor/github.com/pion/webrtc/v3/datachannelstate.go index a2c7b95de..b2a85aaeb 100644 --- a/vendor/github.com/pion/webrtc/v3/datachannelstate.go +++ b/vendor/github.com/pion/webrtc/v3/datachannelstate.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // DataChannelState indicates the state of a data channel. diff --git a/vendor/github.com/pion/webrtc/v3/dtlsfingerprint.go b/vendor/github.com/pion/webrtc/v3/dtlsfingerprint.go index db13d3ec6..b0d061481 100644 --- a/vendor/github.com/pion/webrtc/v3/dtlsfingerprint.go +++ b/vendor/github.com/pion/webrtc/v3/dtlsfingerprint.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // DTLSFingerprint specifies the hash function algorithm and certificate diff --git a/vendor/github.com/pion/webrtc/v3/dtlsparameters.go b/vendor/github.com/pion/webrtc/v3/dtlsparameters.go index 4b4b56836..1dfa42a22 100644 --- a/vendor/github.com/pion/webrtc/v3/dtlsparameters.go +++ b/vendor/github.com/pion/webrtc/v3/dtlsparameters.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // DTLSParameters holds information relating to DTLS configuration. diff --git a/vendor/github.com/pion/webrtc/v3/dtlsrole.go b/vendor/github.com/pion/webrtc/v3/dtlsrole.go index 6e67f60e1..9cee581d9 100644 --- a/vendor/github.com/pion/webrtc/v3/dtlsrole.go +++ b/vendor/github.com/pion/webrtc/v3/dtlsrole.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/dtlstransport.go b/vendor/github.com/pion/webrtc/v3/dtlstransport.go index 560eb4b84..df3472cf2 100644 --- a/vendor/github.com/pion/webrtc/v3/dtlstransport.go +++ b/vendor/github.com/pion/webrtc/v3/dtlstransport.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js @@ -134,18 +137,16 @@ func (t *DTLSTransport) WriteRTCP(pkts []rtcp.Packet) (int, error) { srtcpSession, err := t.getSRTCPSession() if err != nil { - return 0, nil + return 0, err } writeStream, err := srtcpSession.OpenWriteStream() if err != nil { + // nolint return 0, fmt.Errorf("%w: %v", errPeerConnWriteRTCPOpenWriteStream, err) } - if n, err := writeStream.Write(raw); err != nil { - return n, err - } - return 0, nil + return writeStream.Write(raw) } // GetLocalParameters returns the DTLS parameters of the local DTLSTransport upon construction. @@ -212,16 +213,19 @@ func (t *DTLSTransport) startSRTP() error { connState := t.conn.ConnectionState() err := srtpConfig.ExtractSessionKeysFromDTLS(&connState, t.role() == DTLSRoleClient) if err != nil { + // nolint return fmt.Errorf("%w: %v", errDtlsKeyExtractionFailed, err) } srtpSession, err := srtp.NewSessionSRTP(t.srtpEndpoint, srtpConfig) if err != nil { + // nolint return fmt.Errorf("%w: %v", errFailedToStartSRTP, err) } srtcpSession, err := srtp.NewSessionSRTCP(t.srtcpEndpoint, srtpConfig) if err != nil { + // nolint return fmt.Errorf("%w: %v", errFailedToStartSRTCP, err) } @@ -232,16 +236,16 @@ func (t *DTLSTransport) startSRTP() error { } func (t *DTLSTransport) getSRTPSession() (*srtp.SessionSRTP, error) { - if value := t.srtpSession.Load(); value != nil { - return value.(*srtp.SessionSRTP), nil + if value, ok := t.srtpSession.Load().(*srtp.SessionSRTP); ok { + return value, nil } return nil, errDtlsTransportNotStarted } func (t *DTLSTransport) getSRTCPSession() (*srtp.SessionSRTCP, error) { - if value := t.srtcpSession.Load(); value != nil { - return value.(*srtp.SessionSRTCP), nil + if value, ok := t.srtcpSession.Load().(*srtp.SessionSRTCP); ok { + return value, nil } return nil, errDtlsTransportNotStarted @@ -312,7 +316,7 @@ func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error { }(), ClientAuth: dtls.RequireAnyClientCert, LoggerFactory: t.api.settingEngine.LoggerFactory, - InsecureSkipVerify: true, + InsecureSkipVerify: !t.api.settingEngine.dtls.disableInsecureSkipVerify, }, nil } @@ -327,10 +331,18 @@ func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error { dtlsConfig.ReplayProtectionWindow = int(*t.api.settingEngine.replayProtection.DTLS) } - if t.api.settingEngine.dtls.retransmissionInterval != 0 { - dtlsConfig.FlightInterval = t.api.settingEngine.dtls.retransmissionInterval + if t.api.settingEngine.dtls.clientAuth != nil { + dtlsConfig.ClientAuth = *t.api.settingEngine.dtls.clientAuth } + dtlsConfig.FlightInterval = t.api.settingEngine.dtls.retransmissionInterval + dtlsConfig.InsecureSkipVerifyHello = t.api.settingEngine.dtls.insecureSkipHelloVerify + dtlsConfig.EllipticCurves = t.api.settingEngine.dtls.ellipticCurves + dtlsConfig.ConnectContextMaker = t.api.settingEngine.dtls.connectContextMaker + dtlsConfig.ExtendedMasterSecret = t.api.settingEngine.dtls.extendedMasterSecret + dtlsConfig.ClientCAs = t.api.settingEngine.dtls.clientCAs + dtlsConfig.RootCAs = t.api.settingEngine.dtls.rootCAs + // Connect as DTLS Client/Server, function is blocking and we // must not hold the DTLSTransport lock if role == DTLSRoleClient { @@ -357,6 +369,8 @@ func (t *DTLSTransport) Start(remoteParameters DTLSParameters) error { switch srtpProfile { case dtls.SRTP_AEAD_AES_128_GCM: t.srtpProtectionProfile = srtp.ProtectionProfileAeadAes128Gcm + case dtls.SRTP_AEAD_AES_256_GCM: + t.srtpProtectionProfile = srtp.ProtectionProfileAeadAes256Gcm case dtls.SRTP_AES128_CM_HMAC_SHA1_80: t.srtpProtectionProfile = srtp.ProtectionProfileAes128CmHmacSha1_80 default: @@ -407,12 +421,12 @@ func (t *DTLSTransport) Stop() error { // Try closing everything and collect the errors var closeErrs []error - if srtpSessionValue := t.srtpSession.Load(); srtpSessionValue != nil { - closeErrs = append(closeErrs, srtpSessionValue.(*srtp.SessionSRTP).Close()) + if srtpSession, err := t.getSRTPSession(); err == nil && srtpSession != nil { + closeErrs = append(closeErrs, srtpSession.Close()) } - if srtcpSessionValue := t.srtcpSession.Load(); srtcpSessionValue != nil { - closeErrs = append(closeErrs, srtcpSessionValue.(*srtp.SessionSRTCP).Close()) + if srtcpSession, err := t.getSRTCPSession(); err == nil && srtcpSession != nil { + closeErrs = append(closeErrs, srtcpSession.Close()) } for i := range t.simulcastStreams { diff --git a/vendor/github.com/pion/webrtc/v3/dtlstransport_js.go b/vendor/github.com/pion/webrtc/v3/dtlstransport_js.go index d4d8611ef..bc3444e56 100644 --- a/vendor/github.com/pion/webrtc/v3/dtlstransport_js.go +++ b/vendor/github.com/pion/webrtc/v3/dtlstransport_js.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build js && wasm // +build js,wasm diff --git a/vendor/github.com/pion/webrtc/v3/dtlstransportstate.go b/vendor/github.com/pion/webrtc/v3/dtlstransportstate.go index 900b50b75..f986e22cb 100644 --- a/vendor/github.com/pion/webrtc/v3/dtlstransportstate.go +++ b/vendor/github.com/pion/webrtc/v3/dtlstransportstate.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // DTLSTransportState indicates the DTLS transport establishment state. diff --git a/vendor/github.com/pion/webrtc/v3/errors.go b/vendor/github.com/pion/webrtc/v3/errors.go index a28389f58..009e91dd6 100644 --- a/vendor/github.com/pion/webrtc/v3/errors.go +++ b/vendor/github.com/pion/webrtc/v3/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( @@ -142,6 +145,9 @@ var ( // ErrRTPSenderNewTrackHasIncorrectKind indicates that the new track is of a different kind than the previous/original ErrRTPSenderNewTrackHasIncorrectKind = errors.New("new track must be of the same kind as previous") + // ErrRTPSenderNewTrackHasIncorrectEnvelope indicates that the new track has a different envelope than the previous/original + ErrRTPSenderNewTrackHasIncorrectEnvelope = errors.New("new track must have the same envelope as previous") + // ErrUnbindFailed indicates that a TrackLocal was not able to be unbind ErrUnbindFailed = errors.New("failed to unbind TrackLocal from PeerConnection") @@ -202,10 +208,16 @@ var ( errRTPReceiverWithSSRCTrackStreamNotFound = errors.New("unable to find stream for Track with SSRC") errRTPReceiverForRIDTrackStreamNotFound = errors.New("no trackStreams found for RID") - errRTPSenderTrackNil = errors.New("Track must not be nil") - errRTPSenderDTLSTransportNil = errors.New("DTLSTransport must not be nil") - errRTPSenderSendAlreadyCalled = errors.New("Send has already been called") - errRTPSenderTrackRemoved = errors.New("Sender Track has been removed or replaced to nil") + errRTPSenderTrackNil = errors.New("Track must not be nil") + errRTPSenderDTLSTransportNil = errors.New("DTLSTransport must not be nil") + errRTPSenderSendAlreadyCalled = errors.New("Send has already been called") + errRTPSenderStopped = errors.New("Sender has already been stopped") + errRTPSenderTrackRemoved = errors.New("Sender Track has been removed or replaced to nil") + errRTPSenderRidNil = errors.New("Sender cannot add encoding as rid is empty") + errRTPSenderNoBaseEncoding = errors.New("Sender cannot add encoding as there is no base track") + errRTPSenderBaseEncodingMismatch = errors.New("Sender cannot add encoding as provided track does not match base track") + errRTPSenderRIDCollision = errors.New("Sender cannot encoding due to RID collision") + errRTPSenderNoTrackForRID = errors.New("Sender does not have track for RID") errRTPTransceiverCannotChangeMid = errors.New("errRTPSenderTrackNil") errRTPTransceiverSetSendingInvalidState = errors.New("invalid state change in RTPTransceiver.setSending") @@ -224,6 +236,9 @@ var ( errStatsICECandidateStateInvalid = errors.New("cannot convert to StatsICECandidatePairStateSucceeded invalid ice candidate state") + errInvalidICECredentialTypeString = errors.New("invalid ICECredentialType") + errInvalidICEServer = errors.New("invalid ICEServer") + errICETransportNotInNew = errors.New("ICETransport can only be called in ICETransportStateNew") errCertificatePEMFormatError = errors.New("bad Certificate PEM format") diff --git a/vendor/github.com/pion/webrtc/v3/gathering_complete_promise.go b/vendor/github.com/pion/webrtc/v3/gathering_complete_promise.go index a4d52f943..12d3170d6 100644 --- a/vendor/github.com/pion/webrtc/v3/gathering_complete_promise.go +++ b/vendor/github.com/pion/webrtc/v3/gathering_complete_promise.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/ice_go.go b/vendor/github.com/pion/webrtc/v3/ice_go.go index 992cd9cb4..9adcefbb6 100644 --- a/vendor/github.com/pion/webrtc/v3/ice_go.go +++ b/vendor/github.com/pion/webrtc/v3/ice_go.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js diff --git a/vendor/github.com/pion/webrtc/v3/icecandidate.go b/vendor/github.com/pion/webrtc/v3/icecandidate.go index 1b0fbfcdb..fa0b68093 100644 --- a/vendor/github.com/pion/webrtc/v3/icecandidate.go +++ b/vendor/github.com/pion/webrtc/v3/icecandidate.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/icecandidateinit.go b/vendor/github.com/pion/webrtc/v3/icecandidateinit.go index 31ebb4ba7..30ad93c0b 100644 --- a/vendor/github.com/pion/webrtc/v3/icecandidateinit.go +++ b/vendor/github.com/pion/webrtc/v3/icecandidateinit.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // ICECandidateInit is used to serialize ice candidates diff --git a/vendor/github.com/pion/webrtc/v3/icecandidatepair.go b/vendor/github.com/pion/webrtc/v3/icecandidatepair.go index 7350fbe59..b87884bce 100644 --- a/vendor/github.com/pion/webrtc/v3/icecandidatepair.go +++ b/vendor/github.com/pion/webrtc/v3/icecandidatepair.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import "fmt" diff --git a/vendor/github.com/pion/webrtc/v3/icecandidatetype.go b/vendor/github.com/pion/webrtc/v3/icecandidatetype.go index e57bf14af..a274c45db 100644 --- a/vendor/github.com/pion/webrtc/v3/icecandidatetype.go +++ b/vendor/github.com/pion/webrtc/v3/icecandidatetype.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/icecomponent.go b/vendor/github.com/pion/webrtc/v3/icecomponent.go index 1f03ec5b0..c65a893dc 100644 --- a/vendor/github.com/pion/webrtc/v3/icecomponent.go +++ b/vendor/github.com/pion/webrtc/v3/icecomponent.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // ICEComponent describes if the ice transport is used for RTP diff --git a/vendor/github.com/pion/webrtc/v3/iceconnectionstate.go b/vendor/github.com/pion/webrtc/v3/iceconnectionstate.go index 22fd26975..e52e1328a 100644 --- a/vendor/github.com/pion/webrtc/v3/iceconnectionstate.go +++ b/vendor/github.com/pion/webrtc/v3/iceconnectionstate.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // ICEConnectionState indicates signaling state of the ICE Connection. diff --git a/vendor/github.com/pion/webrtc/v3/icecredentialtype.go b/vendor/github.com/pion/webrtc/v3/icecredentialtype.go index 3967c16da..5d704a9bd 100644 --- a/vendor/github.com/pion/webrtc/v3/icecredentialtype.go +++ b/vendor/github.com/pion/webrtc/v3/icecredentialtype.go @@ -1,5 +1,13 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc +import ( + "encoding/json" + "fmt" +) + // ICECredentialType indicates the type of credentials used to connect to // an ICE server. type ICECredentialType int @@ -20,14 +28,14 @@ const ( iceCredentialTypeOauthStr = "oauth" ) -func newICECredentialType(raw string) ICECredentialType { +func newICECredentialType(raw string) (ICECredentialType, error) { switch raw { case iceCredentialTypePasswordStr: - return ICECredentialTypePassword + return ICECredentialTypePassword, nil case iceCredentialTypeOauthStr: - return ICECredentialTypeOauth + return ICECredentialTypeOauth, nil default: - return ICECredentialType(Unknown) + return ICECredentialTypePassword, errInvalidICECredentialTypeString } } @@ -41,3 +49,24 @@ func (t ICECredentialType) String() string { return ErrUnknownType.Error() } } + +// UnmarshalJSON parses the JSON-encoded data and stores the result +func (t *ICECredentialType) UnmarshalJSON(b []byte) error { + var val string + if err := json.Unmarshal(b, &val); err != nil { + return err + } + + tmp, err := newICECredentialType(val) + if err != nil { + return fmt.Errorf("%w: (%s)", err, val) + } + + *t = tmp + return nil +} + +// MarshalJSON returns the JSON encoding +func (t ICECredentialType) MarshalJSON() ([]byte, error) { + return json.Marshal(t.String()) +} diff --git a/vendor/github.com/pion/webrtc/v3/icegatherer.go b/vendor/github.com/pion/webrtc/v3/icegatherer.go index c3a054080..cf12b4206 100644 --- a/vendor/github.com/pion/webrtc/v3/icegatherer.go +++ b/vendor/github.com/pion/webrtc/v3/icegatherer.go @@ -1,14 +1,19 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js package webrtc import ( + "fmt" "sync" "sync/atomic" "github.com/pion/ice/v2" "github.com/pion/logging" + "github.com/pion/stun" ) // ICEGatherer gathers local host, server reflexive and relay @@ -20,7 +25,7 @@ type ICEGatherer struct { log logging.LeveledLogger state ICEGathererState - validatedServers []*ice.URL + validatedServers []*stun.URI gatherPolicy ICETransportPolicy agent *ice.Agent @@ -38,7 +43,7 @@ type ICEGatherer struct { // This constructor is part of the ORTC API. It is not // meant to be used together with the basic WebRTC API. func (api *API) NewICEGatherer(opts ICEGatherOptions) (*ICEGatherer, error) { - var validatedServers []*ice.URL + var validatedServers []*stun.URI if len(opts.ICEServers) > 0 { for _, server := range opts.ICEServers { url, err := server.urls() @@ -104,9 +109,11 @@ func (g *ICEGatherer) createAgent() error { PrflxAcceptanceMinWait: g.api.settingEngine.timeout.ICEPrflxAcceptanceMinWait, RelayAcceptanceMinWait: g.api.settingEngine.timeout.ICERelayAcceptanceMinWait, InterfaceFilter: g.api.settingEngine.candidates.InterfaceFilter, + IPFilter: g.api.settingEngine.candidates.IPFilter, NAT1To1IPs: g.api.settingEngine.candidates.NAT1To1IPs, NAT1To1IPCandidateType: nat1To1CandiTyp, - Net: g.api.settingEngine.vnet, + IncludeLoopback: g.api.settingEngine.candidates.IncludeLoopbackCandidate, + Net: g.api.settingEngine.net, MulticastDNSMode: mDNSMode, MulticastDNSHostName: g.api.settingEngine.candidates.MulticastDNSHostName, LocalUfrag: g.api.settingEngine.candidates.UsernameFragment, @@ -140,9 +147,11 @@ func (g *ICEGatherer) Gather() error { return err } - g.lock.Lock() - agent := g.agent - g.lock.Unlock() + agent := g.getAgent() + // it is possible agent had just been closed + if agent == nil { + return fmt.Errorf("%w: unable to gather", errICEAgentNotExist) + } g.setState(ICEGathererStateGathering) if err := agent.OnCandidate(func(candidate ice.Candidate) { @@ -198,7 +207,13 @@ func (g *ICEGatherer) GetLocalParameters() (ICEParameters, error) { return ICEParameters{}, err } - frag, pwd, err := g.agent.GetLocalUserCredentials() + agent := g.getAgent() + // it is possible agent had just been closed + if agent == nil { + return ICEParameters{}, fmt.Errorf("%w: unable to get local parameters", errICEAgentNotExist) + } + + frag, pwd, err := agent.GetLocalUserCredentials() if err != nil { return ICEParameters{}, err } @@ -215,7 +230,14 @@ func (g *ICEGatherer) GetLocalCandidates() ([]ICECandidate, error) { if err := g.createAgent(); err != nil { return nil, err } - iceCandidates, err := g.agent.GetLocalCandidates() + + agent := g.getAgent() + // it is possible agent had just been closed + if agent == nil { + return nil, fmt.Errorf("%w: unable to get local candidates", errICEAgentNotExist) + } + + iceCandidates, err := agent.GetLocalCandidates() if err != nil { return nil, err } @@ -224,7 +246,7 @@ func (g *ICEGatherer) GetLocalCandidates() ([]ICECandidate, error) { } // OnLocalCandidate sets an event handler which fires when a new local ICE candidate is available -// Take note that the handler is gonna be called with a nil pointer when gathering is finished. +// Take note that the handler will be called with a nil pointer when gathering is finished. func (g *ICEGatherer) OnLocalCandidate(f func(*ICECandidate)) { g.onLocalCandidateHandler.Store(f) } diff --git a/vendor/github.com/pion/webrtc/v3/icegathererstate.go b/vendor/github.com/pion/webrtc/v3/icegathererstate.go index 80dc77a2d..b90acd33e 100644 --- a/vendor/github.com/pion/webrtc/v3/icegathererstate.go +++ b/vendor/github.com/pion/webrtc/v3/icegathererstate.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/icegatheringstate.go b/vendor/github.com/pion/webrtc/v3/icegatheringstate.go index 21361f912..1925dddbf 100644 --- a/vendor/github.com/pion/webrtc/v3/icegatheringstate.go +++ b/vendor/github.com/pion/webrtc/v3/icegatheringstate.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // ICEGatheringState describes the state of the candidate gathering process. diff --git a/vendor/github.com/pion/webrtc/v3/icegatheroptions.go b/vendor/github.com/pion/webrtc/v3/icegatheroptions.go index 88421c74e..cca356fc7 100644 --- a/vendor/github.com/pion/webrtc/v3/icegatheroptions.go +++ b/vendor/github.com/pion/webrtc/v3/icegatheroptions.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // ICEGatherOptions provides options relating to the gathering of ICE candidates. diff --git a/vendor/github.com/pion/webrtc/v3/icemux.go b/vendor/github.com/pion/webrtc/v3/icemux.go index 8291a6c8b..1bae31310 100644 --- a/vendor/github.com/pion/webrtc/v3/icemux.go +++ b/vendor/github.com/pion/webrtc/v3/icemux.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/iceparameters.go b/vendor/github.com/pion/webrtc/v3/iceparameters.go index 0c03a88bf..459ec6007 100644 --- a/vendor/github.com/pion/webrtc/v3/iceparameters.go +++ b/vendor/github.com/pion/webrtc/v3/iceparameters.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // ICEParameters includes the ICE username fragment diff --git a/vendor/github.com/pion/webrtc/v3/iceprotocol.go b/vendor/github.com/pion/webrtc/v3/iceprotocol.go index f9eb0cfab..3582b6830 100644 --- a/vendor/github.com/pion/webrtc/v3/iceprotocol.go +++ b/vendor/github.com/pion/webrtc/v3/iceprotocol.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/icerole.go b/vendor/github.com/pion/webrtc/v3/icerole.go index 11187863b..99268bace 100644 --- a/vendor/github.com/pion/webrtc/v3/icerole.go +++ b/vendor/github.com/pion/webrtc/v3/icerole.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // ICERole describes the role ice.Agent is playing in selecting the diff --git a/vendor/github.com/pion/webrtc/v3/iceserver.go b/vendor/github.com/pion/webrtc/v3/iceserver.go index b83a9e8b3..35d231fd5 100644 --- a/vendor/github.com/pion/webrtc/v3/iceserver.go +++ b/vendor/github.com/pion/webrtc/v3/iceserver.go @@ -1,10 +1,15 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js package webrtc import ( - "github.com/pion/ice/v2" + "encoding/json" + + "github.com/pion/stun" "github.com/pion/webrtc/v3/pkg/rtcerr" ) @@ -17,8 +22,8 @@ type ICEServer struct { CredentialType ICECredentialType `json:"credentialType,omitempty"` } -func (s ICEServer) parseURL(i int) (*ice.URL, error) { - return ice.ParseURL(s.URLs[i]) +func (s ICEServer) parseURL(i int) (*stun.URI, error) { + return stun.ParseURI(s.URLs[i]) } func (s ICEServer) validate() error { @@ -26,8 +31,8 @@ func (s ICEServer) validate() error { return err } -func (s ICEServer) urls() ([]*ice.URL, error) { - urls := []*ice.URL{} +func (s ICEServer) urls() ([]*stun.URI, error) { + urls := []*stun.URI{} for i := range s.URLs { url, err := s.parseURL(i) @@ -35,7 +40,7 @@ func (s ICEServer) urls() ([]*ice.URL, error) { return nil, &rtcerr.InvalidAccessError{Err: err} } - if url.Scheme == ice.SchemeTypeTURN || url.Scheme == ice.SchemeTypeTURNS { + if url.Scheme == stun.SchemeTypeTURN || url.Scheme == stun.SchemeTypeTURNS { // https://www.w3.org/TR/webrtc/#set-the-configuration (step #11.3.2) if s.Username == "" || s.Credential == nil { return nil, &rtcerr.InvalidAccessError{Err: ErrNoTurnCredentials} @@ -67,3 +72,111 @@ func (s ICEServer) urls() ([]*ice.URL, error) { return urls, nil } + +func iceserverUnmarshalUrls(val interface{}) (*[]string, error) { + s, ok := val.([]interface{}) + if !ok { + return nil, errInvalidICEServer + } + out := make([]string, len(s)) + for idx, url := range s { + out[idx], ok = url.(string) + if !ok { + return nil, errInvalidICEServer + } + } + return &out, nil +} + +func iceserverUnmarshalOauth(val interface{}) (*OAuthCredential, error) { + c, ok := val.(map[string]interface{}) + if !ok { + return nil, errInvalidICEServer + } + MACKey, ok := c["MACKey"].(string) + if !ok { + return nil, errInvalidICEServer + } + AccessToken, ok := c["AccessToken"].(string) + if !ok { + return nil, errInvalidICEServer + } + return &OAuthCredential{ + MACKey: MACKey, + AccessToken: AccessToken, + }, nil +} + +func (s *ICEServer) iceserverUnmarshalFields(m map[string]interface{}) error { + if val, ok := m["urls"]; ok { + u, err := iceserverUnmarshalUrls(val) + if err != nil { + return err + } + s.URLs = *u + } else { + s.URLs = []string{} + } + + if val, ok := m["username"]; ok { + s.Username, ok = val.(string) + if !ok { + return errInvalidICEServer + } + } + if val, ok := m["credentialType"]; ok { + ct, ok := val.(string) + if !ok { + return errInvalidICEServer + } + tpe, err := newICECredentialType(ct) + if err != nil { + return err + } + s.CredentialType = tpe + } else { + s.CredentialType = ICECredentialTypePassword + } + if val, ok := m["credential"]; ok { + switch s.CredentialType { + case ICECredentialTypePassword: + s.Credential = val + case ICECredentialTypeOauth: + c, err := iceserverUnmarshalOauth(val) + if err != nil { + return err + } + s.Credential = *c + default: + return errInvalidICECredentialTypeString + } + } + return nil +} + +// UnmarshalJSON parses the JSON-encoded data and stores the result +func (s *ICEServer) UnmarshalJSON(b []byte) error { + var tmp interface{} + err := json.Unmarshal(b, &tmp) + if err != nil { + return err + } + if m, ok := tmp.(map[string]interface{}); ok { + return s.iceserverUnmarshalFields(m) + } + return errInvalidICEServer +} + +// MarshalJSON returns the JSON encoding +func (s ICEServer) MarshalJSON() ([]byte, error) { + m := make(map[string]interface{}) + m["urls"] = s.URLs + if s.Username != "" { + m["username"] = s.Username + } + if s.Credential != nil { + m["credential"] = s.Credential + } + m["credentialType"] = s.CredentialType + return json.Marshal(m) +} diff --git a/vendor/github.com/pion/webrtc/v3/iceserver_js.go b/vendor/github.com/pion/webrtc/v3/iceserver_js.go index 3f4f9c3a9..e4061fa5f 100644 --- a/vendor/github.com/pion/webrtc/v3/iceserver_js.go +++ b/vendor/github.com/pion/webrtc/v3/iceserver_js.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build js && wasm // +build js,wasm diff --git a/vendor/github.com/pion/webrtc/v3/icetransport.go b/vendor/github.com/pion/webrtc/v3/icetransport.go index 31cc4c9a5..f3b6925c8 100644 --- a/vendor/github.com/pion/webrtc/v3/icetransport.go +++ b/vendor/github.com/pion/webrtc/v3/icetransport.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js @@ -22,8 +25,9 @@ type ICETransport struct { role ICERole - onConnectionStateChangeHandler atomic.Value // func(ICETransportState) - onSelectedCandidatePairChangeHandler atomic.Value // func(*ICECandidatePair) + onConnectionStateChangeHandler atomic.Value // func(ICETransportState) + internalOnConnectionStateChangeHandler atomic.Value // func(ICETransportState) + onSelectedCandidatePairChangeHandler atomic.Value // func(*ICECandidatePair) state atomic.Value // ICETransportState @@ -44,7 +48,7 @@ type ICETransport struct { func (t *ICETransport) GetSelectedCandidatePair() (*ICECandidatePair, error) { agent := t.gatherer.getAgent() if agent == nil { - return nil, nil + return nil, nil //nolint:nilnil } icePair, err := agent.GetSelectedCandidatePair() @@ -207,9 +211,8 @@ func (t *ICETransport) OnSelectedCandidatePairChange(f func(*ICECandidatePair)) } func (t *ICETransport) onSelectedCandidatePairChange(pair *ICECandidatePair) { - handler := t.onSelectedCandidatePairChangeHandler.Load() - if handler != nil { - handler.(func(*ICECandidatePair))(pair) + if handler, ok := t.onSelectedCandidatePairChangeHandler.Load().(func(*ICECandidatePair)); ok { + handler(pair) } } @@ -220,9 +223,11 @@ func (t *ICETransport) OnConnectionStateChange(f func(ICETransportState)) { } func (t *ICETransport) onConnectionStateChange(state ICETransportState) { - handler := t.onConnectionStateChangeHandler.Load() - if handler != nil { - handler.(func(ICETransportState))(state) + if handler, ok := t.onConnectionStateChangeHandler.Load().(func(ICETransportState)); ok { + handler(state) + } + if handler, ok := t.internalOnConnectionStateChangeHandler.Load().(func(ICETransportState)); ok { + handler(state) } } @@ -292,8 +297,8 @@ func (t *ICETransport) AddRemoteCandidate(remoteCandidate *ICECandidate) error { // State returns the current ice transport state. func (t *ICETransport) State() ICETransportState { - if v := t.state.Load(); v != nil { - return v.(ICETransportState) + if v, ok := t.state.Load().(ICETransportState); ok { + return v } return ICETransportState(0) } diff --git a/vendor/github.com/pion/webrtc/v3/icetransport_js.go b/vendor/github.com/pion/webrtc/v3/icetransport_js.go index 095f354bb..3ca577b10 100644 --- a/vendor/github.com/pion/webrtc/v3/icetransport_js.go +++ b/vendor/github.com/pion/webrtc/v3/icetransport_js.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build js && wasm // +build js,wasm diff --git a/vendor/github.com/pion/webrtc/v3/icetransportpolicy.go b/vendor/github.com/pion/webrtc/v3/icetransportpolicy.go index 16273f569..a2629fdab 100644 --- a/vendor/github.com/pion/webrtc/v3/icetransportpolicy.go +++ b/vendor/github.com/pion/webrtc/v3/icetransportpolicy.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/icetransportstate.go b/vendor/github.com/pion/webrtc/v3/icetransportstate.go index da93e44d4..4edbf0f47 100644 --- a/vendor/github.com/pion/webrtc/v3/icetransportstate.go +++ b/vendor/github.com/pion/webrtc/v3/icetransportstate.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import "github.com/pion/ice/v2" diff --git a/vendor/github.com/pion/webrtc/v3/interceptor.go b/vendor/github.com/pion/webrtc/v3/interceptor.go index e93fc7666..85232f35d 100644 --- a/vendor/github.com/pion/webrtc/v3/interceptor.go +++ b/vendor/github.com/pion/webrtc/v3/interceptor.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js @@ -26,11 +29,7 @@ func RegisterDefaultInterceptors(mediaEngine *MediaEngine, interceptorRegistry * return err } - if err := ConfigureTWCCSender(mediaEngine, interceptorRegistry); err != nil { - return err - } - - return nil + return ConfigureTWCCSender(mediaEngine, interceptorRegistry) } // ConfigureRTCPReports will setup everything necessary for generating Sender and Receiver Reports diff --git a/vendor/github.com/pion/webrtc/v3/internal/fmtp/fmtp.go b/vendor/github.com/pion/webrtc/v3/internal/fmtp/fmtp.go index 86057594a..5461c019b 100644 --- a/vendor/github.com/pion/webrtc/v3/internal/fmtp/fmtp.go +++ b/vendor/github.com/pion/webrtc/v3/internal/fmtp/fmtp.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package fmtp implements per codec parsing of fmtp lines package fmtp @@ -67,7 +70,7 @@ func (g *genericFMTP) Match(b FMTP) bool { return false } - if g.mimeType != c.MimeType() { + if !strings.EqualFold(g.mimeType, c.MimeType()) { return false } diff --git a/vendor/github.com/pion/webrtc/v3/internal/fmtp/h264.go b/vendor/github.com/pion/webrtc/v3/internal/fmtp/h264.go index 5a79b9e64..b89b97aa2 100644 --- a/vendor/github.com/pion/webrtc/v3/internal/fmtp/h264.go +++ b/vendor/github.com/pion/webrtc/v3/internal/fmtp/h264.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package fmtp import ( @@ -26,16 +29,17 @@ func (h *h264FMTP) MimeType() string { // Match returns true if h and b are compatible fmtp descriptions // Based on RFC6184 Section 8.2.2: -// The parameters identifying a media format configuration for H.264 -// are profile-level-id and packetization-mode. These media format -// configuration parameters (except for the level part of profile- -// level-id) MUST be used symmetrically; that is, the answerer MUST -// either maintain all configuration parameters or remove the media -// format (payload type) completely if one or more of the parameter -// values are not supported. -// Informative note: The requirement for symmetric use does not -// apply for the level part of profile-level-id and does not apply -// for the other stream properties and capability parameters. +// +// The parameters identifying a media format configuration for H.264 +// are profile-level-id and packetization-mode. These media format +// configuration parameters (except for the level part of profile- +// level-id) MUST be used symmetrically; that is, the answerer MUST +// either maintain all configuration parameters or remove the media +// format (payload type) completely if one or more of the parameter +// values are not supported. +// Informative note: The requirement for symmetric use does not +// apply for the level part of profile-level-id and does not apply +// for the other stream properties and capability parameters. func (h *h264FMTP) Match(b FMTP) bool { c, ok := b.(*h264FMTP) if !ok { diff --git a/vendor/github.com/pion/webrtc/v3/internal/mux/endpoint.go b/vendor/github.com/pion/webrtc/v3/internal/mux/endpoint.go index afccc0778..3f53d16c5 100644 --- a/vendor/github.com/pion/webrtc/v3/internal/mux/endpoint.go +++ b/vendor/github.com/pion/webrtc/v3/internal/mux/endpoint.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package mux import ( @@ -7,7 +10,7 @@ import ( "time" "github.com/pion/ice/v2" - "github.com/pion/transport/packetio" + "github.com/pion/transport/v2/packetio" ) // Endpoint implements net.Conn. It is used to read muxed packets. @@ -60,16 +63,16 @@ func (e *Endpoint) RemoteAddr() net.Addr { } // SetDeadline is a stub -func (e *Endpoint) SetDeadline(t time.Time) error { +func (e *Endpoint) SetDeadline(time.Time) error { return nil } // SetReadDeadline is a stub -func (e *Endpoint) SetReadDeadline(t time.Time) error { +func (e *Endpoint) SetReadDeadline(time.Time) error { return nil } // SetWriteDeadline is a stub -func (e *Endpoint) SetWriteDeadline(t time.Time) error { +func (e *Endpoint) SetWriteDeadline(time.Time) error { return nil } diff --git a/vendor/github.com/pion/webrtc/v3/internal/mux/mux.go b/vendor/github.com/pion/webrtc/v3/internal/mux/mux.go index 00bf6ac24..1e167b897 100644 --- a/vendor/github.com/pion/webrtc/v3/internal/mux/mux.go +++ b/vendor/github.com/pion/webrtc/v3/internal/mux/mux.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package mux multiplexes packets on a single socket (RFC7983) package mux @@ -9,7 +12,7 @@ import ( "github.com/pion/ice/v2" "github.com/pion/logging" - "github.com/pion/transport/packetio" + "github.com/pion/transport/v2/packetio" ) // The maximum amount of data that can be buffered before returning errors. @@ -57,8 +60,6 @@ func (m *Mux) NewEndpoint(f MatchFunc) *Endpoint { } // Set a maximum size of the buffer in bytes. - // NOTE: We actually won't get anywhere close to this limit. - // SRTP will constantly read from the endpoint and drop packets if it's full. e.buffer.SetLimitSize(maxBufferSize) m.lock.Lock() @@ -79,8 +80,8 @@ func (m *Mux) RemoveEndpoint(e *Endpoint) { func (m *Mux) Close() error { m.lock.Lock() for e := range m.endpoints { - err := e.close() - if err != nil { + if err := e.close(); err != nil { + m.lock.Unlock() return err } @@ -111,15 +112,15 @@ func (m *Mux) readLoop() { case errors.Is(err, io.EOF), errors.Is(err, ice.ErrClosed): return case errors.Is(err, io.ErrShortBuffer), errors.Is(err, packetio.ErrTimeout): - m.log.Errorf("mux: failed to read from packetio.Buffer %s\n", err.Error()) + m.log.Errorf("mux: failed to read from packetio.Buffer %s", err.Error()) continue case err != nil: - m.log.Errorf("mux: ending readLoop packetio.Buffer error %s\n", err.Error()) + m.log.Errorf("mux: ending readLoop packetio.Buffer error %s", err.Error()) return } if err = m.dispatch(buf[:n]); err != nil { - m.log.Errorf("mux: ending readLoop dispatch error %s\n", err.Error()) + m.log.Errorf("mux: ending readLoop dispatch error %s", err.Error()) return } } @@ -139,7 +140,7 @@ func (m *Mux) dispatch(buf []byte) error { if endpoint == nil { if len(buf) > 0 { - m.log.Warnf("Warning: mux: no endpoint for packet starting with %d\n", buf[0]) + m.log.Warnf("Warning: mux: no endpoint for packet starting with %d", buf[0]) } else { m.log.Warnf("Warning: mux: no endpoint for zero length packet") } @@ -147,9 +148,12 @@ func (m *Mux) dispatch(buf []byte) error { } _, err := endpoint.buffer.Write(buf) - if err != nil { - return err + + // Expected when bytes are received faster than the endpoint can process them (#2152, #2180) + if errors.Is(err, packetio.ErrFull) { + m.log.Infof("mux: endpoint buffer is full, dropping packet") + return nil } - return nil + return err } diff --git a/vendor/github.com/pion/webrtc/v3/internal/mux/muxfunc.go b/vendor/github.com/pion/webrtc/v3/internal/mux/muxfunc.go index fc8efc948..69c3d14cd 100644 --- a/vendor/github.com/pion/webrtc/v3/internal/mux/muxfunc.go +++ b/vendor/github.com/pion/webrtc/v3/internal/mux/muxfunc.go @@ -1,10 +1,13 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package mux // MatchFunc allows custom logic for mapping packets to an Endpoint type MatchFunc func([]byte) bool // MatchAll always returns true -func MatchAll(b []byte) bool { +func MatchAll([]byte) bool { return true } diff --git a/vendor/github.com/pion/webrtc/v3/internal/util/util.go b/vendor/github.com/pion/webrtc/v3/internal/util/util.go index 6e12b6410..3f43c123e 100644 --- a/vendor/github.com/pion/webrtc/v3/internal/util/util.go +++ b/vendor/github.com/pion/webrtc/v3/internal/util/util.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package util provides auxiliary functions internally used in webrtc package package util @@ -39,7 +42,7 @@ func FlattenErrs(errs []error) error { return multiError(errs2) } -type multiError []error +type multiError []error //nolint:errname func (me multiError) Error() string { var errstrings []string @@ -62,7 +65,7 @@ func (me multiError) Is(err error) bool { if errors.Is(e, err) { return true } - if me2, ok := e.(multiError); ok { + if me2, ok := e.(multiError); ok { //nolint:errorlint if me2.Is(err) { return true } diff --git a/vendor/github.com/pion/webrtc/v3/js_utils.go b/vendor/github.com/pion/webrtc/v3/js_utils.go index 7e40da9a6..7e7b5e190 100644 --- a/vendor/github.com/pion/webrtc/v3/js_utils.go +++ b/vendor/github.com/pion/webrtc/v3/js_utils.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build js && wasm // +build js,wasm diff --git a/vendor/github.com/pion/webrtc/v3/mediaengine.go b/vendor/github.com/pion/webrtc/v3/mediaengine.go index bdab6b9fb..c824d1ed4 100644 --- a/vendor/github.com/pion/webrtc/v3/mediaengine.go +++ b/vendor/github.com/pion/webrtc/v3/mediaengine.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js @@ -55,8 +58,7 @@ type mediaEngineHeaderExtension struct { } // A MediaEngine defines the codecs supported by a PeerConnection, and the -// configuration of those codecs. A MediaEngine must not be shared between -// PeerConnections. +// configuration of those codecs. type MediaEngine struct { // If we have attempted to negotiate a codec type yet. negotiatedVideo, negotiatedAudio bool @@ -394,7 +396,7 @@ func (m *MediaEngine) matchRemoteCodec(remoteCodec RTPCodecParameters, typ RTPCo remoteFmtp := fmtp.Parse(remoteCodec.RTPCodecCapability.MimeType, remoteCodec.RTPCodecCapability.SDPFmtpLine) if apt, hasApt := remoteFmtp.Parameter("apt"); hasApt { - payloadType, err := strconv.Atoi(apt) + payloadType, err := strconv.ParseUint(apt, 10, 8) if err != nil { return codecMatchNone, err } @@ -553,7 +555,7 @@ func (m *MediaEngine) getCodecsByKind(typ RTPCodecType) []RTPCodecParameters { return nil } -func (m *MediaEngine) getRTPParametersByKind(typ RTPCodecType, directions []RTPTransceiverDirection) RTPParameters { +func (m *MediaEngine) getRTPParametersByKind(typ RTPCodecType, directions []RTPTransceiverDirection) RTPParameters { //nolint:gocognit headerExtensions := make([]RTPHeaderExtensionParameter, 0) // perform before locking to prevent recursive RLocks @@ -569,9 +571,33 @@ func (m *MediaEngine) getRTPParametersByKind(typ RTPCodecType, directions []RTPT } } } else { - for id, e := range m.headerExtensions { + mediaHeaderExtensions := make(map[int]mediaEngineHeaderExtension) + for _, e := range m.headerExtensions { + usingNegotiatedID := false + for id := range m.negotiatedHeaderExtensions { + if m.negotiatedHeaderExtensions[id].uri == e.uri { + usingNegotiatedID = true + mediaHeaderExtensions[id] = e + break + } + } + if !usingNegotiatedID { + for id := 1; id < 15; id++ { + idAvailable := true + if _, ok := mediaHeaderExtensions[id]; ok { + idAvailable = false + } + if _, taken := m.negotiatedHeaderExtensions[id]; idAvailable && !taken { + mediaHeaderExtensions[id] = e + break + } + } + } + } + + for id, e := range mediaHeaderExtensions { if haveRTPTransceiverDirectionIntersection(e.allowedDirections, directions) && (e.isAudio && typ == RTPCodecTypeAudio || e.isVideo && typ == RTPCodecTypeVideo) { - headerExtensions = append(headerExtensions, RTPHeaderExtensionParameter{ID: id + 1, URI: e.uri}) + headerExtensions = append(headerExtensions, RTPHeaderExtensionParameter{ID: id, URI: e.uri}) } } } @@ -615,6 +641,8 @@ func payloaderForCodec(codec RTPCodecCapability) (rtp.Payloader, error) { }, nil case strings.ToLower(MimeTypeVP9): return &codecs.VP9Payloader{}, nil + case strings.ToLower(MimeTypeAV1): + return &codecs.AV1Payloader{}, nil case strings.ToLower(MimeTypeG722): return &codecs.G722Payloader{}, nil case strings.ToLower(MimeTypePCMU), strings.ToLower(MimeTypePCMA): diff --git a/vendor/github.com/pion/webrtc/v3/networktype.go b/vendor/github.com/pion/webrtc/v3/networktype.go index e5dd84073..601e73ff8 100644 --- a/vendor/github.com/pion/webrtc/v3/networktype.go +++ b/vendor/github.com/pion/webrtc/v3/networktype.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/oauthcredential.go b/vendor/github.com/pion/webrtc/v3/oauthcredential.go index 46170c7a2..16210f9da 100644 --- a/vendor/github.com/pion/webrtc/v3/oauthcredential.go +++ b/vendor/github.com/pion/webrtc/v3/oauthcredential.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // OAuthCredential represents OAuth credential information which is used by diff --git a/vendor/github.com/pion/webrtc/v3/offeransweroptions.go b/vendor/github.com/pion/webrtc/v3/offeransweroptions.go index 2a34aed43..528688975 100644 --- a/vendor/github.com/pion/webrtc/v3/offeransweroptions.go +++ b/vendor/github.com/pion/webrtc/v3/offeransweroptions.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // OfferAnswerOptions is a base structure which describes the options that diff --git a/vendor/github.com/pion/webrtc/v3/operations.go b/vendor/github.com/pion/webrtc/v3/operations.go index 1eb85345c..d9dca4a8e 100644 --- a/vendor/github.com/pion/webrtc/v3/operations.go +++ b/vendor/github.com/pion/webrtc/v3/operations.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( @@ -66,7 +69,10 @@ func (o *operations) pop() func() { e := o.ops.Front() o.ops.Remove(e) - return e.Value.(operation) + if op, ok := e.Value.(operation); ok { + return op + } + return nil } func (o *operations) start() { diff --git a/vendor/github.com/pion/webrtc/v3/peerconnection.go b/vendor/github.com/pion/webrtc/v3/peerconnection.go index 1794e8d41..b3c0ef427 100644 --- a/vendor/github.com/pion/webrtc/v3/peerconnection.go +++ b/vendor/github.com/pion/webrtc/v3/peerconnection.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js @@ -21,6 +24,7 @@ import ( "github.com/pion/logging" "github.com/pion/rtcp" "github.com/pion/sdp/v3" + "github.com/pion/srtp/v2" "github.com/pion/webrtc/v3/internal/util" "github.com/pion/webrtc/v3/pkg/rtcerr" ) @@ -391,7 +395,11 @@ func (pc *PeerConnection) checkNegotiationNeeded() bool { //nolint:gocognit // Step 5.3.1 if t.Direction() == RTPTransceiverDirectionSendrecv || t.Direction() == RTPTransceiverDirectionSendonly { descMsid, okMsid := m.Attribute(sdp.AttrKeyMsid) - track := t.Sender().Track() + sender := t.Sender() + if sender == nil { + return true + } + track := sender.Track() if !okMsid || descMsid != track.StreamID()+" "+track.ID() { return true } @@ -428,7 +436,9 @@ func (pc *PeerConnection) checkNegotiationNeeded() bool { //nolint:gocognit // OnICECandidate sets an event handler which is invoked when a new ICE // candidate is found. -// Take note that the handler is gonna be called with a nil pointer when +// ICE candidate gathering only begins when SetLocalDescription or +// SetRemoteDescription is called. +// Take note that the handler will be called with a nil pointer when // gathering is finished. func (pc *PeerConnection) OnICECandidate(f func(*ICECandidate)) { pc.iceGatherer.OnLocalCandidate(f) @@ -633,8 +643,8 @@ func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription // in-parallel steps to create an offer // https://w3c.github.io/webrtc-pc/#dfn-in-parallel-steps-to-create-an-offer isPlanB := pc.configuration.SDPSemantics == SDPSemanticsPlanB - if pc.currentRemoteDescription != nil { - isPlanB = descriptionIsPlanB(pc.currentRemoteDescription) + if pc.currentRemoteDescription != nil && isPlanB { + isPlanB = descriptionPossiblyPlanB(pc.currentRemoteDescription) } // include unmatched local transceivers @@ -657,7 +667,13 @@ func (pc *PeerConnection) CreateOffer(options *OfferOptions) (SessionDescription } } for _, t := range currentTransceivers { - if t.Mid() != "" { + if mid := t.Mid(); mid != "" { + numericMid, errMid := strconv.Atoi(mid) + if errMid == nil { + if numericMid > pc.greaterMid { + pc.greaterMid = numericMid + } + } continue } pc.greaterMid++ @@ -735,15 +751,23 @@ func (pc *PeerConnection) updateConnectionState(iceConnectionState ICEConnection case iceConnectionState == ICEConnectionStateDisconnected: connectionState = PeerConnectionStateDisconnected - // All RTCIceTransports and RTCDtlsTransports are in the "connected", "completed" or "closed" - // state and at least one of them is in the "connected" or "completed" state. - case iceConnectionState == ICEConnectionStateConnected && dtlsTransportState == DTLSTransportStateConnected: - connectionState = PeerConnectionStateConnected + // None of the previous states apply and all RTCIceTransports are in the "new" or "closed" state, + // and all RTCDtlsTransports are in the "new" or "closed" state, or there are no transports. + case (iceConnectionState == ICEConnectionStateNew || iceConnectionState == ICEConnectionStateClosed) && + (dtlsTransportState == DTLSTransportStateNew || dtlsTransportState == DTLSTransportStateClosed): + connectionState = PeerConnectionStateNew - // Any of the RTCIceTransports or RTCDtlsTransports are in the "connecting" or - // "checking" state and none of them is in the "failed" state. - case iceConnectionState == ICEConnectionStateChecking && dtlsTransportState == DTLSTransportStateConnecting: + // None of the previous states apply and any RTCIceTransport is in the "new" or "checking" state or + // any RTCDtlsTransport is in the "new" or "connecting" state. + case (iceConnectionState == ICEConnectionStateNew || iceConnectionState == ICEConnectionStateChecking) || + (dtlsTransportState == DTLSTransportStateNew || dtlsTransportState == DTLSTransportStateConnecting): connectionState = PeerConnectionStateConnecting + + // All RTCIceTransports and RTCDtlsTransports are in the "connected", "completed" or "closed" + // state and all RTCDtlsTransports are in the "connected" or "closed" state. + case (iceConnectionState == ICEConnectionStateConnected || iceConnectionState == ICEConnectionStateCompleted || iceConnectionState == ICEConnectionStateClosed) && + (dtlsTransportState == DTLSTransportStateConnected || dtlsTransportState == DTLSTransportStateClosed): + connectionState = PeerConnectionStateConnected } if pc.connectionState.Load() == connectionState { @@ -755,7 +779,7 @@ func (pc *PeerConnection) updateConnectionState(iceConnectionState ICEConnection func (pc *PeerConnection) createICETransport() *ICETransport { t := pc.api.NewICETransport(pc.iceGatherer) - t.OnConnectionStateChange(func(state ICETransportState) { + t.internalOnConnectionStateChangeHandler.Store(func(state ICETransportState) { var cs ICEConnectionState switch state { case ICETransportStateNew: @@ -784,7 +808,7 @@ func (pc *PeerConnection) createICETransport() *ICETransport { } // CreateAnswer starts the PeerConnection and generates the localDescription -func (pc *PeerConnection) CreateAnswer(options *AnswerOptions) (SessionDescription, error) { +func (pc *PeerConnection) CreateAnswer(*AnswerOptions) (SessionDescription, error) { useIdentity := pc.idpLoginURL != nil remoteDesc := pc.RemoteDescription() switch { @@ -982,6 +1006,7 @@ func (pc *PeerConnection) SetLocalDescription(desc SessionDescription) error { weAnswer := desc.Type == SDPTypeAnswer remoteDesc := pc.RemoteDescription() if weAnswer && remoteDesc != nil { + _ = setRTPTransceiverCurrentDirection(&desc, currentTransceivers, false) if err := pc.startRTPSenders(currentTransceivers); err != nil { return err } @@ -1009,8 +1034,7 @@ func (pc *PeerConnection) LocalDescription() *SessionDescription { } // SetRemoteDescription sets the SessionDescription of the remote peer -// nolint: gocyclo -func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { //nolint:gocognit +func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { //nolint:gocognit,gocyclo if pc.isClosed.get() { return &rtcerr.InvalidStateError{Err: ErrConnectionClosed} } @@ -1030,7 +1054,11 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { var t *RTPTransceiver localTransceivers := append([]*RTPTransceiver{}, pc.GetTransceivers()...) - detectedPlanB := descriptionIsPlanB(pc.RemoteDescription()) + detectedPlanB := descriptionIsPlanB(pc.RemoteDescription(), pc.log) + if pc.configuration.SDPSemantics != SDPSemanticsUnifiedPlan { + detectedPlanB = descriptionPossiblyPlanB(pc.RemoteDescription()) + } + weOffer := desc.Type == SDPTypeAnswer if !weOffer && !detectedPlanB { @@ -1137,6 +1165,7 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { if isRenegotation { if weOffer { + _ = setRTPTransceiverCurrentDirection(&desc, currentTransceivers, true) if err = pc.startRTPSenders(currentTransceivers); err != nil { return err } @@ -1166,6 +1195,7 @@ func (pc *PeerConnection) SetRemoteDescription(desc SessionDescription) error { // Start the networking in a new routine since it will block until // the connection is actually established. if weOffer { + _ = setRTPTransceiverCurrentDirection(&desc, currentTransceivers, true) if err := pc.startRTPSenders(currentTransceivers); err != nil { return err } @@ -1224,6 +1254,53 @@ func (pc *PeerConnection) startReceiver(incoming trackDetails, receiver *RTPRece } } +func setRTPTransceiverCurrentDirection(answer *SessionDescription, currentTransceivers []*RTPTransceiver, weOffer bool) error { + currentTransceivers = append([]*RTPTransceiver{}, currentTransceivers...) + for _, media := range answer.parsed.MediaDescriptions { + midValue := getMidValue(media) + if midValue == "" { + return errPeerConnRemoteDescriptionWithoutMidValue + } + + if media.MediaName.Media == mediaSectionApplication { + continue + } + + var t *RTPTransceiver + t, currentTransceivers = findByMid(midValue, currentTransceivers) + + if t == nil { + return fmt.Errorf("%w: %q", errPeerConnTranscieverMidNil, midValue) + } + + direction := getPeerDirection(media) + if direction == RTPTransceiverDirection(Unknown) { + continue + } + + // reverse direction if it was a remote answer + if weOffer { + switch direction { + case RTPTransceiverDirectionSendonly: + direction = RTPTransceiverDirectionRecvonly + case RTPTransceiverDirectionRecvonly: + direction = RTPTransceiverDirectionSendonly + default: + } + } + + // If a transceiver is created by applying a remote description that has recvonly transceiver, + // it will have no sender. In this case, the transceiver's current direction is set to inactive so + // that the transceiver can be reused by next AddTrack. + if direction == RTPTransceiverDirectionSendonly && t.Sender() == nil { + direction = RTPTransceiverDirectionInactive + } + + t.setCurrentDirection(direction) + } + return nil +} + func runIfNewReceiver( incomingTrack trackDetails, transceivers []*RTPTransceiver, @@ -1265,6 +1342,7 @@ func (pc *PeerConnection) configureRTPReceivers(isRenegotiation bool, remoteDesc continue } + mid := t.Mid() receiverNeedsStopped := false func() { for _, t := range tracks { @@ -1272,7 +1350,7 @@ func (pc *PeerConnection) configureRTPReceivers(isRenegotiation bool, remoteDesc defer t.mu.Unlock() if t.rid != "" { - if details := trackDetailsForRID(incomingTracks, t.rid); details != nil { + if details := trackDetailsForRID(incomingTracks, mid, t.rid); details != nil { t.id = details.id t.streamID = details.streamID continue @@ -1353,7 +1431,7 @@ func (pc *PeerConnection) startRTPReceivers(remoteDesc *SessionDescription, curr case SDPSemanticsPlanB: remoteIsPlanB = true case SDPSemanticsUnifiedPlanWithFallback: - remoteIsPlanB = descriptionIsPlanB(pc.RemoteDescription()) + remoteIsPlanB = descriptionPossiblyPlanB(pc.RemoteDescription()) default: // none } @@ -1410,6 +1488,8 @@ func (pc *PeerConnection) handleUndeclaredSSRC(ssrc SSRC, remoteDescription *Ses onlyMediaSection := remoteDescription.parsed.MediaDescriptions[0] streamID := "" id := "" + hasRidAttribute := false + hasSSRCAttribute := false for _, a := range onlyMediaSection.Attributes { switch a.Key { @@ -1419,12 +1499,18 @@ func (pc *PeerConnection) handleUndeclaredSSRC(ssrc SSRC, remoteDescription *Ses id = split[1] } case sdp.AttrKeySSRC: - return false, errPeerConnSingleMediaSectionHasExplicitSSRC + hasSSRCAttribute = true case sdpAttributeRid: - return false, nil + hasRidAttribute = true } } + if hasRidAttribute { + return false, nil + } else if hasSSRCAttribute { + return false, errPeerConnSingleMediaSectionHasExplicitSSRC + } + incoming := trackDetails{ ssrcs: []SSRC{ssrc}, kind: RTPCodecTypeVideo, @@ -1439,6 +1525,7 @@ func (pc *PeerConnection) handleUndeclaredSSRC(ssrc SSRC, remoteDescription *Ses Direction: RTPTransceiverDirectionSendrecv, }) if err != nil { + // nolint return false, fmt.Errorf("%w: %d: %s", errPeerConnRemoteSSRCAddTransceiver, ssrc, err) } @@ -1547,61 +1634,71 @@ func (pc *PeerConnection) handleIncomingSSRC(rtpStream io.Reader, ssrc SSRC) err // undeclaredMediaProcessor handles RTP/RTCP packets that don't match any a:ssrc lines func (pc *PeerConnection) undeclaredMediaProcessor() { - go func() { - var simulcastRoutineCount uint64 - for { - srtpSession, err := pc.dtlsTransport.getSRTPSession() - if err != nil { - pc.log.Warnf("undeclaredMediaProcessor failed to open SrtpSession: %v", err) - return - } + go pc.undeclaredRTPMediaProcessor() + go pc.undeclaredRTCPMediaProcessor() +} - stream, ssrc, err := srtpSession.AcceptStream() - if err != nil { - pc.log.Warnf("Failed to accept RTP %v", err) - return - } +func (pc *PeerConnection) undeclaredRTPMediaProcessor() { + var simulcastRoutineCount uint64 + for { + srtpSession, err := pc.dtlsTransport.getSRTPSession() + if err != nil { + pc.log.Warnf("undeclaredMediaProcessor failed to open SrtpSession: %v", err) + return + } - if pc.isClosed.get() { - if err = stream.Close(); err != nil { - pc.log.Warnf("Failed to close RTP stream %v", err) - } - continue - } + stream, ssrc, err := srtpSession.AcceptStream() + if err != nil { + pc.log.Warnf("Failed to accept RTP %v", err) + return + } - if atomic.AddUint64(&simulcastRoutineCount, 1) >= simulcastMaxProbeRoutines { - atomic.AddUint64(&simulcastRoutineCount, ^uint64(0)) - pc.log.Warn(ErrSimulcastProbeOverflow.Error()) - continue + if pc.isClosed.get() { + if err = stream.Close(); err != nil { + pc.log.Warnf("Failed to close RTP stream %v", err) } + continue + } - go func(rtpStream io.Reader, ssrc SSRC) { + if atomic.AddUint64(&simulcastRoutineCount, 1) >= simulcastMaxProbeRoutines { + atomic.AddUint64(&simulcastRoutineCount, ^uint64(0)) + pc.log.Warn(ErrSimulcastProbeOverflow.Error()) + pc.dtlsTransport.storeSimulcastStream(stream) + continue + } + + go func(rtpStream io.Reader, ssrc SSRC) { + if err := pc.handleIncomingSSRC(rtpStream, ssrc); err != nil { + pc.log.Errorf(incomingUnhandledRTPSsrc, ssrc, err) pc.dtlsTransport.storeSimulcastStream(stream) + } + atomic.AddUint64(&simulcastRoutineCount, ^uint64(0)) + }(stream, SSRC(ssrc)) + } +} - if err := pc.handleIncomingSSRC(rtpStream, ssrc); err != nil { - pc.log.Errorf(incomingUnhandledRTPSsrc, ssrc, err) - } - atomic.AddUint64(&simulcastRoutineCount, ^uint64(0)) - }(stream, SSRC(ssrc)) +func (pc *PeerConnection) undeclaredRTCPMediaProcessor() { + var unhandledStreams []*srtp.ReadStreamSRTCP + defer func() { + for _, s := range unhandledStreams { + _ = s.Close() } }() - - go func() { - for { - srtcpSession, err := pc.dtlsTransport.getSRTCPSession() - if err != nil { - pc.log.Warnf("undeclaredMediaProcessor failed to open SrtcpSession: %v", err) - return - } - - _, ssrc, err := srtcpSession.AcceptStream() - if err != nil { - pc.log.Warnf("Failed to accept RTCP %v", err) - return - } - pc.log.Warnf("Incoming unhandled RTCP ssrc(%d), OnTrack will not be fired", ssrc) + for { + srtcpSession, err := pc.dtlsTransport.getSRTCPSession() + if err != nil { + pc.log.Warnf("undeclaredMediaProcessor failed to open SrtcpSession: %v", err) + return } - }() + + stream, ssrc, err := srtcpSession.AcceptStream() + if err != nil { + pc.log.Warnf("Failed to accept RTCP %v", err) + return + } + pc.log.Warnf("Incoming unhandled RTCP ssrc(%d), OnTrack will not be fired", ssrc) + unhandledStreams = append(unhandledStreams, stream) + } } // RemoteDescription returns pendingRemoteDescription if it is not null and @@ -1651,7 +1748,10 @@ func (pc *PeerConnection) AddICECandidate(candidate ICECandidateInit) error { // ICEConnectionState returns the ICE connection state of the // PeerConnection instance. func (pc *PeerConnection) ICEConnectionState() ICEConnectionState { - return pc.iceConnectionState.Load().(ICEConnectionState) + if state, ok := pc.iceConnectionState.Load().(ICEConnectionState); ok { + return state + } + return ICEConnectionState(0) } // GetSenders returns the RTPSender that are currently attached to this PeerConnection @@ -1697,7 +1797,13 @@ func (pc *PeerConnection) AddTrack(track TrackLocal) (*RTPSender, error) { pc.mu.Lock() defer pc.mu.Unlock() for _, t := range pc.rtpTransceivers { - if !t.stopped && t.kind == track.Kind() && t.Sender() == nil { + currentDirection := t.getCurrentDirection() + // According to https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-addtrack, if the + // transceiver can be reused only if it's currentDirection never be sendrecv or sendonly. + // But that will cause sdp inflate. So we only check currentDirection's current value, + // that's worked for all browsers. + if !t.stopped && t.kind == track.Kind() && t.Sender() == nil && + !(currentDirection == RTPTransceiverDirectionSendrecv || currentDirection == RTPTransceiverDirectionSendonly) { sender, err := pc.api.NewRTPSender(track, pc.dtlsTransport) if err == nil { err = t.SetSender(sender, track) @@ -1887,7 +1993,7 @@ func (pc *PeerConnection) CreateDataChannel(label string, options *DataChannelIn } } - d, err := pc.api.newDataChannel(params, pc.log) + d, err := pc.api.newDataChannel(params, nil, pc.log) if err != nil { return nil, err } @@ -1917,7 +2023,7 @@ func (pc *PeerConnection) CreateDataChannel(label string, options *DataChannelIn } // SetIdentityProvider is used to configure an identity provider to generate identity assertions -func (pc *PeerConnection) SetIdentityProvider(provider string) error { +func (pc *PeerConnection) SetIdentityProvider(string) error { return errPeerConnSetIdentityProviderNotImplemented } @@ -2071,7 +2177,10 @@ func (pc *PeerConnection) ICEGatheringState() ICEGatheringState { // ConnectionState attribute returns the connection state of the // PeerConnection instance. func (pc *PeerConnection) ConnectionState() PeerConnectionState { - return pc.connectionState.Load().(PeerConnectionState) + if state, ok := pc.connectionState.Load().(PeerConnectionState); ok { + return state + } + return PeerConnectionState(0) } // GetStats return data providing statistics about the overall connection @@ -2244,7 +2353,7 @@ func (pc *PeerConnection) generateUnmatchedSDP(transceivers []*RTPTransceiver, u return nil, err } - return populateSDP(d, isPlanB, dtlsFingerprints, pc.api.settingEngine.sdpMediaLevelFingerprints, pc.api.settingEngine.candidates.ICELite, pc.api.mediaEngine, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), candidates, iceParams, mediaSections, pc.ICEGatheringState()) + return populateSDP(d, isPlanB, dtlsFingerprints, pc.api.settingEngine.sdpMediaLevelFingerprints, pc.api.settingEngine.candidates.ICELite, true, pc.api.mediaEngine, connectionRoleFromDtlsRole(defaultDtlsRoleOffer), candidates, iceParams, mediaSections, pc.ICEGatheringState()) } // generateMatchedSDP generates a SDP and takes the remote state into account @@ -2271,8 +2380,14 @@ func (pc *PeerConnection) generateMatchedSDP(transceivers []*RTPTransceiver, use if pc.pendingRemoteDescription != nil { remoteDescription = pc.pendingRemoteDescription } + isExtmapAllowMixed := isExtMapAllowMixedSet(remoteDescription.parsed) localTransceivers := append([]*RTPTransceiver{}, transceivers...) - detectedPlanB := descriptionIsPlanB(remoteDescription) + + detectedPlanB := descriptionIsPlanB(remoteDescription, pc.log) + if pc.configuration.SDPSemantics != SDPSemanticsUnifiedPlan { + detectedPlanB = descriptionPossiblyPlanB(remoteDescription) + } + mediaSections := []mediaSection{} alreadyHaveApplicationMediaSection := false for _, media := range remoteDescription.parsed.MediaDescriptions { @@ -2365,7 +2480,7 @@ func (pc *PeerConnection) generateMatchedSDP(transceivers []*RTPTransceiver, use return nil, err } - return populateSDP(d, detectedPlanB, dtlsFingerprints, pc.api.settingEngine.sdpMediaLevelFingerprints, pc.api.settingEngine.candidates.ICELite, pc.api.mediaEngine, connectionRole, candidates, iceParams, mediaSections, pc.ICEGatheringState()) + return populateSDP(d, detectedPlanB, dtlsFingerprints, pc.api.settingEngine.sdpMediaLevelFingerprints, pc.api.settingEngine.candidates.ICELite, isExtmapAllowMixed, pc.api.mediaEngine, connectionRole, candidates, iceParams, mediaSections, pc.ICEGatheringState()) } func (pc *PeerConnection) setGatherCompleteHandler(handler func()) { diff --git a/vendor/github.com/pion/webrtc/v3/peerconnection_js.go b/vendor/github.com/pion/webrtc/v3/peerconnection_js.go index 8e5557783..8322e1be9 100644 --- a/vendor/github.com/pion/webrtc/v3/peerconnection_js.go +++ b/vendor/github.com/pion/webrtc/v3/peerconnection_js.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build js && wasm // +build js,wasm @@ -566,14 +569,31 @@ func iceServersToValue(iceServers []ICEServer) js.Value { return js.ValueOf(maps) } +func oauthCredentialToValue(o OAuthCredential) js.Value { + out := map[string]interface{}{ + "MACKey": o.MACKey, + "AccessToken": o.AccessToken, + } + return js.ValueOf(out) +} + func iceServerToValue(server ICEServer) js.Value { - return js.ValueOf(map[string]interface{}{ - "urls": stringsToValue(server.URLs), // required - "username": stringToValueOrUndefined(server.Username), - // Note: credential and credentialType are not currently supported. - // "credential": interfaceToValueOrUndefined(server.Credential), - // "credentialType": stringEnumToValueOrUndefined(server.CredentialType.String()), - }) + out := map[string]interface{}{ + "urls": stringsToValue(server.URLs), // required + } + if server.Username != "" { + out["username"] = stringToValueOrUndefined(server.Username) + } + if server.Credential != nil { + switch t := server.Credential.(type) { + case string: + out["credential"] = stringToValueOrUndefined(t) + case OAuthCredential: + out["credential"] = oauthCredentialToValue(t) + } + } + out["credentialType"] = stringEnumToValueOrUndefined(server.CredentialType.String()) + return js.ValueOf(out) } func valueToConfiguration(configValue js.Value) Configuration { @@ -604,14 +624,36 @@ func valueToICEServers(iceServersValue js.Value) []ICEServer { return iceServers } +func valueToICECredential(iceCredentialValue js.Value) interface{} { + if iceCredentialValue.IsNull() || iceCredentialValue.IsUndefined() { + return nil + } + if iceCredentialValue.Type() == js.TypeString { + return iceCredentialValue.String() + } + if iceCredentialValue.Type() == js.TypeObject { + return OAuthCredential{ + MACKey: iceCredentialValue.Get("MACKey").String(), + AccessToken: iceCredentialValue.Get("AccessToken").String(), + } + } + return nil +} + func valueToICEServer(iceServerValue js.Value) ICEServer { - return ICEServer{ + tpe, err := newICECredentialType(valueToStringOrZero(iceServerValue.Get("credentialType"))) + if err != nil { + tpe = ICECredentialTypePassword + } + s := ICEServer{ URLs: valueToStrings(iceServerValue.Get("urls")), // required Username: valueToStringOrZero(iceServerValue.Get("username")), // Note: Credential and CredentialType are not currently supported. - // Credential: iceServerValue.Get("credential"), - // CredentialType: newICECredentialType(valueToStringOrZero(iceServerValue.Get("credentialType"))), + Credential: valueToICECredential(iceServerValue.Get("credential")), + CredentialType: tpe, } + + return s } func valueToICECandidate(val js.Value) *ICECandidate { diff --git a/vendor/github.com/pion/webrtc/v3/peerconnectionstate.go b/vendor/github.com/pion/webrtc/v3/peerconnectionstate.go index 66ac20eef..1f1456882 100644 --- a/vendor/github.com/pion/webrtc/v3/peerconnectionstate.go +++ b/vendor/github.com/pion/webrtc/v3/peerconnectionstate.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // PeerConnectionState indicates the state of the PeerConnection. diff --git a/vendor/github.com/pion/webrtc/v3/pkg/media/media.go b/vendor/github.com/pion/webrtc/v3/pkg/media/media.go index 4b00edbe7..2ed5573f8 100644 --- a/vendor/github.com/pion/webrtc/v3/pkg/media/media.go +++ b/vendor/github.com/pion/webrtc/v3/pkg/media/media.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package media provides media writer and filters package media diff --git a/vendor/github.com/pion/webrtc/v3/pkg/rtcerr/errors.go b/vendor/github.com/pion/webrtc/v3/pkg/rtcerr/errors.go index aa94b7bf9..36205c7d0 100644 --- a/vendor/github.com/pion/webrtc/v3/pkg/rtcerr/errors.go +++ b/vendor/github.com/pion/webrtc/v3/pkg/rtcerr/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package rtcerr implements the error wrappers defined throughout the // WebRTC 1.0 specifications. package rtcerr diff --git a/vendor/github.com/pion/webrtc/v3/renovate.json b/vendor/github.com/pion/webrtc/v3/renovate.json index f1614058a..f1bb98c6a 100644 --- a/vendor/github.com/pion/webrtc/v3/renovate.json +++ b/vendor/github.com/pion/webrtc/v3/renovate.json @@ -1,27 +1,6 @@ { + "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:base", - ":disableDependencyDashboard" - ], - "postUpdateOptions": [ - "gomodTidy" - ], - "commitBody": "Generated by renovateBot", - "packageRules": [ - { - "matchUpdateTypes": ["minor", "patch", "pin", "digest"], - "automerge": true - }, - { - "packagePatterns": ["^golang.org/x/"], - "schedule": ["on the first day of the month"] - } - ], - "ignorePaths": [ - ".github/workflows/generate-authors.yml", - ".github/workflows/lint.yaml", - ".github/workflows/renovate-go-mod-fix.yaml", - ".github/workflows/test.yaml", - ".github/workflows/tidy-check.yaml" + "github>pion/renovate-config" ] } diff --git a/vendor/github.com/pion/webrtc/v3/rtcpfeedback.go b/vendor/github.com/pion/webrtc/v3/rtcpfeedback.go index b377738f6..ab6f555af 100644 --- a/vendor/github.com/pion/webrtc/v3/rtcpfeedback.go +++ b/vendor/github.com/pion/webrtc/v3/rtcpfeedback.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc const ( diff --git a/vendor/github.com/pion/webrtc/v3/rtcpmuxpolicy.go b/vendor/github.com/pion/webrtc/v3/rtcpmuxpolicy.go index f74e440b3..bd68ab562 100644 --- a/vendor/github.com/pion/webrtc/v3/rtcpmuxpolicy.go +++ b/vendor/github.com/pion/webrtc/v3/rtcpmuxpolicy.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/rtpcapabilities.go b/vendor/github.com/pion/webrtc/v3/rtpcapabilities.go index dc42230d1..3ac52e450 100644 --- a/vendor/github.com/pion/webrtc/v3/rtpcapabilities.go +++ b/vendor/github.com/pion/webrtc/v3/rtpcapabilities.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // RTPCapabilities represents the capabilities of a transceiver diff --git a/vendor/github.com/pion/webrtc/v3/rtpcodec.go b/vendor/github.com/pion/webrtc/v3/rtpcodec.go index cde2b8e71..5692d5f56 100644 --- a/vendor/github.com/pion/webrtc/v3/rtpcodec.go +++ b/vendor/github.com/pion/webrtc/v3/rtpcodec.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/rtpcodingparameters.go b/vendor/github.com/pion/webrtc/v3/rtpcodingparameters.go index c5e12efaf..f03d8c35f 100644 --- a/vendor/github.com/pion/webrtc/v3/rtpcodingparameters.go +++ b/vendor/github.com/pion/webrtc/v3/rtpcodingparameters.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // RTPRtxParameters dictionary contains information relating to retransmission (RTX) settings. diff --git a/vendor/github.com/pion/webrtc/v3/rtpdecodingparameters.go b/vendor/github.com/pion/webrtc/v3/rtpdecodingparameters.go index 77aa1fc1c..0c45f896f 100644 --- a/vendor/github.com/pion/webrtc/v3/rtpdecodingparameters.go +++ b/vendor/github.com/pion/webrtc/v3/rtpdecodingparameters.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // RTPDecodingParameters provides information relating to both encoding and decoding. diff --git a/vendor/github.com/pion/webrtc/v3/rtpencodingparameters.go b/vendor/github.com/pion/webrtc/v3/rtpencodingparameters.go index 09481a570..ffd4a8d78 100644 --- a/vendor/github.com/pion/webrtc/v3/rtpencodingparameters.go +++ b/vendor/github.com/pion/webrtc/v3/rtpencodingparameters.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // RTPEncodingParameters provides information relating to both encoding and decoding. diff --git a/vendor/github.com/pion/webrtc/v3/rtpreceiveparameters.go b/vendor/github.com/pion/webrtc/v3/rtpreceiveparameters.go index badf6b733..26a667616 100644 --- a/vendor/github.com/pion/webrtc/v3/rtpreceiveparameters.go +++ b/vendor/github.com/pion/webrtc/v3/rtpreceiveparameters.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // RTPReceiveParameters contains the RTP stack settings used by receivers diff --git a/vendor/github.com/pion/webrtc/v3/rtpreceiver.go b/vendor/github.com/pion/webrtc/v3/rtpreceiver.go index a836b2ff8..dac154be6 100644 --- a/vendor/github.com/pion/webrtc/v3/rtpreceiver.go +++ b/vendor/github.com/pion/webrtc/v3/rtpreceiver.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js @@ -407,10 +410,7 @@ func (r *RTPReceiver) SetReadDeadline(t time.Time) error { r.mu.RLock() defer r.mu.RUnlock() - if err := r.tracks[0].rtcpReadStream.SetReadDeadline(t); err != nil { - return err - } - return nil + return r.tracks[0].rtcpReadStream.SetReadDeadline(t) } // SetReadDeadlineSimulcast sets the max amount of time the RTCP stream for a given rid will block before returning. 0 is forever. diff --git a/vendor/github.com/pion/webrtc/v3/rtpreceiver_go.go b/vendor/github.com/pion/webrtc/v3/rtpreceiver_go.go index 430169548..4d150f0f3 100644 --- a/vendor/github.com/pion/webrtc/v3/rtpreceiver_go.go +++ b/vendor/github.com/pion/webrtc/v3/rtpreceiver_go.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js diff --git a/vendor/github.com/pion/webrtc/v3/rtpreceiver_js.go b/vendor/github.com/pion/webrtc/v3/rtpreceiver_js.go index 866757fb8..44cef72d1 100644 --- a/vendor/github.com/pion/webrtc/v3/rtpreceiver_js.go +++ b/vendor/github.com/pion/webrtc/v3/rtpreceiver_js.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build js && wasm // +build js,wasm diff --git a/vendor/github.com/pion/webrtc/v3/rtpsender.go b/vendor/github.com/pion/webrtc/v3/rtpsender.go index 7319cf842..49ae4e82e 100644 --- a/vendor/github.com/pion/webrtc/v3/rtpsender.go +++ b/vendor/github.com/pion/webrtc/v3/rtpsender.go @@ -1,9 +1,13 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js package webrtc import ( + "fmt" "io" "sync" "time" @@ -12,23 +16,30 @@ import ( "github.com/pion/randutil" "github.com/pion/rtcp" "github.com/pion/rtp" + "github.com/pion/webrtc/v3/internal/util" ) -// RTPSender allows an application to control how a given Track is encoded and transmitted to a remote peer -type RTPSender struct { +type trackEncoding struct { track TrackLocal - srtpStream *srtpWriterFuture + srtpStream *srtpWriterFuture + rtcpInterceptor interceptor.RTCPReader streamInfo interceptor.StreamInfo context TrackLocalContext + ssrc SSRC +} + +// RTPSender allows an application to control how a given Track is encoded and transmitted to a remote peer +type RTPSender struct { + trackEncodings []*trackEncoding + transport *DTLSTransport payloadType PayloadType kind RTPCodecType - ssrc SSRC // nolint:godox // TODO(sgotti) remove this when in future we'll avoid replacing @@ -60,23 +71,15 @@ func (api *API) NewRTPSender(track TrackLocal, transport *DTLSTransport) (*RTPSe } r := &RTPSender{ - track: track, transport: transport, api: api, sendCalled: make(chan struct{}), stopCalled: make(chan struct{}), - ssrc: SSRC(randutil.NewMathRandomGenerator().Uint32()), id: id, - srtpStream: &srtpWriterFuture{}, kind: track.Kind(), } - r.srtpStream.rtpSender = r - - r.rtcpInterceptor = r.api.interceptor.BindRTCPReader(interceptor.RTPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) { - n, err = r.srtpStream.Read(in) - return n, a, err - })) + r.addEncoding(track) return r, nil } @@ -108,24 +111,26 @@ func (r *RTPSender) Transport() *DTLSTransport { } func (r *RTPSender) getParameters() RTPSendParameters { - var rid string - if r.track != nil { - rid = r.track.RID() + var encodings []RTPEncodingParameters + for _, trackEncoding := range r.trackEncodings { + var rid string + if trackEncoding.track != nil { + rid = trackEncoding.track.RID() + } + encodings = append(encodings, RTPEncodingParameters{ + RTPCodingParameters: RTPCodingParameters{ + RID: rid, + SSRC: trackEncoding.ssrc, + PayloadType: r.payloadType, + }, + }) } sendParameters := RTPSendParameters{ RTPParameters: r.api.mediaEngine.getRTPParametersByKind( r.kind, []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}, ), - Encodings: []RTPEncodingParameters{ - { - RTPCodingParameters: RTPCodingParameters{ - RID: rid, - SSRC: r.ssrc, - PayloadType: r.payloadType, - }, - }, - }, + Encodings: encodings, } if r.rtpTransceiver != nil { sendParameters.Codecs = r.rtpTransceiver.getCodecs() @@ -143,11 +148,81 @@ func (r *RTPSender) GetParameters() RTPSendParameters { return r.getParameters() } +// AddEncoding adds an encoding to RTPSender. Used by simulcast senders. +func (r *RTPSender) AddEncoding(track TrackLocal) error { + r.mu.Lock() + defer r.mu.Unlock() + + if track == nil { + return errRTPSenderTrackNil + } + + if track.RID() == "" { + return errRTPSenderRidNil + } + + if r.hasStopped() { + return errRTPSenderStopped + } + + if r.hasSent() { + return errRTPSenderSendAlreadyCalled + } + + var refTrack TrackLocal + if len(r.trackEncodings) != 0 { + refTrack = r.trackEncodings[0].track + } + if refTrack == nil || refTrack.RID() == "" { + return errRTPSenderNoBaseEncoding + } + + if refTrack.ID() != track.ID() || refTrack.StreamID() != track.StreamID() || refTrack.Kind() != track.Kind() { + return errRTPSenderBaseEncodingMismatch + } + + for _, encoding := range r.trackEncodings { + if encoding.track == nil { + continue + } + + if encoding.track.RID() == track.RID() { + return errRTPSenderRIDCollision + } + } + + r.addEncoding(track) + return nil +} + +func (r *RTPSender) addEncoding(track TrackLocal) { + ssrc := SSRC(randutil.NewMathRandomGenerator().Uint32()) + trackEncoding := &trackEncoding{ + track: track, + srtpStream: &srtpWriterFuture{ssrc: ssrc}, + ssrc: ssrc, + } + trackEncoding.srtpStream.rtpSender = r + trackEncoding.rtcpInterceptor = r.api.interceptor.BindRTCPReader( + interceptor.RTPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) { + n, err = trackEncoding.srtpStream.Read(in) + return n, a, err + }), + ) + + r.trackEncodings = append(r.trackEncodings, trackEncoding) +} + // Track returns the RTCRtpTransceiver track, or nil func (r *RTPSender) Track() TrackLocal { r.mu.RLock() defer r.mu.RUnlock() - return r.track + + if len(r.trackEncodings) == 0 { + return nil + } + + return r.trackEncodings[0].track } // ReplaceTrack replaces the track currently being used as the sender's source with a new TrackLocal. @@ -161,26 +236,38 @@ func (r *RTPSender) ReplaceTrack(track TrackLocal) error { return ErrRTPSenderNewTrackHasIncorrectKind } - if r.hasSent() && r.track != nil { - if err := r.track.Unbind(r.context); err != nil { + // cannot replace simulcast envelope + if track != nil && len(r.trackEncodings) > 1 { + return ErrRTPSenderNewTrackHasIncorrectEnvelope + } + + var replacedTrack TrackLocal + var context *TrackLocalContext + if len(r.trackEncodings) != 0 { + replacedTrack = r.trackEncodings[0].track + context = &r.trackEncodings[0].context + } + if r.hasSent() && replacedTrack != nil { + if err := replacedTrack.Unbind(*context); err != nil { return err } } if !r.hasSent() || track == nil { - r.track = track + r.trackEncodings[0].track = track return nil } codec, err := track.Bind(TrackLocalContext{ - id: r.context.id, - params: r.api.mediaEngine.getRTPParametersByKind(track.Kind(), []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}), - ssrc: r.context.ssrc, - writeStream: r.context.writeStream, + id: context.id, + params: r.api.mediaEngine.getRTPParametersByKind(track.Kind(), []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}), + ssrc: context.ssrc, + writeStream: context.writeStream, + rtcpInterceptor: context.rtcpInterceptor, }) if err != nil { // Re-bind the original track - if _, reBindErr := r.track.Bind(r.context); reBindErr != nil { + if _, reBindErr := replacedTrack.Bind(*context); reBindErr != nil { return reBindErr } @@ -189,10 +276,10 @@ func (r *RTPSender) ReplaceTrack(track TrackLocal) error { // Codec has changed if r.payloadType != codec.PayloadType { - r.context.params.Codecs = []RTPCodecParameters{codec} + context.params.Codecs = []RTPCodecParameters{codec} } - r.track = track + r.trackEncodings[0].track = track return nil } @@ -204,29 +291,42 @@ func (r *RTPSender) Send(parameters RTPSendParameters) error { switch { case r.hasSent(): return errRTPSenderSendAlreadyCalled - case r.track == nil: + case r.trackEncodings[0].track == nil: return errRTPSenderTrackRemoved } - writeStream := &interceptorToTrackLocalWriter{} - r.context = TrackLocalContext{ - id: r.id, - params: r.api.mediaEngine.getRTPParametersByKind(r.track.Kind(), []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}), - ssrc: parameters.Encodings[0].SSRC, - writeStream: writeStream, - } + for idx, trackEncoding := range r.trackEncodings { + writeStream := &interceptorToTrackLocalWriter{} + trackEncoding.context = TrackLocalContext{ + id: r.id, + params: r.api.mediaEngine.getRTPParametersByKind(trackEncoding.track.Kind(), []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}), + ssrc: parameters.Encodings[idx].SSRC, + writeStream: writeStream, + rtcpInterceptor: trackEncoding.rtcpInterceptor, + } - codec, err := r.track.Bind(r.context) - if err != nil { - return err - } - r.context.params.Codecs = []RTPCodecParameters{codec} + codec, err := trackEncoding.track.Bind(trackEncoding.context) + if err != nil { + return err + } + trackEncoding.context.params.Codecs = []RTPCodecParameters{codec} - r.streamInfo = *createStreamInfo(r.id, parameters.Encodings[0].SSRC, codec.PayloadType, codec.RTPCodecCapability, parameters.HeaderExtensions) - rtpInterceptor := r.api.interceptor.BindLocalStream(&r.streamInfo, interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) { - return r.srtpStream.WriteRTP(header, payload) - })) - writeStream.interceptor.Store(rtpInterceptor) + trackEncoding.streamInfo = *createStreamInfo( + r.id, + parameters.Encodings[idx].SSRC, + codec.PayloadType, + codec.RTPCodecCapability, + parameters.HeaderExtensions, + ) + srtpStream := trackEncoding.srtpStream + rtpInterceptor := r.api.interceptor.BindLocalStream( + &trackEncoding.streamInfo, + interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) { + return srtpStream.WriteRTP(header, payload) + }), + ) + writeStream.interceptor.Store(rtpInterceptor) + } close(r.sendCalled) return nil @@ -252,16 +352,20 @@ func (r *RTPSender) Stop() error { return err } - r.api.interceptor.UnbindLocalStream(&r.streamInfo) + errs := []error{} + for _, trackEncoding := range r.trackEncodings { + r.api.interceptor.UnbindLocalStream(&trackEncoding.streamInfo) + errs = append(errs, trackEncoding.srtpStream.Close()) + } - return r.srtpStream.Close() + return util.FlattenErrs(errs) } -// Read reads incoming RTCP for this RTPReceiver +// Read reads incoming RTCP for this RTPSender func (r *RTPSender) Read(b []byte) (n int, a interceptor.Attributes, err error) { select { case <-r.sendCalled: - return r.rtcpInterceptor.Read(b, a) + return r.trackEncodings[0].rtcpInterceptor.Read(b, a) case <-r.stopCalled: return 0, nil, io.ErrClosedPipe } @@ -283,10 +387,50 @@ func (r *RTPSender) ReadRTCP() ([]rtcp.Packet, interceptor.Attributes, error) { return pkts, attributes, nil } +// ReadSimulcast reads incoming RTCP for this RTPSender for given rid +func (r *RTPSender) ReadSimulcast(b []byte, rid string) (n int, a interceptor.Attributes, err error) { + select { + case <-r.sendCalled: + for _, t := range r.trackEncodings { + if t.track != nil && t.track.RID() == rid { + return t.rtcpInterceptor.Read(b, a) + } + } + return 0, nil, fmt.Errorf("%w: %s", errRTPSenderNoTrackForRID, rid) + case <-r.stopCalled: + return 0, nil, io.ErrClosedPipe + } +} + +// ReadSimulcastRTCP is a convenience method that wraps ReadSimulcast and unmarshal for you +func (r *RTPSender) ReadSimulcastRTCP(rid string) ([]rtcp.Packet, interceptor.Attributes, error) { + b := make([]byte, r.api.settingEngine.getReceiveMTU()) + i, attributes, err := r.ReadSimulcast(b, rid) + if err != nil { + return nil, nil, err + } + + pkts, err := rtcp.Unmarshal(b[:i]) + return pkts, attributes, err +} + // SetReadDeadline sets the deadline for the Read operation. // Setting to zero means no deadline. func (r *RTPSender) SetReadDeadline(t time.Time) error { - return r.srtpStream.SetReadDeadline(t) + return r.trackEncodings[0].srtpStream.SetReadDeadline(t) +} + +// SetReadDeadlineSimulcast sets the max amount of time the RTCP stream for a given rid will block before returning. 0 is forever. +func (r *RTPSender) SetReadDeadlineSimulcast(deadline time.Time, rid string) error { + r.mu.RLock() + defer r.mu.RUnlock() + + for _, t := range r.trackEncodings { + if t.track != nil && t.track.RID() == rid { + return t.srtpStream.SetReadDeadline(deadline) + } + } + return fmt.Errorf("%w: %s", errRTPSenderNoTrackForRID, rid) } // hasSent tells if data has been ever sent for this instance diff --git a/vendor/github.com/pion/webrtc/v3/rtpsender_js.go b/vendor/github.com/pion/webrtc/v3/rtpsender_js.go index cdb9dd595..46ea599f7 100644 --- a/vendor/github.com/pion/webrtc/v3/rtpsender_js.go +++ b/vendor/github.com/pion/webrtc/v3/rtpsender_js.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build js && wasm // +build js,wasm diff --git a/vendor/github.com/pion/webrtc/v3/rtpsendparameters.go b/vendor/github.com/pion/webrtc/v3/rtpsendparameters.go index cc55e5dcc..b955c61ce 100644 --- a/vendor/github.com/pion/webrtc/v3/rtpsendparameters.go +++ b/vendor/github.com/pion/webrtc/v3/rtpsendparameters.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // RTPSendParameters contains the RTP stack settings used by receivers diff --git a/vendor/github.com/pion/webrtc/v3/rtptransceiver.go b/vendor/github.com/pion/webrtc/v3/rtptransceiver.go index 4ff4ead7d..02ddf7924 100644 --- a/vendor/github.com/pion/webrtc/v3/rtptransceiver.go +++ b/vendor/github.com/pion/webrtc/v3/rtptransceiver.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js @@ -13,10 +16,11 @@ import ( // RTPTransceiver represents a combination of an RTPSender and an RTPReceiver that share a common mid. type RTPTransceiver struct { - mid atomic.Value // string - sender atomic.Value // *RTPSender - receiver atomic.Value // *RTPReceiver - direction atomic.Value // RTPTransceiverDirection + mid atomic.Value // string + sender atomic.Value // *RTPSender + receiver atomic.Value // *RTPReceiver + direction atomic.Value // RTPTransceiverDirection + currentDirection atomic.Value // RTPTransceiverDirection codecs []RTPCodecParameters // User provided codecs via SetCodecPreferences @@ -38,6 +42,7 @@ func newRTPTransceiver( t.setReceiver(receiver) t.setSender(sender) t.setDirection(direction) + t.setCurrentDirection(RTPTransceiverDirection(Unknown)) return t } @@ -82,8 +87,8 @@ func (t *RTPTransceiver) getCodecs() []RTPCodecParameters { // Sender returns the RTPTransceiver's RTPSender if it has one func (t *RTPTransceiver) Sender() *RTPSender { - if v := t.sender.Load(); v != nil { - return v.(*RTPSender) + if v, ok := t.sender.Load().(*RTPSender); ok { + return v } return nil @@ -109,8 +114,8 @@ func (t *RTPTransceiver) setSender(s *RTPSender) { // Receiver returns the RTPTransceiver's RTPReceiver if it has one func (t *RTPTransceiver) Receiver() *RTPReceiver { - if v := t.receiver.Load(); v != nil { - return v.(*RTPReceiver) + if v, ok := t.receiver.Load().(*RTPReceiver); ok { + return v } return nil @@ -127,8 +132,8 @@ func (t *RTPTransceiver) SetMid(mid string) error { // Mid gets the Transceiver's mid value. When not already set, this value will be set in CreateOffer or CreateAnswer. func (t *RTPTransceiver) Mid() string { - if v := t.mid.Load(); v != nil { - return v.(string) + if v, ok := t.mid.Load().(string); ok { + return v } return "" } @@ -140,7 +145,10 @@ func (t *RTPTransceiver) Kind() RTPCodecType { // Direction returns the RTPTransceiver's current direction func (t *RTPTransceiver) Direction() RTPTransceiverDirection { - return t.direction.Load().(RTPTransceiverDirection) + if direction, ok := t.direction.Load().(RTPTransceiverDirection); ok { + return direction + } + return RTPTransceiverDirection(0) } // Stop irreversibly stops the RTPTransceiver @@ -157,6 +165,7 @@ func (t *RTPTransceiver) Stop() error { } t.setDirection(RTPTransceiverDirectionInactive) + t.setCurrentDirection(RTPTransceiverDirectionInactive) return nil } @@ -176,6 +185,17 @@ func (t *RTPTransceiver) setDirection(d RTPTransceiverDirection) { t.direction.Store(d) } +func (t *RTPTransceiver) setCurrentDirection(d RTPTransceiverDirection) { + t.currentDirection.Store(d) +} + +func (t *RTPTransceiver) getCurrentDirection() RTPTransceiverDirection { + if v, ok := t.currentDirection.Load().(RTPTransceiverDirection); ok { + return v + } + return RTPTransceiverDirection(Unknown) +} + func (t *RTPTransceiver) setSendingTrack(track TrackLocal) error { if err := t.Sender().ReplaceTrack(track); err != nil { return err @@ -222,9 +242,9 @@ func satisfyTypeAndDirection(remoteKind RTPCodecType, remoteDirection RTPTransce getPreferredDirections := func() []RTPTransceiverDirection { switch remoteDirection { case RTPTransceiverDirectionSendrecv: - return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendrecv} + return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendrecv, RTPTransceiverDirectionSendonly} case RTPTransceiverDirectionSendonly: - return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly} + return []RTPTransceiverDirection{RTPTransceiverDirectionRecvonly, RTPTransceiverDirectionSendrecv} case RTPTransceiverDirectionRecvonly: return []RTPTransceiverDirection{RTPTransceiverDirectionSendonly, RTPTransceiverDirectionSendrecv} default: diff --git a/vendor/github.com/pion/webrtc/v3/rtptransceiver_js.go b/vendor/github.com/pion/webrtc/v3/rtptransceiver_js.go index 0e761315f..43e129af3 100644 --- a/vendor/github.com/pion/webrtc/v3/rtptransceiver_js.go +++ b/vendor/github.com/pion/webrtc/v3/rtptransceiver_js.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build js && wasm // +build js,wasm diff --git a/vendor/github.com/pion/webrtc/v3/rtptransceiverdirection.go b/vendor/github.com/pion/webrtc/v3/rtptransceiverdirection.go index 307301473..752cd5b9c 100644 --- a/vendor/github.com/pion/webrtc/v3/rtptransceiverdirection.go +++ b/vendor/github.com/pion/webrtc/v3/rtptransceiverdirection.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // RTPTransceiverDirection indicates the direction of the RTPTransceiver. @@ -5,19 +8,19 @@ type RTPTransceiverDirection int const ( // RTPTransceiverDirectionSendrecv indicates the RTPSender will offer - // to send RTP and RTPReceiver the will offer to receive RTP. + // to send RTP and the RTPReceiver will offer to receive RTP. RTPTransceiverDirectionSendrecv RTPTransceiverDirection = iota + 1 // RTPTransceiverDirectionSendonly indicates the RTPSender will offer // to send RTP. RTPTransceiverDirectionSendonly - // RTPTransceiverDirectionRecvonly indicates the RTPReceiver the will + // RTPTransceiverDirectionRecvonly indicates the RTPReceiver will // offer to receive RTP. RTPTransceiverDirectionRecvonly // RTPTransceiverDirectionInactive indicates the RTPSender won't offer - // to send RTP and RTPReceiver the won't offer to receive RTP. + // to send RTP and the RTPReceiver won't offer to receive RTP. RTPTransceiverDirectionInactive ) diff --git a/vendor/github.com/pion/webrtc/v3/rtptransceiverinit.go b/vendor/github.com/pion/webrtc/v3/rtptransceiverinit.go index 3c439b7f7..3aac1dc14 100644 --- a/vendor/github.com/pion/webrtc/v3/rtptransceiverinit.go +++ b/vendor/github.com/pion/webrtc/v3/rtptransceiverinit.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // RTPTransceiverInit dictionary is used when calling the WebRTC function addTransceiver() to provide configuration options for the new transceiver. diff --git a/vendor/github.com/pion/webrtc/v3/sctpcapabilities.go b/vendor/github.com/pion/webrtc/v3/sctpcapabilities.go index 34399d3b0..c4c5ff5eb 100644 --- a/vendor/github.com/pion/webrtc/v3/sctpcapabilities.go +++ b/vendor/github.com/pion/webrtc/v3/sctpcapabilities.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // SCTPCapabilities indicates the capabilities of the SCTPTransport. diff --git a/vendor/github.com/pion/webrtc/v3/sctptransport.go b/vendor/github.com/pion/webrtc/v3/sctptransport.go index 27e02ea65..c96fd173f 100644 --- a/vendor/github.com/pion/webrtc/v3/sctptransport.go +++ b/vendor/github.com/pion/webrtc/v3/sctptransport.go @@ -1,9 +1,13 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js package webrtc import ( + "errors" "io" "math" "sync" @@ -91,7 +95,7 @@ func (r *SCTPTransport) GetCapabilities() SCTPCapabilities { // Start the SCTPTransport. Since both local and remote parties must mutually // create an SCTPTransport, SCTP SO (Simultaneous Open) is used to establish // a connection over SCTP. -func (r *SCTPTransport) Start(remoteCaps SCTPCapabilities) error { +func (r *SCTPTransport) Start(SCTPCapabilities) error { if r.isStarted { return nil } @@ -103,8 +107,9 @@ func (r *SCTPTransport) Start(remoteCaps SCTPCapabilities) error { } sctpAssociation, err := sctp.Client(sctp.Config{ - NetConn: dtlsTransport.conn, - LoggerFactory: r.api.settingEngine.LoggerFactory, + NetConn: dtlsTransport.conn, + MaxReceiveBufferSize: r.api.settingEngine.sctp.maxReceiveBufferSize, + LoggerFactory: r.api.settingEngine.LoggerFactory, }) if err != nil { return err @@ -174,7 +179,7 @@ ACCEPT: LoggerFactory: r.api.settingEngine.LoggerFactory, }, dataChannels...) if err != nil { - if err != io.EOF { + if !errors.Is(err, io.EOF) { r.log.Errorf("Failed to accept data channel: %v", err) r.onError(err) } @@ -222,7 +227,7 @@ ACCEPT: Ordered: ordered, MaxPacketLifeTime: maxPacketLifeTime, MaxRetransmits: maxRetransmits, - }, r.api.settingEngine.LoggerFactory.NewLogger("ortc")) + }, r, r.api.settingEngine.LoggerFactory.NewLogger("ortc")) if err != nil { r.log.Errorf("Failed to accept data channel: %v", err) r.onError(err) diff --git a/vendor/github.com/pion/webrtc/v3/sctptransport_js.go b/vendor/github.com/pion/webrtc/v3/sctptransport_js.go index 5a4d1573e..7ab6c3900 100644 --- a/vendor/github.com/pion/webrtc/v3/sctptransport_js.go +++ b/vendor/github.com/pion/webrtc/v3/sctptransport_js.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build js && wasm // +build js,wasm diff --git a/vendor/github.com/pion/webrtc/v3/sctptransportstate.go b/vendor/github.com/pion/webrtc/v3/sctptransportstate.go index 8dc794162..5dd51d6f5 100644 --- a/vendor/github.com/pion/webrtc/v3/sctptransportstate.go +++ b/vendor/github.com/pion/webrtc/v3/sctptransportstate.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc // SCTPTransportState indicates the state of the SCTP transport. diff --git a/vendor/github.com/pion/webrtc/v3/sdp.go b/vendor/github.com/pion/webrtc/v3/sdp.go index c181bfe60..a1c8daaba 100644 --- a/vendor/github.com/pion/webrtc/v3/sdp.go +++ b/vendor/github.com/pion/webrtc/v3/sdp.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js @@ -40,8 +43,12 @@ func trackDetailsForSSRC(trackDetails []trackDetails, ssrc SSRC) *trackDetails { return nil } -func trackDetailsForRID(trackDetails []trackDetails, rid string) *trackDetails { +func trackDetailsForRID(trackDetails []trackDetails, mid, rid string) *trackDetails { for i := range trackDetails { + if trackDetails[i].mid != mid { + continue + } + for j := range trackDetails[i].rids { if trackDetails[i].rids[j] == rid { return &trackDetails[i] @@ -340,7 +347,60 @@ func populateLocalCandidates(sessionDescription *SessionDescription, i *ICEGathe } } -func addTransceiverSDP(d *sdp.SessionDescription, isPlanB, shouldAddCandidates bool, dtlsFingerprints []DTLSFingerprint, mediaEngine *MediaEngine, midValue string, iceParams ICEParameters, candidates []ICECandidate, dtlsRole sdp.ConnectionRole, iceGatheringState ICEGatheringState, mediaSection mediaSection) (bool, error) { +func addSenderSDP( + mediaSection mediaSection, + isPlanB bool, + media *sdp.MediaDescription, +) { + for _, mt := range mediaSection.transceivers { + sender := mt.Sender() + if sender == nil { + continue + } + + track := sender.Track() + if track == nil { + continue + } + + sendParameters := sender.GetParameters() + for _, encoding := range sendParameters.Encodings { + media = media.WithMediaSource(uint32(encoding.SSRC), track.StreamID() /* cname */, track.StreamID() /* streamLabel */, track.ID()) + if !isPlanB { + media = media.WithPropertyAttribute("msid:" + track.StreamID() + " " + track.ID()) + } + } + + if len(sendParameters.Encodings) > 1 { + sendRids := make([]string, 0, len(sendParameters.Encodings)) + + for _, encoding := range sendParameters.Encodings { + media.WithValueAttribute(sdpAttributeRid, encoding.RID+" send") + sendRids = append(sendRids, encoding.RID) + } + // Simulcast + media.WithValueAttribute("simulcast", "send "+strings.Join(sendRids, ";")) + } + + if !isPlanB { + break + } + } +} + +func addTransceiverSDP( + d *sdp.SessionDescription, + isPlanB bool, + shouldAddCandidates bool, + dtlsFingerprints []DTLSFingerprint, + mediaEngine *MediaEngine, + midValue string, + iceParams ICEParameters, + candidates []ICECandidate, + dtlsRole sdp.ConnectionRole, + iceGatheringState ICEGatheringState, + mediaSection mediaSection, +) (bool, error) { transceivers := mediaSection.transceivers if len(transceivers) < 1 { return false, errSDPZeroTransceivers @@ -371,6 +431,10 @@ func addTransceiverSDP(d *sdp.SessionDescription, isPlanB, shouldAddCandidates b } // Explicitly reject track if we don't have the codec + // We need to include connection information even if we're rejecting a track, otherwise Firefox will fail to + // parse the SDP with an error like: + // SIPCC Failed to parse SDP: SDP Parse Error on line 50: c= connection line not specified for every media level, validation failed. + // In addition this makes our SDP compliant with RFC 4566 Section 5.7: https://datatracker.ietf.org/doc/html/rfc4566#section-5.7 d.WithMedia(&sdp.MediaDescription{ MediaName: sdp.MediaName{ Media: t.kind.String(), @@ -378,6 +442,13 @@ func addTransceiverSDP(d *sdp.SessionDescription, isPlanB, shouldAddCandidates b Protos: []string{"UDP", "TLS", "RTP", "SAVPF"}, Formats: []string{"0"}, }, + ConnectionInformation: &sdp.ConnectionInformation{ + NetworkType: "IN", + AddressType: "IP4", + Address: &sdp.Address{ + Address: "0.0.0.0", + }, + }, }) return false, nil } @@ -410,16 +481,7 @@ func addTransceiverSDP(d *sdp.SessionDescription, isPlanB, shouldAddCandidates b media.WithValueAttribute("simulcast", "recv "+strings.Join(recvRids, ";")) } - for _, mt := range transceivers { - if sender := mt.Sender(); sender != nil && sender.Track() != nil { - track := sender.Track() - media = media.WithMediaSource(uint32(sender.ssrc), track.StreamID() /* cname */, track.StreamID() /* streamLabel */, track.ID()) - if !isPlanB { - media = media.WithPropertyAttribute("msid:" + track.StreamID() + " " + track.ID()) - break - } - } - } + addSenderSDP(mediaSection, isPlanB, media) media = media.WithPropertyAttribute(t.Direction().String()) @@ -446,7 +508,7 @@ type mediaSection struct { } // populateSDP serializes a PeerConnections state into an SDP -func populateSDP(d *sdp.SessionDescription, isPlanB bool, dtlsFingerprints []DTLSFingerprint, mediaDescriptionFingerprint bool, isICELite bool, mediaEngine *MediaEngine, connectionRole sdp.ConnectionRole, candidates []ICECandidate, iceParams ICEParameters, mediaSections []mediaSection, iceGatheringState ICEGatheringState) (*sdp.SessionDescription, error) { +func populateSDP(d *sdp.SessionDescription, isPlanB bool, dtlsFingerprints []DTLSFingerprint, mediaDescriptionFingerprint bool, isICELite bool, isExtmapAllowMixed bool, mediaEngine *MediaEngine, connectionRole sdp.ConnectionRole, candidates []ICECandidate, iceParams ICEParameters, mediaSections []mediaSection, iceGatheringState ICEGatheringState) (*sdp.SessionDescription, error) { var err error mediaDtlsFingerprints := []DTLSFingerprint{} @@ -494,7 +556,11 @@ func populateSDP(d *sdp.SessionDescription, isPlanB bool, dtlsFingerprints []DTL if isICELite { // RFC 5245 S15.3 - d = d.WithValueAttribute(sdp.AttrKeyICELite, sdp.AttrKeyICELite) + d = d.WithValueAttribute(sdp.AttrKeyICELite, "") + } + + if isExtmapAllowMixed { + d = d.WithPropertyAttribute(sdp.AttrKeyExtMapAllowMixed) } return d.WithValueAttribute(sdp.AttrKeyGroup, bundleValue), nil @@ -509,7 +575,29 @@ func getMidValue(media *sdp.MediaDescription) string { return "" } -func descriptionIsPlanB(desc *SessionDescription) bool { +// SessionDescription contains a MediaSection with Multiple SSRCs, it is Plan-B +func descriptionIsPlanB(desc *SessionDescription, log logging.LeveledLogger) bool { + if desc == nil || desc.parsed == nil { + return false + } + + // Store all MIDs that already contain a track + midWithTrack := map[string]bool{} + + for _, trackDetail := range trackDetailsFromSDP(log, desc.parsed) { + if _, ok := midWithTrack[trackDetail.mid]; ok { + return true + } + midWithTrack[trackDetail.mid] = true + } + + return false +} + +// SessionDescription contains a MediaSection with name `audio`, `video` or `data` +// If only one SSRC is set we can't know if it is Plan-B or Unified. If users have +// set fallback mode assume it is Plan-B +func descriptionPossiblyPlanB(desc *SessionDescription) bool { if desc == nil || desc.parsed == nil { return false } @@ -659,7 +747,7 @@ func codecsFromMediaDescription(m *sdp.MediaDescription) (out []RTPCodecParamete } for _, payloadStr := range m.MediaName.Formats { - payloadType, err := strconv.Atoi(payloadStr) + payloadType, err := strconv.ParseUint(payloadStr, 10, 8) if err != nil { return nil, err } @@ -673,7 +761,7 @@ func codecsFromMediaDescription(m *sdp.MediaDescription) (out []RTPCodecParamete } channels := uint16(0) - val, err := strconv.Atoi(codec.EncodingParameters) + val, err := strconv.ParseUint(codec.EncodingParameters, 10, 16) if err == nil { channels = uint16(val) } @@ -742,3 +830,13 @@ func isIceLiteSet(desc *sdp.SessionDescription) bool { return false } + +func isExtMapAllowMixedSet(desc *sdp.SessionDescription) bool { + for _, a := range desc.Attributes { + if strings.TrimSpace(a.Key) == sdp.AttrKeyExtMapAllowMixed { + return true + } + } + + return false +} diff --git a/vendor/github.com/pion/webrtc/v3/sdpsemantics.go b/vendor/github.com/pion/webrtc/v3/sdpsemantics.go index b8d396c9b..6d6335047 100644 --- a/vendor/github.com/pion/webrtc/v3/sdpsemantics.go +++ b/vendor/github.com/pion/webrtc/v3/sdpsemantics.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/sdptype.go b/vendor/github.com/pion/webrtc/v3/sdptype.go index bd49b6d14..0c6d1d464 100644 --- a/vendor/github.com/pion/webrtc/v3/sdptype.go +++ b/vendor/github.com/pion/webrtc/v3/sdptype.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/sessiondescription.go b/vendor/github.com/pion/webrtc/v3/sessiondescription.go index 5b9339153..12cdf0dd6 100644 --- a/vendor/github.com/pion/webrtc/v3/sessiondescription.go +++ b/vendor/github.com/pion/webrtc/v3/sessiondescription.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/settingengine.go b/vendor/github.com/pion/webrtc/v3/settingengine.go index a2f5ef418..b11c625e4 100644 --- a/vendor/github.com/pion/webrtc/v3/settingengine.go +++ b/vendor/github.com/pion/webrtc/v3/settingengine.go @@ -1,17 +1,25 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js package webrtc import ( + "context" + "crypto/x509" "io" + "net" "time" "github.com/pion/dtls/v2" + dtlsElliptic "github.com/pion/dtls/v2/pkg/crypto/elliptic" "github.com/pion/ice/v2" "github.com/pion/logging" - "github.com/pion/transport/packetio" - "github.com/pion/transport/vnet" + "github.com/pion/transport/v2" + "github.com/pion/transport/v2/packetio" + "github.com/pion/transport/v2/vnet" "golang.org/x/net/proxy" ) @@ -36,15 +44,17 @@ type SettingEngine struct { ICERelayAcceptanceMinWait *time.Duration } candidates struct { - ICELite bool - ICENetworkTypes []NetworkType - InterfaceFilter func(string) bool - NAT1To1IPs []string - NAT1To1IPCandidateType ICECandidateType - MulticastDNSMode ice.MulticastDNSMode - MulticastDNSHostName string - UsernameFragment string - Password string + ICELite bool + ICENetworkTypes []NetworkType + InterfaceFilter func(string) bool + IPFilter func(net.IP) bool + NAT1To1IPs []string + NAT1To1IPCandidateType ICECandidateType + MulticastDNSMode ice.MulticastDNSMode + MulticastDNSHostName string + UsernameFragment string + Password string + IncludeLoopbackCandidate bool } replayProtection struct { DTLS *uint @@ -52,14 +62,25 @@ type SettingEngine struct { SRTCP *uint } dtls struct { - retransmissionInterval time.Duration + insecureSkipHelloVerify bool + disableInsecureSkipVerify bool + retransmissionInterval time.Duration + ellipticCurves []dtlsElliptic.Curve + connectContextMaker func() (context.Context, func()) + extendedMasterSecret dtls.ExtendedMasterSecretType + clientAuth *dtls.ClientAuthType + clientCAs *x509.CertPool + rootCAs *x509.CertPool + } + sctp struct { + maxReceiveBufferSize uint32 } sdpMediaLevelFingerprints bool answeringDTLSRole DTLSRole disableCertificateFingerprintVerification bool disableSRTPReplayProtection bool disableSRTCPReplayProtection bool - vnet *vnet.Net + net transport.Net BufferFactory func(packetType packetio.BufferPacketType, ssrc uint32) io.ReadWriteCloser LoggerFactory logging.LoggerFactory iceTCPMux ice.TCPMux @@ -93,9 +114,18 @@ func (e *SettingEngine) SetSRTPProtectionProfiles(profiles ...dtls.SRTPProtectio } // SetICETimeouts sets the behavior around ICE Timeouts -// * disconnectedTimeout is the duration without network activity before a Agent is considered disconnected. Default is 5 Seconds -// * failedTimeout is the duration without network activity before a Agent is considered failed after disconnected. Default is 25 Seconds -// * keepAliveInterval is how often the ICE Agent sends extra traffic if there is no activity, if media is flowing no traffic will be sent. Default is 2 seconds +// +// disconnectedTimeout: +// +// Duration without network activity before an Agent is considered disconnected. Default is 5 Seconds +// +// failedTimeout: +// +// Duration without network activity before an Agent is considered failed after disconnected. Default is 25 Seconds +// +// keepAliveInterval: +// +// How often the ICE Agent sends extra traffic if there is no activity, if media is flowing no traffic will be sent. Default is 2 seconds func (e *SettingEngine) SetICETimeouts(disconnectedTimeout, failedTimeout, keepAliveInterval time.Duration) { e.timeout.ICEDisconnectedTimeout = &disconnectedTimeout e.timeout.ICEFailedTimeout = &failedTimeout @@ -125,6 +155,9 @@ func (e *SettingEngine) SetRelayAcceptanceMinWait(t time.Duration) { // SetEphemeralUDPPortRange limits the pool of ephemeral ports that // ICE UDP connections can allocate from. This affects both host candidates, // and the local address of server reflexive candidates. +// +// When portMin and portMax are left to the 0 default value, pion/ice candidate +// gatherer replaces them and uses 1 for portMin and 65535 for portMax. func (e *SettingEngine) SetEphemeralUDPPortRange(portMin, portMax uint16) error { if portMax < portMin { return ice.ErrPort @@ -154,9 +187,17 @@ func (e *SettingEngine) SetInterfaceFilter(filter func(string) bool) { e.candidates.InterfaceFilter = filter } +// SetIPFilter sets the filtering functions when gathering ICE candidates +// This can be used to exclude certain ip from ICE. Which may be +// useful if you know a certain ip will never succeed, or if you wish to reduce +// the amount of information you wish to expose to the remote peer +func (e *SettingEngine) SetIPFilter(filter func(net.IP) bool) { + e.candidates.IPFilter = filter +} + // SetNAT1To1IPs sets a list of external IP addresses of 1:1 (D)NAT // and a candidate type for which the external IP address is used. -// This is useful when you are host a server using Pion on an AWS EC2 instance +// This is useful when you host a server using Pion on an AWS EC2 instance // which has a private address, behind a 1:1 DNAT with a public IP (e.g. // Elastic IP). In this case, you can give the public IP address so that // Pion will use the public IP address in its candidate instead of the private @@ -165,10 +206,12 @@ func (e *SettingEngine) SetInterfaceFilter(filter func(string) bool) { // Two types of candidates are supported: // // ICECandidateTypeHost: -// The public IP address will be used for the host candidate in the SDP. +// +// The public IP address will be used for the host candidate in the SDP. +// // ICECandidateTypeSrflx: -// A server reflexive candidate with the given public IP address will be added -// to the SDP. +// +// A server reflexive candidate with the given public IP address will be added to the SDP. // // Please note that if you choose ICECandidateTypeHost, then the private IP address // won't be advertised with the peer. Also, this option cannot be used along with mDNS. @@ -182,14 +225,23 @@ func (e *SettingEngine) SetNAT1To1IPs(ips []string, candidateType ICECandidateTy e.candidates.NAT1To1IPCandidateType = candidateType } +// SetIncludeLoopbackCandidate enable pion to gather loopback candidates, it is useful +// for some VM have public IP mapped to loopback interface +func (e *SettingEngine) SetIncludeLoopbackCandidate(include bool) { + e.candidates.IncludeLoopbackCandidate = include +} + // SetAnsweringDTLSRole sets the DTLS role that is selected when offering // The DTLS role controls if the WebRTC Client as a client or server. This // may be useful when interacting with non-compliant clients or debugging issues. // // DTLSRoleActive: -// Act as DTLS Client, send the ClientHello and starts the handshake +// +// Act as DTLS Client, send the ClientHello and starts the handshake +// // DTLSRolePassive: -// Act as DTLS Server, wait for ClientHello +// +// Act as DTLS Server, wait for ClientHello func (e *SettingEngine) SetAnsweringDTLSRole(role DTLSRole) error { if role != DTLSRoleClient && role != DTLSRoleServer { return errSettingEngineSetAnsweringDTLSRole @@ -204,8 +256,17 @@ func (e *SettingEngine) SetAnsweringDTLSRole(role DTLSRole) error { // VNet is a virtual network layer for Pion, allowing users to simulate // different topologies, latency, loss and jitter. This can be useful for // learning WebRTC concepts or testing your application in a lab environment +// Deprecated: Please use SetNet() func (e *SettingEngine) SetVNet(vnet *vnet.Net) { - e.vnet = vnet + e.SetNet(vnet) +} + +// SetNet sets the Net instance that is passed to pion/ice +// +// Net is an network interface layer for Pion, allowing users to replace +// Pions network stack with a custom implementation. +func (e *SettingEngine) SetNet(net transport.Net) { + e.net = net } // SetICEMulticastDNSMode controls if pion/ice queries and generates mDNS ICE Candidates @@ -304,3 +365,59 @@ func (e *SettingEngine) SetReceiveMTU(receiveMTU uint) { func (e *SettingEngine) SetDTLSRetransmissionInterval(interval time.Duration) { e.dtls.retransmissionInterval = interval } + +// SetDTLSInsecureSkipHelloVerify sets the skip HelloVerify flag for DTLS. +// If true and when acting as DTLS server, will allow client to skip hello verify phase and +// receive ServerHello after initial ClientHello. This will mean faster connect times, +// but will have lower DoS attack resistance. +func (e *SettingEngine) SetDTLSInsecureSkipHelloVerify(skip bool) { + e.dtls.insecureSkipHelloVerify = skip +} + +// SetDTLSDisableInsecureSkipVerify sets the disable skip insecure verify flag for DTLS. +// This controls whether a client verifies the server's certificate chain and host name. +func (e *SettingEngine) SetDTLSDisableInsecureSkipVerify(disable bool) { + e.dtls.disableInsecureSkipVerify = disable +} + +// SetDTLSEllipticCurves sets the elliptic curves for DTLS. +func (e *SettingEngine) SetDTLSEllipticCurves(ellipticCurves ...dtlsElliptic.Curve) { + e.dtls.ellipticCurves = ellipticCurves +} + +// SetDTLSConnectContextMaker sets the context used during the DTLS Handshake. +// It can be used to extend or reduce the timeout on the DTLS Handshake. +// If nil, the default dtls.ConnectContextMaker is used. It can be implemented as following. +// +// func ConnectContextMaker() (context.Context, func()) { +// return context.WithTimeout(context.Background(), 30*time.Second) +// } +func (e *SettingEngine) SetDTLSConnectContextMaker(connectContextMaker func() (context.Context, func())) { + e.dtls.connectContextMaker = connectContextMaker +} + +// SetDTLSExtendedMasterSecret sets the extended master secret type for DTLS. +func (e *SettingEngine) SetDTLSExtendedMasterSecret(extendedMasterSecret dtls.ExtendedMasterSecretType) { + e.dtls.extendedMasterSecret = extendedMasterSecret +} + +// SetDTLSClientAuth sets the client auth type for DTLS. +func (e *SettingEngine) SetDTLSClientAuth(clientAuth dtls.ClientAuthType) { + e.dtls.clientAuth = &clientAuth +} + +// SetDTLSClientCAs sets the client CA certificate pool for DTLS certificate verification. +func (e *SettingEngine) SetDTLSClientCAs(clientCAs *x509.CertPool) { + e.dtls.clientCAs = clientCAs +} + +// SetDTLSRootCAs sets the root CA certificate pool for DTLS certificate verification. +func (e *SettingEngine) SetDTLSRootCAs(rootCAs *x509.CertPool) { + e.dtls.rootCAs = rootCAs +} + +// SetSCTPMaxReceiveBufferSize sets the maximum receive buffer size. +// Leave this 0 for the default maxReceiveBufferSize. +func (e *SettingEngine) SetSCTPMaxReceiveBufferSize(maxReceiveBufferSize uint32) { + e.sctp.maxReceiveBufferSize = maxReceiveBufferSize +} diff --git a/vendor/github.com/pion/webrtc/v3/settingengine_js.go b/vendor/github.com/pion/webrtc/v3/settingengine_js.go index a4ae0d0ee..0069d04eb 100644 --- a/vendor/github.com/pion/webrtc/v3/settingengine_js.go +++ b/vendor/github.com/pion/webrtc/v3/settingengine_js.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build js && wasm // +build js,wasm diff --git a/vendor/github.com/pion/webrtc/v3/signalingstate.go b/vendor/github.com/pion/webrtc/v3/signalingstate.go index b64dffca8..42911c9af 100644 --- a/vendor/github.com/pion/webrtc/v3/signalingstate.go +++ b/vendor/github.com/pion/webrtc/v3/signalingstate.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/srtp_writer_future.go b/vendor/github.com/pion/webrtc/v3/srtp_writer_future.go index 942996748..6855e8d30 100644 --- a/vendor/github.com/pion/webrtc/v3/srtp_writer_future.go +++ b/vendor/github.com/pion/webrtc/v3/srtp_writer_future.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js @@ -16,6 +19,7 @@ import ( // srtpWriterFuture blocks Read/Write calls until // the SRTP Session is available type srtpWriterFuture struct { + ssrc SSRC rtpSender *RTPSender rtcpReadStream atomic.Value // *srtp.ReadStreamSRTCP rtpWriteStream atomic.Value // *srtp.WriteStreamSRTP @@ -52,7 +56,7 @@ func (s *srtpWriterFuture) init(returnWhenNoSRTP bool) error { return err } - rtcpReadStream, err := srtcpSession.OpenReadStream(uint32(s.rtpSender.ssrc)) + rtcpReadStream, err := srtcpSession.OpenReadStream(uint32(s.ssrc)) if err != nil { return err } @@ -81,16 +85,16 @@ func (s *srtpWriterFuture) Close() error { } s.closed = true - if value := s.rtcpReadStream.Load(); value != nil { - return value.(*srtp.ReadStreamSRTCP).Close() + if value, ok := s.rtcpReadStream.Load().(*srtp.ReadStreamSRTCP); ok { + return value.Close() } return nil } func (s *srtpWriterFuture) Read(b []byte) (n int, err error) { - if value := s.rtcpReadStream.Load(); value != nil { - return value.(*srtp.ReadStreamSRTCP).Read(b) + if value, ok := s.rtcpReadStream.Load().(*srtp.ReadStreamSRTCP); ok { + return value.Read(b) } if err := s.init(false); err != nil || s.rtcpReadStream.Load() == nil { @@ -101,8 +105,8 @@ func (s *srtpWriterFuture) Read(b []byte) (n int, err error) { } func (s *srtpWriterFuture) SetReadDeadline(t time.Time) error { - if value := s.rtcpReadStream.Load(); value != nil { - return value.(*srtp.ReadStreamSRTCP).SetReadDeadline(t) + if value, ok := s.rtcpReadStream.Load().(*srtp.ReadStreamSRTCP); ok { + return value.SetReadDeadline(t) } if err := s.init(false); err != nil || s.rtcpReadStream.Load() == nil { @@ -113,8 +117,8 @@ func (s *srtpWriterFuture) SetReadDeadline(t time.Time) error { } func (s *srtpWriterFuture) WriteRTP(header *rtp.Header, payload []byte) (int, error) { - if value := s.rtpWriteStream.Load(); value != nil { - return value.(*srtp.WriteStreamSRTP).WriteRTP(header, payload) + if value, ok := s.rtpWriteStream.Load().(*srtp.WriteStreamSRTP); ok { + return value.WriteRTP(header, payload) } if err := s.init(true); err != nil || s.rtpWriteStream.Load() == nil { @@ -125,8 +129,8 @@ func (s *srtpWriterFuture) WriteRTP(header *rtp.Header, payload []byte) (int, er } func (s *srtpWriterFuture) Write(b []byte) (int, error) { - if value := s.rtpWriteStream.Load(); value != nil { - return value.(*srtp.WriteStreamSRTP).Write(b) + if value, ok := s.rtpWriteStream.Load().(*srtp.WriteStreamSRTP); ok { + return value.Write(b) } if err := s.init(true); err != nil || s.rtpWriteStream.Load() == nil { diff --git a/vendor/github.com/pion/webrtc/v3/stats.go b/vendor/github.com/pion/webrtc/v3/stats.go index a218d9e8f..5a43cb722 100644 --- a/vendor/github.com/pion/webrtc/v3/stats.go +++ b/vendor/github.com/pion/webrtc/v3/stats.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc import ( diff --git a/vendor/github.com/pion/webrtc/v3/stats_go.go b/vendor/github.com/pion/webrtc/v3/stats_go.go index 10ec55c96..a9a8e2156 100644 --- a/vendor/github.com/pion/webrtc/v3/stats_go.go +++ b/vendor/github.com/pion/webrtc/v3/stats_go.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js diff --git a/vendor/github.com/pion/webrtc/v3/track_local.go b/vendor/github.com/pion/webrtc/v3/track_local.go index 0002e6095..a2e6599a5 100644 --- a/vendor/github.com/pion/webrtc/v3/track_local.go +++ b/vendor/github.com/pion/webrtc/v3/track_local.go @@ -1,6 +1,12 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + package webrtc -import "github.com/pion/rtp" +import ( + "github.com/pion/interceptor" + "github.com/pion/rtp" +) // TrackLocalWriter is the Writer for outbound RTP Packets type TrackLocalWriter interface { @@ -14,10 +20,11 @@ type TrackLocalWriter interface { // TrackLocalContext is the Context passed when a TrackLocal has been Binded/Unbinded from a PeerConnection, and used // in Interceptors. type TrackLocalContext struct { - id string - params RTPParameters - ssrc SSRC - writeStream TrackLocalWriter + id string + params RTPParameters + ssrc SSRC + writeStream TrackLocalWriter + rtcpInterceptor interceptor.RTCPReader } // CodecParameters returns the negotiated RTPCodecParameters. These are the codecs supported by both @@ -49,6 +56,11 @@ func (t *TrackLocalContext) ID() string { return t.id } +// RTCPReader returns the RTCP interceptor for this TrackLocal. Used to read RTCP of this TrackLocal. +func (t *TrackLocalContext) RTCPReader() interceptor.RTCPReader { + return t.rtcpInterceptor +} + // TrackLocal is an interface that controls how the user can send media // The user can provide their own TrackLocal implementations, or use // the implementations in pkg/media diff --git a/vendor/github.com/pion/webrtc/v3/track_local_static.go b/vendor/github.com/pion/webrtc/v3/track_local_static.go index f1ca8b9e0..17ce0587a 100644 --- a/vendor/github.com/pion/webrtc/v3/track_local_static.go +++ b/vendor/github.com/pion/webrtc/v3/track_local_static.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js @@ -128,18 +131,27 @@ var rtpPacketPool = sync.Pool{ }, } +func resetPacketPoolAllocation(localPacket *rtp.Packet) { + *localPacket = rtp.Packet{} + rtpPacketPool.Put(localPacket) +} + +func getPacketAllocationFromPool() *rtp.Packet { + ipacket := rtpPacketPool.Get() + return ipacket.(*rtp.Packet) //nolint:forcetypeassert +} + // WriteRTP writes a RTP Packet to the TrackLocalStaticRTP // If one PeerConnection fails the packets will still be sent to // all PeerConnections. The error message will contain the ID of the failed // PeerConnections so you can remove them func (s *TrackLocalStaticRTP) WriteRTP(p *rtp.Packet) error { - ipacket := rtpPacketPool.Get() - packet := ipacket.(*rtp.Packet) - defer func() { - *packet = rtp.Packet{} - rtpPacketPool.Put(ipacket) - }() + packet := getPacketAllocationFromPool() + + defer resetPacketPoolAllocation(packet) + *packet = *p + return s.writeRTP(packet) } @@ -166,12 +178,9 @@ func (s *TrackLocalStaticRTP) writeRTP(p *rtp.Packet) error { // all PeerConnections. The error message will contain the ID of the failed // PeerConnections so you can remove them func (s *TrackLocalStaticRTP) Write(b []byte) (n int, err error) { - ipacket := rtpPacketPool.Get() - packet := ipacket.(*rtp.Packet) - defer func() { - *packet = rtp.Packet{} - rtpPacketPool.Put(ipacket) - }() + packet := getPacketAllocationFromPool() + + defer resetPacketPoolAllocation(packet) if err = packet.Unmarshal(b); err != nil { return 0, err @@ -282,9 +291,9 @@ func (s *TrackLocalStaticSample) WriteSample(sample media.Sample) error { samples := uint32(sample.Duration.Seconds() * clockRate) if sample.PrevDroppedPackets > 0 { - p.(rtp.Packetizer).SkipSamples(samples * uint32(sample.PrevDroppedPackets)) + p.SkipSamples(samples * uint32(sample.PrevDroppedPackets)) } - packets := p.(rtp.Packetizer).Packetize(sample.Data, samples) + packets := p.Packetize(sample.Data, samples) writeErrs := []error{} for _, p := range packets { diff --git a/vendor/github.com/pion/webrtc/v3/track_remote.go b/vendor/github.com/pion/webrtc/v3/track_remote.go index e98620c31..150b91bc9 100644 --- a/vendor/github.com/pion/webrtc/v3/track_remote.go +++ b/vendor/github.com/pion/webrtc/v3/track_remote.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + //go:build !js // +build !js diff --git a/vendor/github.com/pion/webrtc/v3/webrtc.go b/vendor/github.com/pion/webrtc/v3/webrtc.go index ff32a5578..2781c8d1c 100644 --- a/vendor/github.com/pion/webrtc/v3/webrtc.go +++ b/vendor/github.com/pion/webrtc/v3/webrtc.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + // Package webrtc implements the WebRTC 1.0 as defined in W3C WebRTC specification document. package webrtc diff --git a/vendor/github.com/pion/webrtc/v3/yarn.lock b/vendor/github.com/pion/webrtc/v3/yarn.lock index 90f73c219..01bf7823d 100644 --- a/vendor/github.com/pion/webrtc/v3/yarn.lock +++ b/vendor/github.com/pion/webrtc/v3/yarn.lock @@ -1,6 +1,8 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 +# SPDX-FileCopyrightText: 2023 The Pion community +# SPDX-License-Identifier: MIT abbrev@1: version "1.1.1" diff --git a/vendor/github.com/quic-go/qtls-go1-19/LICENSE b/vendor/github.com/quic-go/qtls-go1-19/LICENSE deleted file mode 100644 index 6a66aea5e..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/quic-go/qtls-go1-19/README.md b/vendor/github.com/quic-go/qtls-go1-19/README.md deleted file mode 100644 index bf41f1c5f..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# qtls - -[![Go Reference](https://pkg.go.dev/badge/github.com/quic-go/qtls-go1-19.svg)](https://pkg.go.dev/github.com/quic-go/qtls-go1-19) -[![.github/workflows/go-test.yml](https://github.com/quic-go/qtls-go1-19/actions/workflows/go-test.yml/badge.svg)](https://github.com/quic-go/qtls-go1-19/actions/workflows/go-test.yml) - -This repository contains a modified version of the standard library's TLS implementation, modified for the QUIC protocol. It is used by [quic-go](https://github.com/lucas-clemente/quic-go). diff --git a/vendor/github.com/quic-go/qtls-go1-19/alert.go b/vendor/github.com/quic-go/qtls-go1-19/alert.go deleted file mode 100644 index 3feac79be..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/alert.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package qtls - -import "strconv" - -type alert uint8 - -// Alert is a TLS alert -type Alert = alert - -const ( - // alert level - alertLevelWarning = 1 - alertLevelError = 2 -) - -const ( - alertCloseNotify alert = 0 - alertUnexpectedMessage alert = 10 - alertBadRecordMAC alert = 20 - alertDecryptionFailed alert = 21 - alertRecordOverflow alert = 22 - alertDecompressionFailure alert = 30 - alertHandshakeFailure alert = 40 - alertBadCertificate alert = 42 - alertUnsupportedCertificate alert = 43 - alertCertificateRevoked alert = 44 - alertCertificateExpired alert = 45 - alertCertificateUnknown alert = 46 - alertIllegalParameter alert = 47 - alertUnknownCA alert = 48 - alertAccessDenied alert = 49 - alertDecodeError alert = 50 - alertDecryptError alert = 51 - alertExportRestriction alert = 60 - alertProtocolVersion alert = 70 - alertInsufficientSecurity alert = 71 - alertInternalError alert = 80 - alertInappropriateFallback alert = 86 - alertUserCanceled alert = 90 - alertNoRenegotiation alert = 100 - alertMissingExtension alert = 109 - alertUnsupportedExtension alert = 110 - alertCertificateUnobtainable alert = 111 - alertUnrecognizedName alert = 112 - alertBadCertificateStatusResponse alert = 113 - alertBadCertificateHashValue alert = 114 - alertUnknownPSKIdentity alert = 115 - alertCertificateRequired alert = 116 - alertNoApplicationProtocol alert = 120 -) - -var alertText = map[alert]string{ - alertCloseNotify: "close notify", - alertUnexpectedMessage: "unexpected message", - alertBadRecordMAC: "bad record MAC", - alertDecryptionFailed: "decryption failed", - alertRecordOverflow: "record overflow", - alertDecompressionFailure: "decompression failure", - alertHandshakeFailure: "handshake failure", - alertBadCertificate: "bad certificate", - alertUnsupportedCertificate: "unsupported certificate", - alertCertificateRevoked: "revoked certificate", - alertCertificateExpired: "expired certificate", - alertCertificateUnknown: "unknown certificate", - alertIllegalParameter: "illegal parameter", - alertUnknownCA: "unknown certificate authority", - alertAccessDenied: "access denied", - alertDecodeError: "error decoding message", - alertDecryptError: "error decrypting message", - alertExportRestriction: "export restriction", - alertProtocolVersion: "protocol version not supported", - alertInsufficientSecurity: "insufficient security level", - alertInternalError: "internal error", - alertInappropriateFallback: "inappropriate fallback", - alertUserCanceled: "user canceled", - alertNoRenegotiation: "no renegotiation", - alertMissingExtension: "missing extension", - alertUnsupportedExtension: "unsupported extension", - alertCertificateUnobtainable: "certificate unobtainable", - alertUnrecognizedName: "unrecognized name", - alertBadCertificateStatusResponse: "bad certificate status response", - alertBadCertificateHashValue: "bad certificate hash value", - alertUnknownPSKIdentity: "unknown PSK identity", - alertCertificateRequired: "certificate required", - alertNoApplicationProtocol: "no application protocol", -} - -func (e alert) String() string { - s, ok := alertText[e] - if ok { - return "tls: " + s - } - return "tls: alert(" + strconv.Itoa(int(e)) + ")" -} - -func (e alert) Error() string { - return e.String() -} diff --git a/vendor/github.com/quic-go/qtls-go1-19/auth.go b/vendor/github.com/quic-go/qtls-go1-19/auth.go deleted file mode 100644 index effc9aced..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/auth.go +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package qtls - -import ( - "bytes" - "crypto" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/elliptic" - "crypto/rsa" - "errors" - "fmt" - "hash" - "io" -) - -// verifyHandshakeSignature verifies a signature against pre-hashed -// (if required) handshake contents. -func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error { - switch sigType { - case signatureECDSA: - pubKey, ok := pubkey.(*ecdsa.PublicKey) - if !ok { - return fmt.Errorf("expected an ECDSA public key, got %T", pubkey) - } - if !ecdsa.VerifyASN1(pubKey, signed, sig) { - return errors.New("ECDSA verification failure") - } - case signatureEd25519: - pubKey, ok := pubkey.(ed25519.PublicKey) - if !ok { - return fmt.Errorf("expected an Ed25519 public key, got %T", pubkey) - } - if !ed25519.Verify(pubKey, signed, sig) { - return errors.New("Ed25519 verification failure") - } - case signaturePKCS1v15: - pubKey, ok := pubkey.(*rsa.PublicKey) - if !ok { - return fmt.Errorf("expected an RSA public key, got %T", pubkey) - } - if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, signed, sig); err != nil { - return err - } - case signatureRSAPSS: - pubKey, ok := pubkey.(*rsa.PublicKey) - if !ok { - return fmt.Errorf("expected an RSA public key, got %T", pubkey) - } - signOpts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash} - if err := rsa.VerifyPSS(pubKey, hashFunc, signed, sig, signOpts); err != nil { - return err - } - default: - return errors.New("internal error: unknown signature type") - } - return nil -} - -const ( - serverSignatureContext = "TLS 1.3, server CertificateVerify\x00" - clientSignatureContext = "TLS 1.3, client CertificateVerify\x00" -) - -var signaturePadding = []byte{ - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, -} - -// signedMessage returns the pre-hashed (if necessary) message to be signed by -// certificate keys in TLS 1.3. See RFC 8446, Section 4.4.3. -func signedMessage(sigHash crypto.Hash, context string, transcript hash.Hash) []byte { - if sigHash == directSigning { - b := &bytes.Buffer{} - b.Write(signaturePadding) - io.WriteString(b, context) - b.Write(transcript.Sum(nil)) - return b.Bytes() - } - h := sigHash.New() - h.Write(signaturePadding) - io.WriteString(h, context) - h.Write(transcript.Sum(nil)) - return h.Sum(nil) -} - -// typeAndHashFromSignatureScheme returns the corresponding signature type and -// crypto.Hash for a given TLS SignatureScheme. -func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType uint8, hash crypto.Hash, err error) { - switch signatureAlgorithm { - case PKCS1WithSHA1, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512: - sigType = signaturePKCS1v15 - case PSSWithSHA256, PSSWithSHA384, PSSWithSHA512: - sigType = signatureRSAPSS - case ECDSAWithSHA1, ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512: - sigType = signatureECDSA - case Ed25519: - sigType = signatureEd25519 - default: - return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm) - } - switch signatureAlgorithm { - case PKCS1WithSHA1, ECDSAWithSHA1: - hash = crypto.SHA1 - case PKCS1WithSHA256, PSSWithSHA256, ECDSAWithP256AndSHA256: - hash = crypto.SHA256 - case PKCS1WithSHA384, PSSWithSHA384, ECDSAWithP384AndSHA384: - hash = crypto.SHA384 - case PKCS1WithSHA512, PSSWithSHA512, ECDSAWithP521AndSHA512: - hash = crypto.SHA512 - case Ed25519: - hash = directSigning - default: - return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm) - } - return sigType, hash, nil -} - -// legacyTypeAndHashFromPublicKey returns the fixed signature type and crypto.Hash for -// a given public key used with TLS 1.0 and 1.1, before the introduction of -// signature algorithm negotiation. -func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash crypto.Hash, err error) { - switch pub.(type) { - case *rsa.PublicKey: - return signaturePKCS1v15, crypto.MD5SHA1, nil - case *ecdsa.PublicKey: - return signatureECDSA, crypto.SHA1, nil - case ed25519.PublicKey: - // RFC 8422 specifies support for Ed25519 in TLS 1.0 and 1.1, - // but it requires holding on to a handshake transcript to do a - // full signature, and not even OpenSSL bothers with the - // complexity, so we can't even test it properly. - return 0, 0, fmt.Errorf("tls: Ed25519 public keys are not supported before TLS 1.2") - default: - return 0, 0, fmt.Errorf("tls: unsupported public key: %T", pub) - } -} - -var rsaSignatureSchemes = []struct { - scheme SignatureScheme - minModulusBytes int - maxVersion uint16 -}{ - // RSA-PSS is used with PSSSaltLengthEqualsHash, and requires - // emLen >= hLen + sLen + 2 - {PSSWithSHA256, crypto.SHA256.Size()*2 + 2, VersionTLS13}, - {PSSWithSHA384, crypto.SHA384.Size()*2 + 2, VersionTLS13}, - {PSSWithSHA512, crypto.SHA512.Size()*2 + 2, VersionTLS13}, - // PKCS #1 v1.5 uses prefixes from hashPrefixes in crypto/rsa, and requires - // emLen >= len(prefix) + hLen + 11 - // TLS 1.3 dropped support for PKCS #1 v1.5 in favor of RSA-PSS. - {PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11, VersionTLS12}, - {PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11, VersionTLS12}, - {PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11, VersionTLS12}, - {PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11, VersionTLS12}, -} - -// signatureSchemesForCertificate returns the list of supported SignatureSchemes -// for a given certificate, based on the public key and the protocol version, -// and optionally filtered by its explicit SupportedSignatureAlgorithms. -// -// This function must be kept in sync with supportedSignatureAlgorithms. -// FIPS filtering is applied in the caller, selectSignatureScheme. -func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme { - priv, ok := cert.PrivateKey.(crypto.Signer) - if !ok { - return nil - } - - var sigAlgs []SignatureScheme - switch pub := priv.Public().(type) { - case *ecdsa.PublicKey: - if version != VersionTLS13 { - // In TLS 1.2 and earlier, ECDSA algorithms are not - // constrained to a single curve. - sigAlgs = []SignatureScheme{ - ECDSAWithP256AndSHA256, - ECDSAWithP384AndSHA384, - ECDSAWithP521AndSHA512, - ECDSAWithSHA1, - } - break - } - switch pub.Curve { - case elliptic.P256(): - sigAlgs = []SignatureScheme{ECDSAWithP256AndSHA256} - case elliptic.P384(): - sigAlgs = []SignatureScheme{ECDSAWithP384AndSHA384} - case elliptic.P521(): - sigAlgs = []SignatureScheme{ECDSAWithP521AndSHA512} - default: - return nil - } - case *rsa.PublicKey: - size := pub.Size() - sigAlgs = make([]SignatureScheme, 0, len(rsaSignatureSchemes)) - for _, candidate := range rsaSignatureSchemes { - if size >= candidate.minModulusBytes && version <= candidate.maxVersion { - sigAlgs = append(sigAlgs, candidate.scheme) - } - } - case ed25519.PublicKey: - sigAlgs = []SignatureScheme{Ed25519} - default: - return nil - } - - if cert.SupportedSignatureAlgorithms != nil { - var filteredSigAlgs []SignatureScheme - for _, sigAlg := range sigAlgs { - if isSupportedSignatureAlgorithm(sigAlg, cert.SupportedSignatureAlgorithms) { - filteredSigAlgs = append(filteredSigAlgs, sigAlg) - } - } - return filteredSigAlgs - } - return sigAlgs -} - -// selectSignatureScheme picks a SignatureScheme from the peer's preference list -// that works with the selected certificate. It's only called for protocol -// versions that support signature algorithms, so TLS 1.2 and 1.3. -func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureScheme) (SignatureScheme, error) { - supportedAlgs := signatureSchemesForCertificate(vers, c) - if len(supportedAlgs) == 0 { - return 0, unsupportedCertificateError(c) - } - if len(peerAlgs) == 0 && vers == VersionTLS12 { - // For TLS 1.2, if the client didn't send signature_algorithms then we - // can assume that it supports SHA1. See RFC 5246, Section 7.4.1.4.1. - peerAlgs = []SignatureScheme{PKCS1WithSHA1, ECDSAWithSHA1} - } - // Pick signature scheme in the peer's preference order, as our - // preference order is not configurable. - for _, preferredAlg := range peerAlgs { - if needFIPS() && !isSupportedSignatureAlgorithm(preferredAlg, fipsSupportedSignatureAlgorithms) { - continue - } - if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) { - return preferredAlg, nil - } - } - return 0, errors.New("tls: peer doesn't support any of the certificate's signature algorithms") -} - -// unsupportedCertificateError returns a helpful error for certificates with -// an unsupported private key. -func unsupportedCertificateError(cert *Certificate) error { - switch cert.PrivateKey.(type) { - case rsa.PrivateKey, ecdsa.PrivateKey: - return fmt.Errorf("tls: unsupported certificate: private key is %T, expected *%T", - cert.PrivateKey, cert.PrivateKey) - case *ed25519.PrivateKey: - return fmt.Errorf("tls: unsupported certificate: private key is *ed25519.PrivateKey, expected ed25519.PrivateKey") - } - - signer, ok := cert.PrivateKey.(crypto.Signer) - if !ok { - return fmt.Errorf("tls: certificate private key (%T) does not implement crypto.Signer", - cert.PrivateKey) - } - - switch pub := signer.Public().(type) { - case *ecdsa.PublicKey: - switch pub.Curve { - case elliptic.P256(): - case elliptic.P384(): - case elliptic.P521(): - default: - return fmt.Errorf("tls: unsupported certificate curve (%s)", pub.Curve.Params().Name) - } - case *rsa.PublicKey: - return fmt.Errorf("tls: certificate RSA key size too small for supported signature algorithms") - case ed25519.PublicKey: - default: - return fmt.Errorf("tls: unsupported certificate key (%T)", pub) - } - - if cert.SupportedSignatureAlgorithms != nil { - return fmt.Errorf("tls: peer doesn't support the certificate custom signature algorithms") - } - - return fmt.Errorf("tls: internal error: unsupported key (%T)", cert.PrivateKey) -} diff --git a/vendor/github.com/quic-go/qtls-go1-19/cipher_suites.go b/vendor/github.com/quic-go/qtls-go1-19/cipher_suites.go deleted file mode 100644 index 56dd45436..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/cipher_suites.go +++ /dev/null @@ -1,693 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package qtls - -import ( - "crypto" - "crypto/aes" - "crypto/cipher" - "crypto/des" - "crypto/hmac" - "crypto/rc4" - "crypto/sha1" - "crypto/sha256" - "fmt" - "hash" - - "golang.org/x/crypto/chacha20poly1305" -) - -// CipherSuite is a TLS cipher suite. Note that most functions in this package -// accept and expose cipher suite IDs instead of this type. -type CipherSuite struct { - ID uint16 - Name string - - // Supported versions is the list of TLS protocol versions that can - // negotiate this cipher suite. - SupportedVersions []uint16 - - // Insecure is true if the cipher suite has known security issues - // due to its primitives, design, or implementation. - Insecure bool -} - -var ( - supportedUpToTLS12 = []uint16{VersionTLS10, VersionTLS11, VersionTLS12} - supportedOnlyTLS12 = []uint16{VersionTLS12} - supportedOnlyTLS13 = []uint16{VersionTLS13} -) - -// CipherSuites returns a list of cipher suites currently implemented by this -// package, excluding those with security issues, which are returned by -// InsecureCipherSuites. -// -// The list is sorted by ID. Note that the default cipher suites selected by -// this package might depend on logic that can't be captured by a static list, -// and might not match those returned by this function. -func CipherSuites() []*CipherSuite { - return []*CipherSuite{ - {TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false}, - {TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false}, - {TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false}, - {TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false}, - - {TLS_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256", supportedOnlyTLS13, false}, - {TLS_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384", supportedOnlyTLS13, false}, - {TLS_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256", supportedOnlyTLS13, false}, - - {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false}, - {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false}, - {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false}, - {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false}, - {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false}, - {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false}, - {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false}, - {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false}, - {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false}, - {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false}, - } -} - -// InsecureCipherSuites returns a list of cipher suites currently implemented by -// this package and which have security issues. -// -// Most applications should not use the cipher suites in this list, and should -// only use those returned by CipherSuites. -func InsecureCipherSuites() []*CipherSuite { - // This list includes RC4, CBC_SHA256, and 3DES cipher suites. See - // cipherSuitesPreferenceOrder for details. - return []*CipherSuite{ - {TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true}, - {TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true}, - {TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true}, - {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", supportedUpToTLS12, true}, - {TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true}, - {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true}, - {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true}, - {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true}, - } -} - -// CipherSuiteName returns the standard name for the passed cipher suite ID -// (e.g. "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"), or a fallback representation -// of the ID value if the cipher suite is not implemented by this package. -func CipherSuiteName(id uint16) string { - for _, c := range CipherSuites() { - if c.ID == id { - return c.Name - } - } - for _, c := range InsecureCipherSuites() { - if c.ID == id { - return c.Name - } - } - return fmt.Sprintf("0x%04X", id) -} - -const ( - // suiteECDHE indicates that the cipher suite involves elliptic curve - // Diffie-Hellman. This means that it should only be selected when the - // client indicates that it supports ECC with a curve and point format - // that we're happy with. - suiteECDHE = 1 << iota - // suiteECSign indicates that the cipher suite involves an ECDSA or - // EdDSA signature and therefore may only be selected when the server's - // certificate is ECDSA or EdDSA. If this is not set then the cipher suite - // is RSA based. - suiteECSign - // suiteTLS12 indicates that the cipher suite should only be advertised - // and accepted when using TLS 1.2. - suiteTLS12 - // suiteSHA384 indicates that the cipher suite uses SHA384 as the - // handshake hash. - suiteSHA384 -) - -// A cipherSuite is a TLS 1.0–1.2 cipher suite, and defines the key exchange -// mechanism, as well as the cipher+MAC pair or the AEAD. -type cipherSuite struct { - id uint16 - // the lengths, in bytes, of the key material needed for each component. - keyLen int - macLen int - ivLen int - ka func(version uint16) keyAgreement - // flags is a bitmask of the suite* values, above. - flags int - cipher func(key, iv []byte, isRead bool) any - mac func(key []byte) hash.Hash - aead func(key, fixedNonce []byte) aead -} - -var cipherSuites = []*cipherSuite{ // TODO: replace with a map, since the order doesn't matter. - {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305}, - {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadChaCha20Poly1305}, - {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM}, - {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadAESGCM}, - {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, - {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, - {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil}, - {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, - {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, cipherAES, macSHA256, nil}, - {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil}, - {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, - {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil}, - {TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM}, - {TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, - {TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil}, - {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil}, - {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil}, - {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil}, - {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil}, - {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil}, - {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil}, - {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherRC4, macSHA1, nil}, -} - -// selectCipherSuite returns the first TLS 1.0–1.2 cipher suite from ids which -// is also in supportedIDs and passes the ok filter. -func selectCipherSuite(ids, supportedIDs []uint16, ok func(*cipherSuite) bool) *cipherSuite { - for _, id := range ids { - candidate := cipherSuiteByID(id) - if candidate == nil || !ok(candidate) { - continue - } - - for _, suppID := range supportedIDs { - if id == suppID { - return candidate - } - } - } - return nil -} - -// A cipherSuiteTLS13 defines only the pair of the AEAD algorithm and hash -// algorithm to be used with HKDF. See RFC 8446, Appendix B.4. -type cipherSuiteTLS13 struct { - id uint16 - keyLen int - aead func(key, fixedNonce []byte) aead - hash crypto.Hash -} - -type CipherSuiteTLS13 struct { - ID uint16 - KeyLen int - Hash crypto.Hash - AEAD func(key, fixedNonce []byte) cipher.AEAD -} - -func (c *CipherSuiteTLS13) IVLen() int { - return aeadNonceLength -} - -var cipherSuitesTLS13 = []*cipherSuiteTLS13{ // TODO: replace with a map. - {TLS_AES_128_GCM_SHA256, 16, aeadAESGCMTLS13, crypto.SHA256}, - {TLS_CHACHA20_POLY1305_SHA256, 32, aeadChaCha20Poly1305, crypto.SHA256}, - {TLS_AES_256_GCM_SHA384, 32, aeadAESGCMTLS13, crypto.SHA384}, -} - -// cipherSuitesPreferenceOrder is the order in which we'll select (on the -// server) or advertise (on the client) TLS 1.0–1.2 cipher suites. -// -// Cipher suites are filtered but not reordered based on the application and -// peer's preferences, meaning we'll never select a suite lower in this list if -// any higher one is available. This makes it more defensible to keep weaker -// cipher suites enabled, especially on the server side where we get the last -// word, since there are no known downgrade attacks on cipher suites selection. -// -// The list is sorted by applying the following priority rules, stopping at the -// first (most important) applicable one: -// -// - Anything else comes before RC4 -// -// RC4 has practically exploitable biases. See https://www.rc4nomore.com. -// -// - Anything else comes before CBC_SHA256 -// -// SHA-256 variants of the CBC ciphersuites don't implement any Lucky13 -// countermeasures. See http://www.isg.rhul.ac.uk/tls/Lucky13.html and -// https://www.imperialviolet.org/2013/02/04/luckythirteen.html. -// -// - Anything else comes before 3DES -// -// 3DES has 64-bit blocks, which makes it fundamentally susceptible to -// birthday attacks. See https://sweet32.info. -// -// - ECDHE comes before anything else -// -// Once we got the broken stuff out of the way, the most important -// property a cipher suite can have is forward secrecy. We don't -// implement FFDHE, so that means ECDHE. -// -// - AEADs come before CBC ciphers -// -// Even with Lucky13 countermeasures, MAC-then-Encrypt CBC cipher suites -// are fundamentally fragile, and suffered from an endless sequence of -// padding oracle attacks. See https://eprint.iacr.org/2015/1129, -// https://www.imperialviolet.org/2014/12/08/poodleagain.html, and -// https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/. -// -// - AES comes before ChaCha20 -// -// When AES hardware is available, AES-128-GCM and AES-256-GCM are faster -// than ChaCha20Poly1305. -// -// When AES hardware is not available, AES-128-GCM is one or more of: much -// slower, way more complex, and less safe (because not constant time) -// than ChaCha20Poly1305. -// -// We use this list if we think both peers have AES hardware, and -// cipherSuitesPreferenceOrderNoAES otherwise. -// -// - AES-128 comes before AES-256 -// -// The only potential advantages of AES-256 are better multi-target -// margins, and hypothetical post-quantum properties. Neither apply to -// TLS, and AES-256 is slower due to its four extra rounds (which don't -// contribute to the advantages above). -// -// - ECDSA comes before RSA -// -// The relative order of ECDSA and RSA cipher suites doesn't matter, -// as they depend on the certificate. Pick one to get a stable order. -var cipherSuitesPreferenceOrder = []uint16{ - // AEADs w/ ECDHE - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - - // CBC w/ ECDHE - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - - // AEADs w/o ECDHE - TLS_RSA_WITH_AES_128_GCM_SHA256, - TLS_RSA_WITH_AES_256_GCM_SHA384, - - // CBC w/o ECDHE - TLS_RSA_WITH_AES_128_CBC_SHA, - TLS_RSA_WITH_AES_256_CBC_SHA, - - // 3DES - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_RSA_WITH_3DES_EDE_CBC_SHA, - - // CBC_SHA256 - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_RSA_WITH_AES_128_CBC_SHA256, - - // RC4 - TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, - TLS_RSA_WITH_RC4_128_SHA, -} - -var cipherSuitesPreferenceOrderNoAES = []uint16{ - // ChaCha20Poly1305 - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - - // AES-GCM w/ ECDHE - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - - // The rest of cipherSuitesPreferenceOrder. - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - TLS_RSA_WITH_AES_128_GCM_SHA256, - TLS_RSA_WITH_AES_256_GCM_SHA384, - TLS_RSA_WITH_AES_128_CBC_SHA, - TLS_RSA_WITH_AES_256_CBC_SHA, - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_RSA_WITH_3DES_EDE_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_RSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, - TLS_RSA_WITH_RC4_128_SHA, -} - -// disabledCipherSuites are not used unless explicitly listed in -// Config.CipherSuites. They MUST be at the end of cipherSuitesPreferenceOrder. -var disabledCipherSuites = []uint16{ - // CBC_SHA256 - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - TLS_RSA_WITH_AES_128_CBC_SHA256, - - // RC4 - TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, - TLS_RSA_WITH_RC4_128_SHA, -} - -var ( - defaultCipherSuitesLen = len(cipherSuitesPreferenceOrder) - len(disabledCipherSuites) - defaultCipherSuites = cipherSuitesPreferenceOrder[:defaultCipherSuitesLen] -) - -// defaultCipherSuitesTLS13 is also the preference order, since there are no -// disabled by default TLS 1.3 cipher suites. The same AES vs ChaCha20 logic as -// cipherSuitesPreferenceOrder applies. -var defaultCipherSuitesTLS13 = []uint16{ - TLS_AES_128_GCM_SHA256, - TLS_AES_256_GCM_SHA384, - TLS_CHACHA20_POLY1305_SHA256, -} - -var defaultCipherSuitesTLS13NoAES = []uint16{ - TLS_CHACHA20_POLY1305_SHA256, - TLS_AES_128_GCM_SHA256, - TLS_AES_256_GCM_SHA384, -} - -var aesgcmCiphers = map[uint16]bool{ - // TLS 1.2 - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true, - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: true, - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: true, - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: true, - // TLS 1.3 - TLS_AES_128_GCM_SHA256: true, - TLS_AES_256_GCM_SHA384: true, -} - -var nonAESGCMAEADCiphers = map[uint16]bool{ - // TLS 1.2 - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: true, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: true, - // TLS 1.3 - TLS_CHACHA20_POLY1305_SHA256: true, -} - -// aesgcmPreferred returns whether the first known cipher in the preference list -// is an AES-GCM cipher, implying the peer has hardware support for it. -func aesgcmPreferred(ciphers []uint16) bool { - for _, cID := range ciphers { - if c := cipherSuiteByID(cID); c != nil { - return aesgcmCiphers[cID] - } - if c := cipherSuiteTLS13ByID(cID); c != nil { - return aesgcmCiphers[cID] - } - } - return false -} - -func cipherRC4(key, iv []byte, isRead bool) any { - cipher, _ := rc4.NewCipher(key) - return cipher -} - -func cipher3DES(key, iv []byte, isRead bool) any { - block, _ := des.NewTripleDESCipher(key) - if isRead { - return cipher.NewCBCDecrypter(block, iv) - } - return cipher.NewCBCEncrypter(block, iv) -} - -func cipherAES(key, iv []byte, isRead bool) any { - block, _ := aes.NewCipher(key) - if isRead { - return cipher.NewCBCDecrypter(block, iv) - } - return cipher.NewCBCEncrypter(block, iv) -} - -// macSHA1 returns a SHA-1 based constant time MAC. -func macSHA1(key []byte) hash.Hash { - h := sha1.New - h = newConstantTimeHash(h) - return hmac.New(h, key) -} - -// macSHA256 returns a SHA-256 based MAC. This is only supported in TLS 1.2 and -// is currently only used in disabled-by-default cipher suites. -func macSHA256(key []byte) hash.Hash { - return hmac.New(sha256.New, key) -} - -type aead interface { - cipher.AEAD - - // explicitNonceLen returns the number of bytes of explicit nonce - // included in each record. This is eight for older AEADs and - // zero for modern ones. - explicitNonceLen() int -} - -const ( - aeadNonceLength = 12 - noncePrefixLength = 4 -) - -// prefixNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to -// each call. -type prefixNonceAEAD struct { - // nonce contains the fixed part of the nonce in the first four bytes. - nonce [aeadNonceLength]byte - aead cipher.AEAD -} - -func (f *prefixNonceAEAD) NonceSize() int { return aeadNonceLength - noncePrefixLength } -func (f *prefixNonceAEAD) Overhead() int { return f.aead.Overhead() } -func (f *prefixNonceAEAD) explicitNonceLen() int { return f.NonceSize() } - -func (f *prefixNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte { - copy(f.nonce[4:], nonce) - return f.aead.Seal(out, f.nonce[:], plaintext, additionalData) -} - -func (f *prefixNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) { - copy(f.nonce[4:], nonce) - return f.aead.Open(out, f.nonce[:], ciphertext, additionalData) -} - -// xoredNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce -// before each call. -type xorNonceAEAD struct { - nonceMask [aeadNonceLength]byte - aead cipher.AEAD -} - -func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number -func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() } -func (f *xorNonceAEAD) explicitNonceLen() int { return 0 } - -func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte { - for i, b := range nonce { - f.nonceMask[4+i] ^= b - } - result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData) - for i, b := range nonce { - f.nonceMask[4+i] ^= b - } - - return result -} - -func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) { - for i, b := range nonce { - f.nonceMask[4+i] ^= b - } - result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData) - for i, b := range nonce { - f.nonceMask[4+i] ^= b - } - - return result, err -} - -func aeadAESGCM(key, noncePrefix []byte) aead { - if len(noncePrefix) != noncePrefixLength { - panic("tls: internal error: wrong nonce length") - } - aes, err := aes.NewCipher(key) - if err != nil { - panic(err) - } - var aead cipher.AEAD - aead, err = cipher.NewGCM(aes) - if err != nil { - panic(err) - } - - ret := &prefixNonceAEAD{aead: aead} - copy(ret.nonce[:], noncePrefix) - return ret -} - -// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3 -func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD { - return aeadAESGCMTLS13(key, fixedNonce) -} - -func aeadAESGCMTLS13(key, nonceMask []byte) aead { - if len(nonceMask) != aeadNonceLength { - panic("tls: internal error: wrong nonce length") - } - aes, err := aes.NewCipher(key) - if err != nil { - panic(err) - } - aead, err := cipher.NewGCM(aes) - if err != nil { - panic(err) - } - - ret := &xorNonceAEAD{aead: aead} - copy(ret.nonceMask[:], nonceMask) - return ret -} - -func aeadChaCha20Poly1305(key, nonceMask []byte) aead { - if len(nonceMask) != aeadNonceLength { - panic("tls: internal error: wrong nonce length") - } - aead, err := chacha20poly1305.New(key) - if err != nil { - panic(err) - } - - ret := &xorNonceAEAD{aead: aead} - copy(ret.nonceMask[:], nonceMask) - return ret -} - -type constantTimeHash interface { - hash.Hash - ConstantTimeSum(b []byte) []byte -} - -// cthWrapper wraps any hash.Hash that implements ConstantTimeSum, and replaces -// with that all calls to Sum. It's used to obtain a ConstantTimeSum-based HMAC. -type cthWrapper struct { - h constantTimeHash -} - -func (c *cthWrapper) Size() int { return c.h.Size() } -func (c *cthWrapper) BlockSize() int { return c.h.BlockSize() } -func (c *cthWrapper) Reset() { c.h.Reset() } -func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) } -func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) } - -func newConstantTimeHash(h func() hash.Hash) func() hash.Hash { - return func() hash.Hash { - return &cthWrapper{h().(constantTimeHash)} - } -} - -// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, Section 6.2.3. -func tls10MAC(h hash.Hash, out, seq, header, data, extra []byte) []byte { - h.Reset() - h.Write(seq) - h.Write(header) - h.Write(data) - res := h.Sum(out) - if extra != nil { - h.Write(extra) - } - return res -} - -func rsaKA(version uint16) keyAgreement { - return rsaKeyAgreement{} -} - -func ecdheECDSAKA(version uint16) keyAgreement { - return &ecdheKeyAgreement{ - isRSA: false, - version: version, - } -} - -func ecdheRSAKA(version uint16) keyAgreement { - return &ecdheKeyAgreement{ - isRSA: true, - version: version, - } -} - -// mutualCipherSuite returns a cipherSuite given a list of supported -// ciphersuites and the id requested by the peer. -func mutualCipherSuite(have []uint16, want uint16) *cipherSuite { - for _, id := range have { - if id == want { - return cipherSuiteByID(id) - } - } - return nil -} - -func cipherSuiteByID(id uint16) *cipherSuite { - for _, cipherSuite := range cipherSuites { - if cipherSuite.id == id { - return cipherSuite - } - } - return nil -} - -func mutualCipherSuiteTLS13(have []uint16, want uint16) *cipherSuiteTLS13 { - for _, id := range have { - if id == want { - return cipherSuiteTLS13ByID(id) - } - } - return nil -} - -func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 { - for _, cipherSuite := range cipherSuitesTLS13 { - if cipherSuite.id == id { - return cipherSuite - } - } - return nil -} - -// A list of cipher suite IDs that are, or have been, implemented by this -// package. -// -// See https://www.iana.org/assignments/tls-parameters/tls-parameters.xml -const ( - // TLS 1.0 - 1.2 cipher suites. - TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 - TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a - TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f - TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035 - TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003c - TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009c - TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009d - TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007 - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009 - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a - TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012 - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013 - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014 - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023 - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc027 - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030 - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca8 - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9 - - // TLS 1.3 cipher suites. - TLS_AES_128_GCM_SHA256 uint16 = 0x1301 - TLS_AES_256_GCM_SHA384 uint16 = 0x1302 - TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303 - - // TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator - // that the client is doing version fallback. See RFC 7507. - TLS_FALLBACK_SCSV uint16 = 0x5600 - - // Legacy names for the corresponding cipher suites with the correct _SHA256 - // suffix, retained for backward compatibility. - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 -) diff --git a/vendor/github.com/quic-go/qtls-go1-19/common.go b/vendor/github.com/quic-go/qtls-go1-19/common.go deleted file mode 100644 index 63e391bf6..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/common.go +++ /dev/null @@ -1,1513 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package qtls - -import ( - "bytes" - "container/list" - "context" - "crypto" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/sha512" - "crypto/tls" - "crypto/x509" - "errors" - "fmt" - "io" - "net" - "strings" - "sync" - "time" -) - -const ( - VersionTLS10 = 0x0301 - VersionTLS11 = 0x0302 - VersionTLS12 = 0x0303 - VersionTLS13 = 0x0304 - - // Deprecated: SSLv3 is cryptographically broken, and is no longer - // supported by this package. See golang.org/issue/32716. - VersionSSL30 = 0x0300 -) - -const ( - maxPlaintext = 16384 // maximum plaintext payload length - maxCiphertext = 16384 + 2048 // maximum ciphertext payload length - maxCiphertextTLS13 = 16384 + 256 // maximum ciphertext length in TLS 1.3 - recordHeaderLen = 5 // record header length - maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB) - maxUselessRecords = 16 // maximum number of consecutive non-advancing records -) - -// TLS record types. -type recordType uint8 - -const ( - recordTypeChangeCipherSpec recordType = 20 - recordTypeAlert recordType = 21 - recordTypeHandshake recordType = 22 - recordTypeApplicationData recordType = 23 -) - -// TLS handshake message types. -const ( - typeHelloRequest uint8 = 0 - typeClientHello uint8 = 1 - typeServerHello uint8 = 2 - typeNewSessionTicket uint8 = 4 - typeEndOfEarlyData uint8 = 5 - typeEncryptedExtensions uint8 = 8 - typeCertificate uint8 = 11 - typeServerKeyExchange uint8 = 12 - typeCertificateRequest uint8 = 13 - typeServerHelloDone uint8 = 14 - typeCertificateVerify uint8 = 15 - typeClientKeyExchange uint8 = 16 - typeFinished uint8 = 20 - typeCertificateStatus uint8 = 22 - typeKeyUpdate uint8 = 24 - typeNextProtocol uint8 = 67 // Not IANA assigned - typeMessageHash uint8 = 254 // synthetic message -) - -// TLS compression types. -const ( - compressionNone uint8 = 0 -) - -type Extension struct { - Type uint16 - Data []byte -} - -// TLS extension numbers -const ( - extensionServerName uint16 = 0 - extensionStatusRequest uint16 = 5 - extensionSupportedCurves uint16 = 10 // supported_groups in TLS 1.3, see RFC 8446, Section 4.2.7 - extensionSupportedPoints uint16 = 11 - extensionSignatureAlgorithms uint16 = 13 - extensionALPN uint16 = 16 - extensionSCT uint16 = 18 - extensionSessionTicket uint16 = 35 - extensionPreSharedKey uint16 = 41 - extensionEarlyData uint16 = 42 - extensionSupportedVersions uint16 = 43 - extensionCookie uint16 = 44 - extensionPSKModes uint16 = 45 - extensionCertificateAuthorities uint16 = 47 - extensionSignatureAlgorithmsCert uint16 = 50 - extensionKeyShare uint16 = 51 - extensionRenegotiationInfo uint16 = 0xff01 -) - -// TLS signaling cipher suite values -const ( - scsvRenegotiation uint16 = 0x00ff -) - -type EncryptionLevel uint8 - -const ( - EncryptionHandshake EncryptionLevel = iota - Encryption0RTT - EncryptionApplication -) - -// CurveID is a tls.CurveID -type CurveID = tls.CurveID - -const ( - CurveP256 CurveID = 23 - CurveP384 CurveID = 24 - CurveP521 CurveID = 25 - X25519 CurveID = 29 -) - -// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8. -type keyShare struct { - group CurveID - data []byte -} - -// TLS 1.3 PSK Key Exchange Modes. See RFC 8446, Section 4.2.9. -const ( - pskModePlain uint8 = 0 - pskModeDHE uint8 = 1 -) - -// TLS 1.3 PSK Identity. Can be a Session Ticket, or a reference to a saved -// session. See RFC 8446, Section 4.2.11. -type pskIdentity struct { - label []byte - obfuscatedTicketAge uint32 -} - -// TLS Elliptic Curve Point Formats -// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9 -const ( - pointFormatUncompressed uint8 = 0 -) - -// TLS CertificateStatusType (RFC 3546) -const ( - statusTypeOCSP uint8 = 1 -) - -// Certificate types (for certificateRequestMsg) -const ( - certTypeRSASign = 1 - certTypeECDSASign = 64 // ECDSA or EdDSA keys, see RFC 8422, Section 3. -) - -// Signature algorithms (for internal signaling use). Starting at 225 to avoid overlap with -// TLS 1.2 codepoints (RFC 5246, Appendix A.4.1), with which these have nothing to do. -const ( - signaturePKCS1v15 uint8 = iota + 225 - signatureRSAPSS - signatureECDSA - signatureEd25519 -) - -// directSigning is a standard Hash value that signals that no pre-hashing -// should be performed, and that the input should be signed directly. It is the -// hash function associated with the Ed25519 signature scheme. -var directSigning crypto.Hash = 0 - -// defaultSupportedSignatureAlgorithms contains the signature and hash algorithms that -// the code advertises as supported in a TLS 1.2+ ClientHello and in a TLS 1.2+ -// CertificateRequest. The two fields are merged to match with TLS 1.3. -// Note that in TLS 1.2, the ECDSA algorithms are not constrained to P-256, etc. -var defaultSupportedSignatureAlgorithms = []SignatureScheme{ - PSSWithSHA256, - ECDSAWithP256AndSHA256, - Ed25519, - PSSWithSHA384, - PSSWithSHA512, - PKCS1WithSHA256, - PKCS1WithSHA384, - PKCS1WithSHA512, - ECDSAWithP384AndSHA384, - ECDSAWithP521AndSHA512, - PKCS1WithSHA1, - ECDSAWithSHA1, -} - -// helloRetryRequestRandom is set as the Random value of a ServerHello -// to signal that the message is actually a HelloRetryRequest. -var helloRetryRequestRandom = []byte{ // See RFC 8446, Section 4.1.3. - 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, - 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, - 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, - 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C, -} - -const ( - // downgradeCanaryTLS12 or downgradeCanaryTLS11 is embedded in the server - // random as a downgrade protection if the server would be capable of - // negotiating a higher version. See RFC 8446, Section 4.1.3. - downgradeCanaryTLS12 = "DOWNGRD\x01" - downgradeCanaryTLS11 = "DOWNGRD\x00" -) - -// testingOnlyForceDowngradeCanary is set in tests to force the server side to -// include downgrade canaries even if it's using its highers supported version. -var testingOnlyForceDowngradeCanary bool - -type ConnectionState = tls.ConnectionState - -// ConnectionState records basic TLS details about the connection. -type connectionState struct { - // Version is the TLS version used by the connection (e.g. VersionTLS12). - Version uint16 - - // HandshakeComplete is true if the handshake has concluded. - HandshakeComplete bool - - // DidResume is true if this connection was successfully resumed from a - // previous session with a session ticket or similar mechanism. - DidResume bool - - // CipherSuite is the cipher suite negotiated for the connection (e.g. - // TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_AES_128_GCM_SHA256). - CipherSuite uint16 - - // NegotiatedProtocol is the application protocol negotiated with ALPN. - NegotiatedProtocol string - - // NegotiatedProtocolIsMutual used to indicate a mutual NPN negotiation. - // - // Deprecated: this value is always true. - NegotiatedProtocolIsMutual bool - - // ServerName is the value of the Server Name Indication extension sent by - // the client. It's available both on the server and on the client side. - ServerName string - - // PeerCertificates are the parsed certificates sent by the peer, in the - // order in which they were sent. The first element is the leaf certificate - // that the connection is verified against. - // - // On the client side, it can't be empty. On the server side, it can be - // empty if Config.ClientAuth is not RequireAnyClientCert or - // RequireAndVerifyClientCert. - PeerCertificates []*x509.Certificate - - // VerifiedChains is a list of one or more chains where the first element is - // PeerCertificates[0] and the last element is from Config.RootCAs (on the - // client side) or Config.ClientCAs (on the server side). - // - // On the client side, it's set if Config.InsecureSkipVerify is false. On - // the server side, it's set if Config.ClientAuth is VerifyClientCertIfGiven - // (and the peer provided a certificate) or RequireAndVerifyClientCert. - VerifiedChains [][]*x509.Certificate - - // SignedCertificateTimestamps is a list of SCTs provided by the peer - // through the TLS handshake for the leaf certificate, if any. - SignedCertificateTimestamps [][]byte - - // OCSPResponse is a stapled Online Certificate Status Protocol (OCSP) - // response provided by the peer for the leaf certificate, if any. - OCSPResponse []byte - - // TLSUnique contains the "tls-unique" channel binding value (see RFC 5929, - // Section 3). This value will be nil for TLS 1.3 connections and for all - // resumed connections. - // - // Deprecated: there are conditions in which this value might not be unique - // to a connection. See the Security Considerations sections of RFC 5705 and - // RFC 7627, and https://mitls.org/pages/attacks/3SHAKE#channelbindings. - TLSUnique []byte - - // ekm is a closure exposed via ExportKeyingMaterial. - ekm func(label string, context []byte, length int) ([]byte, error) -} - -type ConnectionStateWith0RTT struct { - ConnectionState - - Used0RTT bool // true if 0-RTT was both offered and accepted -} - -// ClientAuthType is tls.ClientAuthType -type ClientAuthType = tls.ClientAuthType - -const ( - NoClientCert = tls.NoClientCert - RequestClientCert = tls.RequestClientCert - RequireAnyClientCert = tls.RequireAnyClientCert - VerifyClientCertIfGiven = tls.VerifyClientCertIfGiven - RequireAndVerifyClientCert = tls.RequireAndVerifyClientCert -) - -// requiresClientCert reports whether the ClientAuthType requires a client -// certificate to be provided. -func requiresClientCert(c ClientAuthType) bool { - switch c { - case RequireAnyClientCert, RequireAndVerifyClientCert: - return true - default: - return false - } -} - -// ClientSessionState contains the state needed by clients to resume TLS -// sessions. -type ClientSessionState = tls.ClientSessionState - -type clientSessionState struct { - sessionTicket []uint8 // Encrypted ticket used for session resumption with server - vers uint16 // TLS version negotiated for the session - cipherSuite uint16 // Ciphersuite negotiated for the session - masterSecret []byte // Full handshake MasterSecret, or TLS 1.3 resumption_master_secret - serverCertificates []*x509.Certificate // Certificate chain presented by the server - verifiedChains [][]*x509.Certificate // Certificate chains we built for verification - receivedAt time.Time // When the session ticket was received from the server - ocspResponse []byte // Stapled OCSP response presented by the server - scts [][]byte // SCTs presented by the server - - // TLS 1.3 fields. - nonce []byte // Ticket nonce sent by the server, to derive PSK - useBy time.Time // Expiration of the ticket lifetime as set by the server - ageAdd uint32 // Random obfuscation factor for sending the ticket age -} - -// ClientSessionCache is a cache of ClientSessionState objects that can be used -// by a client to resume a TLS session with a given server. ClientSessionCache -// implementations should expect to be called concurrently from different -// goroutines. Up to TLS 1.2, only ticket-based resumption is supported, not -// SessionID-based resumption. In TLS 1.3 they were merged into PSK modes, which -// are supported via this interface. -// -//go:generate sh -c "mockgen -package qtls -destination mock_client_session_cache_test.go github.com/quic-go/qtls-go1-19 ClientSessionCache" -type ClientSessionCache = tls.ClientSessionCache - -// SignatureScheme is a tls.SignatureScheme -type SignatureScheme = tls.SignatureScheme - -const ( - // RSASSA-PKCS1-v1_5 algorithms. - PKCS1WithSHA256 SignatureScheme = 0x0401 - PKCS1WithSHA384 SignatureScheme = 0x0501 - PKCS1WithSHA512 SignatureScheme = 0x0601 - - // RSASSA-PSS algorithms with public key OID rsaEncryption. - PSSWithSHA256 SignatureScheme = 0x0804 - PSSWithSHA384 SignatureScheme = 0x0805 - PSSWithSHA512 SignatureScheme = 0x0806 - - // ECDSA algorithms. Only constrained to a specific curve in TLS 1.3. - ECDSAWithP256AndSHA256 SignatureScheme = 0x0403 - ECDSAWithP384AndSHA384 SignatureScheme = 0x0503 - ECDSAWithP521AndSHA512 SignatureScheme = 0x0603 - - // EdDSA algorithms. - Ed25519 SignatureScheme = 0x0807 - - // Legacy signature and hash algorithms for TLS 1.2. - PKCS1WithSHA1 SignatureScheme = 0x0201 - ECDSAWithSHA1 SignatureScheme = 0x0203 -) - -// ClientHelloInfo contains information from a ClientHello message in order to -// guide application logic in the GetCertificate and GetConfigForClient callbacks. -type ClientHelloInfo = tls.ClientHelloInfo - -type clientHelloInfo struct { - // CipherSuites lists the CipherSuites supported by the client (e.g. - // TLS_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256). - CipherSuites []uint16 - - // ServerName indicates the name of the server requested by the client - // in order to support virtual hosting. ServerName is only set if the - // client is using SNI (see RFC 4366, Section 3.1). - ServerName string - - // SupportedCurves lists the elliptic curves supported by the client. - // SupportedCurves is set only if the Supported Elliptic Curves - // Extension is being used (see RFC 4492, Section 5.1.1). - SupportedCurves []CurveID - - // SupportedPoints lists the point formats supported by the client. - // SupportedPoints is set only if the Supported Point Formats Extension - // is being used (see RFC 4492, Section 5.1.2). - SupportedPoints []uint8 - - // SignatureSchemes lists the signature and hash schemes that the client - // is willing to verify. SignatureSchemes is set only if the Signature - // Algorithms Extension is being used (see RFC 5246, Section 7.4.1.4.1). - SignatureSchemes []SignatureScheme - - // SupportedProtos lists the application protocols supported by the client. - // SupportedProtos is set only if the Application-Layer Protocol - // Negotiation Extension is being used (see RFC 7301, Section 3.1). - // - // Servers can select a protocol by setting Config.NextProtos in a - // GetConfigForClient return value. - SupportedProtos []string - - // SupportedVersions lists the TLS versions supported by the client. - // For TLS versions less than 1.3, this is extrapolated from the max - // version advertised by the client, so values other than the greatest - // might be rejected if used. - SupportedVersions []uint16 - - // Conn is the underlying net.Conn for the connection. Do not read - // from, or write to, this connection; that will cause the TLS - // connection to fail. - Conn net.Conn - - // config is embedded by the GetCertificate or GetConfigForClient caller, - // for use with SupportsCertificate. - config *Config - - // ctx is the context of the handshake that is in progress. - ctx context.Context -} - -// Context returns the context of the handshake that is in progress. -// This context is a child of the context passed to HandshakeContext, -// if any, and is canceled when the handshake concludes. -func (c *clientHelloInfo) Context() context.Context { - return c.ctx -} - -// CertificateRequestInfo contains information from a server's -// CertificateRequest message, which is used to demand a certificate and proof -// of control from a client. -type CertificateRequestInfo = tls.CertificateRequestInfo - -type certificateRequestInfo struct { - // AcceptableCAs contains zero or more, DER-encoded, X.501 - // Distinguished Names. These are the names of root or intermediate CAs - // that the server wishes the returned certificate to be signed by. An - // empty slice indicates that the server has no preference. - AcceptableCAs [][]byte - - // SignatureSchemes lists the signature schemes that the server is - // willing to verify. - SignatureSchemes []SignatureScheme - - // Version is the TLS version that was negotiated for this connection. - Version uint16 - - // ctx is the context of the handshake that is in progress. - ctx context.Context -} - -// Context returns the context of the handshake that is in progress. -// This context is a child of the context passed to HandshakeContext, -// if any, and is canceled when the handshake concludes. -func (c *certificateRequestInfo) Context() context.Context { - return c.ctx -} - -// RenegotiationSupport enumerates the different levels of support for TLS -// renegotiation. TLS renegotiation is the act of performing subsequent -// handshakes on a connection after the first. This significantly complicates -// the state machine and has been the source of numerous, subtle security -// issues. Initiating a renegotiation is not supported, but support for -// accepting renegotiation requests may be enabled. -// -// Even when enabled, the server may not change its identity between handshakes -// (i.e. the leaf certificate must be the same). Additionally, concurrent -// handshake and application data flow is not permitted so renegotiation can -// only be used with protocols that synchronise with the renegotiation, such as -// HTTPS. -// -// Renegotiation is not defined in TLS 1.3. -type RenegotiationSupport = tls.RenegotiationSupport - -const ( - // RenegotiateNever disables renegotiation. - RenegotiateNever = tls.RenegotiateNever - - // RenegotiateOnceAsClient allows a remote server to request - // renegotiation once per connection. - RenegotiateOnceAsClient = tls.RenegotiateOnceAsClient - - // RenegotiateFreelyAsClient allows a remote server to repeatedly - // request renegotiation. - RenegotiateFreelyAsClient = tls.RenegotiateFreelyAsClient -) - -// A Config structure is used to configure a TLS client or server. -// After one has been passed to a TLS function it must not be -// modified. A Config may be reused; the tls package will also not -// modify it. -type Config = tls.Config - -type config struct { - // Rand provides the source of entropy for nonces and RSA blinding. - // If Rand is nil, TLS uses the cryptographic random reader in package - // crypto/rand. - // The Reader must be safe for use by multiple goroutines. - Rand io.Reader - - // Time returns the current time as the number of seconds since the epoch. - // If Time is nil, TLS uses time.Now. - Time func() time.Time - - // Certificates contains one or more certificate chains to present to the - // other side of the connection. The first certificate compatible with the - // peer's requirements is selected automatically. - // - // Server configurations must set one of Certificates, GetCertificate or - // GetConfigForClient. Clients doing client-authentication may set either - // Certificates or GetClientCertificate. - // - // Note: if there are multiple Certificates, and they don't have the - // optional field Leaf set, certificate selection will incur a significant - // per-handshake performance cost. - Certificates []Certificate - - // NameToCertificate maps from a certificate name to an element of - // Certificates. Note that a certificate name can be of the form - // '*.example.com' and so doesn't have to be a domain name as such. - // - // Deprecated: NameToCertificate only allows associating a single - // certificate with a given name. Leave this field nil to let the library - // select the first compatible chain from Certificates. - NameToCertificate map[string]*Certificate - - // GetCertificate returns a Certificate based on the given - // ClientHelloInfo. It will only be called if the client supplies SNI - // information or if Certificates is empty. - // - // If GetCertificate is nil or returns nil, then the certificate is - // retrieved from NameToCertificate. If NameToCertificate is nil, the - // best element of Certificates will be used. - GetCertificate func(*ClientHelloInfo) (*Certificate, error) - - // GetClientCertificate, if not nil, is called when a server requests a - // certificate from a client. If set, the contents of Certificates will - // be ignored. - // - // If GetClientCertificate returns an error, the handshake will be - // aborted and that error will be returned. Otherwise - // GetClientCertificate must return a non-nil Certificate. If - // Certificate.Certificate is empty then no certificate will be sent to - // the server. If this is unacceptable to the server then it may abort - // the handshake. - // - // GetClientCertificate may be called multiple times for the same - // connection if renegotiation occurs or if TLS 1.3 is in use. - GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error) - - // GetConfigForClient, if not nil, is called after a ClientHello is - // received from a client. It may return a non-nil Config in order to - // change the Config that will be used to handle this connection. If - // the returned Config is nil, the original Config will be used. The - // Config returned by this callback may not be subsequently modified. - // - // If GetConfigForClient is nil, the Config passed to Server() will be - // used for all connections. - // - // If SessionTicketKey was explicitly set on the returned Config, or if - // SetSessionTicketKeys was called on the returned Config, those keys will - // be used. Otherwise, the original Config keys will be used (and possibly - // rotated if they are automatically managed). - GetConfigForClient func(*ClientHelloInfo) (*Config, error) - - // VerifyPeerCertificate, if not nil, is called after normal - // certificate verification by either a TLS client or server. It - // receives the raw ASN.1 certificates provided by the peer and also - // any verified chains that normal processing found. If it returns a - // non-nil error, the handshake is aborted and that error results. - // - // If normal verification fails then the handshake will abort before - // considering this callback. If normal verification is disabled by - // setting InsecureSkipVerify, or (for a server) when ClientAuth is - // RequestClientCert or RequireAnyClientCert, then this callback will - // be considered but the verifiedChains argument will always be nil. - VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error - - // VerifyConnection, if not nil, is called after normal certificate - // verification and after VerifyPeerCertificate by either a TLS client - // or server. If it returns a non-nil error, the handshake is aborted - // and that error results. - // - // If normal verification fails then the handshake will abort before - // considering this callback. This callback will run for all connections - // regardless of InsecureSkipVerify or ClientAuth settings. - VerifyConnection func(ConnectionState) error - - // RootCAs defines the set of root certificate authorities - // that clients use when verifying server certificates. - // If RootCAs is nil, TLS uses the host's root CA set. - RootCAs *x509.CertPool - - // NextProtos is a list of supported application level protocols, in - // order of preference. If both peers support ALPN, the selected - // protocol will be one from this list, and the connection will fail - // if there is no mutually supported protocol. If NextProtos is empty - // or the peer doesn't support ALPN, the connection will succeed and - // ConnectionState.NegotiatedProtocol will be empty. - NextProtos []string - - // ServerName is used to verify the hostname on the returned - // certificates unless InsecureSkipVerify is given. It is also included - // in the client's handshake to support virtual hosting unless it is - // an IP address. - ServerName string - - // ClientAuth determines the server's policy for - // TLS Client Authentication. The default is NoClientCert. - ClientAuth ClientAuthType - - // ClientCAs defines the set of root certificate authorities - // that servers use if required to verify a client certificate - // by the policy in ClientAuth. - ClientCAs *x509.CertPool - - // InsecureSkipVerify controls whether a client verifies the server's - // certificate chain and host name. If InsecureSkipVerify is true, crypto/tls - // accepts any certificate presented by the server and any host name in that - // certificate. In this mode, TLS is susceptible to machine-in-the-middle - // attacks unless custom verification is used. This should be used only for - // testing or in combination with VerifyConnection or VerifyPeerCertificate. - InsecureSkipVerify bool - - // CipherSuites is a list of enabled TLS 1.0–1.2 cipher suites. The order of - // the list is ignored. Note that TLS 1.3 ciphersuites are not configurable. - // - // If CipherSuites is nil, a safe default list is used. The default cipher - // suites might change over time. - CipherSuites []uint16 - - // PreferServerCipherSuites is a legacy field and has no effect. - // - // It used to control whether the server would follow the client's or the - // server's preference. Servers now select the best mutually supported - // cipher suite based on logic that takes into account inferred client - // hardware, server hardware, and security. - // - // Deprecated: PreferServerCipherSuites is ignored. - PreferServerCipherSuites bool - - // SessionTicketsDisabled may be set to true to disable session ticket and - // PSK (resumption) support. Note that on clients, session ticket support is - // also disabled if ClientSessionCache is nil. - SessionTicketsDisabled bool - - // SessionTicketKey is used by TLS servers to provide session resumption. - // See RFC 5077 and the PSK mode of RFC 8446. If zero, it will be filled - // with random data before the first server handshake. - // - // Deprecated: if this field is left at zero, session ticket keys will be - // automatically rotated every day and dropped after seven days. For - // customizing the rotation schedule or synchronizing servers that are - // terminating connections for the same host, use SetSessionTicketKeys. - SessionTicketKey [32]byte - - // ClientSessionCache is a cache of ClientSessionState entries for TLS - // session resumption. It is only used by clients. - ClientSessionCache ClientSessionCache - - // MinVersion contains the minimum TLS version that is acceptable. - // - // By default, TLS 1.2 is currently used as the minimum when acting as a - // client, and TLS 1.0 when acting as a server. TLS 1.0 is the minimum - // supported by this package, both as a client and as a server. - // - // The client-side default can temporarily be reverted to TLS 1.0 by - // including the value "x509sha1=1" in the GODEBUG environment variable. - // Note that this option will be removed in Go 1.19 (but it will still be - // possible to set this field to VersionTLS10 explicitly). - MinVersion uint16 - - // MaxVersion contains the maximum TLS version that is acceptable. - // - // By default, the maximum version supported by this package is used, - // which is currently TLS 1.3. - MaxVersion uint16 - - // CurvePreferences contains the elliptic curves that will be used in - // an ECDHE handshake, in preference order. If empty, the default will - // be used. The client will use the first preference as the type for - // its key share in TLS 1.3. This may change in the future. - CurvePreferences []CurveID - - // DynamicRecordSizingDisabled disables adaptive sizing of TLS records. - // When true, the largest possible TLS record size is always used. When - // false, the size of TLS records may be adjusted in an attempt to - // improve latency. - DynamicRecordSizingDisabled bool - - // Renegotiation controls what types of renegotiation are supported. - // The default, none, is correct for the vast majority of applications. - Renegotiation RenegotiationSupport - - // KeyLogWriter optionally specifies a destination for TLS master secrets - // in NSS key log format that can be used to allow external programs - // such as Wireshark to decrypt TLS connections. - // See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format. - // Use of KeyLogWriter compromises security and should only be - // used for debugging. - KeyLogWriter io.Writer - - // mutex protects sessionTicketKeys and autoSessionTicketKeys. - mutex sync.RWMutex - // sessionTicketKeys contains zero or more ticket keys. If set, it means the - // the keys were set with SessionTicketKey or SetSessionTicketKeys. The - // first key is used for new tickets and any subsequent keys can be used to - // decrypt old tickets. The slice contents are not protected by the mutex - // and are immutable. - sessionTicketKeys []ticketKey - // autoSessionTicketKeys is like sessionTicketKeys but is owned by the - // auto-rotation logic. See Config.ticketKeys. - autoSessionTicketKeys []ticketKey -} - -// A RecordLayer handles encrypting and decrypting of TLS messages. -type RecordLayer interface { - SetReadKey(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte) - SetWriteKey(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte) - ReadHandshakeMessage() ([]byte, error) - WriteRecord([]byte) (int, error) - SendAlert(uint8) -} - -type ExtraConfig struct { - // GetExtensions, if not nil, is called before a message that allows - // sending of extensions is sent. - // Currently only implemented for the ClientHello message (for the client) - // and for the EncryptedExtensions message (for the server). - // Only valid for TLS 1.3. - GetExtensions func(handshakeMessageType uint8) []Extension - - // ReceivedExtensions, if not nil, is called when a message that allows the - // inclusion of extensions is received. - // It is called with an empty slice of extensions, if the message didn't - // contain any extensions. - // Currently only implemented for the ClientHello message (sent by the - // client) and for the EncryptedExtensions message (sent by the server). - // Only valid for TLS 1.3. - ReceivedExtensions func(handshakeMessageType uint8, exts []Extension) - - // AlternativeRecordLayer is used by QUIC - AlternativeRecordLayer RecordLayer - - // Enforce the selection of a supported application protocol. - // Only works for TLS 1.3. - // If enabled, client and server have to agree on an application protocol. - // Otherwise, connection establishment fails. - EnforceNextProtoSelection bool - - // If MaxEarlyData is greater than 0, the client will be allowed to send early - // data when resuming a session. - // Requires the AlternativeRecordLayer to be set. - // - // It has no meaning on the client. - MaxEarlyData uint32 - - // The Accept0RTT callback is called when the client offers 0-RTT. - // The server then has to decide if it wants to accept or reject 0-RTT. - // It is only used for servers. - Accept0RTT func(appData []byte) bool - - // 0RTTRejected is called when the server rejectes 0-RTT. - // It is only used for clients. - Rejected0RTT func() - - // If set, the client will export the 0-RTT key when resuming a session that - // allows sending of early data. - // Requires the AlternativeRecordLayer to be set. - // - // It has no meaning to the server. - Enable0RTT bool - - // Is called when the client saves a session ticket to the session ticket. - // This gives the application the opportunity to save some data along with the ticket, - // which can be restored when the session ticket is used. - GetAppDataForSessionState func() []byte - - // Is called when the client uses a session ticket. - // Restores the application data that was saved earlier on GetAppDataForSessionTicket. - SetAppDataFromSessionState func([]byte) -} - -// Clone clones. -func (c *ExtraConfig) Clone() *ExtraConfig { - return &ExtraConfig{ - GetExtensions: c.GetExtensions, - ReceivedExtensions: c.ReceivedExtensions, - AlternativeRecordLayer: c.AlternativeRecordLayer, - EnforceNextProtoSelection: c.EnforceNextProtoSelection, - MaxEarlyData: c.MaxEarlyData, - Enable0RTT: c.Enable0RTT, - Accept0RTT: c.Accept0RTT, - Rejected0RTT: c.Rejected0RTT, - GetAppDataForSessionState: c.GetAppDataForSessionState, - SetAppDataFromSessionState: c.SetAppDataFromSessionState, - } -} - -func (c *ExtraConfig) usesAlternativeRecordLayer() bool { - return c != nil && c.AlternativeRecordLayer != nil -} - -const ( - // ticketKeyNameLen is the number of bytes of identifier that is prepended to - // an encrypted session ticket in order to identify the key used to encrypt it. - ticketKeyNameLen = 16 - - // ticketKeyLifetime is how long a ticket key remains valid and can be used to - // resume a client connection. - ticketKeyLifetime = 7 * 24 * time.Hour // 7 days - - // ticketKeyRotation is how often the server should rotate the session ticket key - // that is used for new tickets. - ticketKeyRotation = 24 * time.Hour -) - -// ticketKey is the internal representation of a session ticket key. -type ticketKey struct { - // keyName is an opaque byte string that serves to identify the session - // ticket key. It's exposed as plaintext in every session ticket. - keyName [ticketKeyNameLen]byte - aesKey [16]byte - hmacKey [16]byte - // created is the time at which this ticket key was created. See Config.ticketKeys. - created time.Time -} - -// ticketKeyFromBytes converts from the external representation of a session -// ticket key to a ticketKey. Externally, session ticket keys are 32 random -// bytes and this function expands that into sufficient name and key material. -func (c *config) ticketKeyFromBytes(b [32]byte) (key ticketKey) { - hashed := sha512.Sum512(b[:]) - copy(key.keyName[:], hashed[:ticketKeyNameLen]) - copy(key.aesKey[:], hashed[ticketKeyNameLen:ticketKeyNameLen+16]) - copy(key.hmacKey[:], hashed[ticketKeyNameLen+16:ticketKeyNameLen+32]) - key.created = c.time() - return key -} - -// maxSessionTicketLifetime is the maximum allowed lifetime of a TLS 1.3 session -// ticket, and the lifetime we set for tickets we send. -const maxSessionTicketLifetime = 7 * 24 * time.Hour - -// Clone returns a shallow clone of c or nil if c is nil. It is safe to clone a Config that is -// being used concurrently by a TLS client or server. -func (c *config) Clone() *config { - if c == nil { - return nil - } - c.mutex.RLock() - defer c.mutex.RUnlock() - return &config{ - Rand: c.Rand, - Time: c.Time, - Certificates: c.Certificates, - NameToCertificate: c.NameToCertificate, - GetCertificate: c.GetCertificate, - GetClientCertificate: c.GetClientCertificate, - GetConfigForClient: c.GetConfigForClient, - VerifyPeerCertificate: c.VerifyPeerCertificate, - VerifyConnection: c.VerifyConnection, - RootCAs: c.RootCAs, - NextProtos: c.NextProtos, - ServerName: c.ServerName, - ClientAuth: c.ClientAuth, - ClientCAs: c.ClientCAs, - InsecureSkipVerify: c.InsecureSkipVerify, - CipherSuites: c.CipherSuites, - PreferServerCipherSuites: c.PreferServerCipherSuites, - SessionTicketsDisabled: c.SessionTicketsDisabled, - SessionTicketKey: c.SessionTicketKey, - ClientSessionCache: c.ClientSessionCache, - MinVersion: c.MinVersion, - MaxVersion: c.MaxVersion, - CurvePreferences: c.CurvePreferences, - DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled, - Renegotiation: c.Renegotiation, - KeyLogWriter: c.KeyLogWriter, - sessionTicketKeys: c.sessionTicketKeys, - autoSessionTicketKeys: c.autoSessionTicketKeys, - } -} - -// deprecatedSessionTicketKey is set as the prefix of SessionTicketKey if it was -// randomized for backwards compatibility but is not in use. -var deprecatedSessionTicketKey = []byte("DEPRECATED") - -// initLegacySessionTicketKeyRLocked ensures the legacy SessionTicketKey field is -// randomized if empty, and that sessionTicketKeys is populated from it otherwise. -func (c *config) initLegacySessionTicketKeyRLocked() { - // Don't write if SessionTicketKey is already defined as our deprecated string, - // or if it is defined by the user but sessionTicketKeys is already set. - if c.SessionTicketKey != [32]byte{} && - (bytes.HasPrefix(c.SessionTicketKey[:], deprecatedSessionTicketKey) || len(c.sessionTicketKeys) > 0) { - return - } - - // We need to write some data, so get an exclusive lock and re-check any conditions. - c.mutex.RUnlock() - defer c.mutex.RLock() - c.mutex.Lock() - defer c.mutex.Unlock() - if c.SessionTicketKey == [32]byte{} { - if _, err := io.ReadFull(c.rand(), c.SessionTicketKey[:]); err != nil { - panic(fmt.Sprintf("tls: unable to generate random session ticket key: %v", err)) - } - // Write the deprecated prefix at the beginning so we know we created - // it. This key with the DEPRECATED prefix isn't used as an actual - // session ticket key, and is only randomized in case the application - // reuses it for some reason. - copy(c.SessionTicketKey[:], deprecatedSessionTicketKey) - } else if !bytes.HasPrefix(c.SessionTicketKey[:], deprecatedSessionTicketKey) && len(c.sessionTicketKeys) == 0 { - c.sessionTicketKeys = []ticketKey{c.ticketKeyFromBytes(c.SessionTicketKey)} - } - -} - -// ticketKeys returns the ticketKeys for this connection. -// If configForClient has explicitly set keys, those will -// be returned. Otherwise, the keys on c will be used and -// may be rotated if auto-managed. -// During rotation, any expired session ticket keys are deleted from -// c.sessionTicketKeys. If the session ticket key that is currently -// encrypting tickets (ie. the first ticketKey in c.sessionTicketKeys) -// is not fresh, then a new session ticket key will be -// created and prepended to c.sessionTicketKeys. -func (c *config) ticketKeys(configForClient *config) []ticketKey { - // If the ConfigForClient callback returned a Config with explicitly set - // keys, use those, otherwise just use the original Config. - if configForClient != nil { - configForClient.mutex.RLock() - if configForClient.SessionTicketsDisabled { - return nil - } - configForClient.initLegacySessionTicketKeyRLocked() - if len(configForClient.sessionTicketKeys) != 0 { - ret := configForClient.sessionTicketKeys - configForClient.mutex.RUnlock() - return ret - } - configForClient.mutex.RUnlock() - } - - c.mutex.RLock() - defer c.mutex.RUnlock() - if c.SessionTicketsDisabled { - return nil - } - c.initLegacySessionTicketKeyRLocked() - if len(c.sessionTicketKeys) != 0 { - return c.sessionTicketKeys - } - // Fast path for the common case where the key is fresh enough. - if len(c.autoSessionTicketKeys) > 0 && c.time().Sub(c.autoSessionTicketKeys[0].created) < ticketKeyRotation { - return c.autoSessionTicketKeys - } - - // autoSessionTicketKeys are managed by auto-rotation. - c.mutex.RUnlock() - defer c.mutex.RLock() - c.mutex.Lock() - defer c.mutex.Unlock() - // Re-check the condition in case it changed since obtaining the new lock. - if len(c.autoSessionTicketKeys) == 0 || c.time().Sub(c.autoSessionTicketKeys[0].created) >= ticketKeyRotation { - var newKey [32]byte - if _, err := io.ReadFull(c.rand(), newKey[:]); err != nil { - panic(fmt.Sprintf("unable to generate random session ticket key: %v", err)) - } - valid := make([]ticketKey, 0, len(c.autoSessionTicketKeys)+1) - valid = append(valid, c.ticketKeyFromBytes(newKey)) - for _, k := range c.autoSessionTicketKeys { - // While rotating the current key, also remove any expired ones. - if c.time().Sub(k.created) < ticketKeyLifetime { - valid = append(valid, k) - } - } - c.autoSessionTicketKeys = valid - } - return c.autoSessionTicketKeys -} - -// SetSessionTicketKeys updates the session ticket keys for a server. -// -// The first key will be used when creating new tickets, while all keys can be -// used for decrypting tickets. It is safe to call this function while the -// server is running in order to rotate the session ticket keys. The function -// will panic if keys is empty. -// -// Calling this function will turn off automatic session ticket key rotation. -// -// If multiple servers are terminating connections for the same host they should -// all have the same session ticket keys. If the session ticket keys leaks, -// previously recorded and future TLS connections using those keys might be -// compromised. -func (c *config) SetSessionTicketKeys(keys [][32]byte) { - if len(keys) == 0 { - panic("tls: keys must have at least one key") - } - - newKeys := make([]ticketKey, len(keys)) - for i, bytes := range keys { - newKeys[i] = c.ticketKeyFromBytes(bytes) - } - - c.mutex.Lock() - c.sessionTicketKeys = newKeys - c.mutex.Unlock() -} - -func (c *config) rand() io.Reader { - r := c.Rand - if r == nil { - return rand.Reader - } - return r -} - -func (c *config) time() time.Time { - t := c.Time - if t == nil { - t = time.Now - } - return t() -} - -func (c *config) cipherSuites() []uint16 { - if needFIPS() { - return fipsCipherSuites(c) - } - if c.CipherSuites != nil { - return c.CipherSuites - } - return defaultCipherSuites -} - -var supportedVersions = []uint16{ - VersionTLS13, - VersionTLS12, - VersionTLS11, - VersionTLS10, -} - -// roleClient and roleServer are meant to call supportedVersions and parents -// with more readability at the callsite. -const roleClient = true -const roleServer = false - -func (c *config) supportedVersions(isClient bool) []uint16 { - versions := make([]uint16, 0, len(supportedVersions)) - for _, v := range supportedVersions { - if needFIPS() && (v < fipsMinVersion(c) || v > fipsMaxVersion(c)) { - continue - } - if (c == nil || c.MinVersion == 0) && - isClient && v < VersionTLS12 { - continue - } - if c != nil && c.MinVersion != 0 && v < c.MinVersion { - continue - } - if c != nil && c.MaxVersion != 0 && v > c.MaxVersion { - continue - } - versions = append(versions, v) - } - return versions -} - -func (c *config) maxSupportedVersion(isClient bool) uint16 { - supportedVersions := c.supportedVersions(isClient) - if len(supportedVersions) == 0 { - return 0 - } - return supportedVersions[0] -} - -// supportedVersionsFromMax returns a list of supported versions derived from a -// legacy maximum version value. Note that only versions supported by this -// library are returned. Any newer peer will use supportedVersions anyway. -func supportedVersionsFromMax(maxVersion uint16) []uint16 { - versions := make([]uint16, 0, len(supportedVersions)) - for _, v := range supportedVersions { - if v > maxVersion { - continue - } - versions = append(versions, v) - } - return versions -} - -var defaultCurvePreferences = []CurveID{X25519, CurveP256, CurveP384, CurveP521} - -func (c *config) curvePreferences() []CurveID { - if needFIPS() { - return fipsCurvePreferences(c) - } - if c == nil || len(c.CurvePreferences) == 0 { - return defaultCurvePreferences - } - return c.CurvePreferences -} - -func (c *config) supportsCurve(curve CurveID) bool { - for _, cc := range c.curvePreferences() { - if cc == curve { - return true - } - } - return false -} - -// mutualVersion returns the protocol version to use given the advertised -// versions of the peer. Priority is given to the peer preference order. -func (c *config) mutualVersion(isClient bool, peerVersions []uint16) (uint16, bool) { - supportedVersions := c.supportedVersions(isClient) - for _, peerVersion := range peerVersions { - for _, v := range supportedVersions { - if v == peerVersion { - return v, true - } - } - } - return 0, false -} - -var errNoCertificates = errors.New("tls: no certificates configured") - -// getCertificate returns the best certificate for the given ClientHelloInfo, -// defaulting to the first element of c.Certificates. -func (c *config) getCertificate(clientHello *ClientHelloInfo) (*Certificate, error) { - if c.GetCertificate != nil && - (len(c.Certificates) == 0 || len(clientHello.ServerName) > 0) { - cert, err := c.GetCertificate(clientHello) - if cert != nil || err != nil { - return cert, err - } - } - - if len(c.Certificates) == 0 { - return nil, errNoCertificates - } - - if len(c.Certificates) == 1 { - // There's only one choice, so no point doing any work. - return &c.Certificates[0], nil - } - - if c.NameToCertificate != nil { - name := strings.ToLower(clientHello.ServerName) - if cert, ok := c.NameToCertificate[name]; ok { - return cert, nil - } - if len(name) > 0 { - labels := strings.Split(name, ".") - labels[0] = "*" - wildcardName := strings.Join(labels, ".") - if cert, ok := c.NameToCertificate[wildcardName]; ok { - return cert, nil - } - } - } - - for _, cert := range c.Certificates { - if err := clientHello.SupportsCertificate(&cert); err == nil { - return &cert, nil - } - } - - // If nothing matches, return the first certificate. - return &c.Certificates[0], nil -} - -// SupportsCertificate returns nil if the provided certificate is supported by -// the client that sent the ClientHello. Otherwise, it returns an error -// describing the reason for the incompatibility. -// -// If this ClientHelloInfo was passed to a GetConfigForClient or GetCertificate -// callback, this method will take into account the associated Config. Note that -// if GetConfigForClient returns a different Config, the change can't be -// accounted for by this method. -// -// This function will call x509.ParseCertificate unless c.Leaf is set, which can -// incur a significant performance cost. -func (chi *clientHelloInfo) SupportsCertificate(c *Certificate) error { - // Note we don't currently support certificate_authorities nor - // signature_algorithms_cert, and don't check the algorithms of the - // signatures on the chain (which anyway are a SHOULD, see RFC 8446, - // Section 4.4.2.2). - - config := chi.config - if config == nil { - config = &Config{} - } - conf := fromConfig(config) - vers, ok := conf.mutualVersion(roleServer, chi.SupportedVersions) - if !ok { - return errors.New("no mutually supported protocol versions") - } - - // If the client specified the name they are trying to connect to, the - // certificate needs to be valid for it. - if chi.ServerName != "" { - x509Cert, err := leafCertificate(c) - if err != nil { - return fmt.Errorf("failed to parse certificate: %w", err) - } - if err := x509Cert.VerifyHostname(chi.ServerName); err != nil { - return fmt.Errorf("certificate is not valid for requested server name: %w", err) - } - } - - // supportsRSAFallback returns nil if the certificate and connection support - // the static RSA key exchange, and unsupported otherwise. The logic for - // supporting static RSA is completely disjoint from the logic for - // supporting signed key exchanges, so we just check it as a fallback. - supportsRSAFallback := func(unsupported error) error { - // TLS 1.3 dropped support for the static RSA key exchange. - if vers == VersionTLS13 { - return unsupported - } - // The static RSA key exchange works by decrypting a challenge with the - // RSA private key, not by signing, so check the PrivateKey implements - // crypto.Decrypter, like *rsa.PrivateKey does. - if priv, ok := c.PrivateKey.(crypto.Decrypter); ok { - if _, ok := priv.Public().(*rsa.PublicKey); !ok { - return unsupported - } - } else { - return unsupported - } - // Finally, there needs to be a mutual cipher suite that uses the static - // RSA key exchange instead of ECDHE. - rsaCipherSuite := selectCipherSuite(chi.CipherSuites, conf.cipherSuites(), func(c *cipherSuite) bool { - if c.flags&suiteECDHE != 0 { - return false - } - if vers < VersionTLS12 && c.flags&suiteTLS12 != 0 { - return false - } - return true - }) - if rsaCipherSuite == nil { - return unsupported - } - return nil - } - - // If the client sent the signature_algorithms extension, ensure it supports - // schemes we can use with this certificate and TLS version. - if len(chi.SignatureSchemes) > 0 { - if _, err := selectSignatureScheme(vers, c, chi.SignatureSchemes); err != nil { - return supportsRSAFallback(err) - } - } - - // In TLS 1.3 we are done because supported_groups is only relevant to the - // ECDHE computation, point format negotiation is removed, cipher suites are - // only relevant to the AEAD choice, and static RSA does not exist. - if vers == VersionTLS13 { - return nil - } - - // The only signed key exchange we support is ECDHE. - if !supportsECDHE(conf, chi.SupportedCurves, chi.SupportedPoints) { - return supportsRSAFallback(errors.New("client doesn't support ECDHE, can only use legacy RSA key exchange")) - } - - var ecdsaCipherSuite bool - if priv, ok := c.PrivateKey.(crypto.Signer); ok { - switch pub := priv.Public().(type) { - case *ecdsa.PublicKey: - var curve CurveID - switch pub.Curve { - case elliptic.P256(): - curve = CurveP256 - case elliptic.P384(): - curve = CurveP384 - case elliptic.P521(): - curve = CurveP521 - default: - return supportsRSAFallback(unsupportedCertificateError(c)) - } - var curveOk bool - for _, c := range chi.SupportedCurves { - if c == curve && conf.supportsCurve(c) { - curveOk = true - break - } - } - if !curveOk { - return errors.New("client doesn't support certificate curve") - } - ecdsaCipherSuite = true - case ed25519.PublicKey: - if vers < VersionTLS12 || len(chi.SignatureSchemes) == 0 { - return errors.New("connection doesn't support Ed25519") - } - ecdsaCipherSuite = true - case *rsa.PublicKey: - default: - return supportsRSAFallback(unsupportedCertificateError(c)) - } - } else { - return supportsRSAFallback(unsupportedCertificateError(c)) - } - - // Make sure that there is a mutually supported cipher suite that works with - // this certificate. Cipher suite selection will then apply the logic in - // reverse to pick it. See also serverHandshakeState.cipherSuiteOk. - cipherSuite := selectCipherSuite(chi.CipherSuites, conf.cipherSuites(), func(c *cipherSuite) bool { - if c.flags&suiteECDHE == 0 { - return false - } - if c.flags&suiteECSign != 0 { - if !ecdsaCipherSuite { - return false - } - } else { - if ecdsaCipherSuite { - return false - } - } - if vers < VersionTLS12 && c.flags&suiteTLS12 != 0 { - return false - } - return true - }) - if cipherSuite == nil { - return supportsRSAFallback(errors.New("client doesn't support any cipher suites compatible with the certificate")) - } - - return nil -} - -// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate -// from the CommonName and SubjectAlternateName fields of each of the leaf -// certificates. -// -// Deprecated: NameToCertificate only allows associating a single certificate -// with a given name. Leave that field nil to let the library select the first -// compatible chain from Certificates. -func (c *config) BuildNameToCertificate() { - c.NameToCertificate = make(map[string]*Certificate) - for i := range c.Certificates { - cert := &c.Certificates[i] - x509Cert, err := leafCertificate(cert) - if err != nil { - continue - } - // If SANs are *not* present, some clients will consider the certificate - // valid for the name in the Common Name. - if x509Cert.Subject.CommonName != "" && len(x509Cert.DNSNames) == 0 { - c.NameToCertificate[x509Cert.Subject.CommonName] = cert - } - for _, san := range x509Cert.DNSNames { - c.NameToCertificate[san] = cert - } - } -} - -const ( - keyLogLabelTLS12 = "CLIENT_RANDOM" - keyLogLabelEarlyTraffic = "CLIENT_EARLY_TRAFFIC_SECRET" - keyLogLabelClientHandshake = "CLIENT_HANDSHAKE_TRAFFIC_SECRET" - keyLogLabelServerHandshake = "SERVER_HANDSHAKE_TRAFFIC_SECRET" - keyLogLabelClientTraffic = "CLIENT_TRAFFIC_SECRET_0" - keyLogLabelServerTraffic = "SERVER_TRAFFIC_SECRET_0" -) - -func (c *config) writeKeyLog(label string, clientRandom, secret []byte) error { - if c.KeyLogWriter == nil { - return nil - } - - logLine := []byte(fmt.Sprintf("%s %x %x\n", label, clientRandom, secret)) - - writerMutex.Lock() - _, err := c.KeyLogWriter.Write(logLine) - writerMutex.Unlock() - - return err -} - -// writerMutex protects all KeyLogWriters globally. It is rarely enabled, -// and is only for debugging, so a global mutex saves space. -var writerMutex sync.Mutex - -// A Certificate is a chain of one or more certificates, leaf first. -type Certificate = tls.Certificate - -// leaf returns the parsed leaf certificate, either from c.Leaf or by parsing -// the corresponding c.Certificate[0]. -func leafCertificate(c *Certificate) (*x509.Certificate, error) { - if c.Leaf != nil { - return c.Leaf, nil - } - return x509.ParseCertificate(c.Certificate[0]) -} - -type handshakeMessage interface { - marshal() ([]byte, error) - unmarshal([]byte) bool -} - -// lruSessionCache is a ClientSessionCache implementation that uses an LRU -// caching strategy. -type lruSessionCache struct { - sync.Mutex - - m map[string]*list.Element - q *list.List - capacity int -} - -type lruSessionCacheEntry struct { - sessionKey string - state *ClientSessionState -} - -// NewLRUClientSessionCache returns a ClientSessionCache with the given -// capacity that uses an LRU strategy. If capacity is < 1, a default capacity -// is used instead. -func NewLRUClientSessionCache(capacity int) ClientSessionCache { - const defaultSessionCacheCapacity = 64 - - if capacity < 1 { - capacity = defaultSessionCacheCapacity - } - return &lruSessionCache{ - m: make(map[string]*list.Element), - q: list.New(), - capacity: capacity, - } -} - -// Put adds the provided (sessionKey, cs) pair to the cache. If cs is nil, the entry -// corresponding to sessionKey is removed from the cache instead. -func (c *lruSessionCache) Put(sessionKey string, cs *ClientSessionState) { - c.Lock() - defer c.Unlock() - - if elem, ok := c.m[sessionKey]; ok { - if cs == nil { - c.q.Remove(elem) - delete(c.m, sessionKey) - } else { - entry := elem.Value.(*lruSessionCacheEntry) - entry.state = cs - c.q.MoveToFront(elem) - } - return - } - - if c.q.Len() < c.capacity { - entry := &lruSessionCacheEntry{sessionKey, cs} - c.m[sessionKey] = c.q.PushFront(entry) - return - } - - elem := c.q.Back() - entry := elem.Value.(*lruSessionCacheEntry) - delete(c.m, entry.sessionKey) - entry.sessionKey = sessionKey - entry.state = cs - c.q.MoveToFront(elem) - c.m[sessionKey] = elem -} - -// Get returns the ClientSessionState value associated with a given key. It -// returns (nil, false) if no value is found. -func (c *lruSessionCache) Get(sessionKey string) (*ClientSessionState, bool) { - c.Lock() - defer c.Unlock() - - if elem, ok := c.m[sessionKey]; ok { - c.q.MoveToFront(elem) - return elem.Value.(*lruSessionCacheEntry).state, true - } - return nil, false -} - -var emptyConfig Config - -func defaultConfig() *Config { - return &emptyConfig -} - -func unexpectedMessageError(wanted, got any) error { - return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted) -} - -func isSupportedSignatureAlgorithm(sigAlg SignatureScheme, supportedSignatureAlgorithms []SignatureScheme) bool { - for _, s := range supportedSignatureAlgorithms { - if s == sigAlg { - return true - } - } - return false -} diff --git a/vendor/github.com/quic-go/qtls-go1-19/conn.go b/vendor/github.com/quic-go/qtls-go1-19/conn.go deleted file mode 100644 index 19f24e95f..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/conn.go +++ /dev/null @@ -1,1649 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// TLS low level connection and record layer - -package qtls - -import ( - "bytes" - "context" - "crypto/cipher" - "crypto/subtle" - "crypto/x509" - "errors" - "fmt" - "hash" - "io" - "net" - "sync" - "sync/atomic" - "time" -) - -// A Conn represents a secured connection. -// It implements the net.Conn interface. -type Conn struct { - // constant - conn net.Conn - isClient bool - handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake - - // handshakeStatus is 1 if the connection is currently transferring - // application data (i.e. is not currently processing a handshake). - // handshakeStatus == 1 implies handshakeErr == nil. - // This field is only to be accessed with sync/atomic. - handshakeStatus uint32 - // constant after handshake; protected by handshakeMutex - handshakeMutex sync.Mutex - handshakeErr error // error resulting from handshake - vers uint16 // TLS version - haveVers bool // version has been negotiated - config *config // configuration passed to constructor - // handshakes counts the number of handshakes performed on the - // connection so far. If renegotiation is disabled then this is either - // zero or one. - extraConfig *ExtraConfig - - handshakes int - didResume bool // whether this connection was a session resumption - cipherSuite uint16 - ocspResponse []byte // stapled OCSP response - scts [][]byte // signed certificate timestamps from server - peerCertificates []*x509.Certificate - // verifiedChains contains the certificate chains that we built, as - // opposed to the ones presented by the server. - verifiedChains [][]*x509.Certificate - // serverName contains the server name indicated by the client, if any. - serverName string - // secureRenegotiation is true if the server echoed the secure - // renegotiation extension. (This is meaningless as a server because - // renegotiation is not supported in that case.) - secureRenegotiation bool - // ekm is a closure for exporting keying material. - ekm func(label string, context []byte, length int) ([]byte, error) - // For the client: - // resumptionSecret is the resumption_master_secret for handling - // NewSessionTicket messages. nil if config.SessionTicketsDisabled. - // For the server: - // resumptionSecret is the resumption_master_secret for generating - // NewSessionTicket messages. Only used when the alternative record - // layer is set. nil if config.SessionTicketsDisabled. - resumptionSecret []byte - - // ticketKeys is the set of active session ticket keys for this - // connection. The first one is used to encrypt new tickets and - // all are tried to decrypt tickets. - ticketKeys []ticketKey - - // clientFinishedIsFirst is true if the client sent the first Finished - // message during the most recent handshake. This is recorded because - // the first transmitted Finished message is the tls-unique - // channel-binding value. - clientFinishedIsFirst bool - - // closeNotifyErr is any error from sending the alertCloseNotify record. - closeNotifyErr error - // closeNotifySent is true if the Conn attempted to send an - // alertCloseNotify record. - closeNotifySent bool - - // clientFinished and serverFinished contain the Finished message sent - // by the client or server in the most recent handshake. This is - // retained to support the renegotiation extension and tls-unique - // channel-binding. - clientFinished [12]byte - serverFinished [12]byte - - // clientProtocol is the negotiated ALPN protocol. - clientProtocol string - - // input/output - in, out halfConn - rawInput bytes.Buffer // raw input, starting with a record header - input bytes.Reader // application data waiting to be read, from rawInput.Next - hand bytes.Buffer // handshake data waiting to be read - buffering bool // whether records are buffered in sendBuf - sendBuf []byte // a buffer of records waiting to be sent - - // bytesSent counts the bytes of application data sent. - // packetsSent counts packets. - bytesSent int64 - packetsSent int64 - - // retryCount counts the number of consecutive non-advancing records - // received by Conn.readRecord. That is, records that neither advance the - // handshake, nor deliver application data. Protected by in.Mutex. - retryCount int - - // activeCall is an atomic int32; the low bit is whether Close has - // been called. the rest of the bits are the number of goroutines - // in Conn.Write. - activeCall int32 - - used0RTT bool - - tmp [16]byte - - connStateMutex sync.Mutex - connState ConnectionStateWith0RTT -} - -// Access to net.Conn methods. -// Cannot just embed net.Conn because that would -// export the struct field too. - -// LocalAddr returns the local network address. -func (c *Conn) LocalAddr() net.Addr { - return c.conn.LocalAddr() -} - -// RemoteAddr returns the remote network address. -func (c *Conn) RemoteAddr() net.Addr { - return c.conn.RemoteAddr() -} - -// SetDeadline sets the read and write deadlines associated with the connection. -// A zero value for t means Read and Write will not time out. -// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. -func (c *Conn) SetDeadline(t time.Time) error { - return c.conn.SetDeadline(t) -} - -// SetReadDeadline sets the read deadline on the underlying connection. -// A zero value for t means Read will not time out. -func (c *Conn) SetReadDeadline(t time.Time) error { - return c.conn.SetReadDeadline(t) -} - -// SetWriteDeadline sets the write deadline on the underlying connection. -// A zero value for t means Write will not time out. -// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. -func (c *Conn) SetWriteDeadline(t time.Time) error { - return c.conn.SetWriteDeadline(t) -} - -// NetConn returns the underlying connection that is wrapped by c. -// Note that writing to or reading from this connection directly will corrupt the -// TLS session. -func (c *Conn) NetConn() net.Conn { - return c.conn -} - -// A halfConn represents one direction of the record layer -// connection, either sending or receiving. -type halfConn struct { - sync.Mutex - - err error // first permanent error - version uint16 // protocol version - cipher any // cipher algorithm - mac hash.Hash - seq [8]byte // 64-bit sequence number - - scratchBuf [13]byte // to avoid allocs; interface method args escape - - nextCipher any // next encryption state - nextMac hash.Hash // next MAC algorithm - - trafficSecret []byte // current TLS 1.3 traffic secret - - setKeyCallback func(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte) -} - -type permanentError struct { - err net.Error -} - -func (e *permanentError) Error() string { return e.err.Error() } -func (e *permanentError) Unwrap() error { return e.err } -func (e *permanentError) Timeout() bool { return e.err.Timeout() } -func (e *permanentError) Temporary() bool { return false } - -func (hc *halfConn) setErrorLocked(err error) error { - if e, ok := err.(net.Error); ok { - hc.err = &permanentError{err: e} - } else { - hc.err = err - } - return hc.err -} - -// prepareCipherSpec sets the encryption and MAC states -// that a subsequent changeCipherSpec will use. -func (hc *halfConn) prepareCipherSpec(version uint16, cipher any, mac hash.Hash) { - hc.version = version - hc.nextCipher = cipher - hc.nextMac = mac -} - -// changeCipherSpec changes the encryption and MAC states -// to the ones previously passed to prepareCipherSpec. -func (hc *halfConn) changeCipherSpec() error { - if hc.nextCipher == nil || hc.version == VersionTLS13 { - return alertInternalError - } - hc.cipher = hc.nextCipher - hc.mac = hc.nextMac - hc.nextCipher = nil - hc.nextMac = nil - for i := range hc.seq { - hc.seq[i] = 0 - } - return nil -} - -func (hc *halfConn) exportKey(encLevel EncryptionLevel, suite *cipherSuiteTLS13, trafficSecret []byte) { - if hc.setKeyCallback != nil { - s := &CipherSuiteTLS13{ - ID: suite.id, - KeyLen: suite.keyLen, - Hash: suite.hash, - AEAD: func(key, fixedNonce []byte) cipher.AEAD { return suite.aead(key, fixedNonce) }, - } - hc.setKeyCallback(encLevel, s, trafficSecret) - } -} - -func (hc *halfConn) setTrafficSecret(suite *cipherSuiteTLS13, secret []byte) { - hc.trafficSecret = secret - key, iv := suite.trafficKey(secret) - hc.cipher = suite.aead(key, iv) - for i := range hc.seq { - hc.seq[i] = 0 - } -} - -// incSeq increments the sequence number. -func (hc *halfConn) incSeq() { - for i := 7; i >= 0; i-- { - hc.seq[i]++ - if hc.seq[i] != 0 { - return - } - } - - // Not allowed to let sequence number wrap. - // Instead, must renegotiate before it does. - // Not likely enough to bother. - panic("TLS: sequence number wraparound") -} - -// explicitNonceLen returns the number of bytes of explicit nonce or IV included -// in each record. Explicit nonces are present only in CBC modes after TLS 1.0 -// and in certain AEAD modes in TLS 1.2. -func (hc *halfConn) explicitNonceLen() int { - if hc.cipher == nil { - return 0 - } - - switch c := hc.cipher.(type) { - case cipher.Stream: - return 0 - case aead: - return c.explicitNonceLen() - case cbcMode: - // TLS 1.1 introduced a per-record explicit IV to fix the BEAST attack. - if hc.version >= VersionTLS11 { - return c.BlockSize() - } - return 0 - default: - panic("unknown cipher type") - } -} - -// extractPadding returns, in constant time, the length of the padding to remove -// from the end of payload. It also returns a byte which is equal to 255 if the -// padding was valid and 0 otherwise. See RFC 2246, Section 6.2.3.2. -func extractPadding(payload []byte) (toRemove int, good byte) { - if len(payload) < 1 { - return 0, 0 - } - - paddingLen := payload[len(payload)-1] - t := uint(len(payload)-1) - uint(paddingLen) - // if len(payload) >= (paddingLen - 1) then the MSB of t is zero - good = byte(int32(^t) >> 31) - - // The maximum possible padding length plus the actual length field - toCheck := 256 - // The length of the padded data is public, so we can use an if here - if toCheck > len(payload) { - toCheck = len(payload) - } - - for i := 0; i < toCheck; i++ { - t := uint(paddingLen) - uint(i) - // if i <= paddingLen then the MSB of t is zero - mask := byte(int32(^t) >> 31) - b := payload[len(payload)-1-i] - good &^= mask&paddingLen ^ mask&b - } - - // We AND together the bits of good and replicate the result across - // all the bits. - good &= good << 4 - good &= good << 2 - good &= good << 1 - good = uint8(int8(good) >> 7) - - // Zero the padding length on error. This ensures any unchecked bytes - // are included in the MAC. Otherwise, an attacker that could - // distinguish MAC failures from padding failures could mount an attack - // similar to POODLE in SSL 3.0: given a good ciphertext that uses a - // full block's worth of padding, replace the final block with another - // block. If the MAC check passed but the padding check failed, the - // last byte of that block decrypted to the block size. - // - // See also macAndPaddingGood logic below. - paddingLen &= good - - toRemove = int(paddingLen) + 1 - return -} - -func roundUp(a, b int) int { - return a + (b-a%b)%b -} - -// cbcMode is an interface for block ciphers using cipher block chaining. -type cbcMode interface { - cipher.BlockMode - SetIV([]byte) -} - -// decrypt authenticates and decrypts the record if protection is active at -// this stage. The returned plaintext might overlap with the input. -func (hc *halfConn) decrypt(record []byte) ([]byte, recordType, error) { - var plaintext []byte - typ := recordType(record[0]) - payload := record[recordHeaderLen:] - - // In TLS 1.3, change_cipher_spec messages are to be ignored without being - // decrypted. See RFC 8446, Appendix D.4. - if hc.version == VersionTLS13 && typ == recordTypeChangeCipherSpec { - return payload, typ, nil - } - - paddingGood := byte(255) - paddingLen := 0 - - explicitNonceLen := hc.explicitNonceLen() - - if hc.cipher != nil { - switch c := hc.cipher.(type) { - case cipher.Stream: - c.XORKeyStream(payload, payload) - case aead: - if len(payload) < explicitNonceLen { - return nil, 0, alertBadRecordMAC - } - nonce := payload[:explicitNonceLen] - if len(nonce) == 0 { - nonce = hc.seq[:] - } - payload = payload[explicitNonceLen:] - - var additionalData []byte - if hc.version == VersionTLS13 { - additionalData = record[:recordHeaderLen] - } else { - additionalData = append(hc.scratchBuf[:0], hc.seq[:]...) - additionalData = append(additionalData, record[:3]...) - n := len(payload) - c.Overhead() - additionalData = append(additionalData, byte(n>>8), byte(n)) - } - - var err error - plaintext, err = c.Open(payload[:0], nonce, payload, additionalData) - if err != nil { - return nil, 0, alertBadRecordMAC - } - case cbcMode: - blockSize := c.BlockSize() - minPayload := explicitNonceLen + roundUp(hc.mac.Size()+1, blockSize) - if len(payload)%blockSize != 0 || len(payload) < minPayload { - return nil, 0, alertBadRecordMAC - } - - if explicitNonceLen > 0 { - c.SetIV(payload[:explicitNonceLen]) - payload = payload[explicitNonceLen:] - } - c.CryptBlocks(payload, payload) - - // In a limited attempt to protect against CBC padding oracles like - // Lucky13, the data past paddingLen (which is secret) is passed to - // the MAC function as extra data, to be fed into the HMAC after - // computing the digest. This makes the MAC roughly constant time as - // long as the digest computation is constant time and does not - // affect the subsequent write, modulo cache effects. - paddingLen, paddingGood = extractPadding(payload) - default: - panic("unknown cipher type") - } - - if hc.version == VersionTLS13 { - if typ != recordTypeApplicationData { - return nil, 0, alertUnexpectedMessage - } - if len(plaintext) > maxPlaintext+1 { - return nil, 0, alertRecordOverflow - } - // Remove padding and find the ContentType scanning from the end. - for i := len(plaintext) - 1; i >= 0; i-- { - if plaintext[i] != 0 { - typ = recordType(plaintext[i]) - plaintext = plaintext[:i] - break - } - if i == 0 { - return nil, 0, alertUnexpectedMessage - } - } - } - } else { - plaintext = payload - } - - if hc.mac != nil { - macSize := hc.mac.Size() - if len(payload) < macSize { - return nil, 0, alertBadRecordMAC - } - - n := len(payload) - macSize - paddingLen - n = subtle.ConstantTimeSelect(int(uint32(n)>>31), 0, n) // if n < 0 { n = 0 } - record[3] = byte(n >> 8) - record[4] = byte(n) - remoteMAC := payload[n : n+macSize] - localMAC := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload[:n], payload[n+macSize:]) - - // This is equivalent to checking the MACs and paddingGood - // separately, but in constant-time to prevent distinguishing - // padding failures from MAC failures. Depending on what value - // of paddingLen was returned on bad padding, distinguishing - // bad MAC from bad padding can lead to an attack. - // - // See also the logic at the end of extractPadding. - macAndPaddingGood := subtle.ConstantTimeCompare(localMAC, remoteMAC) & int(paddingGood) - if macAndPaddingGood != 1 { - return nil, 0, alertBadRecordMAC - } - - plaintext = payload[:n] - } - - hc.incSeq() - return plaintext, typ, nil -} - -func (c *Conn) setAlternativeRecordLayer() { - if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil { - c.in.setKeyCallback = c.extraConfig.AlternativeRecordLayer.SetReadKey - c.out.setKeyCallback = c.extraConfig.AlternativeRecordLayer.SetWriteKey - } -} - -// sliceForAppend extends the input slice by n bytes. head is the full extended -// slice, while tail is the appended part. If the original slice has sufficient -// capacity no allocation is performed. -func sliceForAppend(in []byte, n int) (head, tail []byte) { - if total := len(in) + n; cap(in) >= total { - head = in[:total] - } else { - head = make([]byte, total) - copy(head, in) - } - tail = head[len(in):] - return -} - -// encrypt encrypts payload, adding the appropriate nonce and/or MAC, and -// appends it to record, which must already contain the record header. -func (hc *halfConn) encrypt(record, payload []byte, rand io.Reader) ([]byte, error) { - if hc.cipher == nil { - return append(record, payload...), nil - } - - var explicitNonce []byte - if explicitNonceLen := hc.explicitNonceLen(); explicitNonceLen > 0 { - record, explicitNonce = sliceForAppend(record, explicitNonceLen) - if _, isCBC := hc.cipher.(cbcMode); !isCBC && explicitNonceLen < 16 { - // The AES-GCM construction in TLS has an explicit nonce so that the - // nonce can be random. However, the nonce is only 8 bytes which is - // too small for a secure, random nonce. Therefore we use the - // sequence number as the nonce. The 3DES-CBC construction also has - // an 8 bytes nonce but its nonces must be unpredictable (see RFC - // 5246, Appendix F.3), forcing us to use randomness. That's not - // 3DES' biggest problem anyway because the birthday bound on block - // collision is reached first due to its similarly small block size - // (see the Sweet32 attack). - copy(explicitNonce, hc.seq[:]) - } else { - if _, err := io.ReadFull(rand, explicitNonce); err != nil { - return nil, err - } - } - } - - var dst []byte - switch c := hc.cipher.(type) { - case cipher.Stream: - mac := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload, nil) - record, dst = sliceForAppend(record, len(payload)+len(mac)) - c.XORKeyStream(dst[:len(payload)], payload) - c.XORKeyStream(dst[len(payload):], mac) - case aead: - nonce := explicitNonce - if len(nonce) == 0 { - nonce = hc.seq[:] - } - - if hc.version == VersionTLS13 { - record = append(record, payload...) - - // Encrypt the actual ContentType and replace the plaintext one. - record = append(record, record[0]) - record[0] = byte(recordTypeApplicationData) - - n := len(payload) + 1 + c.Overhead() - record[3] = byte(n >> 8) - record[4] = byte(n) - - record = c.Seal(record[:recordHeaderLen], - nonce, record[recordHeaderLen:], record[:recordHeaderLen]) - } else { - additionalData := append(hc.scratchBuf[:0], hc.seq[:]...) - additionalData = append(additionalData, record[:recordHeaderLen]...) - record = c.Seal(record, nonce, payload, additionalData) - } - case cbcMode: - mac := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload, nil) - blockSize := c.BlockSize() - plaintextLen := len(payload) + len(mac) - paddingLen := blockSize - plaintextLen%blockSize - record, dst = sliceForAppend(record, plaintextLen+paddingLen) - copy(dst, payload) - copy(dst[len(payload):], mac) - for i := plaintextLen; i < len(dst); i++ { - dst[i] = byte(paddingLen - 1) - } - if len(explicitNonce) > 0 { - c.SetIV(explicitNonce) - } - c.CryptBlocks(dst, dst) - default: - panic("unknown cipher type") - } - - // Update length to include nonce, MAC and any block padding needed. - n := len(record) - recordHeaderLen - record[3] = byte(n >> 8) - record[4] = byte(n) - hc.incSeq() - - return record, nil -} - -// RecordHeaderError is returned when a TLS record header is invalid. -type RecordHeaderError struct { - // Msg contains a human readable string that describes the error. - Msg string - // RecordHeader contains the five bytes of TLS record header that - // triggered the error. - RecordHeader [5]byte - // Conn provides the underlying net.Conn in the case that a client - // sent an initial handshake that didn't look like TLS. - // It is nil if there's already been a handshake or a TLS alert has - // been written to the connection. - Conn net.Conn -} - -func (e RecordHeaderError) Error() string { return "tls: " + e.Msg } - -func (c *Conn) newRecordHeaderError(conn net.Conn, msg string) (err RecordHeaderError) { - err.Msg = msg - err.Conn = conn - copy(err.RecordHeader[:], c.rawInput.Bytes()) - return err -} - -func (c *Conn) readRecord() error { - return c.readRecordOrCCS(false) -} - -func (c *Conn) readChangeCipherSpec() error { - return c.readRecordOrCCS(true) -} - -// readRecordOrCCS reads one or more TLS records from the connection and -// updates the record layer state. Some invariants: -// - c.in must be locked -// - c.input must be empty -// -// During the handshake one and only one of the following will happen: -// - c.hand grows -// - c.in.changeCipherSpec is called -// - an error is returned -// -// After the handshake one and only one of the following will happen: -// - c.hand grows -// - c.input is set -// - an error is returned -func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { - if c.in.err != nil { - return c.in.err - } - handshakeComplete := c.handshakeComplete() - - // This function modifies c.rawInput, which owns the c.input memory. - if c.input.Len() != 0 { - return c.in.setErrorLocked(errors.New("tls: internal error: attempted to read record with pending application data")) - } - c.input.Reset(nil) - - // Read header, payload. - if err := c.readFromUntil(c.conn, recordHeaderLen); err != nil { - // RFC 8446, Section 6.1 suggests that EOF without an alertCloseNotify - // is an error, but popular web sites seem to do this, so we accept it - // if and only if at the record boundary. - if err == io.ErrUnexpectedEOF && c.rawInput.Len() == 0 { - err = io.EOF - } - if e, ok := err.(net.Error); !ok || !e.Temporary() { - c.in.setErrorLocked(err) - } - return err - } - hdr := c.rawInput.Bytes()[:recordHeaderLen] - typ := recordType(hdr[0]) - - // No valid TLS record has a type of 0x80, however SSLv2 handshakes - // start with a uint16 length where the MSB is set and the first record - // is always < 256 bytes long. Therefore typ == 0x80 strongly suggests - // an SSLv2 client. - if !handshakeComplete && typ == 0x80 { - c.sendAlert(alertProtocolVersion) - return c.in.setErrorLocked(c.newRecordHeaderError(nil, "unsupported SSLv2 handshake received")) - } - - vers := uint16(hdr[1])<<8 | uint16(hdr[2]) - n := int(hdr[3])<<8 | int(hdr[4]) - if c.haveVers && c.vers != VersionTLS13 && vers != c.vers { - c.sendAlert(alertProtocolVersion) - msg := fmt.Sprintf("received record with version %x when expecting version %x", vers, c.vers) - return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg)) - } - if !c.haveVers { - // First message, be extra suspicious: this might not be a TLS - // client. Bail out before reading a full 'body', if possible. - // The current max version is 3.3 so if the version is >= 16.0, - // it's probably not real. - if (typ != recordTypeAlert && typ != recordTypeHandshake) || vers >= 0x1000 { - return c.in.setErrorLocked(c.newRecordHeaderError(c.conn, "first record does not look like a TLS handshake")) - } - } - if c.vers == VersionTLS13 && n > maxCiphertextTLS13 || n > maxCiphertext { - c.sendAlert(alertRecordOverflow) - msg := fmt.Sprintf("oversized record received with length %d", n) - return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg)) - } - if err := c.readFromUntil(c.conn, recordHeaderLen+n); err != nil { - if e, ok := err.(net.Error); !ok || !e.Temporary() { - c.in.setErrorLocked(err) - } - return err - } - - // Process message. - record := c.rawInput.Next(recordHeaderLen + n) - data, typ, err := c.in.decrypt(record) - if err != nil { - return c.in.setErrorLocked(c.sendAlert(err.(alert))) - } - if len(data) > maxPlaintext { - return c.in.setErrorLocked(c.sendAlert(alertRecordOverflow)) - } - - // Application Data messages are always protected. - if c.in.cipher == nil && typ == recordTypeApplicationData { - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - - if typ != recordTypeAlert && typ != recordTypeChangeCipherSpec && len(data) > 0 { - // This is a state-advancing message: reset the retry count. - c.retryCount = 0 - } - - // Handshake messages MUST NOT be interleaved with other record types in TLS 1.3. - if c.vers == VersionTLS13 && typ != recordTypeHandshake && c.hand.Len() > 0 { - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - - switch typ { - default: - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - - case recordTypeAlert: - if len(data) != 2 { - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - if alert(data[1]) == alertCloseNotify { - return c.in.setErrorLocked(io.EOF) - } - if c.vers == VersionTLS13 { - return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])}) - } - switch data[0] { - case alertLevelWarning: - // Drop the record on the floor and retry. - return c.retryReadRecord(expectChangeCipherSpec) - case alertLevelError: - return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])}) - default: - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - - case recordTypeChangeCipherSpec: - if len(data) != 1 || data[0] != 1 { - return c.in.setErrorLocked(c.sendAlert(alertDecodeError)) - } - // Handshake messages are not allowed to fragment across the CCS. - if c.hand.Len() > 0 { - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - // In TLS 1.3, change_cipher_spec records are ignored until the - // Finished. See RFC 8446, Appendix D.4. Note that according to Section - // 5, a server can send a ChangeCipherSpec before its ServerHello, when - // c.vers is still unset. That's not useful though and suspicious if the - // server then selects a lower protocol version, so don't allow that. - if c.vers == VersionTLS13 { - return c.retryReadRecord(expectChangeCipherSpec) - } - if !expectChangeCipherSpec { - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - if err := c.in.changeCipherSpec(); err != nil { - return c.in.setErrorLocked(c.sendAlert(err.(alert))) - } - - case recordTypeApplicationData: - if !handshakeComplete || expectChangeCipherSpec { - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - // Some OpenSSL servers send empty records in order to randomize the - // CBC IV. Ignore a limited number of empty records. - if len(data) == 0 { - return c.retryReadRecord(expectChangeCipherSpec) - } - // Note that data is owned by c.rawInput, following the Next call above, - // to avoid copying the plaintext. This is safe because c.rawInput is - // not read from or written to until c.input is drained. - c.input.Reset(data) - - case recordTypeHandshake: - if len(data) == 0 || expectChangeCipherSpec { - return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - c.hand.Write(data) - } - - return nil -} - -// retryReadRecord recurs into readRecordOrCCS to drop a non-advancing record, like -// a warning alert, empty application_data, or a change_cipher_spec in TLS 1.3. -func (c *Conn) retryReadRecord(expectChangeCipherSpec bool) error { - c.retryCount++ - if c.retryCount > maxUselessRecords { - c.sendAlert(alertUnexpectedMessage) - return c.in.setErrorLocked(errors.New("tls: too many ignored records")) - } - return c.readRecordOrCCS(expectChangeCipherSpec) -} - -// atLeastReader reads from R, stopping with EOF once at least N bytes have been -// read. It is different from an io.LimitedReader in that it doesn't cut short -// the last Read call, and in that it considers an early EOF an error. -type atLeastReader struct { - R io.Reader - N int64 -} - -func (r *atLeastReader) Read(p []byte) (int, error) { - if r.N <= 0 { - return 0, io.EOF - } - n, err := r.R.Read(p) - r.N -= int64(n) // won't underflow unless len(p) >= n > 9223372036854775809 - if r.N > 0 && err == io.EOF { - return n, io.ErrUnexpectedEOF - } - if r.N <= 0 && err == nil { - return n, io.EOF - } - return n, err -} - -// readFromUntil reads from r into c.rawInput until c.rawInput contains -// at least n bytes or else returns an error. -func (c *Conn) readFromUntil(r io.Reader, n int) error { - if c.rawInput.Len() >= n { - return nil - } - needs := n - c.rawInput.Len() - // There might be extra input waiting on the wire. Make a best effort - // attempt to fetch it so that it can be used in (*Conn).Read to - // "predict" closeNotify alerts. - c.rawInput.Grow(needs + bytes.MinRead) - _, err := c.rawInput.ReadFrom(&atLeastReader{r, int64(needs)}) - return err -} - -// sendAlert sends a TLS alert message. -func (c *Conn) sendAlertLocked(err alert) error { - switch err { - case alertNoRenegotiation, alertCloseNotify: - c.tmp[0] = alertLevelWarning - default: - c.tmp[0] = alertLevelError - } - c.tmp[1] = byte(err) - - _, writeErr := c.writeRecordLocked(recordTypeAlert, c.tmp[0:2]) - if err == alertCloseNotify { - // closeNotify is a special case in that it isn't an error. - return writeErr - } - - return c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err}) -} - -// sendAlert sends a TLS alert message. -func (c *Conn) sendAlert(err alert) error { - if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil { - c.extraConfig.AlternativeRecordLayer.SendAlert(uint8(err)) - return &net.OpError{Op: "local error", Err: err} - } - - c.out.Lock() - defer c.out.Unlock() - return c.sendAlertLocked(err) -} - -const ( - // tcpMSSEstimate is a conservative estimate of the TCP maximum segment - // size (MSS). A constant is used, rather than querying the kernel for - // the actual MSS, to avoid complexity. The value here is the IPv6 - // minimum MTU (1280 bytes) minus the overhead of an IPv6 header (40 - // bytes) and a TCP header with timestamps (32 bytes). - tcpMSSEstimate = 1208 - - // recordSizeBoostThreshold is the number of bytes of application data - // sent after which the TLS record size will be increased to the - // maximum. - recordSizeBoostThreshold = 128 * 1024 -) - -// maxPayloadSizeForWrite returns the maximum TLS payload size to use for the -// next application data record. There is the following trade-off: -// -// - For latency-sensitive applications, such as web browsing, each TLS -// record should fit in one TCP segment. -// - For throughput-sensitive applications, such as large file transfers, -// larger TLS records better amortize framing and encryption overheads. -// -// A simple heuristic that works well in practice is to use small records for -// the first 1MB of data, then use larger records for subsequent data, and -// reset back to smaller records after the connection becomes idle. See "High -// Performance Web Networking", Chapter 4, or: -// https://www.igvita.com/2013/10/24/optimizing-tls-record-size-and-buffering-latency/ -// -// In the interests of simplicity and determinism, this code does not attempt -// to reset the record size once the connection is idle, however. -func (c *Conn) maxPayloadSizeForWrite(typ recordType) int { - if c.config.DynamicRecordSizingDisabled || typ != recordTypeApplicationData { - return maxPlaintext - } - - if c.bytesSent >= recordSizeBoostThreshold { - return maxPlaintext - } - - // Subtract TLS overheads to get the maximum payload size. - payloadBytes := tcpMSSEstimate - recordHeaderLen - c.out.explicitNonceLen() - if c.out.cipher != nil { - switch ciph := c.out.cipher.(type) { - case cipher.Stream: - payloadBytes -= c.out.mac.Size() - case cipher.AEAD: - payloadBytes -= ciph.Overhead() - case cbcMode: - blockSize := ciph.BlockSize() - // The payload must fit in a multiple of blockSize, with - // room for at least one padding byte. - payloadBytes = (payloadBytes & ^(blockSize - 1)) - 1 - // The MAC is appended before padding so affects the - // payload size directly. - payloadBytes -= c.out.mac.Size() - default: - panic("unknown cipher type") - } - } - if c.vers == VersionTLS13 { - payloadBytes-- // encrypted ContentType - } - - // Allow packet growth in arithmetic progression up to max. - pkt := c.packetsSent - c.packetsSent++ - if pkt > 1000 { - return maxPlaintext // avoid overflow in multiply below - } - - n := payloadBytes * int(pkt+1) - if n > maxPlaintext { - n = maxPlaintext - } - return n -} - -func (c *Conn) write(data []byte) (int, error) { - if c.buffering { - c.sendBuf = append(c.sendBuf, data...) - return len(data), nil - } - - n, err := c.conn.Write(data) - c.bytesSent += int64(n) - return n, err -} - -func (c *Conn) flush() (int, error) { - if len(c.sendBuf) == 0 { - return 0, nil - } - - n, err := c.conn.Write(c.sendBuf) - c.bytesSent += int64(n) - c.sendBuf = nil - c.buffering = false - return n, err -} - -// outBufPool pools the record-sized scratch buffers used by writeRecordLocked. -var outBufPool = sync.Pool{ - New: func() any { - return new([]byte) - }, -} - -// writeRecordLocked writes a TLS record with the given type and payload to the -// connection and updates the record layer state. -func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) { - outBufPtr := outBufPool.Get().(*[]byte) - outBuf := *outBufPtr - defer func() { - // You might be tempted to simplify this by just passing &outBuf to Put, - // but that would make the local copy of the outBuf slice header escape - // to the heap, causing an allocation. Instead, we keep around the - // pointer to the slice header returned by Get, which is already on the - // heap, and overwrite and return that. - *outBufPtr = outBuf - outBufPool.Put(outBufPtr) - }() - - var n int - for len(data) > 0 { - m := len(data) - if maxPayload := c.maxPayloadSizeForWrite(typ); m > maxPayload { - m = maxPayload - } - - _, outBuf = sliceForAppend(outBuf[:0], recordHeaderLen) - outBuf[0] = byte(typ) - vers := c.vers - if vers == 0 { - // Some TLS servers fail if the record version is - // greater than TLS 1.0 for the initial ClientHello. - vers = VersionTLS10 - } else if vers == VersionTLS13 { - // TLS 1.3 froze the record layer version to 1.2. - // See RFC 8446, Section 5.1. - vers = VersionTLS12 - } - outBuf[1] = byte(vers >> 8) - outBuf[2] = byte(vers) - outBuf[3] = byte(m >> 8) - outBuf[4] = byte(m) - - var err error - outBuf, err = c.out.encrypt(outBuf, data[:m], c.config.rand()) - if err != nil { - return n, err - } - if _, err := c.write(outBuf); err != nil { - return n, err - } - n += m - data = data[m:] - } - - if typ == recordTypeChangeCipherSpec && c.vers != VersionTLS13 { - if err := c.out.changeCipherSpec(); err != nil { - return n, c.sendAlertLocked(err.(alert)) - } - } - - return n, nil -} - -// writeHandshakeRecord writes a handshake message to the connection and updates -// the record layer state. If transcript is non-nil the marshalled message is -// written to it. -func (c *Conn) writeHandshakeRecord(msg handshakeMessage, transcript transcriptHash) (int, error) { - data, err := msg.marshal() - if err != nil { - return 0, err - } - - c.out.Lock() - defer c.out.Unlock() - - if transcript != nil { - transcript.Write(data) - } - - if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil { - return c.extraConfig.AlternativeRecordLayer.WriteRecord(data) - } - - return c.writeRecordLocked(recordTypeHandshake, data) -} - -// writeChangeCipherRecord writes a ChangeCipherSpec message to the connection and -// updates the record layer state. -func (c *Conn) writeChangeCipherRecord() error { - if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil { - return nil - } - - c.out.Lock() - defer c.out.Unlock() - _, err := c.writeRecordLocked(recordTypeChangeCipherSpec, []byte{1}) - return err -} - -// readHandshake reads the next handshake message from -// the record layer. If transcript is non-nil, the message -// is written to the passed transcriptHash. -func (c *Conn) readHandshake(transcript transcriptHash) (any, error) { - var data []byte - if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil { - var err error - data, err = c.extraConfig.AlternativeRecordLayer.ReadHandshakeMessage() - if err != nil { - return nil, err - } - } else { - for c.hand.Len() < 4 { - if err := c.readRecord(); err != nil { - return nil, err - } - } - - data = c.hand.Bytes() - n := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) - if n > maxHandshake { - c.sendAlertLocked(alertInternalError) - return nil, c.in.setErrorLocked(fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake)) - } - for c.hand.Len() < 4+n { - if err := c.readRecord(); err != nil { - return nil, err - } - } - data = c.hand.Next(4 + n) - } - var m handshakeMessage - switch data[0] { - case typeHelloRequest: - m = new(helloRequestMsg) - case typeClientHello: - m = new(clientHelloMsg) - case typeServerHello: - m = new(serverHelloMsg) - case typeNewSessionTicket: - if c.vers == VersionTLS13 { - m = new(newSessionTicketMsgTLS13) - } else { - m = new(newSessionTicketMsg) - } - case typeCertificate: - if c.vers == VersionTLS13 { - m = new(certificateMsgTLS13) - } else { - m = new(certificateMsg) - } - case typeCertificateRequest: - if c.vers == VersionTLS13 { - m = new(certificateRequestMsgTLS13) - } else { - m = &certificateRequestMsg{ - hasSignatureAlgorithm: c.vers >= VersionTLS12, - } - } - case typeCertificateStatus: - m = new(certificateStatusMsg) - case typeServerKeyExchange: - m = new(serverKeyExchangeMsg) - case typeServerHelloDone: - m = new(serverHelloDoneMsg) - case typeClientKeyExchange: - m = new(clientKeyExchangeMsg) - case typeCertificateVerify: - m = &certificateVerifyMsg{ - hasSignatureAlgorithm: c.vers >= VersionTLS12, - } - case typeFinished: - m = new(finishedMsg) - case typeEncryptedExtensions: - m = new(encryptedExtensionsMsg) - case typeEndOfEarlyData: - m = new(endOfEarlyDataMsg) - case typeKeyUpdate: - m = new(keyUpdateMsg) - default: - return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - - // The handshake message unmarshalers - // expect to be able to keep references to data, - // so pass in a fresh copy that won't be overwritten. - data = append([]byte(nil), data...) - - if !m.unmarshal(data) { - return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) - } - - if transcript != nil { - transcript.Write(data) - } - - return m, nil -} - -var ( - errShutdown = errors.New("tls: protocol is shutdown") -) - -// Write writes data to the connection. -// -// As Write calls Handshake, in order to prevent indefinite blocking a deadline -// must be set for both Read and Write before Write is called when the handshake -// has not yet completed. See SetDeadline, SetReadDeadline, and -// SetWriteDeadline. -func (c *Conn) Write(b []byte) (int, error) { - // interlock with Close below - for { - x := atomic.LoadInt32(&c.activeCall) - if x&1 != 0 { - return 0, net.ErrClosed - } - if atomic.CompareAndSwapInt32(&c.activeCall, x, x+2) { - break - } - } - defer atomic.AddInt32(&c.activeCall, -2) - - if err := c.Handshake(); err != nil { - return 0, err - } - - c.out.Lock() - defer c.out.Unlock() - - if err := c.out.err; err != nil { - return 0, err - } - - if !c.handshakeComplete() { - return 0, alertInternalError - } - - if c.closeNotifySent { - return 0, errShutdown - } - - // TLS 1.0 is susceptible to a chosen-plaintext - // attack when using block mode ciphers due to predictable IVs. - // This can be prevented by splitting each Application Data - // record into two records, effectively randomizing the IV. - // - // https://www.openssl.org/~bodo/tls-cbc.txt - // https://bugzilla.mozilla.org/show_bug.cgi?id=665814 - // https://www.imperialviolet.org/2012/01/15/beastfollowup.html - - var m int - if len(b) > 1 && c.vers == VersionTLS10 { - if _, ok := c.out.cipher.(cipher.BlockMode); ok { - n, err := c.writeRecordLocked(recordTypeApplicationData, b[:1]) - if err != nil { - return n, c.out.setErrorLocked(err) - } - m, b = 1, b[1:] - } - } - - n, err := c.writeRecordLocked(recordTypeApplicationData, b) - return n + m, c.out.setErrorLocked(err) -} - -// handleRenegotiation processes a HelloRequest handshake message. -func (c *Conn) handleRenegotiation() error { - if c.vers == VersionTLS13 { - return errors.New("tls: internal error: unexpected renegotiation") - } - - msg, err := c.readHandshake(nil) - if err != nil { - return err - } - - helloReq, ok := msg.(*helloRequestMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(helloReq, msg) - } - - if !c.isClient { - return c.sendAlert(alertNoRenegotiation) - } - - switch c.config.Renegotiation { - case RenegotiateNever: - return c.sendAlert(alertNoRenegotiation) - case RenegotiateOnceAsClient: - if c.handshakes > 1 { - return c.sendAlert(alertNoRenegotiation) - } - case RenegotiateFreelyAsClient: - // Ok. - default: - c.sendAlert(alertInternalError) - return errors.New("tls: unknown Renegotiation value") - } - - c.handshakeMutex.Lock() - defer c.handshakeMutex.Unlock() - - atomic.StoreUint32(&c.handshakeStatus, 0) - if c.handshakeErr = c.clientHandshake(context.Background()); c.handshakeErr == nil { - c.handshakes++ - } - return c.handshakeErr -} - -func (c *Conn) HandlePostHandshakeMessage() error { - return c.handlePostHandshakeMessage() -} - -// handlePostHandshakeMessage processes a handshake message arrived after the -// handshake is complete. Up to TLS 1.2, it indicates the start of a renegotiation. -func (c *Conn) handlePostHandshakeMessage() error { - if c.vers != VersionTLS13 { - return c.handleRenegotiation() - } - - msg, err := c.readHandshake(nil) - if err != nil { - return err - } - - c.retryCount++ - if c.retryCount > maxUselessRecords { - c.sendAlert(alertUnexpectedMessage) - return c.in.setErrorLocked(errors.New("tls: too many non-advancing records")) - } - - switch msg := msg.(type) { - case *newSessionTicketMsgTLS13: - return c.handleNewSessionTicket(msg) - case *keyUpdateMsg: - return c.handleKeyUpdate(msg) - default: - c.sendAlert(alertUnexpectedMessage) - return fmt.Errorf("tls: received unexpected handshake message of type %T", msg) - } -} - -func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error { - cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite) - if cipherSuite == nil { - return c.in.setErrorLocked(c.sendAlert(alertInternalError)) - } - - newSecret := cipherSuite.nextTrafficSecret(c.in.trafficSecret) - c.in.setTrafficSecret(cipherSuite, newSecret) - - if keyUpdate.updateRequested { - c.out.Lock() - defer c.out.Unlock() - - msg := &keyUpdateMsg{} - msgBytes, err := msg.marshal() - if err != nil { - return err - } - _, err = c.writeRecordLocked(recordTypeHandshake, msgBytes) - if err != nil { - // Surface the error at the next write. - c.out.setErrorLocked(err) - return nil - } - - newSecret := cipherSuite.nextTrafficSecret(c.out.trafficSecret) - c.out.setTrafficSecret(cipherSuite, newSecret) - } - - return nil -} - -// Read reads data from the connection. -// -// As Read calls Handshake, in order to prevent indefinite blocking a deadline -// must be set for both Read and Write before Read is called when the handshake -// has not yet completed. See SetDeadline, SetReadDeadline, and -// SetWriteDeadline. -func (c *Conn) Read(b []byte) (int, error) { - if err := c.Handshake(); err != nil { - return 0, err - } - if len(b) == 0 { - // Put this after Handshake, in case people were calling - // Read(nil) for the side effect of the Handshake. - return 0, nil - } - - c.in.Lock() - defer c.in.Unlock() - - for c.input.Len() == 0 { - if err := c.readRecord(); err != nil { - return 0, err - } - for c.hand.Len() > 0 { - if err := c.handlePostHandshakeMessage(); err != nil { - return 0, err - } - } - } - - n, _ := c.input.Read(b) - - // If a close-notify alert is waiting, read it so that we can return (n, - // EOF) instead of (n, nil), to signal to the HTTP response reading - // goroutine that the connection is now closed. This eliminates a race - // where the HTTP response reading goroutine would otherwise not observe - // the EOF until its next read, by which time a client goroutine might - // have already tried to reuse the HTTP connection for a new request. - // See https://golang.org/cl/76400046 and https://golang.org/issue/3514 - if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 && - recordType(c.rawInput.Bytes()[0]) == recordTypeAlert { - if err := c.readRecord(); err != nil { - return n, err // will be io.EOF on closeNotify - } - } - - return n, nil -} - -// Close closes the connection. -func (c *Conn) Close() error { - // Interlock with Conn.Write above. - var x int32 - for { - x = atomic.LoadInt32(&c.activeCall) - if x&1 != 0 { - return net.ErrClosed - } - if atomic.CompareAndSwapInt32(&c.activeCall, x, x|1) { - break - } - } - if x != 0 { - // io.Writer and io.Closer should not be used concurrently. - // If Close is called while a Write is currently in-flight, - // interpret that as a sign that this Close is really just - // being used to break the Write and/or clean up resources and - // avoid sending the alertCloseNotify, which may block - // waiting on handshakeMutex or the c.out mutex. - return c.conn.Close() - } - - var alertErr error - if c.handshakeComplete() { - if err := c.closeNotify(); err != nil { - alertErr = fmt.Errorf("tls: failed to send closeNotify alert (but connection was closed anyway): %w", err) - } - } - - if err := c.conn.Close(); err != nil { - return err - } - return alertErr -} - -var errEarlyCloseWrite = errors.New("tls: CloseWrite called before handshake complete") - -// CloseWrite shuts down the writing side of the connection. It should only be -// called once the handshake has completed and does not call CloseWrite on the -// underlying connection. Most callers should just use Close. -func (c *Conn) CloseWrite() error { - if !c.handshakeComplete() { - return errEarlyCloseWrite - } - - return c.closeNotify() -} - -func (c *Conn) closeNotify() error { - c.out.Lock() - defer c.out.Unlock() - - if !c.closeNotifySent { - // Set a Write Deadline to prevent possibly blocking forever. - c.SetWriteDeadline(time.Now().Add(time.Second * 5)) - c.closeNotifyErr = c.sendAlertLocked(alertCloseNotify) - c.closeNotifySent = true - // Any subsequent writes will fail. - c.SetWriteDeadline(time.Now()) - } - return c.closeNotifyErr -} - -// Handshake runs the client or server handshake -// protocol if it has not yet been run. -// -// Most uses of this package need not call Handshake explicitly: the -// first Read or Write will call it automatically. -// -// For control over canceling or setting a timeout on a handshake, use -// HandshakeContext or the Dialer's DialContext method instead. -func (c *Conn) Handshake() error { - return c.HandshakeContext(context.Background()) -} - -// HandshakeContext runs the client or server handshake -// protocol if it has not yet been run. -// -// The provided Context must be non-nil. If the context is canceled before -// the handshake is complete, the handshake is interrupted and an error is returned. -// Once the handshake has completed, cancellation of the context will not affect the -// connection. -// -// Most uses of this package need not call HandshakeContext explicitly: the -// first Read or Write will call it automatically. -func (c *Conn) HandshakeContext(ctx context.Context) error { - // Delegate to unexported method for named return - // without confusing documented signature. - return c.handshakeContext(ctx) -} - -func (c *Conn) handshakeContext(ctx context.Context) (ret error) { - // Fast sync/atomic-based exit if there is no handshake in flight and the - // last one succeeded without an error. Avoids the expensive context setup - // and mutex for most Read and Write calls. - if c.handshakeComplete() { - return nil - } - - handshakeCtx, cancel := context.WithCancel(ctx) - // Note: defer this before starting the "interrupter" goroutine - // so that we can tell the difference between the input being canceled and - // this cancellation. In the former case, we need to close the connection. - defer cancel() - - // Start the "interrupter" goroutine, if this context might be canceled. - // (The background context cannot). - // - // The interrupter goroutine waits for the input context to be done and - // closes the connection if this happens before the function returns. - if ctx.Done() != nil { - done := make(chan struct{}) - interruptRes := make(chan error, 1) - defer func() { - close(done) - if ctxErr := <-interruptRes; ctxErr != nil { - // Return context error to user. - ret = ctxErr - } - }() - go func() { - select { - case <-handshakeCtx.Done(): - // Close the connection, discarding the error - _ = c.conn.Close() - interruptRes <- handshakeCtx.Err() - case <-done: - interruptRes <- nil - } - }() - } - - c.handshakeMutex.Lock() - defer c.handshakeMutex.Unlock() - - if err := c.handshakeErr; err != nil { - return err - } - if c.handshakeComplete() { - return nil - } - - c.in.Lock() - defer c.in.Unlock() - - c.handshakeErr = c.handshakeFn(handshakeCtx) - if c.handshakeErr == nil { - c.handshakes++ - } else { - // If an error occurred during the handshake try to flush the - // alert that might be left in the buffer. - c.flush() - } - - if c.handshakeErr == nil && !c.handshakeComplete() { - c.handshakeErr = errors.New("tls: internal error: handshake should have had a result") - } - if c.handshakeErr != nil && c.handshakeComplete() { - panic("tls: internal error: handshake returned an error but is marked successful") - } - - return c.handshakeErr -} - -// ConnectionState returns basic TLS details about the connection. -func (c *Conn) ConnectionState() ConnectionState { - c.connStateMutex.Lock() - defer c.connStateMutex.Unlock() - return c.connState.ConnectionState -} - -// ConnectionStateWith0RTT returns basic TLS details (incl. 0-RTT status) about the connection. -func (c *Conn) ConnectionStateWith0RTT() ConnectionStateWith0RTT { - c.connStateMutex.Lock() - defer c.connStateMutex.Unlock() - return c.connState -} - -func (c *Conn) connectionStateLocked() ConnectionState { - var state connectionState - state.HandshakeComplete = c.handshakeComplete() - state.Version = c.vers - state.NegotiatedProtocol = c.clientProtocol - state.DidResume = c.didResume - state.NegotiatedProtocolIsMutual = true - state.ServerName = c.serverName - state.CipherSuite = c.cipherSuite - state.PeerCertificates = c.peerCertificates - state.VerifiedChains = c.verifiedChains - state.SignedCertificateTimestamps = c.scts - state.OCSPResponse = c.ocspResponse - if !c.didResume && c.vers != VersionTLS13 { - if c.clientFinishedIsFirst { - state.TLSUnique = c.clientFinished[:] - } else { - state.TLSUnique = c.serverFinished[:] - } - } - if c.config.Renegotiation != RenegotiateNever { - state.ekm = noExportedKeyingMaterial - } else { - state.ekm = c.ekm - } - return toConnectionState(state) -} - -func (c *Conn) updateConnectionState() { - c.connStateMutex.Lock() - defer c.connStateMutex.Unlock() - c.connState = ConnectionStateWith0RTT{ - Used0RTT: c.used0RTT, - ConnectionState: c.connectionStateLocked(), - } -} - -// OCSPResponse returns the stapled OCSP response from the TLS server, if -// any. (Only valid for client connections.) -func (c *Conn) OCSPResponse() []byte { - c.handshakeMutex.Lock() - defer c.handshakeMutex.Unlock() - - return c.ocspResponse -} - -// VerifyHostname checks that the peer certificate chain is valid for -// connecting to host. If so, it returns nil; if not, it returns an error -// describing the problem. -func (c *Conn) VerifyHostname(host string) error { - c.handshakeMutex.Lock() - defer c.handshakeMutex.Unlock() - if !c.isClient { - return errors.New("tls: VerifyHostname called on TLS server connection") - } - if !c.handshakeComplete() { - return errors.New("tls: handshake has not yet been performed") - } - if len(c.verifiedChains) == 0 { - return errors.New("tls: handshake did not verify certificate chain") - } - return c.peerCertificates[0].VerifyHostname(host) -} - -func (c *Conn) handshakeComplete() bool { - return atomic.LoadUint32(&c.handshakeStatus) == 1 -} diff --git a/vendor/github.com/quic-go/qtls-go1-19/cpu.go b/vendor/github.com/quic-go/qtls-go1-19/cpu.go deleted file mode 100644 index 121945087..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/cpu.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build !js -// +build !js - -package qtls - -import ( - "runtime" - - "golang.org/x/sys/cpu" -) - -var ( - hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ - hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL - // Keep in sync with crypto/aes/cipher_s390x.go. - hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && - (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM) - - hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 || - runtime.GOARCH == "arm64" && hasGCMAsmARM64 || - runtime.GOARCH == "s390x" && hasGCMAsmS390X -) diff --git a/vendor/github.com/quic-go/qtls-go1-19/cpu_other.go b/vendor/github.com/quic-go/qtls-go1-19/cpu_other.go deleted file mode 100644 index 33f7d2194..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/cpu_other.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build js -// +build js - -package qtls - -var ( - hasGCMAsmAMD64 = false - hasGCMAsmARM64 = false - hasGCMAsmS390X = false - - hasAESGCMHardwareSupport = false -) diff --git a/vendor/github.com/quic-go/qtls-go1-19/handshake_client.go b/vendor/github.com/quic-go/qtls-go1-19/handshake_client.go deleted file mode 100644 index 8d1fae015..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/handshake_client.go +++ /dev/null @@ -1,1131 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package qtls - -import ( - "bytes" - "context" - "crypto" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/rsa" - "crypto/subtle" - "crypto/x509" - "errors" - "fmt" - "hash" - "io" - "net" - "strings" - "sync/atomic" - "time" - - "golang.org/x/crypto/cryptobyte" -) - -const clientSessionStateVersion = 1 - -type clientHandshakeState struct { - c *Conn - ctx context.Context - serverHello *serverHelloMsg - hello *clientHelloMsg - suite *cipherSuite - finishedHash finishedHash - masterSecret []byte - session *clientSessionState -} - -var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme - -func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) { - config := c.config - if len(config.ServerName) == 0 && !config.InsecureSkipVerify { - return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config") - } - - nextProtosLength := 0 - for _, proto := range config.NextProtos { - if l := len(proto); l == 0 || l > 255 { - return nil, nil, errors.New("tls: invalid NextProtos value") - } else { - nextProtosLength += 1 + l - } - } - if nextProtosLength > 0xffff { - return nil, nil, errors.New("tls: NextProtos values too large") - } - - var supportedVersions []uint16 - var clientHelloVersion uint16 - if c.extraConfig.usesAlternativeRecordLayer() { - if config.maxSupportedVersion(roleClient) < VersionTLS13 { - return nil, nil, errors.New("tls: MaxVersion prevents QUIC from using TLS 1.3") - } - // Only offer TLS 1.3 when QUIC is used. - supportedVersions = []uint16{VersionTLS13} - clientHelloVersion = VersionTLS13 - } else { - supportedVersions = config.supportedVersions(roleClient) - if len(supportedVersions) == 0 { - return nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion") - } - clientHelloVersion = config.maxSupportedVersion(roleClient) - } - - // The version at the beginning of the ClientHello was capped at TLS 1.2 - // for compatibility reasons. The supported_versions extension is used - // to negotiate versions now. See RFC 8446, Section 4.2.1. - if clientHelloVersion > VersionTLS12 { - clientHelloVersion = VersionTLS12 - } - - hello := &clientHelloMsg{ - vers: clientHelloVersion, - compressionMethods: []uint8{compressionNone}, - random: make([]byte, 32), - ocspStapling: true, - scts: true, - serverName: hostnameInSNI(config.ServerName), - supportedCurves: config.curvePreferences(), - supportedPoints: []uint8{pointFormatUncompressed}, - secureRenegotiationSupported: true, - alpnProtocols: config.NextProtos, - supportedVersions: supportedVersions, - } - - if c.handshakes > 0 { - hello.secureRenegotiation = c.clientFinished[:] - } - - preferenceOrder := cipherSuitesPreferenceOrder - if !hasAESGCMHardwareSupport { - preferenceOrder = cipherSuitesPreferenceOrderNoAES - } - configCipherSuites := config.cipherSuites() - hello.cipherSuites = make([]uint16, 0, len(configCipherSuites)) - - for _, suiteId := range preferenceOrder { - suite := mutualCipherSuite(configCipherSuites, suiteId) - if suite == nil { - continue - } - // Don't advertise TLS 1.2-only cipher suites unless - // we're attempting TLS 1.2. - if hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 { - continue - } - hello.cipherSuites = append(hello.cipherSuites, suiteId) - } - - _, err := io.ReadFull(config.rand(), hello.random) - if err != nil { - return nil, nil, errors.New("tls: short read from Rand: " + err.Error()) - } - - // A random session ID is used to detect when the server accepted a ticket - // and is resuming a session (see RFC 5077). In TLS 1.3, it's always set as - // a compatibility measure (see RFC 8446, Section 4.1.2). - if c.extraConfig == nil || c.extraConfig.AlternativeRecordLayer == nil { - hello.sessionId = make([]byte, 32) - if _, err := io.ReadFull(config.rand(), hello.sessionId); err != nil { - return nil, nil, errors.New("tls: short read from Rand: " + err.Error()) - } - } - - if hello.vers >= VersionTLS12 { - hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms() - } - if testingOnlyForceClientHelloSignatureAlgorithms != nil { - hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms - } - - var params ecdheParameters - if hello.supportedVersions[0] == VersionTLS13 { - if len(hello.supportedVersions) == 1 { - hello.cipherSuites = hello.cipherSuites[:0] - } - if hasAESGCMHardwareSupport { - hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13...) - } else { - hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13NoAES...) - } - - curveID := config.curvePreferences()[0] - if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { - return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve") - } - params, err = generateECDHEParameters(config.rand(), curveID) - if err != nil { - return nil, nil, err - } - hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} - } - - if hello.supportedVersions[0] == VersionTLS13 && c.extraConfig != nil && c.extraConfig.GetExtensions != nil { - hello.additionalExtensions = c.extraConfig.GetExtensions(typeClientHello) - } - - return hello, params, nil -} - -func (c *Conn) clientHandshake(ctx context.Context) (err error) { - if c.config == nil { - c.config = fromConfig(defaultConfig()) - } - c.setAlternativeRecordLayer() - - // This may be a renegotiation handshake, in which case some fields - // need to be reset. - c.didResume = false - - hello, ecdheParams, err := c.makeClientHello() - if err != nil { - return err - } - c.serverName = hello.serverName - - cacheKey, session, earlySecret, binderKey, err := c.loadSession(hello) - if err != nil { - return err - } - if cacheKey != "" && session != nil { - var deletedTicket bool - if session.vers == VersionTLS13 && hello.earlyData && c.extraConfig != nil && c.extraConfig.Enable0RTT { - // don't reuse a session ticket that enabled 0-RTT - c.config.ClientSessionCache.Put(cacheKey, nil) - deletedTicket = true - - if suite := cipherSuiteTLS13ByID(session.cipherSuite); suite != nil { - h := suite.hash.New() - helloBytes, err := hello.marshal() - if err != nil { - return err - } - h.Write(helloBytes) - clientEarlySecret := suite.deriveSecret(earlySecret, "c e traffic", h) - c.out.exportKey(Encryption0RTT, suite, clientEarlySecret) - if err := c.config.writeKeyLog(keyLogLabelEarlyTraffic, hello.random, clientEarlySecret); err != nil { - return err - } - } - } - if !deletedTicket { - defer func() { - // If we got a handshake failure when resuming a session, throw away - // the session ticket. See RFC 5077, Section 3.2. - // - // RFC 8446 makes no mention of dropping tickets on failure, but it - // does require servers to abort on invalid binders, so we need to - // delete tickets to recover from a corrupted PSK. - if err != nil { - c.config.ClientSessionCache.Put(cacheKey, nil) - } - }() - } - } - - if _, err := c.writeHandshakeRecord(hello, nil); err != nil { - return err - } - - // serverHelloMsg is not included in the transcript - msg, err := c.readHandshake(nil) - if err != nil { - return err - } - - serverHello, ok := msg.(*serverHelloMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(serverHello, msg) - } - - if err := c.pickTLSVersion(serverHello); err != nil { - return err - } - - // If we are negotiating a protocol version that's lower than what we - // support, check for the server downgrade canaries. - // See RFC 8446, Section 4.1.3. - maxVers := c.config.maxSupportedVersion(roleClient) - tls12Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS12 - tls11Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS11 - if maxVers == VersionTLS13 && c.vers <= VersionTLS12 && (tls12Downgrade || tls11Downgrade) || - maxVers == VersionTLS12 && c.vers <= VersionTLS11 && tls11Downgrade { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: downgrade attempt detected, possibly due to a MitM attack or a broken middlebox") - } - - if c.vers == VersionTLS13 { - hs := &clientHandshakeStateTLS13{ - c: c, - ctx: ctx, - serverHello: serverHello, - hello: hello, - ecdheParams: ecdheParams, - session: session, - earlySecret: earlySecret, - binderKey: binderKey, - } - - // In TLS 1.3, session tickets are delivered after the handshake. - return hs.handshake() - } - - hs := &clientHandshakeState{ - c: c, - ctx: ctx, - serverHello: serverHello, - hello: hello, - session: session, - } - - if err := hs.handshake(); err != nil { - return err - } - - // If we had a successful handshake and hs.session is different from - // the one already cached - cache a new one. - if cacheKey != "" && hs.session != nil && session != hs.session { - c.config.ClientSessionCache.Put(cacheKey, toClientSessionState(hs.session)) - } - - c.updateConnectionState() - return nil -} - -// extract the app data saved in the session.nonce, -// and set the session.nonce to the actual nonce value -func (c *Conn) decodeSessionState(session *clientSessionState) (uint32 /* max early data */, []byte /* app data */, bool /* ok */) { - s := cryptobyte.String(session.nonce) - var version uint16 - if !s.ReadUint16(&version) { - return 0, nil, false - } - if version != clientSessionStateVersion { - return 0, nil, false - } - var maxEarlyData uint32 - if !s.ReadUint32(&maxEarlyData) { - return 0, nil, false - } - var appData []byte - if !readUint16LengthPrefixed(&s, &appData) { - return 0, nil, false - } - var nonce []byte - if !readUint16LengthPrefixed(&s, &nonce) { - return 0, nil, false - } - session.nonce = nonce - return maxEarlyData, appData, true -} - -func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, - session *clientSessionState, earlySecret, binderKey []byte, err error) { - if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil { - return "", nil, nil, nil, nil - } - - hello.ticketSupported = true - - if hello.supportedVersions[0] == VersionTLS13 { - // Require DHE on resumption as it guarantees forward secrecy against - // compromise of the session ticket key. See RFC 8446, Section 4.2.9. - hello.pskModes = []uint8{pskModeDHE} - } - - // Session resumption is not allowed if renegotiating because - // renegotiation is primarily used to allow a client to send a client - // certificate, which would be skipped if session resumption occurred. - if c.handshakes != 0 { - return "", nil, nil, nil, nil - } - - // Try to resume a previously negotiated TLS session, if available. - cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config) - sess, ok := c.config.ClientSessionCache.Get(cacheKey) - if !ok || sess == nil { - return cacheKey, nil, nil, nil, nil - } - session = fromClientSessionState(sess) - - var appData []byte - var maxEarlyData uint32 - if session.vers == VersionTLS13 { - var ok bool - maxEarlyData, appData, ok = c.decodeSessionState(session) - if !ok { // delete it, if parsing failed - c.config.ClientSessionCache.Put(cacheKey, nil) - return cacheKey, nil, nil, nil, nil - } - } - - // Check that version used for the previous session is still valid. - versOk := false - for _, v := range hello.supportedVersions { - if v == session.vers { - versOk = true - break - } - } - if !versOk { - return cacheKey, nil, nil, nil, nil - } - - // Check that the cached server certificate is not expired, and that it's - // valid for the ServerName. This should be ensured by the cache key, but - // protect the application from a faulty ClientSessionCache implementation. - if !c.config.InsecureSkipVerify { - if len(session.verifiedChains) == 0 { - // The original connection had InsecureSkipVerify, while this doesn't. - return cacheKey, nil, nil, nil, nil - } - serverCert := session.serverCertificates[0] - if c.config.time().After(serverCert.NotAfter) { - // Expired certificate, delete the entry. - c.config.ClientSessionCache.Put(cacheKey, nil) - return cacheKey, nil, nil, nil, nil - } - if err := serverCert.VerifyHostname(c.config.ServerName); err != nil { - return cacheKey, nil, nil, nil, nil - } - } - - if session.vers != VersionTLS13 { - // In TLS 1.2 the cipher suite must match the resumed session. Ensure we - // are still offering it. - if mutualCipherSuite(hello.cipherSuites, session.cipherSuite) == nil { - return cacheKey, nil, nil, nil, nil - } - - hello.sessionTicket = session.sessionTicket - return - } - - // Check that the session ticket is not expired. - if c.config.time().After(session.useBy) { - c.config.ClientSessionCache.Put(cacheKey, nil) - return cacheKey, nil, nil, nil, nil - } - - // In TLS 1.3 the KDF hash must match the resumed session. Ensure we - // offer at least one cipher suite with that hash. - cipherSuite := cipherSuiteTLS13ByID(session.cipherSuite) - if cipherSuite == nil { - return cacheKey, nil, nil, nil, nil - } - cipherSuiteOk := false - for _, offeredID := range hello.cipherSuites { - offeredSuite := cipherSuiteTLS13ByID(offeredID) - if offeredSuite != nil && offeredSuite.hash == cipherSuite.hash { - cipherSuiteOk = true - break - } - } - if !cipherSuiteOk { - return cacheKey, nil, nil, nil, nil - } - - // Set the pre_shared_key extension. See RFC 8446, Section 4.2.11.1. - ticketAge := uint32(c.config.time().Sub(session.receivedAt) / time.Millisecond) - identity := pskIdentity{ - label: session.sessionTicket, - obfuscatedTicketAge: ticketAge + session.ageAdd, - } - hello.pskIdentities = []pskIdentity{identity} - hello.pskBinders = [][]byte{make([]byte, cipherSuite.hash.Size())} - - // Compute the PSK binders. See RFC 8446, Section 4.2.11.2. - psk := cipherSuite.expandLabel(session.masterSecret, "resumption", - session.nonce, cipherSuite.hash.Size()) - earlySecret = cipherSuite.extract(psk, nil) - binderKey = cipherSuite.deriveSecret(earlySecret, resumptionBinderLabel, nil) - if c.extraConfig != nil { - hello.earlyData = c.extraConfig.Enable0RTT && maxEarlyData > 0 - } - transcript := cipherSuite.hash.New() - helloBytes, err := hello.marshalWithoutBinders() - if err != nil { - return "", nil, nil, nil, err - } - transcript.Write(helloBytes) - pskBinders := [][]byte{cipherSuite.finishedHash(binderKey, transcript)} - if err := hello.updateBinders(pskBinders); err != nil { - return "", nil, nil, nil, err - } - - if session.vers == VersionTLS13 && c.extraConfig != nil && c.extraConfig.SetAppDataFromSessionState != nil { - c.extraConfig.SetAppDataFromSessionState(appData) - } - return -} - -func (c *Conn) pickTLSVersion(serverHello *serverHelloMsg) error { - peerVersion := serverHello.vers - if serverHello.supportedVersion != 0 { - peerVersion = serverHello.supportedVersion - } - - vers, ok := c.config.mutualVersion(roleClient, []uint16{peerVersion}) - if !ok { - c.sendAlert(alertProtocolVersion) - return fmt.Errorf("tls: server selected unsupported protocol version %x", peerVersion) - } - - c.vers = vers - c.haveVers = true - c.in.version = vers - c.out.version = vers - - return nil -} - -// Does the handshake, either a full one or resumes old session. Requires hs.c, -// hs.hello, hs.serverHello, and, optionally, hs.session to be set. -func (hs *clientHandshakeState) handshake() error { - c := hs.c - - isResume, err := hs.processServerHello() - if err != nil { - return err - } - - hs.finishedHash = newFinishedHash(c.vers, hs.suite) - - // No signatures of the handshake are needed in a resumption. - // Otherwise, in a full handshake, if we don't have any certificates - // configured then we will never send a CertificateVerify message and - // thus no signatures are needed in that case either. - if isResume || (len(c.config.Certificates) == 0 && c.config.GetClientCertificate == nil) { - hs.finishedHash.discardHandshakeBuffer() - } - - if err := transcriptMsg(hs.hello, &hs.finishedHash); err != nil { - return err - } - if err := transcriptMsg(hs.serverHello, &hs.finishedHash); err != nil { - return err - } - - c.buffering = true - c.didResume = isResume - if isResume { - if err := hs.establishKeys(); err != nil { - return err - } - if err := hs.readSessionTicket(); err != nil { - return err - } - if err := hs.readFinished(c.serverFinished[:]); err != nil { - return err - } - c.clientFinishedIsFirst = false - // Make sure the connection is still being verified whether or not this - // is a resumption. Resumptions currently don't reverify certificates so - // they don't call verifyServerCertificate. See Issue 31641. - if c.config.VerifyConnection != nil { - if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - if err := hs.sendFinished(c.clientFinished[:]); err != nil { - return err - } - if _, err := c.flush(); err != nil { - return err - } - } else { - if err := hs.doFullHandshake(); err != nil { - return err - } - if err := hs.establishKeys(); err != nil { - return err - } - if err := hs.sendFinished(c.clientFinished[:]); err != nil { - return err - } - if _, err := c.flush(); err != nil { - return err - } - c.clientFinishedIsFirst = true - if err := hs.readSessionTicket(); err != nil { - return err - } - if err := hs.readFinished(c.serverFinished[:]); err != nil { - return err - } - } - - c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random) - atomic.StoreUint32(&c.handshakeStatus, 1) - - return nil -} - -func (hs *clientHandshakeState) pickCipherSuite() error { - if hs.suite = mutualCipherSuite(hs.hello.cipherSuites, hs.serverHello.cipherSuite); hs.suite == nil { - hs.c.sendAlert(alertHandshakeFailure) - return errors.New("tls: server chose an unconfigured cipher suite") - } - - hs.c.cipherSuite = hs.suite.id - return nil -} - -func (hs *clientHandshakeState) doFullHandshake() error { - c := hs.c - - msg, err := c.readHandshake(&hs.finishedHash) - if err != nil { - return err - } - certMsg, ok := msg.(*certificateMsg) - if !ok || len(certMsg.certificates) == 0 { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certMsg, msg) - } - - msg, err = c.readHandshake(&hs.finishedHash) - if err != nil { - return err - } - - cs, ok := msg.(*certificateStatusMsg) - if ok { - // RFC4366 on Certificate Status Request: - // The server MAY return a "certificate_status" message. - - if !hs.serverHello.ocspStapling { - // If a server returns a "CertificateStatus" message, then the - // server MUST have included an extension of type "status_request" - // with empty "extension_data" in the extended server hello. - - c.sendAlert(alertUnexpectedMessage) - return errors.New("tls: received unexpected CertificateStatus message") - } - - c.ocspResponse = cs.response - - msg, err = c.readHandshake(&hs.finishedHash) - if err != nil { - return err - } - } - - if c.handshakes == 0 { - // If this is the first handshake on a connection, process and - // (optionally) verify the server's certificates. - if err := c.verifyServerCertificate(certMsg.certificates); err != nil { - return err - } - } else { - // This is a renegotiation handshake. We require that the - // server's identity (i.e. leaf certificate) is unchanged and - // thus any previous trust decision is still valid. - // - // See https://mitls.org/pages/attacks/3SHAKE for the - // motivation behind this requirement. - if !bytes.Equal(c.peerCertificates[0].Raw, certMsg.certificates[0]) { - c.sendAlert(alertBadCertificate) - return errors.New("tls: server's identity changed during renegotiation") - } - } - - keyAgreement := hs.suite.ka(c.vers) - - skx, ok := msg.(*serverKeyExchangeMsg) - if ok { - err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, c.peerCertificates[0], skx) - if err != nil { - c.sendAlert(alertUnexpectedMessage) - return err - } - - msg, err = c.readHandshake(&hs.finishedHash) - if err != nil { - return err - } - } - - var chainToSend *Certificate - var certRequested bool - certReq, ok := msg.(*certificateRequestMsg) - if ok { - certRequested = true - - cri := certificateRequestInfoFromMsg(hs.ctx, c.vers, certReq) - if chainToSend, err = c.getClientCertificate(cri); err != nil { - c.sendAlert(alertInternalError) - return err - } - - msg, err = c.readHandshake(&hs.finishedHash) - if err != nil { - return err - } - } - - shd, ok := msg.(*serverHelloDoneMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(shd, msg) - } - - // If the server requested a certificate then we have to send a - // Certificate message, even if it's empty because we don't have a - // certificate to send. - if certRequested { - certMsg = new(certificateMsg) - certMsg.certificates = chainToSend.Certificate - if _, err := hs.c.writeHandshakeRecord(certMsg, &hs.finishedHash); err != nil { - return err - } - } - - preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, c.peerCertificates[0]) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - if ckx != nil { - if _, err := hs.c.writeHandshakeRecord(ckx, &hs.finishedHash); err != nil { - return err - } - } - - if chainToSend != nil && len(chainToSend.Certificate) > 0 { - certVerify := &certificateVerifyMsg{} - - key, ok := chainToSend.PrivateKey.(crypto.Signer) - if !ok { - c.sendAlert(alertInternalError) - return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey) - } - - var sigType uint8 - var sigHash crypto.Hash - if c.vers >= VersionTLS12 { - signatureAlgorithm, err := selectSignatureScheme(c.vers, chainToSend, certReq.supportedSignatureAlgorithms) - if err != nil { - c.sendAlert(alertIllegalParameter) - return err - } - sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm) - if err != nil { - return c.sendAlert(alertInternalError) - } - certVerify.hasSignatureAlgorithm = true - certVerify.signatureAlgorithm = signatureAlgorithm - } else { - sigType, sigHash, err = legacyTypeAndHashFromPublicKey(key.Public()) - if err != nil { - c.sendAlert(alertIllegalParameter) - return err - } - } - - signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash, hs.masterSecret) - signOpts := crypto.SignerOpts(sigHash) - if sigType == signatureRSAPSS { - signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash} - } - certVerify.signature, err = key.Sign(c.config.rand(), signed, signOpts) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - - if _, err := hs.c.writeHandshakeRecord(certVerify, &hs.finishedHash); err != nil { - return err - } - } - - hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random) - if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.hello.random, hs.masterSecret); err != nil { - c.sendAlert(alertInternalError) - return errors.New("tls: failed to write to key log: " + err.Error()) - } - - hs.finishedHash.discardHandshakeBuffer() - - return nil -} - -func (hs *clientHandshakeState) establishKeys() error { - c := hs.c - - clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := - keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen) - var clientCipher, serverCipher any - var clientHash, serverHash hash.Hash - if hs.suite.cipher != nil { - clientCipher = hs.suite.cipher(clientKey, clientIV, false /* not for reading */) - clientHash = hs.suite.mac(clientMAC) - serverCipher = hs.suite.cipher(serverKey, serverIV, true /* for reading */) - serverHash = hs.suite.mac(serverMAC) - } else { - clientCipher = hs.suite.aead(clientKey, clientIV) - serverCipher = hs.suite.aead(serverKey, serverIV) - } - - c.in.prepareCipherSpec(c.vers, serverCipher, serverHash) - c.out.prepareCipherSpec(c.vers, clientCipher, clientHash) - return nil -} - -func (hs *clientHandshakeState) serverResumedSession() bool { - // If the server responded with the same sessionId then it means the - // sessionTicket is being used to resume a TLS session. - return hs.session != nil && hs.hello.sessionId != nil && - bytes.Equal(hs.serverHello.sessionId, hs.hello.sessionId) -} - -func (hs *clientHandshakeState) processServerHello() (bool, error) { - c := hs.c - - if err := hs.pickCipherSuite(); err != nil { - return false, err - } - - if hs.serverHello.compressionMethod != compressionNone { - c.sendAlert(alertUnexpectedMessage) - return false, errors.New("tls: server selected unsupported compression format") - } - - if c.handshakes == 0 && hs.serverHello.secureRenegotiationSupported { - c.secureRenegotiation = true - if len(hs.serverHello.secureRenegotiation) != 0 { - c.sendAlert(alertHandshakeFailure) - return false, errors.New("tls: initial handshake had non-empty renegotiation extension") - } - } - - if c.handshakes > 0 && c.secureRenegotiation { - var expectedSecureRenegotiation [24]byte - copy(expectedSecureRenegotiation[:], c.clientFinished[:]) - copy(expectedSecureRenegotiation[12:], c.serverFinished[:]) - if !bytes.Equal(hs.serverHello.secureRenegotiation, expectedSecureRenegotiation[:]) { - c.sendAlert(alertHandshakeFailure) - return false, errors.New("tls: incorrect renegotiation extension contents") - } - } - - if err := checkALPN(hs.hello.alpnProtocols, hs.serverHello.alpnProtocol); err != nil { - c.sendAlert(alertUnsupportedExtension) - return false, err - } - c.clientProtocol = hs.serverHello.alpnProtocol - - c.scts = hs.serverHello.scts - - if !hs.serverResumedSession() { - return false, nil - } - - if hs.session.vers != c.vers { - c.sendAlert(alertHandshakeFailure) - return false, errors.New("tls: server resumed a session with a different version") - } - - if hs.session.cipherSuite != hs.suite.id { - c.sendAlert(alertHandshakeFailure) - return false, errors.New("tls: server resumed a session with a different cipher suite") - } - - // Restore masterSecret, peerCerts, and ocspResponse from previous state - hs.masterSecret = hs.session.masterSecret - c.peerCertificates = hs.session.serverCertificates - c.verifiedChains = hs.session.verifiedChains - c.ocspResponse = hs.session.ocspResponse - // Let the ServerHello SCTs override the session SCTs from the original - // connection, if any are provided - if len(c.scts) == 0 && len(hs.session.scts) != 0 { - c.scts = hs.session.scts - } - - return true, nil -} - -// checkALPN ensure that the server's choice of ALPN protocol is compatible with -// the protocols that we advertised in the Client Hello. -func checkALPN(clientProtos []string, serverProto string) error { - if serverProto == "" { - return nil - } - if len(clientProtos) == 0 { - return errors.New("tls: server advertised unrequested ALPN extension") - } - for _, proto := range clientProtos { - if proto == serverProto { - return nil - } - } - return errors.New("tls: server selected unadvertised ALPN protocol") -} - -func (hs *clientHandshakeState) readFinished(out []byte) error { - c := hs.c - - if err := c.readChangeCipherSpec(); err != nil { - return err - } - - // finishedMsg is included in the transcript, but not until after we - // check the client version, since the state before this message was - // sent is used during verification. - msg, err := c.readHandshake(nil) - if err != nil { - return err - } - serverFinished, ok := msg.(*finishedMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(serverFinished, msg) - } - - verify := hs.finishedHash.serverSum(hs.masterSecret) - if len(verify) != len(serverFinished.verifyData) || - subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: server's Finished message was incorrect") - } - - if err := transcriptMsg(serverFinished, &hs.finishedHash); err != nil { - return err - } - - copy(out, verify) - return nil -} - -func (hs *clientHandshakeState) readSessionTicket() error { - if !hs.serverHello.ticketSupported { - return nil - } - - c := hs.c - msg, err := c.readHandshake(&hs.finishedHash) - if err != nil { - return err - } - sessionTicketMsg, ok := msg.(*newSessionTicketMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(sessionTicketMsg, msg) - } - - hs.session = &clientSessionState{ - sessionTicket: sessionTicketMsg.ticket, - vers: c.vers, - cipherSuite: hs.suite.id, - masterSecret: hs.masterSecret, - serverCertificates: c.peerCertificates, - verifiedChains: c.verifiedChains, - receivedAt: c.config.time(), - ocspResponse: c.ocspResponse, - scts: c.scts, - } - - return nil -} - -func (hs *clientHandshakeState) sendFinished(out []byte) error { - c := hs.c - - if err := c.writeChangeCipherRecord(); err != nil { - return err - } - - finished := new(finishedMsg) - finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret) - if _, err := hs.c.writeHandshakeRecord(finished, &hs.finishedHash); err != nil { - return err - } - copy(out, finished.verifyData) - return nil -} - -// maxRSAKeySize is the maximum RSA key size in bits that we are willing -// to verify the signatures of during a TLS handshake. -const maxRSAKeySize = 8192 - -// verifyServerCertificate parses and verifies the provided chain, setting -// c.verifiedChains and c.peerCertificates or sending the appropriate alert. -func (c *Conn) verifyServerCertificate(certificates [][]byte) error { - certs := make([]*x509.Certificate, len(certificates)) - for i, asn1Data := range certificates { - cert, err := x509.ParseCertificate(asn1Data) - if err != nil { - c.sendAlert(alertBadCertificate) - return errors.New("tls: failed to parse certificate from server: " + err.Error()) - } - if cert.PublicKeyAlgorithm == x509.RSA && cert.PublicKey.(*rsa.PublicKey).N.BitLen() > maxRSAKeySize { - c.sendAlert(alertBadCertificate) - return fmt.Errorf("tls: server sent certificate containing RSA key larger than %d bits", maxRSAKeySize) - } - certs[i] = cert - } - - if !c.config.InsecureSkipVerify { - opts := x509.VerifyOptions{ - Roots: c.config.RootCAs, - CurrentTime: c.config.time(), - DNSName: c.config.ServerName, - Intermediates: x509.NewCertPool(), - } - - for _, cert := range certs[1:] { - opts.Intermediates.AddCert(cert) - } - var err error - c.verifiedChains, err = certs[0].Verify(opts) - if err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - - switch certs[0].PublicKey.(type) { - case *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey: - break - default: - c.sendAlert(alertUnsupportedCertificate) - return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey) - } - - c.peerCertificates = certs - - if c.config.VerifyPeerCertificate != nil { - if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - - if c.config.VerifyConnection != nil { - if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - - return nil -} - -// certificateRequestInfoFromMsg generates a CertificateRequestInfo from a TLS -// <= 1.2 CertificateRequest, making an effort to fill in missing information. -func certificateRequestInfoFromMsg(ctx context.Context, vers uint16, certReq *certificateRequestMsg) *CertificateRequestInfo { - cri := &certificateRequestInfo{ - AcceptableCAs: certReq.certificateAuthorities, - Version: vers, - ctx: ctx, - } - - var rsaAvail, ecAvail bool - for _, certType := range certReq.certificateTypes { - switch certType { - case certTypeRSASign: - rsaAvail = true - case certTypeECDSASign: - ecAvail = true - } - } - - if !certReq.hasSignatureAlgorithm { - // Prior to TLS 1.2, signature schemes did not exist. In this case we - // make up a list based on the acceptable certificate types, to help - // GetClientCertificate and SupportsCertificate select the right certificate. - // The hash part of the SignatureScheme is a lie here, because - // TLS 1.0 and 1.1 always use MD5+SHA1 for RSA and SHA1 for ECDSA. - switch { - case rsaAvail && ecAvail: - cri.SignatureSchemes = []SignatureScheme{ - ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512, - PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1, - } - case rsaAvail: - cri.SignatureSchemes = []SignatureScheme{ - PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512, PKCS1WithSHA1, - } - case ecAvail: - cri.SignatureSchemes = []SignatureScheme{ - ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512, - } - } - return toCertificateRequestInfo(cri) - } - - // Filter the signature schemes based on the certificate types. - // See RFC 5246, Section 7.4.4 (where it calls this "somewhat complicated"). - cri.SignatureSchemes = make([]SignatureScheme, 0, len(certReq.supportedSignatureAlgorithms)) - for _, sigScheme := range certReq.supportedSignatureAlgorithms { - sigType, _, err := typeAndHashFromSignatureScheme(sigScheme) - if err != nil { - continue - } - switch sigType { - case signatureECDSA, signatureEd25519: - if ecAvail { - cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme) - } - case signatureRSAPSS, signaturePKCS1v15: - if rsaAvail { - cri.SignatureSchemes = append(cri.SignatureSchemes, sigScheme) - } - } - } - - return toCertificateRequestInfo(cri) -} - -func (c *Conn) getClientCertificate(cri *CertificateRequestInfo) (*Certificate, error) { - if c.config.GetClientCertificate != nil { - return c.config.GetClientCertificate(cri) - } - - for _, chain := range c.config.Certificates { - if err := cri.SupportsCertificate(&chain); err != nil { - continue - } - return &chain, nil - } - - // No acceptable certificate found. Don't send a certificate. - return new(Certificate), nil -} - -const clientSessionCacheKeyPrefix = "qtls-" - -// clientSessionCacheKey returns a key used to cache sessionTickets that could -// be used to resume previously negotiated TLS sessions with a server. -func clientSessionCacheKey(serverAddr net.Addr, config *config) string { - if len(config.ServerName) > 0 { - return clientSessionCacheKeyPrefix + config.ServerName - } - return clientSessionCacheKeyPrefix + serverAddr.String() -} - -// hostnameInSNI converts name into an appropriate hostname for SNI. -// Literal IP addresses and absolute FQDNs are not permitted as SNI values. -// See RFC 6066, Section 3. -func hostnameInSNI(name string) string { - host := name - if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' { - host = host[1 : len(host)-1] - } - if i := strings.LastIndex(host, "%"); i > 0 { - host = host[:i] - } - if net.ParseIP(host) != nil { - return "" - } - for len(name) > 0 && name[len(name)-1] == '.' { - name = name[:len(name)-1] - } - return name -} diff --git a/vendor/github.com/quic-go/qtls-go1-19/handshake_client_tls13.go b/vendor/github.com/quic-go/qtls-go1-19/handshake_client_tls13.go deleted file mode 100644 index 05ca1333b..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/handshake_client_tls13.go +++ /dev/null @@ -1,755 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package qtls - -import ( - "bytes" - "context" - "crypto" - "crypto/hmac" - "crypto/rsa" - "encoding/binary" - "errors" - "hash" - "sync/atomic" - "time" - - "golang.org/x/crypto/cryptobyte" -) - -type clientHandshakeStateTLS13 struct { - c *Conn - ctx context.Context - serverHello *serverHelloMsg - hello *clientHelloMsg - ecdheParams ecdheParameters - - session *clientSessionState - earlySecret []byte - binderKey []byte - - certReq *certificateRequestMsgTLS13 - usingPSK bool - sentDummyCCS bool - suite *cipherSuiteTLS13 - transcript hash.Hash - masterSecret []byte - trafficSecret []byte // client_application_traffic_secret_0 -} - -// handshake requires hs.c, hs.hello, hs.serverHello, hs.ecdheParams, and, -// optionally, hs.session, hs.earlySecret and hs.binderKey to be set. -func (hs *clientHandshakeStateTLS13) handshake() error { - c := hs.c - - if needFIPS() { - return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode") - } - - // The server must not select TLS 1.3 in a renegotiation. See RFC 8446, - // sections 4.1.2 and 4.1.3. - if c.handshakes > 0 { - c.sendAlert(alertProtocolVersion) - return errors.New("tls: server selected TLS 1.3 in a renegotiation") - } - - // Consistency check on the presence of a keyShare and its parameters. - if hs.ecdheParams == nil || len(hs.hello.keyShares) != 1 { - return c.sendAlert(alertInternalError) - } - - if err := hs.checkServerHelloOrHRR(); err != nil { - return err - } - - hs.transcript = hs.suite.hash.New() - - if err := transcriptMsg(hs.hello, hs.transcript); err != nil { - return err - } - - if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) { - if err := hs.sendDummyChangeCipherSpec(); err != nil { - return err - } - if err := hs.processHelloRetryRequest(); err != nil { - return err - } - } - - if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil { - return err - } - - c.buffering = true - if err := hs.processServerHello(); err != nil { - return err - } - c.updateConnectionState() - if err := hs.sendDummyChangeCipherSpec(); err != nil { - return err - } - if err := hs.establishHandshakeKeys(); err != nil { - return err - } - if err := hs.readServerParameters(); err != nil { - return err - } - if err := hs.readServerCertificate(); err != nil { - return err - } - c.updateConnectionState() - if err := hs.readServerFinished(); err != nil { - return err - } - if err := hs.sendClientCertificate(); err != nil { - return err - } - if err := hs.sendClientFinished(); err != nil { - return err - } - if _, err := c.flush(); err != nil { - return err - } - - atomic.StoreUint32(&c.handshakeStatus, 1) - c.updateConnectionState() - return nil -} - -// checkServerHelloOrHRR does validity checks that apply to both ServerHello and -// HelloRetryRequest messages. It sets hs.suite. -func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error { - c := hs.c - - if hs.serverHello.supportedVersion == 0 { - c.sendAlert(alertMissingExtension) - return errors.New("tls: server selected TLS 1.3 using the legacy version field") - } - - if hs.serverHello.supportedVersion != VersionTLS13 { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server selected an invalid version after a HelloRetryRequest") - } - - if hs.serverHello.vers != VersionTLS12 { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server sent an incorrect legacy version") - } - - if hs.serverHello.ocspStapling || - hs.serverHello.ticketSupported || - hs.serverHello.secureRenegotiationSupported || - len(hs.serverHello.secureRenegotiation) != 0 || - len(hs.serverHello.alpnProtocol) != 0 || - len(hs.serverHello.scts) != 0 { - c.sendAlert(alertUnsupportedExtension) - return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3") - } - - if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server did not echo the legacy session ID") - } - - if hs.serverHello.compressionMethod != compressionNone { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server selected unsupported compression format") - } - - selectedSuite := mutualCipherSuiteTLS13(hs.hello.cipherSuites, hs.serverHello.cipherSuite) - if hs.suite != nil && selectedSuite != hs.suite { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server changed cipher suite after a HelloRetryRequest") - } - if selectedSuite == nil { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server chose an unconfigured cipher suite") - } - hs.suite = selectedSuite - c.cipherSuite = hs.suite.id - - return nil -} - -// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility -// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4. -func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error { - if hs.sentDummyCCS { - return nil - } - hs.sentDummyCCS = true - - return hs.c.writeChangeCipherRecord() -} - -// processHelloRetryRequest handles the HRR in hs.serverHello, modifies and -// resends hs.hello, and reads the new ServerHello into hs.serverHello. -func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { - c := hs.c - - // The first ClientHello gets double-hashed into the transcript upon a - // HelloRetryRequest. (The idea is that the server might offload transcript - // storage to the client in the cookie.) See RFC 8446, Section 4.4.1. - chHash := hs.transcript.Sum(nil) - hs.transcript.Reset() - hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) - hs.transcript.Write(chHash) - if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil { - return err - } - - // The only HelloRetryRequest extensions we support are key_share and - // cookie, and clients must abort the handshake if the HRR would not result - // in any change in the ClientHello. - if hs.serverHello.selectedGroup == 0 && hs.serverHello.cookie == nil { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server sent an unnecessary HelloRetryRequest message") - } - - if hs.serverHello.cookie != nil { - hs.hello.cookie = hs.serverHello.cookie - } - - if hs.serverHello.serverShare.group != 0 { - c.sendAlert(alertDecodeError) - return errors.New("tls: received malformed key_share extension") - } - - // If the server sent a key_share extension selecting a group, ensure it's - // a group we advertised but did not send a key share for, and send a key - // share for it this time. - if curveID := hs.serverHello.selectedGroup; curveID != 0 { - curveOK := false - for _, id := range hs.hello.supportedCurves { - if id == curveID { - curveOK = true - break - } - } - if !curveOK { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server selected unsupported group") - } - if hs.ecdheParams.CurveID() == curveID { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share") - } - if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { - c.sendAlert(alertInternalError) - return errors.New("tls: CurvePreferences includes unsupported curve") - } - params, err := generateECDHEParameters(c.config.rand(), curveID) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - hs.ecdheParams = params - hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}} - } - - hs.hello.raw = nil - if len(hs.hello.pskIdentities) > 0 { - pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite) - if pskSuite == nil { - return c.sendAlert(alertInternalError) - } - if pskSuite.hash == hs.suite.hash { - // Update binders and obfuscated_ticket_age. - ticketAge := uint32(c.config.time().Sub(hs.session.receivedAt) / time.Millisecond) - hs.hello.pskIdentities[0].obfuscatedTicketAge = ticketAge + hs.session.ageAdd - - transcript := hs.suite.hash.New() - transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) - transcript.Write(chHash) - if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil { - return err - } - helloBytes, err := hs.hello.marshalWithoutBinders() - if err != nil { - return err - } - transcript.Write(helloBytes) - pskBinders := [][]byte{hs.suite.finishedHash(hs.binderKey, transcript)} - if err := hs.hello.updateBinders(pskBinders); err != nil { - return err - } - } else { - // Server selected a cipher suite incompatible with the PSK. - hs.hello.pskIdentities = nil - hs.hello.pskBinders = nil - } - } - - if hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil { - c.extraConfig.Rejected0RTT() - } - hs.hello.earlyData = false // disable 0-RTT - if _, err := hs.c.writeHandshakeRecord(hs.hello, hs.transcript); err != nil { - return err - } - - // serverHelloMsg is not included in the transcript - msg, err := c.readHandshake(nil) - if err != nil { - return err - } - - serverHello, ok := msg.(*serverHelloMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(serverHello, msg) - } - hs.serverHello = serverHello - - if err := hs.checkServerHelloOrHRR(); err != nil { - return err - } - - return nil -} - -func (hs *clientHandshakeStateTLS13) processServerHello() error { - c := hs.c - - if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) { - c.sendAlert(alertUnexpectedMessage) - return errors.New("tls: server sent two HelloRetryRequest messages") - } - - if len(hs.serverHello.cookie) != 0 { - c.sendAlert(alertUnsupportedExtension) - return errors.New("tls: server sent a cookie in a normal ServerHello") - } - - if hs.serverHello.selectedGroup != 0 { - c.sendAlert(alertDecodeError) - return errors.New("tls: malformed key_share extension") - } - - if hs.serverHello.serverShare.group == 0 { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server did not send a key share") - } - if hs.serverHello.serverShare.group != hs.ecdheParams.CurveID() { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server selected unsupported group") - } - - if !hs.serverHello.selectedIdentityPresent { - return nil - } - - if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server selected an invalid PSK") - } - - if len(hs.hello.pskIdentities) != 1 || hs.session == nil { - return c.sendAlert(alertInternalError) - } - pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite) - if pskSuite == nil { - return c.sendAlert(alertInternalError) - } - if pskSuite.hash != hs.suite.hash { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: server selected an invalid PSK and cipher suite pair") - } - - hs.usingPSK = true - c.didResume = true - c.peerCertificates = hs.session.serverCertificates - c.verifiedChains = hs.session.verifiedChains - c.ocspResponse = hs.session.ocspResponse - c.scts = hs.session.scts - return nil -} - -func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error { - c := hs.c - - sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data) - if sharedKey == nil { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: invalid server key share") - } - - earlySecret := hs.earlySecret - if !hs.usingPSK { - earlySecret = hs.suite.extract(nil, nil) - } - - handshakeSecret := hs.suite.extract(sharedKey, - hs.suite.deriveSecret(earlySecret, "derived", nil)) - - clientSecret := hs.suite.deriveSecret(handshakeSecret, - clientHandshakeTrafficLabel, hs.transcript) - c.out.exportKey(EncryptionHandshake, hs.suite, clientSecret) - c.out.setTrafficSecret(hs.suite, clientSecret) - serverSecret := hs.suite.deriveSecret(handshakeSecret, - serverHandshakeTrafficLabel, hs.transcript) - c.in.exportKey(EncryptionHandshake, hs.suite, serverSecret) - c.in.setTrafficSecret(hs.suite, serverSecret) - - err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.hello.random, serverSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - - hs.masterSecret = hs.suite.extract(nil, - hs.suite.deriveSecret(handshakeSecret, "derived", nil)) - - return nil -} - -func (hs *clientHandshakeStateTLS13) readServerParameters() error { - c := hs.c - - msg, err := c.readHandshake(hs.transcript) - if err != nil { - return err - } - - encryptedExtensions, ok := msg.(*encryptedExtensionsMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(encryptedExtensions, msg) - } - // Notify the caller if 0-RTT was rejected. - if !encryptedExtensions.earlyData && hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil { - c.extraConfig.Rejected0RTT() - } - c.used0RTT = encryptedExtensions.earlyData - if hs.c.extraConfig != nil && hs.c.extraConfig.ReceivedExtensions != nil { - hs.c.extraConfig.ReceivedExtensions(typeEncryptedExtensions, encryptedExtensions.additionalExtensions) - } - - if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol); err != nil { - c.sendAlert(alertUnsupportedExtension) - return err - } - c.clientProtocol = encryptedExtensions.alpnProtocol - - if c.extraConfig != nil && c.extraConfig.EnforceNextProtoSelection { - if len(encryptedExtensions.alpnProtocol) == 0 { - // the server didn't select an ALPN - c.sendAlert(alertNoApplicationProtocol) - return errors.New("ALPN negotiation failed. Server didn't offer any protocols") - } - } - return nil -} - -func (hs *clientHandshakeStateTLS13) readServerCertificate() error { - c := hs.c - - // Either a PSK or a certificate is always used, but not both. - // See RFC 8446, Section 4.1.1. - if hs.usingPSK { - // Make sure the connection is still being verified whether or not this - // is a resumption. Resumptions currently don't reverify certificates so - // they don't call verifyServerCertificate. See Issue 31641. - if c.config.VerifyConnection != nil { - if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - return nil - } - - msg, err := c.readHandshake(hs.transcript) - if err != nil { - return err - } - - certReq, ok := msg.(*certificateRequestMsgTLS13) - if ok { - hs.certReq = certReq - - msg, err = c.readHandshake(hs.transcript) - if err != nil { - return err - } - } - - certMsg, ok := msg.(*certificateMsgTLS13) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certMsg, msg) - } - if len(certMsg.certificate.Certificate) == 0 { - c.sendAlert(alertDecodeError) - return errors.New("tls: received empty certificates message") - } - - c.scts = certMsg.certificate.SignedCertificateTimestamps - c.ocspResponse = certMsg.certificate.OCSPStaple - - if err := c.verifyServerCertificate(certMsg.certificate.Certificate); err != nil { - return err - } - - // certificateVerifyMsg is included in the transcript, but not until - // after we verify the handshake signature, since the state before - // this message was sent is used. - msg, err = c.readHandshake(nil) - if err != nil { - return err - } - - certVerify, ok := msg.(*certificateVerifyMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certVerify, msg) - } - - // See RFC 8446, Section 4.4.3. - if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: certificate used with invalid signature algorithm") - } - sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm) - if err != nil { - return c.sendAlert(alertInternalError) - } - if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: certificate used with invalid signature algorithm") - } - signed := signedMessage(sigHash, serverSignatureContext, hs.transcript) - if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey, - sigHash, signed, certVerify.signature); err != nil { - c.sendAlert(alertDecryptError) - return errors.New("tls: invalid signature by the server certificate: " + err.Error()) - } - - if err := transcriptMsg(certVerify, hs.transcript); err != nil { - return err - } - - return nil -} - -func (hs *clientHandshakeStateTLS13) readServerFinished() error { - c := hs.c - - // finishedMsg is included in the transcript, but not until after we - // check the client version, since the state before this message was - // sent is used during verification. - msg, err := c.readHandshake(nil) - if err != nil { - return err - } - - finished, ok := msg.(*finishedMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(finished, msg) - } - - expectedMAC := hs.suite.finishedHash(c.in.trafficSecret, hs.transcript) - if !hmac.Equal(expectedMAC, finished.verifyData) { - c.sendAlert(alertDecryptError) - return errors.New("tls: invalid server finished hash") - } - - if err := transcriptMsg(finished, hs.transcript); err != nil { - return err - } - - // Derive secrets that take context through the server Finished. - - hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret, - clientApplicationTrafficLabel, hs.transcript) - serverSecret := hs.suite.deriveSecret(hs.masterSecret, - serverApplicationTrafficLabel, hs.transcript) - c.in.exportKey(EncryptionApplication, hs.suite, serverSecret) - c.in.setTrafficSecret(hs.suite, serverSecret) - - err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.hello.random, serverSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - - c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript) - - return nil -} - -func (hs *clientHandshakeStateTLS13) sendClientCertificate() error { - c := hs.c - - if hs.certReq == nil { - return nil - } - - cert, err := c.getClientCertificate(toCertificateRequestInfo(&certificateRequestInfo{ - AcceptableCAs: hs.certReq.certificateAuthorities, - SignatureSchemes: hs.certReq.supportedSignatureAlgorithms, - Version: c.vers, - ctx: hs.ctx, - })) - if err != nil { - return err - } - - certMsg := new(certificateMsgTLS13) - - certMsg.certificate = *cert - certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0 - certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0 - - if _, err := hs.c.writeHandshakeRecord(certMsg, hs.transcript); err != nil { - return err - } - - // If we sent an empty certificate message, skip the CertificateVerify. - if len(cert.Certificate) == 0 { - return nil - } - - certVerifyMsg := new(certificateVerifyMsg) - certVerifyMsg.hasSignatureAlgorithm = true - - certVerifyMsg.signatureAlgorithm, err = selectSignatureScheme(c.vers, cert, hs.certReq.supportedSignatureAlgorithms) - if err != nil { - // getClientCertificate returned a certificate incompatible with the - // CertificateRequestInfo supported signature algorithms. - c.sendAlert(alertHandshakeFailure) - return err - } - - sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerifyMsg.signatureAlgorithm) - if err != nil { - return c.sendAlert(alertInternalError) - } - - signed := signedMessage(sigHash, clientSignatureContext, hs.transcript) - signOpts := crypto.SignerOpts(sigHash) - if sigType == signatureRSAPSS { - signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash} - } - sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts) - if err != nil { - c.sendAlert(alertInternalError) - return errors.New("tls: failed to sign handshake: " + err.Error()) - } - certVerifyMsg.signature = sig - - if _, err := hs.c.writeHandshakeRecord(certVerifyMsg, hs.transcript); err != nil { - return err - } - - return nil -} - -func (hs *clientHandshakeStateTLS13) sendClientFinished() error { - c := hs.c - - finished := &finishedMsg{ - verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript), - } - - if _, err := hs.c.writeHandshakeRecord(finished, hs.transcript); err != nil { - return err - } - - c.out.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret) - c.out.setTrafficSecret(hs.suite, hs.trafficSecret) - - if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil { - c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret, - resumptionLabel, hs.transcript) - } - - return nil -} - -func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error { - if !c.isClient { - c.sendAlert(alertUnexpectedMessage) - return errors.New("tls: received new session ticket from a client") - } - - if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil { - return nil - } - - // See RFC 8446, Section 4.6.1. - if msg.lifetime == 0 { - return nil - } - lifetime := time.Duration(msg.lifetime) * time.Second - if lifetime > maxSessionTicketLifetime { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: received a session ticket with invalid lifetime") - } - - cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite) - if cipherSuite == nil || c.resumptionSecret == nil { - return c.sendAlert(alertInternalError) - } - - // We need to save the max_early_data_size that the server sent us, in order - // to decide if we're going to try 0-RTT with this ticket. - // However, at the same time, the qtls.ClientSessionTicket needs to be equal to - // the tls.ClientSessionTicket, so we can't just add a new field to the struct. - // We therefore abuse the nonce field (which is a byte slice) - nonceWithEarlyData := make([]byte, len(msg.nonce)+4) - binary.BigEndian.PutUint32(nonceWithEarlyData, msg.maxEarlyData) - copy(nonceWithEarlyData[4:], msg.nonce) - - var appData []byte - if c.extraConfig != nil && c.extraConfig.GetAppDataForSessionState != nil { - appData = c.extraConfig.GetAppDataForSessionState() - } - var b cryptobyte.Builder - b.AddUint16(clientSessionStateVersion) // revision - b.AddUint32(msg.maxEarlyData) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(appData) - }) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(msg.nonce) - }) - - // Save the resumption_master_secret and nonce instead of deriving the PSK - // to do the least amount of work on NewSessionTicket messages before we - // know if the ticket will be used. Forward secrecy of resumed connections - // is guaranteed by the requirement for pskModeDHE. - session := &clientSessionState{ - sessionTicket: msg.label, - vers: c.vers, - cipherSuite: c.cipherSuite, - masterSecret: c.resumptionSecret, - serverCertificates: c.peerCertificates, - verifiedChains: c.verifiedChains, - receivedAt: c.config.time(), - nonce: b.BytesOrPanic(), - useBy: c.config.time().Add(lifetime), - ageAdd: msg.ageAdd, - ocspResponse: c.ocspResponse, - scts: c.scts, - } - - cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config) - c.config.ClientSessionCache.Put(cacheKey, toClientSessionState(session)) - - return nil -} diff --git a/vendor/github.com/quic-go/qtls-go1-19/handshake_messages.go b/vendor/github.com/quic-go/qtls-go1-19/handshake_messages.go deleted file mode 100644 index c69fcefda..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/handshake_messages.go +++ /dev/null @@ -1,1875 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package qtls - -import ( - "errors" - "fmt" - "strings" - - "golang.org/x/crypto/cryptobyte" -) - -// The marshalingFunction type is an adapter to allow the use of ordinary -// functions as cryptobyte.MarshalingValue. -type marshalingFunction func(b *cryptobyte.Builder) error - -func (f marshalingFunction) Marshal(b *cryptobyte.Builder) error { - return f(b) -} - -// addBytesWithLength appends a sequence of bytes to the cryptobyte.Builder. If -// the length of the sequence is not the value specified, it produces an error. -func addBytesWithLength(b *cryptobyte.Builder, v []byte, n int) { - b.AddValue(marshalingFunction(func(b *cryptobyte.Builder) error { - if len(v) != n { - return fmt.Errorf("invalid value length: expected %d, got %d", n, len(v)) - } - b.AddBytes(v) - return nil - })) -} - -// addUint64 appends a big-endian, 64-bit value to the cryptobyte.Builder. -func addUint64(b *cryptobyte.Builder, v uint64) { - b.AddUint32(uint32(v >> 32)) - b.AddUint32(uint32(v)) -} - -// readUint64 decodes a big-endian, 64-bit value into out and advances over it. -// It reports whether the read was successful. -func readUint64(s *cryptobyte.String, out *uint64) bool { - var hi, lo uint32 - if !s.ReadUint32(&hi) || !s.ReadUint32(&lo) { - return false - } - *out = uint64(hi)<<32 | uint64(lo) - return true -} - -// readUint8LengthPrefixed acts like s.ReadUint8LengthPrefixed, but targets a -// []byte instead of a cryptobyte.String. -func readUint8LengthPrefixed(s *cryptobyte.String, out *[]byte) bool { - return s.ReadUint8LengthPrefixed((*cryptobyte.String)(out)) -} - -// readUint16LengthPrefixed acts like s.ReadUint16LengthPrefixed, but targets a -// []byte instead of a cryptobyte.String. -func readUint16LengthPrefixed(s *cryptobyte.String, out *[]byte) bool { - return s.ReadUint16LengthPrefixed((*cryptobyte.String)(out)) -} - -// readUint24LengthPrefixed acts like s.ReadUint24LengthPrefixed, but targets a -// []byte instead of a cryptobyte.String. -func readUint24LengthPrefixed(s *cryptobyte.String, out *[]byte) bool { - return s.ReadUint24LengthPrefixed((*cryptobyte.String)(out)) -} - -type clientHelloMsg struct { - raw []byte - vers uint16 - random []byte - sessionId []byte - cipherSuites []uint16 - compressionMethods []uint8 - serverName string - ocspStapling bool - supportedCurves []CurveID - supportedPoints []uint8 - ticketSupported bool - sessionTicket []uint8 - supportedSignatureAlgorithms []SignatureScheme - supportedSignatureAlgorithmsCert []SignatureScheme - secureRenegotiationSupported bool - secureRenegotiation []byte - alpnProtocols []string - scts bool - supportedVersions []uint16 - cookie []byte - keyShares []keyShare - earlyData bool - pskModes []uint8 - pskIdentities []pskIdentity - pskBinders [][]byte - additionalExtensions []Extension -} - -func (m *clientHelloMsg) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - - var exts cryptobyte.Builder - if len(m.serverName) > 0 { - // RFC 6066, Section 3 - exts.AddUint16(extensionServerName) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint8(0) // name_type = host_name - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes([]byte(m.serverName)) - }) - }) - }) - } - if m.ocspStapling { - // RFC 4366, Section 3.6 - exts.AddUint16(extensionStatusRequest) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint8(1) // status_type = ocsp - exts.AddUint16(0) // empty responder_id_list - exts.AddUint16(0) // empty request_extensions - }) - } - if len(m.supportedCurves) > 0 { - // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7 - exts.AddUint16(extensionSupportedCurves) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - for _, curve := range m.supportedCurves { - exts.AddUint16(uint16(curve)) - } - }) - }) - } - if len(m.supportedPoints) > 0 { - // RFC 4492, Section 5.1.2 - exts.AddUint16(extensionSupportedPoints) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(m.supportedPoints) - }) - }) - } - if m.ticketSupported { - // RFC 5077, Section 3.2 - exts.AddUint16(extensionSessionTicket) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(m.sessionTicket) - }) - } - if len(m.supportedSignatureAlgorithms) > 0 { - // RFC 5246, Section 7.4.1.4.1 - exts.AddUint16(extensionSignatureAlgorithms) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - for _, sigAlgo := range m.supportedSignatureAlgorithms { - exts.AddUint16(uint16(sigAlgo)) - } - }) - }) - } - if len(m.supportedSignatureAlgorithmsCert) > 0 { - // RFC 8446, Section 4.2.3 - exts.AddUint16(extensionSignatureAlgorithmsCert) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - for _, sigAlgo := range m.supportedSignatureAlgorithmsCert { - exts.AddUint16(uint16(sigAlgo)) - } - }) - }) - } - if m.secureRenegotiationSupported { - // RFC 5746, Section 3.2 - exts.AddUint16(extensionRenegotiationInfo) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(m.secureRenegotiation) - }) - }) - } - if len(m.alpnProtocols) > 0 { - // RFC 7301, Section 3.1 - exts.AddUint16(extensionALPN) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - for _, proto := range m.alpnProtocols { - exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes([]byte(proto)) - }) - } - }) - }) - } - if m.scts { - // RFC 6962, Section 3.3.1 - exts.AddUint16(extensionSCT) - exts.AddUint16(0) // empty extension_data - } - if len(m.supportedVersions) > 0 { - // RFC 8446, Section 4.2.1 - exts.AddUint16(extensionSupportedVersions) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { - for _, vers := range m.supportedVersions { - exts.AddUint16(vers) - } - }) - }) - } - if len(m.cookie) > 0 { - // RFC 8446, Section 4.2.2 - exts.AddUint16(extensionCookie) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(m.cookie) - }) - }) - } - if len(m.keyShares) > 0 { - // RFC 8446, Section 4.2.8 - exts.AddUint16(extensionKeyShare) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - for _, ks := range m.keyShares { - exts.AddUint16(uint16(ks.group)) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(ks.data) - }) - } - }) - }) - } - if m.earlyData { - // RFC 8446, Section 4.2.10 - exts.AddUint16(extensionEarlyData) - exts.AddUint16(0) // empty extension_data - } - if len(m.pskModes) > 0 { - // RFC 8446, Section 4.2.9 - exts.AddUint16(extensionPSKModes) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(m.pskModes) - }) - }) - } - for _, ext := range m.additionalExtensions { - exts.AddUint16(ext.Type) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(ext.Data) - }) - } - if len(m.pskIdentities) > 0 { // pre_shared_key must be the last extension - // RFC 8446, Section 4.2.11 - exts.AddUint16(extensionPreSharedKey) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - for _, psk := range m.pskIdentities { - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(psk.label) - }) - exts.AddUint32(psk.obfuscatedTicketAge) - } - }) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - for _, binder := range m.pskBinders { - exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(binder) - }) - } - }) - }) - } - extBytes, err := exts.Bytes() - if err != nil { - return nil, err - } - - var b cryptobyte.Builder - b.AddUint8(typeClientHello) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16(m.vers) - addBytesWithLength(b, m.random, 32) - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.sessionId) - }) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, suite := range m.cipherSuites { - b.AddUint16(suite) - } - }) - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.compressionMethods) - }) - - if len(extBytes) > 0 { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(extBytes) - }) - } - }) - - m.raw, err = b.Bytes() - return m.raw, err -} - -// marshalWithoutBinders returns the ClientHello through the -// PreSharedKeyExtension.identities field, according to RFC 8446, Section -// 4.2.11.2. Note that m.pskBinders must be set to slices of the correct length. -func (m *clientHelloMsg) marshalWithoutBinders() ([]byte, error) { - bindersLen := 2 // uint16 length prefix - for _, binder := range m.pskBinders { - bindersLen += 1 // uint8 length prefix - bindersLen += len(binder) - } - - fullMessage, err := m.marshal() - if err != nil { - return nil, err - } - return fullMessage[:len(fullMessage)-bindersLen], nil -} - -// updateBinders updates the m.pskBinders field, if necessary updating the -// cached marshaled representation. The supplied binders must have the same -// length as the current m.pskBinders. -func (m *clientHelloMsg) updateBinders(pskBinders [][]byte) error { - if len(pskBinders) != len(m.pskBinders) { - return errors.New("tls: internal error: pskBinders length mismatch") - } - for i := range m.pskBinders { - if len(pskBinders[i]) != len(m.pskBinders[i]) { - return errors.New("tls: internal error: pskBinders length mismatch") - } - } - m.pskBinders = pskBinders - if m.raw != nil { - helloBytes, err := m.marshalWithoutBinders() - if err != nil { - return err - } - lenWithoutBinders := len(helloBytes) - b := cryptobyte.NewFixedBuilder(m.raw[:lenWithoutBinders]) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, binder := range m.pskBinders { - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(binder) - }) - } - }) - if out, err := b.Bytes(); err != nil || len(out) != len(m.raw) { - return errors.New("tls: internal error: failed to update binders") - } - } - - return nil -} - -func (m *clientHelloMsg) unmarshal(data []byte) bool { - *m = clientHelloMsg{raw: data} - s := cryptobyte.String(data) - - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint16(&m.vers) || !s.ReadBytes(&m.random, 32) || - !readUint8LengthPrefixed(&s, &m.sessionId) { - return false - } - - var cipherSuites cryptobyte.String - if !s.ReadUint16LengthPrefixed(&cipherSuites) { - return false - } - m.cipherSuites = []uint16{} - m.secureRenegotiationSupported = false - for !cipherSuites.Empty() { - var suite uint16 - if !cipherSuites.ReadUint16(&suite) { - return false - } - if suite == scsvRenegotiation { - m.secureRenegotiationSupported = true - } - m.cipherSuites = append(m.cipherSuites, suite) - } - - if !readUint8LengthPrefixed(&s, &m.compressionMethods) { - return false - } - - if s.Empty() { - // ClientHello is optionally followed by extension data - return true - } - - var extensions cryptobyte.String - if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() { - return false - } - - seenExts := make(map[uint16]bool) - for !extensions.Empty() { - var extension uint16 - var extData cryptobyte.String - if !extensions.ReadUint16(&extension) || - !extensions.ReadUint16LengthPrefixed(&extData) { - return false - } - - if seenExts[extension] { - return false - } - seenExts[extension] = true - - switch extension { - case extensionServerName: - // RFC 6066, Section 3 - var nameList cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() { - return false - } - for !nameList.Empty() { - var nameType uint8 - var serverName cryptobyte.String - if !nameList.ReadUint8(&nameType) || - !nameList.ReadUint16LengthPrefixed(&serverName) || - serverName.Empty() { - return false - } - if nameType != 0 { - continue - } - if len(m.serverName) != 0 { - // Multiple names of the same name_type are prohibited. - return false - } - m.serverName = string(serverName) - // An SNI value may not include a trailing dot. - if strings.HasSuffix(m.serverName, ".") { - return false - } - } - case extensionStatusRequest: - // RFC 4366, Section 3.6 - var statusType uint8 - var ignored cryptobyte.String - if !extData.ReadUint8(&statusType) || - !extData.ReadUint16LengthPrefixed(&ignored) || - !extData.ReadUint16LengthPrefixed(&ignored) { - return false - } - m.ocspStapling = statusType == statusTypeOCSP - case extensionSupportedCurves: - // RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7 - var curves cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&curves) || curves.Empty() { - return false - } - for !curves.Empty() { - var curve uint16 - if !curves.ReadUint16(&curve) { - return false - } - m.supportedCurves = append(m.supportedCurves, CurveID(curve)) - } - case extensionSupportedPoints: - // RFC 4492, Section 5.1.2 - if !readUint8LengthPrefixed(&extData, &m.supportedPoints) || - len(m.supportedPoints) == 0 { - return false - } - case extensionSessionTicket: - // RFC 5077, Section 3.2 - m.ticketSupported = true - extData.ReadBytes(&m.sessionTicket, len(extData)) - case extensionSignatureAlgorithms: - // RFC 5246, Section 7.4.1.4.1 - var sigAndAlgs cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() { - return false - } - for !sigAndAlgs.Empty() { - var sigAndAlg uint16 - if !sigAndAlgs.ReadUint16(&sigAndAlg) { - return false - } - m.supportedSignatureAlgorithms = append( - m.supportedSignatureAlgorithms, SignatureScheme(sigAndAlg)) - } - case extensionSignatureAlgorithmsCert: - // RFC 8446, Section 4.2.3 - var sigAndAlgs cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() { - return false - } - for !sigAndAlgs.Empty() { - var sigAndAlg uint16 - if !sigAndAlgs.ReadUint16(&sigAndAlg) { - return false - } - m.supportedSignatureAlgorithmsCert = append( - m.supportedSignatureAlgorithmsCert, SignatureScheme(sigAndAlg)) - } - case extensionRenegotiationInfo: - // RFC 5746, Section 3.2 - if !readUint8LengthPrefixed(&extData, &m.secureRenegotiation) { - return false - } - m.secureRenegotiationSupported = true - case extensionALPN: - // RFC 7301, Section 3.1 - var protoList cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() { - return false - } - for !protoList.Empty() { - var proto cryptobyte.String - if !protoList.ReadUint8LengthPrefixed(&proto) || proto.Empty() { - return false - } - m.alpnProtocols = append(m.alpnProtocols, string(proto)) - } - case extensionSCT: - // RFC 6962, Section 3.3.1 - m.scts = true - case extensionSupportedVersions: - // RFC 8446, Section 4.2.1 - var versList cryptobyte.String - if !extData.ReadUint8LengthPrefixed(&versList) || versList.Empty() { - return false - } - for !versList.Empty() { - var vers uint16 - if !versList.ReadUint16(&vers) { - return false - } - m.supportedVersions = append(m.supportedVersions, vers) - } - case extensionCookie: - // RFC 8446, Section 4.2.2 - if !readUint16LengthPrefixed(&extData, &m.cookie) || - len(m.cookie) == 0 { - return false - } - case extensionKeyShare: - // RFC 8446, Section 4.2.8 - var clientShares cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&clientShares) { - return false - } - for !clientShares.Empty() { - var ks keyShare - if !clientShares.ReadUint16((*uint16)(&ks.group)) || - !readUint16LengthPrefixed(&clientShares, &ks.data) || - len(ks.data) == 0 { - return false - } - m.keyShares = append(m.keyShares, ks) - } - case extensionEarlyData: - // RFC 8446, Section 4.2.10 - m.earlyData = true - case extensionPSKModes: - // RFC 8446, Section 4.2.9 - if !readUint8LengthPrefixed(&extData, &m.pskModes) { - return false - } - case extensionPreSharedKey: - // RFC 8446, Section 4.2.11 - if !extensions.Empty() { - return false // pre_shared_key must be the last extension - } - var identities cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&identities) || identities.Empty() { - return false - } - for !identities.Empty() { - var psk pskIdentity - if !readUint16LengthPrefixed(&identities, &psk.label) || - !identities.ReadUint32(&psk.obfuscatedTicketAge) || - len(psk.label) == 0 { - return false - } - m.pskIdentities = append(m.pskIdentities, psk) - } - var binders cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&binders) || binders.Empty() { - return false - } - for !binders.Empty() { - var binder []byte - if !readUint8LengthPrefixed(&binders, &binder) || - len(binder) == 0 { - return false - } - m.pskBinders = append(m.pskBinders, binder) - } - default: - m.additionalExtensions = append(m.additionalExtensions, Extension{Type: extension, Data: extData}) - continue - } - - if !extData.Empty() { - return false - } - } - - return true -} - -type serverHelloMsg struct { - raw []byte - vers uint16 - random []byte - sessionId []byte - cipherSuite uint16 - compressionMethod uint8 - ocspStapling bool - ticketSupported bool - secureRenegotiationSupported bool - secureRenegotiation []byte - alpnProtocol string - scts [][]byte - supportedVersion uint16 - serverShare keyShare - selectedIdentityPresent bool - selectedIdentity uint16 - supportedPoints []uint8 - - // HelloRetryRequest extensions - cookie []byte - selectedGroup CurveID -} - -func (m *serverHelloMsg) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - - var exts cryptobyte.Builder - if m.ocspStapling { - exts.AddUint16(extensionStatusRequest) - exts.AddUint16(0) // empty extension_data - } - if m.ticketSupported { - exts.AddUint16(extensionSessionTicket) - exts.AddUint16(0) // empty extension_data - } - if m.secureRenegotiationSupported { - exts.AddUint16(extensionRenegotiationInfo) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(m.secureRenegotiation) - }) - }) - } - if len(m.alpnProtocol) > 0 { - exts.AddUint16(extensionALPN) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes([]byte(m.alpnProtocol)) - }) - }) - }) - } - if len(m.scts) > 0 { - exts.AddUint16(extensionSCT) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - for _, sct := range m.scts { - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(sct) - }) - } - }) - }) - } - if m.supportedVersion != 0 { - exts.AddUint16(extensionSupportedVersions) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16(m.supportedVersion) - }) - } - if m.serverShare.group != 0 { - exts.AddUint16(extensionKeyShare) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16(uint16(m.serverShare.group)) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(m.serverShare.data) - }) - }) - } - if m.selectedIdentityPresent { - exts.AddUint16(extensionPreSharedKey) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16(m.selectedIdentity) - }) - } - - if len(m.cookie) > 0 { - exts.AddUint16(extensionCookie) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(m.cookie) - }) - }) - } - if m.selectedGroup != 0 { - exts.AddUint16(extensionKeyShare) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint16(uint16(m.selectedGroup)) - }) - } - if len(m.supportedPoints) > 0 { - exts.AddUint16(extensionSupportedPoints) - exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(m.supportedPoints) - }) - }) - } - - extBytes, err := exts.Bytes() - if err != nil { - return nil, err - } - - var b cryptobyte.Builder - b.AddUint8(typeServerHello) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16(m.vers) - addBytesWithLength(b, m.random, 32) - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.sessionId) - }) - b.AddUint16(m.cipherSuite) - b.AddUint8(m.compressionMethod) - - if len(extBytes) > 0 { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(extBytes) - }) - } - }) - - m.raw, err = b.Bytes() - return m.raw, err -} - -func (m *serverHelloMsg) unmarshal(data []byte) bool { - *m = serverHelloMsg{raw: data} - s := cryptobyte.String(data) - - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint16(&m.vers) || !s.ReadBytes(&m.random, 32) || - !readUint8LengthPrefixed(&s, &m.sessionId) || - !s.ReadUint16(&m.cipherSuite) || - !s.ReadUint8(&m.compressionMethod) { - return false - } - - if s.Empty() { - // ServerHello is optionally followed by extension data - return true - } - - var extensions cryptobyte.String - if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() { - return false - } - - seenExts := make(map[uint16]bool) - for !extensions.Empty() { - var extension uint16 - var extData cryptobyte.String - if !extensions.ReadUint16(&extension) || - !extensions.ReadUint16LengthPrefixed(&extData) { - return false - } - - if seenExts[extension] { - return false - } - seenExts[extension] = true - - switch extension { - case extensionStatusRequest: - m.ocspStapling = true - case extensionSessionTicket: - m.ticketSupported = true - case extensionRenegotiationInfo: - if !readUint8LengthPrefixed(&extData, &m.secureRenegotiation) { - return false - } - m.secureRenegotiationSupported = true - case extensionALPN: - var protoList cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() { - return false - } - var proto cryptobyte.String - if !protoList.ReadUint8LengthPrefixed(&proto) || - proto.Empty() || !protoList.Empty() { - return false - } - m.alpnProtocol = string(proto) - case extensionSCT: - var sctList cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&sctList) || sctList.Empty() { - return false - } - for !sctList.Empty() { - var sct []byte - if !readUint16LengthPrefixed(&sctList, &sct) || - len(sct) == 0 { - return false - } - m.scts = append(m.scts, sct) - } - case extensionSupportedVersions: - if !extData.ReadUint16(&m.supportedVersion) { - return false - } - case extensionCookie: - if !readUint16LengthPrefixed(&extData, &m.cookie) || - len(m.cookie) == 0 { - return false - } - case extensionKeyShare: - // This extension has different formats in SH and HRR, accept either - // and let the handshake logic decide. See RFC 8446, Section 4.2.8. - if len(extData) == 2 { - if !extData.ReadUint16((*uint16)(&m.selectedGroup)) { - return false - } - } else { - if !extData.ReadUint16((*uint16)(&m.serverShare.group)) || - !readUint16LengthPrefixed(&extData, &m.serverShare.data) { - return false - } - } - case extensionPreSharedKey: - m.selectedIdentityPresent = true - if !extData.ReadUint16(&m.selectedIdentity) { - return false - } - case extensionSupportedPoints: - // RFC 4492, Section 5.1.2 - if !readUint8LengthPrefixed(&extData, &m.supportedPoints) || - len(m.supportedPoints) == 0 { - return false - } - default: - // Ignore unknown extensions. - continue - } - - if !extData.Empty() { - return false - } - } - - return true -} - -type encryptedExtensionsMsg struct { - raw []byte - alpnProtocol string - earlyData bool - - additionalExtensions []Extension -} - -func (m *encryptedExtensionsMsg) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - - var b cryptobyte.Builder - b.AddUint8(typeEncryptedExtensions) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - if len(m.alpnProtocol) > 0 { - b.AddUint16(extensionALPN) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes([]byte(m.alpnProtocol)) - }) - }) - }) - } - if m.earlyData { - // RFC 8446, Section 4.2.10 - b.AddUint16(extensionEarlyData) - b.AddUint16(0) // empty extension_data - } - for _, ext := range m.additionalExtensions { - b.AddUint16(ext.Type) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(ext.Data) - }) - } - }) - }) - - var err error - m.raw, err = b.Bytes() - return m.raw, err -} - -func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool { - *m = encryptedExtensionsMsg{raw: data} - s := cryptobyte.String(data) - - var extensions cryptobyte.String - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() { - return false - } - - for !extensions.Empty() { - var ext uint16 - var extData cryptobyte.String - if !extensions.ReadUint16(&ext) || - !extensions.ReadUint16LengthPrefixed(&extData) { - return false - } - - switch ext { - case extensionALPN: - var protoList cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() { - return false - } - var proto cryptobyte.String - if !protoList.ReadUint8LengthPrefixed(&proto) || - proto.Empty() || !protoList.Empty() { - return false - } - m.alpnProtocol = string(proto) - case extensionEarlyData: - m.earlyData = true - default: - m.additionalExtensions = append(m.additionalExtensions, Extension{Type: ext, Data: extData}) - continue - } - - if !extData.Empty() { - return false - } - } - - return true -} - -type endOfEarlyDataMsg struct{} - -func (m *endOfEarlyDataMsg) marshal() ([]byte, error) { - x := make([]byte, 4) - x[0] = typeEndOfEarlyData - return x, nil -} - -func (m *endOfEarlyDataMsg) unmarshal(data []byte) bool { - return len(data) == 4 -} - -type keyUpdateMsg struct { - raw []byte - updateRequested bool -} - -func (m *keyUpdateMsg) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - - var b cryptobyte.Builder - b.AddUint8(typeKeyUpdate) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - if m.updateRequested { - b.AddUint8(1) - } else { - b.AddUint8(0) - } - }) - - var err error - m.raw, err = b.Bytes() - return m.raw, err -} - -func (m *keyUpdateMsg) unmarshal(data []byte) bool { - m.raw = data - s := cryptobyte.String(data) - - var updateRequested uint8 - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint8(&updateRequested) || !s.Empty() { - return false - } - switch updateRequested { - case 0: - m.updateRequested = false - case 1: - m.updateRequested = true - default: - return false - } - return true -} - -type newSessionTicketMsgTLS13 struct { - raw []byte - lifetime uint32 - ageAdd uint32 - nonce []byte - label []byte - maxEarlyData uint32 -} - -func (m *newSessionTicketMsgTLS13) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - - var b cryptobyte.Builder - b.AddUint8(typeNewSessionTicket) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint32(m.lifetime) - b.AddUint32(m.ageAdd) - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.nonce) - }) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.label) - }) - - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - if m.maxEarlyData > 0 { - b.AddUint16(extensionEarlyData) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint32(m.maxEarlyData) - }) - } - }) - }) - - var err error - m.raw, err = b.Bytes() - return m.raw, err -} - -func (m *newSessionTicketMsgTLS13) unmarshal(data []byte) bool { - *m = newSessionTicketMsgTLS13{raw: data} - s := cryptobyte.String(data) - - var extensions cryptobyte.String - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint32(&m.lifetime) || - !s.ReadUint32(&m.ageAdd) || - !readUint8LengthPrefixed(&s, &m.nonce) || - !readUint16LengthPrefixed(&s, &m.label) || - !s.ReadUint16LengthPrefixed(&extensions) || - !s.Empty() { - return false - } - - for !extensions.Empty() { - var extension uint16 - var extData cryptobyte.String - if !extensions.ReadUint16(&extension) || - !extensions.ReadUint16LengthPrefixed(&extData) { - return false - } - - switch extension { - case extensionEarlyData: - if !extData.ReadUint32(&m.maxEarlyData) { - return false - } - default: - // Ignore unknown extensions. - continue - } - - if !extData.Empty() { - return false - } - } - - return true -} - -type certificateRequestMsgTLS13 struct { - raw []byte - ocspStapling bool - scts bool - supportedSignatureAlgorithms []SignatureScheme - supportedSignatureAlgorithmsCert []SignatureScheme - certificateAuthorities [][]byte -} - -func (m *certificateRequestMsgTLS13) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - - var b cryptobyte.Builder - b.AddUint8(typeCertificateRequest) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - // certificate_request_context (SHALL be zero length unless used for - // post-handshake authentication) - b.AddUint8(0) - - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - if m.ocspStapling { - b.AddUint16(extensionStatusRequest) - b.AddUint16(0) // empty extension_data - } - if m.scts { - // RFC 8446, Section 4.4.2.1 makes no mention of - // signed_certificate_timestamp in CertificateRequest, but - // "Extensions in the Certificate message from the client MUST - // correspond to extensions in the CertificateRequest message - // from the server." and it appears in the table in Section 4.2. - b.AddUint16(extensionSCT) - b.AddUint16(0) // empty extension_data - } - if len(m.supportedSignatureAlgorithms) > 0 { - b.AddUint16(extensionSignatureAlgorithms) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, sigAlgo := range m.supportedSignatureAlgorithms { - b.AddUint16(uint16(sigAlgo)) - } - }) - }) - } - if len(m.supportedSignatureAlgorithmsCert) > 0 { - b.AddUint16(extensionSignatureAlgorithmsCert) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, sigAlgo := range m.supportedSignatureAlgorithmsCert { - b.AddUint16(uint16(sigAlgo)) - } - }) - }) - } - if len(m.certificateAuthorities) > 0 { - b.AddUint16(extensionCertificateAuthorities) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, ca := range m.certificateAuthorities { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(ca) - }) - } - }) - }) - } - }) - }) - - var err error - m.raw, err = b.Bytes() - return m.raw, err -} - -func (m *certificateRequestMsgTLS13) unmarshal(data []byte) bool { - *m = certificateRequestMsgTLS13{raw: data} - s := cryptobyte.String(data) - - var context, extensions cryptobyte.String - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint8LengthPrefixed(&context) || !context.Empty() || - !s.ReadUint16LengthPrefixed(&extensions) || - !s.Empty() { - return false - } - - for !extensions.Empty() { - var extension uint16 - var extData cryptobyte.String - if !extensions.ReadUint16(&extension) || - !extensions.ReadUint16LengthPrefixed(&extData) { - return false - } - - switch extension { - case extensionStatusRequest: - m.ocspStapling = true - case extensionSCT: - m.scts = true - case extensionSignatureAlgorithms: - var sigAndAlgs cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() { - return false - } - for !sigAndAlgs.Empty() { - var sigAndAlg uint16 - if !sigAndAlgs.ReadUint16(&sigAndAlg) { - return false - } - m.supportedSignatureAlgorithms = append( - m.supportedSignatureAlgorithms, SignatureScheme(sigAndAlg)) - } - case extensionSignatureAlgorithmsCert: - var sigAndAlgs cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() { - return false - } - for !sigAndAlgs.Empty() { - var sigAndAlg uint16 - if !sigAndAlgs.ReadUint16(&sigAndAlg) { - return false - } - m.supportedSignatureAlgorithmsCert = append( - m.supportedSignatureAlgorithmsCert, SignatureScheme(sigAndAlg)) - } - case extensionCertificateAuthorities: - var auths cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&auths) || auths.Empty() { - return false - } - for !auths.Empty() { - var ca []byte - if !readUint16LengthPrefixed(&auths, &ca) || len(ca) == 0 { - return false - } - m.certificateAuthorities = append(m.certificateAuthorities, ca) - } - default: - // Ignore unknown extensions. - continue - } - - if !extData.Empty() { - return false - } - } - - return true -} - -type certificateMsg struct { - raw []byte - certificates [][]byte -} - -func (m *certificateMsg) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - - var i int - for _, slice := range m.certificates { - i += len(slice) - } - - length := 3 + 3*len(m.certificates) + i - x := make([]byte, 4+length) - x[0] = typeCertificate - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) - - certificateOctets := length - 3 - x[4] = uint8(certificateOctets >> 16) - x[5] = uint8(certificateOctets >> 8) - x[6] = uint8(certificateOctets) - - y := x[7:] - for _, slice := range m.certificates { - y[0] = uint8(len(slice) >> 16) - y[1] = uint8(len(slice) >> 8) - y[2] = uint8(len(slice)) - copy(y[3:], slice) - y = y[3+len(slice):] - } - - m.raw = x - return m.raw, nil -} - -func (m *certificateMsg) unmarshal(data []byte) bool { - if len(data) < 7 { - return false - } - - m.raw = data - certsLen := uint32(data[4])<<16 | uint32(data[5])<<8 | uint32(data[6]) - if uint32(len(data)) != certsLen+7 { - return false - } - - numCerts := 0 - d := data[7:] - for certsLen > 0 { - if len(d) < 4 { - return false - } - certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2]) - if uint32(len(d)) < 3+certLen { - return false - } - d = d[3+certLen:] - certsLen -= 3 + certLen - numCerts++ - } - - m.certificates = make([][]byte, numCerts) - d = data[7:] - for i := 0; i < numCerts; i++ { - certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2]) - m.certificates[i] = d[3 : 3+certLen] - d = d[3+certLen:] - } - - return true -} - -type certificateMsgTLS13 struct { - raw []byte - certificate Certificate - ocspStapling bool - scts bool -} - -func (m *certificateMsgTLS13) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - - var b cryptobyte.Builder - b.AddUint8(typeCertificate) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8(0) // certificate_request_context - - certificate := m.certificate - if !m.ocspStapling { - certificate.OCSPStaple = nil - } - if !m.scts { - certificate.SignedCertificateTimestamps = nil - } - marshalCertificate(b, certificate) - }) - - var err error - m.raw, err = b.Bytes() - return m.raw, err -} - -func marshalCertificate(b *cryptobyte.Builder, certificate Certificate) { - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - for i, cert := range certificate.Certificate { - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(cert) - }) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - if i > 0 { - // This library only supports OCSP and SCT for leaf certificates. - return - } - if certificate.OCSPStaple != nil { - b.AddUint16(extensionStatusRequest) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8(statusTypeOCSP) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(certificate.OCSPStaple) - }) - }) - } - if certificate.SignedCertificateTimestamps != nil { - b.AddUint16(extensionSCT) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - for _, sct := range certificate.SignedCertificateTimestamps { - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(sct) - }) - } - }) - }) - } - }) - } - }) -} - -func (m *certificateMsgTLS13) unmarshal(data []byte) bool { - *m = certificateMsgTLS13{raw: data} - s := cryptobyte.String(data) - - var context cryptobyte.String - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint8LengthPrefixed(&context) || !context.Empty() || - !unmarshalCertificate(&s, &m.certificate) || - !s.Empty() { - return false - } - - m.scts = m.certificate.SignedCertificateTimestamps != nil - m.ocspStapling = m.certificate.OCSPStaple != nil - - return true -} - -func unmarshalCertificate(s *cryptobyte.String, certificate *Certificate) bool { - var certList cryptobyte.String - if !s.ReadUint24LengthPrefixed(&certList) { - return false - } - for !certList.Empty() { - var cert []byte - var extensions cryptobyte.String - if !readUint24LengthPrefixed(&certList, &cert) || - !certList.ReadUint16LengthPrefixed(&extensions) { - return false - } - certificate.Certificate = append(certificate.Certificate, cert) - for !extensions.Empty() { - var extension uint16 - var extData cryptobyte.String - if !extensions.ReadUint16(&extension) || - !extensions.ReadUint16LengthPrefixed(&extData) { - return false - } - if len(certificate.Certificate) > 1 { - // This library only supports OCSP and SCT for leaf certificates. - continue - } - - switch extension { - case extensionStatusRequest: - var statusType uint8 - if !extData.ReadUint8(&statusType) || statusType != statusTypeOCSP || - !readUint24LengthPrefixed(&extData, &certificate.OCSPStaple) || - len(certificate.OCSPStaple) == 0 { - return false - } - case extensionSCT: - var sctList cryptobyte.String - if !extData.ReadUint16LengthPrefixed(&sctList) || sctList.Empty() { - return false - } - for !sctList.Empty() { - var sct []byte - if !readUint16LengthPrefixed(&sctList, &sct) || - len(sct) == 0 { - return false - } - certificate.SignedCertificateTimestamps = append( - certificate.SignedCertificateTimestamps, sct) - } - default: - // Ignore unknown extensions. - continue - } - - if !extData.Empty() { - return false - } - } - } - return true -} - -type serverKeyExchangeMsg struct { - raw []byte - key []byte -} - -func (m *serverKeyExchangeMsg) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - length := len(m.key) - x := make([]byte, length+4) - x[0] = typeServerKeyExchange - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) - copy(x[4:], m.key) - - m.raw = x - return x, nil -} - -func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool { - m.raw = data - if len(data) < 4 { - return false - } - m.key = data[4:] - return true -} - -type certificateStatusMsg struct { - raw []byte - response []byte -} - -func (m *certificateStatusMsg) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - - var b cryptobyte.Builder - b.AddUint8(typeCertificateStatus) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddUint8(statusTypeOCSP) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.response) - }) - }) - - var err error - m.raw, err = b.Bytes() - return m.raw, err -} - -func (m *certificateStatusMsg) unmarshal(data []byte) bool { - m.raw = data - s := cryptobyte.String(data) - - var statusType uint8 - if !s.Skip(4) || // message type and uint24 length field - !s.ReadUint8(&statusType) || statusType != statusTypeOCSP || - !readUint24LengthPrefixed(&s, &m.response) || - len(m.response) == 0 || !s.Empty() { - return false - } - return true -} - -type serverHelloDoneMsg struct{} - -func (m *serverHelloDoneMsg) marshal() ([]byte, error) { - x := make([]byte, 4) - x[0] = typeServerHelloDone - return x, nil -} - -func (m *serverHelloDoneMsg) unmarshal(data []byte) bool { - return len(data) == 4 -} - -type clientKeyExchangeMsg struct { - raw []byte - ciphertext []byte -} - -func (m *clientKeyExchangeMsg) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - length := len(m.ciphertext) - x := make([]byte, length+4) - x[0] = typeClientKeyExchange - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) - copy(x[4:], m.ciphertext) - - m.raw = x - return x, nil -} - -func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool { - m.raw = data - if len(data) < 4 { - return false - } - l := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) - if l != len(data)-4 { - return false - } - m.ciphertext = data[4:] - return true -} - -type finishedMsg struct { - raw []byte - verifyData []byte -} - -func (m *finishedMsg) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - - var b cryptobyte.Builder - b.AddUint8(typeFinished) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.verifyData) - }) - - var err error - m.raw, err = b.Bytes() - return m.raw, err -} - -func (m *finishedMsg) unmarshal(data []byte) bool { - m.raw = data - s := cryptobyte.String(data) - return s.Skip(1) && - readUint24LengthPrefixed(&s, &m.verifyData) && - s.Empty() -} - -type certificateRequestMsg struct { - raw []byte - // hasSignatureAlgorithm indicates whether this message includes a list of - // supported signature algorithms. This change was introduced with TLS 1.2. - hasSignatureAlgorithm bool - - certificateTypes []byte - supportedSignatureAlgorithms []SignatureScheme - certificateAuthorities [][]byte -} - -func (m *certificateRequestMsg) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - - // See RFC 4346, Section 7.4.4. - length := 1 + len(m.certificateTypes) + 2 - casLength := 0 - for _, ca := range m.certificateAuthorities { - casLength += 2 + len(ca) - } - length += casLength - - if m.hasSignatureAlgorithm { - length += 2 + 2*len(m.supportedSignatureAlgorithms) - } - - x := make([]byte, 4+length) - x[0] = typeCertificateRequest - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) - - x[4] = uint8(len(m.certificateTypes)) - - copy(x[5:], m.certificateTypes) - y := x[5+len(m.certificateTypes):] - - if m.hasSignatureAlgorithm { - n := len(m.supportedSignatureAlgorithms) * 2 - y[0] = uint8(n >> 8) - y[1] = uint8(n) - y = y[2:] - for _, sigAlgo := range m.supportedSignatureAlgorithms { - y[0] = uint8(sigAlgo >> 8) - y[1] = uint8(sigAlgo) - y = y[2:] - } - } - - y[0] = uint8(casLength >> 8) - y[1] = uint8(casLength) - y = y[2:] - for _, ca := range m.certificateAuthorities { - y[0] = uint8(len(ca) >> 8) - y[1] = uint8(len(ca)) - y = y[2:] - copy(y, ca) - y = y[len(ca):] - } - - m.raw = x - return m.raw, nil -} - -func (m *certificateRequestMsg) unmarshal(data []byte) bool { - m.raw = data - - if len(data) < 5 { - return false - } - - length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) - if uint32(len(data))-4 != length { - return false - } - - numCertTypes := int(data[4]) - data = data[5:] - if numCertTypes == 0 || len(data) <= numCertTypes { - return false - } - - m.certificateTypes = make([]byte, numCertTypes) - if copy(m.certificateTypes, data) != numCertTypes { - return false - } - - data = data[numCertTypes:] - - if m.hasSignatureAlgorithm { - if len(data) < 2 { - return false - } - sigAndHashLen := uint16(data[0])<<8 | uint16(data[1]) - data = data[2:] - if sigAndHashLen&1 != 0 { - return false - } - if len(data) < int(sigAndHashLen) { - return false - } - numSigAlgos := sigAndHashLen / 2 - m.supportedSignatureAlgorithms = make([]SignatureScheme, numSigAlgos) - for i := range m.supportedSignatureAlgorithms { - m.supportedSignatureAlgorithms[i] = SignatureScheme(data[0])<<8 | SignatureScheme(data[1]) - data = data[2:] - } - } - - if len(data) < 2 { - return false - } - casLength := uint16(data[0])<<8 | uint16(data[1]) - data = data[2:] - if len(data) < int(casLength) { - return false - } - cas := make([]byte, casLength) - copy(cas, data) - data = data[casLength:] - - m.certificateAuthorities = nil - for len(cas) > 0 { - if len(cas) < 2 { - return false - } - caLen := uint16(cas[0])<<8 | uint16(cas[1]) - cas = cas[2:] - - if len(cas) < int(caLen) { - return false - } - - m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen]) - cas = cas[caLen:] - } - - return len(data) == 0 -} - -type certificateVerifyMsg struct { - raw []byte - hasSignatureAlgorithm bool // format change introduced in TLS 1.2 - signatureAlgorithm SignatureScheme - signature []byte -} - -func (m *certificateVerifyMsg) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - - var b cryptobyte.Builder - b.AddUint8(typeCertificateVerify) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - if m.hasSignatureAlgorithm { - b.AddUint16(uint16(m.signatureAlgorithm)) - } - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.signature) - }) - }) - - var err error - m.raw, err = b.Bytes() - return m.raw, err -} - -func (m *certificateVerifyMsg) unmarshal(data []byte) bool { - m.raw = data - s := cryptobyte.String(data) - - if !s.Skip(4) { // message type and uint24 length field - return false - } - if m.hasSignatureAlgorithm { - if !s.ReadUint16((*uint16)(&m.signatureAlgorithm)) { - return false - } - } - return readUint16LengthPrefixed(&s, &m.signature) && s.Empty() -} - -type newSessionTicketMsg struct { - raw []byte - ticket []byte -} - -func (m *newSessionTicketMsg) marshal() ([]byte, error) { - if m.raw != nil { - return m.raw, nil - } - - // See RFC 5077, Section 3.3. - ticketLen := len(m.ticket) - length := 2 + 4 + ticketLen - x := make([]byte, 4+length) - x[0] = typeNewSessionTicket - x[1] = uint8(length >> 16) - x[2] = uint8(length >> 8) - x[3] = uint8(length) - x[8] = uint8(ticketLen >> 8) - x[9] = uint8(ticketLen) - copy(x[10:], m.ticket) - - m.raw = x - - return m.raw, nil -} - -func (m *newSessionTicketMsg) unmarshal(data []byte) bool { - m.raw = data - - if len(data) < 10 { - return false - } - - length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) - if uint32(len(data))-4 != length { - return false - } - - ticketLen := int(data[8])<<8 + int(data[9]) - if len(data)-10 != ticketLen { - return false - } - - m.ticket = data[10:] - - return true -} - -type helloRequestMsg struct { -} - -func (*helloRequestMsg) marshal() ([]byte, error) { - return []byte{typeHelloRequest, 0, 0, 0}, nil -} - -func (*helloRequestMsg) unmarshal(data []byte) bool { - return len(data) == 4 -} - -type transcriptHash interface { - Write([]byte) (int, error) -} - -// transcriptMsg is a helper used to marshal and hash messages which typically -// are not written to the wire, and as such aren't hashed during Conn.writeRecord. -func transcriptMsg(msg handshakeMessage, h transcriptHash) error { - data, err := msg.marshal() - if err != nil { - return err - } - h.Write(data) - return nil -} diff --git a/vendor/github.com/quic-go/qtls-go1-19/handshake_server.go b/vendor/github.com/quic-go/qtls-go1-19/handshake_server.go deleted file mode 100644 index 738fc9471..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/handshake_server.go +++ /dev/null @@ -1,926 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package qtls - -import ( - "context" - "crypto" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/rsa" - "crypto/subtle" - "crypto/x509" - "errors" - "fmt" - "hash" - "io" - "sync/atomic" - "time" -) - -// serverHandshakeState contains details of a server handshake in progress. -// It's discarded once the handshake has completed. -type serverHandshakeState struct { - c *Conn - ctx context.Context - clientHello *clientHelloMsg - hello *serverHelloMsg - suite *cipherSuite - ecdheOk bool - ecSignOk bool - rsaDecryptOk bool - rsaSignOk bool - sessionState *sessionState - finishedHash finishedHash - masterSecret []byte - cert *Certificate -} - -// serverHandshake performs a TLS handshake as a server. -func (c *Conn) serverHandshake(ctx context.Context) error { - c.setAlternativeRecordLayer() - - clientHello, err := c.readClientHello(ctx) - if err != nil { - return err - } - - if c.vers == VersionTLS13 { - hs := serverHandshakeStateTLS13{ - c: c, - ctx: ctx, - clientHello: clientHello, - } - return hs.handshake() - } else if c.extraConfig.usesAlternativeRecordLayer() { - // This should already have been caught by the check that the ClientHello doesn't - // offer any (supported) versions older than TLS 1.3. - // Check again to make sure we can't be tricked into using an older version. - c.sendAlert(alertProtocolVersion) - return errors.New("tls: negotiated TLS < 1.3 when using QUIC") - } - - hs := serverHandshakeState{ - c: c, - ctx: ctx, - clientHello: clientHello, - } - return hs.handshake() -} - -func (hs *serverHandshakeState) handshake() error { - c := hs.c - - if err := hs.processClientHello(); err != nil { - return err - } - - // For an overview of TLS handshaking, see RFC 5246, Section 7.3. - c.buffering = true - if hs.checkForResumption() { - // The client has included a session ticket and so we do an abbreviated handshake. - c.didResume = true - if err := hs.doResumeHandshake(); err != nil { - return err - } - if err := hs.establishKeys(); err != nil { - return err - } - if err := hs.sendSessionTicket(); err != nil { - return err - } - if err := hs.sendFinished(c.serverFinished[:]); err != nil { - return err - } - if _, err := c.flush(); err != nil { - return err - } - c.clientFinishedIsFirst = false - if err := hs.readFinished(nil); err != nil { - return err - } - } else { - // The client didn't include a session ticket, or it wasn't - // valid so we do a full handshake. - if err := hs.pickCipherSuite(); err != nil { - return err - } - if err := hs.doFullHandshake(); err != nil { - return err - } - if err := hs.establishKeys(); err != nil { - return err - } - if err := hs.readFinished(c.clientFinished[:]); err != nil { - return err - } - c.clientFinishedIsFirst = true - c.buffering = true - if err := hs.sendSessionTicket(); err != nil { - return err - } - if err := hs.sendFinished(nil); err != nil { - return err - } - if _, err := c.flush(); err != nil { - return err - } - } - - c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random) - atomic.StoreUint32(&c.handshakeStatus, 1) - - c.updateConnectionState() - return nil -} - -// readClientHello reads a ClientHello message and selects the protocol version. -func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) { - // clientHelloMsg is included in the transcript, but we haven't initialized - // it yet. The respective handshake functions will record it themselves. - msg, err := c.readHandshake(nil) - if err != nil { - return nil, err - } - clientHello, ok := msg.(*clientHelloMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return nil, unexpectedMessageError(clientHello, msg) - } - - var configForClient *config - originalConfig := c.config - if c.config.GetConfigForClient != nil { - chi := newClientHelloInfo(ctx, c, clientHello) - if cfc, err := c.config.GetConfigForClient(chi); err != nil { - c.sendAlert(alertInternalError) - return nil, err - } else if cfc != nil { - configForClient = fromConfig(cfc) - c.config = configForClient - } - } - c.ticketKeys = originalConfig.ticketKeys(configForClient) - - clientVersions := clientHello.supportedVersions - if len(clientHello.supportedVersions) == 0 { - clientVersions = supportedVersionsFromMax(clientHello.vers) - } - if c.extraConfig.usesAlternativeRecordLayer() { - // In QUIC, the client MUST NOT offer any old TLS versions. - // Here, we can only check that none of the other supported versions of this library - // (TLS 1.0 - TLS 1.2) is offered. We don't check for any SSL versions here. - for _, ver := range clientVersions { - if ver == VersionTLS13 { - continue - } - for _, v := range supportedVersions { - if ver == v { - c.sendAlert(alertProtocolVersion) - return nil, fmt.Errorf("tls: client offered old TLS version %#x", ver) - } - } - } - // Make the config we're using allows us to use TLS 1.3. - if c.config.maxSupportedVersion(roleServer) < VersionTLS13 { - c.sendAlert(alertInternalError) - return nil, errors.New("tls: MaxVersion prevents QUIC from using TLS 1.3") - } - } - c.vers, ok = c.config.mutualVersion(roleServer, clientVersions) - if !ok { - c.sendAlert(alertProtocolVersion) - return nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions) - } - c.haveVers = true - c.in.version = c.vers - c.out.version = c.vers - - return clientHello, nil -} - -func (hs *serverHandshakeState) processClientHello() error { - c := hs.c - - hs.hello = new(serverHelloMsg) - hs.hello.vers = c.vers - - foundCompression := false - // We only support null compression, so check that the client offered it. - for _, compression := range hs.clientHello.compressionMethods { - if compression == compressionNone { - foundCompression = true - break - } - } - - if !foundCompression { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: client does not support uncompressed connections") - } - - hs.hello.random = make([]byte, 32) - serverRandom := hs.hello.random - // Downgrade protection canaries. See RFC 8446, Section 4.1.3. - maxVers := c.config.maxSupportedVersion(roleServer) - if maxVers >= VersionTLS12 && c.vers < maxVers || testingOnlyForceDowngradeCanary { - if c.vers == VersionTLS12 { - copy(serverRandom[24:], downgradeCanaryTLS12) - } else { - copy(serverRandom[24:], downgradeCanaryTLS11) - } - serverRandom = serverRandom[:24] - } - _, err := io.ReadFull(c.config.rand(), serverRandom) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - - if len(hs.clientHello.secureRenegotiation) != 0 { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: initial handshake had non-empty renegotiation extension") - } - - hs.hello.secureRenegotiationSupported = hs.clientHello.secureRenegotiationSupported - hs.hello.compressionMethod = compressionNone - if len(hs.clientHello.serverName) > 0 { - c.serverName = hs.clientHello.serverName - } - - selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols) - if err != nil { - c.sendAlert(alertNoApplicationProtocol) - return err - } - hs.hello.alpnProtocol = selectedProto - c.clientProtocol = selectedProto - - hs.cert, err = c.config.getCertificate(newClientHelloInfo(hs.ctx, c, hs.clientHello)) - if err != nil { - if err == errNoCertificates { - c.sendAlert(alertUnrecognizedName) - } else { - c.sendAlert(alertInternalError) - } - return err - } - if hs.clientHello.scts { - hs.hello.scts = hs.cert.SignedCertificateTimestamps - } - - hs.ecdheOk = supportsECDHE(c.config, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints) - - if hs.ecdheOk && len(hs.clientHello.supportedPoints) > 0 { - // Although omitting the ec_point_formats extension is permitted, some - // old OpenSSL version will refuse to handshake if not present. - // - // Per RFC 4492, section 5.1.2, implementations MUST support the - // uncompressed point format. See golang.org/issue/31943. - hs.hello.supportedPoints = []uint8{pointFormatUncompressed} - } - - if priv, ok := hs.cert.PrivateKey.(crypto.Signer); ok { - switch priv.Public().(type) { - case *ecdsa.PublicKey: - hs.ecSignOk = true - case ed25519.PublicKey: - hs.ecSignOk = true - case *rsa.PublicKey: - hs.rsaSignOk = true - default: - c.sendAlert(alertInternalError) - return fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public()) - } - } - if priv, ok := hs.cert.PrivateKey.(crypto.Decrypter); ok { - switch priv.Public().(type) { - case *rsa.PublicKey: - hs.rsaDecryptOk = true - default: - c.sendAlert(alertInternalError) - return fmt.Errorf("tls: unsupported decryption key type (%T)", priv.Public()) - } - } - - return nil -} - -// negotiateALPN picks a shared ALPN protocol that both sides support in server -// preference order. If ALPN is not configured or the peer doesn't support it, -// it returns "" and no error. -func negotiateALPN(serverProtos, clientProtos []string) (string, error) { - if len(serverProtos) == 0 || len(clientProtos) == 0 { - return "", nil - } - var http11fallback bool - for _, s := range serverProtos { - for _, c := range clientProtos { - if s == c { - return s, nil - } - if s == "h2" && c == "http/1.1" { - http11fallback = true - } - } - } - // As a special case, let http/1.1 clients connect to h2 servers as if they - // didn't support ALPN. We used not to enforce protocol overlap, so over - // time a number of HTTP servers were configured with only "h2", but - // expected to accept connections from "http/1.1" clients. See Issue 46310. - if http11fallback { - return "", nil - } - return "", fmt.Errorf("tls: client requested unsupported application protocols (%s)", clientProtos) -} - -// supportsECDHE returns whether ECDHE key exchanges can be used with this -// pre-TLS 1.3 client. -func supportsECDHE(c *config, supportedCurves []CurveID, supportedPoints []uint8) bool { - supportsCurve := false - for _, curve := range supportedCurves { - if c.supportsCurve(curve) { - supportsCurve = true - break - } - } - - supportsPointFormat := false - for _, pointFormat := range supportedPoints { - if pointFormat == pointFormatUncompressed { - supportsPointFormat = true - break - } - } - // Per RFC 8422, Section 5.1.2, if the Supported Point Formats extension is - // missing, uncompressed points are supported. If supportedPoints is empty, - // the extension must be missing, as an empty extension body is rejected by - // the parser. See https://go.dev/issue/49126. - if len(supportedPoints) == 0 { - supportsPointFormat = true - } - - return supportsCurve && supportsPointFormat -} - -func (hs *serverHandshakeState) pickCipherSuite() error { - c := hs.c - - preferenceOrder := cipherSuitesPreferenceOrder - if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) { - preferenceOrder = cipherSuitesPreferenceOrderNoAES - } - - configCipherSuites := c.config.cipherSuites() - preferenceList := make([]uint16, 0, len(configCipherSuites)) - for _, suiteID := range preferenceOrder { - for _, id := range configCipherSuites { - if id == suiteID { - preferenceList = append(preferenceList, id) - break - } - } - } - - hs.suite = selectCipherSuite(preferenceList, hs.clientHello.cipherSuites, hs.cipherSuiteOk) - if hs.suite == nil { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: no cipher suite supported by both client and server") - } - c.cipherSuite = hs.suite.id - - for _, id := range hs.clientHello.cipherSuites { - if id == TLS_FALLBACK_SCSV { - // The client is doing a fallback connection. See RFC 7507. - if hs.clientHello.vers < c.config.maxSupportedVersion(roleServer) { - c.sendAlert(alertInappropriateFallback) - return errors.New("tls: client using inappropriate protocol fallback") - } - break - } - } - - return nil -} - -func (hs *serverHandshakeState) cipherSuiteOk(c *cipherSuite) bool { - if c.flags&suiteECDHE != 0 { - if !hs.ecdheOk { - return false - } - if c.flags&suiteECSign != 0 { - if !hs.ecSignOk { - return false - } - } else if !hs.rsaSignOk { - return false - } - } else if !hs.rsaDecryptOk { - return false - } - if hs.c.vers < VersionTLS12 && c.flags&suiteTLS12 != 0 { - return false - } - return true -} - -// checkForResumption reports whether we should perform resumption on this connection. -func (hs *serverHandshakeState) checkForResumption() bool { - c := hs.c - - if c.config.SessionTicketsDisabled { - return false - } - - plaintext, usedOldKey := c.decryptTicket(hs.clientHello.sessionTicket) - if plaintext == nil { - return false - } - hs.sessionState = &sessionState{usedOldKey: usedOldKey} - ok := hs.sessionState.unmarshal(plaintext) - if !ok { - return false - } - - createdAt := time.Unix(int64(hs.sessionState.createdAt), 0) - if c.config.time().Sub(createdAt) > maxSessionTicketLifetime { - return false - } - - // Never resume a session for a different TLS version. - if c.vers != hs.sessionState.vers { - return false - } - - cipherSuiteOk := false - // Check that the client is still offering the ciphersuite in the session. - for _, id := range hs.clientHello.cipherSuites { - if id == hs.sessionState.cipherSuite { - cipherSuiteOk = true - break - } - } - if !cipherSuiteOk { - return false - } - - // Check that we also support the ciphersuite from the session. - hs.suite = selectCipherSuite([]uint16{hs.sessionState.cipherSuite}, - c.config.cipherSuites(), hs.cipherSuiteOk) - if hs.suite == nil { - return false - } - - sessionHasClientCerts := len(hs.sessionState.certificates) != 0 - needClientCerts := requiresClientCert(c.config.ClientAuth) - if needClientCerts && !sessionHasClientCerts { - return false - } - if sessionHasClientCerts && c.config.ClientAuth == NoClientCert { - return false - } - - return true -} - -func (hs *serverHandshakeState) doResumeHandshake() error { - c := hs.c - - hs.hello.cipherSuite = hs.suite.id - c.cipherSuite = hs.suite.id - // We echo the client's session ID in the ServerHello to let it know - // that we're doing a resumption. - hs.hello.sessionId = hs.clientHello.sessionId - hs.hello.ticketSupported = hs.sessionState.usedOldKey - hs.finishedHash = newFinishedHash(c.vers, hs.suite) - hs.finishedHash.discardHandshakeBuffer() - if err := transcriptMsg(hs.clientHello, &hs.finishedHash); err != nil { - return err - } - if _, err := hs.c.writeHandshakeRecord(hs.hello, &hs.finishedHash); err != nil { - return err - } - - if err := c.processCertsFromClient(Certificate{ - Certificate: hs.sessionState.certificates, - }); err != nil { - return err - } - - if c.config.VerifyConnection != nil { - if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - - hs.masterSecret = hs.sessionState.masterSecret - - return nil -} - -func (hs *serverHandshakeState) doFullHandshake() error { - c := hs.c - - if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 { - hs.hello.ocspStapling = true - } - - hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled - hs.hello.cipherSuite = hs.suite.id - - hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite) - if c.config.ClientAuth == NoClientCert { - // No need to keep a full record of the handshake if client - // certificates won't be used. - hs.finishedHash.discardHandshakeBuffer() - } - if err := transcriptMsg(hs.clientHello, &hs.finishedHash); err != nil { - return err - } - if _, err := hs.c.writeHandshakeRecord(hs.hello, &hs.finishedHash); err != nil { - return err - } - - certMsg := new(certificateMsg) - certMsg.certificates = hs.cert.Certificate - if _, err := hs.c.writeHandshakeRecord(certMsg, &hs.finishedHash); err != nil { - return err - } - - if hs.hello.ocspStapling { - certStatus := new(certificateStatusMsg) - certStatus.response = hs.cert.OCSPStaple - if _, err := hs.c.writeHandshakeRecord(certStatus, &hs.finishedHash); err != nil { - return err - } - } - - keyAgreement := hs.suite.ka(c.vers) - skx, err := keyAgreement.generateServerKeyExchange(c.config, hs.cert, hs.clientHello, hs.hello) - if err != nil { - c.sendAlert(alertHandshakeFailure) - return err - } - if skx != nil { - if _, err := hs.c.writeHandshakeRecord(skx, &hs.finishedHash); err != nil { - return err - } - } - - var certReq *certificateRequestMsg - if c.config.ClientAuth >= RequestClientCert { - // Request a client certificate - certReq = new(certificateRequestMsg) - certReq.certificateTypes = []byte{ - byte(certTypeRSASign), - byte(certTypeECDSASign), - } - if c.vers >= VersionTLS12 { - certReq.hasSignatureAlgorithm = true - certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms() - } - - // An empty list of certificateAuthorities signals to - // the client that it may send any certificate in response - // to our request. When we know the CAs we trust, then - // we can send them down, so that the client can choose - // an appropriate certificate to give to us. - if c.config.ClientCAs != nil { - certReq.certificateAuthorities = c.config.ClientCAs.Subjects() - } - if _, err := hs.c.writeHandshakeRecord(certReq, &hs.finishedHash); err != nil { - return err - } - } - - helloDone := new(serverHelloDoneMsg) - if _, err := hs.c.writeHandshakeRecord(helloDone, &hs.finishedHash); err != nil { - return err - } - - if _, err := c.flush(); err != nil { - return err - } - - var pub crypto.PublicKey // public key for client auth, if any - - msg, err := c.readHandshake(&hs.finishedHash) - if err != nil { - return err - } - - // If we requested a client certificate, then the client must send a - // certificate message, even if it's empty. - if c.config.ClientAuth >= RequestClientCert { - certMsg, ok := msg.(*certificateMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certMsg, msg) - } - - if err := c.processCertsFromClient(Certificate{ - Certificate: certMsg.certificates, - }); err != nil { - return err - } - if len(certMsg.certificates) != 0 { - pub = c.peerCertificates[0].PublicKey - } - - msg, err = c.readHandshake(&hs.finishedHash) - if err != nil { - return err - } - } - if c.config.VerifyConnection != nil { - if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - - // Get client key exchange - ckx, ok := msg.(*clientKeyExchangeMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(ckx, msg) - } - - preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers) - if err != nil { - c.sendAlert(alertHandshakeFailure) - return err - } - hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random) - if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.clientHello.random, hs.masterSecret); err != nil { - c.sendAlert(alertInternalError) - return err - } - - // If we received a client cert in response to our certificate request message, - // the client will send us a certificateVerifyMsg immediately after the - // clientKeyExchangeMsg. This message is a digest of all preceding - // handshake-layer messages that is signed using the private key corresponding - // to the client's certificate. This allows us to verify that the client is in - // possession of the private key of the certificate. - if len(c.peerCertificates) > 0 { - // certificateVerifyMsg is included in the transcript, but not until - // after we verify the handshake signature, since the state before - // this message was sent is used. - msg, err = c.readHandshake(nil) - if err != nil { - return err - } - certVerify, ok := msg.(*certificateVerifyMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certVerify, msg) - } - - var sigType uint8 - var sigHash crypto.Hash - if c.vers >= VersionTLS12 { - if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, certReq.supportedSignatureAlgorithms) { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: client certificate used with invalid signature algorithm") - } - sigType, sigHash, err = typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm) - if err != nil { - return c.sendAlert(alertInternalError) - } - } else { - sigType, sigHash, err = legacyTypeAndHashFromPublicKey(pub) - if err != nil { - c.sendAlert(alertIllegalParameter) - return err - } - } - - signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash, hs.masterSecret) - if err := verifyHandshakeSignature(sigType, pub, sigHash, signed, certVerify.signature); err != nil { - c.sendAlert(alertDecryptError) - return errors.New("tls: invalid signature by the client certificate: " + err.Error()) - } - - if err := transcriptMsg(certVerify, &hs.finishedHash); err != nil { - return err - } - } - - hs.finishedHash.discardHandshakeBuffer() - - return nil -} - -func (hs *serverHandshakeState) establishKeys() error { - c := hs.c - - clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := - keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen) - - var clientCipher, serverCipher any - var clientHash, serverHash hash.Hash - - if hs.suite.aead == nil { - clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */) - clientHash = hs.suite.mac(clientMAC) - serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */) - serverHash = hs.suite.mac(serverMAC) - } else { - clientCipher = hs.suite.aead(clientKey, clientIV) - serverCipher = hs.suite.aead(serverKey, serverIV) - } - - c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) - c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) - - return nil -} - -func (hs *serverHandshakeState) readFinished(out []byte) error { - c := hs.c - - if err := c.readChangeCipherSpec(); err != nil { - return err - } - - // finishedMsg is included in the transcript, but not until after we - // check the client version, since the state before this message was - // sent is used during verification. - msg, err := c.readHandshake(nil) - if err != nil { - return err - } - clientFinished, ok := msg.(*finishedMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(clientFinished, msg) - } - - verify := hs.finishedHash.clientSum(hs.masterSecret) - if len(verify) != len(clientFinished.verifyData) || - subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: client's Finished message is incorrect") - } - - if err := transcriptMsg(clientFinished, &hs.finishedHash); err != nil { - return err - } - - copy(out, verify) - return nil -} - -func (hs *serverHandshakeState) sendSessionTicket() error { - // ticketSupported is set in a resumption handshake if the - // ticket from the client was encrypted with an old session - // ticket key and thus a refreshed ticket should be sent. - if !hs.hello.ticketSupported { - return nil - } - - c := hs.c - m := new(newSessionTicketMsg) - - createdAt := uint64(c.config.time().Unix()) - if hs.sessionState != nil { - // If this is re-wrapping an old key, then keep - // the original time it was created. - createdAt = hs.sessionState.createdAt - } - - var certsFromClient [][]byte - for _, cert := range c.peerCertificates { - certsFromClient = append(certsFromClient, cert.Raw) - } - state := sessionState{ - vers: c.vers, - cipherSuite: hs.suite.id, - createdAt: createdAt, - masterSecret: hs.masterSecret, - certificates: certsFromClient, - } - stateBytes, err := state.marshal() - if err != nil { - return err - } - m.ticket, err = c.encryptTicket(stateBytes) - if err != nil { - return err - } - - if _, err := hs.c.writeHandshakeRecord(m, &hs.finishedHash); err != nil { - return err - } - - return nil -} - -func (hs *serverHandshakeState) sendFinished(out []byte) error { - c := hs.c - - if err := c.writeChangeCipherRecord(); err != nil { - return err - } - - finished := new(finishedMsg) - finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret) - if _, err := hs.c.writeHandshakeRecord(finished, &hs.finishedHash); err != nil { - return err - } - - copy(out, finished.verifyData) - - return nil -} - -// processCertsFromClient takes a chain of client certificates either from a -// Certificates message or from a sessionState and verifies them. It returns -// the public key of the leaf certificate. -func (c *Conn) processCertsFromClient(certificate Certificate) error { - certificates := certificate.Certificate - certs := make([]*x509.Certificate, len(certificates)) - var err error - for i, asn1Data := range certificates { - if certs[i], err = x509.ParseCertificate(asn1Data); err != nil { - c.sendAlert(alertBadCertificate) - return errors.New("tls: failed to parse client certificate: " + err.Error()) - } - if certs[i].PublicKeyAlgorithm == x509.RSA && certs[i].PublicKey.(*rsa.PublicKey).N.BitLen() > maxRSAKeySize { - c.sendAlert(alertBadCertificate) - return fmt.Errorf("tls: client sent certificate containing RSA key larger than %d bits", maxRSAKeySize) - } - } - - if len(certs) == 0 && requiresClientCert(c.config.ClientAuth) { - c.sendAlert(alertBadCertificate) - return errors.New("tls: client didn't provide a certificate") - } - - if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 { - opts := x509.VerifyOptions{ - Roots: c.config.ClientCAs, - CurrentTime: c.config.time(), - Intermediates: x509.NewCertPool(), - KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - } - - for _, cert := range certs[1:] { - opts.Intermediates.AddCert(cert) - } - - chains, err := certs[0].Verify(opts) - if err != nil { - c.sendAlert(alertBadCertificate) - return errors.New("tls: failed to verify client certificate: " + err.Error()) - } - - c.verifiedChains = chains - } - - c.peerCertificates = certs - c.ocspResponse = certificate.OCSPStaple - c.scts = certificate.SignedCertificateTimestamps - - if len(certs) > 0 { - switch certs[0].PublicKey.(type) { - case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey: - default: - c.sendAlert(alertUnsupportedCertificate) - return fmt.Errorf("tls: client certificate contains an unsupported public key of type %T", certs[0].PublicKey) - } - } - - if c.config.VerifyPeerCertificate != nil { - if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - - return nil -} - -func newClientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo { - supportedVersions := clientHello.supportedVersions - if len(clientHello.supportedVersions) == 0 { - supportedVersions = supportedVersionsFromMax(clientHello.vers) - } - - return toClientHelloInfo(&clientHelloInfo{ - CipherSuites: clientHello.cipherSuites, - ServerName: clientHello.serverName, - SupportedCurves: clientHello.supportedCurves, - SupportedPoints: clientHello.supportedPoints, - SignatureSchemes: clientHello.supportedSignatureAlgorithms, - SupportedProtos: clientHello.alpnProtocols, - SupportedVersions: supportedVersions, - Conn: c.conn, - config: toConfig(c.config), - ctx: ctx, - }) -} diff --git a/vendor/github.com/quic-go/qtls-go1-19/handshake_server_tls13.go b/vendor/github.com/quic-go/qtls-go1-19/handshake_server_tls13.go deleted file mode 100644 index c4706c44d..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/handshake_server_tls13.go +++ /dev/null @@ -1,903 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package qtls - -import ( - "bytes" - "context" - "crypto" - "crypto/hmac" - "crypto/rsa" - "errors" - "hash" - "io" - "sync/atomic" - "time" -) - -// maxClientPSKIdentities is the number of client PSK identities the server will -// attempt to validate. It will ignore the rest not to let cheap ClientHello -// messages cause too much work in session ticket decryption attempts. -const maxClientPSKIdentities = 5 - -type serverHandshakeStateTLS13 struct { - c *Conn - ctx context.Context - clientHello *clientHelloMsg - hello *serverHelloMsg - alpnNegotiationErr error - encryptedExtensions *encryptedExtensionsMsg - sentDummyCCS bool - usingPSK bool - suite *cipherSuiteTLS13 - cert *Certificate - sigAlg SignatureScheme - earlySecret []byte - sharedKey []byte - handshakeSecret []byte - masterSecret []byte - trafficSecret []byte // client_application_traffic_secret_0 - transcript hash.Hash - clientFinished []byte -} - -func (hs *serverHandshakeStateTLS13) handshake() error { - c := hs.c - - if needFIPS() { - return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode") - } - - // For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2. - if err := hs.processClientHello(); err != nil { - return err - } - if err := hs.checkForResumption(); err != nil { - return err - } - c.updateConnectionState() - if err := hs.pickCertificate(); err != nil { - return err - } - c.buffering = true - if err := hs.sendServerParameters(); err != nil { - return err - } - if err := hs.sendServerCertificate(); err != nil { - return err - } - if err := hs.sendServerFinished(); err != nil { - return err - } - // Note that at this point we could start sending application data without - // waiting for the client's second flight, but the application might not - // expect the lack of replay protection of the ClientHello parameters. - if _, err := c.flush(); err != nil { - return err - } - if err := hs.readClientCertificate(); err != nil { - return err - } - c.updateConnectionState() - if err := hs.readClientFinished(); err != nil { - return err - } - - atomic.StoreUint32(&c.handshakeStatus, 1) - c.updateConnectionState() - return nil -} - -func (hs *serverHandshakeStateTLS13) processClientHello() error { - c := hs.c - - hs.hello = new(serverHelloMsg) - hs.encryptedExtensions = new(encryptedExtensionsMsg) - - // TLS 1.3 froze the ServerHello.legacy_version field, and uses - // supported_versions instead. See RFC 8446, sections 4.1.3 and 4.2.1. - hs.hello.vers = VersionTLS12 - hs.hello.supportedVersion = c.vers - - if len(hs.clientHello.supportedVersions) == 0 { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: client used the legacy version field to negotiate TLS 1.3") - } - - // Abort if the client is doing a fallback and landing lower than what we - // support. See RFC 7507, which however does not specify the interaction - // with supported_versions. The only difference is that with - // supported_versions a client has a chance to attempt a [TLS 1.2, TLS 1.4] - // handshake in case TLS 1.3 is broken but 1.2 is not. Alas, in that case, - // it will have to drop the TLS_FALLBACK_SCSV protection if it falls back to - // TLS 1.2, because a TLS 1.3 server would abort here. The situation before - // supported_versions was not better because there was just no way to do a - // TLS 1.4 handshake without risking the server selecting TLS 1.3. - for _, id := range hs.clientHello.cipherSuites { - if id == TLS_FALLBACK_SCSV { - // Use c.vers instead of max(supported_versions) because an attacker - // could defeat this by adding an arbitrary high version otherwise. - if c.vers < c.config.maxSupportedVersion(roleServer) { - c.sendAlert(alertInappropriateFallback) - return errors.New("tls: client using inappropriate protocol fallback") - } - break - } - } - - if len(hs.clientHello.compressionMethods) != 1 || - hs.clientHello.compressionMethods[0] != compressionNone { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: TLS 1.3 client supports illegal compression methods") - } - - hs.hello.random = make([]byte, 32) - if _, err := io.ReadFull(c.config.rand(), hs.hello.random); err != nil { - c.sendAlert(alertInternalError) - return err - } - - if len(hs.clientHello.secureRenegotiation) != 0 { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: initial handshake had non-empty renegotiation extension") - } - - hs.hello.sessionId = hs.clientHello.sessionId - hs.hello.compressionMethod = compressionNone - - preferenceList := defaultCipherSuitesTLS13 - if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) { - preferenceList = defaultCipherSuitesTLS13NoAES - } - for _, suiteID := range preferenceList { - hs.suite = mutualCipherSuiteTLS13(hs.clientHello.cipherSuites, suiteID) - if hs.suite != nil { - break - } - } - if hs.suite == nil { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: no cipher suite supported by both client and server") - } - c.cipherSuite = hs.suite.id - hs.hello.cipherSuite = hs.suite.id - hs.transcript = hs.suite.hash.New() - - // Pick the ECDHE group in server preference order, but give priority to - // groups with a key share, to avoid a HelloRetryRequest round-trip. - var selectedGroup CurveID - var clientKeyShare *keyShare -GroupSelection: - for _, preferredGroup := range c.config.curvePreferences() { - for _, ks := range hs.clientHello.keyShares { - if ks.group == preferredGroup { - selectedGroup = ks.group - clientKeyShare = &ks - break GroupSelection - } - } - if selectedGroup != 0 { - continue - } - for _, group := range hs.clientHello.supportedCurves { - if group == preferredGroup { - selectedGroup = group - break - } - } - } - if selectedGroup == 0 { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: no ECDHE curve supported by both client and server") - } - if clientKeyShare == nil { - if err := hs.doHelloRetryRequest(selectedGroup); err != nil { - return err - } - clientKeyShare = &hs.clientHello.keyShares[0] - } - - if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && !ok { - c.sendAlert(alertInternalError) - return errors.New("tls: CurvePreferences includes unsupported curve") - } - params, err := generateECDHEParameters(c.config.rand(), selectedGroup) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - hs.hello.serverShare = keyShare{group: selectedGroup, data: params.PublicKey()} - hs.sharedKey = params.SharedKey(clientKeyShare.data) - if hs.sharedKey == nil { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: invalid client key share") - } - - c.serverName = hs.clientHello.serverName - - if c.extraConfig != nil && c.extraConfig.ReceivedExtensions != nil { - c.extraConfig.ReceivedExtensions(typeClientHello, hs.clientHello.additionalExtensions) - } - - selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols) - if err != nil { - hs.alpnNegotiationErr = err - } - hs.encryptedExtensions.alpnProtocol = selectedProto - c.clientProtocol = selectedProto - - return nil -} - -func (hs *serverHandshakeStateTLS13) checkForResumption() error { - c := hs.c - - if c.config.SessionTicketsDisabled { - return nil - } - - modeOK := false - for _, mode := range hs.clientHello.pskModes { - if mode == pskModeDHE { - modeOK = true - break - } - } - if !modeOK { - return nil - } - - if len(hs.clientHello.pskIdentities) != len(hs.clientHello.pskBinders) { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: invalid or missing PSK binders") - } - if len(hs.clientHello.pskIdentities) == 0 { - return nil - } - - for i, identity := range hs.clientHello.pskIdentities { - if i >= maxClientPSKIdentities { - break - } - - plaintext, _ := c.decryptTicket(identity.label) - if plaintext == nil { - continue - } - sessionState := new(sessionStateTLS13) - if ok := sessionState.unmarshal(plaintext); !ok { - continue - } - - if hs.clientHello.earlyData { - if sessionState.maxEarlyData == 0 { - c.sendAlert(alertUnsupportedExtension) - return errors.New("tls: client sent unexpected early data") - } - - if hs.alpnNegotiationErr == nil && sessionState.alpn == c.clientProtocol && - c.extraConfig != nil && c.extraConfig.MaxEarlyData > 0 && - c.extraConfig.Accept0RTT != nil && c.extraConfig.Accept0RTT(sessionState.appData) { - hs.encryptedExtensions.earlyData = true - c.used0RTT = true - } - } - - createdAt := time.Unix(int64(sessionState.createdAt), 0) - if c.config.time().Sub(createdAt) > maxSessionTicketLifetime { - continue - } - - // We don't check the obfuscated ticket age because it's affected by - // clock skew and it's only a freshness signal useful for shrinking the - // window for replay attacks, which don't affect us as we don't do 0-RTT. - - pskSuite := cipherSuiteTLS13ByID(sessionState.cipherSuite) - if pskSuite == nil || pskSuite.hash != hs.suite.hash { - continue - } - - // PSK connections don't re-establish client certificates, but carry - // them over in the session ticket. Ensure the presence of client certs - // in the ticket is consistent with the configured requirements. - sessionHasClientCerts := len(sessionState.certificate.Certificate) != 0 - needClientCerts := requiresClientCert(c.config.ClientAuth) - if needClientCerts && !sessionHasClientCerts { - continue - } - if sessionHasClientCerts && c.config.ClientAuth == NoClientCert { - continue - } - - psk := hs.suite.expandLabel(sessionState.resumptionSecret, "resumption", - nil, hs.suite.hash.Size()) - hs.earlySecret = hs.suite.extract(psk, nil) - binderKey := hs.suite.deriveSecret(hs.earlySecret, resumptionBinderLabel, nil) - // Clone the transcript in case a HelloRetryRequest was recorded. - transcript := cloneHash(hs.transcript, hs.suite.hash) - if transcript == nil { - c.sendAlert(alertInternalError) - return errors.New("tls: internal error: failed to clone hash") - } - clientHelloBytes, err := hs.clientHello.marshalWithoutBinders() - if err != nil { - c.sendAlert(alertInternalError) - return err - } - transcript.Write(clientHelloBytes) - pskBinder := hs.suite.finishedHash(binderKey, transcript) - if !hmac.Equal(hs.clientHello.pskBinders[i], pskBinder) { - c.sendAlert(alertDecryptError) - return errors.New("tls: invalid PSK binder") - } - - c.didResume = true - if err := c.processCertsFromClient(sessionState.certificate); err != nil { - return err - } - - h := cloneHash(hs.transcript, hs.suite.hash) - clientHelloWithBindersBytes, err := hs.clientHello.marshal() - if err != nil { - c.sendAlert(alertInternalError) - return err - } - h.Write(clientHelloWithBindersBytes) - if hs.encryptedExtensions.earlyData { - clientEarlySecret := hs.suite.deriveSecret(hs.earlySecret, "c e traffic", h) - c.in.exportKey(Encryption0RTT, hs.suite, clientEarlySecret) - if err := c.config.writeKeyLog(keyLogLabelEarlyTraffic, hs.clientHello.random, clientEarlySecret); err != nil { - c.sendAlert(alertInternalError) - return err - } - } - - hs.hello.selectedIdentityPresent = true - hs.hello.selectedIdentity = uint16(i) - hs.usingPSK = true - return nil - } - - return nil -} - -// cloneHash uses the encoding.BinaryMarshaler and encoding.BinaryUnmarshaler -// interfaces implemented by standard library hashes to clone the state of in -// to a new instance of h. It returns nil if the operation fails. -func cloneHash(in hash.Hash, h crypto.Hash) hash.Hash { - // Recreate the interface to avoid importing encoding. - type binaryMarshaler interface { - MarshalBinary() (data []byte, err error) - UnmarshalBinary(data []byte) error - } - marshaler, ok := in.(binaryMarshaler) - if !ok { - return nil - } - state, err := marshaler.MarshalBinary() - if err != nil { - return nil - } - out := h.New() - unmarshaler, ok := out.(binaryMarshaler) - if !ok { - return nil - } - if err := unmarshaler.UnmarshalBinary(state); err != nil { - return nil - } - return out -} - -func (hs *serverHandshakeStateTLS13) pickCertificate() error { - c := hs.c - - // Only one of PSK and certificates are used at a time. - if hs.usingPSK { - return nil - } - - // signature_algorithms is required in TLS 1.3. See RFC 8446, Section 4.2.3. - if len(hs.clientHello.supportedSignatureAlgorithms) == 0 { - return c.sendAlert(alertMissingExtension) - } - - certificate, err := c.config.getCertificate(newClientHelloInfo(hs.ctx, c, hs.clientHello)) - if err != nil { - if err == errNoCertificates { - c.sendAlert(alertUnrecognizedName) - } else { - c.sendAlert(alertInternalError) - } - return err - } - hs.sigAlg, err = selectSignatureScheme(c.vers, certificate, hs.clientHello.supportedSignatureAlgorithms) - if err != nil { - // getCertificate returned a certificate that is unsupported or - // incompatible with the client's signature algorithms. - c.sendAlert(alertHandshakeFailure) - return err - } - hs.cert = certificate - - return nil -} - -// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility -// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4. -func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error { - if hs.sentDummyCCS { - return nil - } - hs.sentDummyCCS = true - - return hs.c.writeChangeCipherRecord() -} - -func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error { - c := hs.c - - // The first ClientHello gets double-hashed into the transcript upon a - // HelloRetryRequest. See RFC 8446, Section 4.4.1. - if err := transcriptMsg(hs.clientHello, hs.transcript); err != nil { - return err - } - chHash := hs.transcript.Sum(nil) - hs.transcript.Reset() - hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) - hs.transcript.Write(chHash) - - helloRetryRequest := &serverHelloMsg{ - vers: hs.hello.vers, - random: helloRetryRequestRandom, - sessionId: hs.hello.sessionId, - cipherSuite: hs.hello.cipherSuite, - compressionMethod: hs.hello.compressionMethod, - supportedVersion: hs.hello.supportedVersion, - selectedGroup: selectedGroup, - } - - if _, err := hs.c.writeHandshakeRecord(helloRetryRequest, hs.transcript); err != nil { - return err - } - - if err := hs.sendDummyChangeCipherSpec(); err != nil { - return err - } - - // clientHelloMsg is not included in the transcript. - msg, err := c.readHandshake(nil) - if err != nil { - return err - } - - clientHello, ok := msg.(*clientHelloMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(clientHello, msg) - } - - if len(clientHello.keyShares) != 1 || clientHello.keyShares[0].group != selectedGroup { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: client sent invalid key share in second ClientHello") - } - - if clientHello.earlyData { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: client indicated early data in second ClientHello") - } - - if illegalClientHelloChange(clientHello, hs.clientHello) { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: client illegally modified second ClientHello") - } - - if clientHello.earlyData { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: client offered 0-RTT data in second ClientHello") - } - - hs.clientHello = clientHello - return nil -} - -// illegalClientHelloChange reports whether the two ClientHello messages are -// different, with the exception of the changes allowed before and after a -// HelloRetryRequest. See RFC 8446, Section 4.1.2. -func illegalClientHelloChange(ch, ch1 *clientHelloMsg) bool { - if len(ch.supportedVersions) != len(ch1.supportedVersions) || - len(ch.cipherSuites) != len(ch1.cipherSuites) || - len(ch.supportedCurves) != len(ch1.supportedCurves) || - len(ch.supportedSignatureAlgorithms) != len(ch1.supportedSignatureAlgorithms) || - len(ch.supportedSignatureAlgorithmsCert) != len(ch1.supportedSignatureAlgorithmsCert) || - len(ch.alpnProtocols) != len(ch1.alpnProtocols) { - return true - } - for i := range ch.supportedVersions { - if ch.supportedVersions[i] != ch1.supportedVersions[i] { - return true - } - } - for i := range ch.cipherSuites { - if ch.cipherSuites[i] != ch1.cipherSuites[i] { - return true - } - } - for i := range ch.supportedCurves { - if ch.supportedCurves[i] != ch1.supportedCurves[i] { - return true - } - } - for i := range ch.supportedSignatureAlgorithms { - if ch.supportedSignatureAlgorithms[i] != ch1.supportedSignatureAlgorithms[i] { - return true - } - } - for i := range ch.supportedSignatureAlgorithmsCert { - if ch.supportedSignatureAlgorithmsCert[i] != ch1.supportedSignatureAlgorithmsCert[i] { - return true - } - } - for i := range ch.alpnProtocols { - if ch.alpnProtocols[i] != ch1.alpnProtocols[i] { - return true - } - } - return ch.vers != ch1.vers || - !bytes.Equal(ch.random, ch1.random) || - !bytes.Equal(ch.sessionId, ch1.sessionId) || - !bytes.Equal(ch.compressionMethods, ch1.compressionMethods) || - ch.serverName != ch1.serverName || - ch.ocspStapling != ch1.ocspStapling || - !bytes.Equal(ch.supportedPoints, ch1.supportedPoints) || - ch.ticketSupported != ch1.ticketSupported || - !bytes.Equal(ch.sessionTicket, ch1.sessionTicket) || - ch.secureRenegotiationSupported != ch1.secureRenegotiationSupported || - !bytes.Equal(ch.secureRenegotiation, ch1.secureRenegotiation) || - ch.scts != ch1.scts || - !bytes.Equal(ch.cookie, ch1.cookie) || - !bytes.Equal(ch.pskModes, ch1.pskModes) -} - -func (hs *serverHandshakeStateTLS13) sendServerParameters() error { - c := hs.c - - if err := transcriptMsg(hs.clientHello, hs.transcript); err != nil { - return err - } - if _, err := hs.c.writeHandshakeRecord(hs.hello, hs.transcript); err != nil { - return err - } - - if err := hs.sendDummyChangeCipherSpec(); err != nil { - return err - } - - earlySecret := hs.earlySecret - if earlySecret == nil { - earlySecret = hs.suite.extract(nil, nil) - } - hs.handshakeSecret = hs.suite.extract(hs.sharedKey, - hs.suite.deriveSecret(earlySecret, "derived", nil)) - - clientSecret := hs.suite.deriveSecret(hs.handshakeSecret, - clientHandshakeTrafficLabel, hs.transcript) - c.in.exportKey(EncryptionHandshake, hs.suite, clientSecret) - c.in.setTrafficSecret(hs.suite, clientSecret) - serverSecret := hs.suite.deriveSecret(hs.handshakeSecret, - serverHandshakeTrafficLabel, hs.transcript) - c.out.exportKey(EncryptionHandshake, hs.suite, serverSecret) - c.out.setTrafficSecret(hs.suite, serverSecret) - - err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.clientHello.random, clientSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.clientHello.random, serverSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - - if hs.alpnNegotiationErr != nil { - c.sendAlert(alertNoApplicationProtocol) - return hs.alpnNegotiationErr - } - if hs.c.extraConfig != nil && hs.c.extraConfig.GetExtensions != nil { - hs.encryptedExtensions.additionalExtensions = hs.c.extraConfig.GetExtensions(typeEncryptedExtensions) - } - - if _, err := hs.c.writeHandshakeRecord(hs.encryptedExtensions, hs.transcript); err != nil { - return err - } - - return nil -} - -func (hs *serverHandshakeStateTLS13) requestClientCert() bool { - return hs.c.config.ClientAuth >= RequestClientCert && !hs.usingPSK -} - -func (hs *serverHandshakeStateTLS13) sendServerCertificate() error { - c := hs.c - - // Only one of PSK and certificates are used at a time. - if hs.usingPSK { - return nil - } - - if hs.requestClientCert() { - // Request a client certificate - certReq := new(certificateRequestMsgTLS13) - certReq.ocspStapling = true - certReq.scts = true - certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms() - if c.config.ClientCAs != nil { - certReq.certificateAuthorities = c.config.ClientCAs.Subjects() - } - - if _, err := hs.c.writeHandshakeRecord(certReq, hs.transcript); err != nil { - return err - } - } - - certMsg := new(certificateMsgTLS13) - - certMsg.certificate = *hs.cert - certMsg.scts = hs.clientHello.scts && len(hs.cert.SignedCertificateTimestamps) > 0 - certMsg.ocspStapling = hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 - - if _, err := hs.c.writeHandshakeRecord(certMsg, hs.transcript); err != nil { - return err - } - - certVerifyMsg := new(certificateVerifyMsg) - certVerifyMsg.hasSignatureAlgorithm = true - certVerifyMsg.signatureAlgorithm = hs.sigAlg - - sigType, sigHash, err := typeAndHashFromSignatureScheme(hs.sigAlg) - if err != nil { - return c.sendAlert(alertInternalError) - } - - signed := signedMessage(sigHash, serverSignatureContext, hs.transcript) - signOpts := crypto.SignerOpts(sigHash) - if sigType == signatureRSAPSS { - signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash} - } - sig, err := hs.cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts) - if err != nil { - public := hs.cert.PrivateKey.(crypto.Signer).Public() - if rsaKey, ok := public.(*rsa.PublicKey); ok && sigType == signatureRSAPSS && - rsaKey.N.BitLen()/8 < sigHash.Size()*2+2 { // key too small for RSA-PSS - c.sendAlert(alertHandshakeFailure) - } else { - c.sendAlert(alertInternalError) - } - return errors.New("tls: failed to sign handshake: " + err.Error()) - } - certVerifyMsg.signature = sig - - if _, err := hs.c.writeHandshakeRecord(certVerifyMsg, hs.transcript); err != nil { - return err - } - - return nil -} - -func (hs *serverHandshakeStateTLS13) sendServerFinished() error { - c := hs.c - - finished := &finishedMsg{ - verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript), - } - - if _, err := hs.c.writeHandshakeRecord(finished, hs.transcript); err != nil { - return err - } - - // Derive secrets that take context through the server Finished. - - hs.masterSecret = hs.suite.extract(nil, - hs.suite.deriveSecret(hs.handshakeSecret, "derived", nil)) - - hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret, - clientApplicationTrafficLabel, hs.transcript) - serverSecret := hs.suite.deriveSecret(hs.masterSecret, - serverApplicationTrafficLabel, hs.transcript) - c.out.exportKey(EncryptionApplication, hs.suite, serverSecret) - c.out.setTrafficSecret(hs.suite, serverSecret) - - err := c.config.writeKeyLog(keyLogLabelClientTraffic, hs.clientHello.random, hs.trafficSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.clientHello.random, serverSecret) - if err != nil { - c.sendAlert(alertInternalError) - return err - } - - c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript) - - // If we did not request client certificates, at this point we can - // precompute the client finished and roll the transcript forward to send - // session tickets in our first flight. - if !hs.requestClientCert() { - if err := hs.sendSessionTickets(); err != nil { - return err - } - } - - return nil -} - -func (hs *serverHandshakeStateTLS13) shouldSendSessionTickets() bool { - if hs.c.config.SessionTicketsDisabled { - return false - } - - // Don't send tickets the client wouldn't use. See RFC 8446, Section 4.2.9. - for _, pskMode := range hs.clientHello.pskModes { - if pskMode == pskModeDHE { - return true - } - } - return false -} - -func (hs *serverHandshakeStateTLS13) sendSessionTickets() error { - c := hs.c - - hs.clientFinished = hs.suite.finishedHash(c.in.trafficSecret, hs.transcript) - finishedMsg := &finishedMsg{ - verifyData: hs.clientFinished, - } - if err := transcriptMsg(finishedMsg, hs.transcript); err != nil { - return err - } - - if !hs.shouldSendSessionTickets() { - return nil - } - - c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret, - resumptionLabel, hs.transcript) - - // Don't send session tickets when the alternative record layer is set. - // Instead, save the resumption secret on the Conn. - // Session tickets can then be generated by calling Conn.GetSessionTicket(). - if hs.c.extraConfig != nil && hs.c.extraConfig.AlternativeRecordLayer != nil { - return nil - } - - m, err := hs.c.getSessionTicketMsg(nil) - if err != nil { - return err - } - - if _, err := c.writeHandshakeRecord(m, nil); err != nil { - return err - } - - return nil -} - -func (hs *serverHandshakeStateTLS13) readClientCertificate() error { - c := hs.c - - if !hs.requestClientCert() { - // Make sure the connection is still being verified whether or not - // the server requested a client certificate. - if c.config.VerifyConnection != nil { - if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - return nil - } - - // If we requested a client certificate, then the client must send a - // certificate message. If it's empty, no CertificateVerify is sent. - - msg, err := c.readHandshake(hs.transcript) - if err != nil { - return err - } - - certMsg, ok := msg.(*certificateMsgTLS13) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certMsg, msg) - } - - if err := c.processCertsFromClient(certMsg.certificate); err != nil { - return err - } - - if c.config.VerifyConnection != nil { - if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil { - c.sendAlert(alertBadCertificate) - return err - } - } - - if len(certMsg.certificate.Certificate) != 0 { - // certificateVerifyMsg is included in the transcript, but not until - // after we verify the handshake signature, since the state before - // this message was sent is used. - msg, err = c.readHandshake(nil) - if err != nil { - return err - } - - certVerify, ok := msg.(*certificateVerifyMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certVerify, msg) - } - - // See RFC 8446, Section 4.4.3. - if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: client certificate used with invalid signature algorithm") - } - sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm) - if err != nil { - return c.sendAlert(alertInternalError) - } - if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: client certificate used with invalid signature algorithm") - } - signed := signedMessage(sigHash, clientSignatureContext, hs.transcript) - if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey, - sigHash, signed, certVerify.signature); err != nil { - c.sendAlert(alertDecryptError) - return errors.New("tls: invalid signature by the client certificate: " + err.Error()) - } - - if err := transcriptMsg(certVerify, hs.transcript); err != nil { - return err - } - } - - // If we waited until the client certificates to send session tickets, we - // are ready to do it now. - if err := hs.sendSessionTickets(); err != nil { - return err - } - - return nil -} - -func (hs *serverHandshakeStateTLS13) readClientFinished() error { - c := hs.c - - // finishedMsg is not included in the transcript. - msg, err := c.readHandshake(nil) - if err != nil { - return err - } - - finished, ok := msg.(*finishedMsg) - if !ok { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(finished, msg) - } - - if !hmac.Equal(hs.clientFinished, finished.verifyData) { - c.sendAlert(alertDecryptError) - return errors.New("tls: invalid client finished hash") - } - - c.in.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret) - c.in.setTrafficSecret(hs.suite, hs.trafficSecret) - - return nil -} diff --git a/vendor/github.com/quic-go/qtls-go1-19/key_agreement.go b/vendor/github.com/quic-go/qtls-go1-19/key_agreement.go deleted file mode 100644 index 453a8dcf0..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/key_agreement.go +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package qtls - -import ( - "crypto" - "crypto/md5" - "crypto/rsa" - "crypto/sha1" - "crypto/x509" - "errors" - "fmt" - "io" -) - -// a keyAgreement implements the client and server side of a TLS key agreement -// protocol by generating and processing key exchange messages. -type keyAgreement interface { - // On the server side, the first two methods are called in order. - - // In the case that the key agreement protocol doesn't use a - // ServerKeyExchange message, generateServerKeyExchange can return nil, - // nil. - generateServerKeyExchange(*config, *Certificate, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error) - processClientKeyExchange(*config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error) - - // On the client side, the next two methods are called in order. - - // This method may not be called if the server doesn't send a - // ServerKeyExchange message. - processServerKeyExchange(*config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error - generateClientKeyExchange(*config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) -} - -var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message") -var errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message") - -// rsaKeyAgreement implements the standard TLS key agreement where the client -// encrypts the pre-master secret to the server's public key. -type rsaKeyAgreement struct{} - -func (ka rsaKeyAgreement) generateServerKeyExchange(config *config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { - return nil, nil -} - -func (ka rsaKeyAgreement) processClientKeyExchange(config *config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { - if len(ckx.ciphertext) < 2 { - return nil, errClientKeyExchange - } - ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) - if ciphertextLen != len(ckx.ciphertext)-2 { - return nil, errClientKeyExchange - } - ciphertext := ckx.ciphertext[2:] - - priv, ok := cert.PrivateKey.(crypto.Decrypter) - if !ok { - return nil, errors.New("tls: certificate private key does not implement crypto.Decrypter") - } - // Perform constant time RSA PKCS #1 v1.5 decryption - preMasterSecret, err := priv.Decrypt(config.rand(), ciphertext, &rsa.PKCS1v15DecryptOptions{SessionKeyLen: 48}) - if err != nil { - return nil, err - } - // We don't check the version number in the premaster secret. For one, - // by checking it, we would leak information about the validity of the - // encrypted pre-master secret. Secondly, it provides only a small - // benefit against a downgrade attack and some implementations send the - // wrong version anyway. See the discussion at the end of section - // 7.4.7.1 of RFC 4346. - return preMasterSecret, nil -} - -func (ka rsaKeyAgreement) processServerKeyExchange(config *config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { - return errors.New("tls: unexpected ServerKeyExchange") -} - -func (ka rsaKeyAgreement) generateClientKeyExchange(config *config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { - preMasterSecret := make([]byte, 48) - preMasterSecret[0] = byte(clientHello.vers >> 8) - preMasterSecret[1] = byte(clientHello.vers) - _, err := io.ReadFull(config.rand(), preMasterSecret[2:]) - if err != nil { - return nil, nil, err - } - - rsaKey, ok := cert.PublicKey.(*rsa.PublicKey) - if !ok { - return nil, nil, errors.New("tls: server certificate contains incorrect key type for selected ciphersuite") - } - encrypted, err := rsa.EncryptPKCS1v15(config.rand(), rsaKey, preMasterSecret) - if err != nil { - return nil, nil, err - } - ckx := new(clientKeyExchangeMsg) - ckx.ciphertext = make([]byte, len(encrypted)+2) - ckx.ciphertext[0] = byte(len(encrypted) >> 8) - ckx.ciphertext[1] = byte(len(encrypted)) - copy(ckx.ciphertext[2:], encrypted) - return preMasterSecret, ckx, nil -} - -// sha1Hash calculates a SHA1 hash over the given byte slices. -func sha1Hash(slices [][]byte) []byte { - hsha1 := sha1.New() - for _, slice := range slices { - hsha1.Write(slice) - } - return hsha1.Sum(nil) -} - -// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the -// concatenation of an MD5 and SHA1 hash. -func md5SHA1Hash(slices [][]byte) []byte { - md5sha1 := make([]byte, md5.Size+sha1.Size) - hmd5 := md5.New() - for _, slice := range slices { - hmd5.Write(slice) - } - copy(md5sha1, hmd5.Sum(nil)) - copy(md5sha1[md5.Size:], sha1Hash(slices)) - return md5sha1 -} - -// hashForServerKeyExchange hashes the given slices and returns their digest -// using the given hash function (for >= TLS 1.2) or using a default based on -// the sigType (for earlier TLS versions). For Ed25519 signatures, which don't -// do pre-hashing, it returns the concatenation of the slices. -func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint16, slices ...[]byte) []byte { - if sigType == signatureEd25519 { - var signed []byte - for _, slice := range slices { - signed = append(signed, slice...) - } - return signed - } - if version >= VersionTLS12 { - h := hashFunc.New() - for _, slice := range slices { - h.Write(slice) - } - digest := h.Sum(nil) - return digest - } - if sigType == signatureECDSA { - return sha1Hash(slices) - } - return md5SHA1Hash(slices) -} - -// ecdheKeyAgreement implements a TLS key agreement where the server -// generates an ephemeral EC public/private key pair and signs it. The -// pre-master secret is then calculated using ECDH. The signature may -// be ECDSA, Ed25519 or RSA. -type ecdheKeyAgreement struct { - version uint16 - isRSA bool - params ecdheParameters - - // ckx and preMasterSecret are generated in processServerKeyExchange - // and returned in generateClientKeyExchange. - ckx *clientKeyExchangeMsg - preMasterSecret []byte -} - -func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { - var curveID CurveID - for _, c := range clientHello.supportedCurves { - if config.supportsCurve(c) { - curveID = c - break - } - } - - if curveID == 0 { - return nil, errors.New("tls: no supported elliptic curves offered") - } - if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { - return nil, errors.New("tls: CurvePreferences includes unsupported curve") - } - - params, err := generateECDHEParameters(config.rand(), curveID) - if err != nil { - return nil, err - } - ka.params = params - - // See RFC 4492, Section 5.4. - ecdhePublic := params.PublicKey() - serverECDHEParams := make([]byte, 1+2+1+len(ecdhePublic)) - serverECDHEParams[0] = 3 // named curve - serverECDHEParams[1] = byte(curveID >> 8) - serverECDHEParams[2] = byte(curveID) - serverECDHEParams[3] = byte(len(ecdhePublic)) - copy(serverECDHEParams[4:], ecdhePublic) - - priv, ok := cert.PrivateKey.(crypto.Signer) - if !ok { - return nil, fmt.Errorf("tls: certificate private key of type %T does not implement crypto.Signer", cert.PrivateKey) - } - - var signatureAlgorithm SignatureScheme - var sigType uint8 - var sigHash crypto.Hash - if ka.version >= VersionTLS12 { - signatureAlgorithm, err = selectSignatureScheme(ka.version, cert, clientHello.supportedSignatureAlgorithms) - if err != nil { - return nil, err - } - sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm) - if err != nil { - return nil, err - } - } else { - sigType, sigHash, err = legacyTypeAndHashFromPublicKey(priv.Public()) - if err != nil { - return nil, err - } - } - if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA { - return nil, errors.New("tls: certificate cannot be used with the selected cipher suite") - } - - signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, hello.random, serverECDHEParams) - - signOpts := crypto.SignerOpts(sigHash) - if sigType == signatureRSAPSS { - signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash} - } - sig, err := priv.Sign(config.rand(), signed, signOpts) - if err != nil { - return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error()) - } - - skx := new(serverKeyExchangeMsg) - sigAndHashLen := 0 - if ka.version >= VersionTLS12 { - sigAndHashLen = 2 - } - skx.key = make([]byte, len(serverECDHEParams)+sigAndHashLen+2+len(sig)) - copy(skx.key, serverECDHEParams) - k := skx.key[len(serverECDHEParams):] - if ka.version >= VersionTLS12 { - k[0] = byte(signatureAlgorithm >> 8) - k[1] = byte(signatureAlgorithm) - k = k[2:] - } - k[0] = byte(len(sig) >> 8) - k[1] = byte(len(sig)) - copy(k[2:], sig) - - return skx, nil -} - -func (ka *ecdheKeyAgreement) processClientKeyExchange(config *config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { - if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { - return nil, errClientKeyExchange - } - - preMasterSecret := ka.params.SharedKey(ckx.ciphertext[1:]) - if preMasterSecret == nil { - return nil, errClientKeyExchange - } - - return preMasterSecret, nil -} - -func (ka *ecdheKeyAgreement) processServerKeyExchange(config *config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { - if len(skx.key) < 4 { - return errServerKeyExchange - } - if skx.key[0] != 3 { // named curve - return errors.New("tls: server selected unsupported curve") - } - curveID := CurveID(skx.key[1])<<8 | CurveID(skx.key[2]) - - publicLen := int(skx.key[3]) - if publicLen+4 > len(skx.key) { - return errServerKeyExchange - } - serverECDHEParams := skx.key[:4+publicLen] - publicKey := serverECDHEParams[4:] - - sig := skx.key[4+publicLen:] - if len(sig) < 2 { - return errServerKeyExchange - } - - if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { - return errors.New("tls: server selected unsupported curve") - } - - params, err := generateECDHEParameters(config.rand(), curveID) - if err != nil { - return err - } - ka.params = params - - ka.preMasterSecret = params.SharedKey(publicKey) - if ka.preMasterSecret == nil { - return errServerKeyExchange - } - - ourPublicKey := params.PublicKey() - ka.ckx = new(clientKeyExchangeMsg) - ka.ckx.ciphertext = make([]byte, 1+len(ourPublicKey)) - ka.ckx.ciphertext[0] = byte(len(ourPublicKey)) - copy(ka.ckx.ciphertext[1:], ourPublicKey) - - var sigType uint8 - var sigHash crypto.Hash - if ka.version >= VersionTLS12 { - signatureAlgorithm := SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1]) - sig = sig[2:] - if len(sig) < 2 { - return errServerKeyExchange - } - - if !isSupportedSignatureAlgorithm(signatureAlgorithm, clientHello.supportedSignatureAlgorithms) { - return errors.New("tls: certificate used with invalid signature algorithm") - } - sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm) - if err != nil { - return err - } - } else { - sigType, sigHash, err = legacyTypeAndHashFromPublicKey(cert.PublicKey) - if err != nil { - return err - } - } - if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA { - return errServerKeyExchange - } - - sigLen := int(sig[0])<<8 | int(sig[1]) - if sigLen+2 != len(sig) { - return errServerKeyExchange - } - sig = sig[2:] - - signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, serverHello.random, serverECDHEParams) - if err := verifyHandshakeSignature(sigType, cert.PublicKey, sigHash, signed, sig); err != nil { - return errors.New("tls: invalid signature by the server certificate: " + err.Error()) - } - return nil -} - -func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { - if ka.ckx == nil { - return nil, nil, errors.New("tls: missing ServerKeyExchange message") - } - - return ka.preMasterSecret, ka.ckx, nil -} diff --git a/vendor/github.com/quic-go/qtls-go1-19/key_schedule.go b/vendor/github.com/quic-go/qtls-go1-19/key_schedule.go deleted file mode 100644 index 708bdc7c3..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/key_schedule.go +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package qtls - -import ( - "crypto/elliptic" - "crypto/hmac" - "errors" - "fmt" - "hash" - "io" - "math/big" - - "golang.org/x/crypto/cryptobyte" - "golang.org/x/crypto/curve25519" - "golang.org/x/crypto/hkdf" -) - -// This file contains the functions necessary to compute the TLS 1.3 key -// schedule. See RFC 8446, Section 7. - -const ( - resumptionBinderLabel = "res binder" - clientHandshakeTrafficLabel = "c hs traffic" - serverHandshakeTrafficLabel = "s hs traffic" - clientApplicationTrafficLabel = "c ap traffic" - serverApplicationTrafficLabel = "s ap traffic" - exporterLabel = "exp master" - resumptionLabel = "res master" - trafficUpdateLabel = "traffic upd" -) - -// expandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1. -func (c *cipherSuiteTLS13) expandLabel(secret []byte, label string, context []byte, length int) []byte { - var hkdfLabel cryptobyte.Builder - hkdfLabel.AddUint16(uint16(length)) - hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes([]byte("tls13 ")) - b.AddBytes([]byte(label)) - }) - hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(context) - }) - hkdfLabelBytes, err := hkdfLabel.Bytes() - if err != nil { - // Rather than calling BytesOrPanic, we explicitly handle this error, in - // order to provide a reasonable error message. It should be basically - // impossible for this to panic, and routing errors back through the - // tree rooted in this function is quite painful. The labels are fixed - // size, and the context is either a fixed-length computed hash, or - // parsed from a field which has the same length limitation. As such, an - // error here is likely to only be caused during development. - // - // NOTE: another reasonable approach here might be to return a - // randomized slice if we encounter an error, which would break the - // connection, but avoid panicking. This would perhaps be safer but - // significantly more confusing to users. - panic(fmt.Errorf("failed to construct HKDF label: %s", err)) - } - out := make([]byte, length) - n, err := hkdf.Expand(c.hash.New, secret, hkdfLabelBytes).Read(out) - if err != nil || n != length { - panic("tls: HKDF-Expand-Label invocation failed unexpectedly") - } - return out -} - -// deriveSecret implements Derive-Secret from RFC 8446, Section 7.1. -func (c *cipherSuiteTLS13) deriveSecret(secret []byte, label string, transcript hash.Hash) []byte { - if transcript == nil { - transcript = c.hash.New() - } - return c.expandLabel(secret, label, transcript.Sum(nil), c.hash.Size()) -} - -// extract implements HKDF-Extract with the cipher suite hash. -func (c *cipherSuiteTLS13) extract(newSecret, currentSecret []byte) []byte { - if newSecret == nil { - newSecret = make([]byte, c.hash.Size()) - } - return hkdf.Extract(c.hash.New, newSecret, currentSecret) -} - -// nextTrafficSecret generates the next traffic secret, given the current one, -// according to RFC 8446, Section 7.2. -func (c *cipherSuiteTLS13) nextTrafficSecret(trafficSecret []byte) []byte { - return c.expandLabel(trafficSecret, trafficUpdateLabel, nil, c.hash.Size()) -} - -// trafficKey generates traffic keys according to RFC 8446, Section 7.3. -func (c *cipherSuiteTLS13) trafficKey(trafficSecret []byte) (key, iv []byte) { - key = c.expandLabel(trafficSecret, "key", nil, c.keyLen) - iv = c.expandLabel(trafficSecret, "iv", nil, aeadNonceLength) - return -} - -// finishedHash generates the Finished verify_data or PskBinderEntry according -// to RFC 8446, Section 4.4.4. See sections 4.4 and 4.2.11.2 for the baseKey -// selection. -func (c *cipherSuiteTLS13) finishedHash(baseKey []byte, transcript hash.Hash) []byte { - finishedKey := c.expandLabel(baseKey, "finished", nil, c.hash.Size()) - verifyData := hmac.New(c.hash.New, finishedKey) - verifyData.Write(transcript.Sum(nil)) - return verifyData.Sum(nil) -} - -// exportKeyingMaterial implements RFC5705 exporters for TLS 1.3 according to -// RFC 8446, Section 7.5. -func (c *cipherSuiteTLS13) exportKeyingMaterial(masterSecret []byte, transcript hash.Hash) func(string, []byte, int) ([]byte, error) { - expMasterSecret := c.deriveSecret(masterSecret, exporterLabel, transcript) - return func(label string, context []byte, length int) ([]byte, error) { - secret := c.deriveSecret(expMasterSecret, label, nil) - h := c.hash.New() - h.Write(context) - return c.expandLabel(secret, "exporter", h.Sum(nil), length), nil - } -} - -// ecdheParameters implements Diffie-Hellman with either NIST curves or X25519, -// according to RFC 8446, Section 4.2.8.2. -type ecdheParameters interface { - CurveID() CurveID - PublicKey() []byte - SharedKey(peerPublicKey []byte) []byte -} - -func generateECDHEParameters(rand io.Reader, curveID CurveID) (ecdheParameters, error) { - if curveID == X25519 { - privateKey := make([]byte, curve25519.ScalarSize) - if _, err := io.ReadFull(rand, privateKey); err != nil { - return nil, err - } - publicKey, err := curve25519.X25519(privateKey, curve25519.Basepoint) - if err != nil { - return nil, err - } - return &x25519Parameters{privateKey: privateKey, publicKey: publicKey}, nil - } - - curve, ok := curveForCurveID(curveID) - if !ok { - return nil, errors.New("tls: internal error: unsupported curve") - } - - p := &nistParameters{curveID: curveID} - var err error - p.privateKey, p.x, p.y, err = elliptic.GenerateKey(curve, rand) - if err != nil { - return nil, err - } - return p, nil -} - -func curveForCurveID(id CurveID) (elliptic.Curve, bool) { - switch id { - case CurveP256: - return elliptic.P256(), true - case CurveP384: - return elliptic.P384(), true - case CurveP521: - return elliptic.P521(), true - default: - return nil, false - } -} - -type nistParameters struct { - privateKey []byte - x, y *big.Int // public key - curveID CurveID -} - -func (p *nistParameters) CurveID() CurveID { - return p.curveID -} - -func (p *nistParameters) PublicKey() []byte { - curve, _ := curveForCurveID(p.curveID) - return elliptic.Marshal(curve, p.x, p.y) -} - -func (p *nistParameters) SharedKey(peerPublicKey []byte) []byte { - curve, _ := curveForCurveID(p.curveID) - // Unmarshal also checks whether the given point is on the curve. - x, y := elliptic.Unmarshal(curve, peerPublicKey) - if x == nil { - return nil - } - - xShared, _ := curve.ScalarMult(x, y, p.privateKey) - sharedKey := make([]byte, (curve.Params().BitSize+7)/8) - return xShared.FillBytes(sharedKey) -} - -type x25519Parameters struct { - privateKey []byte - publicKey []byte -} - -func (p *x25519Parameters) CurveID() CurveID { - return X25519 -} - -func (p *x25519Parameters) PublicKey() []byte { - return p.publicKey[:] -} - -func (p *x25519Parameters) SharedKey(peerPublicKey []byte) []byte { - sharedKey, err := curve25519.X25519(p.privateKey, peerPublicKey) - if err != nil { - return nil - } - return sharedKey -} diff --git a/vendor/github.com/quic-go/qtls-go1-19/notboring.go b/vendor/github.com/quic-go/qtls-go1-19/notboring.go deleted file mode 100644 index f292e4f02..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/notboring.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package qtls - -func needFIPS() bool { return false } - -func supportedSignatureAlgorithms() []SignatureScheme { - return defaultSupportedSignatureAlgorithms -} - -func fipsMinVersion(c *config) uint16 { panic("fipsMinVersion") } -func fipsMaxVersion(c *config) uint16 { panic("fipsMaxVersion") } -func fipsCurvePreferences(c *config) []CurveID { panic("fipsCurvePreferences") } -func fipsCipherSuites(c *config) []uint16 { panic("fipsCipherSuites") } - -var fipsSupportedSignatureAlgorithms []SignatureScheme diff --git a/vendor/github.com/quic-go/qtls-go1-19/prf.go b/vendor/github.com/quic-go/qtls-go1-19/prf.go deleted file mode 100644 index 9eb0221a0..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/prf.go +++ /dev/null @@ -1,283 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package qtls - -import ( - "crypto" - "crypto/hmac" - "crypto/md5" - "crypto/sha1" - "crypto/sha256" - "crypto/sha512" - "errors" - "fmt" - "hash" -) - -// Split a premaster secret in two as specified in RFC 4346, Section 5. -func splitPreMasterSecret(secret []byte) (s1, s2 []byte) { - s1 = secret[0 : (len(secret)+1)/2] - s2 = secret[len(secret)/2:] - return -} - -// pHash implements the P_hash function, as defined in RFC 4346, Section 5. -func pHash(result, secret, seed []byte, hash func() hash.Hash) { - h := hmac.New(hash, secret) - h.Write(seed) - a := h.Sum(nil) - - j := 0 - for j < len(result) { - h.Reset() - h.Write(a) - h.Write(seed) - b := h.Sum(nil) - copy(result[j:], b) - j += len(b) - - h.Reset() - h.Write(a) - a = h.Sum(nil) - } -} - -// prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, Section 5. -func prf10(result, secret, label, seed []byte) { - hashSHA1 := sha1.New - hashMD5 := md5.New - - labelAndSeed := make([]byte, len(label)+len(seed)) - copy(labelAndSeed, label) - copy(labelAndSeed[len(label):], seed) - - s1, s2 := splitPreMasterSecret(secret) - pHash(result, s1, labelAndSeed, hashMD5) - result2 := make([]byte, len(result)) - pHash(result2, s2, labelAndSeed, hashSHA1) - - for i, b := range result2 { - result[i] ^= b - } -} - -// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5. -func prf12(hashFunc func() hash.Hash) func(result, secret, label, seed []byte) { - return func(result, secret, label, seed []byte) { - labelAndSeed := make([]byte, len(label)+len(seed)) - copy(labelAndSeed, label) - copy(labelAndSeed[len(label):], seed) - - pHash(result, secret, labelAndSeed, hashFunc) - } -} - -const ( - masterSecretLength = 48 // Length of a master secret in TLS 1.1. - finishedVerifyLength = 12 // Length of verify_data in a Finished message. -) - -var masterSecretLabel = []byte("master secret") -var keyExpansionLabel = []byte("key expansion") -var clientFinishedLabel = []byte("client finished") -var serverFinishedLabel = []byte("server finished") - -func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secret, label, seed []byte), crypto.Hash) { - switch version { - case VersionTLS10, VersionTLS11: - return prf10, crypto.Hash(0) - case VersionTLS12: - if suite.flags&suiteSHA384 != 0 { - return prf12(sha512.New384), crypto.SHA384 - } - return prf12(sha256.New), crypto.SHA256 - default: - panic("unknown version") - } -} - -func prfForVersion(version uint16, suite *cipherSuite) func(result, secret, label, seed []byte) { - prf, _ := prfAndHashForVersion(version, suite) - return prf -} - -// masterFromPreMasterSecret generates the master secret from the pre-master -// secret. See RFC 5246, Section 8.1. -func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, clientRandom, serverRandom []byte) []byte { - seed := make([]byte, 0, len(clientRandom)+len(serverRandom)) - seed = append(seed, clientRandom...) - seed = append(seed, serverRandom...) - - masterSecret := make([]byte, masterSecretLength) - prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed) - return masterSecret -} - -// keysFromMasterSecret generates the connection keys from the master -// secret, given the lengths of the MAC key, cipher key and IV, as defined in -// RFC 2246, Section 6.3. -func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { - seed := make([]byte, 0, len(serverRandom)+len(clientRandom)) - seed = append(seed, serverRandom...) - seed = append(seed, clientRandom...) - - n := 2*macLen + 2*keyLen + 2*ivLen - keyMaterial := make([]byte, n) - prfForVersion(version, suite)(keyMaterial, masterSecret, keyExpansionLabel, seed) - clientMAC = keyMaterial[:macLen] - keyMaterial = keyMaterial[macLen:] - serverMAC = keyMaterial[:macLen] - keyMaterial = keyMaterial[macLen:] - clientKey = keyMaterial[:keyLen] - keyMaterial = keyMaterial[keyLen:] - serverKey = keyMaterial[:keyLen] - keyMaterial = keyMaterial[keyLen:] - clientIV = keyMaterial[:ivLen] - keyMaterial = keyMaterial[ivLen:] - serverIV = keyMaterial[:ivLen] - return -} - -func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash { - var buffer []byte - if version >= VersionTLS12 { - buffer = []byte{} - } - - prf, hash := prfAndHashForVersion(version, cipherSuite) - if hash != 0 { - return finishedHash{hash.New(), hash.New(), nil, nil, buffer, version, prf} - } - - return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), buffer, version, prf} -} - -// A finishedHash calculates the hash of a set of handshake messages suitable -// for including in a Finished message. -type finishedHash struct { - client hash.Hash - server hash.Hash - - // Prior to TLS 1.2, an additional MD5 hash is required. - clientMD5 hash.Hash - serverMD5 hash.Hash - - // In TLS 1.2, a full buffer is sadly required. - buffer []byte - - version uint16 - prf func(result, secret, label, seed []byte) -} - -func (h *finishedHash) Write(msg []byte) (n int, err error) { - h.client.Write(msg) - h.server.Write(msg) - - if h.version < VersionTLS12 { - h.clientMD5.Write(msg) - h.serverMD5.Write(msg) - } - - if h.buffer != nil { - h.buffer = append(h.buffer, msg...) - } - - return len(msg), nil -} - -func (h finishedHash) Sum() []byte { - if h.version >= VersionTLS12 { - return h.client.Sum(nil) - } - - out := make([]byte, 0, md5.Size+sha1.Size) - out = h.clientMD5.Sum(out) - return h.client.Sum(out) -} - -// clientSum returns the contents of the verify_data member of a client's -// Finished message. -func (h finishedHash) clientSum(masterSecret []byte) []byte { - out := make([]byte, finishedVerifyLength) - h.prf(out, masterSecret, clientFinishedLabel, h.Sum()) - return out -} - -// serverSum returns the contents of the verify_data member of a server's -// Finished message. -func (h finishedHash) serverSum(masterSecret []byte) []byte { - out := make([]byte, finishedVerifyLength) - h.prf(out, masterSecret, serverFinishedLabel, h.Sum()) - return out -} - -// hashForClientCertificate returns the handshake messages so far, pre-hashed if -// necessary, suitable for signing by a TLS client certificate. -func (h finishedHash) hashForClientCertificate(sigType uint8, hashAlg crypto.Hash, masterSecret []byte) []byte { - if (h.version >= VersionTLS12 || sigType == signatureEd25519) && h.buffer == nil { - panic("tls: handshake hash for a client certificate requested after discarding the handshake buffer") - } - - if sigType == signatureEd25519 { - return h.buffer - } - - if h.version >= VersionTLS12 { - hash := hashAlg.New() - hash.Write(h.buffer) - return hash.Sum(nil) - } - - if sigType == signatureECDSA { - return h.server.Sum(nil) - } - - return h.Sum() -} - -// discardHandshakeBuffer is called when there is no more need to -// buffer the entirety of the handshake messages. -func (h *finishedHash) discardHandshakeBuffer() { - h.buffer = nil -} - -// noExportedKeyingMaterial is used as a value of -// ConnectionState.ekm when renegotiation is enabled and thus -// we wish to fail all key-material export requests. -func noExportedKeyingMaterial(label string, context []byte, length int) ([]byte, error) { - return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled") -} - -// ekmFromMasterSecret generates exported keying material as defined in RFC 5705. -func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte) func(string, []byte, int) ([]byte, error) { - return func(label string, context []byte, length int) ([]byte, error) { - switch label { - case "client finished", "server finished", "master secret", "key expansion": - // These values are reserved and may not be used. - return nil, fmt.Errorf("crypto/tls: reserved ExportKeyingMaterial label: %s", label) - } - - seedLen := len(serverRandom) + len(clientRandom) - if context != nil { - seedLen += 2 + len(context) - } - seed := make([]byte, 0, seedLen) - - seed = append(seed, clientRandom...) - seed = append(seed, serverRandom...) - - if context != nil { - if len(context) >= 1<<16 { - return nil, fmt.Errorf("crypto/tls: ExportKeyingMaterial context too long") - } - seed = append(seed, byte(len(context)>>8), byte(len(context))) - seed = append(seed, context...) - } - - keyMaterial := make([]byte, length) - prfForVersion(version, suite)(keyMaterial, masterSecret, []byte(label), seed) - return keyMaterial, nil - } -} diff --git a/vendor/github.com/quic-go/qtls-go1-19/ticket.go b/vendor/github.com/quic-go/qtls-go1-19/ticket.go deleted file mode 100644 index fe1c7a884..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/ticket.go +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package qtls - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/hmac" - "crypto/sha256" - "crypto/subtle" - "encoding/binary" - "errors" - "io" - "time" - - "golang.org/x/crypto/cryptobyte" -) - -// sessionState contains the information that is serialized into a session -// ticket in order to later resume a connection. -type sessionState struct { - vers uint16 - cipherSuite uint16 - createdAt uint64 - masterSecret []byte // opaque master_secret<1..2^16-1>; - // struct { opaque certificate<1..2^24-1> } Certificate; - certificates [][]byte // Certificate certificate_list<0..2^24-1>; - - // usedOldKey is true if the ticket from which this session came from - // was encrypted with an older key and thus should be refreshed. - usedOldKey bool -} - -func (m *sessionState) marshal() ([]byte, error) { - var b cryptobyte.Builder - b.AddUint16(m.vers) - b.AddUint16(m.cipherSuite) - addUint64(&b, m.createdAt) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.masterSecret) - }) - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - for _, cert := range m.certificates { - b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(cert) - }) - } - }) - return b.Bytes() -} - -func (m *sessionState) unmarshal(data []byte) bool { - *m = sessionState{usedOldKey: m.usedOldKey} - s := cryptobyte.String(data) - if ok := s.ReadUint16(&m.vers) && - s.ReadUint16(&m.cipherSuite) && - readUint64(&s, &m.createdAt) && - readUint16LengthPrefixed(&s, &m.masterSecret) && - len(m.masterSecret) != 0; !ok { - return false - } - var certList cryptobyte.String - if !s.ReadUint24LengthPrefixed(&certList) { - return false - } - for !certList.Empty() { - var cert []byte - if !readUint24LengthPrefixed(&certList, &cert) { - return false - } - m.certificates = append(m.certificates, cert) - } - return s.Empty() -} - -// sessionStateTLS13 is the content of a TLS 1.3 session ticket. Its first -// version (revision = 0) doesn't carry any of the information needed for 0-RTT -// validation and the nonce is always empty. -// version (revision = 1) carries the max_early_data_size sent in the ticket. -// version (revision = 2) carries the ALPN sent in the ticket. -type sessionStateTLS13 struct { - // uint8 version = 0x0304; - // uint8 revision = 2; - cipherSuite uint16 - createdAt uint64 - resumptionSecret []byte // opaque resumption_master_secret<1..2^8-1>; - certificate Certificate // CertificateEntry certificate_list<0..2^24-1>; - maxEarlyData uint32 - alpn string - - appData []byte -} - -func (m *sessionStateTLS13) marshal() ([]byte, error) { - var b cryptobyte.Builder - b.AddUint16(VersionTLS13) - b.AddUint8(2) // revision - b.AddUint16(m.cipherSuite) - addUint64(&b, m.createdAt) - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.resumptionSecret) - }) - marshalCertificate(&b, m.certificate) - b.AddUint32(m.maxEarlyData) - b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes([]byte(m.alpn)) - }) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(m.appData) - }) - return b.Bytes() -} - -func (m *sessionStateTLS13) unmarshal(data []byte) bool { - *m = sessionStateTLS13{} - s := cryptobyte.String(data) - var version uint16 - var revision uint8 - var alpn []byte - ret := s.ReadUint16(&version) && - version == VersionTLS13 && - s.ReadUint8(&revision) && - revision == 2 && - s.ReadUint16(&m.cipherSuite) && - readUint64(&s, &m.createdAt) && - readUint8LengthPrefixed(&s, &m.resumptionSecret) && - len(m.resumptionSecret) != 0 && - unmarshalCertificate(&s, &m.certificate) && - s.ReadUint32(&m.maxEarlyData) && - readUint8LengthPrefixed(&s, &alpn) && - readUint16LengthPrefixed(&s, &m.appData) && - s.Empty() - m.alpn = string(alpn) - return ret -} - -func (c *Conn) encryptTicket(state []byte) ([]byte, error) { - if len(c.ticketKeys) == 0 { - return nil, errors.New("tls: internal error: session ticket keys unavailable") - } - - encrypted := make([]byte, ticketKeyNameLen+aes.BlockSize+len(state)+sha256.Size) - keyName := encrypted[:ticketKeyNameLen] - iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize] - macBytes := encrypted[len(encrypted)-sha256.Size:] - - if _, err := io.ReadFull(c.config.rand(), iv); err != nil { - return nil, err - } - key := c.ticketKeys[0] - copy(keyName, key.keyName[:]) - block, err := aes.NewCipher(key.aesKey[:]) - if err != nil { - return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error()) - } - cipher.NewCTR(block, iv).XORKeyStream(encrypted[ticketKeyNameLen+aes.BlockSize:], state) - - mac := hmac.New(sha256.New, key.hmacKey[:]) - mac.Write(encrypted[:len(encrypted)-sha256.Size]) - mac.Sum(macBytes[:0]) - - return encrypted, nil -} - -func (c *Conn) decryptTicket(encrypted []byte) (plaintext []byte, usedOldKey bool) { - if len(encrypted) < ticketKeyNameLen+aes.BlockSize+sha256.Size { - return nil, false - } - - keyName := encrypted[:ticketKeyNameLen] - iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize] - macBytes := encrypted[len(encrypted)-sha256.Size:] - ciphertext := encrypted[ticketKeyNameLen+aes.BlockSize : len(encrypted)-sha256.Size] - - keyIndex := -1 - for i, candidateKey := range c.ticketKeys { - if bytes.Equal(keyName, candidateKey.keyName[:]) { - keyIndex = i - break - } - } - if keyIndex == -1 { - return nil, false - } - key := &c.ticketKeys[keyIndex] - - mac := hmac.New(sha256.New, key.hmacKey[:]) - mac.Write(encrypted[:len(encrypted)-sha256.Size]) - expected := mac.Sum(nil) - - if subtle.ConstantTimeCompare(macBytes, expected) != 1 { - return nil, false - } - - block, err := aes.NewCipher(key.aesKey[:]) - if err != nil { - return nil, false - } - plaintext = make([]byte, len(ciphertext)) - cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext) - - return plaintext, keyIndex > 0 -} - -func (c *Conn) getSessionTicketMsg(appData []byte) (*newSessionTicketMsgTLS13, error) { - m := new(newSessionTicketMsgTLS13) - - var certsFromClient [][]byte - for _, cert := range c.peerCertificates { - certsFromClient = append(certsFromClient, cert.Raw) - } - state := sessionStateTLS13{ - cipherSuite: c.cipherSuite, - createdAt: uint64(c.config.time().Unix()), - resumptionSecret: c.resumptionSecret, - certificate: Certificate{ - Certificate: certsFromClient, - OCSPStaple: c.ocspResponse, - SignedCertificateTimestamps: c.scts, - }, - appData: appData, - alpn: c.clientProtocol, - } - if c.extraConfig != nil { - state.maxEarlyData = c.extraConfig.MaxEarlyData - } - stateBytes, err := state.marshal() - if err != nil { - return nil, err - } - m.label, err = c.encryptTicket(stateBytes) - if err != nil { - return nil, err - } - m.lifetime = uint32(maxSessionTicketLifetime / time.Second) - - // ticket_age_add is a random 32-bit value. See RFC 8446, section 4.6.1 - // The value is not stored anywhere; we never need to check the ticket age - // because 0-RTT is not supported. - ageAdd := make([]byte, 4) - _, err = c.config.rand().Read(ageAdd) - if err != nil { - return nil, err - } - m.ageAdd = binary.LittleEndian.Uint32(ageAdd) - - // ticket_nonce, which must be unique per connection, is always left at - // zero because we only ever send one ticket per connection. - - if c.extraConfig != nil { - m.maxEarlyData = c.extraConfig.MaxEarlyData - } - return m, nil -} - -// GetSessionTicket generates a new session ticket. -// It should only be called after the handshake completes. -// It can only be used for servers, and only if the alternative record layer is set. -// The ticket may be nil if config.SessionTicketsDisabled is set, -// or if the client isn't able to receive session tickets. -func (c *Conn) GetSessionTicket(appData []byte) ([]byte, error) { - if c.isClient || !c.handshakeComplete() || c.extraConfig == nil || c.extraConfig.AlternativeRecordLayer == nil { - return nil, errors.New("GetSessionTicket is only valid for servers after completion of the handshake, and if an alternative record layer is set.") - } - if c.config.SessionTicketsDisabled { - return nil, nil - } - - m, err := c.getSessionTicketMsg(appData) - if err != nil { - return nil, err - } - return m.marshal() -} diff --git a/vendor/github.com/quic-go/qtls-go1-19/tls.go b/vendor/github.com/quic-go/qtls-go1-19/tls.go deleted file mode 100644 index 42207c235..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/tls.go +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// package qtls partially implements TLS 1.2, as specified in RFC 5246, -// and TLS 1.3, as specified in RFC 8446. -package qtls - -// BUG(agl): The crypto/tls package only implements some countermeasures -// against Lucky13 attacks on CBC-mode encryption, and only on SHA1 -// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and -// https://www.imperialviolet.org/2013/02/04/luckythirteen.html. - -import ( - "bytes" - "context" - "crypto" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "errors" - "fmt" - "net" - "os" - "strings" -) - -// Server returns a new TLS server side connection -// using conn as the underlying transport. -// The configuration config must be non-nil and must include -// at least one certificate or else set GetCertificate. -func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { - c := &Conn{ - conn: conn, - config: fromConfig(config), - extraConfig: extraConfig, - } - c.handshakeFn = c.serverHandshake - return c -} - -// Client returns a new TLS client side connection -// using conn as the underlying transport. -// The config cannot be nil: users must set either ServerName or -// InsecureSkipVerify in the config. -func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { - c := &Conn{ - conn: conn, - config: fromConfig(config), - extraConfig: extraConfig, - isClient: true, - } - c.handshakeFn = c.clientHandshake - return c -} - -// A listener implements a network listener (net.Listener) for TLS connections. -type listener struct { - net.Listener - config *Config - extraConfig *ExtraConfig -} - -// Accept waits for and returns the next incoming TLS connection. -// The returned connection is of type *Conn. -func (l *listener) Accept() (net.Conn, error) { - c, err := l.Listener.Accept() - if err != nil { - return nil, err - } - return Server(c, l.config, l.extraConfig), nil -} - -// NewListener creates a Listener which accepts connections from an inner -// Listener and wraps each connection with Server. -// The configuration config must be non-nil and must include -// at least one certificate or else set GetCertificate. -func NewListener(inner net.Listener, config *Config, extraConfig *ExtraConfig) net.Listener { - l := new(listener) - l.Listener = inner - l.config = config - l.extraConfig = extraConfig - return l -} - -// Listen creates a TLS listener accepting connections on the -// given network address using net.Listen. -// The configuration config must be non-nil and must include -// at least one certificate or else set GetCertificate. -func Listen(network, laddr string, config *Config, extraConfig *ExtraConfig) (net.Listener, error) { - if config == nil || len(config.Certificates) == 0 && - config.GetCertificate == nil && config.GetConfigForClient == nil { - return nil, errors.New("tls: neither Certificates, GetCertificate, nor GetConfigForClient set in Config") - } - l, err := net.Listen(network, laddr) - if err != nil { - return nil, err - } - return NewListener(l, config, extraConfig), nil -} - -type timeoutError struct{} - -func (timeoutError) Error() string { return "tls: DialWithDialer timed out" } -func (timeoutError) Timeout() bool { return true } -func (timeoutError) Temporary() bool { return true } - -// DialWithDialer connects to the given network address using dialer.Dial and -// then initiates a TLS handshake, returning the resulting TLS connection. Any -// timeout or deadline given in the dialer apply to connection and TLS -// handshake as a whole. -// -// DialWithDialer interprets a nil configuration as equivalent to the zero -// configuration; see the documentation of Config for the defaults. -// -// DialWithDialer uses context.Background internally; to specify the context, -// use Dialer.DialContext with NetDialer set to the desired dialer. -func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) { - return dial(context.Background(), dialer, network, addr, config, extraConfig) -} - -func dial(ctx context.Context, netDialer *net.Dialer, network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) { - if netDialer.Timeout != 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, netDialer.Timeout) - defer cancel() - } - - if !netDialer.Deadline.IsZero() { - var cancel context.CancelFunc - ctx, cancel = context.WithDeadline(ctx, netDialer.Deadline) - defer cancel() - } - - rawConn, err := netDialer.DialContext(ctx, network, addr) - if err != nil { - return nil, err - } - - colonPos := strings.LastIndex(addr, ":") - if colonPos == -1 { - colonPos = len(addr) - } - hostname := addr[:colonPos] - - if config == nil { - config = defaultConfig() - } - // If no ServerName is set, infer the ServerName - // from the hostname we're connecting to. - if config.ServerName == "" { - // Make a copy to avoid polluting argument or default. - c := config.Clone() - c.ServerName = hostname - config = c - } - - conn := Client(rawConn, config, extraConfig) - if err := conn.HandshakeContext(ctx); err != nil { - rawConn.Close() - return nil, err - } - return conn, nil -} - -// Dial connects to the given network address using net.Dial -// and then initiates a TLS handshake, returning the resulting -// TLS connection. -// Dial interprets a nil configuration as equivalent to -// the zero configuration; see the documentation of Config -// for the defaults. -func Dial(network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) { - return DialWithDialer(new(net.Dialer), network, addr, config, extraConfig) -} - -// Dialer dials TLS connections given a configuration and a Dialer for the -// underlying connection. -type Dialer struct { - // NetDialer is the optional dialer to use for the TLS connections' - // underlying TCP connections. - // A nil NetDialer is equivalent to the net.Dialer zero value. - NetDialer *net.Dialer - - // Config is the TLS configuration to use for new connections. - // A nil configuration is equivalent to the zero - // configuration; see the documentation of Config for the - // defaults. - Config *Config - - ExtraConfig *ExtraConfig -} - -// Dial connects to the given network address and initiates a TLS -// handshake, returning the resulting TLS connection. -// -// The returned Conn, if any, will always be of type *Conn. -// -// Dial uses context.Background internally; to specify the context, -// use DialContext. -func (d *Dialer) Dial(network, addr string) (net.Conn, error) { - return d.DialContext(context.Background(), network, addr) -} - -func (d *Dialer) netDialer() *net.Dialer { - if d.NetDialer != nil { - return d.NetDialer - } - return new(net.Dialer) -} - -// DialContext connects to the given network address and initiates a TLS -// handshake, returning the resulting TLS connection. -// -// The provided Context must be non-nil. If the context expires before -// the connection is complete, an error is returned. Once successfully -// connected, any expiration of the context will not affect the -// connection. -// -// The returned Conn, if any, will always be of type *Conn. -func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { - c, err := dial(ctx, d.netDialer(), network, addr, d.Config, d.ExtraConfig) - if err != nil { - // Don't return c (a typed nil) in an interface. - return nil, err - } - return c, nil -} - -// LoadX509KeyPair reads and parses a public/private key pair from a pair -// of files. The files must contain PEM encoded data. The certificate file -// may contain intermediate certificates following the leaf certificate to -// form a certificate chain. On successful return, Certificate.Leaf will -// be nil because the parsed form of the certificate is not retained. -func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) { - certPEMBlock, err := os.ReadFile(certFile) - if err != nil { - return Certificate{}, err - } - keyPEMBlock, err := os.ReadFile(keyFile) - if err != nil { - return Certificate{}, err - } - return X509KeyPair(certPEMBlock, keyPEMBlock) -} - -// X509KeyPair parses a public/private key pair from a pair of -// PEM encoded data. On successful return, Certificate.Leaf will be nil because -// the parsed form of the certificate is not retained. -func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) { - fail := func(err error) (Certificate, error) { return Certificate{}, err } - - var cert Certificate - var skippedBlockTypes []string - for { - var certDERBlock *pem.Block - certDERBlock, certPEMBlock = pem.Decode(certPEMBlock) - if certDERBlock == nil { - break - } - if certDERBlock.Type == "CERTIFICATE" { - cert.Certificate = append(cert.Certificate, certDERBlock.Bytes) - } else { - skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type) - } - } - - if len(cert.Certificate) == 0 { - if len(skippedBlockTypes) == 0 { - return fail(errors.New("tls: failed to find any PEM data in certificate input")) - } - if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") { - return fail(errors.New("tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched")) - } - return fail(fmt.Errorf("tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes)) - } - - skippedBlockTypes = skippedBlockTypes[:0] - var keyDERBlock *pem.Block - for { - keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock) - if keyDERBlock == nil { - if len(skippedBlockTypes) == 0 { - return fail(errors.New("tls: failed to find any PEM data in key input")) - } - if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" { - return fail(errors.New("tls: found a certificate rather than a key in the PEM for the private key")) - } - return fail(fmt.Errorf("tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes)) - } - if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") { - break - } - skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type) - } - - // We don't need to parse the public key for TLS, but we so do anyway - // to check that it looks sane and matches the private key. - x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) - if err != nil { - return fail(err) - } - - cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes) - if err != nil { - return fail(err) - } - - switch pub := x509Cert.PublicKey.(type) { - case *rsa.PublicKey: - priv, ok := cert.PrivateKey.(*rsa.PrivateKey) - if !ok { - return fail(errors.New("tls: private key type does not match public key type")) - } - if pub.N.Cmp(priv.N) != 0 { - return fail(errors.New("tls: private key does not match public key")) - } - case *ecdsa.PublicKey: - priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey) - if !ok { - return fail(errors.New("tls: private key type does not match public key type")) - } - if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 { - return fail(errors.New("tls: private key does not match public key")) - } - case ed25519.PublicKey: - priv, ok := cert.PrivateKey.(ed25519.PrivateKey) - if !ok { - return fail(errors.New("tls: private key type does not match public key type")) - } - if !bytes.Equal(priv.Public().(ed25519.PublicKey), pub) { - return fail(errors.New("tls: private key does not match public key")) - } - default: - return fail(errors.New("tls: unknown public key algorithm")) - } - - return cert, nil -} - -// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates -// PKCS #1 private keys by default, while OpenSSL 1.0.0 generates PKCS #8 keys. -// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three. -func parsePrivateKey(der []byte) (crypto.PrivateKey, error) { - if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { - return key, nil - } - if key, err := x509.ParsePKCS8PrivateKey(der); err == nil { - switch key := key.(type) { - case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey: - return key, nil - default: - return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping") - } - } - if key, err := x509.ParseECPrivateKey(der); err == nil { - return key, nil - } - - return nil, errors.New("tls: failed to parse private key") -} diff --git a/vendor/github.com/quic-go/qtls-go1-19/unsafe.go b/vendor/github.com/quic-go/qtls-go1-19/unsafe.go deleted file mode 100644 index 55fa01b3d..000000000 --- a/vendor/github.com/quic-go/qtls-go1-19/unsafe.go +++ /dev/null @@ -1,96 +0,0 @@ -package qtls - -import ( - "crypto/tls" - "reflect" - "unsafe" -) - -func init() { - if !structsEqual(&tls.ConnectionState{}, &connectionState{}) { - panic("qtls.ConnectionState doesn't match") - } - if !structsEqual(&tls.ClientSessionState{}, &clientSessionState{}) { - panic("qtls.ClientSessionState doesn't match") - } - if !structsEqual(&tls.CertificateRequestInfo{}, &certificateRequestInfo{}) { - panic("qtls.CertificateRequestInfo doesn't match") - } - if !structsEqual(&tls.Config{}, &config{}) { - panic("qtls.Config doesn't match") - } - if !structsEqual(&tls.ClientHelloInfo{}, &clientHelloInfo{}) { - panic("qtls.ClientHelloInfo doesn't match") - } -} - -func toConnectionState(c connectionState) ConnectionState { - return *(*ConnectionState)(unsafe.Pointer(&c)) -} - -func toClientSessionState(s *clientSessionState) *ClientSessionState { - return (*ClientSessionState)(unsafe.Pointer(s)) -} - -func fromClientSessionState(s *ClientSessionState) *clientSessionState { - return (*clientSessionState)(unsafe.Pointer(s)) -} - -func toCertificateRequestInfo(i *certificateRequestInfo) *CertificateRequestInfo { - return (*CertificateRequestInfo)(unsafe.Pointer(i)) -} - -func toConfig(c *config) *Config { - return (*Config)(unsafe.Pointer(c)) -} - -func fromConfig(c *Config) *config { - return (*config)(unsafe.Pointer(c)) -} - -func toClientHelloInfo(chi *clientHelloInfo) *ClientHelloInfo { - return (*ClientHelloInfo)(unsafe.Pointer(chi)) -} - -func structsEqual(a, b interface{}) bool { - return compare(reflect.ValueOf(a), reflect.ValueOf(b)) -} - -func compare(a, b reflect.Value) bool { - sa := a.Elem() - sb := b.Elem() - if sa.NumField() != sb.NumField() { - return false - } - for i := 0; i < sa.NumField(); i++ { - fa := sa.Type().Field(i) - fb := sb.Type().Field(i) - if !reflect.DeepEqual(fa.Index, fb.Index) || fa.Name != fb.Name || fa.Anonymous != fb.Anonymous || fa.Offset != fb.Offset || !reflect.DeepEqual(fa.Type, fb.Type) { - if fa.Type.Kind() != fb.Type.Kind() { - return false - } - if fa.Type.Kind() == reflect.Slice { - if !compareStruct(fa.Type.Elem(), fb.Type.Elem()) { - return false - } - continue - } - return false - } - } - return true -} - -func compareStruct(a, b reflect.Type) bool { - if a.NumField() != b.NumField() { - return false - } - for i := 0; i < a.NumField(); i++ { - fa := a.Field(i) - fb := b.Field(i) - if !reflect.DeepEqual(fa.Index, fb.Index) || fa.Name != fb.Name || fa.Anonymous != fb.Anonymous || fa.Offset != fb.Offset || !reflect.DeepEqual(fa.Type, fb.Type) { - return false - } - } - return true -} diff --git a/vendor/github.com/quic-go/qtls-go1-20/alert.go b/vendor/github.com/quic-go/qtls-go1-20/alert.go index 3feac79be..687ada843 100644 --- a/vendor/github.com/quic-go/qtls-go1-20/alert.go +++ b/vendor/github.com/quic-go/qtls-go1-20/alert.go @@ -6,10 +6,17 @@ package qtls import "strconv" -type alert uint8 +// An AlertError is a TLS alert. +// +// When using a QUIC transport, QUICConn methods will return an error +// which wraps AlertError rather than sending a TLS alert. +type AlertError uint8 -// Alert is a TLS alert -type Alert = alert +func (e AlertError) Error() string { + return alert(e).String() +} + +type alert uint8 const ( // alert level diff --git a/vendor/github.com/quic-go/qtls-go1-20/cipher_suites.go b/vendor/github.com/quic-go/qtls-go1-20/cipher_suites.go index 43d213157..2946ffb32 100644 --- a/vendor/github.com/quic-go/qtls-go1-20/cipher_suites.go +++ b/vendor/github.com/quic-go/qtls-go1-20/cipher_suites.go @@ -15,8 +15,10 @@ import ( "crypto/sha256" "fmt" "hash" + "runtime" "golang.org/x/crypto/chacha20poly1305" + "golang.org/x/sys/cpu" ) // CipherSuite is a TLS cipher suite. Note that most functions in this package @@ -195,17 +197,6 @@ type cipherSuiteTLS13 struct { hash crypto.Hash } -type CipherSuiteTLS13 struct { - ID uint16 - KeyLen int - Hash crypto.Hash - AEAD func(key, fixedNonce []byte) cipher.AEAD -} - -func (c *CipherSuiteTLS13) IVLen() int { - return aeadNonceLength -} - var cipherSuitesTLS13 = []*cipherSuiteTLS13{ // TODO: replace with a map. {TLS_AES_128_GCM_SHA256, 16, aeadAESGCMTLS13, crypto.SHA256}, {TLS_CHACHA20_POLY1305_SHA256, 32, aeadChaCha20Poly1305, crypto.SHA256}, @@ -362,6 +353,18 @@ var defaultCipherSuitesTLS13NoAES = []uint16{ TLS_AES_256_GCM_SHA384, } +var ( + hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ + hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL + // Keep in sync with crypto/aes/cipher_s390x.go. + hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && + (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM) + + hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 || + runtime.GOARCH == "arm64" && hasGCMAsmARM64 || + runtime.GOARCH == "s390x" && hasGCMAsmS390X +) + var aesgcmCiphers = map[uint16]bool{ // TLS 1.2 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true, @@ -519,11 +522,6 @@ func aeadAESGCM(key, noncePrefix []byte) aead { return ret } -// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3 -func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD { - return aeadAESGCMTLS13(key, fixedNonce) -} - func aeadAESGCMTLS13(key, nonceMask []byte) aead { if len(nonceMask) != aeadNonceLength { panic("tls: internal error: wrong nonce length") diff --git a/vendor/github.com/quic-go/qtls-go1-20/common.go b/vendor/github.com/quic-go/qtls-go1-20/common.go index 074dd9dce..841c1a44b 100644 --- a/vendor/github.com/quic-go/qtls-go1-20/common.go +++ b/vendor/github.com/quic-go/qtls-go1-20/common.go @@ -82,11 +82,6 @@ const ( compressionNone uint8 = 0 ) -type Extension struct { - Type uint16 - Data []byte -} - // TLS extension numbers const ( extensionServerName uint16 = 0 @@ -105,6 +100,7 @@ const ( extensionCertificateAuthorities uint16 = 47 extensionSignatureAlgorithmsCert uint16 = 50 extensionKeyShare uint16 = 51 + extensionQUICTransportParameters uint16 = 57 extensionRenegotiationInfo uint16 = 0xff01 ) @@ -113,14 +109,6 @@ const ( scsvRenegotiation uint16 = 0x00ff ) -type EncryptionLevel uint8 - -const ( - EncryptionHandshake EncryptionLevel = iota - Encryption0RTT - EncryptionApplication -) - // CurveID is a tls.CurveID type CurveID = tls.CurveID @@ -294,12 +282,6 @@ type connectionState struct { ekm func(label string, context []byte, length int) ([]byte, error) } -type ConnectionStateWith0RTT struct { - ConnectionState - - Used0RTT bool // true if 0-RTT was both offered and accepted -} - // ClientAuthType is tls.ClientAuthType type ClientAuthType = tls.ClientAuthType @@ -349,8 +331,6 @@ type clientSessionState struct { // goroutines. Up to TLS 1.2, only ticket-based resumption is supported, not // SessionID-based resumption. In TLS 1.3 they were merged into PSK modes, which // are supported via this interface. -// -//go:generate sh -c "mockgen -package qtls -destination mock_client_session_cache_test.go github.com/quic-go/qtls-go1-20 ClientSessionCache" type ClientSessionCache = tls.ClientSessionCache // SignatureScheme is a tls.SignatureScheme @@ -736,64 +716,22 @@ type config struct { autoSessionTicketKeys []ticketKey } -// A RecordLayer handles encrypting and decrypting of TLS messages. -type RecordLayer interface { - SetReadKey(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte) - SetWriteKey(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte) - ReadHandshakeMessage() ([]byte, error) - WriteRecord([]byte) (int, error) - SendAlert(uint8) -} - type ExtraConfig struct { - // GetExtensions, if not nil, is called before a message that allows - // sending of extensions is sent. - // Currently only implemented for the ClientHello message (for the client) - // and for the EncryptedExtensions message (for the server). - // Only valid for TLS 1.3. - GetExtensions func(handshakeMessageType uint8) []Extension - - // ReceivedExtensions, if not nil, is called when a message that allows the - // inclusion of extensions is received. - // It is called with an empty slice of extensions, if the message didn't - // contain any extensions. - // Currently only implemented for the ClientHello message (sent by the - // client) and for the EncryptedExtensions message (sent by the server). - // Only valid for TLS 1.3. - ReceivedExtensions func(handshakeMessageType uint8, exts []Extension) - - // AlternativeRecordLayer is used by QUIC - AlternativeRecordLayer RecordLayer - - // Enforce the selection of a supported application protocol. - // Only works for TLS 1.3. - // If enabled, client and server have to agree on an application protocol. - // Otherwise, connection establishment fails. - EnforceNextProtoSelection bool - - // If MaxEarlyData is greater than 0, the client will be allowed to send early - // data when resuming a session. - // Requires the AlternativeRecordLayer to be set. + // If Enable0RTT is enabled, the client will be allowed to send early data when resuming a session. // // It has no meaning on the client. - MaxEarlyData uint32 + Enable0RTT bool + + // GetAppDataForSessionTicket requests application data to be sent with a session ticket. + // + // It has no meaning on the client. + GetAppDataForSessionTicket func() []byte // The Accept0RTT callback is called when the client offers 0-RTT. // The server then has to decide if it wants to accept or reject 0-RTT. // It is only used for servers. Accept0RTT func(appData []byte) bool - // 0RTTRejected is called when the server rejectes 0-RTT. - // It is only used for clients. - Rejected0RTT func() - - // If set, the client will export the 0-RTT key when resuming a session that - // allows sending of early data. - // Requires the AlternativeRecordLayer to be set. - // - // It has no meaning to the server. - Enable0RTT bool - // Is called when the client saves a session ticket to the session ticket. // This gives the application the opportunity to save some data along with the ticket, // which can be restored when the session ticket is used. @@ -807,23 +745,14 @@ type ExtraConfig struct { // Clone clones. func (c *ExtraConfig) Clone() *ExtraConfig { return &ExtraConfig{ - GetExtensions: c.GetExtensions, - ReceivedExtensions: c.ReceivedExtensions, - AlternativeRecordLayer: c.AlternativeRecordLayer, - EnforceNextProtoSelection: c.EnforceNextProtoSelection, - MaxEarlyData: c.MaxEarlyData, Enable0RTT: c.Enable0RTT, + GetAppDataForSessionTicket: c.GetAppDataForSessionTicket, Accept0RTT: c.Accept0RTT, - Rejected0RTT: c.Rejected0RTT, GetAppDataForSessionState: c.GetAppDataForSessionState, SetAppDataFromSessionState: c.SetAppDataFromSessionState, } } -func (c *ExtraConfig) usesAlternativeRecordLayer() bool { - return c != nil && c.AlternativeRecordLayer != nil -} - const ( // ticketKeyNameLen is the number of bytes of identifier that is prepended to // an encrypted session ticket in order to identify the key used to encrypt it. @@ -1384,7 +1313,6 @@ func (c *config) BuildNameToCertificate() { const ( keyLogLabelTLS12 = "CLIENT_RANDOM" - keyLogLabelEarlyTraffic = "CLIENT_EARLY_TRAFFIC_SECRET" keyLogLabelClientHandshake = "CLIENT_HANDSHAKE_TRAFFIC_SECRET" keyLogLabelServerHandshake = "SERVER_HANDSHAKE_TRAFFIC_SECRET" keyLogLabelClientTraffic = "CLIENT_TRAFFIC_SECRET_0" @@ -1523,16 +1451,4 @@ func isSupportedSignatureAlgorithm(sigAlg SignatureScheme, supportedSignatureAlg } // CertificateVerificationError is returned when certificate verification fails during the handshake. -type CertificateVerificationError struct { - // UnverifiedCertificates and its contents should not be modified. - UnverifiedCertificates []*x509.Certificate - Err error -} - -func (e *CertificateVerificationError) Error() string { - return fmt.Sprintf("tls: failed to verify certificate: %s", e.Err) -} - -func (e *CertificateVerificationError) Unwrap() error { - return e.Err -} +type CertificateVerificationError = tls.CertificateVerificationError diff --git a/vendor/github.com/quic-go/qtls-go1-20/conn.go b/vendor/github.com/quic-go/qtls-go1-20/conn.go index 656c83c71..b7ebdb0a7 100644 --- a/vendor/github.com/quic-go/qtls-go1-20/conn.go +++ b/vendor/github.com/quic-go/qtls-go1-20/conn.go @@ -29,6 +29,7 @@ type Conn struct { conn net.Conn isClient bool handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake + quic *quicState // nil for non-QUIC connections // isHandshakeComplete is true if the connection is currently transferring // application data (i.e. is not currently processing a handshake). @@ -40,11 +41,10 @@ type Conn struct { vers uint16 // TLS version haveVers bool // version has been negotiated config *config // configuration passed to constructor + extraConfig *ExtraConfig // handshakes counts the number of handshakes performed on the // connection so far. If renegotiation is disabled then this is either // zero or one. - extraConfig *ExtraConfig - handshakes int didResume bool // whether this connection was a session resumption cipherSuite uint16 @@ -65,13 +65,8 @@ type Conn struct { secureRenegotiation bool // ekm is a closure for exporting keying material. ekm func(label string, context []byte, length int) ([]byte, error) - // For the client: // resumptionSecret is the resumption_master_secret for handling - // NewSessionTicket messages. nil if config.SessionTicketsDisabled. - // For the server: - // resumptionSecret is the resumption_master_secret for generating - // NewSessionTicket messages. Only used when the alternative record - // layer is set. nil if config.SessionTicketsDisabled. + // or sending NewSessionTicket messages. resumptionSecret []byte // ticketKeys is the set of active session ticket keys for this @@ -123,12 +118,7 @@ type Conn struct { // the rest of the bits are the number of goroutines in Conn.Write. activeCall atomic.Int32 - used0RTT bool - tmp [16]byte - - connStateMutex sync.Mutex - connState ConnectionStateWith0RTT } // Access to net.Conn methods. @@ -188,9 +178,8 @@ type halfConn struct { nextCipher any // next encryption state nextMac hash.Hash // next MAC algorithm - trafficSecret []byte // current TLS 1.3 traffic secret - - setKeyCallback func(encLevel EncryptionLevel, suite *CipherSuiteTLS13, trafficSecret []byte) + level QUICEncryptionLevel // current QUIC encryption level + trafficSecret []byte // current TLS 1.3 traffic secret } type permanentError struct { @@ -235,20 +224,9 @@ func (hc *halfConn) changeCipherSpec() error { return nil } -func (hc *halfConn) exportKey(encLevel EncryptionLevel, suite *cipherSuiteTLS13, trafficSecret []byte) { - if hc.setKeyCallback != nil { - s := &CipherSuiteTLS13{ - ID: suite.id, - KeyLen: suite.keyLen, - Hash: suite.hash, - AEAD: func(key, fixedNonce []byte) cipher.AEAD { return suite.aead(key, fixedNonce) }, - } - hc.setKeyCallback(encLevel, s, trafficSecret) - } -} - -func (hc *halfConn) setTrafficSecret(suite *cipherSuiteTLS13, secret []byte) { +func (hc *halfConn) setTrafficSecret(suite *cipherSuiteTLS13, level QUICEncryptionLevel, secret []byte) { hc.trafficSecret = secret + hc.level = level key, iv := suite.trafficKey(secret) hc.cipher = suite.aead(key, iv) for i := range hc.seq { @@ -481,13 +459,6 @@ func (hc *halfConn) decrypt(record []byte) ([]byte, recordType, error) { return plaintext, typ, nil } -func (c *Conn) setAlternativeRecordLayer() { - if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil { - c.in.setKeyCallback = c.extraConfig.AlternativeRecordLayer.SetReadKey - c.out.setKeyCallback = c.extraConfig.AlternativeRecordLayer.SetWriteKey - } -} - // sliceForAppend extends the input slice by n bytes. head is the full extended // slice, while tail is the appended part. If the original slice has sufficient // capacity no allocation is performed. @@ -646,6 +617,10 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { } c.input.Reset(nil) + if c.quic != nil { + return c.in.setErrorLocked(errors.New("tls: internal error: attempted to read record with QUIC transport")) + } + // Read header, payload. if err := c.readFromUntil(c.conn, recordHeaderLen); err != nil { // RFC 8446, Section 6.1 suggests that EOF without an alertCloseNotify @@ -729,6 +704,9 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) case recordTypeAlert: + if c.quic != nil { + return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } if len(data) != 2 { return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) } @@ -846,6 +824,9 @@ func (c *Conn) readFromUntil(r io.Reader, n int) error { // sendAlert sends a TLS alert message. func (c *Conn) sendAlertLocked(err alert) error { + if c.quic != nil { + return c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err}) + } switch err { case alertNoRenegotiation, alertCloseNotify: c.tmp[0] = alertLevelWarning @@ -865,11 +846,6 @@ func (c *Conn) sendAlertLocked(err alert) error { // sendAlert sends a TLS alert message. func (c *Conn) sendAlert(err alert) error { - if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil { - c.extraConfig.AlternativeRecordLayer.SendAlert(uint8(err)) - return &net.OpError{Op: "local error", Err: err} - } - c.out.Lock() defer c.out.Unlock() return c.sendAlertLocked(err) @@ -985,6 +961,19 @@ var outBufPool = sync.Pool{ // writeRecordLocked writes a TLS record with the given type and payload to the // connection and updates the record layer state. func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) { + if c.quic != nil { + if typ != recordTypeHandshake { + return 0, errors.New("tls: internal error: sending non-handshake message to QUIC transport") + } + c.quicWriteCryptoData(c.out.level, data) + if !c.buffering { + if _, err := c.flush(); err != nil { + return 0, err + } + } + return len(data), nil + } + outBufPtr := outBufPool.Get().(*[]byte) outBuf := *outBufPtr defer func() { @@ -1046,69 +1035,63 @@ func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) { // the record layer state. If transcript is non-nil the marshalled message is // written to it. func (c *Conn) writeHandshakeRecord(msg handshakeMessage, transcript transcriptHash) (int, error) { + c.out.Lock() + defer c.out.Unlock() + data, err := msg.marshal() if err != nil { return 0, err } - - c.out.Lock() - defer c.out.Unlock() - if transcript != nil { transcript.Write(data) } - if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil { - return c.extraConfig.AlternativeRecordLayer.WriteRecord(data) - } - return c.writeRecordLocked(recordTypeHandshake, data) } // writeChangeCipherRecord writes a ChangeCipherSpec message to the connection and // updates the record layer state. func (c *Conn) writeChangeCipherRecord() error { - if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil { - return nil - } - c.out.Lock() defer c.out.Unlock() _, err := c.writeRecordLocked(recordTypeChangeCipherSpec, []byte{1}) return err } +// readHandshakeBytes reads handshake data until c.hand contains at least n bytes. +func (c *Conn) readHandshakeBytes(n int) error { + if c.quic != nil { + return c.quicReadHandshakeBytes(n) + } + for c.hand.Len() < n { + if err := c.readRecord(); err != nil { + return err + } + } + return nil +} + // readHandshake reads the next handshake message from // the record layer. If transcript is non-nil, the message // is written to the passed transcriptHash. func (c *Conn) readHandshake(transcript transcriptHash) (any, error) { - var data []byte - if c.extraConfig != nil && c.extraConfig.AlternativeRecordLayer != nil { - var err error - data, err = c.extraConfig.AlternativeRecordLayer.ReadHandshakeMessage() - if err != nil { - return nil, err - } - } else { - for c.hand.Len() < 4 { - if err := c.readRecord(); err != nil { - return nil, err - } - } - - data = c.hand.Bytes() - n := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) - if n > maxHandshake { - c.sendAlertLocked(alertInternalError) - return nil, c.in.setErrorLocked(fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake)) - } - for c.hand.Len() < 4+n { - if err := c.readRecord(); err != nil { - return nil, err - } - } - data = c.hand.Next(4 + n) + if err := c.readHandshakeBytes(4); err != nil { + return nil, err } + data := c.hand.Bytes() + n := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) + if n > maxHandshake { + c.sendAlertLocked(alertInternalError) + return nil, c.in.setErrorLocked(fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake)) + } + if err := c.readHandshakeBytes(4 + n); err != nil { + return nil, err + } + data = c.hand.Next(4 + n) + return c.unmarshalHandshakeMessage(data, transcript) +} + +func (c *Conn) unmarshalHandshakeMessage(data []byte, transcript transcriptHash) (handshakeMessage, error) { var m handshakeMessage switch data[0] { case typeHelloRequest: @@ -1288,10 +1271,6 @@ func (c *Conn) handleRenegotiation() error { return c.handshakeErr } -func (c *Conn) HandlePostHandshakeMessage() error { - return c.handlePostHandshakeMessage() -} - // handlePostHandshakeMessage processes a handshake message arrived after the // handshake is complete. Up to TLS 1.2, it indicates the start of a renegotiation. func (c *Conn) handlePostHandshakeMessage() error { @@ -1303,7 +1282,6 @@ func (c *Conn) handlePostHandshakeMessage() error { if err != nil { return err } - c.retryCount++ if c.retryCount > maxUselessRecords { c.sendAlert(alertUnexpectedMessage) @@ -1315,20 +1293,28 @@ func (c *Conn) handlePostHandshakeMessage() error { return c.handleNewSessionTicket(msg) case *keyUpdateMsg: return c.handleKeyUpdate(msg) - default: - c.sendAlert(alertUnexpectedMessage) - return fmt.Errorf("tls: received unexpected handshake message of type %T", msg) } + // The QUIC layer is supposed to treat an unexpected post-handshake CertificateRequest + // as a QUIC-level PROTOCOL_VIOLATION error (RFC 9001, Section 4.4). Returning an + // unexpected_message alert here doesn't provide it with enough information to distinguish + // this condition from other unexpected messages. This is probably fine. + c.sendAlert(alertUnexpectedMessage) + return fmt.Errorf("tls: received unexpected handshake message of type %T", msg) } func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error { + if c.quic != nil { + c.sendAlert(alertUnexpectedMessage) + return c.in.setErrorLocked(errors.New("tls: received unexpected key update message")) + } + cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite) if cipherSuite == nil { return c.in.setErrorLocked(c.sendAlert(alertInternalError)) } newSecret := cipherSuite.nextTrafficSecret(c.in.trafficSecret) - c.in.setTrafficSecret(cipherSuite, newSecret) + c.in.setTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret) if keyUpdate.updateRequested { c.out.Lock() @@ -1347,7 +1333,7 @@ func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error { } newSecret := cipherSuite.nextTrafficSecret(c.out.trafficSecret) - c.out.setTrafficSecret(cipherSuite, newSecret) + c.out.setTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret) } return nil @@ -1508,12 +1494,15 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) { // this cancellation. In the former case, we need to close the connection. defer cancel() - // Start the "interrupter" goroutine, if this context might be canceled. - // (The background context cannot). - // - // The interrupter goroutine waits for the input context to be done and - // closes the connection if this happens before the function returns. - if ctx.Done() != nil { + if c.quic != nil { + c.quic.cancelc = handshakeCtx.Done() + c.quic.cancel = cancel + } else if ctx.Done() != nil { + // Start the "interrupter" goroutine, if this context might be canceled. + // (The background context cannot). + // + // The interrupter goroutine waits for the input context to be done and + // closes the connection if this happens before the function returns. done := make(chan struct{}) interruptRes := make(chan error, 1) defer func() { @@ -1564,21 +1553,38 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) { panic("tls: internal error: handshake returned an error but is marked successful") } + if c.quic != nil { + if c.handshakeErr == nil { + c.quicHandshakeComplete() + // Provide the 1-RTT read secret now that the handshake is complete. + // The QUIC layer MUST NOT decrypt 1-RTT packets prior to completing + // the handshake (RFC 9001, Section 5.7). + c.quicSetReadSecret(QUICEncryptionLevelApplication, c.cipherSuite, c.in.trafficSecret) + } else { + var a alert + c.out.Lock() + if !errors.As(c.out.err, &a) { + a = alertInternalError + } + c.out.Unlock() + // Return an error which wraps both the handshake error and + // any alert error we may have sent, or alertInternalError + // if we didn't send an alert. + // Truncate the text of the alert to 0 characters. + c.handshakeErr = fmt.Errorf("%w%.0w", c.handshakeErr, AlertError(a)) + } + close(c.quic.blockedc) + close(c.quic.signalc) + } + return c.handshakeErr } // ConnectionState returns basic TLS details about the connection. func (c *Conn) ConnectionState() ConnectionState { - c.connStateMutex.Lock() - defer c.connStateMutex.Unlock() - return c.connState.ConnectionState -} - -// ConnectionStateWith0RTT returns basic TLS details (incl. 0-RTT status) about the connection. -func (c *Conn) ConnectionStateWith0RTT() ConnectionStateWith0RTT { - c.connStateMutex.Lock() - defer c.connStateMutex.Unlock() - return c.connState + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + return c.connectionStateLocked() } func (c *Conn) connectionStateLocked() ConnectionState { @@ -1609,15 +1615,6 @@ func (c *Conn) connectionStateLocked() ConnectionState { return toConnectionState(state) } -func (c *Conn) updateConnectionState() { - c.connStateMutex.Lock() - defer c.connStateMutex.Unlock() - c.connState = ConnectionStateWith0RTT{ - Used0RTT: c.used0RTT, - ConnectionState: c.connectionStateLocked(), - } -} - // OCSPResponse returns the stapled OCSP response from the TLS server, if // any. (Only valid for client connections.) func (c *Conn) OCSPResponse() []byte { diff --git a/vendor/github.com/quic-go/qtls-go1-20/cpu.go b/vendor/github.com/quic-go/qtls-go1-20/cpu.go deleted file mode 100644 index 121945087..000000000 --- a/vendor/github.com/quic-go/qtls-go1-20/cpu.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build !js -// +build !js - -package qtls - -import ( - "runtime" - - "golang.org/x/sys/cpu" -) - -var ( - hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ - hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL - // Keep in sync with crypto/aes/cipher_s390x.go. - hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && - (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM) - - hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 || - runtime.GOARCH == "arm64" && hasGCMAsmARM64 || - runtime.GOARCH == "s390x" && hasGCMAsmS390X -) diff --git a/vendor/github.com/quic-go/qtls-go1-20/cpu_other.go b/vendor/github.com/quic-go/qtls-go1-20/cpu_other.go deleted file mode 100644 index 33f7d2194..000000000 --- a/vendor/github.com/quic-go/qtls-go1-20/cpu_other.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build js -// +build js - -package qtls - -var ( - hasGCMAsmAMD64 = false - hasGCMAsmARM64 = false - hasGCMAsmS390X = false - - hasAESGCMHardwareSupport = false -) diff --git a/vendor/github.com/quic-go/qtls-go1-20/handshake_client.go b/vendor/github.com/quic-go/qtls-go1-20/handshake_client.go index ebb56ebe1..a5fdd54ca 100644 --- a/vendor/github.com/quic-go/qtls-go1-20/handshake_client.go +++ b/vendor/github.com/quic-go/qtls-go1-20/handshake_client.go @@ -58,23 +58,12 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) { return nil, nil, errors.New("tls: NextProtos values too large") } - var supportedVersions []uint16 - var clientHelloVersion uint16 - if c.extraConfig.usesAlternativeRecordLayer() { - if config.maxSupportedVersion(roleClient) < VersionTLS13 { - return nil, nil, errors.New("tls: MaxVersion prevents QUIC from using TLS 1.3") - } - // Only offer TLS 1.3 when QUIC is used. - supportedVersions = []uint16{VersionTLS13} - clientHelloVersion = VersionTLS13 - } else { - supportedVersions = config.supportedVersions(roleClient) - if len(supportedVersions) == 0 { - return nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion") - } - clientHelloVersion = config.maxSupportedVersion(roleClient) + supportedVersions := config.supportedVersions(roleClient) + if len(supportedVersions) == 0 { + return nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion") } + clientHelloVersion := config.maxSupportedVersion(roleClient) // The version at the beginning of the ClientHello was capped at TLS 1.2 // for compatibility reasons. The supported_versions extension is used // to negotiate versions now. See RFC 8446, Section 4.2.1. @@ -128,7 +117,9 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) { // A random session ID is used to detect when the server accepted a ticket // and is resuming a session (see RFC 5077). In TLS 1.3, it's always set as // a compatibility measure (see RFC 8446, Section 4.1.2). - if c.extraConfig == nil || c.extraConfig.AlternativeRecordLayer == nil { + // + // The session ID is not set for QUIC connections (see RFC 9001, Section 8.4). + if c.quic == nil { hello.sessionId = make([]byte, 32) if _, err := io.ReadFull(config.rand(), hello.sessionId); err != nil { return nil, nil, errors.New("tls: short read from Rand: " + err.Error()) @@ -164,8 +155,15 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) { hello.keyShares = []keyShare{{group: curveID, data: key.PublicKey().Bytes()}} } - if hello.supportedVersions[0] == VersionTLS13 && c.extraConfig != nil && c.extraConfig.GetExtensions != nil { - hello.additionalExtensions = c.extraConfig.GetExtensions(typeClientHello) + if c.quic != nil { + p, err := c.quicGetTransportParameters() + if err != nil { + return nil, nil, err + } + if p == nil { + p = []byte{} + } + hello.quicTransportParameters = p } return hello, key, nil @@ -175,7 +173,6 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { if c.config == nil { c.config = fromConfig(defaultConfig()) } - c.setAlternativeRecordLayer() // This may be a renegotiation handshake, in which case some fields // need to be reset. @@ -192,45 +189,33 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { return err } if cacheKey != "" && session != nil { - var deletedTicket bool - if session.vers == VersionTLS13 && hello.earlyData && c.extraConfig != nil && c.extraConfig.Enable0RTT { - // don't reuse a session ticket that enabled 0-RTT - c.config.ClientSessionCache.Put(cacheKey, nil) - deletedTicket = true - - if suite := cipherSuiteTLS13ByID(session.cipherSuite); suite != nil { - h := suite.hash.New() - helloBytes, err := hello.marshal() - if err != nil { - return err - } - h.Write(helloBytes) - clientEarlySecret := suite.deriveSecret(earlySecret, "c e traffic", h) - c.out.exportKey(Encryption0RTT, suite, clientEarlySecret) - if err := c.config.writeKeyLog(keyLogLabelEarlyTraffic, hello.random, clientEarlySecret); err != nil { - return err - } + defer func() { + // If we got a handshake failure when resuming a session, throw away + // the session ticket. See RFC 5077, Section 3.2. + // + // RFC 8446 makes no mention of dropping tickets on failure, but it + // does require servers to abort on invalid binders, so we need to + // delete tickets to recover from a corrupted PSK. + if err != nil { + c.config.ClientSessionCache.Put(cacheKey, nil) } - } - if !deletedTicket { - defer func() { - // If we got a handshake failure when resuming a session, throw away - // the session ticket. See RFC 5077, Section 3.2. - // - // RFC 8446 makes no mention of dropping tickets on failure, but it - // does require servers to abort on invalid binders, so we need to - // delete tickets to recover from a corrupted PSK. - if err != nil { - c.config.ClientSessionCache.Put(cacheKey, nil) - } - }() - } + }() } if _, err := c.writeHandshakeRecord(hello, nil); err != nil { return err } + if hello.earlyData { + suite := cipherSuiteTLS13ByID(session.cipherSuite) + transcript := suite.hash.New() + if err := transcriptMsg(hello, transcript); err != nil { + return err + } + earlyTrafficSecret := suite.deriveSecret(earlySecret, clientEarlyTrafficLabel, transcript) + c.quicSetWriteSecret(QUICEncryptionLevelEarly, suite.id, earlyTrafficSecret) + } + // serverHelloMsg is not included in the transcript msg, err := c.readHandshake(nil) if err != nil { @@ -293,7 +278,6 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { c.config.ClientSessionCache.Put(cacheKey, toClientSessionState(hs.session)) } - c.updateConnectionState() return nil } @@ -346,7 +330,10 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, } // Try to resume a previously negotiated TLS session, if available. - cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config) + cacheKey = c.clientSessionCacheKey() + if cacheKey == "" { + return "", nil, nil, nil, nil + } sess, ok := c.config.ClientSessionCache.Get(cacheKey) if !ok || sess == nil { return cacheKey, nil, nil, nil, nil @@ -430,6 +417,13 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, return cacheKey, nil, nil, nil, nil } + if c.quic != nil && maxEarlyData > 0 { + // For 0-RTT, the cipher suite has to match exactly. + if mutualCipherSuiteTLS13(hello.cipherSuites, session.cipherSuite) != nil { + hello.earlyData = true + } + } + // Set the pre_shared_key extension. See RFC 8446, Section 4.2.11.1. ticketAge := uint32(c.config.time().Sub(session.receivedAt) / time.Millisecond) identity := pskIdentity{ @@ -444,9 +438,6 @@ func (c *Conn) loadSession(hello *clientHelloMsg) (cacheKey string, session.nonce, cipherSuite.hash.Size()) earlySecret = cipherSuite.extract(psk, nil) binderKey = cipherSuite.deriveSecret(earlySecret, resumptionBinderLabel, nil) - if c.extraConfig != nil { - hello.earlyData = c.extraConfig.Enable0RTT && maxEarlyData > 0 - } transcript := cipherSuite.hash.New() helloBytes, err := hello.marshalWithoutBinders() if err != nil { @@ -815,7 +806,7 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { } } - if err := checkALPN(hs.hello.alpnProtocols, hs.serverHello.alpnProtocol); err != nil { + if err := checkALPN(hs.hello.alpnProtocols, hs.serverHello.alpnProtocol, false); err != nil { c.sendAlert(alertUnsupportedExtension) return false, err } @@ -853,8 +844,12 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { // checkALPN ensure that the server's choice of ALPN protocol is compatible with // the protocols that we advertised in the Client Hello. -func checkALPN(clientProtos []string, serverProto string) error { +func checkALPN(clientProtos []string, serverProto string, quic bool) error { if serverProto == "" { + if quic && len(clientProtos) > 0 { + // RFC 9001, Section 8.1 + return errors.New("tls: server did not select an ALPN protocol") + } return nil } if len(clientProtos) == 0 { @@ -1102,15 +1097,16 @@ func (c *Conn) getClientCertificate(cri *CertificateRequestInfo) (*Certificate, return new(Certificate), nil } -const clientSessionCacheKeyPrefix = "qtls-" - // clientSessionCacheKey returns a key used to cache sessionTickets that could // be used to resume previously negotiated TLS sessions with a server. -func clientSessionCacheKey(serverAddr net.Addr, config *config) string { - if len(config.ServerName) > 0 { - return clientSessionCacheKeyPrefix + config.ServerName +func (c *Conn) clientSessionCacheKey() string { + if len(c.config.ServerName) > 0 { + return c.config.ServerName } - return clientSessionCacheKeyPrefix + serverAddr.String() + if c.conn != nil { + return c.conn.RemoteAddr().String() + } + return "" } // hostnameInSNI converts name into an appropriate hostname for SNI. diff --git a/vendor/github.com/quic-go/qtls-go1-20/handshake_client_tls13.go b/vendor/github.com/quic-go/qtls-go1-20/handshake_client_tls13.go index 60ae29954..e9d0a533e 100644 --- a/vendor/github.com/quic-go/qtls-go1-20/handshake_client_tls13.go +++ b/vendor/github.com/quic-go/qtls-go1-20/handshake_client_tls13.go @@ -87,7 +87,6 @@ func (hs *clientHandshakeStateTLS13) handshake() error { if err := hs.processServerHello(); err != nil { return err } - c.updateConnectionState() if err := hs.sendDummyChangeCipherSpec(); err != nil { return err } @@ -100,7 +99,6 @@ func (hs *clientHandshakeStateTLS13) handshake() error { if err := hs.readServerCertificate(); err != nil { return err } - c.updateConnectionState() if err := hs.readServerFinished(); err != nil { return err } @@ -115,7 +113,7 @@ func (hs *clientHandshakeStateTLS13) handshake() error { } c.isHandshakeComplete.Store(true) - c.updateConnectionState() + return nil } @@ -177,6 +175,9 @@ func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error { // sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility // with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4. func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error { + if hs.c.quic != nil { + return nil + } if hs.sentDummyCCS { return nil } @@ -264,7 +265,7 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { transcript := hs.suite.hash.New() transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) transcript.Write(chHash) - if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil { + if err := transcriptMsg(hs.serverHello, transcript); err != nil { return err } helloBytes, err := hs.hello.marshalWithoutBinders() @@ -283,10 +284,11 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { } } - if hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil { - c.extraConfig.Rejected0RTT() + if hs.hello.earlyData { + hs.hello.earlyData = false + c.quicRejectedEarlyData() } - hs.hello.earlyData = false // disable 0-RTT + if _, err := hs.c.writeHandshakeRecord(hs.hello, hs.transcript); err != nil { return err } @@ -392,12 +394,18 @@ func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error { clientSecret := hs.suite.deriveSecret(handshakeSecret, clientHandshakeTrafficLabel, hs.transcript) - c.out.exportKey(EncryptionHandshake, hs.suite, clientSecret) - c.out.setTrafficSecret(hs.suite, clientSecret) + c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret) serverSecret := hs.suite.deriveSecret(handshakeSecret, serverHandshakeTrafficLabel, hs.transcript) - c.in.exportKey(EncryptionHandshake, hs.suite, serverSecret) - c.in.setTrafficSecret(hs.suite, serverSecret) + c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret) + + if c.quic != nil { + if c.hand.Len() != 0 { + c.sendAlert(alertUnexpectedMessage) + } + c.quicSetWriteSecret(QUICEncryptionLevelHandshake, hs.suite.id, clientSecret) + c.quicSetReadSecret(QUICEncryptionLevelHandshake, hs.suite.id, serverSecret) + } err = c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret) if err != nil { @@ -429,28 +437,35 @@ func (hs *clientHandshakeStateTLS13) readServerParameters() error { c.sendAlert(alertUnexpectedMessage) return unexpectedMessageError(encryptedExtensions, msg) } - // Notify the caller if 0-RTT was rejected. - if !encryptedExtensions.earlyData && hs.hello.earlyData && c.extraConfig != nil && c.extraConfig.Rejected0RTT != nil { - c.extraConfig.Rejected0RTT() - } - c.used0RTT = encryptedExtensions.earlyData - if hs.c.extraConfig != nil && hs.c.extraConfig.ReceivedExtensions != nil { - hs.c.extraConfig.ReceivedExtensions(typeEncryptedExtensions, encryptedExtensions.additionalExtensions) - } - if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol); err != nil { - c.sendAlert(alertUnsupportedExtension) + if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol, c.quic != nil); err != nil { + // RFC 8446 specifies that no_application_protocol is sent by servers, but + // does not specify how clients handle the selection of an incompatible protocol. + // RFC 9001 Section 8.1 specifies that QUIC clients send no_application_protocol + // in this case. Always sending no_application_protocol seems reasonable. + c.sendAlert(alertNoApplicationProtocol) return err } c.clientProtocol = encryptedExtensions.alpnProtocol - if c.extraConfig != nil && c.extraConfig.EnforceNextProtoSelection { - if len(encryptedExtensions.alpnProtocol) == 0 { - // the server didn't select an ALPN - c.sendAlert(alertNoApplicationProtocol) - return errors.New("ALPN negotiation failed. Server didn't offer any protocols") + if c.quic != nil { + if encryptedExtensions.quicTransportParameters == nil { + // RFC 9001 Section 8.2. + c.sendAlert(alertMissingExtension) + return errors.New("tls: server did not send a quic_transport_parameters extension") + } + c.quicSetTransportParameters(encryptedExtensions.quicTransportParameters) + } else { + if encryptedExtensions.quicTransportParameters != nil { + c.sendAlert(alertUnsupportedExtension) + return errors.New("tls: server sent an unexpected quic_transport_parameters extension") } } + + if hs.hello.earlyData && !encryptedExtensions.earlyData { + c.quicRejectedEarlyData() + } + return nil } @@ -578,8 +593,7 @@ func (hs *clientHandshakeStateTLS13) readServerFinished() error { clientApplicationTrafficLabel, hs.transcript) serverSecret := hs.suite.deriveSecret(hs.masterSecret, serverApplicationTrafficLabel, hs.transcript) - c.in.exportKey(EncryptionApplication, hs.suite, serverSecret) - c.in.setTrafficSecret(hs.suite, serverSecret) + c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret) err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret) if err != nil { @@ -675,14 +689,20 @@ func (hs *clientHandshakeStateTLS13) sendClientFinished() error { return err } - c.out.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret) - c.out.setTrafficSecret(hs.suite, hs.trafficSecret) + c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret) if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil { c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret, resumptionLabel, hs.transcript) } + if c.quic != nil { + if c.hand.Len() != 0 { + c.sendAlert(alertUnexpectedMessage) + } + c.quicSetWriteSecret(QUICEncryptionLevelApplication, hs.suite.id, hs.trafficSecret) + } + return nil } @@ -753,8 +773,10 @@ func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error { scts: c.scts, } - cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config) - c.config.ClientSessionCache.Put(cacheKey, toClientSessionState(session)) + cacheKey := c.clientSessionCacheKey() + if cacheKey != "" { + c.config.ClientSessionCache.Put(cacheKey, toClientSessionState(session)) + } return nil } diff --git a/vendor/github.com/quic-go/qtls-go1-20/handshake_messages.go b/vendor/github.com/quic-go/qtls-go1-20/handshake_messages.go index c69fcefda..37b012363 100644 --- a/vendor/github.com/quic-go/qtls-go1-20/handshake_messages.go +++ b/vendor/github.com/quic-go/qtls-go1-20/handshake_messages.go @@ -93,7 +93,7 @@ type clientHelloMsg struct { pskModes []uint8 pskIdentities []pskIdentity pskBinders [][]byte - additionalExtensions []Extension + quicTransportParameters []byte } func (m *clientHelloMsg) marshal() ([]byte, error) { @@ -247,10 +247,11 @@ func (m *clientHelloMsg) marshal() ([]byte, error) { }) }) } - for _, ext := range m.additionalExtensions { - exts.AddUint16(ext.Type) + if m.quicTransportParameters != nil { // marshal zero-length parameters when present + // RFC 9001, Section 8.2 + exts.AddUint16(extensionQUICTransportParameters) exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { - exts.AddBytes(ext.Data) + exts.AddBytes(m.quicTransportParameters) }) } if len(m.pskIdentities) > 0 { // pre_shared_key must be the last extension @@ -567,6 +568,11 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { if !readUint8LengthPrefixed(&extData, &m.pskModes) { return false } + case extensionQUICTransportParameters: + m.quicTransportParameters = make([]byte, len(extData)) + if !extData.CopyBytes(m.quicTransportParameters) { + return false + } case extensionPreSharedKey: // RFC 8446, Section 4.2.11 if !extensions.Empty() { @@ -598,7 +604,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { m.pskBinders = append(m.pskBinders, binder) } default: - m.additionalExtensions = append(m.additionalExtensions, Extension{Type: extension, Data: extData}) + // Ignore unknown extensions. continue } @@ -867,11 +873,10 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool { } type encryptedExtensionsMsg struct { - raw []byte - alpnProtocol string - earlyData bool - - additionalExtensions []Extension + raw []byte + alpnProtocol string + quicTransportParameters []byte + earlyData bool } func (m *encryptedExtensionsMsg) marshal() ([]byte, error) { @@ -893,17 +898,18 @@ func (m *encryptedExtensionsMsg) marshal() ([]byte, error) { }) }) } + if m.quicTransportParameters != nil { // marshal zero-length parameters when present + // draft-ietf-quic-tls-32, Section 8.2 + b.AddUint16(extensionQUICTransportParameters) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(m.quicTransportParameters) + }) + } if m.earlyData { // RFC 8446, Section 4.2.10 b.AddUint16(extensionEarlyData) b.AddUint16(0) // empty extension_data } - for _, ext := range m.additionalExtensions { - b.AddUint16(ext.Type) - b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { - b.AddBytes(ext.Data) - }) - } }) }) @@ -923,14 +929,14 @@ func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool { } for !extensions.Empty() { - var ext uint16 + var extension uint16 var extData cryptobyte.String - if !extensions.ReadUint16(&ext) || + if !extensions.ReadUint16(&extension) || !extensions.ReadUint16LengthPrefixed(&extData) { return false } - switch ext { + switch extension { case extensionALPN: var protoList cryptobyte.String if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() { @@ -942,10 +948,15 @@ func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool { return false } m.alpnProtocol = string(proto) + case extensionQUICTransportParameters: + m.quicTransportParameters = make([]byte, len(extData)) + if !extData.CopyBytes(m.quicTransportParameters) { + return false + } case extensionEarlyData: m.earlyData = true default: - m.additionalExtensions = append(m.additionalExtensions, Extension{Type: ext, Data: extData}) + // Ignore unknown extensions. continue } diff --git a/vendor/github.com/quic-go/qtls-go1-20/handshake_server.go b/vendor/github.com/quic-go/qtls-go1-20/handshake_server.go index 05321cfb6..7539c95d5 100644 --- a/vendor/github.com/quic-go/qtls-go1-20/handshake_server.go +++ b/vendor/github.com/quic-go/qtls-go1-20/handshake_server.go @@ -39,8 +39,6 @@ type serverHandshakeState struct { // serverHandshake performs a TLS handshake as a server. func (c *Conn) serverHandshake(ctx context.Context) error { - c.setAlternativeRecordLayer() - clientHello, err := c.readClientHello(ctx) if err != nil { return err @@ -53,12 +51,6 @@ func (c *Conn) serverHandshake(ctx context.Context) error { clientHello: clientHello, } return hs.handshake() - } else if c.extraConfig.usesAlternativeRecordLayer() { - // This should already have been caught by the check that the ClientHello doesn't - // offer any (supported) versions older than TLS 1.3. - // Check again to make sure we can't be tricked into using an older version. - c.sendAlert(alertProtocolVersion) - return errors.New("tls: negotiated TLS < 1.3 when using QUIC") } hs := serverHandshakeState{ @@ -131,7 +123,6 @@ func (hs *serverHandshakeState) handshake() error { c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random) c.isHandshakeComplete.Store(true) - c.updateConnectionState() return nil } @@ -167,27 +158,6 @@ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) { if len(clientHello.supportedVersions) == 0 { clientVersions = supportedVersionsFromMax(clientHello.vers) } - if c.extraConfig.usesAlternativeRecordLayer() { - // In QUIC, the client MUST NOT offer any old TLS versions. - // Here, we can only check that none of the other supported versions of this library - // (TLS 1.0 - TLS 1.2) is offered. We don't check for any SSL versions here. - for _, ver := range clientVersions { - if ver == VersionTLS13 { - continue - } - for _, v := range supportedVersions { - if ver == v { - c.sendAlert(alertProtocolVersion) - return nil, fmt.Errorf("tls: client offered old TLS version %#x", ver) - } - } - } - // Make the config we're using allows us to use TLS 1.3. - if c.config.maxSupportedVersion(roleServer) < VersionTLS13 { - c.sendAlert(alertInternalError) - return nil, errors.New("tls: MaxVersion prevents QUIC from using TLS 1.3") - } - } c.vers, ok = c.config.mutualVersion(roleServer, clientVersions) if !ok { c.sendAlert(alertProtocolVersion) @@ -249,7 +219,7 @@ func (hs *serverHandshakeState) processClientHello() error { c.serverName = hs.clientHello.serverName } - selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols) + selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols, false) if err != nil { c.sendAlert(alertNoApplicationProtocol) return err @@ -310,8 +280,12 @@ func (hs *serverHandshakeState) processClientHello() error { // negotiateALPN picks a shared ALPN protocol that both sides support in server // preference order. If ALPN is not configured or the peer doesn't support it, // it returns "" and no error. -func negotiateALPN(serverProtos, clientProtos []string) (string, error) { +func negotiateALPN(serverProtos, clientProtos []string, quic bool) (string, error) { if len(serverProtos) == 0 || len(clientProtos) == 0 { + if quic && len(serverProtos) != 0 { + // RFC 9001, Section 8.1 + return "", fmt.Errorf("tls: client did not request an application protocol") + } return "", nil } var http11fallback bool diff --git a/vendor/github.com/quic-go/qtls-go1-20/handshake_server_tls13.go b/vendor/github.com/quic-go/qtls-go1-20/handshake_server_tls13.go index 6189c7806..03bc5eb51 100644 --- a/vendor/github.com/quic-go/qtls-go1-20/handshake_server_tls13.go +++ b/vendor/github.com/quic-go/qtls-go1-20/handshake_server_tls13.go @@ -40,6 +40,7 @@ type serverHandshakeStateTLS13 struct { trafficSecret []byte // client_application_traffic_secret_0 transcript hash.Hash clientFinished []byte + earlyData bool } func (hs *serverHandshakeStateTLS13) handshake() error { @@ -56,7 +57,6 @@ func (hs *serverHandshakeStateTLS13) handshake() error { if err := hs.checkForResumption(); err != nil { return err } - c.updateConnectionState() if err := hs.pickCertificate(); err != nil { return err } @@ -79,13 +79,12 @@ func (hs *serverHandshakeStateTLS13) handshake() error { if err := hs.readClientCertificate(); err != nil { return err } - c.updateConnectionState() if err := hs.readClientFinished(); err != nil { return err } c.isHandshakeComplete.Store(true) - c.updateConnectionState() + return nil } @@ -219,13 +218,23 @@ GroupSelection: return errors.New("tls: invalid client key share") } - c.serverName = hs.clientHello.serverName - - if c.extraConfig != nil && c.extraConfig.ReceivedExtensions != nil { - c.extraConfig.ReceivedExtensions(typeClientHello, hs.clientHello.additionalExtensions) + if c.quic != nil { + if hs.clientHello.quicTransportParameters == nil { + // RFC 9001 Section 8.2. + c.sendAlert(alertMissingExtension) + return errors.New("tls: client did not send a quic_transport_parameters extension") + } + c.quicSetTransportParameters(hs.clientHello.quicTransportParameters) + } else { + if hs.clientHello.quicTransportParameters != nil { + c.sendAlert(alertUnsupportedExtension) + return errors.New("tls: client sent an unexpected quic_transport_parameters extension") + } } - selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols) + c.serverName = hs.clientHello.serverName + + selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols, c.quic != nil) if err != nil { hs.alpnNegotiationErr = err } @@ -282,10 +291,9 @@ func (hs *serverHandshakeStateTLS13) checkForResumption() error { } if hs.alpnNegotiationErr == nil && sessionState.alpn == c.clientProtocol && - c.extraConfig != nil && c.extraConfig.MaxEarlyData > 0 && + c.extraConfig != nil && c.extraConfig.Enable0RTT && c.extraConfig.Accept0RTT != nil && c.extraConfig.Accept0RTT(sessionState.appData) { hs.encryptedExtensions.earlyData = true - c.used0RTT = true } } @@ -337,27 +345,23 @@ func (hs *serverHandshakeStateTLS13) checkForResumption() error { return errors.New("tls: invalid PSK binder") } + if c.quic != nil && hs.clientHello.earlyData && hs.encryptedExtensions.earlyData && i == 0 && + sessionState.maxEarlyData > 0 && sessionState.cipherSuite == hs.suite.id { + hs.earlyData = true + + transcript := hs.suite.hash.New() + if err := transcriptMsg(hs.clientHello, transcript); err != nil { + return err + } + earlyTrafficSecret := hs.suite.deriveSecret(hs.earlySecret, clientEarlyTrafficLabel, transcript) + c.quicSetReadSecret(QUICEncryptionLevelEarly, hs.suite.id, earlyTrafficSecret) + } + c.didResume = true if err := c.processCertsFromClient(sessionState.certificate); err != nil { return err } - h := cloneHash(hs.transcript, hs.suite.hash) - clientHelloWithBindersBytes, err := hs.clientHello.marshal() - if err != nil { - c.sendAlert(alertInternalError) - return err - } - h.Write(clientHelloWithBindersBytes) - if hs.encryptedExtensions.earlyData { - clientEarlySecret := hs.suite.deriveSecret(hs.earlySecret, "c e traffic", h) - c.in.exportKey(Encryption0RTT, hs.suite, clientEarlySecret) - if err := c.config.writeKeyLog(keyLogLabelEarlyTraffic, hs.clientHello.random, clientEarlySecret); err != nil { - c.sendAlert(alertInternalError) - return err - } - } - hs.hello.selectedIdentityPresent = true hs.hello.selectedIdentity = uint16(i) hs.usingPSK = true @@ -432,6 +436,9 @@ func (hs *serverHandshakeStateTLS13) pickCertificate() error { // sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility // with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4. func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error { + if hs.c.quic != nil { + return nil + } if hs.sentDummyCCS { return nil } @@ -498,9 +505,9 @@ func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) return errors.New("tls: client illegally modified second ClientHello") } - if clientHello.earlyData { + if illegalClientHelloChange(clientHello, hs.clientHello) { c.sendAlert(alertIllegalParameter) - return errors.New("tls: client offered 0-RTT data in second ClientHello") + return errors.New("tls: client illegally modified second ClientHello") } hs.clientHello = clientHello @@ -588,12 +595,18 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error { clientSecret := hs.suite.deriveSecret(hs.handshakeSecret, clientHandshakeTrafficLabel, hs.transcript) - c.in.exportKey(EncryptionHandshake, hs.suite, clientSecret) - c.in.setTrafficSecret(hs.suite, clientSecret) + c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret) serverSecret := hs.suite.deriveSecret(hs.handshakeSecret, serverHandshakeTrafficLabel, hs.transcript) - c.out.exportKey(EncryptionHandshake, hs.suite, serverSecret) - c.out.setTrafficSecret(hs.suite, serverSecret) + c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret) + + if c.quic != nil { + if c.hand.Len() != 0 { + c.sendAlert(alertUnexpectedMessage) + } + c.quicSetWriteSecret(QUICEncryptionLevelHandshake, hs.suite.id, serverSecret) + c.quicSetReadSecret(QUICEncryptionLevelHandshake, hs.suite.id, clientSecret) + } err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.clientHello.random, clientSecret) if err != nil { @@ -606,12 +619,20 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error { return err } - if hs.alpnNegotiationErr != nil { + selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols, c.quic != nil) + if err != nil { c.sendAlert(alertNoApplicationProtocol) - return hs.alpnNegotiationErr + return err } - if hs.c.extraConfig != nil && hs.c.extraConfig.GetExtensions != nil { - hs.encryptedExtensions.additionalExtensions = hs.c.extraConfig.GetExtensions(typeEncryptedExtensions) + hs.encryptedExtensions.alpnProtocol = selectedProto + c.clientProtocol = selectedProto + + if c.quic != nil { + p, err := c.quicGetTransportParameters() + if err != nil { + return err + } + hs.encryptedExtensions.quicTransportParameters = p } if _, err := hs.c.writeHandshakeRecord(hs.encryptedExtensions, hs.transcript); err != nil { @@ -712,8 +733,15 @@ func (hs *serverHandshakeStateTLS13) sendServerFinished() error { clientApplicationTrafficLabel, hs.transcript) serverSecret := hs.suite.deriveSecret(hs.masterSecret, serverApplicationTrafficLabel, hs.transcript) - c.out.exportKey(EncryptionApplication, hs.suite, serverSecret) - c.out.setTrafficSecret(hs.suite, serverSecret) + c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret) + + if c.quic != nil { + if c.hand.Len() != 0 { + // TODO: Handle this in setTrafficSecret? + c.sendAlert(alertUnexpectedMessage) + } + c.quicSetWriteSecret(QUICEncryptionLevelApplication, hs.suite.id, serverSecret) + } err := c.config.writeKeyLog(keyLogLabelClientTraffic, hs.clientHello.random, hs.trafficSecret) if err != nil { @@ -745,6 +773,10 @@ func (hs *serverHandshakeStateTLS13) shouldSendSessionTickets() bool { return false } + // QUIC tickets are sent by QUICConn.SendSessionTicket, not automatically. + if hs.c.quic != nil { + return false + } // Don't send tickets the client wouldn't use. See RFC 8446, Section 4.2.9. for _, pskMode := range hs.clientHello.pskModes { if pskMode == pskModeDHE { @@ -764,25 +796,66 @@ func (hs *serverHandshakeStateTLS13) sendSessionTickets() error { if err := transcriptMsg(finishedMsg, hs.transcript); err != nil { return err } + c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret, + resumptionLabel, hs.transcript) if !hs.shouldSendSessionTickets() { return nil } + return c.sendSessionTicket(false) +} - c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret, - resumptionLabel, hs.transcript) - - // Don't send session tickets when the alternative record layer is set. - // Instead, save the resumption secret on the Conn. - // Session tickets can then be generated by calling Conn.GetSessionTicket(). - if hs.c.extraConfig != nil && hs.c.extraConfig.AlternativeRecordLayer != nil { - return nil +func (c *Conn) sendSessionTicket(earlyData bool) error { + suite := cipherSuiteTLS13ByID(c.cipherSuite) + if suite == nil { + return errors.New("tls: internal error: unknown cipher suite") } - m, err := hs.c.getSessionTicketMsg(nil) + m := new(newSessionTicketMsgTLS13) + + var certsFromClient [][]byte + for _, cert := range c.peerCertificates { + certsFromClient = append(certsFromClient, cert.Raw) + } + state := sessionStateTLS13{ + cipherSuite: suite.id, + createdAt: uint64(c.config.time().Unix()), + resumptionSecret: c.resumptionSecret, + certificate: Certificate{ + Certificate: certsFromClient, + OCSPStaple: c.ocspResponse, + SignedCertificateTimestamps: c.scts, + }, + alpn: c.clientProtocol, + } + if earlyData { + state.maxEarlyData = 0xffffffff + state.appData = c.extraConfig.GetAppDataForSessionTicket() + } + stateBytes, err := state.marshal() + if err != nil { + c.sendAlert(alertInternalError) + return err + } + m.label, err = c.encryptTicket(stateBytes) if err != nil { return err } + m.lifetime = uint32(maxSessionTicketLifetime / time.Second) + + // ticket_age_add is a random 32-bit value. See RFC 8446, section 4.6.1 + // The value is not stored anywhere; we never need to check the ticket age + // because 0-RTT is not supported. + ageAdd := make([]byte, 4) + _, err = c.config.rand().Read(ageAdd) + if err != nil { + return err + } + + if earlyData { + // RFC 9001, Section 4.6.1 + m.maxEarlyData = 0xffffffff + } if _, err := c.writeHandshakeRecord(m, nil); err != nil { return err @@ -900,8 +973,7 @@ func (hs *serverHandshakeStateTLS13) readClientFinished() error { return errors.New("tls: invalid client finished hash") } - c.in.exportKey(EncryptionApplication, hs.suite, hs.trafficSecret) - c.in.setTrafficSecret(hs.suite, hs.trafficSecret) + c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret) return nil } diff --git a/vendor/github.com/quic-go/qtls-go1-20/key_schedule.go b/vendor/github.com/quic-go/qtls-go1-20/key_schedule.go index c410a3e82..a4568933d 100644 --- a/vendor/github.com/quic-go/qtls-go1-20/key_schedule.go +++ b/vendor/github.com/quic-go/qtls-go1-20/key_schedule.go @@ -21,6 +21,7 @@ import ( const ( resumptionBinderLabel = "res binder" + clientEarlyTrafficLabel = "c e traffic" clientHandshakeTrafficLabel = "c hs traffic" serverHandshakeTrafficLabel = "s hs traffic" clientApplicationTrafficLabel = "c ap traffic" diff --git a/vendor/github.com/quic-go/qtls-go1-20/quic.go b/vendor/github.com/quic-go/qtls-go1-20/quic.go new file mode 100644 index 000000000..f146688b1 --- /dev/null +++ b/vendor/github.com/quic-go/qtls-go1-20/quic.go @@ -0,0 +1,418 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package qtls + +import ( + "context" + "errors" + "fmt" +) + +// QUICEncryptionLevel represents a QUIC encryption level used to transmit +// handshake messages. +type QUICEncryptionLevel int + +const ( + QUICEncryptionLevelInitial = QUICEncryptionLevel(iota) + QUICEncryptionLevelEarly + QUICEncryptionLevelHandshake + QUICEncryptionLevelApplication +) + +func (l QUICEncryptionLevel) String() string { + switch l { + case QUICEncryptionLevelInitial: + return "Initial" + case QUICEncryptionLevelEarly: + return "Early" + case QUICEncryptionLevelHandshake: + return "Handshake" + case QUICEncryptionLevelApplication: + return "Application" + default: + return fmt.Sprintf("QUICEncryptionLevel(%v)", int(l)) + } +} + +// A QUICConn represents a connection which uses a QUIC implementation as the underlying +// transport as described in RFC 9001. +// +// Methods of QUICConn are not safe for concurrent use. +type QUICConn struct { + conn *Conn + + sessionTicketSent bool +} + +// A QUICConfig configures a QUICConn. +type QUICConfig struct { + TLSConfig *Config + ExtraConfig *ExtraConfig +} + +// A QUICEventKind is a type of operation on a QUIC connection. +type QUICEventKind int + +const ( + // QUICNoEvent indicates that there are no events available. + QUICNoEvent QUICEventKind = iota + + // QUICSetReadSecret and QUICSetWriteSecret provide the read and write + // secrets for a given encryption level. + // QUICEvent.Level, QUICEvent.Data, and QUICEvent.Suite are set. + // + // Secrets for the Initial encryption level are derived from the initial + // destination connection ID, and are not provided by the QUICConn. + QUICSetReadSecret + QUICSetWriteSecret + + // QUICWriteData provides data to send to the peer in CRYPTO frames. + // QUICEvent.Data is set. + QUICWriteData + + // QUICTransportParameters provides the peer's QUIC transport parameters. + // QUICEvent.Data is set. + QUICTransportParameters + + // QUICTransportParametersRequired indicates that the caller must provide + // QUIC transport parameters to send to the peer. The caller should set + // the transport parameters with QUICConn.SetTransportParameters and call + // QUICConn.NextEvent again. + // + // If transport parameters are set before calling QUICConn.Start, the + // connection will never generate a QUICTransportParametersRequired event. + QUICTransportParametersRequired + + // QUICRejectedEarlyData indicates that the server rejected 0-RTT data even + // if we offered it. It's returned before QUICEncryptionLevelApplication + // keys are returned. + QUICRejectedEarlyData + + // QUICHandshakeDone indicates that the TLS handshake has completed. + QUICHandshakeDone +) + +// A QUICEvent is an event occurring on a QUIC connection. +// +// The type of event is specified by the Kind field. +// The contents of the other fields are kind-specific. +type QUICEvent struct { + Kind QUICEventKind + + // Set for QUICSetReadSecret, QUICSetWriteSecret, and QUICWriteData. + Level QUICEncryptionLevel + + // Set for QUICTransportParameters, QUICSetReadSecret, QUICSetWriteSecret, and QUICWriteData. + // The contents are owned by crypto/tls, and are valid until the next NextEvent call. + Data []byte + + // Set for QUICSetReadSecret and QUICSetWriteSecret. + Suite uint16 +} + +type quicState struct { + events []QUICEvent + nextEvent int + + // eventArr is a statically allocated event array, large enough to handle + // the usual maximum number of events resulting from a single call: transport + // parameters, Initial data, Early read secret, Handshake write and read + // secrets, Handshake data, Application write secret, Application data. + eventArr [8]QUICEvent + + started bool + signalc chan struct{} // handshake data is available to be read + blockedc chan struct{} // handshake is waiting for data, closed when done + cancelc <-chan struct{} // handshake has been canceled + cancel context.CancelFunc + + // readbuf is shared between HandleData and the handshake goroutine. + // HandshakeCryptoData passes ownership to the handshake goroutine by + // reading from signalc, and reclaims ownership by reading from blockedc. + readbuf []byte + + transportParams []byte // to send to the peer +} + +// QUICClient returns a new TLS client side connection using QUICTransport as the +// underlying transport. The config cannot be nil. +// +// The config's MinVersion must be at least TLS 1.3. +func QUICClient(config *QUICConfig) *QUICConn { + return newQUICConn(Client(nil, config.TLSConfig), config.ExtraConfig) +} + +// QUICServer returns a new TLS server side connection using QUICTransport as the +// underlying transport. The config cannot be nil. +// +// The config's MinVersion must be at least TLS 1.3. +func QUICServer(config *QUICConfig) *QUICConn { + return newQUICConn(Server(nil, config.TLSConfig), config.ExtraConfig) +} + +func newQUICConn(conn *Conn, extraConfig *ExtraConfig) *QUICConn { + conn.quic = &quicState{ + signalc: make(chan struct{}), + blockedc: make(chan struct{}), + } + conn.quic.events = conn.quic.eventArr[:0] + conn.extraConfig = extraConfig + return &QUICConn{ + conn: conn, + } +} + +// Start starts the client or server handshake protocol. +// It may produce connection events, which may be read with NextEvent. +// +// Start must be called at most once. +func (q *QUICConn) Start(ctx context.Context) error { + if q.conn.quic.started { + return quicError(errors.New("tls: Start called more than once")) + } + q.conn.quic.started = true + if q.conn.config.MinVersion < VersionTLS13 { + return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.13")) + } + go q.conn.HandshakeContext(ctx) + if _, ok := <-q.conn.quic.blockedc; !ok { + return q.conn.handshakeErr + } + return nil +} + +// NextEvent returns the next event occurring on the connection. +// It returns an event with a Kind of QUICNoEvent when no events are available. +func (q *QUICConn) NextEvent() QUICEvent { + qs := q.conn.quic + if last := qs.nextEvent - 1; last >= 0 && len(qs.events[last].Data) > 0 { + // Write over some of the previous event's data, + // to catch callers erroniously retaining it. + qs.events[last].Data[0] = 0 + } + if qs.nextEvent >= len(qs.events) { + qs.events = qs.events[:0] + qs.nextEvent = 0 + return QUICEvent{Kind: QUICNoEvent} + } + e := qs.events[qs.nextEvent] + qs.events[qs.nextEvent] = QUICEvent{} // zero out references to data + qs.nextEvent++ + return e +} + +// Close closes the connection and stops any in-progress handshake. +func (q *QUICConn) Close() error { + if q.conn.quic.cancel == nil { + return nil // never started + } + q.conn.quic.cancel() + for range q.conn.quic.blockedc { + // Wait for the handshake goroutine to return. + } + return q.conn.handshakeErr +} + +// HandleData handles handshake bytes received from the peer. +// It may produce connection events, which may be read with NextEvent. +func (q *QUICConn) HandleData(level QUICEncryptionLevel, data []byte) error { + c := q.conn + if c.in.level != level { + return quicError(c.in.setErrorLocked(errors.New("tls: handshake data received at wrong level"))) + } + c.quic.readbuf = data + <-c.quic.signalc + _, ok := <-c.quic.blockedc + if ok { + // The handshake goroutine is waiting for more data. + return nil + } + // The handshake goroutine has exited. + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + c.hand.Write(c.quic.readbuf) + c.quic.readbuf = nil + for q.conn.hand.Len() >= 4 && q.conn.handshakeErr == nil { + b := q.conn.hand.Bytes() + n := int(b[1])<<16 | int(b[2])<<8 | int(b[3]) + if n > maxHandshake { + q.conn.handshakeErr = fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake) + break + } + if len(b) < 4+n { + return nil + } + if err := q.conn.handlePostHandshakeMessage(); err != nil { + q.conn.handshakeErr = err + } + } + if q.conn.handshakeErr != nil { + return quicError(q.conn.handshakeErr) + } + return nil +} + +// SendSessionTicket sends a session ticket to the client. +// It produces connection events, which may be read with NextEvent. +// Currently, it can only be called once. +func (q *QUICConn) SendSessionTicket(earlyData bool) error { + c := q.conn + if !c.isHandshakeComplete.Load() { + return quicError(errors.New("tls: SendSessionTicket called before handshake completed")) + } + if c.isClient { + return quicError(errors.New("tls: SendSessionTicket called on the client")) + } + if q.sessionTicketSent { + return quicError(errors.New("tls: SendSessionTicket called multiple times")) + } + q.sessionTicketSent = true + return quicError(c.sendSessionTicket(earlyData)) +} + +// ConnectionState returns basic TLS details about the connection. +func (q *QUICConn) ConnectionState() ConnectionState { + return q.conn.ConnectionState() +} + +// SetTransportParameters sets the transport parameters to send to the peer. +// +// Server connections may delay setting the transport parameters until after +// receiving the client's transport parameters. See QUICTransportParametersRequired. +func (q *QUICConn) SetTransportParameters(params []byte) { + if params == nil { + params = []byte{} + } + q.conn.quic.transportParams = params + if q.conn.quic.started { + <-q.conn.quic.signalc + <-q.conn.quic.blockedc + } +} + +// quicError ensures err is an AlertError. +// If err is not already, quicError wraps it with alertInternalError. +func quicError(err error) error { + if err == nil { + return nil + } + var ae AlertError + if errors.As(err, &ae) { + return err + } + var a alert + if !errors.As(err, &a) { + a = alertInternalError + } + // Return an error wrapping the original error and an AlertError. + // Truncate the text of the alert to 0 characters. + return fmt.Errorf("%w%.0w", err, AlertError(a)) +} + +func (c *Conn) quicReadHandshakeBytes(n int) error { + for c.hand.Len() < n { + if err := c.quicWaitForSignal(); err != nil { + return err + } + } + return nil +} + +func (c *Conn) quicSetReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) { + c.quic.events = append(c.quic.events, QUICEvent{ + Kind: QUICSetReadSecret, + Level: level, + Suite: suite, + Data: secret, + }) +} + +func (c *Conn) quicSetWriteSecret(level QUICEncryptionLevel, suite uint16, secret []byte) { + c.quic.events = append(c.quic.events, QUICEvent{ + Kind: QUICSetWriteSecret, + Level: level, + Suite: suite, + Data: secret, + }) +} + +func (c *Conn) quicWriteCryptoData(level QUICEncryptionLevel, data []byte) { + var last *QUICEvent + if len(c.quic.events) > 0 { + last = &c.quic.events[len(c.quic.events)-1] + } + if last == nil || last.Kind != QUICWriteData || last.Level != level { + c.quic.events = append(c.quic.events, QUICEvent{ + Kind: QUICWriteData, + Level: level, + }) + last = &c.quic.events[len(c.quic.events)-1] + } + last.Data = append(last.Data, data...) +} + +func (c *Conn) quicSetTransportParameters(params []byte) { + c.quic.events = append(c.quic.events, QUICEvent{ + Kind: QUICTransportParameters, + Data: params, + }) +} + +func (c *Conn) quicGetTransportParameters() ([]byte, error) { + if c.quic.transportParams == nil { + c.quic.events = append(c.quic.events, QUICEvent{ + Kind: QUICTransportParametersRequired, + }) + } + for c.quic.transportParams == nil { + if err := c.quicWaitForSignal(); err != nil { + return nil, err + } + } + return c.quic.transportParams, nil +} + +func (c *Conn) quicHandshakeComplete() { + c.quic.events = append(c.quic.events, QUICEvent{ + Kind: QUICHandshakeDone, + }) +} + +func (c *Conn) quicRejectedEarlyData() { + c.quic.events = append(c.quic.events, QUICEvent{ + Kind: QUICRejectedEarlyData, + }) +} + +// quicWaitForSignal notifies the QUICConn that handshake progress is blocked, +// and waits for a signal that the handshake should proceed. +// +// The handshake may become blocked waiting for handshake bytes +// or for the user to provide transport parameters. +func (c *Conn) quicWaitForSignal() error { + // Drop the handshake mutex while blocked to allow the user + // to call ConnectionState before the handshake completes. + c.handshakeMutex.Unlock() + defer c.handshakeMutex.Lock() + // Send on blockedc to notify the QUICConn that the handshake is blocked. + // Exported methods of QUICConn wait for the handshake to become blocked + // before returning to the user. + select { + case c.quic.blockedc <- struct{}{}: + case <-c.quic.cancelc: + return c.sendAlertLocked(alertCloseNotify) + } + // The QUICConn reads from signalc to notify us that the handshake may + // be able to proceed. (The QUICConn reads, because we close signalc to + // indicate that the handshake has completed.) + select { + case c.quic.signalc <- struct{}{}: + c.hand.Write(c.quic.readbuf) + c.quic.readbuf = nil + case <-c.quic.cancelc: + return c.sendAlertLocked(alertCloseNotify) + } + return nil +} diff --git a/vendor/github.com/quic-go/qtls-go1-20/ticket.go b/vendor/github.com/quic-go/qtls-go1-20/ticket.go index 1b9289c2f..366620707 100644 --- a/vendor/github.com/quic-go/qtls-go1-20/ticket.go +++ b/vendor/github.com/quic-go/qtls-go1-20/ticket.go @@ -11,12 +11,9 @@ import ( "crypto/hmac" "crypto/sha256" "crypto/subtle" - "encoding/binary" "errors" - "io" - "time" - "golang.org/x/crypto/cryptobyte" + "io" ) // sessionState contains the information that is serialized into a session @@ -204,74 +201,3 @@ func (c *Conn) decryptTicket(encrypted []byte) (plaintext []byte, usedOldKey boo return plaintext, keyIndex > 0 } - -func (c *Conn) getSessionTicketMsg(appData []byte) (*newSessionTicketMsgTLS13, error) { - m := new(newSessionTicketMsgTLS13) - - var certsFromClient [][]byte - for _, cert := range c.peerCertificates { - certsFromClient = append(certsFromClient, cert.Raw) - } - state := sessionStateTLS13{ - cipherSuite: c.cipherSuite, - createdAt: uint64(c.config.time().Unix()), - resumptionSecret: c.resumptionSecret, - certificate: Certificate{ - Certificate: certsFromClient, - OCSPStaple: c.ocspResponse, - SignedCertificateTimestamps: c.scts, - }, - appData: appData, - alpn: c.clientProtocol, - } - if c.extraConfig != nil { - state.maxEarlyData = c.extraConfig.MaxEarlyData - } - stateBytes, err := state.marshal() - if err != nil { - return nil, err - } - m.label, err = c.encryptTicket(stateBytes) - if err != nil { - return nil, err - } - m.lifetime = uint32(maxSessionTicketLifetime / time.Second) - - // ticket_age_add is a random 32-bit value. See RFC 8446, section 4.6.1 - // The value is not stored anywhere; we never need to check the ticket age - // because 0-RTT is not supported. - ageAdd := make([]byte, 4) - _, err = c.config.rand().Read(ageAdd) - if err != nil { - return nil, err - } - m.ageAdd = binary.LittleEndian.Uint32(ageAdd) - - // ticket_nonce, which must be unique per connection, is always left at - // zero because we only ever send one ticket per connection. - - if c.extraConfig != nil { - m.maxEarlyData = c.extraConfig.MaxEarlyData - } - return m, nil -} - -// GetSessionTicket generates a new session ticket. -// It should only be called after the handshake completes. -// It can only be used for servers, and only if the alternative record layer is set. -// The ticket may be nil if config.SessionTicketsDisabled is set, -// or if the client isn't able to receive session tickets. -func (c *Conn) GetSessionTicket(appData []byte) ([]byte, error) { - if c.isClient || !c.isHandshakeComplete.Load() || c.extraConfig == nil || c.extraConfig.AlternativeRecordLayer == nil { - return nil, errors.New("GetSessionTicket is only valid for servers after completion of the handshake, and if an alternative record layer is set.") - } - if c.config.SessionTicketsDisabled { - return nil, nil - } - - m, err := c.getSessionTicketMsg(appData) - if err != nil { - return nil, err - } - return m.marshal() -} diff --git a/vendor/github.com/quic-go/qtls-go1-20/tls.go b/vendor/github.com/quic-go/qtls-go1-20/tls.go index 42207c235..47eed0855 100644 --- a/vendor/github.com/quic-go/qtls-go1-20/tls.go +++ b/vendor/github.com/quic-go/qtls-go1-20/tls.go @@ -31,11 +31,10 @@ import ( // using conn as the underlying transport. // The configuration config must be non-nil and must include // at least one certificate or else set GetCertificate. -func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { +func Server(conn net.Conn, config *Config) *Conn { c := &Conn{ - conn: conn, - config: fromConfig(config), - extraConfig: extraConfig, + conn: conn, + config: fromConfig(config), } c.handshakeFn = c.serverHandshake return c @@ -45,12 +44,11 @@ func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { // using conn as the underlying transport. // The config cannot be nil: users must set either ServerName or // InsecureSkipVerify in the config. -func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { +func Client(conn net.Conn, config *Config) *Conn { c := &Conn{ - conn: conn, - config: fromConfig(config), - extraConfig: extraConfig, - isClient: true, + conn: conn, + config: fromConfig(config), + isClient: true, } c.handshakeFn = c.clientHandshake return c @@ -59,8 +57,7 @@ func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { // A listener implements a network listener (net.Listener) for TLS connections. type listener struct { net.Listener - config *Config - extraConfig *ExtraConfig + config *Config } // Accept waits for and returns the next incoming TLS connection. @@ -70,18 +67,17 @@ func (l *listener) Accept() (net.Conn, error) { if err != nil { return nil, err } - return Server(c, l.config, l.extraConfig), nil + return Server(c, l.config), nil } // NewListener creates a Listener which accepts connections from an inner // Listener and wraps each connection with Server. // The configuration config must be non-nil and must include // at least one certificate or else set GetCertificate. -func NewListener(inner net.Listener, config *Config, extraConfig *ExtraConfig) net.Listener { +func NewListener(inner net.Listener, config *Config) net.Listener { l := new(listener) l.Listener = inner l.config = config - l.extraConfig = extraConfig return l } @@ -89,7 +85,7 @@ func NewListener(inner net.Listener, config *Config, extraConfig *ExtraConfig) n // given network address using net.Listen. // The configuration config must be non-nil and must include // at least one certificate or else set GetCertificate. -func Listen(network, laddr string, config *Config, extraConfig *ExtraConfig) (net.Listener, error) { +func Listen(network, laddr string, config *Config) (net.Listener, error) { if config == nil || len(config.Certificates) == 0 && config.GetCertificate == nil && config.GetConfigForClient == nil { return nil, errors.New("tls: neither Certificates, GetCertificate, nor GetConfigForClient set in Config") @@ -98,7 +94,7 @@ func Listen(network, laddr string, config *Config, extraConfig *ExtraConfig) (ne if err != nil { return nil, err } - return NewListener(l, config, extraConfig), nil + return NewListener(l, config), nil } type timeoutError struct{} @@ -117,11 +113,11 @@ func (timeoutError) Temporary() bool { return true } // // DialWithDialer uses context.Background internally; to specify the context, // use Dialer.DialContext with NetDialer set to the desired dialer. -func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) { - return dial(context.Background(), dialer, network, addr, config, extraConfig) +func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error) { + return dial(context.Background(), dialer, network, addr, config) } -func dial(ctx context.Context, netDialer *net.Dialer, network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) { +func dial(ctx context.Context, netDialer *net.Dialer, network, addr string, config *Config) (*Conn, error) { if netDialer.Timeout != 0 { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, netDialer.Timeout) @@ -157,7 +153,7 @@ func dial(ctx context.Context, netDialer *net.Dialer, network, addr string, conf config = c } - conn := Client(rawConn, config, extraConfig) + conn := Client(rawConn, config) if err := conn.HandshakeContext(ctx); err != nil { rawConn.Close() return nil, err @@ -171,8 +167,8 @@ func dial(ctx context.Context, netDialer *net.Dialer, network, addr string, conf // Dial interprets a nil configuration as equivalent to // the zero configuration; see the documentation of Config // for the defaults. -func Dial(network, addr string, config *Config, extraConfig *ExtraConfig) (*Conn, error) { - return DialWithDialer(new(net.Dialer), network, addr, config, extraConfig) +func Dial(network, addr string, config *Config) (*Conn, error) { + return DialWithDialer(new(net.Dialer), network, addr, config) } // Dialer dials TLS connections given a configuration and a Dialer for the @@ -188,8 +184,6 @@ type Dialer struct { // configuration; see the documentation of Config for the // defaults. Config *Config - - ExtraConfig *ExtraConfig } // Dial connects to the given network address and initiates a TLS @@ -220,7 +214,7 @@ func (d *Dialer) netDialer() *net.Dialer { // // The returned Conn, if any, will always be of type *Conn. func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { - c, err := dial(ctx, d.netDialer(), network, addr, d.Config, d.ExtraConfig) + c, err := dial(ctx, d.netDialer(), network, addr, d.Config) if err != nil { // Don't return c (a typed nil) in an interface. return nil, err diff --git a/vendor/github.com/quic-go/qtls-go1-20/unsafe.go b/vendor/github.com/quic-go/qtls-go1-20/unsafe.go index 55fa01b3d..67a75677c 100644 --- a/vendor/github.com/quic-go/qtls-go1-20/unsafe.go +++ b/vendor/github.com/quic-go/qtls-go1-20/unsafe.go @@ -94,3 +94,8 @@ func compareStruct(a, b reflect.Type) bool { } return true } + +// InitSessionTicketKeys triggers the initialization of session ticket keys. +func InitSessionTicketKeys(conf *Config) { + fromConfig(conf).ticketKeys(nil) +} diff --git a/vendor/github.com/quic-go/quic-go/.golangci.yml b/vendor/github.com/quic-go/quic-go/.golangci.yml index 7820be8c9..1315759bc 100644 --- a/vendor/github.com/quic-go/quic-go/.golangci.yml +++ b/vendor/github.com/quic-go/quic-go/.golangci.yml @@ -1,4 +1,6 @@ run: + skip-files: + - internal/handshake/cipher_suite.go linters-settings: depguard: type: blacklist diff --git a/vendor/github.com/quic-go/quic-go/README.md b/vendor/github.com/quic-go/quic-go/README.md index 53638882a..976a07ccd 100644 --- a/vendor/github.com/quic-go/quic-go/README.md +++ b/vendor/github.com/quic-go/quic-go/README.md @@ -4,6 +4,7 @@ [![PkgGoDev](https://pkg.go.dev/badge/github.com/quic-go/quic-go)](https://pkg.go.dev/github.com/quic-go/quic-go) [![Code Coverage](https://img.shields.io/codecov/c/github/quic-go/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/quic-go/quic-go/) +[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/quic-go.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:quic-go) quic-go is an implementation of the QUIC protocol ([RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000), [RFC 9001](https://datatracker.ietf.org/doc/html/rfc9001), [RFC 9002](https://datatracker.ietf.org/doc/html/rfc9002)) in Go. It has support for HTTP/3 ([RFC 9114](https://datatracker.ietf.org/doc/html/rfc9114)), including QPACK ([RFC 9204](https://datatracker.ietf.org/doc/html/rfc9204)). @@ -12,10 +13,6 @@ In addition to these base RFCs, it also implements the following RFCs: * Datagram Packetization Layer Path MTU Discovery (DPLPMTUD, [RFC 8899](https://datatracker.ietf.org/doc/html/rfc8899)) * QUIC Version 2 ([RFC 9369](https://datatracker.ietf.org/doc/html/rfc9369)) -In addition to the RFCs listed above, it currently implements the [IETF QUIC draft-29](https://tools.ietf.org/html/draft-ietf-quic-transport-29). Support for draft-29 will eventually be dropped, as it is phased out of the ecosystem. - -This repository provides both a QUIC implementation, located in the `quic` package, as well as an HTTP/3 implementation, located in the `http3` package. - ## Using QUIC ### Running a Server @@ -36,7 +33,7 @@ go func() { // ... error handling // handle the connection, usually in a new Go routine } -} +}() ``` The listener `ln` can now be used to accept incoming QUIC connections by (repeatedly) calling the `Accept` method (see below for more information on the `quic.Connection`). @@ -136,7 +133,7 @@ The `quic.Transport` contains a few configuration options that don't apply to an #### When the remote Peer closes the Connection -In case the peer closes the QUIC connection, all calls to open streams, accept streams, as well as all methods on streams immediately return an error. Users can use errors assertions to find out what exactly went wrong: +In case the peer closes the QUIC connection, all calls to open streams, accept streams, as well as all methods on streams immediately return an error. Additionally, it is set as cancellation cause of the connection context. Users can use errors assertions to find out what exactly went wrong: * `quic.VersionNegotiationError`: Happens during the handshake, if there is no overlap between our and the remote's supported QUIC versions. * `quic.HandshakeTimeoutError`: Happens if the QUIC handshake doesn't complete within the time specified in `quic.Config.HandshakeTimeout`. @@ -203,12 +200,13 @@ http.Client{ ## Projects using quic-go | Project | Description | Stars | -|-----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| +| --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | | [AdGuardHome](https://github.com/AdguardTeam/AdGuardHome) | Free and open source, powerful network-wide ads & trackers blocking DNS server. | ![GitHub Repo stars](https://img.shields.io/github/stars/AdguardTeam/AdGuardHome?style=flat-square) | | [algernon](https://github.com/xyproto/algernon) | Small self-contained pure-Go web server with Lua, Markdown, HTTP/2, QUIC, Redis and PostgreSQL support | ![GitHub Repo stars](https://img.shields.io/github/stars/xyproto/algernon?style=flat-square) | | [caddy](https://github.com/caddyserver/caddy/) | Fast, multi-platform web server with automatic HTTPS | ![GitHub Repo stars](https://img.shields.io/github/stars/caddyserver/caddy?style=flat-square) | | [cloudflared](https://github.com/cloudflare/cloudflared) | A tunneling daemon that proxies traffic from the Cloudflare network to your origins | ![GitHub Repo stars](https://img.shields.io/github/stars/cloudflare/cloudflared?style=flat-square) | | [go-libp2p](https://github.com/libp2p/go-libp2p) | libp2p implementation in Go, powering [Kubo](https://github.com/ipfs/kubo) (IPFS) and [Lotus](https://github.com/filecoin-project/lotus) (Filecoin), among others | ![GitHub Repo stars](https://img.shields.io/github/stars/libp2p/go-libp2p?style=flat-square) | +| [Hysteria](https://github.com/apernet/hysteria) | A powerful, lightning fast and censorship resistant proxy | ![GitHub Repo stars](https://img.shields.io/github/stars/apernet/hysteria?style=flat-square) | | [Mercure](https://github.com/dunglas/mercure) | An open, easy, fast, reliable and battery-efficient solution for real-time communications | ![GitHub Repo stars](https://img.shields.io/github/stars/dunglas/mercure?style=flat-square) | | [OONI Probe](https://github.com/ooni/probe-cli) | Next generation OONI Probe. Library and CLI tool. | ![GitHub Repo stars](https://img.shields.io/github/stars/ooni/probe-cli?style=flat-square) | | [syncthing](https://github.com/syncthing/syncthing/) | Open Source Continuous File Synchronization | ![GitHub Repo stars](https://img.shields.io/github/stars/syncthing/syncthing?style=flat-square) | @@ -224,7 +222,8 @@ quic-go always aims to support the latest two Go releases. ### Dependency on forked crypto/tls -Since the standard library didn't provide any QUIC APIs before the Go 1.21 release, we had to fork crypto/tls to add the required APIs ourselves: [qtls for Go 1.20](https://github.com/quic-go/qtls-go1-20) and [qtls for Go 1.19](https://github.com/quic-go/qtls-go1-19). This had led to a lot of pain in the Go ecosystem, and we're happy that we can rely on Go 1.21 going forward. +Since the standard library didn't provide any QUIC APIs before the Go 1.21 release, we had to fork crypto/tls to add the required APIs ourselves: [qtls for Go 1.20](https://github.com/quic-go/qtls-go1-20). +This had led to a lot of pain in the Go ecosystem, and we're happy that we can rely on Go 1.21 going forward. ## Contributing diff --git a/vendor/github.com/quic-go/quic-go/client.go b/vendor/github.com/quic-go/quic-go/client.go index 4dfacb50d..e0ae5d3cb 100644 --- a/vendor/github.com/quic-go/quic-go/client.go +++ b/vendor/github.com/quic-go/quic-go/client.go @@ -34,7 +34,7 @@ type client struct { conn quicConn - tracer logging.ConnectionTracer + tracer *logging.ConnectionTracer tracingID uint64 logger utils.Logger } @@ -55,11 +55,11 @@ func DialAddr(ctx context.Context, addr string, tlsConf *tls.Config, conf *Confi if err != nil { return nil, err } - dl, err := setupTransport(udpConn, tlsConf, true) + tr, err := setupTransport(udpConn, tlsConf, true) if err != nil { return nil, err } - return dl.Dial(ctx, udpAddr, tlsConf, conf) + return tr.dial(ctx, udpAddr, addr, tlsConf, conf, false) } // DialAddrEarly establishes a new 0-RTT QUIC connection to a server. @@ -73,13 +73,13 @@ func DialAddrEarly(ctx context.Context, addr string, tlsConf *tls.Config, conf * if err != nil { return nil, err } - dl, err := setupTransport(udpConn, tlsConf, true) + tr, err := setupTransport(udpConn, tlsConf, true) if err != nil { return nil, err } - conn, err := dl.DialEarly(ctx, udpAddr, tlsConf, conf) + conn, err := tr.dial(ctx, udpAddr, addr, tlsConf, conf, true) if err != nil { - dl.Close() + tr.Close() return nil, err } return conn, nil @@ -153,7 +153,7 @@ func dial( if c.config.Tracer != nil { c.tracer = c.config.Tracer(context.WithValue(ctx, ConnectionTracingKey, c.tracingID), protocol.PerspectiveClient, c.destConnID) } - if c.tracer != nil { + if c.tracer != nil && c.tracer.StartedConnection != nil { c.tracer.StartedConnection(c.sendConn.LocalAddr(), c.sendConn.RemoteAddr(), c.srcConnID, c.destConnID) } if err := c.dial(ctx); err != nil { @@ -163,12 +163,6 @@ func dial( } func newClient(sendConn sendConn, connIDGenerator ConnectionIDGenerator, config *Config, tlsConf *tls.Config, onClose func(), use0RTT bool) (*client, error) { - if tlsConf == nil { - tlsConf = &tls.Config{} - } else { - tlsConf = tlsConf.Clone() - } - srcConnID, err := connIDGenerator.GenerateConnectionID() if err != nil { return nil, err @@ -239,7 +233,7 @@ func (c *client) dial(ctx context.Context) error { select { case <-ctx.Done(): c.conn.shutdown() - return ctx.Err() + return context.Cause(ctx) case err := <-errorChan: return err case recreateErr := <-recreateChan: diff --git a/vendor/github.com/quic-go/quic-go/codecov.yml b/vendor/github.com/quic-go/quic-go/codecov.yml index 074d98325..a24c7a15e 100644 --- a/vendor/github.com/quic-go/quic-go/codecov.yml +++ b/vendor/github.com/quic-go/quic-go/codecov.yml @@ -1,18 +1,10 @@ coverage: round: nearest ignore: - - streams_map_incoming_bidi.go - - streams_map_incoming_uni.go - - streams_map_outgoing_bidi.go - - streams_map_outgoing_uni.go - http3/gzip_reader.go - interop/ - - internal/ackhandler/packet_linkedlist.go - - internal/utils/byteinterval_linkedlist.go - - internal/utils/newconnectionid_linkedlist.go - - internal/utils/packetinterval_linkedlist.go + - internal/handshake/cipher_suite.go - internal/utils/linkedlist/linkedlist.go - - logging/null_tracer.go - fuzzing/ - metrics/ status: diff --git a/vendor/github.com/quic-go/quic-go/config.go b/vendor/github.com/quic-go/quic-go/config.go index 59df4cfd9..49b9fc3f0 100644 --- a/vendor/github.com/quic-go/quic-go/config.go +++ b/vendor/github.com/quic-go/quic-go/config.go @@ -6,7 +6,6 @@ import ( "time" "github.com/quic-go/quic-go/internal/protocol" - "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/quicvarint" ) @@ -17,7 +16,11 @@ func (c *Config) Clone() *Config { } func (c *Config) handshakeTimeout() time.Duration { - return utils.Max(protocol.DefaultHandshakeTimeout, 2*c.HandshakeIdleTimeout) + return 2 * c.HandshakeIdleTimeout +} + +func (c *Config) maxRetryTokenAge() time.Duration { + return c.handshakeTimeout() } func validateConfig(config *Config) error { @@ -50,12 +53,6 @@ func validateConfig(config *Config) error { // it may be called with nil func populateServerConfig(config *Config) *Config { config = populateConfig(config) - if config.MaxTokenAge == 0 { - config.MaxTokenAge = protocol.TokenValidity - } - if config.MaxRetryTokenAge == 0 { - config.MaxRetryTokenAge = protocol.RetryTokenValidity - } if config.RequireAddressValidation == nil { config.RequireAddressValidation = func(net.Addr) bool { return false } } @@ -110,26 +107,23 @@ func populateConfig(config *Config) *Config { } return &Config{ - GetConfigForClient: config.GetConfigForClient, - Versions: versions, - HandshakeIdleTimeout: handshakeIdleTimeout, - MaxIdleTimeout: idleTimeout, - MaxTokenAge: config.MaxTokenAge, - MaxRetryTokenAge: config.MaxRetryTokenAge, - RequireAddressValidation: config.RequireAddressValidation, - KeepAlivePeriod: config.KeepAlivePeriod, - InitialStreamReceiveWindow: initialStreamReceiveWindow, - MaxStreamReceiveWindow: maxStreamReceiveWindow, - InitialConnectionReceiveWindow: initialConnectionReceiveWindow, - MaxConnectionReceiveWindow: maxConnectionReceiveWindow, - AllowConnectionWindowIncrease: config.AllowConnectionWindowIncrease, - MaxIncomingStreams: maxIncomingStreams, - MaxIncomingUniStreams: maxIncomingUniStreams, - TokenStore: config.TokenStore, - EnableDatagrams: config.EnableDatagrams, - DisablePathMTUDiscovery: config.DisablePathMTUDiscovery, - DisableVersionNegotiationPackets: config.DisableVersionNegotiationPackets, - Allow0RTT: config.Allow0RTT, - Tracer: config.Tracer, + GetConfigForClient: config.GetConfigForClient, + Versions: versions, + HandshakeIdleTimeout: handshakeIdleTimeout, + MaxIdleTimeout: idleTimeout, + RequireAddressValidation: config.RequireAddressValidation, + KeepAlivePeriod: config.KeepAlivePeriod, + InitialStreamReceiveWindow: initialStreamReceiveWindow, + MaxStreamReceiveWindow: maxStreamReceiveWindow, + InitialConnectionReceiveWindow: initialConnectionReceiveWindow, + MaxConnectionReceiveWindow: maxConnectionReceiveWindow, + AllowConnectionWindowIncrease: config.AllowConnectionWindowIncrease, + MaxIncomingStreams: maxIncomingStreams, + MaxIncomingUniStreams: maxIncomingUniStreams, + TokenStore: config.TokenStore, + EnableDatagrams: config.EnableDatagrams, + DisablePathMTUDiscovery: config.DisablePathMTUDiscovery, + Allow0RTT: config.Allow0RTT, + Tracer: config.Tracer, } } diff --git a/vendor/github.com/quic-go/quic-go/conn_id_manager.go b/vendor/github.com/quic-go/quic-go/conn_id_manager.go index 7840cdbf4..ba65aec04 100644 --- a/vendor/github.com/quic-go/quic-go/conn_id_manager.go +++ b/vendor/github.com/quic-go/quic-go/conn_id_manager.go @@ -174,13 +174,13 @@ func (h *connIDManager) ChangeInitialConnID(newConnID protocol.ConnectionID) { h.activeConnectionID = newConnID } -// SetStatelessResetToken is called when the server provides a stateless reset token in the transport parameters +// is called when the server provides a stateless reset token in the transport parameters func (h *connIDManager) SetStatelessResetToken(token protocol.StatelessResetToken) { - // Only set the stateless reset token if we're still using the connection ID with sequence number 0. - if h.activeSequenceNumber == 0 { - h.activeStatelessResetToken = &token - h.addStatelessResetToken(token) + if h.activeSequenceNumber != 0 { + panic("expected first connection ID to have sequence number 0") } + h.activeStatelessResetToken = &token + h.addStatelessResetToken(token) } func (h *connIDManager) SentPacket() { diff --git a/vendor/github.com/quic-go/quic-go/connection.go b/vendor/github.com/quic-go/quic-go/connection.go index c544b591a..09b522a9e 100644 --- a/vendor/github.com/quic-go/quic-go/connection.go +++ b/vendor/github.com/quic-go/quic-go/connection.go @@ -52,11 +52,13 @@ type streamManager interface { } type cryptoStreamHandler interface { - RunHandshake() + StartHandshake() error ChangeConnectionID(protocol.ConnectionID) SetLargest1RTTAcked(protocol.PacketNumber) error SetHandshakeConfirmed() GetSessionTicket() ([]byte, error) + NextEvent() handshake.Event + DiscardInitialKeys() io.Closer ConnectionState() handshake.ConnectionState } @@ -96,18 +98,6 @@ type connRunner interface { RemoveResetToken(protocol.StatelessResetToken) } -type handshakeRunner struct { - onReceivedParams func(*wire.TransportParameters) - onError func(error) - dropKeys func(protocol.EncryptionLevel) - onHandshakeComplete func() -} - -func (r *handshakeRunner) OnReceivedParams(tp *wire.TransportParameters) { r.onReceivedParams(tp) } -func (r *handshakeRunner) OnError(e error) { r.onError(e) } -func (r *handshakeRunner) DropKeys(el protocol.EncryptionLevel) { r.dropKeys(el) } -func (r *handshakeRunner) OnHandshakeComplete() { r.onHandshakeComplete() } - type closeError struct { err error remote bool @@ -165,6 +155,8 @@ type connection struct { packer packer mtuDiscoverer mtuDiscoverer // initialized when the handshake completes + initialStream cryptoStream + handshakeStream cryptoStream oneRTTStream cryptoStream // only set for the server cryptoStreamHandler cryptoStreamHandler @@ -176,19 +168,17 @@ type connection struct { closeChan chan closeError ctx context.Context - ctxCancel context.CancelFunc + ctxCancel context.CancelCauseFunc handshakeCtx context.Context handshakeCtxCancel context.CancelFunc undecryptablePackets []receivedPacket // undecryptable packets, waiting for a change in encryption level undecryptablePacketsToProcess []receivedPacket - clientHelloWritten <-chan *wire.TransportParameters - earlyConnReadyChan chan struct{} - handshakeCompleteChan chan struct{} // is closed when the handshake completes - sentFirstPacket bool - handshakeComplete bool - handshakeConfirmed bool + earlyConnReadyChan chan struct{} + sentFirstPacket bool + handshakeComplete bool + handshakeConfirmed bool receivedRetry bool versionNegotiated bool @@ -218,7 +208,7 @@ type connection struct { connState ConnectionState logID string - tracer logging.ConnectionTracer + tracer *logging.ConnectionTracer logger utils.Logger } @@ -242,23 +232,22 @@ var newConnection = func( tlsConf *tls.Config, tokenGenerator *handshake.TokenGenerator, clientAddressValidated bool, - tracer logging.ConnectionTracer, + tracer *logging.ConnectionTracer, tracingID uint64, logger utils.Logger, v protocol.VersionNumber, ) quicConn { s := &connection{ - conn: conn, - config: conf, - handshakeDestConnID: destConnID, - srcConnIDLen: srcConnID.Len(), - tokenGenerator: tokenGenerator, - oneRTTStream: newCryptoStream(), - perspective: protocol.PerspectiveServer, - handshakeCompleteChan: make(chan struct{}), - tracer: tracer, - logger: logger, - version: v, + conn: conn, + config: conf, + handshakeDestConnID: destConnID, + srcConnIDLen: srcConnID.Len(), + tokenGenerator: tokenGenerator, + oneRTTStream: newCryptoStream(), + perspective: protocol.PerspectiveServer, + tracer: tracer, + logger: logger, + version: v, } if origDestConnID.Len() > 0 { s.logID = origDestConnID.String() @@ -283,19 +272,18 @@ var newConnection = func( connIDGenerator, ) s.preSetup() - s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), ConnectionTracingKey, tracingID)) + s.ctx, s.ctxCancel = context.WithCancelCause(context.WithValue(context.Background(), ConnectionTracingKey, tracingID)) s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler( 0, getMaxPacketSize(s.conn.RemoteAddr()), s.rttStats, clientAddressValidated, + s.conn.capabilities().ECN, s.perspective, s.tracer, s.logger, ) s.mtuDiscoverer = newMTUDiscoverer(s.rttStats, getMaxPacketSize(s.conn.RemoteAddr()), s.sentPacketHandler.SetMaxDatagramSize) - initialStream := newCryptoStream() - handshakeStream := newCryptoStream() params := &wire.TransportParameters{ InitialMaxStreamDataBidiLocal: protocol.ByteCount(s.config.InitialStreamReceiveWindow), InitialMaxStreamDataBidiRemote: protocol.ByteCount(s.config.InitialStreamReceiveWindow), @@ -323,25 +311,14 @@ var newConnection = func( } else { params.MaxDatagramFrameSize = protocol.InvalidByteCount } - if s.tracer != nil { + if s.tracer != nil && s.tracer.SentTransportParameters != nil { s.tracer.SentTransportParameters(params) } cs := handshake.NewCryptoSetupServer( - initialStream, - handshakeStream, clientDestConnID, conn.LocalAddr(), conn.RemoteAddr(), params, - &handshakeRunner{ - onReceivedParams: s.handleTransportParameters, - onError: s.closeLocal, - dropKeys: s.dropEncryptionLevel, - onHandshakeComplete: func() { - runner.Retire(clientDestConnID) - close(s.handshakeCompleteChan) - }, - }, tlsConf, conf.Allow0RTT, s.rttStats, @@ -350,9 +327,9 @@ var newConnection = func( s.version, ) s.cryptoStreamHandler = cs - s.packer = newPacketPacker(srcConnID, s.connIDManager.Get, initialStream, handshakeStream, s.sentPacketHandler, s.retransmissionQueue, cs, s.framer, s.receivedPacketHandler, s.datagramQueue, s.perspective) + s.packer = newPacketPacker(srcConnID, s.connIDManager.Get, s.initialStream, s.handshakeStream, s.sentPacketHandler, s.retransmissionQueue, cs, s.framer, s.receivedPacketHandler, s.datagramQueue, s.perspective) s.unpacker = newPacketUnpacker(cs, s.srcConnIDLen) - s.cryptoStreamManager = newCryptoStreamManager(cs, initialStream, handshakeStream, s.oneRTTStream) + s.cryptoStreamManager = newCryptoStreamManager(cs, s.initialStream, s.handshakeStream, s.oneRTTStream) return s } @@ -368,24 +345,23 @@ var newClientConnection = func( initialPacketNumber protocol.PacketNumber, enable0RTT bool, hasNegotiatedVersion bool, - tracer logging.ConnectionTracer, + tracer *logging.ConnectionTracer, tracingID uint64, logger utils.Logger, v protocol.VersionNumber, ) quicConn { s := &connection{ - conn: conn, - config: conf, - origDestConnID: destConnID, - handshakeDestConnID: destConnID, - srcConnIDLen: srcConnID.Len(), - perspective: protocol.PerspectiveClient, - handshakeCompleteChan: make(chan struct{}), - logID: destConnID.String(), - logger: logger, - tracer: tracer, - versionNegotiated: hasNegotiatedVersion, - version: v, + conn: conn, + config: conf, + origDestConnID: destConnID, + handshakeDestConnID: destConnID, + srcConnIDLen: srcConnID.Len(), + perspective: protocol.PerspectiveClient, + logID: destConnID.String(), + logger: logger, + tracer: tracer, + versionNegotiated: hasNegotiatedVersion, + version: v, } s.connIDManager = newConnIDManager( destConnID, @@ -405,19 +381,19 @@ var newClientConnection = func( connIDGenerator, ) s.preSetup() - s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), ConnectionTracingKey, tracingID)) + s.ctx, s.ctxCancel = context.WithCancelCause(context.WithValue(context.Background(), ConnectionTracingKey, tracingID)) s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler( initialPacketNumber, getMaxPacketSize(s.conn.RemoteAddr()), s.rttStats, - false, /* has no effect */ + false, // has no effect + s.conn.capabilities().ECN, s.perspective, s.tracer, s.logger, ) s.mtuDiscoverer = newMTUDiscoverer(s.rttStats, getMaxPacketSize(s.conn.RemoteAddr()), s.sentPacketHandler.SetMaxDatagramSize) - initialStream := newCryptoStream() - handshakeStream := newCryptoStream() + oneRTTStream := newCryptoStream() params := &wire.TransportParameters{ InitialMaxStreamDataBidiRemote: protocol.ByteCount(s.config.InitialStreamReceiveWindow), InitialMaxStreamDataBidiLocal: protocol.ByteCount(s.config.InitialStreamReceiveWindow), @@ -442,22 +418,12 @@ var newClientConnection = func( } else { params.MaxDatagramFrameSize = protocol.InvalidByteCount } - if s.tracer != nil { + if s.tracer != nil && s.tracer.SentTransportParameters != nil { s.tracer.SentTransportParameters(params) } - cs, clientHelloWritten := handshake.NewCryptoSetupClient( - initialStream, - handshakeStream, + cs := handshake.NewCryptoSetupClient( destConnID, - conn.LocalAddr(), - conn.RemoteAddr(), params, - &handshakeRunner{ - onReceivedParams: s.handleTransportParameters, - onError: s.closeLocal, - dropKeys: s.dropEncryptionLevel, - onHandshakeComplete: func() { close(s.handshakeCompleteChan) }, - }, tlsConf, enable0RTT, s.rttStats, @@ -465,11 +431,10 @@ var newClientConnection = func( logger, s.version, ) - s.clientHelloWritten = clientHelloWritten s.cryptoStreamHandler = cs - s.cryptoStreamManager = newCryptoStreamManager(cs, initialStream, handshakeStream, newCryptoStream()) + s.cryptoStreamManager = newCryptoStreamManager(cs, s.initialStream, s.handshakeStream, oneRTTStream) s.unpacker = newPacketUnpacker(cs, s.srcConnIDLen) - s.packer = newPacketPacker(srcConnID, s.connIDManager.Get, initialStream, handshakeStream, s.sentPacketHandler, s.retransmissionQueue, cs, s.framer, s.receivedPacketHandler, s.datagramQueue, s.perspective) + s.packer = newPacketPacker(srcConnID, s.connIDManager.Get, s.initialStream, s.handshakeStream, s.sentPacketHandler, s.retransmissionQueue, cs, s.framer, s.receivedPacketHandler, s.datagramQueue, s.perspective) if len(tlsConf.ServerName) > 0 { s.tokenStoreKey = tlsConf.ServerName } else { @@ -484,6 +449,8 @@ var newClientConnection = func( } func (s *connection) preSetup() { + s.initialStream = newCryptoStream() + s.handshakeStream = newCryptoStream() s.sendQueue = newSendQueue(s.conn) s.retransmissionQueue = newRetransmissionQueue() s.frameParser = wire.NewFrameParser(s.config.EnableDatagrams) @@ -526,15 +493,19 @@ func (s *connection) preSetup() { // run the connection main loop func (s *connection) run() error { - defer s.ctxCancel() + var closeErr closeError + defer func() { + s.ctxCancel(closeErr.err) + }() s.timer = *newTimer() - handshaking := make(chan struct{}) - go func() { - defer close(handshaking) - s.cryptoStreamHandler.RunHandshake() - }() + if err := s.cryptoStreamHandler.StartHandshake(); err != nil { + return err + } + if err := s.handleHandshakeEvents(); err != nil { + return err + } go func() { if err := s.sendQueue.Run(); err != nil { s.destroyImpl(err) @@ -542,23 +513,10 @@ func (s *connection) run() error { }() if s.perspective == protocol.PerspectiveClient { - select { - case zeroRTTParams := <-s.clientHelloWritten: - s.scheduleSending() - if zeroRTTParams != nil { - s.restoreTransportParameters(zeroRTTParams) - close(s.earlyConnReadyChan) - } - case closeErr := <-s.closeChan: - // put the close error back into the channel, so that the run loop can receive it - s.closeChan <- closeErr - } + s.scheduleSending() // so the ClientHello actually gets sent } - var ( - closeErr closeError - sendQueueAvailable <-chan struct{} - ) + var sendQueueAvailable <-chan struct{} runLoop: for { @@ -566,8 +524,6 @@ runLoop: select { case closeErr = <-s.closeChan: break runLoop - case <-s.handshakeCompleteChan: - s.handleHandshakeComplete() default: } @@ -638,8 +594,6 @@ runLoop: if !wasProcessed { continue } - case <-s.handshakeCompleteChan: - s.handleHandshakeComplete() } } @@ -686,11 +640,12 @@ runLoop: } s.cryptoStreamHandler.Close() - <-handshaking s.sendQueue.Close() // close the send queue before sending the CONNECTION_CLOSE s.handleCloseError(&closeErr) - if e := (&errCloseForRecreating{}); !errors.As(closeErr.err, &e) && s.tracer != nil { - s.tracer.Close() + if s.tracer != nil && s.tracer.Close != nil { + if e := (&errCloseForRecreating{}); !errors.As(closeErr.err, &e) { + s.tracer.Close() + } } s.logger.Infof("Connection %s closed.", s.logID) s.timer.Stop() @@ -717,7 +672,10 @@ func (s *connection) supportsDatagrams() bool { func (s *connection) ConnectionState() ConnectionState { s.connStateMutex.Lock() defer s.connStateMutex.Unlock() - s.connState.TLS = s.cryptoStreamHandler.ConnectionState() + cs := s.cryptoStreamHandler.ConnectionState() + s.connState.TLS = cs.ConnectionState + s.connState.Used0RTT = cs.Used0RTT + s.connState.GSO = s.conn.capabilities().GSO return s.connState } @@ -764,29 +722,32 @@ func (s *connection) idleTimeoutStartTime() time.Time { return utils.MaxTime(s.lastPacketReceivedTime, s.firstAckElicitingPacketAfterIdleSentTime) } -func (s *connection) handleHandshakeComplete() { - s.handshakeComplete = true - s.handshakeCompleteChan = nil // prevent this case from ever being selected again +func (s *connection) handleHandshakeComplete() error { defer s.handshakeCtxCancel() // Once the handshake completes, we have derived 1-RTT keys. - // There's no point in queueing undecryptable packets for later decryption any more. + // There's no point in queueing undecryptable packets for later decryption anymore. s.undecryptablePackets = nil s.connIDManager.SetHandshakeComplete() s.connIDGenerator.SetHandshakeComplete() + // The server applies transport parameters right away, but the client side has to wait for handshake completion. + // During a 0-RTT connection, the client is only allowed to use the new transport parameters for 1-RTT packets. if s.perspective == protocol.PerspectiveClient { s.applyTransportParameters() - return + return nil } - s.handleHandshakeConfirmed() + // All these only apply to the server side. + if err := s.handleHandshakeConfirmed(); err != nil { + return err + } ticket, err := s.cryptoStreamHandler.GetSessionTicket() if err != nil { - s.closeLocal(err) + return err } - if ticket != nil { + if ticket != nil { // may be nil if session tickets are disabled via tls.Config.SessionTicketsDisabled s.oneRTTStream.Write(ticket) for s.oneRTTStream.HasData() { s.queueControlFrame(s.oneRTTStream.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize)) @@ -794,13 +755,18 @@ func (s *connection) handleHandshakeComplete() { } token, err := s.tokenGenerator.NewToken(s.conn.RemoteAddr()) if err != nil { - s.closeLocal(err) + return err } s.queueControlFrame(&wire.NewTokenFrame{Token: token}) s.queueControlFrame(&wire.HandshakeDoneFrame{}) + return nil } -func (s *connection) handleHandshakeConfirmed() { +func (s *connection) handleHandshakeConfirmed() error { + if err := s.dropEncryptionLevel(protocol.EncryptionHandshake); err != nil { + return err + } + s.handshakeConfirmed = true s.sentPacketHandler.SetHandshakeConfirmed() s.cryptoStreamHandler.SetHandshakeConfirmed() @@ -812,6 +778,7 @@ func (s *connection) handleHandshakeConfirmed() { } s.mtuDiscoverer.Start(utils.Min(maxPacketSize, protocol.MaxPacketBufferSize)) } + return nil } func (s *connection) handlePacketImpl(rp receivedPacket) bool { @@ -836,14 +803,14 @@ func (s *connection) handlePacketImpl(rp receivedPacket) bool { var err error destConnID, err = wire.ParseConnectionID(p.data, s.srcConnIDLen) if err != nil { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.ByteCount(len(data)), logging.PacketDropHeaderParseError) } s.logger.Debugf("error parsing packet, couldn't parse connection ID: %s", err) break } if destConnID != lastConnID { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.ByteCount(len(data)), logging.PacketDropUnknownConnectionID) } s.logger.Debugf("coalesced packet has different destination connection ID: %s, expected %s", destConnID, lastConnID) @@ -854,7 +821,7 @@ func (s *connection) handlePacketImpl(rp receivedPacket) bool { if wire.IsLongHeaderPacket(p.data[0]) { hdr, packetData, rest, err := wire.ParsePacket(p.data) if err != nil { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { dropReason := logging.PacketDropHeaderParseError if err == wire.ErrUnsupportedVersion { dropReason = logging.PacketDropUnsupportedVersion @@ -867,7 +834,7 @@ func (s *connection) handlePacketImpl(rp receivedPacket) bool { lastConnID = hdr.DestConnectionID if hdr.Version != s.version { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), protocol.ByteCount(len(data)), logging.PacketDropUnexpectedVersion) } s.logger.Debugf("Dropping packet with version %x. Expected %x.", hdr.Version, s.version) @@ -926,14 +893,14 @@ func (s *connection) handleShortHeaderPacket(p receivedPacket, destConnID protoc if s.receivedPacketHandler.IsPotentiallyDuplicate(pn, protocol.Encryption1RTT) { s.logger.Debugf("Dropping (potentially) duplicate packet.") - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketType1RTT, p.Size(), logging.PacketDropDuplicate) } return false } var log func([]logging.Frame) - if s.tracer != nil { + if s.tracer != nil && s.tracer.ReceivedShortHeaderPacket != nil { log = func(frames []logging.Frame) { s.tracer.ReceivedShortHeaderPacket( &logging.ShortHeader{ @@ -943,6 +910,7 @@ func (s *connection) handleShortHeaderPacket(p receivedPacket, destConnID protoc KeyPhase: keyPhase, }, p.Size(), + p.ecn, frames, ) } @@ -965,13 +933,13 @@ func (s *connection) handleLongHeaderPacket(p receivedPacket, hdr *wire.Header) }() if hdr.Type == protocol.PacketTypeRetry { - return s.handleRetryPacket(hdr, p.data) + return s.handleRetryPacket(hdr, p.data, p.rcvTime) } // The server can change the source connection ID with the first Handshake packet. // After this, all packets with a different source connection have to be ignored. if s.receivedFirstPacket && hdr.Type == protocol.PacketTypeInitial && hdr.SrcConnectionID != s.handshakeDestConnID { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeInitial, p.Size(), logging.PacketDropUnknownConnectionID) } s.logger.Debugf("Dropping Initial packet (%d bytes) with unexpected source connection ID: %s (expected %s)", p.Size(), hdr.SrcConnectionID, s.handshakeDestConnID) @@ -979,7 +947,7 @@ func (s *connection) handleLongHeaderPacket(p receivedPacket, hdr *wire.Header) } // drop 0-RTT packets, if we are a client if s.perspective == protocol.PerspectiveClient && hdr.Type == protocol.PacketType0RTT { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketType0RTT, p.Size(), logging.PacketDropKeyUnavailable) } return false @@ -998,7 +966,7 @@ func (s *connection) handleLongHeaderPacket(p receivedPacket, hdr *wire.Header) if s.receivedPacketHandler.IsPotentiallyDuplicate(packet.hdr.PacketNumber, packet.encryptionLevel) { s.logger.Debugf("Dropping (potentially) duplicate packet.") - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropDuplicate) } return false @@ -1014,7 +982,7 @@ func (s *connection) handleLongHeaderPacket(p receivedPacket, hdr *wire.Header) func (s *connection) handleUnpackError(err error, p receivedPacket, pt logging.PacketType) (wasQueued bool) { switch err { case handshake.ErrKeysDropped: - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(pt, p.Size(), logging.PacketDropKeyUnavailable) } s.logger.Debugf("Dropping %s packet (%d bytes) because we already dropped the keys.", pt, p.Size()) @@ -1030,7 +998,7 @@ func (s *connection) handleUnpackError(err error, p receivedPacket, pt logging.P }) case handshake.ErrDecryptionFailed: // This might be a packet injected by an attacker. Drop it. - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(pt, p.Size(), logging.PacketDropPayloadDecryptError) } s.logger.Debugf("Dropping %s packet (%d bytes) that could not be unpacked. Error: %s", pt, p.Size(), err) @@ -1038,7 +1006,7 @@ func (s *connection) handleUnpackError(err error, p receivedPacket, pt logging.P var headerErr *headerParseError if errors.As(err, &headerErr) { // This might be a packet injected by an attacker. Drop it. - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(pt, p.Size(), logging.PacketDropHeaderParseError) } s.logger.Debugf("Dropping %s packet (%d bytes) for which we couldn't unpack the header. Error: %s", pt, p.Size(), err) @@ -1051,16 +1019,16 @@ func (s *connection) handleUnpackError(err error, p receivedPacket, pt logging.P return false } -func (s *connection) handleRetryPacket(hdr *wire.Header, data []byte) bool /* was this a valid Retry */ { +func (s *connection) handleRetryPacket(hdr *wire.Header, data []byte, rcvTime time.Time) bool /* was this a valid Retry */ { if s.perspective == protocol.PerspectiveServer { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket) } s.logger.Debugf("Ignoring Retry.") return false } if s.receivedFirstPacket { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket) } s.logger.Debugf("Ignoring Retry, since we already received a packet.") @@ -1068,7 +1036,7 @@ func (s *connection) handleRetryPacket(hdr *wire.Header, data []byte) bool /* wa } destConnID := s.connIDManager.Get() if hdr.SrcConnectionID == destConnID { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket) } s.logger.Debugf("Ignoring Retry, since the server didn't change the Source Connection ID.") @@ -1083,7 +1051,7 @@ func (s *connection) handleRetryPacket(hdr *wire.Header, data []byte) bool /* wa tag := handshake.GetRetryIntegrityTag(data[:len(data)-16], destConnID, hdr.Version) if !bytes.Equal(data[len(data)-16:], tag[:]) { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.ByteCount(len(data)), logging.PacketDropPayloadDecryptError) } s.logger.Debugf("Ignoring spoofed Retry. Integrity Tag doesn't match.") @@ -1095,12 +1063,12 @@ func (s *connection) handleRetryPacket(hdr *wire.Header, data []byte) bool /* wa (&wire.ExtendedHeader{Header: *hdr}).Log(s.logger) s.logger.Debugf("Switching destination connection ID to: %s", hdr.SrcConnectionID) } - if s.tracer != nil { + if s.tracer != nil && s.tracer.ReceivedRetry != nil { s.tracer.ReceivedRetry(hdr) } newDestConnID := hdr.SrcConnectionID s.receivedRetry = true - if err := s.sentPacketHandler.ResetForRetry(); err != nil { + if err := s.sentPacketHandler.ResetForRetry(rcvTime); err != nil { s.closeLocal(err) return false } @@ -1116,7 +1084,7 @@ func (s *connection) handleRetryPacket(hdr *wire.Header, data []byte) bool /* wa func (s *connection) handleVersionNegotiationPacket(p receivedPacket) { if s.perspective == protocol.PerspectiveServer || // servers never receive version negotiation packets s.receivedFirstPacket || s.versionNegotiated { // ignore delayed / duplicated version negotiation packets - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropUnexpectedPacket) } return @@ -1124,7 +1092,7 @@ func (s *connection) handleVersionNegotiationPacket(p receivedPacket) { src, dest, supportedVersions, err := wire.ParseVersionNegotiationPacket(p.data) if err != nil { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropHeaderParseError) } s.logger.Debugf("Error parsing Version Negotiation packet: %s", err) @@ -1133,7 +1101,7 @@ func (s *connection) handleVersionNegotiationPacket(p receivedPacket) { for _, v := range supportedVersions { if v == s.version { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropUnexpectedVersion) } // The Version Negotiation packet contains the version that we offered. @@ -1143,7 +1111,7 @@ func (s *connection) handleVersionNegotiationPacket(p receivedPacket) { } s.logger.Infof("Received a Version Negotiation packet. Supported Versions: %s", supportedVersions) - if s.tracer != nil { + if s.tracer != nil && s.tracer.ReceivedVersionNegotiationPacket != nil { s.tracer.ReceivedVersionNegotiationPacket(dest, src, supportedVersions) } newVersion, ok := protocol.ChooseSupportedVersion(s.config.Versions, supportedVersions) @@ -1155,7 +1123,7 @@ func (s *connection) handleVersionNegotiationPacket(p receivedPacket) { s.logger.Infof("No compatible QUIC version found.") return } - if s.tracer != nil { + if s.tracer != nil && s.tracer.NegotiatedVersion != nil { s.tracer.NegotiatedVersion(newVersion, s.config.Versions, supportedVersions) } @@ -1175,7 +1143,7 @@ func (s *connection) handleUnpackedLongHeaderPacket( ) error { if !s.receivedFirstPacket { s.receivedFirstPacket = true - if !s.versionNegotiated && s.tracer != nil { + if !s.versionNegotiated && s.tracer != nil && s.tracer.NegotiatedVersion != nil { var clientVersions, serverVersions []protocol.VersionNumber switch s.perspective { case protocol.PerspectiveClient: @@ -1202,7 +1170,7 @@ func (s *connection) handleUnpackedLongHeaderPacket( s.handshakeDestConnID = packet.hdr.SrcConnectionID s.connIDManager.ChangeInitialConnID(packet.hdr.SrcConnectionID) } - if s.tracer != nil { + if s.tracer != nil && s.tracer.StartedConnection != nil { s.tracer.StartedConnection( s.conn.LocalAddr(), s.conn.RemoteAddr(), @@ -1213,14 +1181,22 @@ func (s *connection) handleUnpackedLongHeaderPacket( } } + if s.perspective == protocol.PerspectiveServer && packet.encryptionLevel == protocol.EncryptionHandshake { + // On the server side, Initial keys are dropped as soon as the first Handshake packet is received. + // See Section 4.9.1 of RFC 9001. + if err := s.dropEncryptionLevel(protocol.EncryptionInitial); err != nil { + return err + } + } + s.lastPacketReceivedTime = rcvTime s.firstAckElicitingPacketAfterIdleSentTime = time.Time{} s.keepAlivePingSent = false var log func([]logging.Frame) - if s.tracer != nil { + if s.tracer != nil && s.tracer.ReceivedLongHeaderPacket != nil { log = func(frames []logging.Frame) { - s.tracer.ReceivedLongHeaderPacket(packet.hdr, packetSize, frames) + s.tracer.ReceivedLongHeaderPacket(packet.hdr, packetSize, ecn, frames) } } isAckEliciting, err := s.handleFrames(packet.data, packet.hdr.DestConnectionID, packet.encryptionLevel, log) @@ -1261,6 +1237,7 @@ func (s *connection) handleFrames( if log != nil { frames = make([]logging.Frame, 0, 4) } + handshakeWasComplete := s.handshakeComplete var handleErr error for len(data) > 0 { l, frame, err := s.frameParser.ParseNext(data, encLevel, s.version) @@ -1297,6 +1274,17 @@ func (s *connection) handleFrames( return false, handleErr } } + + // Handle completion of the handshake after processing all the frames. + // This ensures that we correctly handle the following case on the server side: + // We receive a Handshake packet that contains the CRYPTO frame that allows us to complete the handshake, + // and an ACK serialized after that CRYPTO frame. In this case, we still want to process the ACK frame. + if !handshakeWasComplete && s.handshakeComplete { + if err := s.handleHandshakeComplete(); err != nil { + return false, err + } + } + return } @@ -1354,7 +1342,7 @@ func (s *connection) handlePacket(p receivedPacket) { select { case s.receivedPackets <- p: default: - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropDOSPrevention) } } @@ -1378,16 +1366,43 @@ func (s *connection) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame } func (s *connection) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error { - encLevelChanged, err := s.cryptoStreamManager.HandleCryptoFrame(frame, encLevel) - if err != nil { + if err := s.cryptoStreamManager.HandleCryptoFrame(frame, encLevel); err != nil { return err } - if encLevelChanged { - // Queue all packets for decryption that have been undecryptable so far. - s.undecryptablePacketsToProcess = s.undecryptablePackets - s.undecryptablePackets = nil + return s.handleHandshakeEvents() +} + +func (s *connection) handleHandshakeEvents() error { + for { + ev := s.cryptoStreamHandler.NextEvent() + var err error + switch ev.Kind { + case handshake.EventNoEvent: + return nil + case handshake.EventHandshakeComplete: + // Don't call handleHandshakeComplete yet. + // It's advantageous to process ACK frames that might be serialized after the CRYPTO frame first. + s.handshakeComplete = true + case handshake.EventReceivedTransportParameters: + err = s.handleTransportParameters(ev.TransportParameters) + case handshake.EventRestoredTransportParameters: + s.restoreTransportParameters(ev.TransportParameters) + close(s.earlyConnReadyChan) + case handshake.EventReceivedReadKeys: + // Queue all packets for decryption that have been undecryptable so far. + s.undecryptablePacketsToProcess = s.undecryptablePackets + s.undecryptablePackets = nil + case handshake.EventDiscard0RTTKeys: + err = s.dropEncryptionLevel(protocol.Encryption0RTT) + case handshake.EventWriteInitialData: + _, err = s.initialStream.Write(ev.Data) + case handshake.EventWriteHandshakeData: + _, err = s.handshakeStream.Write(ev.Data) + } + if err != nil { + return err + } } - return nil } func (s *connection) handleStreamFrame(frame *wire.StreamFrame) error { @@ -1482,7 +1497,7 @@ func (s *connection) handleHandshakeDoneFrame() error { } } if !s.handshakeConfirmed { - s.handleHandshakeConfirmed() + return s.handleHandshakeConfirmed() } return nil } @@ -1495,8 +1510,13 @@ func (s *connection) handleAckFrame(frame *wire.AckFrame, encLevel protocol.Encr if !acked1RTTPacket { return nil } + // On the client side: If the packet acknowledged a 1-RTT packet, this confirms the handshake. + // This is only possible if the ACK was sent in a 1-RTT packet. + // This is an optimization over simply waiting for a HANDSHAKE_DONE frame, see section 4.1.2 of RFC 9001. if s.perspective == protocol.PerspectiveClient && !s.handshakeConfirmed { - s.handleHandshakeConfirmed() + if err := s.handleHandshakeConfirmed(); err != nil { + return err + } } return s.cryptoStreamHandler.SetLargest1RTTAcked(frame.LargestAcked()) } @@ -1602,7 +1622,7 @@ func (s *connection) handleCloseError(closeErr *closeError) { s.datagramQueue.CloseWithError(e) } - if s.tracer != nil && !errors.As(e, &recreateErr) { + if s.tracer != nil && s.tracer.ClosedConnection != nil && !errors.As(e, &recreateErr) { s.tracer.ClosedConnection(e) } @@ -1628,21 +1648,24 @@ func (s *connection) handleCloseError(closeErr *closeError) { s.connIDGenerator.ReplaceWithClosed(s.perspective, connClosePacket) } -func (s *connection) dropEncryptionLevel(encLevel protocol.EncryptionLevel) { - s.sentPacketHandler.DropPackets(encLevel) - s.receivedPacketHandler.DropPackets(encLevel) - if s.tracer != nil { +func (s *connection) dropEncryptionLevel(encLevel protocol.EncryptionLevel) error { + if s.tracer != nil && s.tracer.DroppedEncryptionLevel != nil { s.tracer.DroppedEncryptionLevel(encLevel) } - if encLevel == protocol.Encryption0RTT { + s.sentPacketHandler.DropPackets(encLevel) + s.receivedPacketHandler.DropPackets(encLevel) + //nolint:exhaustive // only Initial and 0-RTT need special treatment + switch encLevel { + case protocol.EncryptionInitial: + s.cryptoStreamHandler.DiscardInitialKeys() + case protocol.Encryption0RTT: s.streamsMap.ResetFor0RTT() if err := s.connFlowController.Reset(); err != nil { - s.closeLocal(err) - } - if err := s.framer.Handle0RTTRejection(); err != nil { - s.closeLocal(err) + return err } + return s.framer.Handle0RTTRejection() } + return s.cryptoStreamManager.Drop(encLevel) } // is called for the client, when restoring transport parameters saved for 0-RTT @@ -1660,14 +1683,24 @@ func (s *connection) restoreTransportParameters(params *wire.TransportParameters s.connStateMutex.Unlock() } -func (s *connection) handleTransportParameters(params *wire.TransportParameters) { +func (s *connection) handleTransportParameters(params *wire.TransportParameters) error { + if s.tracer != nil && s.tracer.ReceivedTransportParameters != nil { + s.tracer.ReceivedTransportParameters(params) + } if err := s.checkTransportParameters(params); err != nil { - s.closeLocal(&qerr.TransportError{ + return &qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: err.Error(), - }) - return + } } + + if s.perspective == protocol.PerspectiveClient && s.peerParams != nil && s.ConnectionState().Used0RTT && !params.ValidForUpdate(s.peerParams) { + return &qerr.TransportError{ + ErrorCode: qerr.ProtocolViolation, + ErrorMessage: "server sent reduced limits after accepting 0-RTT data", + } + } + s.peerParams = params // On the client side we have to wait for handshake completion. // During a 0-RTT connection, we are only allowed to use the new transport parameters for 1-RTT packets. @@ -1681,15 +1714,13 @@ func (s *connection) handleTransportParameters(params *wire.TransportParameters) s.connStateMutex.Lock() s.connState.SupportsDatagrams = s.supportsDatagrams() s.connStateMutex.Unlock() + return nil } func (s *connection) checkTransportParameters(params *wire.TransportParameters) error { if s.logger.Debug() { s.logger.Debugf("Processed Transport Parameters: %s", params) } - if s.tracer != nil { - s.tracer.ReceivedTransportParameters(params) - } // check the initial_source_connection_id if params.InitialSourceConnectionID != s.handshakeDestConnID { @@ -1805,9 +1836,10 @@ func (s *connection) sendPackets(now time.Time) error { if err != nil { return err } - s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, buf.Len(), false) - s.registerPackedShortHeaderPacket(p, now) - s.sendQueue.Send(buf, buf.Len()) + ecn := s.sentPacketHandler.ECNMode(true) + s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, buf.Len(), false) + s.registerPackedShortHeaderPacket(p, ecn, now) + s.sendQueue.Send(buf, 0, ecn) // This is kind of a hack. We need to trigger sending again somehow. s.pacingDeadline = deadlineSendImmediately return nil @@ -1817,6 +1849,9 @@ func (s *connection) sendPackets(now time.Time) error { s.framer.QueueControlFrame(&wire.DataBlockedFrame{MaximumData: offset}) } s.windowUpdateQueue.QueueAll() + if cf := s.cryptoStreamManager.GetPostHandshakeData(protocol.MaxPostHandshakeCryptoFrameSize); cf != nil { + s.queueControlFrame(cf) + } if !s.handshakeConfirmed { packet, err := s.packer.PackCoalescedPacket(false, s.mtuDiscoverer.CurrentSize(), s.version) @@ -1824,7 +1859,9 @@ func (s *connection) sendPackets(now time.Time) error { return err } s.sentFirstPacket = true - s.sendPackedCoalescedPacket(packet, now) + if err := s.sendPackedCoalescedPacket(packet, s.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()), now); err != nil { + return err + } sendMode := s.sentPacketHandler.SendMode(now) if sendMode == ackhandler.SendPacingLimited { s.resetPacingDeadline() @@ -1843,7 +1880,8 @@ func (s *connection) sendPackets(now time.Time) error { func (s *connection) sendPacketsWithoutGSO(now time.Time) error { for { buf := getPacketBuffer() - if _, err := s.appendPacket(buf, s.mtuDiscoverer.CurrentSize(), now); err != nil { + ecn := s.sentPacketHandler.ECNMode(true) + if _, err := s.appendOneShortHeaderPacket(buf, s.mtuDiscoverer.CurrentSize(), ecn, now); err != nil { if err == errNothingToPack { buf.Release() return nil @@ -1851,7 +1889,7 @@ func (s *connection) sendPacketsWithoutGSO(now time.Time) error { return err } - s.sendQueue.Send(buf, buf.Len()) + s.sendQueue.Send(buf, 0, ecn) if s.sendQueue.WouldBlock() { return nil @@ -1876,9 +1914,10 @@ func (s *connection) sendPacketsWithGSO(now time.Time) error { buf := getLargePacketBuffer() maxSize := s.mtuDiscoverer.CurrentSize() + ecn := s.sentPacketHandler.ECNMode(true) for { var dontSendMore bool - size, err := s.appendPacket(buf, maxSize, now) + size, err := s.appendOneShortHeaderPacket(buf, maxSize, ecn, now) if err != nil { if err != errNothingToPack { return err @@ -1900,15 +1939,19 @@ func (s *connection) sendPacketsWithGSO(now time.Time) error { } } + // Don't send more packets in this batch if they require a different ECN marking than the previous ones. + nextECN := s.sentPacketHandler.ECNMode(true) + // Append another packet if // 1. The congestion controller and pacer allow sending more // 2. The last packet appended was a full-size packet - // 3. We still have enough space for another full-size packet in the buffer - if !dontSendMore && size == maxSize && buf.Len()+maxSize <= buf.Cap() { + // 3. The next packet will have the same ECN marking + // 4. We still have enough space for another full-size packet in the buffer + if !dontSendMore && size == maxSize && nextECN == ecn && buf.Len()+maxSize <= buf.Cap() { continue } - s.sendQueue.Send(buf, maxSize) + s.sendQueue.Send(buf, uint16(maxSize), ecn) if dontSendMore { return nil @@ -1937,6 +1980,7 @@ func (s *connection) resetPacingDeadline() { func (s *connection) maybeSendAckOnlyPacket(now time.Time) error { if !s.handshakeConfirmed { + ecn := s.sentPacketHandler.ECNMode(false) packet, err := s.packer.PackCoalescedPacket(true, s.mtuDiscoverer.CurrentSize(), s.version) if err != nil { return err @@ -1944,10 +1988,10 @@ func (s *connection) maybeSendAckOnlyPacket(now time.Time) error { if packet == nil { return nil } - s.sendPackedCoalescedPacket(packet, time.Now()) - return nil + return s.sendPackedCoalescedPacket(packet, ecn, time.Now()) } + ecn := s.sentPacketHandler.ECNMode(true) p, buf, err := s.packer.PackAckOnlyPacket(s.mtuDiscoverer.CurrentSize(), s.version) if err != nil { if err == errNothingToPack { @@ -1955,9 +1999,9 @@ func (s *connection) maybeSendAckOnlyPacket(now time.Time) error { } return err } - s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, buf.Len(), false) - s.registerPackedShortHeaderPacket(p, now) - s.sendQueue.Send(buf, buf.Len()) + s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, buf.Len(), false) + s.registerPackedShortHeaderPacket(p, ecn, now) + s.sendQueue.Send(buf, 0, ecn) return nil } @@ -1989,25 +2033,24 @@ func (s *connection) sendProbePacket(encLevel protocol.EncryptionLevel, now time if packet == nil || (len(packet.longHdrPackets) == 0 && packet.shortHdrPacket == nil) { return fmt.Errorf("connection BUG: couldn't pack %s probe packet", encLevel) } - s.sendPackedCoalescedPacket(packet, now) - return nil + return s.sendPackedCoalescedPacket(packet, s.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()), now) } -// appendPacket appends a new packet to the given packetBuffer. +// appendOneShortHeaderPacket appends a new packet to the given packetBuffer. // If there was nothing to pack, the returned size is 0. -func (s *connection) appendPacket(buf *packetBuffer, maxSize protocol.ByteCount, now time.Time) (protocol.ByteCount, error) { +func (s *connection) appendOneShortHeaderPacket(buf *packetBuffer, maxSize protocol.ByteCount, ecn protocol.ECN, now time.Time) (protocol.ByteCount, error) { startLen := buf.Len() p, err := s.packer.AppendPacket(buf, maxSize, s.version) if err != nil { return 0, err } size := buf.Len() - startLen - s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, size, false) - s.registerPackedShortHeaderPacket(p, now) + s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, size, false) + s.registerPackedShortHeaderPacket(p, ecn, now) return size, nil } -func (s *connection) registerPackedShortHeaderPacket(p shortHeaderPacket, now time.Time) { +func (s *connection) registerPackedShortHeaderPacket(p shortHeaderPacket, ecn protocol.ECN, now time.Time) { if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && (len(p.StreamFrames) > 0 || ackhandler.HasAckElicitingFrames(p.Frames)) { s.firstAckElicitingPacketAfterIdleSentTime = now } @@ -2016,12 +2059,12 @@ func (s *connection) registerPackedShortHeaderPacket(p shortHeaderPacket, now ti if p.Ack != nil { largestAcked = p.Ack.LargestAcked() } - s.sentPacketHandler.SentPacket(now, p.PacketNumber, largestAcked, p.StreamFrames, p.Frames, protocol.Encryption1RTT, p.Length, p.IsPathMTUProbePacket) + s.sentPacketHandler.SentPacket(now, p.PacketNumber, largestAcked, p.StreamFrames, p.Frames, protocol.Encryption1RTT, ecn, p.Length, p.IsPathMTUProbePacket) s.connIDManager.SentPacket() } -func (s *connection) sendPackedCoalescedPacket(packet *coalescedPacket, now time.Time) { - s.logCoalescedPacket(packet) +func (s *connection) sendPackedCoalescedPacket(packet *coalescedPacket, ecn protocol.ECN, now time.Time) error { + s.logCoalescedPacket(packet, ecn) for _, p := range packet.longHdrPackets { if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() { s.firstAckElicitingPacketAfterIdleSentTime = now @@ -2030,7 +2073,14 @@ func (s *connection) sendPackedCoalescedPacket(packet *coalescedPacket, now time if p.ack != nil { largestAcked = p.ack.LargestAcked() } - s.sentPacketHandler.SentPacket(now, p.header.PacketNumber, largestAcked, p.streamFrames, p.frames, p.EncryptionLevel(), p.length, false) + s.sentPacketHandler.SentPacket(now, p.header.PacketNumber, largestAcked, p.streamFrames, p.frames, p.EncryptionLevel(), ecn, p.length, false) + if s.perspective == protocol.PerspectiveClient && p.EncryptionLevel() == protocol.EncryptionHandshake { + // On the client side, Initial keys are dropped as soon as the first Handshake packet is sent. + // See Section 4.9.1 of RFC 9001. + if err := s.dropEncryptionLevel(protocol.EncryptionInitial); err != nil { + return err + } + } } if p := packet.shortHdrPacket; p != nil { if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() { @@ -2040,10 +2090,11 @@ func (s *connection) sendPackedCoalescedPacket(packet *coalescedPacket, now time if p.Ack != nil { largestAcked = p.Ack.LargestAcked() } - s.sentPacketHandler.SentPacket(now, p.PacketNumber, largestAcked, p.StreamFrames, p.Frames, protocol.Encryption1RTT, p.Length, p.IsPathMTUProbePacket) + s.sentPacketHandler.SentPacket(now, p.PacketNumber, largestAcked, p.StreamFrames, p.Frames, protocol.Encryption1RTT, ecn, p.Length, p.IsPathMTUProbePacket) } s.connIDManager.SentPacket() - s.sendQueue.Send(packet.buffer, packet.buffer.Len()) + s.sendQueue.Send(packet.buffer, 0, ecn) + return nil } func (s *connection) sendConnectionClose(e error) ([]byte, error) { @@ -2064,11 +2115,12 @@ func (s *connection) sendConnectionClose(e error) ([]byte, error) { if err != nil { return nil, err } - s.logCoalescedPacket(packet) - return packet.buffer.Data, s.conn.Write(packet.buffer.Data, packet.buffer.Len()) + ecn := s.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()) + s.logCoalescedPacket(packet, ecn) + return packet.buffer.Data, s.conn.Write(packet.buffer.Data, 0, ecn) } -func (s *connection) logLongHeaderPacket(p *longHeaderPacket) { +func (s *connection) logLongHeaderPacket(p *longHeaderPacket, ecn protocol.ECN) { // quic-go logging if s.logger.Debug() { p.header.Log(s.logger) @@ -2078,19 +2130,25 @@ func (s *connection) logLongHeaderPacket(p *longHeaderPacket) { for _, frame := range p.frames { wire.LogFrame(s.logger, frame.Frame, true) } + for _, frame := range p.streamFrames { + wire.LogFrame(s.logger, frame.Frame, true) + } } // tracing - if s.tracer != nil { + if s.tracer != nil && s.tracer.SentLongHeaderPacket != nil { frames := make([]logging.Frame, 0, len(p.frames)) for _, f := range p.frames { frames = append(frames, logutils.ConvertFrame(f.Frame)) } + for _, f := range p.streamFrames { + frames = append(frames, logutils.ConvertFrame(f.Frame)) + } var ack *logging.AckFrame if p.ack != nil { ack = logutils.ConvertAckFrame(p.ack) } - s.tracer.SentLongHeaderPacket(p.header, p.length, ack, frames) + s.tracer.SentLongHeaderPacket(p.header, p.length, ecn, ack, frames) } } @@ -2102,11 +2160,12 @@ func (s *connection) logShortHeaderPacket( pn protocol.PacketNumber, pnLen protocol.PacketNumberLen, kp protocol.KeyPhaseBit, + ecn protocol.ECN, size protocol.ByteCount, isCoalesced bool, ) { if s.logger.Debug() && !isCoalesced { - s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, 1-RTT", pn, size, s.logID) + s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, 1-RTT (ECN: %s)", pn, size, s.logID, ecn) } // quic-go logging if s.logger.Debug() { @@ -2123,7 +2182,7 @@ func (s *connection) logShortHeaderPacket( } // tracing - if s.tracer != nil { + if s.tracer != nil && s.tracer.SentShortHeaderPacket != nil { fs := make([]logging.Frame, 0, len(frames)+len(streamFrames)) for _, f := range frames { fs = append(fs, logutils.ConvertFrame(f.Frame)) @@ -2143,13 +2202,14 @@ func (s *connection) logShortHeaderPacket( KeyPhase: kp, }, size, + ecn, ack, fs, ) } } -func (s *connection) logCoalescedPacket(packet *coalescedPacket) { +func (s *connection) logCoalescedPacket(packet *coalescedPacket, ecn protocol.ECN) { if s.logger.Debug() { // There's a short period between dropping both Initial and Handshake keys and completion of the handshake, // during which we might call PackCoalescedPacket but just pack a short header packet. @@ -2162,6 +2222,7 @@ func (s *connection) logCoalescedPacket(packet *coalescedPacket) { packet.shortHdrPacket.PacketNumber, packet.shortHdrPacket.PacketNumberLen, packet.shortHdrPacket.KeyPhase, + ecn, packet.shortHdrPacket.Length, false, ) @@ -2174,10 +2235,10 @@ func (s *connection) logCoalescedPacket(packet *coalescedPacket) { } } for _, p := range packet.longHdrPackets { - s.logLongHeaderPacket(p) + s.logLongHeaderPacket(p, ecn) } if p := packet.shortHdrPacket; p != nil { - s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, p.Length, true) + s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, p.Length, true) } } @@ -2243,14 +2304,14 @@ func (s *connection) tryQueueingUndecryptablePacket(p receivedPacket, pt logging panic("shouldn't queue undecryptable packets after handshake completion") } if len(s.undecryptablePackets)+1 > protocol.MaxUndecryptablePackets { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(pt, p.Size(), logging.PacketDropDOSPrevention) } s.logger.Infof("Dropping undecryptable packet (%d bytes). Undecryptable packet queue full.", p.Size()) return } s.logger.Infof("Queueing packet (%d bytes) for later decryption", p.Size()) - if s.tracer != nil { + if s.tracer != nil && s.tracer.BufferedPacket != nil { s.tracer.BufferedPacket(pt, p.Size()) } s.undecryptablePackets = append(s.undecryptablePackets, p) @@ -2296,11 +2357,11 @@ func (s *connection) SendMessage(p []byte) error { return s.datagramQueue.AddAndWait(f) } -func (s *connection) ReceiveMessage() ([]byte, error) { +func (s *connection) ReceiveMessage(ctx context.Context) ([]byte, error) { if !s.config.EnableDatagrams { return nil, errors.New("datagram support disabled") } - return s.datagramQueue.Receive() + return s.datagramQueue.Receive(ctx) } func (s *connection) LocalAddr() net.Addr { diff --git a/vendor/github.com/quic-go/quic-go/crypto_stream.go b/vendor/github.com/quic-go/quic-go/crypto_stream.go index f10e91202..4be2a07ae 100644 --- a/vendor/github.com/quic-go/quic-go/crypto_stream.go +++ b/vendor/github.com/quic-go/quic-go/crypto_stream.go @@ -71,17 +71,9 @@ func (s *cryptoStreamImpl) HandleCryptoFrame(f *wire.CryptoFrame) error { // GetCryptoData retrieves data that was received in CRYPTO frames func (s *cryptoStreamImpl) GetCryptoData() []byte { - if len(s.msgBuf) < 4 { - return nil - } - msgLen := 4 + int(s.msgBuf[1])<<16 + int(s.msgBuf[2])<<8 + int(s.msgBuf[3]) - if len(s.msgBuf) < msgLen { - return nil - } - msg := make([]byte, msgLen) - copy(msg, s.msgBuf[:msgLen]) - s.msgBuf = s.msgBuf[msgLen:] - return msg + b := s.msgBuf + s.msgBuf = nil + return b } func (s *cryptoStreamImpl) Finish() error { diff --git a/vendor/github.com/quic-go/quic-go/crypto_stream_manager.go b/vendor/github.com/quic-go/quic-go/crypto_stream_manager.go index 91946acfa..c48e238a7 100644 --- a/vendor/github.com/quic-go/quic-go/crypto_stream_manager.go +++ b/vendor/github.com/quic-go/quic-go/crypto_stream_manager.go @@ -3,12 +3,14 @@ package quic import ( "fmt" + "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" ) type cryptoDataHandler interface { - HandleMessage([]byte, protocol.EncryptionLevel) bool + HandleMessage([]byte, protocol.EncryptionLevel) error + NextEvent() handshake.Event } type cryptoStreamManager struct { @@ -33,7 +35,7 @@ func newCryptoStreamManager( } } -func (m *cryptoStreamManager) HandleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) (bool /* encryption level changed */, error) { +func (m *cryptoStreamManager) HandleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error { var str cryptoStream //nolint:exhaustive // CRYPTO frames cannot be sent in 0-RTT packets. switch encLevel { @@ -44,18 +46,37 @@ func (m *cryptoStreamManager) HandleCryptoFrame(frame *wire.CryptoFrame, encLeve case protocol.Encryption1RTT: str = m.oneRTTStream default: - return false, fmt.Errorf("received CRYPTO frame with unexpected encryption level: %s", encLevel) + return fmt.Errorf("received CRYPTO frame with unexpected encryption level: %s", encLevel) } if err := str.HandleCryptoFrame(frame); err != nil { - return false, err + return err } for { data := str.GetCryptoData() if data == nil { - return false, nil + return nil } - if encLevelFinished := m.cryptoHandler.HandleMessage(data, encLevel); encLevelFinished { - return true, str.Finish() + if err := m.cryptoHandler.HandleMessage(data, encLevel); err != nil { + return err } } } + +func (m *cryptoStreamManager) GetPostHandshakeData(maxSize protocol.ByteCount) *wire.CryptoFrame { + if !m.oneRTTStream.HasData() { + return nil + } + return m.oneRTTStream.PopCryptoFrame(maxSize) +} + +func (m *cryptoStreamManager) Drop(encLevel protocol.EncryptionLevel) error { + //nolint:exhaustive // 1-RTT keys should never get dropped. + switch encLevel { + case protocol.EncryptionInitial: + return m.initialStream.Finish() + case protocol.EncryptionHandshake: + return m.handshakeStream.Finish() + default: + panic(fmt.Sprintf("dropped unexpected encryption level: %s", encLevel)) + } +} diff --git a/vendor/github.com/quic-go/quic-go/datagram_queue.go b/vendor/github.com/quic-go/quic-go/datagram_queue.go index 59c7d069b..ca80d404c 100644 --- a/vendor/github.com/quic-go/quic-go/datagram_queue.go +++ b/vendor/github.com/quic-go/quic-go/datagram_queue.go @@ -1,6 +1,7 @@ package quic import ( + "context" "sync" "github.com/quic-go/quic-go/internal/protocol" @@ -98,7 +99,7 @@ func (h *datagramQueue) HandleDatagramFrame(f *wire.DatagramFrame) { } // Receive gets a received DATAGRAM frame. -func (h *datagramQueue) Receive() ([]byte, error) { +func (h *datagramQueue) Receive(ctx context.Context) ([]byte, error) { for { h.rcvMx.Lock() if len(h.rcvQueue) > 0 { @@ -113,6 +114,8 @@ func (h *datagramQueue) Receive() ([]byte, error) { continue case <-h.closed: return nil, h.closeErr + case <-ctx.Done(): + return nil, ctx.Err() } } } diff --git a/vendor/github.com/quic-go/quic-go/framer.go b/vendor/github.com/quic-go/quic-go/framer.go index 9409af4c2..d5c61bcf7 100644 --- a/vendor/github.com/quic-go/quic-go/framer.go +++ b/vendor/github.com/quic-go/quic-go/framer.go @@ -23,6 +23,8 @@ type framer interface { Handle0RTTRejection() error } +const maxPathResponses = 256 + type framerI struct { mutex sync.Mutex @@ -33,6 +35,7 @@ type framerI struct { controlFrameMutex sync.Mutex controlFrames []wire.Frame + pathResponses []*wire.PathResponseFrame } var _ framer = &framerI{} @@ -52,20 +55,43 @@ func (f *framerI) HasData() bool { return true } f.controlFrameMutex.Lock() - hasData = len(f.controlFrames) > 0 - f.controlFrameMutex.Unlock() - return hasData + defer f.controlFrameMutex.Unlock() + return len(f.controlFrames) > 0 || len(f.pathResponses) > 0 } func (f *framerI) QueueControlFrame(frame wire.Frame) { f.controlFrameMutex.Lock() + defer f.controlFrameMutex.Unlock() + + if pr, ok := frame.(*wire.PathResponseFrame); ok { + // Only queue up to maxPathResponses PATH_RESPONSE frames. + // This limit should be high enough to never be hit in practice, + // unless the peer is doing something malicious. + if len(f.pathResponses) >= maxPathResponses { + return + } + f.pathResponses = append(f.pathResponses, pr) + return + } f.controlFrames = append(f.controlFrames, frame) - f.controlFrameMutex.Unlock() } func (f *framerI) AppendControlFrames(frames []ackhandler.Frame, maxLen protocol.ByteCount, v protocol.VersionNumber) ([]ackhandler.Frame, protocol.ByteCount) { - var length protocol.ByteCount f.controlFrameMutex.Lock() + defer f.controlFrameMutex.Unlock() + + var length protocol.ByteCount + // add a PATH_RESPONSE first, but only pack a single PATH_RESPONSE per packet + if len(f.pathResponses) > 0 { + frame := f.pathResponses[0] + frameLen := frame.Length(v) + if frameLen <= maxLen { + frames = append(frames, ackhandler.Frame{Frame: frame}) + length += frameLen + f.pathResponses = f.pathResponses[1:] + } + } + for len(f.controlFrames) > 0 { frame := f.controlFrames[len(f.controlFrames)-1] frameLen := frame.Length(v) @@ -76,7 +102,6 @@ func (f *framerI) AppendControlFrames(frames []ackhandler.Frame, maxLen protocol length += frameLen f.controlFrames = f.controlFrames[:len(f.controlFrames)-1] } - f.controlFrameMutex.Unlock() return frames, length } diff --git a/vendor/github.com/quic-go/quic-go/http3/body.go b/vendor/github.com/quic-go/quic-go/http3/body.go index 63ff43664..dc168e9e8 100644 --- a/vendor/github.com/quic-go/quic-go/http3/body.go +++ b/vendor/github.com/quic-go/quic-go/http3/body.go @@ -63,7 +63,8 @@ func (r *body) wasStreamHijacked() bool { } func (r *body) Read(b []byte) (int, error) { - return r.str.Read(b) + n, err := r.str.Read(b) + return n, maybeReplaceError(err) } func (r *body) Close() error { @@ -106,7 +107,7 @@ func (r *hijackableBody) Read(b []byte) (int, error) { if err != nil { r.requestDone() } - return n, err + return n, maybeReplaceError(err) } func (r *hijackableBody) requestDone() { diff --git a/vendor/github.com/quic-go/quic-go/http3/client.go b/vendor/github.com/quic-go/quic-go/http3/client.go index c54de5ea9..8aca48078 100644 --- a/vendor/github.com/quic-go/quic-go/http3/client.go +++ b/vendor/github.com/quic-go/quic-go/http3/client.go @@ -15,7 +15,6 @@ import ( "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/protocol" - "github.com/quic-go/quic-go/internal/qtls" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/quicvarint" @@ -319,40 +318,52 @@ func (c *client) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Respon } conn.CloseWithError(quic.ApplicationErrorCode(rerr.connErr), reason) } - return nil, rerr.err + return nil, maybeReplaceError(rerr.err) } if opt.DontCloseRequestStream { close(reqDone) <-done } - return rsp, rerr.err + return rsp, maybeReplaceError(rerr.err) } -func (c *client) sendRequestBody(str Stream, body io.ReadCloser) error { - defer body.Close() - b := make([]byte, bodyCopyBufferSize) - for { - n, rerr := body.Read(b) - if n == 0 { - if rerr == nil { - continue - } - if rerr == io.EOF { - break - } - } - if _, err := str.Write(b[:n]); err != nil { - return err - } - if rerr != nil { - if rerr == io.EOF { - break - } - str.CancelWrite(quic.StreamErrorCode(ErrCodeRequestCanceled)) - return rerr - } +// cancelingReader reads from the io.Reader. +// It cancels writing on the stream if any error other than io.EOF occurs. +type cancelingReader struct { + r io.Reader + str Stream +} + +func (r *cancelingReader) Read(b []byte) (int, error) { + n, err := r.r.Read(b) + if err != nil && err != io.EOF { + r.str.CancelWrite(quic.StreamErrorCode(ErrCodeRequestCanceled)) } - return nil + return n, err +} + +func (c *client) sendRequestBody(str Stream, body io.ReadCloser, contentLength int64) error { + defer body.Close() + buf := make([]byte, bodyCopyBufferSize) + sr := &cancelingReader{str: str, r: body} + if contentLength == -1 { + _, err := io.CopyBuffer(str, sr, buf) + return err + } + + // make sure we don't send more bytes than the content length + n, err := io.CopyBuffer(str, io.LimitReader(sr, contentLength), buf) + if err != nil { + return err + } + var extra int64 + extra, err = io.CopyBuffer(io.Discard, sr, buf) + n += extra + if n > contentLength { + str.CancelWrite(quic.StreamErrorCode(ErrCodeRequestCanceled)) + return fmt.Errorf("http: ContentLength=%d with Body length %d", contentLength, n) + } + return err } func (c *client) doRequest(req *http.Request, conn quic.EarlyConnection, str quic.Stream, opt RoundTripOpt, reqDone chan<- struct{}) (*http.Response, requestError) { @@ -372,7 +383,13 @@ func (c *client) doRequest(req *http.Request, conn quic.EarlyConnection, str qui if req.Body != nil { // send the request body asynchronously go func() { - if err := c.sendRequestBody(hstr, req.Body); err != nil { + contentLength := int64(-1) + // According to the documentation for http.Request.ContentLength, + // a value of 0 with a non-nil Body is also treated as unknown content length. + if req.ContentLength > 0 { + contentLength = req.ContentLength + } + if err := c.sendRequestBody(hstr, req.Body, contentLength); err != nil { c.logger.Errorf("Error writing request: %s", err) } if !opt.DontCloseRequestStream { @@ -402,28 +419,22 @@ func (c *client) doRequest(req *http.Request, conn quic.EarlyConnection, str qui return nil, newConnError(ErrCodeGeneralProtocolError, err) } - connState := qtls.ToTLSConnectionState(conn.ConnectionState().TLS) - res := &http.Response{ - Proto: "HTTP/3.0", - ProtoMajor: 3, - Header: http.Header{}, - TLS: &connState, - Request: req, + res, err := responseFromHeaders(hfs) + if err != nil { + return nil, newStreamError(ErrCodeMessageError, err) } - for _, hf := range hfs { - switch hf.Name { - case ":status": - status, err := strconv.Atoi(hf.Value) - if err != nil { - return nil, newStreamError(ErrCodeGeneralProtocolError, errors.New("malformed non-numeric status pseudo header")) - } - res.StatusCode = status - res.Status = hf.Value + " " + http.StatusText(status) - default: - res.Header.Add(hf.Name, hf.Value) - } + connState := conn.ConnectionState().TLS + res.TLS = &connState + res.Request = req + // Check that the server doesn't send more data in DATA frames than indicated by the Content-Length header (if set). + // See section 4.1.2 of RFC 9114. + var httpStr Stream + if _, ok := res.Header["Content-Length"]; ok && res.ContentLength >= 0 { + httpStr = newLengthLimitedStream(hstr, res.ContentLength) + } else { + httpStr = hstr } - respBody := newResponseBody(hstr, conn, reqDone) + respBody := newResponseBody(httpStr, conn, reqDone) // Rules for when to set Content-Length are defined in https://tools.ietf.org/html/rfc7230#section-3.3.2. _, hasTransferEncoding := res.Header["Transfer-Encoding"] diff --git a/vendor/github.com/quic-go/quic-go/http3/error.go b/vendor/github.com/quic-go/quic-go/http3/error.go new file mode 100644 index 000000000..b96ebeec0 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/http3/error.go @@ -0,0 +1,58 @@ +package http3 + +import ( + "errors" + "fmt" + + "github.com/quic-go/quic-go" +) + +// Error is returned from the round tripper (for HTTP clients) +// and inside the HTTP handler (for HTTP servers) if an HTTP/3 error occurs. +// See section 8 of RFC 9114. +type Error struct { + Remote bool + ErrorCode ErrCode + ErrorMessage string +} + +var _ error = &Error{} + +func (e *Error) Error() string { + s := e.ErrorCode.string() + if s == "" { + s = fmt.Sprintf("H3 error (%#x)", uint64(e.ErrorCode)) + } + // Usually errors are remote. Only make it explicit for local errors. + if !e.Remote { + s += " (local)" + } + if e.ErrorMessage != "" { + s += ": " + e.ErrorMessage + } + return s +} + +func maybeReplaceError(err error) error { + if err == nil { + return nil + } + + var ( + e Error + strErr *quic.StreamError + appErr *quic.ApplicationError + ) + switch { + default: + return err + case errors.As(err, &strErr): + e.Remote = strErr.Remote + e.ErrorCode = ErrCode(strErr.ErrorCode) + case errors.As(err, &appErr): + e.Remote = appErr.Remote + e.ErrorCode = ErrCode(appErr.ErrorCode) + e.ErrorMessage = appErr.ErrorMessage + } + return &e +} diff --git a/vendor/github.com/quic-go/quic-go/http3/error_codes.go b/vendor/github.com/quic-go/quic-go/http3/error_codes.go index 67b215d85..ae646586a 100644 --- a/vendor/github.com/quic-go/quic-go/http3/error_codes.go +++ b/vendor/github.com/quic-go/quic-go/http3/error_codes.go @@ -26,10 +26,18 @@ const ( ErrCodeMessageError ErrCode = 0x10e ErrCodeConnectError ErrCode = 0x10f ErrCodeVersionFallback ErrCode = 0x110 - ErrCodeDatagramError ErrCode = 0x4a1268 + ErrCodeDatagramError ErrCode = 0x33 ) func (e ErrCode) String() string { + s := e.string() + if s != "" { + return s + } + return fmt.Sprintf("unknown error code: %#x", uint16(e)) +} + +func (e ErrCode) string() string { switch e { case ErrCodeNoError: return "H3_NO_ERROR" @@ -68,6 +76,6 @@ func (e ErrCode) String() string { case ErrCodeDatagramError: return "H3_DATAGRAM_ERROR" default: - return fmt.Sprintf("unknown error code: %#x", uint16(e)) + return "" } } diff --git a/vendor/github.com/quic-go/quic-go/http3/frames.go b/vendor/github.com/quic-go/quic-go/http3/frames.go index cdd97bc5e..454e5f945 100644 --- a/vendor/github.com/quic-go/quic-go/http3/frames.go +++ b/vendor/github.com/quic-go/quic-go/http3/frames.go @@ -88,7 +88,7 @@ func (f *headersFrame) Append(b []byte) []byte { return quicvarint.Append(b, f.Length) } -const settingDatagram = 0xffd277 +const settingDatagram = 0x33 type settingsFrame struct { Datagram bool diff --git a/vendor/github.com/quic-go/quic-go/http3/headers.go b/vendor/github.com/quic-go/quic-go/http3/headers.go new file mode 100644 index 000000000..79c070b55 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/http3/headers.go @@ -0,0 +1,198 @@ +package http3 + +import ( + "errors" + "fmt" + "net/http" + "net/url" + "strconv" + "strings" + + "golang.org/x/net/http/httpguts" + + "github.com/quic-go/qpack" +) + +type header struct { + // Pseudo header fields defined in RFC 9114 + Path string + Method string + Authority string + Scheme string + Status string + // for Extended connect + Protocol string + // parsed and deduplicated + ContentLength int64 + // all non-pseudo headers + Headers http.Header +} + +func parseHeaders(headers []qpack.HeaderField, isRequest bool) (header, error) { + hdr := header{Headers: make(http.Header, len(headers))} + var readFirstRegularHeader, readContentLength bool + var contentLengthStr string + for _, h := range headers { + // field names need to be lowercase, see section 4.2 of RFC 9114 + if strings.ToLower(h.Name) != h.Name { + return header{}, fmt.Errorf("header field is not lower-case: %s", h.Name) + } + if !httpguts.ValidHeaderFieldValue(h.Value) { + return header{}, fmt.Errorf("invalid header field value for %s: %q", h.Name, h.Value) + } + if h.IsPseudo() { + if readFirstRegularHeader { + // all pseudo headers must appear before regular header fields, see section 4.3 of RFC 9114 + return header{}, fmt.Errorf("received pseudo header %s after a regular header field", h.Name) + } + var isResponsePseudoHeader bool // pseudo headers are either valid for requests or for responses + switch h.Name { + case ":path": + hdr.Path = h.Value + case ":method": + hdr.Method = h.Value + case ":authority": + hdr.Authority = h.Value + case ":protocol": + hdr.Protocol = h.Value + case ":scheme": + hdr.Scheme = h.Value + case ":status": + hdr.Status = h.Value + isResponsePseudoHeader = true + default: + return header{}, fmt.Errorf("unknown pseudo header: %s", h.Name) + } + if isRequest && isResponsePseudoHeader { + return header{}, fmt.Errorf("invalid request pseudo header: %s", h.Name) + } + if !isRequest && !isResponsePseudoHeader { + return header{}, fmt.Errorf("invalid response pseudo header: %s", h.Name) + } + } else { + if !httpguts.ValidHeaderFieldName(h.Name) { + return header{}, fmt.Errorf("invalid header field name: %q", h.Name) + } + readFirstRegularHeader = true + switch h.Name { + case "content-length": + // Ignore duplicate Content-Length headers. + // Fail if the duplicates differ. + if !readContentLength { + readContentLength = true + contentLengthStr = h.Value + } else if contentLengthStr != h.Value { + return header{}, fmt.Errorf("contradicting content lengths (%s and %s)", contentLengthStr, h.Value) + } + default: + hdr.Headers.Add(h.Name, h.Value) + } + } + } + if len(contentLengthStr) > 0 { + // use ParseUint instead of ParseInt, so that parsing fails on negative values + cl, err := strconv.ParseUint(contentLengthStr, 10, 63) + if err != nil { + return header{}, fmt.Errorf("invalid content length: %w", err) + } + hdr.Headers.Set("Content-Length", contentLengthStr) + hdr.ContentLength = int64(cl) + } + return hdr, nil +} + +func requestFromHeaders(headerFields []qpack.HeaderField) (*http.Request, error) { + hdr, err := parseHeaders(headerFields, true) + if err != nil { + return nil, err + } + // concatenate cookie headers, see https://tools.ietf.org/html/rfc6265#section-5.4 + if len(hdr.Headers["Cookie"]) > 0 { + hdr.Headers.Set("Cookie", strings.Join(hdr.Headers["Cookie"], "; ")) + } + + isConnect := hdr.Method == http.MethodConnect + // Extended CONNECT, see https://datatracker.ietf.org/doc/html/rfc8441#section-4 + isExtendedConnected := isConnect && hdr.Protocol != "" + if isExtendedConnected { + if hdr.Scheme == "" || hdr.Path == "" || hdr.Authority == "" { + return nil, errors.New("extended CONNECT: :scheme, :path and :authority must not be empty") + } + } else if isConnect { + if hdr.Path != "" || hdr.Authority == "" { // normal CONNECT + return nil, errors.New(":path must be empty and :authority must not be empty") + } + } else if len(hdr.Path) == 0 || len(hdr.Authority) == 0 || len(hdr.Method) == 0 { + return nil, errors.New(":path, :authority and :method must not be empty") + } + + var u *url.URL + var requestURI string + var protocol string + + if isConnect { + u = &url.URL{} + if isExtendedConnected { + u, err = url.ParseRequestURI(hdr.Path) + if err != nil { + return nil, err + } + } else { + u.Path = hdr.Path + } + u.Scheme = hdr.Scheme + u.Host = hdr.Authority + requestURI = hdr.Authority + protocol = hdr.Protocol + } else { + protocol = "HTTP/3.0" + u, err = url.ParseRequestURI(hdr.Path) + if err != nil { + return nil, fmt.Errorf("invalid content length: %w", err) + } + requestURI = hdr.Path + } + + return &http.Request{ + Method: hdr.Method, + URL: u, + Proto: protocol, + ProtoMajor: 3, + ProtoMinor: 0, + Header: hdr.Headers, + Body: nil, + ContentLength: hdr.ContentLength, + Host: hdr.Authority, + RequestURI: requestURI, + }, nil +} + +func hostnameFromRequest(req *http.Request) string { + if req.URL != nil { + return req.URL.Host + } + return "" +} + +func responseFromHeaders(headerFields []qpack.HeaderField) (*http.Response, error) { + hdr, err := parseHeaders(headerFields, false) + if err != nil { + return nil, err + } + if hdr.Status == "" { + return nil, errors.New("missing status field") + } + rsp := &http.Response{ + Proto: "HTTP/3.0", + ProtoMajor: 3, + Header: hdr.Headers, + ContentLength: hdr.ContentLength, + } + status, err := strconv.Atoi(hdr.Status) + if err != nil { + return nil, fmt.Errorf("invalid status code: %w", err) + } + rsp.StatusCode = status + rsp.Status = hdr.Status + " " + http.StatusText(status) + return rsp, nil +} diff --git a/vendor/github.com/quic-go/quic-go/http3/http_stream.go b/vendor/github.com/quic-go/quic-go/http3/http_stream.go index 2799e2b3c..1c0ec4f18 100644 --- a/vendor/github.com/quic-go/quic-go/http3/http_stream.go +++ b/vendor/github.com/quic-go/quic-go/http3/http_stream.go @@ -1,9 +1,11 @@ package http3 import ( + "errors" "fmt" "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/internal/utils" ) // A Stream is a HTTP/3 stream. @@ -66,6 +68,10 @@ func (s *stream) Read(b []byte) (int, error) { return n, err } +func (s *stream) hasMoreData() bool { + return s.bytesRemainingInFrame > 0 +} + func (s *stream) Write(b []byte) (int, error) { s.buf = s.buf[:0] s.buf = (&dataFrame{Length: uint64(len(b))}).Append(s.buf) @@ -74,3 +80,45 @@ func (s *stream) Write(b []byte) (int, error) { } return s.Stream.Write(b) } + +var errTooMuchData = errors.New("peer sent too much data") + +type lengthLimitedStream struct { + *stream + contentLength int64 + read int64 + resetStream bool +} + +var _ Stream = &lengthLimitedStream{} + +func newLengthLimitedStream(str *stream, contentLength int64) *lengthLimitedStream { + return &lengthLimitedStream{ + stream: str, + contentLength: contentLength, + } +} + +func (s *lengthLimitedStream) checkContentLengthViolation() error { + if s.read > s.contentLength || s.read == s.contentLength && s.hasMoreData() { + if !s.resetStream { + s.CancelRead(quic.StreamErrorCode(ErrCodeMessageError)) + s.CancelWrite(quic.StreamErrorCode(ErrCodeMessageError)) + s.resetStream = true + } + return errTooMuchData + } + return nil +} + +func (s *lengthLimitedStream) Read(b []byte) (int, error) { + if err := s.checkContentLengthViolation(); err != nil { + return 0, err + } + n, err := s.stream.Read(b[:utils.Min(int64(len(b)), s.contentLength-s.read)]) + s.read += int64(n) + if err := s.checkContentLengthViolation(); err != nil { + return n, err + } + return n, err +} diff --git a/vendor/github.com/quic-go/quic-go/http3/mockgen.go b/vendor/github.com/quic-go/quic-go/http3/mockgen.go index 38939e605..ad0a8a26e 100644 --- a/vendor/github.com/quic-go/quic-go/http3/mockgen.go +++ b/vendor/github.com/quic-go/quic-go/http3/mockgen.go @@ -2,7 +2,7 @@ package http3 -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package http3 -destination mock_roundtripcloser_test.go github.com/quic-go/quic-go/http3 RoundTripCloser" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package http3 -destination mock_roundtripcloser_test.go github.com/quic-go/quic-go/http3 RoundTripCloser" type RoundTripCloser = roundTripCloser -//go:generate sh -c "go run github.com/golang/mock/mockgen -package http3 -destination mock_quic_early_listener_test.go github.com/quic-go/quic-go/http3 QUICEarlyListener" +//go:generate sh -c "go run go.uber.org/mock/mockgen -package http3 -destination mock_quic_early_listener_test.go github.com/quic-go/quic-go/http3 QUICEarlyListener" diff --git a/vendor/github.com/quic-go/quic-go/http3/request.go b/vendor/github.com/quic-go/quic-go/http3/request.go deleted file mode 100644 index 9af25a570..000000000 --- a/vendor/github.com/quic-go/quic-go/http3/request.go +++ /dev/null @@ -1,111 +0,0 @@ -package http3 - -import ( - "errors" - "net/http" - "net/url" - "strconv" - "strings" - - "github.com/quic-go/qpack" -) - -func requestFromHeaders(headers []qpack.HeaderField) (*http.Request, error) { - var path, authority, method, protocol, scheme, contentLengthStr string - - httpHeaders := http.Header{} - for _, h := range headers { - switch h.Name { - case ":path": - path = h.Value - case ":method": - method = h.Value - case ":authority": - authority = h.Value - case ":protocol": - protocol = h.Value - case ":scheme": - scheme = h.Value - case "content-length": - contentLengthStr = h.Value - default: - if !h.IsPseudo() { - httpHeaders.Add(h.Name, h.Value) - } - } - } - - // concatenate cookie headers, see https://tools.ietf.org/html/rfc6265#section-5.4 - if len(httpHeaders["Cookie"]) > 0 { - httpHeaders.Set("Cookie", strings.Join(httpHeaders["Cookie"], "; ")) - } - - isConnect := method == http.MethodConnect - // Extended CONNECT, see https://datatracker.ietf.org/doc/html/rfc8441#section-4 - isExtendedConnected := isConnect && protocol != "" - if isExtendedConnected { - if scheme == "" || path == "" || authority == "" { - return nil, errors.New("extended CONNECT: :scheme, :path and :authority must not be empty") - } - } else if isConnect { - if path != "" || authority == "" { // normal CONNECT - return nil, errors.New(":path must be empty and :authority must not be empty") - } - } else if len(path) == 0 || len(authority) == 0 || len(method) == 0 { - return nil, errors.New(":path, :authority and :method must not be empty") - } - - var u *url.URL - var requestURI string - var err error - - if isConnect { - u = &url.URL{} - if isExtendedConnected { - u, err = url.ParseRequestURI(path) - if err != nil { - return nil, err - } - } else { - u.Path = path - } - u.Scheme = scheme - u.Host = authority - requestURI = authority - } else { - protocol = "HTTP/3.0" - u, err = url.ParseRequestURI(path) - if err != nil { - return nil, err - } - requestURI = path - } - - var contentLength int64 - if len(contentLengthStr) > 0 { - contentLength, err = strconv.ParseInt(contentLengthStr, 10, 64) - if err != nil { - return nil, err - } - } - - return &http.Request{ - Method: method, - URL: u, - Proto: protocol, - ProtoMajor: 3, - ProtoMinor: 0, - Header: httpHeaders, - Body: nil, - ContentLength: contentLength, - Host: authority, - RequestURI: requestURI, - }, nil -} - -func hostnameFromRequest(req *http.Request) string { - if req.URL != nil { - return req.URL.Host - } - return "" -} diff --git a/vendor/github.com/quic-go/quic-go/http3/response_writer.go b/vendor/github.com/quic-go/quic-go/http3/response_writer.go index b7c79d50e..0d9314782 100644 --- a/vendor/github.com/quic-go/quic-go/http3/response_writer.go +++ b/vendor/github.com/quic-go/quic-go/http3/response_writer.go @@ -3,6 +3,7 @@ package http3 import ( "bufio" "bytes" + "fmt" "net/http" "strconv" "strings" @@ -14,17 +15,61 @@ import ( "github.com/quic-go/qpack" ) +// The maximum length of an encoded HTTP/3 frame header is 16: +// The frame has a type and length field, both QUIC varints (maximum 8 bytes in length) +const frameHeaderLen = 16 + +// headerWriter wraps the stream, so that the first Write call flushes the header to the stream +type headerWriter struct { + str quic.Stream + header http.Header + status int // status code passed to WriteHeader + written bool + + logger utils.Logger +} + +// writeHeader encodes and flush header to the stream +func (hw *headerWriter) writeHeader() error { + var headers bytes.Buffer + enc := qpack.NewEncoder(&headers) + enc.WriteField(qpack.HeaderField{Name: ":status", Value: strconv.Itoa(hw.status)}) + + for k, v := range hw.header { + for index := range v { + enc.WriteField(qpack.HeaderField{Name: strings.ToLower(k), Value: v[index]}) + } + } + + buf := make([]byte, 0, frameHeaderLen+headers.Len()) + buf = (&headersFrame{Length: uint64(headers.Len())}).Append(buf) + hw.logger.Infof("Responding with %d", hw.status) + buf = append(buf, headers.Bytes()...) + + _, err := hw.str.Write(buf) + return err +} + +// first Write will trigger flushing header +func (hw *headerWriter) Write(p []byte) (int, error) { + if !hw.written { + if err := hw.writeHeader(); err != nil { + return 0, err + } + hw.written = true + } + return hw.str.Write(p) +} + type responseWriter struct { + *headerWriter conn quic.Connection - str quic.Stream bufferedStr *bufio.Writer buf []byte - header http.Header - status int // status code passed to WriteHeader headerWritten bool - - logger utils.Logger + contentLen int64 // if handler set valid Content-Length header + numWritten int64 // bytes written } var ( @@ -34,13 +79,16 @@ var ( ) func newResponseWriter(str quic.Stream, conn quic.Connection, logger utils.Logger) *responseWriter { + hw := &headerWriter{ + str: str, + header: http.Header{}, + logger: logger, + } return &responseWriter{ - header: http.Header{}, - buf: make([]byte, 16), - conn: conn, - str: str, - bufferedStr: bufio.NewWriter(str), - logger: logger, + headerWriter: hw, + buf: make([]byte, frameHeaderLen), + conn: conn, + bufferedStr: bufio.NewWriter(hw), } } @@ -53,32 +101,35 @@ func (w *responseWriter) WriteHeader(status int) { return } - if status < 100 || status >= 200 { + // http status must be 3 digits + if status < 100 || status > 999 { + panic(fmt.Sprintf("invalid WriteHeader code %v", status)) + } + + if status >= 200 { w.headerWritten = true + // Add Date header. + // This is what the standard library does. + // Can be disabled by setting the Date header to nil. + if _, ok := w.header["Date"]; !ok { + w.header.Set("Date", time.Now().UTC().Format(http.TimeFormat)) + } + // Content-Length checking + // use ParseUint instead of ParseInt, as negative values are invalid + if clen := w.header.Get("Content-Length"); clen != "" { + if cl, err := strconv.ParseUint(clen, 10, 63); err == nil { + w.contentLen = int64(cl) + } else { + // emit a warning for malformed Content-Length and remove it + w.logger.Errorf("Malformed Content-Length %s", clen) + w.header.Del("Content-Length") + } + } } w.status = status - var headers bytes.Buffer - enc := qpack.NewEncoder(&headers) - enc.WriteField(qpack.HeaderField{Name: ":status", Value: strconv.Itoa(status)}) - - for k, v := range w.header { - for index := range v { - enc.WriteField(qpack.HeaderField{Name: strings.ToLower(k), Value: v[index]}) - } - } - - w.buf = w.buf[:0] - w.buf = (&headersFrame{Length: uint64(headers.Len())}).Append(w.buf) - w.logger.Infof("Responding with %d", status) - if _, err := w.bufferedStr.Write(w.buf); err != nil { - w.logger.Errorf("could not write headers frame: %s", err.Error()) - } - if _, err := w.bufferedStr.Write(headers.Bytes()); err != nil { - w.logger.Errorf("could not write header frame payload: %s", err.Error()) - } if !w.headerWritten { - w.Flush() + w.writeHeader() } } @@ -105,17 +156,37 @@ func (w *responseWriter) Write(p []byte) (int, error) { if !bodyAllowed { return 0, http.ErrBodyNotAllowed } + + w.numWritten += int64(len(p)) + if w.contentLen != 0 && w.numWritten > w.contentLen { + return 0, http.ErrContentLength + } + df := &dataFrame{Length: uint64(len(p))} w.buf = w.buf[:0] w.buf = df.Append(w.buf) if _, err := w.bufferedStr.Write(w.buf); err != nil { - return 0, err + return 0, maybeReplaceError(err) } - return w.bufferedStr.Write(p) + n, err := w.bufferedStr.Write(p) + return n, maybeReplaceError(err) +} + +func (w *responseWriter) FlushError() error { + if !w.headerWritten { + w.WriteHeader(http.StatusOK) + } + if !w.written { + if err := w.writeHeader(); err != nil { + return maybeReplaceError(err) + } + w.written = true + } + return w.bufferedStr.Flush() } func (w *responseWriter) Flush() { - if err := w.bufferedStr.Flush(); err != nil { + if err := w.FlushError(); err != nil { w.logger.Errorf("could not flush to stream: %s", err.Error()) } } diff --git a/vendor/github.com/quic-go/quic-go/http3/roundtrip.go b/vendor/github.com/quic-go/quic-go/http3/roundtrip.go index 066b762b8..bed421030 100644 --- a/vendor/github.com/quic-go/quic-go/http3/roundtrip.go +++ b/vendor/github.com/quic-go/quic-go/http3/roundtrip.go @@ -52,7 +52,7 @@ type RoundTripper struct { // Enable support for HTTP/3 datagrams. // If set to true, QuicConfig.EnableDatagram will be set. - // See https://www.ietf.org/archive/id/draft-schinazi-masque-h3-datagram-02.html. + // See https://datatracker.ietf.org/doc/html/rfc9297. EnableDatagrams bool // Additional HTTP/3 settings. diff --git a/vendor/github.com/quic-go/quic-go/http3/server.go b/vendor/github.com/quic-go/quic-go/http3/server.go index a03b1fc53..4587a1fca 100644 --- a/vendor/github.com/quic-go/quic-go/http3/server.go +++ b/vendor/github.com/quic-go/quic-go/http3/server.go @@ -9,6 +9,7 @@ import ( "net" "net/http" "runtime" + "strconv" "strings" "sync" "time" @@ -31,12 +32,8 @@ var ( } ) -const ( - // NextProtoH3Draft29 is the ALPN protocol negotiated during the TLS handshake, for QUIC draft 29. - NextProtoH3Draft29 = "h3-29" - // NextProtoH3 is the ALPN protocol negotiated during the TLS handshake, for QUIC v1 and v2. - NextProtoH3 = "h3" -) +// NextProtoH3 is the ALPN protocol negotiated during the TLS handshake, for QUIC v1 and v2. +const NextProtoH3 = "h3" // StreamType is the stream type of a unidirectional stream. type StreamType uint64 @@ -62,8 +59,6 @@ func versionToALPN(v protocol.VersionNumber) string { switch v { case protocol.Version1, protocol.Version2: return NextProtoH3 - case protocol.VersionDraft29: - return NextProtoH3Draft29 default: return "" } @@ -178,7 +173,7 @@ type Server struct { // EnableDatagrams enables support for HTTP/3 datagrams. // If set to true, QuicConfig.EnableDatagram will be set. - // See https://datatracker.ietf.org/doc/html/draft-ietf-masque-h3-datagram-07. + // See https://datatracker.ietf.org/doc/html/rfc9297. EnableDatagrams bool // MaxHeaderBytes controls the maximum number of bytes the server will @@ -575,14 +570,22 @@ func (s *Server) handleRequest(conn quic.Connection, str quic.Stream, decoder *q } req, err := requestFromHeaders(hfs) if err != nil { - // TODO: use the right error code - return newStreamError(ErrCodeGeneralProtocolError, err) + return newStreamError(ErrCodeMessageError, err) } - connState := conn.ConnectionState().TLS.ConnectionState + connState := conn.ConnectionState().TLS req.TLS = &connState req.RemoteAddr = conn.RemoteAddr().String() - body := newRequestBody(newStream(str, onFrameError)) + + // Check that the client doesn't send more data in DATA frames than indicated by the Content-Length header (if set). + // See section 4.1.2 of RFC 9114. + var httpStr Stream + if _, ok := req.Header["Content-Length"]; ok && req.ContentLength >= 0 { + httpStr = newLengthLimitedStream(newStream(str, onFrameError), req.ContentLength) + } else { + httpStr = newStream(str, onFrameError) + } + body := newRequestBody(httpStr) req.Body = body if s.logger.Debug() { @@ -596,7 +599,6 @@ func (s *Server) handleRequest(conn quic.Connection, str quic.Stream, decoder *q ctx = context.WithValue(ctx, http.LocalAddrContextKey, conn.LocalAddr()) req = req.WithContext(ctx) r := newResponseWriter(str, conn, s.logger) - defer r.Flush() handler := s.Handler if handler == nil { handler = http.DefaultServeMux @@ -624,10 +626,15 @@ func (s *Server) handleRequest(conn quic.Connection, str quic.Stream, decoder *q return requestError{err: errHijacked} } - if panicked { - r.WriteHeader(http.StatusInternalServerError) - } else { - r.WriteHeader(http.StatusOK) + // only write response when there is no panic + if !panicked { + // response not written to the client yet, set Content-Length + if !r.written { + if _, haveCL := r.header["Content-Length"]; !haveCL { + r.header.Set("Content-Length", strconv.FormatInt(r.numWritten, 10)) + } + } + r.Flush() } // If the EOF was read by the handler, CancelRead() is a no-op. str.CancelRead(quic.StreamErrorCode(ErrCodeNoError)) diff --git a/vendor/github.com/quic-go/quic-go/interface.go b/vendor/github.com/quic-go/quic-go/interface.go index 8486c7fe0..6eac385df 100644 --- a/vendor/github.com/quic-go/quic-go/interface.go +++ b/vendor/github.com/quic-go/quic-go/interface.go @@ -2,6 +2,7 @@ package quic import ( "context" + "crypto/tls" "errors" "io" "net" @@ -19,10 +20,9 @@ type StreamID = protocol.StreamID type VersionNumber = protocol.VersionNumber const ( - // VersionDraft29 is IETF QUIC draft-29 - VersionDraft29 = protocol.VersionDraft29 // Version1 is RFC 9000 Version1 = protocol.Version1 + // Version2 is RFC 9369 Version2 = protocol.Version2 ) @@ -122,6 +122,8 @@ type SendStream interface { // The Context is canceled as soon as the write-side of the stream is closed. // This happens when Close() or CancelWrite() is called, or when the peer // cancels the read-side of their stream. + // The cancellation cause is set to the error that caused the stream to + // close, or `context.Canceled` in case the stream is closed without error. Context() context.Context // SetWriteDeadline sets the deadline for future Write calls // and any currently-blocked Write call. @@ -178,6 +180,8 @@ type Connection interface { // The error string will be sent to the peer. CloseWithError(ApplicationErrorCode, string) error // Context returns a context that is cancelled when the connection is closed. + // The cancellation cause is set to the error that caused the connection to + // close, or `context.Canceled` in case the listener is closed first. Context() context.Context // ConnectionState returns basic details about the QUIC connection. // Warning: This API should not be considered stable and might change soon. @@ -186,7 +190,7 @@ type Connection interface { // SendMessage sends a message as a datagram, as specified in RFC 9221. SendMessage([]byte) error // ReceiveMessage gets a message received in a datagram, as specified in RFC 9221. - ReceiveMessage() ([]byte, error) + ReceiveMessage(context.Context) ([]byte, error) } // An EarlyConnection is a connection that is handshaking. @@ -208,6 +212,9 @@ type EarlyConnection interface { // StatelessResetKey is a key used to derive stateless reset tokens. type StatelessResetKey [32]byte +// TokenGeneratorKey is a key used to encrypt session resumption tokens. +type TokenGeneratorKey = handshake.TokenProtectorKey + // A ConnectionID is a QUIC Connection ID, as defined in RFC 9000. // It is not able to handle QUIC Connection IDs longer than 20 bytes, // as they are allowed by RFC 8999. @@ -246,7 +253,8 @@ type Config struct { // If not set, it uses all versions available. Versions []VersionNumber // HandshakeIdleTimeout is the idle timeout before completion of the handshake. - // Specifically, if we don't receive any packet from the peer within this time, the connection attempt is aborted. + // If we don't receive any packet from the peer within this time, the connection attempt is aborted. + // Additionally, if the handshake doesn't complete in twice this time, the connection attempt is also aborted. // If this value is zero, the timeout is set to 5 seconds. HandshakeIdleTimeout time.Duration // MaxIdleTimeout is the maximum duration that may pass without any incoming network activity. @@ -260,13 +268,6 @@ type Config struct { // See https://datatracker.ietf.org/doc/html/rfc9000#section-8 for details. // If not set, every client is forced to prove its remote address. RequireAddressValidation func(net.Addr) bool - // MaxRetryTokenAge is the maximum age of a Retry token. - // If not set, it defaults to 5 seconds. Only valid for a server. - MaxRetryTokenAge time.Duration - // MaxTokenAge is the maximum age of the token presented during the handshake, - // for tokens that were issued on a previous connection. - // If not set, it defaults to 24 hours. Only valid for a server. - MaxTokenAge time.Duration // The TokenStore stores tokens received from the server. // Tokens are used to skip address validation on future connection attempts. // The key used to store tokens is the ServerName from the tls.Config, if set @@ -318,16 +319,12 @@ type Config struct { // Path MTU discovery is only available on systems that allow setting of the Don't Fragment (DF) bit. // If unavailable or disabled, packets will be at most 1252 (IPv4) / 1232 (IPv6) bytes in size. DisablePathMTUDiscovery bool - // DisableVersionNegotiationPackets disables the sending of Version Negotiation packets. - // This can be useful if version information is exchanged out-of-band. - // It has no effect for a client. - DisableVersionNegotiationPackets bool // Allow0RTT allows the application to decide if a 0-RTT connection attempt should be accepted. // Only valid for the server. Allow0RTT bool // Enable QUIC datagram support (RFC 9221). EnableDatagrams bool - Tracer func(context.Context, logging.Perspective, ConnectionID) logging.ConnectionTracer + Tracer func(context.Context, logging.Perspective, ConnectionID) *logging.ConnectionTracer } type ClientHelloInfo struct { @@ -337,12 +334,16 @@ type ClientHelloInfo struct { // ConnectionState records basic details about a QUIC connection type ConnectionState struct { // TLS contains information about the TLS connection state, incl. the tls.ConnectionState. - TLS handshake.ConnectionState + TLS tls.ConnectionState // SupportsDatagrams says if support for QUIC datagrams (RFC 9221) was negotiated. // This requires both nodes to support and enable the datagram extensions (via Config.EnableDatagrams). // If datagram support was negotiated, datagrams can be sent and received using the // SendMessage and ReceiveMessage methods on the Connection. SupportsDatagrams bool + // Used0RTT says if 0-RTT resumption was used. + Used0RTT bool // Version is the QUIC version of the QUIC connection. Version VersionNumber + // GSO says if generic segmentation offload is used + GSO bool } diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go index 2c7cc4fcf..cb28582a3 100644 --- a/vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/ackhandler.go @@ -14,10 +14,11 @@ func NewAckHandler( initialMaxDatagramSize protocol.ByteCount, rttStats *utils.RTTStats, clientAddressValidated bool, + enableECN bool, pers protocol.Perspective, - tracer logging.ConnectionTracer, + tracer *logging.ConnectionTracer, logger utils.Logger, ) (SentPacketHandler, ReceivedPacketHandler) { - sph := newSentPacketHandler(initialPacketNumber, initialMaxDatagramSize, rttStats, clientAddressValidated, pers, tracer, logger) + sph := newSentPacketHandler(initialPacketNumber, initialMaxDatagramSize, rttStats, clientAddressValidated, enableECN, pers, tracer, logger) return sph, newReceivedPacketHandler(sph, rttStats, logger) } diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/ecn.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/ecn.go new file mode 100644 index 000000000..68415ac6c --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/ecn.go @@ -0,0 +1,296 @@ +package ackhandler + +import ( + "fmt" + + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/logging" +) + +type ecnState uint8 + +const ( + ecnStateInitial ecnState = iota + ecnStateTesting + ecnStateUnknown + ecnStateCapable + ecnStateFailed +) + +// must fit into an uint8, otherwise numSentTesting and numLostTesting must have a larger type +const numECNTestingPackets = 10 + +type ecnHandler interface { + SentPacket(protocol.PacketNumber, protocol.ECN) + Mode() protocol.ECN + HandleNewlyAcked(packets []*packet, ect0, ect1, ecnce int64) (congested bool) + LostPacket(protocol.PacketNumber) +} + +// The ecnTracker performs ECN validation of a path. +// Once failed, it doesn't do any re-validation of the path. +// It is designed only work for 1-RTT packets, it doesn't handle multiple packet number spaces. +// In order to avoid revealing any internal state to on-path observers, +// callers should make sure to start using ECN (i.e. calling Mode) for the very first 1-RTT packet sent. +// The validation logic implemented here strictly follows the algorithm described in RFC 9000 section 13.4.2 and A.4. +type ecnTracker struct { + state ecnState + numSentTesting, numLostTesting uint8 + + firstTestingPacket protocol.PacketNumber + lastTestingPacket protocol.PacketNumber + firstCapablePacket protocol.PacketNumber + + numSentECT0, numSentECT1 int64 + numAckedECT0, numAckedECT1, numAckedECNCE int64 + + tracer *logging.ConnectionTracer + logger utils.Logger +} + +var _ ecnHandler = &ecnTracker{} + +func newECNTracker(logger utils.Logger, tracer *logging.ConnectionTracer) *ecnTracker { + return &ecnTracker{ + firstTestingPacket: protocol.InvalidPacketNumber, + lastTestingPacket: protocol.InvalidPacketNumber, + firstCapablePacket: protocol.InvalidPacketNumber, + state: ecnStateInitial, + logger: logger, + tracer: tracer, + } +} + +func (e *ecnTracker) SentPacket(pn protocol.PacketNumber, ecn protocol.ECN) { + //nolint:exhaustive // These are the only ones we need to take care of. + switch ecn { + case protocol.ECNNon: + return + case protocol.ECT0: + e.numSentECT0++ + case protocol.ECT1: + e.numSentECT1++ + case protocol.ECNUnsupported: + if e.state != ecnStateFailed { + panic("didn't expect ECN to be unsupported") + } + default: + panic(fmt.Sprintf("sent packet with unexpected ECN marking: %s", ecn)) + } + + if e.state == ecnStateCapable && e.firstCapablePacket == protocol.InvalidPacketNumber { + e.firstCapablePacket = pn + } + + if e.state != ecnStateTesting { + return + } + + e.numSentTesting++ + if e.firstTestingPacket == protocol.InvalidPacketNumber { + e.firstTestingPacket = pn + } + if e.numSentECT0+e.numSentECT1 >= numECNTestingPackets { + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateUnknown, logging.ECNTriggerNoTrigger) + } + e.state = ecnStateUnknown + e.lastTestingPacket = pn + } +} + +func (e *ecnTracker) Mode() protocol.ECN { + switch e.state { + case ecnStateInitial: + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateTesting, logging.ECNTriggerNoTrigger) + } + e.state = ecnStateTesting + return e.Mode() + case ecnStateTesting, ecnStateCapable: + return protocol.ECT0 + case ecnStateUnknown, ecnStateFailed: + return protocol.ECNNon + default: + panic(fmt.Sprintf("unknown ECN state: %d", e.state)) + } +} + +func (e *ecnTracker) LostPacket(pn protocol.PacketNumber) { + if e.state != ecnStateTesting && e.state != ecnStateUnknown { + return + } + if !e.isTestingPacket(pn) { + return + } + e.numLostTesting++ + // Only proceed if we have sent all 10 testing packets. + if e.state != ecnStateUnknown { + return + } + if e.numLostTesting >= e.numSentTesting { + e.logger.Debugf("Disabling ECN. All testing packets were lost.") + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedLostAllTestingPackets) + } + e.state = ecnStateFailed + return + } + // Path validation also fails if some testing packets are lost, and all other testing packets where CE-marked + e.failIfMangled() +} + +// HandleNewlyAcked handles the ECN counts on an ACK frame. +// It must only be called for ACK frames that increase the largest acknowledged packet number, +// see section 13.4.2.1 of RFC 9000. +func (e *ecnTracker) HandleNewlyAcked(packets []*packet, ect0, ect1, ecnce int64) (congested bool) { + if e.state == ecnStateFailed { + return false + } + + // ECN validation can fail if the received total count for either ECT(0) or ECT(1) exceeds + // the total number of packets sent with each corresponding ECT codepoint. + if ect0 > e.numSentECT0 || ect1 > e.numSentECT1 { + e.logger.Debugf("Disabling ECN. Received more ECT(0) / ECT(1) acknowledgements than packets sent.") + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedMoreECNCountsThanSent) + } + e.state = ecnStateFailed + return false + } + + // Count ECT0 and ECT1 marks that we used when sending the packets that are now being acknowledged. + var ackedECT0, ackedECT1 int64 + for _, p := range packets { + //nolint:exhaustive // We only ever send ECT(0) and ECT(1). + switch e.ecnMarking(p.PacketNumber) { + case protocol.ECT0: + ackedECT0++ + case protocol.ECT1: + ackedECT1++ + } + } + + // If an ACK frame newly acknowledges a packet that the endpoint sent with either the ECT(0) or ECT(1) + // codepoint set, ECN validation fails if the corresponding ECN counts are not present in the ACK frame. + // This check detects: + // * paths that bleach all ECN marks, and + // * peers that don't report any ECN counts + if (ackedECT0 > 0 || ackedECT1 > 0) && ect0 == 0 && ect1 == 0 && ecnce == 0 { + e.logger.Debugf("Disabling ECN. ECN-marked packet acknowledged, but no ECN counts on ACK frame.") + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedNoECNCounts) + } + e.state = ecnStateFailed + return false + } + + // Determine the increase in ECT0, ECT1 and ECNCE marks + newECT0 := ect0 - e.numAckedECT0 + newECT1 := ect1 - e.numAckedECT1 + newECNCE := ecnce - e.numAckedECNCE + + // We're only processing ACKs that increase the Largest Acked. + // Therefore, the ECN counters should only ever increase. + // Any decrease means that the peer's counting logic is broken. + if newECT0 < 0 || newECT1 < 0 || newECNCE < 0 { + e.logger.Debugf("Disabling ECN. ECN counts decreased unexpectedly.") + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedDecreasedECNCounts) + } + e.state = ecnStateFailed + return false + } + + // ECN validation also fails if the sum of the increase in ECT(0) and ECN-CE counts is less than the number + // of newly acknowledged packets that were originally sent with an ECT(0) marking. + // This could be the result of (partial) bleaching. + if newECT0+newECNCE < ackedECT0 { + e.logger.Debugf("Disabling ECN. Received less ECT(0) + ECN-CE than packets sent with ECT(0).") + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedTooFewECNCounts) + } + e.state = ecnStateFailed + return false + } + // Similarly, ECN validation fails if the sum of the increases to ECT(1) and ECN-CE counts is less than + // the number of newly acknowledged packets sent with an ECT(1) marking. + if newECT1+newECNCE < ackedECT1 { + e.logger.Debugf("Disabling ECN. Received less ECT(1) + ECN-CE than packets sent with ECT(1).") + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedTooFewECNCounts) + } + e.state = ecnStateFailed + return false + } + + // update our counters + e.numAckedECT0 = ect0 + e.numAckedECT1 = ect1 + e.numAckedECNCE = ecnce + + // Detect mangling (a path remarking all ECN-marked testing packets as CE), + // once all 10 testing packets have been sent out. + if e.state == ecnStateUnknown { + e.failIfMangled() + if e.state == ecnStateFailed { + return false + } + } + if e.state == ecnStateTesting || e.state == ecnStateUnknown { + var ackedTestingPacket bool + for _, p := range packets { + if e.isTestingPacket(p.PacketNumber) { + ackedTestingPacket = true + break + } + } + // This check won't succeed if the path is mangling ECN-marks (i.e. rewrites all ECN-marked packets to CE). + if ackedTestingPacket && (newECT0 > 0 || newECT1 > 0) { + e.logger.Debugf("ECN capability confirmed.") + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger) + } + e.state = ecnStateCapable + } + } + + // Don't trust CE marks before having confirmed ECN capability of the path. + // Otherwise, mangling would be misinterpreted as actual congestion. + return e.state == ecnStateCapable && newECNCE > 0 +} + +// failIfMangled fails ECN validation if all testing packets are lost or CE-marked. +func (e *ecnTracker) failIfMangled() { + numAckedECNCE := e.numAckedECNCE + int64(e.numLostTesting) + if e.numSentECT0+e.numSentECT1 > numAckedECNCE { + return + } + if e.tracer != nil && e.tracer.ECNStateUpdated != nil { + e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedManglingDetected) + } + e.state = ecnStateFailed +} + +func (e *ecnTracker) ecnMarking(pn protocol.PacketNumber) protocol.ECN { + if pn < e.firstTestingPacket || e.firstTestingPacket == protocol.InvalidPacketNumber { + return protocol.ECNNon + } + if pn < e.lastTestingPacket || e.lastTestingPacket == protocol.InvalidPacketNumber { + return protocol.ECT0 + } + if pn < e.firstCapablePacket || e.firstCapablePacket == protocol.InvalidPacketNumber { + return protocol.ECNNon + } + // We don't need to deal with the case when ECN validation fails, + // since we're ignoring any ECN counts reported in ACK frames in that case. + return protocol.ECT0 +} + +func (e *ecnTracker) isTestingPacket(pn protocol.PacketNumber) bool { + if e.firstTestingPacket == protocol.InvalidPacketNumber { + return false + } + return pn >= e.firstTestingPacket && (pn <= e.lastTestingPacket || e.lastTestingPacket == protocol.InvalidPacketNumber) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go index ba95f8a9b..ba8cbbdae 100644 --- a/vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/interfaces.go @@ -10,13 +10,13 @@ import ( // SentPacketHandler handles ACKs received for outgoing packets type SentPacketHandler interface { // SentPacket may modify the packet - SentPacket(t time.Time, pn, largestAcked protocol.PacketNumber, streamFrames []StreamFrame, frames []Frame, encLevel protocol.EncryptionLevel, size protocol.ByteCount, isPathMTUProbePacket bool) + SentPacket(t time.Time, pn, largestAcked protocol.PacketNumber, streamFrames []StreamFrame, frames []Frame, encLevel protocol.EncryptionLevel, ecn protocol.ECN, size protocol.ByteCount, isPathMTUProbePacket bool) // ReceivedAck processes an ACK frame. // It does not store a copy of the frame. - ReceivedAck(f *wire.AckFrame, encLevel protocol.EncryptionLevel, recvTime time.Time) (bool /* 1-RTT packet acked */, error) + ReceivedAck(f *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) (bool /* 1-RTT packet acked */, error) ReceivedBytes(protocol.ByteCount) DropPackets(protocol.EncryptionLevel) - ResetForRetry() error + ResetForRetry(rcvTime time.Time) error SetHandshakeConfirmed() // The SendMode determines if and what kind of packets can be sent. @@ -29,6 +29,7 @@ type SentPacketHandler interface { // only to be called once the handshake is complete QueueProbePacket(protocol.EncryptionLevel) bool /* was a packet queued */ + ECNMode(isShortHeaderPacket bool) protocol.ECN // isShortHeaderPacket should only be true for non-coalesced 1-RTT packets PeekPacketNumber(protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) PopPacketNumber(protocol.EncryptionLevel) protocol.PacketNumber @@ -44,7 +45,7 @@ type sentPacketTracker interface { // ReceivedPacketHandler handles ACKs needed to send for incoming packets type ReceivedPacketHandler interface { IsPotentiallyDuplicate(protocol.PacketNumber, protocol.EncryptionLevel) bool - ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, encLevel protocol.EncryptionLevel, rcvTime time.Time, shouldInstigateAck bool) error + ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, encLevel protocol.EncryptionLevel, rcvTime time.Time, ackEliciting bool) error DropPackets(protocol.EncryptionLevel) GetAlarmTimeout() time.Time diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go index d61783671..dbf6ee2d1 100644 --- a/vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/mockgen.go @@ -2,5 +2,8 @@ package ackhandler -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package ackhandler -destination mock_sent_packet_tracker_test.go github.com/quic-go/quic-go/internal/ackhandler SentPacketTracker" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package ackhandler -destination mock_sent_packet_tracker_test.go github.com/quic-go/quic-go/internal/ackhandler SentPacketTracker" type SentPacketTracker = sentPacketTracker + +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package ackhandler -destination mock_ecn_handler_test.go github.com/quic-go/quic-go/internal/ackhandler ECNHandler" +type ECNHandler = ecnHandler diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go index 3675694f4..b37f91c5c 100644 --- a/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_handler.go @@ -40,24 +40,29 @@ func (h *receivedPacketHandler) ReceivedPacket( ecn protocol.ECN, encLevel protocol.EncryptionLevel, rcvTime time.Time, - shouldInstigateAck bool, + ackEliciting bool, ) error { h.sentPackets.ReceivedPacket(encLevel) switch encLevel { case protocol.EncryptionInitial: - return h.initialPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck) + return h.initialPackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting) case protocol.EncryptionHandshake: - return h.handshakePackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck) + // The Handshake packet number space might already have been dropped as a result + // of processing the CRYPTO frame that was contained in this packet. + if h.handshakePackets == nil { + return nil + } + return h.handshakePackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting) case protocol.Encryption0RTT: if h.lowest1RTTPacket != protocol.InvalidPacketNumber && pn > h.lowest1RTTPacket { return fmt.Errorf("received packet number %d on a 0-RTT packet after receiving %d on a 1-RTT packet", pn, h.lowest1RTTPacket) } - return h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck) + return h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting) case protocol.Encryption1RTT: if h.lowest1RTTPacket == protocol.InvalidPacketNumber || pn < h.lowest1RTTPacket { h.lowest1RTTPacket = pn } - if err := h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck); err != nil { + if err := h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting); err != nil { return err } h.appDataPackets.IgnoreBelow(h.sentPackets.GetLowestPacketNotConfirmedAcked()) diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go index b18838663..7fd071e68 100644 --- a/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/received_packet_tracker.go @@ -13,10 +13,10 @@ import ( const packetsBeforeAck = 2 type receivedPacketTracker struct { - largestObserved protocol.PacketNumber - ignoreBelow protocol.PacketNumber - largestObservedReceivedTime time.Time - ect0, ect1, ecnce uint64 + largestObserved protocol.PacketNumber + ignoreBelow protocol.PacketNumber + largestObservedRcvdTime time.Time + ect0, ect1, ecnce uint64 packetHistory *receivedPacketHistory @@ -45,25 +45,25 @@ func newReceivedPacketTracker( } } -func (h *receivedPacketTracker) ReceivedPacket(packetNumber protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, shouldInstigateAck bool) error { - if isNew := h.packetHistory.ReceivedPacket(packetNumber); !isNew { - return fmt.Errorf("recevedPacketTracker BUG: ReceivedPacket called for old / duplicate packet %d", packetNumber) +func (h *receivedPacketTracker) ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, ackEliciting bool) error { + if isNew := h.packetHistory.ReceivedPacket(pn); !isNew { + return fmt.Errorf("recevedPacketTracker BUG: ReceivedPacket called for old / duplicate packet %d", pn) } - isMissing := h.isMissing(packetNumber) - if packetNumber >= h.largestObserved { - h.largestObserved = packetNumber - h.largestObservedReceivedTime = rcvTime + isMissing := h.isMissing(pn) + if pn >= h.largestObserved { + h.largestObserved = pn + h.largestObservedRcvdTime = rcvTime } - if shouldInstigateAck { + if ackEliciting { h.hasNewAck = true } - if shouldInstigateAck { - h.maybeQueueAck(packetNumber, rcvTime, isMissing) + if ackEliciting { + h.maybeQueueACK(pn, rcvTime, isMissing) } + //nolint:exhaustive // Only need to count ECT(0), ECT(1) and ECNCE. switch ecn { - case protocol.ECNNon: case protocol.ECT0: h.ect0++ case protocol.ECT1: @@ -76,14 +76,14 @@ func (h *receivedPacketTracker) ReceivedPacket(packetNumber protocol.PacketNumbe // IgnoreBelow sets a lower limit for acknowledging packets. // Packets with packet numbers smaller than p will not be acked. -func (h *receivedPacketTracker) IgnoreBelow(p protocol.PacketNumber) { - if p <= h.ignoreBelow { +func (h *receivedPacketTracker) IgnoreBelow(pn protocol.PacketNumber) { + if pn <= h.ignoreBelow { return } - h.ignoreBelow = p - h.packetHistory.DeleteBelow(p) + h.ignoreBelow = pn + h.packetHistory.DeleteBelow(pn) if h.logger.Debug() { - h.logger.Debugf("\tIgnoring all packets below %d.", p) + h.logger.Debugf("\tIgnoring all packets below %d.", pn) } } @@ -103,8 +103,8 @@ func (h *receivedPacketTracker) hasNewMissingPackets() bool { return highestRange.Smallest > h.lastAck.LargestAcked()+1 && highestRange.Len() == 1 } -// maybeQueueAck queues an ACK, if necessary. -func (h *receivedPacketTracker) maybeQueueAck(pn protocol.PacketNumber, rcvTime time.Time, wasMissing bool) { +// maybeQueueACK queues an ACK, if necessary. +func (h *receivedPacketTracker) maybeQueueACK(pn protocol.PacketNumber, rcvTime time.Time, wasMissing bool) { // always acknowledge the first packet if h.lastAck == nil { if !h.ackQueued { @@ -175,7 +175,7 @@ func (h *receivedPacketTracker) GetAckFrame(onlyIfQueued bool) *wire.AckFrame { ack = &wire.AckFrame{} } ack.Reset() - ack.DelayTime = utils.Max(0, now.Sub(h.largestObservedReceivedTime)) + ack.DelayTime = utils.Max(0, now.Sub(h.largestObservedRcvdTime)) ack.ECT0 = h.ect0 ack.ECT1 = h.ect1 ack.ECNCE = h.ecnce diff --git a/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go b/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go index 079789101..c8265a78d 100644 --- a/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go +++ b/vendor/github.com/quic-go/quic-go/internal/ackhandler/sent_packet_handler.go @@ -92,9 +92,12 @@ type sentPacketHandler struct { // The alarm timeout alarm time.Time + enableECN bool + ecnTracker ecnHandler + perspective protocol.Perspective - tracer logging.ConnectionTracer + tracer *logging.ConnectionTracer logger utils.Logger } @@ -110,8 +113,9 @@ func newSentPacketHandler( initialMaxDatagramSize protocol.ByteCount, rttStats *utils.RTTStats, clientAddressValidated bool, + enableECN bool, pers protocol.Perspective, - tracer logging.ConnectionTracer, + tracer *logging.ConnectionTracer, logger utils.Logger, ) *sentPacketHandler { congestion := congestion.NewCubicSender( @@ -122,7 +126,7 @@ func newSentPacketHandler( tracer, ) - return &sentPacketHandler{ + h := &sentPacketHandler{ peerCompletedAddressValidation: pers == protocol.PerspectiveServer, peerAddressValidated: pers == protocol.PerspectiveClient || clientAddressValidated, initialPackets: newPacketNumberSpace(initialPN, false), @@ -134,16 +138,11 @@ func newSentPacketHandler( tracer: tracer, logger: logger, } -} - -func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) { - if h.perspective == protocol.PerspectiveClient && encLevel == protocol.EncryptionInitial { - // This function is called when the crypto setup seals a Handshake packet. - // If this Handshake packet is coalesced behind an Initial packet, we would drop the Initial packet number space - // before SentPacket() was called for that Initial packet. - return + if enableECN { + h.enableECN = true + h.ecnTracker = newECNTracker(logger, tracer) } - h.dropPackets(encLevel) + return h } func (h *sentPacketHandler) removeFromBytesInFlight(p *packet) { @@ -156,7 +155,7 @@ func (h *sentPacketHandler) removeFromBytesInFlight(p *packet) { } } -func (h *sentPacketHandler) dropPackets(encLevel protocol.EncryptionLevel) { +func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) { // The server won't await address validation after the handshake is confirmed. // This applies even if we didn't receive an ACK for a Handshake packet. if h.perspective == protocol.PerspectiveClient && encLevel == protocol.EncryptionHandshake { @@ -165,6 +164,10 @@ func (h *sentPacketHandler) dropPackets(encLevel protocol.EncryptionLevel) { // remove outstanding packets from bytes_in_flight if encLevel == protocol.EncryptionInitial || encLevel == protocol.EncryptionHandshake { pnSpace := h.getPacketNumberSpace(encLevel) + // We might already have dropped this packet number space. + if pnSpace == nil { + return + } pnSpace.history.Iterate(func(p *packet) (bool, error) { h.removeFromBytesInFlight(p) return true, nil @@ -193,7 +196,7 @@ func (h *sentPacketHandler) dropPackets(encLevel protocol.EncryptionLevel) { default: panic(fmt.Sprintf("Cannot drop keys for encryption level %s", encLevel)) } - if h.tracer != nil && h.ptoCount != 0 { + if h.tracer != nil && h.tracer.UpdatedPTOCount != nil && h.ptoCount != 0 { h.tracer.UpdatedPTOCount(0) } h.ptoCount = 0 @@ -234,14 +237,11 @@ func (h *sentPacketHandler) SentPacket( streamFrames []StreamFrame, frames []Frame, encLevel protocol.EncryptionLevel, + ecn protocol.ECN, size protocol.ByteCount, isPathMTUProbePacket bool, ) { h.bytesSent += size - // For the client, drop the Initial packet number space when the first Handshake packet is sent. - if h.perspective == protocol.PerspectiveClient && encLevel == protocol.EncryptionHandshake && h.initialPackets != nil { - h.dropPackets(protocol.EncryptionInitial) - } pnSpace := h.getPacketNumberSpace(encLevel) if h.logger.Debug() && pnSpace.history.HasOutstandingPackets() { @@ -262,6 +262,10 @@ func (h *sentPacketHandler) SentPacket( } h.congestion.OnPacketSent(t, h.bytesInFlight, pn, size, isAckEliciting) + if encLevel == protocol.Encryption1RTT && h.ecnTracker != nil { + h.ecnTracker.SentPacket(pn, ecn) + } + if !isAckEliciting { pnSpace.history.SentNonAckElicitingPacket(pn) if !h.peerCompletedAddressValidation { @@ -282,7 +286,7 @@ func (h *sentPacketHandler) SentPacket( p.includedInBytesInFlight = true pnSpace.history.SentAckElicitingPacket(p) - if h.tracer != nil { + if h.tracer != nil && h.tracer.UpdatedMetrics != nil { h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) } h.setLossDetectionTimer() @@ -312,8 +316,6 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En } } - pnSpace.largestAcked = utils.Max(pnSpace.largestAcked, largestAcked) - // Servers complete address validation when a protected packet is received. if h.perspective == protocol.PerspectiveClient && !h.peerCompletedAddressValidation && (encLevel == protocol.EncryptionHandshake || encLevel == protocol.Encryption1RTT) { @@ -343,6 +345,17 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En h.congestion.MaybeExitSlowStart() } } + + // Only inform the ECN tracker about new 1-RTT ACKs if the ACK increases the largest acked. + if encLevel == protocol.Encryption1RTT && h.ecnTracker != nil && largestAcked > pnSpace.largestAcked { + congested := h.ecnTracker.HandleNewlyAcked(ackedPackets, int64(ack.ECT0), int64(ack.ECT1), int64(ack.ECNCE)) + if congested { + h.congestion.OnCongestionEvent(largestAcked, 0, priorInFlight) + } + } + + pnSpace.largestAcked = utils.Max(pnSpace.largestAcked, largestAcked) + if err := h.detectLostPackets(rcvTime, encLevel); err != nil { return false, err } @@ -363,14 +376,14 @@ func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.En // Reset the pto_count unless the client is unsure if the server has validated the client's address. if h.peerCompletedAddressValidation { - if h.tracer != nil && h.ptoCount != 0 { + if h.tracer != nil && h.tracer.UpdatedPTOCount != nil && h.ptoCount != 0 { h.tracer.UpdatedPTOCount(0) } h.ptoCount = 0 } h.numProbesToSend = 0 - if h.tracer != nil { + if h.tracer != nil && h.tracer.UpdatedMetrics != nil { h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) } @@ -449,7 +462,7 @@ func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encL if err := pnSpace.history.Remove(p.PacketNumber); err != nil { return nil, err } - if h.tracer != nil { + if h.tracer != nil && h.tracer.AcknowledgedPacket != nil { h.tracer.AcknowledgedPacket(encLevel, p.PacketNumber) } } @@ -542,7 +555,7 @@ func (h *sentPacketHandler) setLossDetectionTimer() { if !lossTime.IsZero() { // Early retransmit timer or time loss detection. h.alarm = lossTime - if h.tracer != nil && h.alarm != oldAlarm { + if h.tracer != nil && h.tracer.SetLossTimer != nil && h.alarm != oldAlarm { h.tracer.SetLossTimer(logging.TimerTypeACK, encLevel, h.alarm) } return @@ -553,7 +566,7 @@ func (h *sentPacketHandler) setLossDetectionTimer() { h.alarm = time.Time{} if !oldAlarm.IsZero() { h.logger.Debugf("Canceling loss detection timer. Amplification limited.") - if h.tracer != nil { + if h.tracer != nil && h.tracer.LossTimerCanceled != nil { h.tracer.LossTimerCanceled() } } @@ -565,7 +578,7 @@ func (h *sentPacketHandler) setLossDetectionTimer() { h.alarm = time.Time{} if !oldAlarm.IsZero() { h.logger.Debugf("Canceling loss detection timer. No packets in flight.") - if h.tracer != nil { + if h.tracer != nil && h.tracer.LossTimerCanceled != nil { h.tracer.LossTimerCanceled() } } @@ -578,14 +591,14 @@ func (h *sentPacketHandler) setLossDetectionTimer() { if !oldAlarm.IsZero() { h.alarm = time.Time{} h.logger.Debugf("Canceling loss detection timer. No PTO needed..") - if h.tracer != nil { + if h.tracer != nil && h.tracer.LossTimerCanceled != nil { h.tracer.LossTimerCanceled() } } return } h.alarm = ptoTime - if h.tracer != nil && h.alarm != oldAlarm { + if h.tracer != nil && h.tracer.SetLossTimer != nil && h.alarm != oldAlarm { h.tracer.SetLossTimer(logging.TimerTypePTO, encLevel, h.alarm) } } @@ -616,7 +629,7 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E if h.logger.Debug() { h.logger.Debugf("\tlost packet %d (time threshold)", p.PacketNumber) } - if h.tracer != nil { + if h.tracer != nil && h.tracer.LostPacket != nil { h.tracer.LostPacket(p.EncryptionLevel, p.PacketNumber, logging.PacketLossTimeThreshold) } } @@ -626,7 +639,7 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E if h.logger.Debug() { h.logger.Debugf("\tlost packet %d (reordering threshold)", p.PacketNumber) } - if h.tracer != nil { + if h.tracer != nil && h.tracer.LostPacket != nil { h.tracer.LostPacket(p.EncryptionLevel, p.PacketNumber, logging.PacketLossReorderingThreshold) } } @@ -645,7 +658,10 @@ func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.E h.removeFromBytesInFlight(p) h.queueFramesForRetransmission(p) if !p.IsPathMTUProbePacket { - h.congestion.OnPacketLost(p.PacketNumber, p.Length, priorInFlight) + h.congestion.OnCongestionEvent(p.PacketNumber, p.Length, priorInFlight) + } + if encLevel == protocol.Encryption1RTT && h.ecnTracker != nil { + h.ecnTracker.LostPacket(p.PacketNumber) } } } @@ -660,7 +676,7 @@ func (h *sentPacketHandler) OnLossDetectionTimeout() error { if h.logger.Debug() { h.logger.Debugf("Loss detection alarm fired in loss timer mode. Loss time: %s", earliestLossTime) } - if h.tracer != nil { + if h.tracer != nil && h.tracer.LossTimerExpired != nil { h.tracer.LossTimerExpired(logging.TimerTypeACK, encLevel) } // Early retransmit or time loss detection @@ -697,8 +713,12 @@ func (h *sentPacketHandler) OnLossDetectionTimeout() error { h.logger.Debugf("Loss detection alarm for %s fired in PTO mode. PTO count: %d", encLevel, h.ptoCount) } if h.tracer != nil { - h.tracer.LossTimerExpired(logging.TimerTypePTO, encLevel) - h.tracer.UpdatedPTOCount(h.ptoCount) + if h.tracer.LossTimerExpired != nil { + h.tracer.LossTimerExpired(logging.TimerTypePTO, encLevel) + } + if h.tracer.UpdatedPTOCount != nil { + h.tracer.UpdatedPTOCount(h.ptoCount) + } } h.numProbesToSend += 2 //nolint:exhaustive // We never arm a PTO timer for 0-RTT packets. @@ -722,6 +742,16 @@ func (h *sentPacketHandler) GetLossDetectionTimeout() time.Time { return h.alarm } +func (h *sentPacketHandler) ECNMode(isShortHeaderPacket bool) protocol.ECN { + if !h.enableECN { + return protocol.ECNUnsupported + } + if !isShortHeaderPacket { + return protocol.ECNNon + } + return h.ecnTracker.Mode() +} + func (h *sentPacketHandler) PeekPacketNumber(encLevel protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) { pnSpace := h.getPacketNumberSpace(encLevel) pn := pnSpace.pns.Peek() @@ -834,7 +864,7 @@ func (h *sentPacketHandler) queueFramesForRetransmission(p *packet) { p.Frames = nil } -func (h *sentPacketHandler) ResetForRetry() error { +func (h *sentPacketHandler) ResetForRetry(now time.Time) error { h.bytesInFlight = 0 var firstPacketSendTime time.Time h.initialPackets.history.Iterate(func(p *packet) (bool, error) { @@ -860,12 +890,11 @@ func (h *sentPacketHandler) ResetForRetry() error { // Otherwise, we don't know which Initial the Retry was sent in response to. if h.ptoCount == 0 { // Don't set the RTT to a value lower than 5ms here. - now := time.Now() h.rttStats.UpdateRTT(utils.Max(minRTTAfterRetry, now.Sub(firstPacketSendTime)), 0, now) if h.logger.Debug() { h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation()) } - if h.tracer != nil { + if h.tracer != nil && h.tracer.UpdatedMetrics != nil { h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) } } @@ -874,8 +903,10 @@ func (h *sentPacketHandler) ResetForRetry() error { oldAlarm := h.alarm h.alarm = time.Time{} if h.tracer != nil { - h.tracer.UpdatedPTOCount(0) - if !oldAlarm.IsZero() { + if h.tracer.UpdatedPTOCount != nil { + h.tracer.UpdatedPTOCount(0) + } + if !oldAlarm.IsZero() && h.tracer.LossTimerCanceled != nil { h.tracer.LossTimerCanceled() } } @@ -884,6 +915,12 @@ func (h *sentPacketHandler) ResetForRetry() error { } func (h *sentPacketHandler) SetHandshakeConfirmed() { + if h.initialPackets != nil { + panic("didn't drop initial correctly") + } + if h.handshakePackets != nil { + panic("didn't drop handshake correctly") + } h.handshakeConfirmed = true // We don't send PTOs for application data packets before the handshake completes. // Make sure the timer is armed now, if necessary. diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go b/vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go index 2e5084751..ee558f2d5 100644 --- a/vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go +++ b/vendor/github.com/quic-go/quic-go/internal/congestion/cubic_sender.go @@ -56,7 +56,7 @@ type cubicSender struct { maxDatagramSize protocol.ByteCount lastState logging.CongestionState - tracer logging.ConnectionTracer + tracer *logging.ConnectionTracer } var ( @@ -70,7 +70,7 @@ func NewCubicSender( rttStats *utils.RTTStats, initialMaxDatagramSize protocol.ByteCount, reno bool, - tracer logging.ConnectionTracer, + tracer *logging.ConnectionTracer, ) *cubicSender { return newCubicSender( clock, @@ -90,7 +90,7 @@ func newCubicSender( initialMaxDatagramSize, initialCongestionWindow, initialMaxCongestionWindow protocol.ByteCount, - tracer logging.ConnectionTracer, + tracer *logging.ConnectionTracer, ) *cubicSender { c := &cubicSender{ rttStats: rttStats, @@ -108,7 +108,7 @@ func newCubicSender( maxDatagramSize: initialMaxDatagramSize, } c.pacer = newPacer(c.BandwidthEstimate) - if c.tracer != nil { + if c.tracer != nil && c.tracer.UpdatedCongestionState != nil { c.lastState = logging.CongestionStateSlowStart c.tracer.UpdatedCongestionState(logging.CongestionStateSlowStart) } @@ -188,7 +188,7 @@ func (c *cubicSender) OnPacketAcked( } } -func (c *cubicSender) OnPacketLost(packetNumber protocol.PacketNumber, lostBytes, priorInFlight protocol.ByteCount) { +func (c *cubicSender) OnCongestionEvent(packetNumber protocol.PacketNumber, lostBytes, priorInFlight protocol.ByteCount) { // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets // already sent should be treated as a single loss event, since it's expected. if packetNumber <= c.largestSentAtLastCutback { @@ -296,7 +296,7 @@ func (c *cubicSender) OnConnectionMigration() { } func (c *cubicSender) maybeTraceStateChange(new logging.CongestionState) { - if c.tracer == nil || new == c.lastState { + if c.tracer == nil || c.tracer.UpdatedCongestionState == nil || new == c.lastState { return } c.tracer.UpdatedCongestionState(new) diff --git a/vendor/github.com/quic-go/quic-go/internal/congestion/interface.go b/vendor/github.com/quic-go/quic-go/internal/congestion/interface.go index 484bd5f81..881f453b6 100644 --- a/vendor/github.com/quic-go/quic-go/internal/congestion/interface.go +++ b/vendor/github.com/quic-go/quic-go/internal/congestion/interface.go @@ -14,7 +14,7 @@ type SendAlgorithm interface { CanSend(bytesInFlight protocol.ByteCount) bool MaybeExitSlowStart() OnPacketAcked(number protocol.PacketNumber, ackedBytes protocol.ByteCount, priorInFlight protocol.ByteCount, eventTime time.Time) - OnPacketLost(number protocol.PacketNumber, lostBytes protocol.ByteCount, priorInFlight protocol.ByteCount) + OnCongestionEvent(number protocol.PacketNumber, lostBytes protocol.ByteCount, priorInFlight protocol.ByteCount) OnRetransmissionTimeout(packetsRetransmitted bool) SetMaxDatagramSize(protocol.ByteCount) } diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/aead.go b/vendor/github.com/quic-go/quic-go/internal/handshake/aead.go index 410745f1a..6aa89fb3f 100644 --- a/vendor/github.com/quic-go/quic-go/internal/handshake/aead.go +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/aead.go @@ -5,11 +5,10 @@ import ( "encoding/binary" "github.com/quic-go/quic-go/internal/protocol" - "github.com/quic-go/quic-go/internal/qtls" "github.com/quic-go/quic-go/internal/utils" ) -func createAEAD(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, v protocol.VersionNumber) cipher.AEAD { +func createAEAD(suite *cipherSuite, trafficSecret []byte, v protocol.VersionNumber) cipher.AEAD { keyLabel := hkdfLabelKeyV1 ivLabel := hkdfLabelIVV1 if v == protocol.Version2 { @@ -93,69 +92,3 @@ func (o *longHeaderOpener) Open(dst, src []byte, pn protocol.PacketNumber, ad [] func (o *longHeaderOpener) DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte) { o.headerProtector.DecryptHeader(sample, firstByte, pnBytes) } - -type handshakeSealer struct { - LongHeaderSealer - - dropInitialKeys func() - dropped bool -} - -func newHandshakeSealer( - aead cipher.AEAD, - headerProtector headerProtector, - dropInitialKeys func(), - perspective protocol.Perspective, -) LongHeaderSealer { - sealer := newLongHeaderSealer(aead, headerProtector) - // The client drops Initial keys when sending the first Handshake packet. - if perspective == protocol.PerspectiveServer { - return sealer - } - return &handshakeSealer{ - LongHeaderSealer: sealer, - dropInitialKeys: dropInitialKeys, - } -} - -func (s *handshakeSealer) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byte) []byte { - data := s.LongHeaderSealer.Seal(dst, src, pn, ad) - if !s.dropped { - s.dropInitialKeys() - s.dropped = true - } - return data -} - -type handshakeOpener struct { - LongHeaderOpener - - dropInitialKeys func() - dropped bool -} - -func newHandshakeOpener( - aead cipher.AEAD, - headerProtector headerProtector, - dropInitialKeys func(), - perspective protocol.Perspective, -) LongHeaderOpener { - opener := newLongHeaderOpener(aead, headerProtector) - // The server drops Initial keys when first successfully processing a Handshake packet. - if perspective == protocol.PerspectiveClient { - return opener - } - return &handshakeOpener{ - LongHeaderOpener: opener, - dropInitialKeys: dropInitialKeys, - } -} - -func (o *handshakeOpener) Open(dst, src []byte, pn protocol.PacketNumber, ad []byte) ([]byte, error) { - dec, err := o.LongHeaderOpener.Open(dst, src, pn, ad) - if err == nil && !o.dropped { - o.dropInitialKeys() - o.dropped = true - } - return dec, err -} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/cipher_suite.go b/vendor/github.com/quic-go/quic-go/internal/handshake/cipher_suite.go new file mode 100644 index 000000000..265231f0c --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/cipher_suite.go @@ -0,0 +1,104 @@ +package handshake + +import ( + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/tls" + "fmt" + + "golang.org/x/crypto/chacha20poly1305" +) + +// These cipher suite implementations are copied from the standard library crypto/tls package. + +const aeadNonceLength = 12 + +type cipherSuite struct { + ID uint16 + Hash crypto.Hash + KeyLen int + AEAD func(key, nonceMask []byte) cipher.AEAD +} + +func (s cipherSuite) IVLen() int { return aeadNonceLength } + +func getCipherSuite(id uint16) *cipherSuite { + switch id { + case tls.TLS_AES_128_GCM_SHA256: + return &cipherSuite{ID: tls.TLS_AES_128_GCM_SHA256, Hash: crypto.SHA256, KeyLen: 16, AEAD: aeadAESGCMTLS13} + case tls.TLS_CHACHA20_POLY1305_SHA256: + return &cipherSuite{ID: tls.TLS_CHACHA20_POLY1305_SHA256, Hash: crypto.SHA256, KeyLen: 32, AEAD: aeadChaCha20Poly1305} + case tls.TLS_AES_256_GCM_SHA384: + return &cipherSuite{ID: tls.TLS_AES_256_GCM_SHA384, Hash: crypto.SHA384, KeyLen: 32, AEAD: aeadAESGCMTLS13} + default: + panic(fmt.Sprintf("unknown cypher suite: %d", id)) + } +} + +func aeadAESGCMTLS13(key, nonceMask []byte) cipher.AEAD { + if len(nonceMask) != aeadNonceLength { + panic("tls: internal error: wrong nonce length") + } + aes, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + aead, err := cipher.NewGCM(aes) + if err != nil { + panic(err) + } + + ret := &xorNonceAEAD{aead: aead} + copy(ret.nonceMask[:], nonceMask) + return ret +} + +func aeadChaCha20Poly1305(key, nonceMask []byte) cipher.AEAD { + if len(nonceMask) != aeadNonceLength { + panic("tls: internal error: wrong nonce length") + } + aead, err := chacha20poly1305.New(key) + if err != nil { + panic(err) + } + + ret := &xorNonceAEAD{aead: aead} + copy(ret.nonceMask[:], nonceMask) + return ret +} + +// xorNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce +// before each call. +type xorNonceAEAD struct { + nonceMask [aeadNonceLength]byte + aead cipher.AEAD +} + +func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number +func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() } +func (f *xorNonceAEAD) explicitNonceLen() int { return 0 } + +func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte { + for i, b := range nonce { + f.nonceMask[4+i] ^= b + } + result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData) + for i, b := range nonce { + f.nonceMask[4+i] ^= b + } + + return result +} + +func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) { + for i, b := range nonce { + f.nonceMask[4+i] ^= b + } + result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData) + for i, b := range nonce { + f.nonceMask[4+i] ^= b + } + + return result, err +} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/conn.go b/vendor/github.com/quic-go/quic-go/internal/handshake/conn.go new file mode 100644 index 000000000..54af823ba --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/conn.go @@ -0,0 +1,21 @@ +package handshake + +import ( + "net" + "time" +) + +type conn struct { + localAddr, remoteAddr net.Addr +} + +var _ net.Conn = &conn{} + +func (c *conn) Read([]byte) (int, error) { return 0, nil } +func (c *conn) Write([]byte) (int, error) { return 0, nil } +func (c *conn) Close() error { return nil } +func (c *conn) RemoteAddr() net.Addr { return c.remoteAddr } +func (c *conn) LocalAddr() net.Addr { return c.localAddr } +func (c *conn) SetReadDeadline(time.Time) error { return nil } +func (c *conn) SetWriteDeadline(time.Time) error { return nil } +func (c *conn) SetDeadline(time.Time) error { return nil } diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go b/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go index 8c9c2a8f8..861494c47 100644 --- a/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/crypto_setup.go @@ -6,10 +6,10 @@ import ( "crypto/tls" "errors" "fmt" - "io" - "math" "net" + "strings" "sync" + "sync/atomic" "time" "github.com/quic-go/quic-go/internal/protocol" @@ -25,102 +25,25 @@ type quicVersionContextKey struct{} var QUICVersionContextKey = &quicVersionContextKey{} -// TLS unexpected_message alert -const alertUnexpectedMessage uint8 = 10 - -type messageType uint8 - -// TLS handshake message types. -const ( - typeClientHello messageType = 1 - typeServerHello messageType = 2 - typeNewSessionTicket messageType = 4 - typeEncryptedExtensions messageType = 8 - typeCertificate messageType = 11 - typeCertificateRequest messageType = 13 - typeCertificateVerify messageType = 15 - typeFinished messageType = 20 -) - -func (m messageType) String() string { - switch m { - case typeClientHello: - return "ClientHello" - case typeServerHello: - return "ServerHello" - case typeNewSessionTicket: - return "NewSessionTicket" - case typeEncryptedExtensions: - return "EncryptedExtensions" - case typeCertificate: - return "Certificate" - case typeCertificateRequest: - return "CertificateRequest" - case typeCertificateVerify: - return "CertificateVerify" - case typeFinished: - return "Finished" - default: - return fmt.Sprintf("unknown message type: %d", m) - } -} - const clientSessionStateRevision = 3 -type conn struct { - localAddr, remoteAddr net.Addr -} - -var _ net.Conn = &conn{} - -func newConn(local, remote net.Addr) net.Conn { - return &conn{ - localAddr: local, - remoteAddr: remote, - } -} - -func (c *conn) Read([]byte) (int, error) { return 0, nil } -func (c *conn) Write([]byte) (int, error) { return 0, nil } -func (c *conn) Close() error { return nil } -func (c *conn) RemoteAddr() net.Addr { return c.remoteAddr } -func (c *conn) LocalAddr() net.Addr { return c.localAddr } -func (c *conn) SetReadDeadline(time.Time) error { return nil } -func (c *conn) SetWriteDeadline(time.Time) error { return nil } -func (c *conn) SetDeadline(time.Time) error { return nil } - type cryptoSetup struct { - tlsConf *tls.Config - extraConf *qtls.ExtraConfig - conn *qtls.Conn + tlsConf *tls.Config + conn *qtls.QUICConn + + events []Event version protocol.VersionNumber - messageChan chan []byte - isReadingHandshakeMessage chan struct{} - readFirstHandshakeMessage bool - ourParams *wire.TransportParameters peerParams *wire.TransportParameters - paramsChan <-chan []byte - runner handshakeRunner - - alertChan chan uint8 - // handshakeDone is closed as soon as the go routine running qtls.Handshake() returns - handshakeDone chan struct{} - // is closed when Close() is called - closeChan chan struct{} - - zeroRTTParameters *wire.TransportParameters - clientHelloWritten bool - clientHelloWrittenChan chan struct{} // is closed as soon as the ClientHello is written - zeroRTTParametersChan chan<- *wire.TransportParameters - allow0RTT bool + zeroRTTParameters *wire.TransportParameters + allow0RTT bool rttStats *utils.RTTStats - tracer logging.ConnectionTracer + tracer *logging.ConnectionTracer logger utils.Logger perspective protocol.Perspective @@ -129,169 +52,151 @@ type cryptoSetup struct { handshakeCompleteTime time.Time - readEncLevel protocol.EncryptionLevel - writeEncLevel protocol.EncryptionLevel - zeroRTTOpener LongHeaderOpener // only set for the server zeroRTTSealer LongHeaderSealer // only set for the client - initialStream io.Writer initialOpener LongHeaderOpener initialSealer LongHeaderSealer - handshakeStream io.Writer handshakeOpener LongHeaderOpener handshakeSealer LongHeaderSealer + used0RTT atomic.Bool + aead *updatableAEAD has1RTTSealer bool has1RTTOpener bool } -var ( - _ qtls.RecordLayer = &cryptoSetup{} - _ CryptoSetup = &cryptoSetup{} -) +var _ CryptoSetup = &cryptoSetup{} // NewCryptoSetupClient creates a new crypto setup for the client func NewCryptoSetupClient( - initialStream io.Writer, - handshakeStream io.Writer, connID protocol.ConnectionID, - localAddr net.Addr, - remoteAddr net.Addr, tp *wire.TransportParameters, - runner handshakeRunner, tlsConf *tls.Config, enable0RTT bool, rttStats *utils.RTTStats, - tracer logging.ConnectionTracer, + tracer *logging.ConnectionTracer, logger utils.Logger, version protocol.VersionNumber, -) (CryptoSetup, <-chan *wire.TransportParameters /* ClientHello written. Receive nil for non-0-RTT */) { - cs, clientHelloWritten := newCryptoSetup( - initialStream, - handshakeStream, +) CryptoSetup { + cs := newCryptoSetup( connID, tp, - runner, - tlsConf, - enable0RTT, rttStats, tracer, logger, protocol.PerspectiveClient, version, ) - cs.conn = qtls.Client(newConn(localAddr, remoteAddr), cs.tlsConf, cs.extraConf) - return cs, clientHelloWritten + + tlsConf = tlsConf.Clone() + tlsConf.MinVersion = tls.VersionTLS13 + quicConf := &qtls.QUICConfig{TLSConfig: tlsConf} + qtls.SetupConfigForClient(quicConf, cs.marshalDataForSessionState, cs.handleDataFromSessionState) + cs.tlsConf = tlsConf + + cs.conn = qtls.QUICClient(quicConf) + cs.conn.SetTransportParameters(cs.ourParams.Marshal(protocol.PerspectiveClient)) + + return cs } // NewCryptoSetupServer creates a new crypto setup for the server func NewCryptoSetupServer( - initialStream io.Writer, - handshakeStream io.Writer, connID protocol.ConnectionID, - localAddr net.Addr, - remoteAddr net.Addr, + localAddr, remoteAddr net.Addr, tp *wire.TransportParameters, - runner handshakeRunner, tlsConf *tls.Config, allow0RTT bool, rttStats *utils.RTTStats, - tracer logging.ConnectionTracer, + tracer *logging.ConnectionTracer, logger utils.Logger, version protocol.VersionNumber, ) CryptoSetup { - cs, _ := newCryptoSetup( - initialStream, - handshakeStream, + cs := newCryptoSetup( connID, tp, - runner, - tlsConf, - allow0RTT, rttStats, tracer, logger, protocol.PerspectiveServer, version, ) - cs.conn = qtls.Server(newConn(localAddr, remoteAddr), cs.tlsConf, cs.extraConf) + cs.allow0RTT = allow0RTT + + quicConf := &qtls.QUICConfig{TLSConfig: tlsConf} + qtls.SetupConfigForServer(quicConf, cs.allow0RTT, cs.getDataForSessionTicket, cs.handleSessionTicket) + addConnToClientHelloInfo(quicConf.TLSConfig, localAddr, remoteAddr) + + cs.tlsConf = quicConf.TLSConfig + cs.conn = qtls.QUICServer(quicConf) + return cs } +// The tls.Config contains two callbacks that pass in a tls.ClientHelloInfo. +// Since crypto/tls doesn't do it, we need to make sure to set the Conn field with a fake net.Conn +// that allows the caller to get the local and the remote address. +func addConnToClientHelloInfo(conf *tls.Config, localAddr, remoteAddr net.Addr) { + if conf.GetConfigForClient != nil { + gcfc := conf.GetConfigForClient + conf.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) { + info.Conn = &conn{localAddr: localAddr, remoteAddr: remoteAddr} + c, err := gcfc(info) + if c != nil { + c = c.Clone() + // This won't be necessary anymore once https://github.com/golang/go/issues/63722 is accepted. + c.MinVersion = tls.VersionTLS13 + // We're returning a tls.Config here, so we need to apply this recursively. + addConnToClientHelloInfo(c, localAddr, remoteAddr) + } + return c, err + } + } + if conf.GetCertificate != nil { + gc := conf.GetCertificate + conf.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { + info.Conn = &conn{localAddr: localAddr, remoteAddr: remoteAddr} + return gc(info) + } + } +} + func newCryptoSetup( - initialStream io.Writer, - handshakeStream io.Writer, connID protocol.ConnectionID, tp *wire.TransportParameters, - runner handshakeRunner, - tlsConf *tls.Config, - enable0RTT bool, rttStats *utils.RTTStats, - tracer logging.ConnectionTracer, + tracer *logging.ConnectionTracer, logger utils.Logger, perspective protocol.Perspective, version protocol.VersionNumber, -) (*cryptoSetup, <-chan *wire.TransportParameters /* ClientHello written. Receive nil for non-0-RTT */) { +) *cryptoSetup { initialSealer, initialOpener := NewInitialAEAD(connID, perspective, version) - if tracer != nil { + if tracer != nil && tracer.UpdatedKeyFromTLS != nil { tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient) tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer) } - extHandler := newExtensionHandler(tp.Marshal(perspective), perspective, version) - zeroRTTParametersChan := make(chan *wire.TransportParameters, 1) - cs := &cryptoSetup{ - tlsConf: tlsConf, - initialStream: initialStream, - initialSealer: initialSealer, - initialOpener: initialOpener, - handshakeStream: handshakeStream, - aead: newUpdatableAEAD(rttStats, tracer, logger, version), - readEncLevel: protocol.EncryptionInitial, - writeEncLevel: protocol.EncryptionInitial, - runner: runner, - allow0RTT: enable0RTT, - ourParams: tp, - paramsChan: extHandler.TransportParameters(), - rttStats: rttStats, - tracer: tracer, - logger: logger, - perspective: perspective, - handshakeDone: make(chan struct{}), - alertChan: make(chan uint8), - clientHelloWrittenChan: make(chan struct{}), - zeroRTTParametersChan: zeroRTTParametersChan, - messageChan: make(chan []byte, 1), - isReadingHandshakeMessage: make(chan struct{}), - closeChan: make(chan struct{}), - version: version, + return &cryptoSetup{ + initialSealer: initialSealer, + initialOpener: initialOpener, + aead: newUpdatableAEAD(rttStats, tracer, logger, version), + events: make([]Event, 0, 16), + ourParams: tp, + rttStats: rttStats, + tracer: tracer, + logger: logger, + perspective: perspective, + version: version, } - var maxEarlyData uint32 - if enable0RTT { - maxEarlyData = math.MaxUint32 - } - cs.extraConf = &qtls.ExtraConfig{ - GetExtensions: extHandler.GetExtensions, - ReceivedExtensions: extHandler.ReceivedExtensions, - AlternativeRecordLayer: cs, - EnforceNextProtoSelection: true, - MaxEarlyData: maxEarlyData, - Accept0RTT: cs.accept0RTT, - Rejected0RTT: cs.rejected0RTT, - Enable0RTT: enable0RTT, - GetAppDataForSessionState: cs.marshalDataForSessionState, - SetAppDataFromSessionState: cs.handleDataFromSessionState, - } - return cs, zeroRTTParametersChan } func (h *cryptoSetup) ChangeConnectionID(id protocol.ConnectionID) { initialSealer, initialOpener := NewInitialAEAD(id, h.perspective, h.version) h.initialSealer = initialSealer h.initialOpener = initialOpener - if h.tracer != nil { + if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { h.tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient) h.tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer) } @@ -301,142 +206,109 @@ func (h *cryptoSetup) SetLargest1RTTAcked(pn protocol.PacketNumber) error { return h.aead.SetLargestAcked(pn) } -func (h *cryptoSetup) RunHandshake() { - // Handle errors that might occur when HandleData() is called. - handshakeComplete := make(chan struct{}) - handshakeErrChan := make(chan error, 1) - go func() { - defer close(h.handshakeDone) - if err := h.conn.HandshakeContext(context.WithValue(context.Background(), QUICVersionContextKey, h.version)); err != nil { - handshakeErrChan <- err - return +func (h *cryptoSetup) StartHandshake() error { + err := h.conn.Start(context.WithValue(context.Background(), QUICVersionContextKey, h.version)) + if err != nil { + return wrapError(err) + } + for { + ev := h.conn.NextEvent() + done, err := h.handleEvent(ev) + if err != nil { + return wrapError(err) } - close(handshakeComplete) - }() - + if done { + break + } + } if h.perspective == protocol.PerspectiveClient { - select { - case err := <-handshakeErrChan: - h.onError(0, err.Error()) - return - case <-h.clientHelloWrittenChan: + if h.zeroRTTSealer != nil && h.zeroRTTParameters != nil { + h.logger.Debugf("Doing 0-RTT.") + h.events = append(h.events, Event{Kind: EventRestoredTransportParameters, TransportParameters: h.zeroRTTParameters}) + } else { + h.logger.Debugf("Not doing 0-RTT. Has sealer: %t, has params: %t", h.zeroRTTSealer != nil, h.zeroRTTParameters != nil) } } - - select { - case <-handshakeComplete: // return when the handshake is done - h.mutex.Lock() - h.handshakeCompleteTime = time.Now() - h.mutex.Unlock() - h.runner.OnHandshakeComplete() - case <-h.closeChan: - // wait until the Handshake() go routine has returned - <-h.handshakeDone - case alert := <-h.alertChan: - handshakeErr := <-handshakeErrChan - h.onError(alert, handshakeErr.Error()) - } -} - -func (h *cryptoSetup) onError(alert uint8, message string) { - var err error - if alert == 0 { - err = &qerr.TransportError{ErrorCode: qerr.InternalError, ErrorMessage: message} - } else { - err = qerr.NewLocalCryptoError(alert, message) - } - h.runner.OnError(err) + return nil } // Close closes the crypto setup. // It aborts the handshake, if it is still running. -// It must only be called once. func (h *cryptoSetup) Close() error { - close(h.closeChan) - // wait until qtls.Handshake() actually returned - <-h.handshakeDone - return nil + return h.conn.Close() } -// handleMessage handles a TLS handshake message. +// HandleMessage handles a TLS handshake message. // It is called by the crypto streams when a new message is available. -// It returns if it is done with messages on the same encryption level. -func (h *cryptoSetup) HandleMessage(data []byte, encLevel protocol.EncryptionLevel) bool /* stream finished */ { - msgType := messageType(data[0]) - h.logger.Debugf("Received %s message (%d bytes, encryption level: %s)", msgType, len(data), encLevel) - if err := h.checkEncryptionLevel(msgType, encLevel); err != nil { - h.onError(alertUnexpectedMessage, err.Error()) - return false - } - if encLevel != protocol.Encryption1RTT { - select { - case h.messageChan <- data: - case <-h.handshakeDone: // handshake errored, nobody is going to consume this message - return false - } - } - if encLevel == protocol.Encryption1RTT { - h.messageChan <- data - h.handlePostHandshakeMessage() - return false - } -readLoop: - for { - select { - case data := <-h.paramsChan: - if data == nil { - h.onError(0x6d, "missing quic_transport_parameters extension") - } else { - h.handleTransportParameters(data) - } - case <-h.isReadingHandshakeMessage: - break readLoop - case <-h.handshakeDone: - break readLoop - case <-h.closeChan: - break readLoop - } - } - // We're done with the Initial encryption level after processing a ClientHello / ServerHello, - // but only if a handshake opener and sealer was created. - // Otherwise, a HelloRetryRequest was performed. - // We're done with the Handshake encryption level after processing the Finished message. - return ((msgType == typeClientHello || msgType == typeServerHello) && h.handshakeOpener != nil && h.handshakeSealer != nil) || - msgType == typeFinished -} - -func (h *cryptoSetup) checkEncryptionLevel(msgType messageType, encLevel protocol.EncryptionLevel) error { - var expected protocol.EncryptionLevel - switch msgType { - case typeClientHello, typeServerHello: - expected = protocol.EncryptionInitial - case typeEncryptedExtensions, - typeCertificate, - typeCertificateRequest, - typeCertificateVerify, - typeFinished: - expected = protocol.EncryptionHandshake - case typeNewSessionTicket: - expected = protocol.Encryption1RTT - default: - return fmt.Errorf("unexpected handshake message: %d", msgType) - } - if encLevel != expected { - return fmt.Errorf("expected handshake message %s to have encryption level %s, has %s", msgType, expected, encLevel) +func (h *cryptoSetup) HandleMessage(data []byte, encLevel protocol.EncryptionLevel) error { + if err := h.handleMessage(data, encLevel); err != nil { + return wrapError(err) } return nil } -func (h *cryptoSetup) handleTransportParameters(data []byte) { +func (h *cryptoSetup) handleMessage(data []byte, encLevel protocol.EncryptionLevel) error { + if err := h.conn.HandleData(qtls.ToTLSEncryptionLevel(encLevel), data); err != nil { + return err + } + for { + ev := h.conn.NextEvent() + done, err := h.handleEvent(ev) + if err != nil { + return err + } + if done { + return nil + } + } +} + +func (h *cryptoSetup) handleEvent(ev qtls.QUICEvent) (done bool, err error) { + switch ev.Kind { + case qtls.QUICNoEvent: + return true, nil + case qtls.QUICSetReadSecret: + h.SetReadKey(ev.Level, ev.Suite, ev.Data) + return false, nil + case qtls.QUICSetWriteSecret: + h.SetWriteKey(ev.Level, ev.Suite, ev.Data) + return false, nil + case qtls.QUICTransportParameters: + return false, h.handleTransportParameters(ev.Data) + case qtls.QUICTransportParametersRequired: + h.conn.SetTransportParameters(h.ourParams.Marshal(h.perspective)) + return false, nil + case qtls.QUICRejectedEarlyData: + h.rejected0RTT() + return false, nil + case qtls.QUICWriteData: + h.WriteRecord(ev.Level, ev.Data) + return false, nil + case qtls.QUICHandshakeDone: + h.handshakeComplete() + return false, nil + default: + return false, fmt.Errorf("unexpected event: %d", ev.Kind) + } +} + +func (h *cryptoSetup) NextEvent() Event { + if len(h.events) == 0 { + return Event{Kind: EventNoEvent} + } + ev := h.events[0] + h.events = h.events[1:] + return ev +} + +func (h *cryptoSetup) handleTransportParameters(data []byte) error { var tp wire.TransportParameters if err := tp.Unmarshal(data, h.perspective.Opposite()); err != nil { - h.runner.OnError(&qerr.TransportError{ - ErrorCode: qerr.TransportParameterError, - ErrorMessage: err.Error(), - }) + return err } h.peerParams = &tp - h.runner.OnReceivedParams(h.peerParams) + h.events = append(h.events, Event{Kind: EventReceivedTransportParameters, TransportParameters: h.peerParams}) + return nil } // must be called after receiving the transport parameters @@ -477,25 +349,52 @@ func (h *cryptoSetup) handleDataFromSessionStateImpl(data []byte) (*wire.Transpo return &tp, nil } -// only valid for the server -func (h *cryptoSetup) GetSessionTicket() ([]byte, error) { - var appData []byte - // Save transport parameters to the session ticket if we're allowing 0-RTT. - if h.extraConf.MaxEarlyData > 0 { - appData = (&sessionTicket{ - Parameters: h.ourParams, - RTT: h.rttStats.SmoothedRTT(), - }).Marshal() +func (h *cryptoSetup) getDataForSessionTicket() []byte { + ticket := &sessionTicket{ + RTT: h.rttStats.SmoothedRTT(), } - return h.conn.GetSessionTicket(appData) + if h.allow0RTT { + ticket.Parameters = h.ourParams + } + return ticket.Marshal() } -// accept0RTT is called for the server when receiving the client's session ticket. -// It decides whether to accept 0-RTT. -func (h *cryptoSetup) accept0RTT(sessionTicketData []byte) bool { +// GetSessionTicket generates a new session ticket. +// Due to limitations in crypto/tls, it's only possible to generate a single session ticket per connection. +// It is only valid for the server. +func (h *cryptoSetup) GetSessionTicket() ([]byte, error) { + if err := qtls.SendSessionTicket(h.conn, h.allow0RTT); err != nil { + // Session tickets might be disabled by tls.Config.SessionTicketsDisabled. + // We can't check h.tlsConfig here, since the actual config might have been obtained from + // the GetConfigForClient callback. + // See https://github.com/golang/go/issues/62032. + // Once that issue is resolved, this error assertion can be removed. + if strings.Contains(err.Error(), "session ticket keys unavailable") { + return nil, nil + } + return nil, err + } + ev := h.conn.NextEvent() + if ev.Kind != qtls.QUICWriteData || ev.Level != qtls.QUICEncryptionLevelApplication { + panic("crypto/tls bug: where's my session ticket?") + } + ticket := ev.Data + if ev := h.conn.NextEvent(); ev.Kind != qtls.QUICNoEvent { + panic("crypto/tls bug: why more than one ticket?") + } + return ticket, nil +} + +// handleSessionTicket is called for the server when receiving the client's session ticket. +// It reads parameters from the session ticket and decides whether to accept 0-RTT when the session ticket is used for 0-RTT. +func (h *cryptoSetup) handleSessionTicket(sessionTicketData []byte, using0RTT bool) bool { var t sessionTicket - if err := t.Unmarshal(sessionTicketData); err != nil { - h.logger.Debugf("Unmarshalling transport parameters from session ticket failed: %s", err.Error()) + if err := t.Unmarshal(sessionTicketData, using0RTT); err != nil { + h.logger.Debugf("Unmarshalling session ticket failed: %s", err.Error()) + return false + } + h.rttStats.SetInitialRTT(t.RTT) + if !using0RTT { return false } valid := h.ourParams.ValidFor0RTT(t.Parameters) @@ -508,7 +407,6 @@ func (h *cryptoSetup) accept0RTT(sessionTicketData []byte) bool { return false } h.logger.Debugf("Accepting 0-RTT. Restoring RTT from session ticket: %s", t.RTT) - h.rttStats.SetInitialRTT(t.RTT) return true } @@ -522,64 +420,16 @@ func (h *cryptoSetup) rejected0RTT() { h.mutex.Unlock() if had0RTTKeys { - h.runner.DropKeys(protocol.Encryption0RTT) + h.events = append(h.events, Event{Kind: EventDiscard0RTTKeys}) } } -func (h *cryptoSetup) handlePostHandshakeMessage() { - // make sure the handshake has already completed - <-h.handshakeDone - - done := make(chan struct{}) - defer close(done) - - // h.alertChan is an unbuffered channel. - // If an error occurs during conn.HandlePostHandshakeMessage, - // it will be sent on this channel. - // Read it from a go-routine so that HandlePostHandshakeMessage doesn't deadlock. - alertChan := make(chan uint8, 1) - go func() { - <-h.isReadingHandshakeMessage - select { - case alert := <-h.alertChan: - alertChan <- alert - case <-done: - } - }() - - if err := h.conn.HandlePostHandshakeMessage(); err != nil { - select { - case <-h.closeChan: - case alert := <-alertChan: - h.onError(alert, err.Error()) - } - } -} - -// ReadHandshakeMessage is called by TLS. -// It blocks until a new handshake message is available. -func (h *cryptoSetup) ReadHandshakeMessage() ([]byte, error) { - if !h.readFirstHandshakeMessage { - h.readFirstHandshakeMessage = true - } else { - select { - case h.isReadingHandshakeMessage <- struct{}{}: - case <-h.closeChan: - return nil, errors.New("error while handling the handshake message") - } - } - select { - case msg := <-h.messageChan: - return msg, nil - case <-h.closeChan: - return nil, errors.New("error while handling the handshake message") - } -} - -func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.CipherSuiteTLS13, trafficSecret []byte) { +func (h *cryptoSetup) SetReadKey(el qtls.QUICEncryptionLevel, suiteID uint16, trafficSecret []byte) { + suite := getCipherSuite(suiteID) h.mutex.Lock() - switch encLevel { - case qtls.Encryption0RTT: + //nolint:exhaustive // The TLS stack doesn't export Initial keys. + switch el { + case qtls.QUICEncryptionLevelEarly: if h.perspective == protocol.PerspectiveClient { panic("Received 0-RTT read key for the client") } @@ -587,27 +437,19 @@ func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.Ciph createAEAD(suite, trafficSecret, h.version), newHeaderProtector(suite, trafficSecret, true, h.version), ) - h.mutex.Unlock() + h.used0RTT.Store(true) if h.logger.Debug() { h.logger.Debugf("Installed 0-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID)) } - if h.tracer != nil { - h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective.Opposite()) - } - return - case qtls.EncryptionHandshake: - h.readEncLevel = protocol.EncryptionHandshake - h.handshakeOpener = newHandshakeOpener( + case qtls.QUICEncryptionLevelHandshake: + h.handshakeOpener = newLongHeaderOpener( createAEAD(suite, trafficSecret, h.version), newHeaderProtector(suite, trafficSecret, true, h.version), - h.dropInitialKeys, - h.perspective, ) if h.logger.Debug() { h.logger.Debugf("Installed Handshake Read keys (using %s)", tls.CipherSuiteName(suite.ID)) } - case qtls.EncryptionApplication: - h.readEncLevel = protocol.Encryption1RTT + case qtls.QUICEncryptionLevelApplication: h.aead.SetReadKey(suite, trafficSecret) h.has1RTTOpener = true if h.logger.Debug() { @@ -617,15 +459,18 @@ func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.Ciph panic("unexpected read encryption level") } h.mutex.Unlock() - if h.tracer != nil { - h.tracer.UpdatedKeyFromTLS(h.readEncLevel, h.perspective.Opposite()) + h.events = append(h.events, Event{Kind: EventReceivedReadKeys}) + if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { + h.tracer.UpdatedKeyFromTLS(qtls.FromTLSEncryptionLevel(el), h.perspective.Opposite()) } } -func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.CipherSuiteTLS13, trafficSecret []byte) { +func (h *cryptoSetup) SetWriteKey(el qtls.QUICEncryptionLevel, suiteID uint16, trafficSecret []byte) { + suite := getCipherSuite(suiteID) h.mutex.Lock() - switch encLevel { - case qtls.Encryption0RTT: + //nolint:exhaustive // The TLS stack doesn't export Initial keys. + switch el { + case qtls.QUICEncryptionLevelEarly: if h.perspective == protocol.PerspectiveServer { panic("Received 0-RTT write key for the server") } @@ -637,32 +482,31 @@ func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.Cip if h.logger.Debug() { h.logger.Debugf("Installed 0-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID)) } - if h.tracer != nil { + if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective) } + // don't set used0RTT here. 0-RTT might still get rejected. return - case qtls.EncryptionHandshake: - h.writeEncLevel = protocol.EncryptionHandshake - h.handshakeSealer = newHandshakeSealer( + case qtls.QUICEncryptionLevelHandshake: + h.handshakeSealer = newLongHeaderSealer( createAEAD(suite, trafficSecret, h.version), newHeaderProtector(suite, trafficSecret, true, h.version), - h.dropInitialKeys, - h.perspective, ) if h.logger.Debug() { h.logger.Debugf("Installed Handshake Write keys (using %s)", tls.CipherSuiteName(suite.ID)) } - case qtls.EncryptionApplication: - h.writeEncLevel = protocol.Encryption1RTT + case qtls.QUICEncryptionLevelApplication: h.aead.SetWriteKey(suite, trafficSecret) h.has1RTTSealer = true if h.logger.Debug() { h.logger.Debugf("Installed 1-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID)) } if h.zeroRTTSealer != nil { + // Once we receive handshake keys, we know that 0-RTT was not rejected. + h.used0RTT.Store(true) h.zeroRTTSealer = nil h.logger.Debugf("Dropping 0-RTT keys.") - if h.tracer != nil { + if h.tracer != nil && h.tracer.DroppedEncryptionLevel != nil { h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT) } } @@ -670,56 +514,40 @@ func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.Cip panic("unexpected write encryption level") } h.mutex.Unlock() - if h.tracer != nil { - h.tracer.UpdatedKeyFromTLS(h.writeEncLevel, h.perspective) + if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { + h.tracer.UpdatedKeyFromTLS(qtls.FromTLSEncryptionLevel(el), h.perspective) } } // WriteRecord is called when TLS writes data -func (h *cryptoSetup) WriteRecord(p []byte) (int, error) { - h.mutex.Lock() - defer h.mutex.Unlock() - - //nolint:exhaustive // LS records can only be written for Initial and Handshake. - switch h.writeEncLevel { - case protocol.EncryptionInitial: - // assume that the first WriteRecord call contains the ClientHello - n, err := h.initialStream.Write(p) - if !h.clientHelloWritten && h.perspective == protocol.PerspectiveClient { - h.clientHelloWritten = true - close(h.clientHelloWrittenChan) - if h.zeroRTTSealer != nil && h.zeroRTTParameters != nil { - h.logger.Debugf("Doing 0-RTT.") - h.zeroRTTParametersChan <- h.zeroRTTParameters - } else { - h.logger.Debugf("Not doing 0-RTT.") - h.zeroRTTParametersChan <- nil - } - } - return n, err - case protocol.EncryptionHandshake: - return h.handshakeStream.Write(p) +func (h *cryptoSetup) WriteRecord(encLevel qtls.QUICEncryptionLevel, p []byte) { + //nolint:exhaustive // handshake records can only be written for Initial and Handshake. + switch encLevel { + case qtls.QUICEncryptionLevelInitial: + h.events = append(h.events, Event{Kind: EventWriteInitialData, Data: p}) + case qtls.QUICEncryptionLevelHandshake: + h.events = append(h.events, Event{Kind: EventWriteHandshakeData, Data: p}) + case qtls.QUICEncryptionLevelApplication: + panic("unexpected write") default: - panic(fmt.Sprintf("unexpected write encryption level: %s", h.writeEncLevel)) + panic(fmt.Sprintf("unexpected write encryption level: %s", encLevel)) } } -func (h *cryptoSetup) SendAlert(alert uint8) { - select { - case h.alertChan <- alert: - case <-h.closeChan: - // no need to send an alert when we've already closed - } -} - -// used a callback in the handshakeSealer and handshakeOpener -func (h *cryptoSetup) dropInitialKeys() { +func (h *cryptoSetup) DiscardInitialKeys() { h.mutex.Lock() + dropped := h.initialOpener != nil h.initialOpener = nil h.initialSealer = nil h.mutex.Unlock() - h.runner.DropKeys(protocol.EncryptionInitial) - h.logger.Debugf("Dropping Initial keys.") + if dropped { + h.logger.Debugf("Dropping Initial keys.") + } +} + +func (h *cryptoSetup) handshakeComplete() { + h.handshakeCompleteTime = time.Now() + h.events = append(h.events, Event{Kind: EventHandshakeComplete}) } func (h *cryptoSetup) SetHandshakeConfirmed() { @@ -734,7 +562,6 @@ func (h *cryptoSetup) SetHandshakeConfirmed() { } h.mutex.Unlock() if dropped { - h.runner.DropKeys(protocol.EncryptionHandshake) h.logger.Debugf("Dropping Handshake keys.") } } @@ -827,7 +654,7 @@ func (h *cryptoSetup) Get1RTTOpener() (ShortHeaderOpener, error) { if h.zeroRTTOpener != nil && time.Since(h.handshakeCompleteTime) > 3*h.rttStats.PTO(true) { h.zeroRTTOpener = nil h.logger.Debugf("Dropping 0-RTT keys.") - if h.tracer != nil { + if h.tracer != nil && h.tracer.DroppedEncryptionLevel != nil { h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT) } } @@ -839,5 +666,16 @@ func (h *cryptoSetup) Get1RTTOpener() (ShortHeaderOpener, error) { } func (h *cryptoSetup) ConnectionState() ConnectionState { - return qtls.GetConnectionState(h.conn) + return ConnectionState{ + ConnectionState: h.conn.ConnectionState(), + Used0RTT: h.used0RTT.Load(), + } +} + +func wrapError(err error) error { + // alert 80 is an internal error + if alertErr := qtls.AlertError(0); errors.As(err, &alertErr) && alertErr != 80 { + return qerr.NewLocalCryptoError(uint8(alertErr), err) + } + return &qerr.TransportError{ErrorCode: qerr.InternalError, ErrorMessage: err.Error()} } diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/header_protector.go b/vendor/github.com/quic-go/quic-go/internal/handshake/header_protector.go index 274fb30cb..fb6092e04 100644 --- a/vendor/github.com/quic-go/quic-go/internal/handshake/header_protector.go +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/header_protector.go @@ -10,7 +10,6 @@ import ( "golang.org/x/crypto/chacha20" "github.com/quic-go/quic-go/internal/protocol" - "github.com/quic-go/quic-go/internal/qtls" ) type headerProtector interface { @@ -25,7 +24,7 @@ func hkdfHeaderProtectionLabel(v protocol.VersionNumber) string { return "quic hp" } -func newHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool, v protocol.VersionNumber) headerProtector { +func newHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, v protocol.VersionNumber) headerProtector { hkdfLabel := hkdfHeaderProtectionLabel(v) switch suite.ID { case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384: @@ -45,7 +44,7 @@ type aesHeaderProtector struct { var _ headerProtector = &aesHeaderProtector{} -func newAESHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector { +func newAESHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector { hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen) block, err := aes.NewCipher(hpKey) if err != nil { @@ -90,7 +89,7 @@ type chachaHeaderProtector struct { var _ headerProtector = &chachaHeaderProtector{} -func newChaChaHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector { +func newChaChaHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector { hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen) p := &chachaHeaderProtector{ diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/initial_aead.go b/vendor/github.com/quic-go/quic-go/internal/handshake/initial_aead.go index 3967fdb83..b0377c39a 100644 --- a/vendor/github.com/quic-go/quic-go/internal/handshake/initial_aead.go +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/initial_aead.go @@ -7,13 +7,11 @@ import ( "golang.org/x/crypto/hkdf" "github.com/quic-go/quic-go/internal/protocol" - "github.com/quic-go/quic-go/internal/qtls" ) var ( - quicSaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99} - quicSaltV1 = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a} - quicSaltV2 = []byte{0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26, 0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9} + quicSaltV1 = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a} + quicSaltV2 = []byte{0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26, 0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9} ) const ( @@ -27,18 +25,10 @@ func getSalt(v protocol.VersionNumber) []byte { if v == protocol.Version2 { return quicSaltV2 } - if v == protocol.Version1 { - return quicSaltV1 - } - return quicSaltOld + return quicSaltV1 } -var initialSuite = &qtls.CipherSuiteTLS13{ - ID: tls.TLS_AES_128_GCM_SHA256, - KeyLen: 16, - AEAD: qtls.AEADAESGCMTLS13, - Hash: crypto.SHA256, -} +var initialSuite = getCipherSuite(tls.TLS_AES_128_GCM_SHA256) // NewInitialAEAD creates a new AEAD for Initial encryption / decryption. func NewInitialAEAD(connID protocol.ConnectionID, pers protocol.Perspective, v protocol.VersionNumber) (LongHeaderSealer, LongHeaderOpener) { @@ -54,8 +44,8 @@ func NewInitialAEAD(connID protocol.ConnectionID, pers protocol.Perspective, v p myKey, myIV := computeInitialKeyAndIV(mySecret, v) otherKey, otherIV := computeInitialKeyAndIV(otherSecret, v) - encrypter := qtls.AEADAESGCMTLS13(myKey, myIV) - decrypter := qtls.AEADAESGCMTLS13(otherKey, otherIV) + encrypter := initialSuite.AEAD(myKey, myIV) + decrypter := initialSuite.AEAD(otherKey, otherIV) return newLongHeaderSealer(encrypter, newHeaderProtector(initialSuite, mySecret, true, v)), newLongHeaderOpener(decrypter, newAESHeaderProtector(initialSuite, otherSecret, true, hkdfHeaderProtectionLabel(v))) diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/interface.go b/vendor/github.com/quic-go/quic-go/internal/handshake/interface.go index f80b6e0e3..fab224f9b 100644 --- a/vendor/github.com/quic-go/quic-go/internal/handshake/interface.go +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/interface.go @@ -1,12 +1,12 @@ package handshake import ( + "crypto/tls" "errors" "io" "time" "github.com/quic-go/quic-go/internal/protocol" - "github.com/quic-go/quic-go/internal/qtls" "github.com/quic-go/quic-go/internal/wire" ) @@ -22,9 +22,6 @@ var ( ErrDecryptionFailed = errors.New("decryption failed") ) -// ConnectionState contains information about the state of the connection. -type ConnectionState = qtls.ConnectionState - type headerDecryptor interface { DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte) } @@ -56,29 +53,54 @@ type ShortHeaderSealer interface { KeyPhase() protocol.KeyPhaseBit } -// A tlsExtensionHandler sends and received the QUIC TLS extension. -type tlsExtensionHandler interface { - GetExtensions(msgType uint8) []qtls.Extension - ReceivedExtensions(msgType uint8, exts []qtls.Extension) - TransportParameters() <-chan []byte +type ConnectionState struct { + tls.ConnectionState + Used0RTT bool } -type handshakeRunner interface { - OnReceivedParams(*wire.TransportParameters) - OnHandshakeComplete() - OnError(error) - DropKeys(protocol.EncryptionLevel) +// EventKind is the kind of handshake event. +type EventKind uint8 + +const ( + // EventNoEvent signals that there are no new handshake events + EventNoEvent EventKind = iota + 1 + // EventWriteInitialData contains new CRYPTO data to send at the Initial encryption level + EventWriteInitialData + // EventWriteHandshakeData contains new CRYPTO data to send at the Handshake encryption level + EventWriteHandshakeData + // EventReceivedReadKeys signals that new decryption keys are available. + // It doesn't say which encryption level those keys are for. + EventReceivedReadKeys + // EventDiscard0RTTKeys signals that the Handshake keys were discarded. + EventDiscard0RTTKeys + // EventReceivedTransportParameters contains the transport parameters sent by the peer. + EventReceivedTransportParameters + // EventRestoredTransportParameters contains the transport parameters restored from the session ticket. + // It is only used for the client. + EventRestoredTransportParameters + // EventHandshakeComplete signals that the TLS handshake was completed. + EventHandshakeComplete +) + +// Event is a handshake event. +type Event struct { + Kind EventKind + Data []byte + TransportParameters *wire.TransportParameters } // CryptoSetup handles the handshake and protecting / unprotecting packets type CryptoSetup interface { - RunHandshake() + StartHandshake() error io.Closer ChangeConnectionID(protocol.ConnectionID) GetSessionTicket() ([]byte, error) - HandleMessage([]byte, protocol.EncryptionLevel) bool + HandleMessage([]byte, protocol.EncryptionLevel) error + NextEvent() Event + SetLargest1RTTAcked(protocol.PacketNumber) error + DiscardInitialKeys() SetHandshakeConfirmed() ConnectionState() ConnectionState diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/mockgen.go b/vendor/github.com/quic-go/quic-go/internal/handshake/mockgen.go deleted file mode 100644 index 68b0988c6..000000000 --- a/vendor/github.com/quic-go/quic-go/internal/handshake/mockgen.go +++ /dev/null @@ -1,6 +0,0 @@ -//go:build gomock || generate - -package handshake - -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package handshake -destination mock_handshake_runner_test.go github.com/quic-go/quic-go/internal/handshake HandshakeRunner" -type HandshakeRunner = handshakeRunner diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/retry.go b/vendor/github.com/quic-go/quic-go/internal/handshake/retry.go index ff14f7e0d..68fa53ed1 100644 --- a/vendor/github.com/quic-go/quic-go/internal/handshake/retry.go +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/retry.go @@ -11,13 +11,11 @@ import ( ) var ( - retryAEADdraft29 cipher.AEAD // used for QUIC draft versions up to 34 - retryAEADv1 cipher.AEAD // used for QUIC v1 (RFC 9000) - retryAEADv2 cipher.AEAD // used for QUIC v2 + retryAEADv1 cipher.AEAD // used for QUIC v1 (RFC 9000) + retryAEADv2 cipher.AEAD // used for QUIC v2 (RFC 9369) ) func init() { - retryAEADdraft29 = initAEAD([16]byte{0xcc, 0xce, 0x18, 0x7e, 0xd0, 0x9a, 0x09, 0xd0, 0x57, 0x28, 0x15, 0x5a, 0x6c, 0xb9, 0x6b, 0xe1}) retryAEADv1 = initAEAD([16]byte{0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68, 0xc8, 0x4e}) retryAEADv2 = initAEAD([16]byte{0x8f, 0xb4, 0xb0, 0x1b, 0x56, 0xac, 0x48, 0xe2, 0x60, 0xfb, 0xcb, 0xce, 0xad, 0x7c, 0xcc, 0x92}) } @@ -35,11 +33,10 @@ func initAEAD(key [16]byte) cipher.AEAD { } var ( - retryBuf bytes.Buffer - retryMutex sync.Mutex - retryNonceDraft29 = [12]byte{0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c} - retryNonceV1 = [12]byte{0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb} - retryNonceV2 = [12]byte{0xd8, 0x69, 0x69, 0xbc, 0x2d, 0x7c, 0x6d, 0x99, 0x90, 0xef, 0xb0, 0x4a} + retryBuf bytes.Buffer + retryMutex sync.Mutex + retryNonceV1 = [12]byte{0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb} + retryNonceV2 = [12]byte{0xd8, 0x69, 0x69, 0xbc, 0x2d, 0x7c, 0x6d, 0x99, 0x90, 0xef, 0xb0, 0x4a} ) // GetRetryIntegrityTag calculates the integrity tag on a Retry packet @@ -54,14 +51,10 @@ func GetRetryIntegrityTag(retry []byte, origDestConnID protocol.ConnectionID, ve var tag [16]byte var sealed []byte - //nolint:exhaustive // These are all the versions we support - switch version { - case protocol.Version1: - sealed = retryAEADv1.Seal(tag[:0], retryNonceV1[:], nil, retryBuf.Bytes()) - case protocol.Version2: + if version == protocol.Version2 { sealed = retryAEADv2.Seal(tag[:0], retryNonceV2[:], nil, retryBuf.Bytes()) - default: - sealed = retryAEADdraft29.Seal(tag[:0], retryNonceDraft29[:], nil, retryBuf.Bytes()) + } else { + sealed = retryAEADv1.Seal(tag[:0], retryNonceV1[:], nil, retryBuf.Bytes()) } if len(sealed) != 16 { panic(fmt.Sprintf("unexpected Retry integrity tag length: %d", len(sealed))) diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go b/vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go index 56bcbcd5d..9481af563 100644 --- a/vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/session_ticket.go @@ -10,7 +10,7 @@ import ( "github.com/quic-go/quic-go/quicvarint" ) -const sessionTicketRevision = 2 +const sessionTicketRevision = 4 type sessionTicket struct { Parameters *wire.TransportParameters @@ -21,10 +21,13 @@ func (t *sessionTicket) Marshal() []byte { b := make([]byte, 0, 256) b = quicvarint.Append(b, sessionTicketRevision) b = quicvarint.Append(b, uint64(t.RTT.Microseconds())) + if t.Parameters == nil { + return b + } return t.Parameters.MarshalForSessionTicket(b) } -func (t *sessionTicket) Unmarshal(b []byte) error { +func (t *sessionTicket) Unmarshal(b []byte, using0RTT bool) error { r := bytes.NewReader(b) rev, err := quicvarint.Read(r) if err != nil { @@ -37,11 +40,15 @@ func (t *sessionTicket) Unmarshal(b []byte) error { if err != nil { return errors.New("failed to read RTT") } - var tp wire.TransportParameters - if err := tp.UnmarshalFromSessionTicket(r); err != nil { - return fmt.Errorf("unmarshaling transport parameters from session ticket failed: %s", err.Error()) + if using0RTT { + var tp wire.TransportParameters + if err := tp.UnmarshalFromSessionTicket(r); err != nil { + return fmt.Errorf("unmarshaling transport parameters from session ticket failed: %s", err.Error()) + } + t.Parameters = &tp + } else if r.Len() > 0 { + return fmt.Errorf("the session ticket has more bytes than expected") } - t.Parameters = &tp t.RTT = time.Duration(rtt) * time.Microsecond return nil } diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/tls_extension_handler.go b/vendor/github.com/quic-go/quic-go/internal/handshake/tls_extension_handler.go deleted file mode 100644 index 6105fe401..000000000 --- a/vendor/github.com/quic-go/quic-go/internal/handshake/tls_extension_handler.go +++ /dev/null @@ -1,68 +0,0 @@ -package handshake - -import ( - "github.com/quic-go/quic-go/internal/protocol" - "github.com/quic-go/quic-go/internal/qtls" -) - -const ( - quicTLSExtensionTypeOldDrafts = 0xffa5 - quicTLSExtensionType = 0x39 -) - -type extensionHandler struct { - ourParams []byte - paramsChan chan []byte - - extensionType uint16 - - perspective protocol.Perspective -} - -var _ tlsExtensionHandler = &extensionHandler{} - -// newExtensionHandler creates a new extension handler -func newExtensionHandler(params []byte, pers protocol.Perspective, v protocol.VersionNumber) tlsExtensionHandler { - et := uint16(quicTLSExtensionType) - if v == protocol.VersionDraft29 { - et = quicTLSExtensionTypeOldDrafts - } - return &extensionHandler{ - ourParams: params, - paramsChan: make(chan []byte), - perspective: pers, - extensionType: et, - } -} - -func (h *extensionHandler) GetExtensions(msgType uint8) []qtls.Extension { - if (h.perspective == protocol.PerspectiveClient && messageType(msgType) != typeClientHello) || - (h.perspective == protocol.PerspectiveServer && messageType(msgType) != typeEncryptedExtensions) { - return nil - } - return []qtls.Extension{{ - Type: h.extensionType, - Data: h.ourParams, - }} -} - -func (h *extensionHandler) ReceivedExtensions(msgType uint8, exts []qtls.Extension) { - if (h.perspective == protocol.PerspectiveClient && messageType(msgType) != typeEncryptedExtensions) || - (h.perspective == protocol.PerspectiveServer && messageType(msgType) != typeClientHello) { - return - } - - var data []byte - for _, ext := range exts { - if ext.Type == h.extensionType { - data = ext.Data - break - } - } - - h.paramsChan <- data -} - -func (h *extensionHandler) TransportParameters() <-chan []byte { - return h.paramsChan -} diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go b/vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go index e5e90bb3b..2d91e6b25 100644 --- a/vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/token_generator.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/asn1" "fmt" - "io" "net" "time" @@ -45,15 +44,9 @@ type TokenGenerator struct { tokenProtector tokenProtector } -// NewTokenGenerator initializes a new TookenGenerator -func NewTokenGenerator(rand io.Reader) (*TokenGenerator, error) { - tokenProtector, err := newTokenProtector(rand) - if err != nil { - return nil, err - } - return &TokenGenerator{ - tokenProtector: tokenProtector, - }, nil +// NewTokenGenerator initializes a new TokenGenerator +func NewTokenGenerator(key TokenProtectorKey) *TokenGenerator { + return &TokenGenerator{tokenProtector: newTokenProtector(key)} } // NewRetryToken generates a new token for a Retry for a given source address diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/token_protector.go b/vendor/github.com/quic-go/quic-go/internal/handshake/token_protector.go index 650f230b2..f3a99e411 100644 --- a/vendor/github.com/quic-go/quic-go/internal/handshake/token_protector.go +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/token_protector.go @@ -3,6 +3,7 @@ package handshake import ( "crypto/aes" "crypto/cipher" + "crypto/rand" "crypto/sha256" "fmt" "io" @@ -10,6 +11,9 @@ import ( "golang.org/x/crypto/hkdf" ) +// TokenProtectorKey is the key used to encrypt both Retry and session resumption tokens. +type TokenProtectorKey [32]byte + // TokenProtector is used to create and verify a token type tokenProtector interface { // NewToken creates a new token @@ -18,40 +22,29 @@ type tokenProtector interface { DecodeToken([]byte) ([]byte, error) } -const ( - tokenSecretSize = 32 - tokenNonceSize = 32 -) +const tokenNonceSize = 32 // tokenProtector is used to create and verify a token type tokenProtectorImpl struct { - rand io.Reader - secret []byte + key TokenProtectorKey } // newTokenProtector creates a source for source address tokens -func newTokenProtector(rand io.Reader) (tokenProtector, error) { - secret := make([]byte, tokenSecretSize) - if _, err := rand.Read(secret); err != nil { - return nil, err - } - return &tokenProtectorImpl{ - rand: rand, - secret: secret, - }, nil +func newTokenProtector(key TokenProtectorKey) tokenProtector { + return &tokenProtectorImpl{key: key} } // NewToken encodes data into a new token. func (s *tokenProtectorImpl) NewToken(data []byte) ([]byte, error) { - nonce := make([]byte, tokenNonceSize) - if _, err := s.rand.Read(nonce); err != nil { + var nonce [tokenNonceSize]byte + if _, err := rand.Read(nonce[:]); err != nil { return nil, err } - aead, aeadNonce, err := s.createAEAD(nonce) + aead, aeadNonce, err := s.createAEAD(nonce[:]) if err != nil { return nil, err } - return append(nonce, aead.Seal(nil, aeadNonce, data, nil)...), nil + return append(nonce[:], aead.Seal(nil, aeadNonce, data, nil)...), nil } // DecodeToken decodes a token. @@ -68,7 +61,7 @@ func (s *tokenProtectorImpl) DecodeToken(p []byte) ([]byte, error) { } func (s *tokenProtectorImpl) createAEAD(nonce []byte) (cipher.AEAD, []byte, error) { - h := hkdf.New(sha256.New, s.secret, nonce, []byte("quic-go token source")) + h := hkdf.New(sha256.New, s.key[:], nonce, []byte("quic-go token source")) key := make([]byte, 32) // use a 32 byte key, in order to select AES-256 if _, err := io.ReadFull(h, key); err != nil { return nil, nil, err diff --git a/vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go b/vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go index ac01acdb1..a583f2773 100644 --- a/vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go +++ b/vendor/github.com/quic-go/quic-go/internal/handshake/updatable_aead.go @@ -10,7 +10,6 @@ import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" - "github.com/quic-go/quic-go/internal/qtls" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" ) @@ -24,7 +23,7 @@ var KeyUpdateInterval uint64 = protocol.KeyUpdateInterval var FirstKeyUpdateInterval uint64 = 100 type updatableAEAD struct { - suite *qtls.CipherSuiteTLS13 + suite *cipherSuite keyPhase protocol.KeyPhase largestAcked protocol.PacketNumber @@ -58,7 +57,7 @@ type updatableAEAD struct { rttStats *utils.RTTStats - tracer logging.ConnectionTracer + tracer *logging.ConnectionTracer logger utils.Logger version protocol.VersionNumber @@ -71,7 +70,7 @@ var ( _ ShortHeaderSealer = &updatableAEAD{} ) -func newUpdatableAEAD(rttStats *utils.RTTStats, tracer logging.ConnectionTracer, logger utils.Logger, version protocol.VersionNumber) *updatableAEAD { +func newUpdatableAEAD(rttStats *utils.RTTStats, tracer *logging.ConnectionTracer, logger utils.Logger, version protocol.VersionNumber) *updatableAEAD { return &updatableAEAD{ firstPacketNumber: protocol.InvalidPacketNumber, largestAcked: protocol.InvalidPacketNumber, @@ -87,7 +86,7 @@ func newUpdatableAEAD(rttStats *utils.RTTStats, tracer logging.ConnectionTracer, func (a *updatableAEAD) rollKeys() { if a.prevRcvAEAD != nil { a.logger.Debugf("Dropping key phase %d ahead of scheduled time. Drop time was: %s", a.keyPhase-1, a.prevRcvAEADExpiry) - if a.tracer != nil { + if a.tracer != nil && a.tracer.DroppedKey != nil { a.tracer.DroppedKey(a.keyPhase - 1) } a.prevRcvAEADExpiry = time.Time{} @@ -121,7 +120,7 @@ func (a *updatableAEAD) getNextTrafficSecret(hash crypto.Hash, ts []byte) []byte // SetReadKey sets the read key. // For the client, this function is called before SetWriteKey. // For the server, this function is called after SetWriteKey. -func (a *updatableAEAD) SetReadKey(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) { +func (a *updatableAEAD) SetReadKey(suite *cipherSuite, trafficSecret []byte) { a.rcvAEAD = createAEAD(suite, trafficSecret, a.version) a.headerDecrypter = newHeaderProtector(suite, trafficSecret, false, a.version) if a.suite == nil { @@ -135,7 +134,7 @@ func (a *updatableAEAD) SetReadKey(suite *qtls.CipherSuiteTLS13, trafficSecret [ // SetWriteKey sets the write key. // For the client, this function is called after SetReadKey. // For the server, this function is called before SetWriteKey. -func (a *updatableAEAD) SetWriteKey(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) { +func (a *updatableAEAD) SetWriteKey(suite *cipherSuite, trafficSecret []byte) { a.sendAEAD = createAEAD(suite, trafficSecret, a.version) a.headerEncrypter = newHeaderProtector(suite, trafficSecret, false, a.version) if a.suite == nil { @@ -146,7 +145,7 @@ func (a *updatableAEAD) SetWriteKey(suite *qtls.CipherSuiteTLS13, trafficSecret a.nextSendAEAD = createAEAD(suite, a.nextSendTrafficSecret, a.version) } -func (a *updatableAEAD) setAEADParameters(aead cipher.AEAD, suite *qtls.CipherSuiteTLS13) { +func (a *updatableAEAD) setAEADParameters(aead cipher.AEAD, suite *cipherSuite) { a.nonceBuf = make([]byte, aead.NonceSize()) a.aeadOverhead = aead.Overhead() a.suite = suite @@ -183,7 +182,7 @@ func (a *updatableAEAD) open(dst, src []byte, rcvTime time.Time, pn protocol.Pac a.prevRcvAEAD = nil a.logger.Debugf("Dropping key phase %d", a.keyPhase-1) a.prevRcvAEADExpiry = time.Time{} - if a.tracer != nil { + if a.tracer != nil && a.tracer.DroppedKey != nil { a.tracer.DroppedKey(a.keyPhase - 1) } } @@ -217,7 +216,7 @@ func (a *updatableAEAD) open(dst, src []byte, rcvTime time.Time, pn protocol.Pac // The peer initiated this key update. It's safe to drop the keys for the previous generation now. // Start a timer to drop the previous key generation. a.startKeyDropTimer(rcvTime) - if a.tracer != nil { + if a.tracer != nil && a.tracer.UpdatedKey != nil { a.tracer.UpdatedKey(a.keyPhase, true) } a.firstRcvdWithCurrentKey = pn @@ -309,7 +308,7 @@ func (a *updatableAEAD) KeyPhase() protocol.KeyPhaseBit { if a.shouldInitiateKeyUpdate() { a.rollKeys() a.logger.Debugf("Initiating key update to key phase %d", a.keyPhase) - if a.tracer != nil { + if a.tracer != nil && a.tracer.UpdatedKey != nil { a.tracer.UpdatedKey(a.keyPhase, false) } } diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/params.go b/vendor/github.com/quic-go/quic-go/internal/protocol/params.go index fe3a75625..3ca68bf83 100644 --- a/vendor/github.com/quic-go/quic-go/internal/protocol/params.go +++ b/vendor/github.com/quic-go/quic-go/internal/protocol/params.go @@ -65,9 +65,6 @@ const MaxAcceptQueueSize = 32 // TokenValidity is the duration that a (non-retry) token is considered valid const TokenValidity = 24 * time.Hour -// RetryTokenValidity is the duration that a retry token is considered valid -const RetryTokenValidity = 10 * time.Second - // MaxOutstandingSentPackets is maximum number of packets saved for retransmission. // When reached, it imposes a soft limit on sending new packets: // Sending ACKs and retransmission is still allowed, but now new regular packets can be sent. @@ -108,9 +105,6 @@ const DefaultIdleTimeout = 30 * time.Second // DefaultHandshakeIdleTimeout is the default idle timeout used before handshake completion. const DefaultHandshakeIdleTimeout = 5 * time.Second -// DefaultHandshakeTimeout is the default timeout for a connection until the crypto handshake succeeds. -const DefaultHandshakeTimeout = 10 * time.Second - // MaxKeepAliveInterval is the maximum time until we send a packet to keep a connection alive. // It should be shorter than the time that NATs clear their mapping. const MaxKeepAliveInterval = 20 * time.Second diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go b/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go index 98bc9ffbc..d056cb9de 100644 --- a/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go +++ b/vendor/github.com/quic-go/quic-go/internal/protocol/protocol.go @@ -37,12 +37,61 @@ func (t PacketType) String() string { type ECN uint8 const ( - ECNNon ECN = iota // 00 - ECT1 // 01 - ECT0 // 10 - ECNCE // 11 + ECNUnsupported ECN = iota + ECNNon // 00 + ECT1 // 01 + ECT0 // 10 + ECNCE // 11 ) +func ParseECNHeaderBits(bits byte) ECN { + switch bits { + case 0: + return ECNNon + case 0b00000010: + return ECT0 + case 0b00000001: + return ECT1 + case 0b00000011: + return ECNCE + default: + panic("invalid ECN bits") + } +} + +func (e ECN) ToHeaderBits() byte { + //nolint:exhaustive // There are only 4 values. + switch e { + case ECNNon: + return 0 + case ECT0: + return 0b00000010 + case ECT1: + return 0b00000001 + case ECNCE: + return 0b00000011 + default: + panic("ECN unsupported") + } +} + +func (e ECN) String() string { + switch e { + case ECNUnsupported: + return "ECN unsupported" + case ECNNon: + return "Not-ECT" + case ECT1: + return "ECT(1)" + case ECT0: + return "ECT(0)" + case ECNCE: + return "CE" + default: + return fmt.Sprintf("invalid ECN value: %d", e) + } +} + // A ByteCount in QUIC type ByteCount int64 diff --git a/vendor/github.com/quic-go/quic-go/internal/protocol/version.go b/vendor/github.com/quic-go/quic-go/internal/protocol/version.go index 20e8976e3..5c2decbdc 100644 --- a/vendor/github.com/quic-go/quic-go/internal/protocol/version.go +++ b/vendor/github.com/quic-go/quic-go/internal/protocol/version.go @@ -19,14 +19,14 @@ const ( // The version numbers, making grepping easier const ( VersionUnknown VersionNumber = math.MaxUint32 - VersionDraft29 VersionNumber = 0xff00001d + versionDraft29 VersionNumber = 0xff00001d // draft-29 used to be a widely deployed version Version1 VersionNumber = 0x1 Version2 VersionNumber = 0x6b3343cf ) // SupportedVersions lists the versions that the server supports // must be in sorted descending order -var SupportedVersions = []VersionNumber{Version1, Version2, VersionDraft29} +var SupportedVersions = []VersionNumber{Version1, Version2} // IsValidVersion says if the version is known to quic-go func IsValidVersion(v VersionNumber) bool { @@ -38,7 +38,7 @@ func (vn VersionNumber) String() string { switch vn { case VersionUnknown: return "unknown" - case VersionDraft29: + case versionDraft29: return "draft-29" case Version1: return "v1" diff --git a/vendor/github.com/quic-go/quic-go/internal/qerr/error_codes.go b/vendor/github.com/quic-go/quic-go/internal/qerr/error_codes.go index cc846df6a..a037acd22 100644 --- a/vendor/github.com/quic-go/quic-go/internal/qerr/error_codes.go +++ b/vendor/github.com/quic-go/quic-go/internal/qerr/error_codes.go @@ -40,7 +40,7 @@ func (e TransportErrorCode) Message() string { if !e.IsCryptoError() { return "" } - return qtls.Alert(e - 0x100).Error() + return qtls.AlertError(e - 0x100).Error() } func (e TransportErrorCode) String() string { diff --git a/vendor/github.com/quic-go/quic-go/internal/qerr/errors.go b/vendor/github.com/quic-go/quic-go/internal/qerr/errors.go index 26ea34452..2d8511f77 100644 --- a/vendor/github.com/quic-go/quic-go/internal/qerr/errors.go +++ b/vendor/github.com/quic-go/quic-go/internal/qerr/errors.go @@ -17,15 +17,16 @@ type TransportError struct { FrameType uint64 ErrorCode TransportErrorCode ErrorMessage string + error error // only set for local errors, sometimes } var _ error = &TransportError{} // NewLocalCryptoError create a new TransportError instance for a crypto error -func NewLocalCryptoError(tlsAlert uint8, errorMessage string) *TransportError { +func NewLocalCryptoError(tlsAlert uint8, err error) *TransportError { return &TransportError{ - ErrorCode: 0x100 + TransportErrorCode(tlsAlert), - ErrorMessage: errorMessage, + ErrorCode: 0x100 + TransportErrorCode(tlsAlert), + error: err, } } @@ -35,6 +36,9 @@ func (e *TransportError) Error() string { str += fmt.Sprintf(" (frame type: %#x)", e.FrameType) } msg := e.ErrorMessage + if len(msg) == 0 && e.error != nil { + msg = e.error.Error() + } if len(msg) == 0 { msg = e.ErrorCode.Message() } @@ -48,6 +52,10 @@ func (e *TransportError) Is(target error) bool { return target == net.ErrClosed } +func (e *TransportError) Unwrap() error { + return e.error +} + // An ApplicationErrorCode is an application-defined error code. type ApplicationErrorCode uint64 diff --git a/vendor/github.com/quic-go/quic-go/internal/qtls/cipher_suite_go121.go b/vendor/github.com/quic-go/quic-go/internal/qtls/cipher_suite_go121.go new file mode 100644 index 000000000..aa8c768fd --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/qtls/cipher_suite_go121.go @@ -0,0 +1,66 @@ +//go:build go1.21 + +package qtls + +import ( + "crypto" + "crypto/cipher" + "crypto/tls" + "fmt" + "unsafe" +) + +type cipherSuiteTLS13 struct { + ID uint16 + KeyLen int + AEAD func(key, fixedNonce []byte) cipher.AEAD + Hash crypto.Hash +} + +//go:linkname cipherSuiteTLS13ByID crypto/tls.cipherSuiteTLS13ByID +func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 + +//go:linkname cipherSuitesTLS13 crypto/tls.cipherSuitesTLS13 +var cipherSuitesTLS13 []unsafe.Pointer + +//go:linkname defaultCipherSuitesTLS13 crypto/tls.defaultCipherSuitesTLS13 +var defaultCipherSuitesTLS13 []uint16 + +//go:linkname defaultCipherSuitesTLS13NoAES crypto/tls.defaultCipherSuitesTLS13NoAES +var defaultCipherSuitesTLS13NoAES []uint16 + +var cipherSuitesModified bool + +// SetCipherSuite modifies the cipherSuiteTLS13 slice of cipher suites inside qtls +// such that it only contains the cipher suite with the chosen id. +// The reset function returned resets them back to the original value. +func SetCipherSuite(id uint16) (reset func()) { + if cipherSuitesModified { + panic("cipher suites modified multiple times without resetting") + } + cipherSuitesModified = true + + origCipherSuitesTLS13 := append([]unsafe.Pointer{}, cipherSuitesTLS13...) + origDefaultCipherSuitesTLS13 := append([]uint16{}, defaultCipherSuitesTLS13...) + origDefaultCipherSuitesTLS13NoAES := append([]uint16{}, defaultCipherSuitesTLS13NoAES...) + // The order is given by the order of the slice elements in cipherSuitesTLS13 in qtls. + switch id { + case tls.TLS_AES_128_GCM_SHA256: + cipherSuitesTLS13 = cipherSuitesTLS13[:1] + case tls.TLS_CHACHA20_POLY1305_SHA256: + cipherSuitesTLS13 = cipherSuitesTLS13[1:2] + case tls.TLS_AES_256_GCM_SHA384: + cipherSuitesTLS13 = cipherSuitesTLS13[2:] + default: + panic(fmt.Sprintf("unexpected cipher suite: %d", id)) + } + defaultCipherSuitesTLS13 = []uint16{id} + defaultCipherSuitesTLS13NoAES = []uint16{id} + + return func() { + cipherSuitesTLS13 = origCipherSuitesTLS13 + defaultCipherSuitesTLS13 = origDefaultCipherSuitesTLS13 + defaultCipherSuitesTLS13NoAES = origDefaultCipherSuitesTLS13NoAES + cipherSuitesModified = false + } +} diff --git a/vendor/github.com/quic-go/quic-go/internal/qtls/client_session_cache.go b/vendor/github.com/quic-go/quic-go/internal/qtls/client_session_cache.go new file mode 100644 index 000000000..519895ee6 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/internal/qtls/client_session_cache.go @@ -0,0 +1,61 @@ +//go:build go1.21 + +package qtls + +import ( + "crypto/tls" +) + +type clientSessionCache struct { + getData func() []byte + setData func([]byte) + wrapped tls.ClientSessionCache +} + +var _ tls.ClientSessionCache = &clientSessionCache{} + +func (c clientSessionCache) Put(key string, cs *tls.ClientSessionState) { + if cs == nil { + c.wrapped.Put(key, nil) + return + } + ticket, state, err := cs.ResumptionState() + if err != nil || state == nil { + c.wrapped.Put(key, cs) + return + } + state.Extra = append(state.Extra, addExtraPrefix(c.getData())) + newCS, err := tls.NewResumptionState(ticket, state) + if err != nil { + // It's not clear why this would error. Just save the original state. + c.wrapped.Put(key, cs) + return + } + c.wrapped.Put(key, newCS) +} + +func (c clientSessionCache) Get(key string) (*tls.ClientSessionState, bool) { + cs, ok := c.wrapped.Get(key) + if !ok || cs == nil { + return cs, ok + } + ticket, state, err := cs.ResumptionState() + if err != nil { + // It's not clear why this would error. + // Remove the ticket from the session cache, so we don't run into this error over and over again + c.wrapped.Put(key, nil) + return nil, false + } + // restore QUIC transport parameters and RTT stored in state.Extra + if extra := findExtraData(state.Extra); extra != nil { + c.setData(extra) + } + session, err := tls.NewResumptionState(ticket, state) + if err != nil { + // It's not clear why this would error. + // Remove the ticket from the session cache, so we don't run into this error over and over again + c.wrapped.Put(key, nil) + return nil, false + } + return session, true +} diff --git a/vendor/github.com/quic-go/quic-go/internal/qtls/go119.go b/vendor/github.com/quic-go/quic-go/internal/qtls/go119.go deleted file mode 100644 index f040b859c..000000000 --- a/vendor/github.com/quic-go/quic-go/internal/qtls/go119.go +++ /dev/null @@ -1,145 +0,0 @@ -//go:build go1.19 && !go1.20 - -package qtls - -import ( - "crypto" - "crypto/cipher" - "crypto/tls" - "fmt" - "net" - "unsafe" - - "github.com/quic-go/qtls-go1-19" -) - -type ( - // Alert is a TLS alert - Alert = qtls.Alert - // A Certificate is qtls.Certificate. - Certificate = qtls.Certificate - // CertificateRequestInfo contains information about a certificate request. - CertificateRequestInfo = qtls.CertificateRequestInfo - // A CipherSuiteTLS13 is a cipher suite for TLS 1.3 - CipherSuiteTLS13 = qtls.CipherSuiteTLS13 - // ClientHelloInfo contains information about a ClientHello. - ClientHelloInfo = qtls.ClientHelloInfo - // ClientSessionCache is a cache used for session resumption. - ClientSessionCache = qtls.ClientSessionCache - // ClientSessionState is a state needed for session resumption. - ClientSessionState = qtls.ClientSessionState - // A Config is a qtls.Config. - Config = qtls.Config - // A Conn is a qtls.Conn. - Conn = qtls.Conn - // ConnectionState contains information about the state of the connection. - ConnectionState = qtls.ConnectionStateWith0RTT - // EncryptionLevel is the encryption level of a message. - EncryptionLevel = qtls.EncryptionLevel - // Extension is a TLS extension - Extension = qtls.Extension - // ExtraConfig is the qtls.ExtraConfig - ExtraConfig = qtls.ExtraConfig - // RecordLayer is a qtls RecordLayer. - RecordLayer = qtls.RecordLayer -) - -const ( - // EncryptionHandshake is the Handshake encryption level - EncryptionHandshake = qtls.EncryptionHandshake - // Encryption0RTT is the 0-RTT encryption level - Encryption0RTT = qtls.Encryption0RTT - // EncryptionApplication is the application data encryption level - EncryptionApplication = qtls.EncryptionApplication -) - -// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3 -func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD { - return qtls.AEADAESGCMTLS13(key, fixedNonce) -} - -// Client returns a new TLS client side connection. -func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { - return qtls.Client(conn, config, extraConfig) -} - -// Server returns a new TLS server side connection. -func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { - return qtls.Server(conn, config, extraConfig) -} - -func GetConnectionState(conn *Conn) ConnectionState { - return conn.ConnectionStateWith0RTT() -} - -// ToTLSConnectionState extracts the tls.ConnectionState -func ToTLSConnectionState(cs ConnectionState) tls.ConnectionState { - return cs.ConnectionState -} - -type cipherSuiteTLS13 struct { - ID uint16 - KeyLen int - AEAD func(key, fixedNonce []byte) cipher.AEAD - Hash crypto.Hash -} - -//go:linkname cipherSuiteTLS13ByID github.com/quic-go/qtls-go1-19.cipherSuiteTLS13ByID -func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 - -// CipherSuiteTLS13ByID gets a TLS 1.3 cipher suite. -func CipherSuiteTLS13ByID(id uint16) *CipherSuiteTLS13 { - val := cipherSuiteTLS13ByID(id) - cs := (*cipherSuiteTLS13)(unsafe.Pointer(val)) - return &qtls.CipherSuiteTLS13{ - ID: cs.ID, - KeyLen: cs.KeyLen, - AEAD: cs.AEAD, - Hash: cs.Hash, - } -} - -//go:linkname cipherSuitesTLS13 github.com/quic-go/qtls-go1-19.cipherSuitesTLS13 -var cipherSuitesTLS13 []unsafe.Pointer - -//go:linkname defaultCipherSuitesTLS13 github.com/quic-go/qtls-go1-19.defaultCipherSuitesTLS13 -var defaultCipherSuitesTLS13 []uint16 - -//go:linkname defaultCipherSuitesTLS13NoAES github.com/quic-go/qtls-go1-19.defaultCipherSuitesTLS13NoAES -var defaultCipherSuitesTLS13NoAES []uint16 - -var cipherSuitesModified bool - -// SetCipherSuite modifies the cipherSuiteTLS13 slice of cipher suites inside qtls -// such that it only contains the cipher suite with the chosen id. -// The reset function returned resets them back to the original value. -func SetCipherSuite(id uint16) (reset func()) { - if cipherSuitesModified { - panic("cipher suites modified multiple times without resetting") - } - cipherSuitesModified = true - - origCipherSuitesTLS13 := append([]unsafe.Pointer{}, cipherSuitesTLS13...) - origDefaultCipherSuitesTLS13 := append([]uint16{}, defaultCipherSuitesTLS13...) - origDefaultCipherSuitesTLS13NoAES := append([]uint16{}, defaultCipherSuitesTLS13NoAES...) - // The order is given by the order of the slice elements in cipherSuitesTLS13 in qtls. - switch id { - case tls.TLS_AES_128_GCM_SHA256: - cipherSuitesTLS13 = cipherSuitesTLS13[:1] - case tls.TLS_CHACHA20_POLY1305_SHA256: - cipherSuitesTLS13 = cipherSuitesTLS13[1:2] - case tls.TLS_AES_256_GCM_SHA384: - cipherSuitesTLS13 = cipherSuitesTLS13[2:] - default: - panic(fmt.Sprintf("unexpected cipher suite: %d", id)) - } - defaultCipherSuitesTLS13 = []uint16{id} - defaultCipherSuitesTLS13NoAES = []uint16{id} - - return func() { - cipherSuitesTLS13 = origCipherSuitesTLS13 - defaultCipherSuitesTLS13 = origDefaultCipherSuitesTLS13 - defaultCipherSuitesTLS13NoAES = origDefaultCipherSuitesTLS13NoAES - cipherSuitesModified = false - } -} diff --git a/vendor/github.com/quic-go/quic-go/internal/qtls/go120.go b/vendor/github.com/quic-go/quic-go/internal/qtls/go120.go index a40146ab4..898f03526 100644 --- a/vendor/github.com/quic-go/quic-go/internal/qtls/go120.go +++ b/vendor/github.com/quic-go/quic-go/internal/qtls/go120.go @@ -1,101 +1,99 @@ -//go:build go1.20 +//go:build go1.20 && !go1.21 package qtls import ( - "crypto" - "crypto/cipher" "crypto/tls" "fmt" - "net" "unsafe" + "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/qtls-go1-20" ) type ( - // Alert is a TLS alert - Alert = qtls.Alert - // A Certificate is qtls.Certificate. - Certificate = qtls.Certificate - // CertificateRequestInfo contains information about a certificate request. - CertificateRequestInfo = qtls.CertificateRequestInfo - // A CipherSuiteTLS13 is a cipher suite for TLS 1.3 - CipherSuiteTLS13 = qtls.CipherSuiteTLS13 - // ClientHelloInfo contains information about a ClientHello. - ClientHelloInfo = qtls.ClientHelloInfo - // ClientSessionCache is a cache used for session resumption. - ClientSessionCache = qtls.ClientSessionCache - // ClientSessionState is a state needed for session resumption. - ClientSessionState = qtls.ClientSessionState - // A Config is a qtls.Config. - Config = qtls.Config - // A Conn is a qtls.Conn. - Conn = qtls.Conn - // ConnectionState contains information about the state of the connection. - ConnectionState = qtls.ConnectionStateWith0RTT - // EncryptionLevel is the encryption level of a message. - EncryptionLevel = qtls.EncryptionLevel - // Extension is a TLS extension - Extension = qtls.Extension - // ExtraConfig is the qtls.ExtraConfig - ExtraConfig = qtls.ExtraConfig - // RecordLayer is a qtls RecordLayer. - RecordLayer = qtls.RecordLayer + QUICConn = qtls.QUICConn + QUICConfig = qtls.QUICConfig + QUICEvent = qtls.QUICEvent + QUICEventKind = qtls.QUICEventKind + QUICEncryptionLevel = qtls.QUICEncryptionLevel + AlertError = qtls.AlertError ) const ( - // EncryptionHandshake is the Handshake encryption level - EncryptionHandshake = qtls.EncryptionHandshake - // Encryption0RTT is the 0-RTT encryption level - Encryption0RTT = qtls.Encryption0RTT - // EncryptionApplication is the application data encryption level - EncryptionApplication = qtls.EncryptionApplication + QUICEncryptionLevelInitial = qtls.QUICEncryptionLevelInitial + QUICEncryptionLevelEarly = qtls.QUICEncryptionLevelEarly + QUICEncryptionLevelHandshake = qtls.QUICEncryptionLevelHandshake + QUICEncryptionLevelApplication = qtls.QUICEncryptionLevelApplication ) -// AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3 -func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD { - return qtls.AEADAESGCMTLS13(key, fixedNonce) +const ( + QUICNoEvent = qtls.QUICNoEvent + QUICSetReadSecret = qtls.QUICSetReadSecret + QUICSetWriteSecret = qtls.QUICSetWriteSecret + QUICWriteData = qtls.QUICWriteData + QUICTransportParameters = qtls.QUICTransportParameters + QUICTransportParametersRequired = qtls.QUICTransportParametersRequired + QUICRejectedEarlyData = qtls.QUICRejectedEarlyData + QUICHandshakeDone = qtls.QUICHandshakeDone +) + +func SetupConfigForServer(conf *QUICConfig, enable0RTT bool, getDataForSessionTicket func() []byte, handleSessionTicket func([]byte, bool) bool) { + qtls.InitSessionTicketKeys(conf.TLSConfig) + conf.TLSConfig = conf.TLSConfig.Clone() + conf.TLSConfig.MinVersion = tls.VersionTLS13 + conf.ExtraConfig = &qtls.ExtraConfig{ + Enable0RTT: enable0RTT, + Accept0RTT: func(data []byte) bool { + return handleSessionTicket(data, true) + }, + GetAppDataForSessionTicket: getDataForSessionTicket, + } } -// Client returns a new TLS client side connection. -func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { - return qtls.Client(conn, config, extraConfig) +func SetupConfigForClient(conf *QUICConfig, getDataForSessionState func() []byte, setDataFromSessionState func([]byte)) { + conf.ExtraConfig = &qtls.ExtraConfig{ + GetAppDataForSessionState: getDataForSessionState, + SetAppDataFromSessionState: setDataFromSessionState, + } } -// Server returns a new TLS server side connection. -func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { - return qtls.Server(conn, config, extraConfig) +func QUICServer(config *QUICConfig) *QUICConn { + return qtls.QUICServer(config) } -func GetConnectionState(conn *Conn) ConnectionState { - return conn.ConnectionStateWith0RTT() +func QUICClient(config *QUICConfig) *QUICConn { + return qtls.QUICClient(config) } -// ToTLSConnectionState extracts the tls.ConnectionState -func ToTLSConnectionState(cs ConnectionState) tls.ConnectionState { - return cs.ConnectionState +func ToTLSEncryptionLevel(e protocol.EncryptionLevel) qtls.QUICEncryptionLevel { + switch e { + case protocol.EncryptionInitial: + return qtls.QUICEncryptionLevelInitial + case protocol.EncryptionHandshake: + return qtls.QUICEncryptionLevelHandshake + case protocol.Encryption1RTT: + return qtls.QUICEncryptionLevelApplication + case protocol.Encryption0RTT: + return qtls.QUICEncryptionLevelEarly + default: + panic(fmt.Sprintf("unexpected encryption level: %s", e)) + } } -type cipherSuiteTLS13 struct { - ID uint16 - KeyLen int - AEAD func(key, fixedNonce []byte) cipher.AEAD - Hash crypto.Hash -} - -//go:linkname cipherSuiteTLS13ByID github.com/quic-go/qtls-go1-20.cipherSuiteTLS13ByID -func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 - -// CipherSuiteTLS13ByID gets a TLS 1.3 cipher suite. -func CipherSuiteTLS13ByID(id uint16) *CipherSuiteTLS13 { - val := cipherSuiteTLS13ByID(id) - cs := (*cipherSuiteTLS13)(unsafe.Pointer(val)) - return &qtls.CipherSuiteTLS13{ - ID: cs.ID, - KeyLen: cs.KeyLen, - AEAD: cs.AEAD, - Hash: cs.Hash, +func FromTLSEncryptionLevel(e qtls.QUICEncryptionLevel) protocol.EncryptionLevel { + switch e { + case qtls.QUICEncryptionLevelInitial: + return protocol.EncryptionInitial + case qtls.QUICEncryptionLevelHandshake: + return protocol.EncryptionHandshake + case qtls.QUICEncryptionLevelApplication: + return protocol.Encryption1RTT + case qtls.QUICEncryptionLevelEarly: + return protocol.Encryption0RTT + default: + panic(fmt.Sprintf("unexpect encryption level: %s", e)) } } @@ -143,3 +141,7 @@ func SetCipherSuite(id uint16) (reset func()) { cipherSuitesModified = false } } + +func SendSessionTicket(c *QUICConn, allow0RTT bool) error { + return c.SendSessionTicket(allow0RTT) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/qtls/go121.go b/vendor/github.com/quic-go/quic-go/internal/qtls/go121.go index b33406397..65a274ac0 100644 --- a/vendor/github.com/quic-go/quic-go/internal/qtls/go121.go +++ b/vendor/github.com/quic-go/quic-go/internal/qtls/go121.go @@ -2,4 +2,158 @@ package qtls -var _ int = "The version of quic-go you're using can't be built on Go 1.21 yet. For more details, please see https://github.com/quic-go/quic-go/wiki/quic-go-and-Go-versions." +import ( + "bytes" + "crypto/tls" + "fmt" + + "github.com/quic-go/quic-go/internal/protocol" +) + +type ( + QUICConn = tls.QUICConn + QUICConfig = tls.QUICConfig + QUICEvent = tls.QUICEvent + QUICEventKind = tls.QUICEventKind + QUICEncryptionLevel = tls.QUICEncryptionLevel + QUICSessionTicketOptions = tls.QUICSessionTicketOptions + AlertError = tls.AlertError +) + +const ( + QUICEncryptionLevelInitial = tls.QUICEncryptionLevelInitial + QUICEncryptionLevelEarly = tls.QUICEncryptionLevelEarly + QUICEncryptionLevelHandshake = tls.QUICEncryptionLevelHandshake + QUICEncryptionLevelApplication = tls.QUICEncryptionLevelApplication +) + +const ( + QUICNoEvent = tls.QUICNoEvent + QUICSetReadSecret = tls.QUICSetReadSecret + QUICSetWriteSecret = tls.QUICSetWriteSecret + QUICWriteData = tls.QUICWriteData + QUICTransportParameters = tls.QUICTransportParameters + QUICTransportParametersRequired = tls.QUICTransportParametersRequired + QUICRejectedEarlyData = tls.QUICRejectedEarlyData + QUICHandshakeDone = tls.QUICHandshakeDone +) + +func QUICServer(config *QUICConfig) *QUICConn { return tls.QUICServer(config) } +func QUICClient(config *QUICConfig) *QUICConn { return tls.QUICClient(config) } + +func SetupConfigForServer(qconf *QUICConfig, _ bool, getData func() []byte, handleSessionTicket func([]byte, bool) bool) { + conf := qconf.TLSConfig + + // Workaround for https://github.com/golang/go/issues/60506. + // This initializes the session tickets _before_ cloning the config. + _, _ = conf.DecryptTicket(nil, tls.ConnectionState{}) + + conf = conf.Clone() + conf.MinVersion = tls.VersionTLS13 + qconf.TLSConfig = conf + + // add callbacks to save transport parameters into the session ticket + origWrapSession := conf.WrapSession + conf.WrapSession = func(cs tls.ConnectionState, state *tls.SessionState) ([]byte, error) { + // Add QUIC session ticket + state.Extra = append(state.Extra, addExtraPrefix(getData())) + + if origWrapSession != nil { + return origWrapSession(cs, state) + } + b, err := conf.EncryptTicket(cs, state) + return b, err + } + origUnwrapSession := conf.UnwrapSession + // UnwrapSession might be called multiple times, as the client can use multiple session tickets. + // However, using 0-RTT is only possible with the first session ticket. + // crypto/tls guarantees that this callback is called in the same order as the session ticket in the ClientHello. + var unwrapCount int + conf.UnwrapSession = func(identity []byte, connState tls.ConnectionState) (*tls.SessionState, error) { + unwrapCount++ + var state *tls.SessionState + var err error + if origUnwrapSession != nil { + state, err = origUnwrapSession(identity, connState) + } else { + state, err = conf.DecryptTicket(identity, connState) + } + if err != nil || state == nil { + return nil, err + } + + extra := findExtraData(state.Extra) + if extra != nil { + state.EarlyData = handleSessionTicket(extra, state.EarlyData && unwrapCount == 1) + } else { + state.EarlyData = false + } + + return state, nil + } +} + +func SetupConfigForClient(qconf *QUICConfig, getData func() []byte, setData func([]byte)) { + conf := qconf.TLSConfig + if conf.ClientSessionCache != nil { + origCache := conf.ClientSessionCache + conf.ClientSessionCache = &clientSessionCache{ + wrapped: origCache, + getData: getData, + setData: setData, + } + } +} + +func ToTLSEncryptionLevel(e protocol.EncryptionLevel) tls.QUICEncryptionLevel { + switch e { + case protocol.EncryptionInitial: + return tls.QUICEncryptionLevelInitial + case protocol.EncryptionHandshake: + return tls.QUICEncryptionLevelHandshake + case protocol.Encryption1RTT: + return tls.QUICEncryptionLevelApplication + case protocol.Encryption0RTT: + return tls.QUICEncryptionLevelEarly + default: + panic(fmt.Sprintf("unexpected encryption level: %s", e)) + } +} + +func FromTLSEncryptionLevel(e tls.QUICEncryptionLevel) protocol.EncryptionLevel { + switch e { + case tls.QUICEncryptionLevelInitial: + return protocol.EncryptionInitial + case tls.QUICEncryptionLevelHandshake: + return protocol.EncryptionHandshake + case tls.QUICEncryptionLevelApplication: + return protocol.Encryption1RTT + case tls.QUICEncryptionLevelEarly: + return protocol.Encryption0RTT + default: + panic(fmt.Sprintf("unexpect encryption level: %s", e)) + } +} + +const extraPrefix = "quic-go1" + +func addExtraPrefix(b []byte) []byte { + return append([]byte(extraPrefix), b...) +} + +func findExtraData(extras [][]byte) []byte { + prefix := []byte(extraPrefix) + for _, extra := range extras { + if len(extra) < len(prefix) || !bytes.Equal(prefix, extra[:len(prefix)]) { + continue + } + return extra[len(prefix):] + } + return nil +} + +func SendSessionTicket(c *QUICConn, allow0RTT bool) error { + return c.SendSessionTicket(tls.QUICSessionTicketOptions{ + EarlyData: allow0RTT, + }) +} diff --git a/vendor/github.com/quic-go/quic-go/internal/qtls/go_oldversion.go b/vendor/github.com/quic-go/quic-go/internal/qtls/go_oldversion.go index e15f03629..0fca80a38 100644 --- a/vendor/github.com/quic-go/quic-go/internal/qtls/go_oldversion.go +++ b/vendor/github.com/quic-go/quic-go/internal/qtls/go_oldversion.go @@ -1,4 +1,4 @@ -//go:build !go1.19 +//go:build !go1.20 package qtls diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/header.go b/vendor/github.com/quic-go/quic-go/internal/wire/header.go index 37f48cced..0c60f4dd9 100644 --- a/vendor/github.com/quic-go/quic-go/internal/wire/header.go +++ b/vendor/github.com/quic-go/quic-go/internal/wire/header.go @@ -74,6 +74,10 @@ func parseArbitraryLenConnectionIDs(r *bytes.Reader) (dest, src protocol.Arbitra return destConnID, srcConnID, nil } +func IsPotentialQUICPacket(firstByte byte) bool { + return firstByte&0x40 > 0 +} + // IsLongHeaderPacket says if this is a Long Header packet func IsLongHeaderPacket(firstByte byte) bool { return firstByte&0x80 > 0 @@ -108,7 +112,7 @@ func Is0RTTPacket(b []byte) bool { version := protocol.VersionNumber(binary.BigEndian.Uint32(b[1:5])) //nolint:exhaustive // We only need to test QUIC versions that we support. switch version { - case protocol.Version1, protocol.VersionDraft29: + case protocol.Version1: return b[0]>>4&0b11 == 0b01 case protocol.Version2: return b[0]>>4&0b11 == 0b10 diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go b/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go index 1ec1e59d3..7226521b0 100644 --- a/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go +++ b/vendor/github.com/quic-go/quic-go/internal/wire/transport_parameters.go @@ -454,6 +454,10 @@ func (p *TransportParameters) MarshalForSessionTicket(b []byte) []byte { b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum)) // initial_max_uni_streams b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum)) + // max_datagram_frame_size + if p.MaxDatagramFrameSize != protocol.InvalidByteCount { + b = p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize)) + } // active_connection_id_limit return p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) } @@ -472,6 +476,9 @@ func (p *TransportParameters) UnmarshalFromSessionTicket(r *bytes.Reader) error // ValidFor0RTT checks if the transport parameters match those saved in the session ticket. func (p *TransportParameters) ValidFor0RTT(saved *TransportParameters) bool { + if saved.MaxDatagramFrameSize != protocol.InvalidByteCount && (p.MaxDatagramFrameSize == protocol.InvalidByteCount || p.MaxDatagramFrameSize < saved.MaxDatagramFrameSize) { + return false + } return p.InitialMaxStreamDataBidiLocal >= saved.InitialMaxStreamDataBidiLocal && p.InitialMaxStreamDataBidiRemote >= saved.InitialMaxStreamDataBidiRemote && p.InitialMaxStreamDataUni >= saved.InitialMaxStreamDataUni && @@ -481,6 +488,21 @@ func (p *TransportParameters) ValidFor0RTT(saved *TransportParameters) bool { p.ActiveConnectionIDLimit == saved.ActiveConnectionIDLimit } +// ValidForUpdate checks that the new transport parameters don't reduce limits after resuming a 0-RTT connection. +// It is only used on the client side. +func (p *TransportParameters) ValidForUpdate(saved *TransportParameters) bool { + if saved.MaxDatagramFrameSize != protocol.InvalidByteCount && (p.MaxDatagramFrameSize == protocol.InvalidByteCount || p.MaxDatagramFrameSize < saved.MaxDatagramFrameSize) { + return false + } + return p.ActiveConnectionIDLimit >= saved.ActiveConnectionIDLimit && + p.InitialMaxData >= saved.InitialMaxData && + p.InitialMaxStreamDataBidiLocal >= saved.InitialMaxStreamDataBidiLocal && + p.InitialMaxStreamDataBidiRemote >= saved.InitialMaxStreamDataBidiRemote && + p.InitialMaxStreamDataUni >= saved.InitialMaxStreamDataUni && + p.MaxBidiStreamNum >= saved.MaxBidiStreamNum && + p.MaxUniStreamNum >= saved.MaxUniStreamNum +} + // String returns a string representation, intended for logging. func (p *TransportParameters) String() string { logString := "&wire.TransportParameters{OriginalDestinationConnectionID: %s, InitialSourceConnectionID: %s, " diff --git a/vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go b/vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go index 3dc621135..afde70fa4 100644 --- a/vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go +++ b/vendor/github.com/quic-go/quic-go/internal/wire/version_negotiation.go @@ -40,7 +40,10 @@ func ComposeVersionNegotiation(destConnID, srcConnID protocol.ArbitraryLenConnec buf := bytes.NewBuffer(make([]byte, 0, expectedLen)) r := make([]byte, 1) _, _ = rand.Read(r) // ignore the error here. It is not critical to have perfect random here. - buf.WriteByte(r[0] | 0x80) + // Setting the "QUIC bit" (0x40) is not required by the RFC, + // but it allows clients to demultiplex QUIC with a long list of other protocols. + // See RFC 9443 and https://mailarchive.ietf.org/arch/msg/quic/oR4kxGKY6mjtPC1CZegY1ED4beg/ for details. + buf.WriteByte(r[0] | 0xc0) utils.BigEndian.WriteUint32(buf, 0) // version 0 buf.WriteByte(uint8(destConnID.Len())) buf.Write(destConnID.Bytes()) diff --git a/vendor/github.com/quic-go/quic-go/logging/connection_tracer.go b/vendor/github.com/quic-go/quic-go/logging/connection_tracer.go new file mode 100644 index 000000000..e3f322d91 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/logging/connection_tracer.go @@ -0,0 +1,255 @@ +package logging + +import ( + "net" + "time" +) + +// A ConnectionTracer records events. +type ConnectionTracer struct { + StartedConnection func(local, remote net.Addr, srcConnID, destConnID ConnectionID) + NegotiatedVersion func(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) + ClosedConnection func(error) + SentTransportParameters func(*TransportParameters) + ReceivedTransportParameters func(*TransportParameters) + RestoredTransportParameters func(parameters *TransportParameters) // for 0-RTT + SentLongHeaderPacket func(*ExtendedHeader, ByteCount, ECN, *AckFrame, []Frame) + SentShortHeaderPacket func(*ShortHeader, ByteCount, ECN, *AckFrame, []Frame) + ReceivedVersionNegotiationPacket func(dest, src ArbitraryLenConnectionID, _ []VersionNumber) + ReceivedRetry func(*Header) + ReceivedLongHeaderPacket func(*ExtendedHeader, ByteCount, ECN, []Frame) + ReceivedShortHeaderPacket func(*ShortHeader, ByteCount, ECN, []Frame) + BufferedPacket func(PacketType, ByteCount) + DroppedPacket func(PacketType, ByteCount, PacketDropReason) + UpdatedMetrics func(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int) + AcknowledgedPacket func(EncryptionLevel, PacketNumber) + LostPacket func(EncryptionLevel, PacketNumber, PacketLossReason) + UpdatedCongestionState func(CongestionState) + UpdatedPTOCount func(value uint32) + UpdatedKeyFromTLS func(EncryptionLevel, Perspective) + UpdatedKey func(generation KeyPhase, remote bool) + DroppedEncryptionLevel func(EncryptionLevel) + DroppedKey func(generation KeyPhase) + SetLossTimer func(TimerType, EncryptionLevel, time.Time) + LossTimerExpired func(TimerType, EncryptionLevel) + LossTimerCanceled func() + ECNStateUpdated func(state ECNState, trigger ECNStateTrigger) + // Close is called when the connection is closed. + Close func() + Debug func(name, msg string) +} + +// NewMultiplexedConnectionTracer creates a new connection tracer that multiplexes events to multiple tracers. +func NewMultiplexedConnectionTracer(tracers ...*ConnectionTracer) *ConnectionTracer { + if len(tracers) == 0 { + return nil + } + if len(tracers) == 1 { + return tracers[0] + } + return &ConnectionTracer{ + StartedConnection: func(local, remote net.Addr, srcConnID, destConnID ConnectionID) { + for _, t := range tracers { + if t.StartedConnection != nil { + t.StartedConnection(local, remote, srcConnID, destConnID) + } + } + }, + NegotiatedVersion: func(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) { + for _, t := range tracers { + if t.NegotiatedVersion != nil { + t.NegotiatedVersion(chosen, clientVersions, serverVersions) + } + } + }, + ClosedConnection: func(e error) { + for _, t := range tracers { + if t.ClosedConnection != nil { + t.ClosedConnection(e) + } + } + }, + SentTransportParameters: func(tp *TransportParameters) { + for _, t := range tracers { + if t.SentTransportParameters != nil { + t.SentTransportParameters(tp) + } + } + }, + ReceivedTransportParameters: func(tp *TransportParameters) { + for _, t := range tracers { + if t.ReceivedTransportParameters != nil { + t.ReceivedTransportParameters(tp) + } + } + }, + RestoredTransportParameters: func(tp *TransportParameters) { + for _, t := range tracers { + if t.RestoredTransportParameters != nil { + t.RestoredTransportParameters(tp) + } + } + }, + SentLongHeaderPacket: func(hdr *ExtendedHeader, size ByteCount, ecn ECN, ack *AckFrame, frames []Frame) { + for _, t := range tracers { + if t.SentLongHeaderPacket != nil { + t.SentLongHeaderPacket(hdr, size, ecn, ack, frames) + } + } + }, + SentShortHeaderPacket: func(hdr *ShortHeader, size ByteCount, ecn ECN, ack *AckFrame, frames []Frame) { + for _, t := range tracers { + if t.SentShortHeaderPacket != nil { + t.SentShortHeaderPacket(hdr, size, ecn, ack, frames) + } + } + }, + ReceivedVersionNegotiationPacket: func(dest, src ArbitraryLenConnectionID, versions []VersionNumber) { + for _, t := range tracers { + if t.ReceivedVersionNegotiationPacket != nil { + t.ReceivedVersionNegotiationPacket(dest, src, versions) + } + } + }, + ReceivedRetry: func(hdr *Header) { + for _, t := range tracers { + if t.ReceivedRetry != nil { + t.ReceivedRetry(hdr) + } + } + }, + ReceivedLongHeaderPacket: func(hdr *ExtendedHeader, size ByteCount, ecn ECN, frames []Frame) { + for _, t := range tracers { + if t.ReceivedLongHeaderPacket != nil { + t.ReceivedLongHeaderPacket(hdr, size, ecn, frames) + } + } + }, + ReceivedShortHeaderPacket: func(hdr *ShortHeader, size ByteCount, ecn ECN, frames []Frame) { + for _, t := range tracers { + if t.ReceivedShortHeaderPacket != nil { + t.ReceivedShortHeaderPacket(hdr, size, ecn, frames) + } + } + }, + BufferedPacket: func(typ PacketType, size ByteCount) { + for _, t := range tracers { + if t.BufferedPacket != nil { + t.BufferedPacket(typ, size) + } + } + }, + DroppedPacket: func(typ PacketType, size ByteCount, reason PacketDropReason) { + for _, t := range tracers { + if t.DroppedPacket != nil { + t.DroppedPacket(typ, size, reason) + } + } + }, + UpdatedMetrics: func(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int) { + for _, t := range tracers { + if t.UpdatedMetrics != nil { + t.UpdatedMetrics(rttStats, cwnd, bytesInFlight, packetsInFlight) + } + } + }, + AcknowledgedPacket: func(encLevel EncryptionLevel, pn PacketNumber) { + for _, t := range tracers { + if t.AcknowledgedPacket != nil { + t.AcknowledgedPacket(encLevel, pn) + } + } + }, + LostPacket: func(encLevel EncryptionLevel, pn PacketNumber, reason PacketLossReason) { + for _, t := range tracers { + if t.LostPacket != nil { + t.LostPacket(encLevel, pn, reason) + } + } + }, + UpdatedCongestionState: func(state CongestionState) { + for _, t := range tracers { + if t.UpdatedCongestionState != nil { + t.UpdatedCongestionState(state) + } + } + }, + UpdatedPTOCount: func(value uint32) { + for _, t := range tracers { + if t.UpdatedPTOCount != nil { + t.UpdatedPTOCount(value) + } + } + }, + UpdatedKeyFromTLS: func(encLevel EncryptionLevel, perspective Perspective) { + for _, t := range tracers { + if t.UpdatedKeyFromTLS != nil { + t.UpdatedKeyFromTLS(encLevel, perspective) + } + } + }, + UpdatedKey: func(generation KeyPhase, remote bool) { + for _, t := range tracers { + if t.UpdatedKey != nil { + t.UpdatedKey(generation, remote) + } + } + }, + DroppedEncryptionLevel: func(encLevel EncryptionLevel) { + for _, t := range tracers { + if t.DroppedEncryptionLevel != nil { + t.DroppedEncryptionLevel(encLevel) + } + } + }, + DroppedKey: func(generation KeyPhase) { + for _, t := range tracers { + if t.DroppedKey != nil { + t.DroppedKey(generation) + } + } + }, + SetLossTimer: func(typ TimerType, encLevel EncryptionLevel, exp time.Time) { + for _, t := range tracers { + if t.SetLossTimer != nil { + t.SetLossTimer(typ, encLevel, exp) + } + } + }, + LossTimerExpired: func(typ TimerType, encLevel EncryptionLevel) { + for _, t := range tracers { + if t.LossTimerExpired != nil { + t.LossTimerExpired(typ, encLevel) + } + } + }, + LossTimerCanceled: func() { + for _, t := range tracers { + if t.LossTimerCanceled != nil { + t.LossTimerCanceled() + } + } + }, + ECNStateUpdated: func(state ECNState, trigger ECNStateTrigger) { + for _, t := range tracers { + if t.ECNStateUpdated != nil { + t.ECNStateUpdated(state, trigger) + } + } + }, + Close: func() { + for _, t := range tracers { + if t.Close != nil { + t.Close() + } + } + }, + Debug: func(name, msg string) { + for _, t := range tracers { + if t.Debug != nil { + t.Debug(name, msg) + } + } + }, + } +} diff --git a/vendor/github.com/quic-go/quic-go/logging/interface.go b/vendor/github.com/quic-go/quic-go/logging/interface.go index 2ce8582ec..10ac038fb 100644 --- a/vendor/github.com/quic-go/quic-go/logging/interface.go +++ b/vendor/github.com/quic-go/quic-go/logging/interface.go @@ -3,9 +3,6 @@ package logging import ( - "net" - "time" - "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" @@ -15,6 +12,8 @@ import ( type ( // A ByteCount is used to count bytes. ByteCount = protocol.ByteCount + // ECN is the ECN value + ECN = protocol.ECN // A ConnectionID is a QUIC Connection ID. ConnectionID = protocol.ConnectionID // An ArbitraryLenConnectionID is a QUIC Connection ID that can be up to 255 bytes long. @@ -58,6 +57,19 @@ type ( RTTStats = utils.RTTStats ) +const ( + // ECNUnsupported means that no ECN value was set / received + ECNUnsupported = protocol.ECNUnsupported + // ECTNot is Not-ECT + ECTNot = protocol.ECNNon + // ECT0 is ECT(0) + ECT0 = protocol.ECT0 + // ECT1 is ECT(1) + ECT1 = protocol.ECT1 + // ECNCE is CE + ECNCE = protocol.ECNCE +) + const ( // KeyPhaseZero is key phase bit 0 KeyPhaseZero KeyPhaseBit = protocol.KeyPhaseZero @@ -97,43 +109,3 @@ type ShortHeader struct { PacketNumberLen protocol.PacketNumberLen KeyPhase KeyPhaseBit } - -// A Tracer traces events. -type Tracer interface { - SentPacket(net.Addr, *Header, ByteCount, []Frame) - SentVersionNegotiationPacket(_ net.Addr, dest, src ArbitraryLenConnectionID, _ []VersionNumber) - DroppedPacket(net.Addr, PacketType, ByteCount, PacketDropReason) -} - -// A ConnectionTracer records events. -type ConnectionTracer interface { - StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID) - NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) - ClosedConnection(error) - SentTransportParameters(*TransportParameters) - ReceivedTransportParameters(*TransportParameters) - RestoredTransportParameters(parameters *TransportParameters) // for 0-RTT - SentLongHeaderPacket(hdr *ExtendedHeader, size ByteCount, ack *AckFrame, frames []Frame) - SentShortHeaderPacket(hdr *ShortHeader, size ByteCount, ack *AckFrame, frames []Frame) - ReceivedVersionNegotiationPacket(dest, src ArbitraryLenConnectionID, _ []VersionNumber) - ReceivedRetry(*Header) - ReceivedLongHeaderPacket(hdr *ExtendedHeader, size ByteCount, frames []Frame) - ReceivedShortHeaderPacket(hdr *ShortHeader, size ByteCount, frames []Frame) - BufferedPacket(PacketType, ByteCount) - DroppedPacket(PacketType, ByteCount, PacketDropReason) - UpdatedMetrics(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int) - AcknowledgedPacket(EncryptionLevel, PacketNumber) - LostPacket(EncryptionLevel, PacketNumber, PacketLossReason) - UpdatedCongestionState(CongestionState) - UpdatedPTOCount(value uint32) - UpdatedKeyFromTLS(EncryptionLevel, Perspective) - UpdatedKey(generation KeyPhase, remote bool) - DroppedEncryptionLevel(EncryptionLevel) - DroppedKey(generation KeyPhase) - SetLossTimer(TimerType, EncryptionLevel, time.Time) - LossTimerExpired(TimerType, EncryptionLevel) - LossTimerCanceled() - // Close is called when the connection is closed. - Close() - Debug(name, msg string) -} diff --git a/vendor/github.com/quic-go/quic-go/logging/mockgen.go b/vendor/github.com/quic-go/quic-go/logging/mockgen.go deleted file mode 100644 index d50916799..000000000 --- a/vendor/github.com/quic-go/quic-go/logging/mockgen.go +++ /dev/null @@ -1,4 +0,0 @@ -package logging - -//go:generate sh -c "go run github.com/golang/mock/mockgen -package logging -self_package github.com/quic-go/quic-go/logging -destination mock_connection_tracer_test.go github.com/quic-go/quic-go/logging ConnectionTracer" -//go:generate sh -c "go run github.com/golang/mock/mockgen -package logging -self_package github.com/quic-go/quic-go/logging -destination mock_tracer_test.go github.com/quic-go/quic-go/logging Tracer" diff --git a/vendor/github.com/quic-go/quic-go/logging/multiplex.go b/vendor/github.com/quic-go/quic-go/logging/multiplex.go deleted file mode 100644 index 672a5cdbd..000000000 --- a/vendor/github.com/quic-go/quic-go/logging/multiplex.go +++ /dev/null @@ -1,226 +0,0 @@ -package logging - -import ( - "net" - "time" -) - -type tracerMultiplexer struct { - tracers []Tracer -} - -var _ Tracer = &tracerMultiplexer{} - -// NewMultiplexedTracer creates a new tracer that multiplexes events to multiple tracers. -func NewMultiplexedTracer(tracers ...Tracer) Tracer { - if len(tracers) == 0 { - return nil - } - if len(tracers) == 1 { - return tracers[0] - } - return &tracerMultiplexer{tracers} -} - -func (m *tracerMultiplexer) SentPacket(remote net.Addr, hdr *Header, size ByteCount, frames []Frame) { - for _, t := range m.tracers { - t.SentPacket(remote, hdr, size, frames) - } -} - -func (m *tracerMultiplexer) SentVersionNegotiationPacket(remote net.Addr, dest, src ArbitraryLenConnectionID, versions []VersionNumber) { - for _, t := range m.tracers { - t.SentVersionNegotiationPacket(remote, dest, src, versions) - } -} - -func (m *tracerMultiplexer) DroppedPacket(remote net.Addr, typ PacketType, size ByteCount, reason PacketDropReason) { - for _, t := range m.tracers { - t.DroppedPacket(remote, typ, size, reason) - } -} - -type connTracerMultiplexer struct { - tracers []ConnectionTracer -} - -var _ ConnectionTracer = &connTracerMultiplexer{} - -// NewMultiplexedConnectionTracer creates a new connection tracer that multiplexes events to multiple tracers. -func NewMultiplexedConnectionTracer(tracers ...ConnectionTracer) ConnectionTracer { - if len(tracers) == 0 { - return nil - } - if len(tracers) == 1 { - return tracers[0] - } - return &connTracerMultiplexer{tracers: tracers} -} - -func (m *connTracerMultiplexer) StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID) { - for _, t := range m.tracers { - t.StartedConnection(local, remote, srcConnID, destConnID) - } -} - -func (m *connTracerMultiplexer) NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) { - for _, t := range m.tracers { - t.NegotiatedVersion(chosen, clientVersions, serverVersions) - } -} - -func (m *connTracerMultiplexer) ClosedConnection(e error) { - for _, t := range m.tracers { - t.ClosedConnection(e) - } -} - -func (m *connTracerMultiplexer) SentTransportParameters(tp *TransportParameters) { - for _, t := range m.tracers { - t.SentTransportParameters(tp) - } -} - -func (m *connTracerMultiplexer) ReceivedTransportParameters(tp *TransportParameters) { - for _, t := range m.tracers { - t.ReceivedTransportParameters(tp) - } -} - -func (m *connTracerMultiplexer) RestoredTransportParameters(tp *TransportParameters) { - for _, t := range m.tracers { - t.RestoredTransportParameters(tp) - } -} - -func (m *connTracerMultiplexer) SentLongHeaderPacket(hdr *ExtendedHeader, size ByteCount, ack *AckFrame, frames []Frame) { - for _, t := range m.tracers { - t.SentLongHeaderPacket(hdr, size, ack, frames) - } -} - -func (m *connTracerMultiplexer) SentShortHeaderPacket(hdr *ShortHeader, size ByteCount, ack *AckFrame, frames []Frame) { - for _, t := range m.tracers { - t.SentShortHeaderPacket(hdr, size, ack, frames) - } -} - -func (m *connTracerMultiplexer) ReceivedVersionNegotiationPacket(dest, src ArbitraryLenConnectionID, versions []VersionNumber) { - for _, t := range m.tracers { - t.ReceivedVersionNegotiationPacket(dest, src, versions) - } -} - -func (m *connTracerMultiplexer) ReceivedRetry(hdr *Header) { - for _, t := range m.tracers { - t.ReceivedRetry(hdr) - } -} - -func (m *connTracerMultiplexer) ReceivedLongHeaderPacket(hdr *ExtendedHeader, size ByteCount, frames []Frame) { - for _, t := range m.tracers { - t.ReceivedLongHeaderPacket(hdr, size, frames) - } -} - -func (m *connTracerMultiplexer) ReceivedShortHeaderPacket(hdr *ShortHeader, size ByteCount, frames []Frame) { - for _, t := range m.tracers { - t.ReceivedShortHeaderPacket(hdr, size, frames) - } -} - -func (m *connTracerMultiplexer) BufferedPacket(typ PacketType, size ByteCount) { - for _, t := range m.tracers { - t.BufferedPacket(typ, size) - } -} - -func (m *connTracerMultiplexer) DroppedPacket(typ PacketType, size ByteCount, reason PacketDropReason) { - for _, t := range m.tracers { - t.DroppedPacket(typ, size, reason) - } -} - -func (m *connTracerMultiplexer) UpdatedCongestionState(state CongestionState) { - for _, t := range m.tracers { - t.UpdatedCongestionState(state) - } -} - -func (m *connTracerMultiplexer) UpdatedMetrics(rttStats *RTTStats, cwnd, bytesInFLight ByteCount, packetsInFlight int) { - for _, t := range m.tracers { - t.UpdatedMetrics(rttStats, cwnd, bytesInFLight, packetsInFlight) - } -} - -func (m *connTracerMultiplexer) AcknowledgedPacket(encLevel EncryptionLevel, pn PacketNumber) { - for _, t := range m.tracers { - t.AcknowledgedPacket(encLevel, pn) - } -} - -func (m *connTracerMultiplexer) LostPacket(encLevel EncryptionLevel, pn PacketNumber, reason PacketLossReason) { - for _, t := range m.tracers { - t.LostPacket(encLevel, pn, reason) - } -} - -func (m *connTracerMultiplexer) UpdatedPTOCount(value uint32) { - for _, t := range m.tracers { - t.UpdatedPTOCount(value) - } -} - -func (m *connTracerMultiplexer) UpdatedKeyFromTLS(encLevel EncryptionLevel, perspective Perspective) { - for _, t := range m.tracers { - t.UpdatedKeyFromTLS(encLevel, perspective) - } -} - -func (m *connTracerMultiplexer) UpdatedKey(generation KeyPhase, remote bool) { - for _, t := range m.tracers { - t.UpdatedKey(generation, remote) - } -} - -func (m *connTracerMultiplexer) DroppedEncryptionLevel(encLevel EncryptionLevel) { - for _, t := range m.tracers { - t.DroppedEncryptionLevel(encLevel) - } -} - -func (m *connTracerMultiplexer) DroppedKey(generation KeyPhase) { - for _, t := range m.tracers { - t.DroppedKey(generation) - } -} - -func (m *connTracerMultiplexer) SetLossTimer(typ TimerType, encLevel EncryptionLevel, exp time.Time) { - for _, t := range m.tracers { - t.SetLossTimer(typ, encLevel, exp) - } -} - -func (m *connTracerMultiplexer) LossTimerExpired(typ TimerType, encLevel EncryptionLevel) { - for _, t := range m.tracers { - t.LossTimerExpired(typ, encLevel) - } -} - -func (m *connTracerMultiplexer) LossTimerCanceled() { - for _, t := range m.tracers { - t.LossTimerCanceled() - } -} - -func (m *connTracerMultiplexer) Debug(name, msg string) { - for _, t := range m.tracers { - t.Debug(name, msg) - } -} - -func (m *connTracerMultiplexer) Close() { - for _, t := range m.tracers { - t.Close() - } -} diff --git a/vendor/github.com/quic-go/quic-go/logging/null_tracer.go b/vendor/github.com/quic-go/quic-go/logging/null_tracer.go deleted file mode 100644 index de9703857..000000000 --- a/vendor/github.com/quic-go/quic-go/logging/null_tracer.go +++ /dev/null @@ -1,58 +0,0 @@ -package logging - -import ( - "net" - "time" -) - -// The NullTracer is a Tracer that does nothing. -// It is useful for embedding. -type NullTracer struct{} - -var _ Tracer = &NullTracer{} - -func (n NullTracer) SentPacket(net.Addr, *Header, ByteCount, []Frame) {} -func (n NullTracer) SentVersionNegotiationPacket(_ net.Addr, dest, src ArbitraryLenConnectionID, _ []VersionNumber) { -} -func (n NullTracer) DroppedPacket(net.Addr, PacketType, ByteCount, PacketDropReason) {} - -// The NullConnectionTracer is a ConnectionTracer that does nothing. -// It is useful for embedding. -type NullConnectionTracer struct{} - -var _ ConnectionTracer = &NullConnectionTracer{} - -func (n NullConnectionTracer) StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID) { -} - -func (n NullConnectionTracer) NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) { -} -func (n NullConnectionTracer) ClosedConnection(err error) {} -func (n NullConnectionTracer) SentTransportParameters(*TransportParameters) {} -func (n NullConnectionTracer) ReceivedTransportParameters(*TransportParameters) {} -func (n NullConnectionTracer) RestoredTransportParameters(*TransportParameters) {} -func (n NullConnectionTracer) SentLongHeaderPacket(*ExtendedHeader, ByteCount, *AckFrame, []Frame) {} -func (n NullConnectionTracer) SentShortHeaderPacket(*ShortHeader, ByteCount, *AckFrame, []Frame) {} -func (n NullConnectionTracer) ReceivedVersionNegotiationPacket(dest, src ArbitraryLenConnectionID, _ []VersionNumber) { -} -func (n NullConnectionTracer) ReceivedRetry(*Header) {} -func (n NullConnectionTracer) ReceivedLongHeaderPacket(*ExtendedHeader, ByteCount, []Frame) {} -func (n NullConnectionTracer) ReceivedShortHeaderPacket(*ShortHeader, ByteCount, []Frame) {} -func (n NullConnectionTracer) BufferedPacket(PacketType, ByteCount) {} -func (n NullConnectionTracer) DroppedPacket(PacketType, ByteCount, PacketDropReason) {} - -func (n NullConnectionTracer) UpdatedMetrics(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int) { -} -func (n NullConnectionTracer) AcknowledgedPacket(EncryptionLevel, PacketNumber) {} -func (n NullConnectionTracer) LostPacket(EncryptionLevel, PacketNumber, PacketLossReason) {} -func (n NullConnectionTracer) UpdatedCongestionState(CongestionState) {} -func (n NullConnectionTracer) UpdatedPTOCount(uint32) {} -func (n NullConnectionTracer) UpdatedKeyFromTLS(EncryptionLevel, Perspective) {} -func (n NullConnectionTracer) UpdatedKey(keyPhase KeyPhase, remote bool) {} -func (n NullConnectionTracer) DroppedEncryptionLevel(EncryptionLevel) {} -func (n NullConnectionTracer) DroppedKey(KeyPhase) {} -func (n NullConnectionTracer) SetLossTimer(TimerType, EncryptionLevel, time.Time) {} -func (n NullConnectionTracer) LossTimerExpired(timerType TimerType, level EncryptionLevel) {} -func (n NullConnectionTracer) LossTimerCanceled() {} -func (n NullConnectionTracer) Close() {} -func (n NullConnectionTracer) Debug(name, msg string) {} diff --git a/vendor/github.com/quic-go/quic-go/logging/tracer.go b/vendor/github.com/quic-go/quic-go/logging/tracer.go new file mode 100644 index 000000000..5918f30f8 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/logging/tracer.go @@ -0,0 +1,43 @@ +package logging + +import "net" + +// A Tracer traces events. +type Tracer struct { + SentPacket func(net.Addr, *Header, ByteCount, []Frame) + SentVersionNegotiationPacket func(_ net.Addr, dest, src ArbitraryLenConnectionID, _ []VersionNumber) + DroppedPacket func(net.Addr, PacketType, ByteCount, PacketDropReason) +} + +// NewMultiplexedTracer creates a new tracer that multiplexes events to multiple tracers. +func NewMultiplexedTracer(tracers ...*Tracer) *Tracer { + if len(tracers) == 0 { + return nil + } + if len(tracers) == 1 { + return tracers[0] + } + return &Tracer{ + SentPacket: func(remote net.Addr, hdr *Header, size ByteCount, frames []Frame) { + for _, t := range tracers { + if t.SentPacket != nil { + t.SentPacket(remote, hdr, size, frames) + } + } + }, + SentVersionNegotiationPacket: func(remote net.Addr, dest, src ArbitraryLenConnectionID, versions []VersionNumber) { + for _, t := range tracers { + if t.SentVersionNegotiationPacket != nil { + t.SentVersionNegotiationPacket(remote, dest, src, versions) + } + } + }, + DroppedPacket: func(remote net.Addr, typ PacketType, size ByteCount, reason PacketDropReason) { + for _, t := range tracers { + if t.DroppedPacket != nil { + t.DroppedPacket(remote, typ, size, reason) + } + } + }, + } +} diff --git a/vendor/github.com/quic-go/quic-go/logging/types.go b/vendor/github.com/quic-go/quic-go/logging/types.go index ad8006923..0d79b0a90 100644 --- a/vendor/github.com/quic-go/quic-go/logging/types.go +++ b/vendor/github.com/quic-go/quic-go/logging/types.go @@ -92,3 +92,37 @@ const ( // CongestionStateApplicationLimited means that the congestion controller is application limited CongestionStateApplicationLimited ) + +// ECNState is the state of the ECN state machine (see Appendix A.4 of RFC 9000) +type ECNState uint8 + +const ( + // ECNStateTesting is the testing state + ECNStateTesting ECNState = 1 + iota + // ECNStateUnknown is the unknown state + ECNStateUnknown + // ECNStateFailed is the failed state + ECNStateFailed + // ECNStateCapable is the capable state + ECNStateCapable +) + +// ECNStateTrigger is a trigger for an ECN state transition. +type ECNStateTrigger uint8 + +const ( + ECNTriggerNoTrigger ECNStateTrigger = iota + // ECNFailedNoECNCounts is emitted when an ACK acknowledges ECN-marked packets, + // but doesn't contain any ECN counts + ECNFailedNoECNCounts + // ECNFailedDecreasedECNCounts is emitted when an ACK frame decreases ECN counts + ECNFailedDecreasedECNCounts + // ECNFailedLostAllTestingPackets is emitted when all ECN testing packets are declared lost + ECNFailedLostAllTestingPackets + // ECNFailedMoreECNCountsThanSent is emitted when an ACK contains more ECN counts than ECN-marked packets were sent + ECNFailedMoreECNCountsThanSent + // ECNFailedTooFewECNCounts is emitted when an ACK contains fewer ECN counts than it acknowledges packets + ECNFailedTooFewECNCounts + // ECNFailedManglingDetected is emitted when the path marks all ECN-marked packets as CE + ECNFailedManglingDetected +) diff --git a/vendor/github.com/quic-go/quic-go/mockgen.go b/vendor/github.com/quic-go/quic-go/mockgen.go index eb700864a..eb2473863 100644 --- a/vendor/github.com/quic-go/quic-go/mockgen.go +++ b/vendor/github.com/quic-go/quic-go/mockgen.go @@ -2,73 +2,73 @@ package quic -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_send_conn_test.go github.com/quic-go/quic-go SendConn" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_send_conn_test.go github.com/quic-go/quic-go SendConn" type SendConn = sendConn -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_sender_test.go github.com/quic-go/quic-go Sender" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_raw_conn_test.go github.com/quic-go/quic-go RawConn" +type RawConn = rawConn + +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_sender_test.go github.com/quic-go/quic-go Sender" type Sender = sender -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_internal_test.go github.com/quic-go/quic-go StreamI" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_internal_test.go github.com/quic-go/quic-go StreamI" type StreamI = streamI -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_crypto_stream_test.go github.com/quic-go/quic-go CryptoStream" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_crypto_stream_test.go github.com/quic-go/quic-go CryptoStream" type CryptoStream = cryptoStream -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_receive_stream_internal_test.go github.com/quic-go/quic-go ReceiveStreamI" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_receive_stream_internal_test.go github.com/quic-go/quic-go ReceiveStreamI" type ReceiveStreamI = receiveStreamI -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_send_stream_internal_test.go github.com/quic-go/quic-go SendStreamI" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_send_stream_internal_test.go github.com/quic-go/quic-go SendStreamI" type SendStreamI = sendStreamI -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_getter_test.go github.com/quic-go/quic-go StreamGetter" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_getter_test.go github.com/quic-go/quic-go StreamGetter" type StreamGetter = streamGetter -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_sender_test.go github.com/quic-go/quic-go StreamSender" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_sender_test.go github.com/quic-go/quic-go StreamSender" type StreamSender = streamSender -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_crypto_data_handler_test.go github.com/quic-go/quic-go CryptoDataHandler" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_crypto_data_handler_test.go github.com/quic-go/quic-go CryptoDataHandler" type CryptoDataHandler = cryptoDataHandler -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_frame_source_test.go github.com/quic-go/quic-go FrameSource" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_frame_source_test.go github.com/quic-go/quic-go FrameSource" type FrameSource = frameSource -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_ack_frame_source_test.go github.com/quic-go/quic-go AckFrameSource" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_ack_frame_source_test.go github.com/quic-go/quic-go AckFrameSource" type AckFrameSource = ackFrameSource -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_manager_test.go github.com/quic-go/quic-go StreamManager" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_manager_test.go github.com/quic-go/quic-go StreamManager" type StreamManager = streamManager -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_sealing_manager_test.go github.com/quic-go/quic-go SealingManager" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_sealing_manager_test.go github.com/quic-go/quic-go SealingManager" type SealingManager = sealingManager -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_unpacker_test.go github.com/quic-go/quic-go Unpacker" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_unpacker_test.go github.com/quic-go/quic-go Unpacker" type Unpacker = unpacker -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packer_test.go github.com/quic-go/quic-go Packer" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packer_test.go github.com/quic-go/quic-go Packer" type Packer = packer -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_mtu_discoverer_test.go github.com/quic-go/quic-go MTUDiscoverer" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_mtu_discoverer_test.go github.com/quic-go/quic-go MTUDiscoverer" type MTUDiscoverer = mtuDiscoverer -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_conn_runner_test.go github.com/quic-go/quic-go ConnRunner" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_conn_runner_test.go github.com/quic-go/quic-go ConnRunner" type ConnRunner = connRunner -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_quic_conn_test.go github.com/quic-go/quic-go QUICConn" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_quic_conn_test.go github.com/quic-go/quic-go QUICConn" type QUICConn = quicConn -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_test.go github.com/quic-go/quic-go PacketHandler" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_test.go github.com/quic-go/quic-go PacketHandler" type PacketHandler = packetHandler -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_unknown_packet_handler_test.go github.com/quic-go/quic-go UnknownPacketHandler" -type UnknownPacketHandler = unknownPacketHandler - -//go:generate sh -c "go run github.com/golang/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_manager_test.go github.com/quic-go/quic-go PacketHandlerManager" +//go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_manager_test.go github.com/quic-go/quic-go PacketHandlerManager" type PacketHandlerManager = packetHandlerManager // Need to use source mode for the batchConn, since reflect mode follows type aliases. // See https://github.com/golang/mock/issues/244 for details. // -//go:generate sh -c "go run github.com/golang/mock/mockgen -package quic -self_package github.com/quic-go/quic-go -source sys_conn_oob.go -destination mock_batch_conn_test.go -mock_names batchConn=MockBatchConn" +//go:generate sh -c "go run go.uber.org/mock/mockgen -package quic -self_package github.com/quic-go/quic-go -source sys_conn_oob.go -destination mock_batch_conn_test.go -mock_names batchConn=MockBatchConn" -//go:generate sh -c "go run github.com/golang/mock/mockgen -package quic -self_package github.com/quic-go/quic-go -self_package github.com/quic-go/quic-go -destination mock_token_store_test.go github.com/quic-go/quic-go TokenStore" -//go:generate sh -c "go run github.com/golang/mock/mockgen -package quic -self_package github.com/quic-go/quic-go -self_package github.com/quic-go/quic-go -destination mock_packetconn_test.go net PacketConn" +//go:generate sh -c "go run go.uber.org/mock/mockgen -package quic -self_package github.com/quic-go/quic-go -self_package github.com/quic-go/quic-go -destination mock_token_store_test.go github.com/quic-go/quic-go TokenStore" +//go:generate sh -c "go run go.uber.org/mock/mockgen -package quic -self_package github.com/quic-go/quic-go -self_package github.com/quic-go/quic-go -destination mock_packetconn_test.go net PacketConn" diff --git a/vendor/github.com/quic-go/quic-go/oss-fuzz.sh b/vendor/github.com/quic-go/quic-go/oss-fuzz.sh new file mode 100644 index 000000000..1efe6baa6 --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/oss-fuzz.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# Install Go manually, since oss-fuzz ships with an outdated Go version. +# See https://github.com/google/oss-fuzz/pull/10643. +export CXX="${CXX} -lresolv" # required by Go 1.20 +wget https://go.dev/dl/go1.20.5.linux-amd64.tar.gz \ + && mkdir temp-go \ + && rm -rf /root/.go/* \ + && tar -C temp-go/ -xzf go1.20.5.linux-amd64.tar.gz \ + && mv temp-go/go/* /root/.go/ \ + && rm -rf temp-go go1.20.5.linux-amd64.tar.gz + +( +# fuzz qpack +compile_go_fuzzer github.com/quic-go/qpack/fuzzing Fuzz qpack_fuzzer +) + +( +# fuzz quic-go +compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/frames Fuzz frame_fuzzer +compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/header Fuzz header_fuzzer +compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/transportparameters Fuzz transportparameter_fuzzer +compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/tokens Fuzz token_fuzzer +compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/handshake Fuzz handshake_fuzzer + +if [ $SANITIZER == "coverage" ]; then + # no need for corpora if coverage + exit 0 +fi + +# generate seed corpora +cd $GOPATH/src/github.com/quic-go/quic-go/ +go generate -x ./fuzzing/... + +zip --quiet -r $OUT/header_fuzzer_seed_corpus.zip fuzzing/header/corpus +zip --quiet -r $OUT/frame_fuzzer_seed_corpus.zip fuzzing/frames/corpus +zip --quiet -r $OUT/transportparameter_fuzzer_seed_corpus.zip fuzzing/transportparameters/corpus +zip --quiet -r $OUT/handshake_fuzzer_seed_corpus.zip fuzzing/handshake/corpus +) + +# for debugging +ls -al $OUT diff --git a/vendor/github.com/quic-go/quic-go/packet_handler_map.go b/vendor/github.com/quic-go/quic-go/packet_handler_map.go index 2a16773c8..006eadf92 100644 --- a/vendor/github.com/quic-go/quic-go/packet_handler_map.go +++ b/vendor/github.com/quic-go/quic-go/packet_handler_map.go @@ -4,7 +4,6 @@ import ( "crypto/hmac" "crypto/rand" "crypto/sha256" - "errors" "hash" "io" "net" @@ -21,14 +20,17 @@ type connCapabilities struct { DF bool // GSO (Generic Segmentation Offload) supported GSO bool + // ECN (Explicit Congestion Notifications) supported + ECN bool } // rawConn is a connection that allow reading of a receivedPackeh. type rawConn interface { ReadPacket() (receivedPacket, error) - // The size parameter is used for GSO. - // If GSO is not support, len(b) must be equal to size. - WritePacket(b []byte, size uint16, addr net.Addr, oob []byte) (int, error) + // WritePacket writes a packet on the wire. + // gsoSize is the size of a single packet, or 0 to disable GSO. + // It is invalid to set gsoSize if capabilities.GSO is not set. + WritePacket(b []byte, addr net.Addr, packetInfoOOB []byte, gsoSize uint16, ecn protocol.ECN) (int, error) LocalAddr() net.Addr SetReadDeadline(time.Time) error io.Closer @@ -42,13 +44,6 @@ type closePacket struct { info packetInfo } -type unknownPacketHandler interface { - handlePacket(receivedPacket) - setCloseError(error) -} - -var errListenerAlreadySet = errors.New("listener already set") - type packetHandlerMap struct { mutex sync.Mutex handlers map[protocol.ConnectionID]packetHandler diff --git a/vendor/github.com/quic-go/quic-go/packet_packer.go b/vendor/github.com/quic-go/quic-go/packet_packer.go index 577f3b043..a330632be 100644 --- a/vendor/github.com/quic-go/quic-go/packet_packer.go +++ b/vendor/github.com/quic-go/quic-go/packet_packer.go @@ -1,9 +1,13 @@ package quic import ( + crand "crypto/rand" + "encoding/binary" "errors" "fmt" + "golang.org/x/exp/rand" + "github.com/quic-go/quic-go/internal/ackhandler" "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/protocol" @@ -67,6 +71,11 @@ type coalescedPacket struct { shortHdrPacket *shortHeaderPacket } +// IsOnlyShortHeaderPacket says if this packet only contains a short header packet (and no long header packets). +func (p *coalescedPacket) IsOnlyShortHeaderPacket() bool { + return len(p.longHdrPackets) == 0 && p.shortHdrPacket != nil +} + func (p *longHeaderPacket) EncryptionLevel() protocol.EncryptionLevel { //nolint:exhaustive // Will never be called for Retry packets (and they don't have encrypted data). switch p.header.Type { @@ -122,6 +131,7 @@ type packetPacker struct { acks ackFrameSource datagramQueue *datagramQueue retransmissionQueue *retransmissionQueue + rand rand.Rand numNonAckElicitingAcks int } @@ -140,6 +150,9 @@ func newPacketPacker( datagramQueue *datagramQueue, perspective protocol.Perspective, ) *packetPacker { + var b [8]byte + _, _ = crand.Read(b[:]) + return &packetPacker{ cryptoSetup: cryptoSetup, getDestConnID: getDestConnID, @@ -151,6 +164,7 @@ func newPacketPacker( perspective: perspective, framer: framer, acks: acks, + rand: *rand.New(rand.NewSource(binary.BigEndian.Uint64(b[:]))), pnManager: packetNumberManager, } } @@ -626,7 +640,13 @@ func (p *packetPacker) composeNextPacket(maxFrameSize protocol.ByteCount, onlyAc pl.length += lengthAdded // add handlers for the control frames that were added for i := startLen; i < len(pl.frames); i++ { - pl.frames[i].Handler = p.retransmissionQueue.AppDataAckHandler() + switch pl.frames[i].Frame.(type) { + case *wire.PathChallengeFrame, *wire.PathResponseFrame: + // Path probing is currently not supported, therefore we don't need to set the OnAcked callback yet. + // PATH_CHALLENGE and PATH_RESPONSE are never retransmitted. + default: + pl.frames[i].Handler = p.retransmissionQueue.AppDataAckHandler() + } } pl.streamFrames, lengthAdded = p.framer.AppendStreamFrames(pl.streamFrames, maxFrameSize-pl.length, v) @@ -832,6 +852,8 @@ func (p *packetPacker) appendShortHeaderPacket( }, nil } +// appendPacketPayload serializes the payload of a packet into the raw byte slice. +// It modifies the order of payload.frames. func (p *packetPacker) appendPacketPayload(raw []byte, pl payload, paddingLen protocol.ByteCount, v protocol.VersionNumber) ([]byte, error) { payloadOffset := len(raw) if pl.ack != nil { @@ -844,6 +866,11 @@ func (p *packetPacker) appendPacketPayload(raw []byte, pl payload, paddingLen pr if paddingLen > 0 { raw = append(raw, make([]byte, paddingLen)...) } + // Randomize the order of the control frames. + // This makes sure that the receiver doesn't rely on the order in which frames are packed. + if len(pl.frames) > 1 { + p.rand.Shuffle(len(pl.frames), func(i, j int) { pl.frames[i], pl.frames[j] = pl.frames[j], pl.frames[i] }) + } for _, f := range pl.frames { var err error raw, err = f.Frame.Append(raw, v) diff --git a/vendor/github.com/quic-go/quic-go/qlog/event.go b/vendor/github.com/quic-go/quic-go/qlog/event.go index 9dae74441..740d3c2e1 100644 --- a/vendor/github.com/quic-go/quic-go/qlog/event.go +++ b/vendor/github.com/quic-go/quic-go/qlog/event.go @@ -158,6 +158,7 @@ type eventPacketSent struct { PayloadLength logging.ByteCount Frames frames IsCoalesced bool + ECN logging.ECN Trigger string } @@ -172,6 +173,9 @@ func (e eventPacketSent) MarshalJSONObject(enc *gojay.Encoder) { enc.ObjectKey("raw", rawInfo{Length: e.Length, PayloadLength: e.PayloadLength}) enc.ArrayKeyOmitEmpty("frames", e.Frames) enc.BoolKeyOmitEmpty("is_coalesced", e.IsCoalesced) + if e.ECN != logging.ECNUnsupported { + enc.StringKey("ecn", ecn(e.ECN).String()) + } enc.StringKeyOmitEmpty("trigger", e.Trigger) } @@ -180,6 +184,7 @@ type eventPacketReceived struct { Length logging.ByteCount PayloadLength logging.ByteCount Frames frames + ECN logging.ECN IsCoalesced bool Trigger string } @@ -195,6 +200,9 @@ func (e eventPacketReceived) MarshalJSONObject(enc *gojay.Encoder) { enc.ObjectKey("raw", rawInfo{Length: e.Length, PayloadLength: e.PayloadLength}) enc.ArrayKeyOmitEmpty("frames", e.Frames) enc.BoolKeyOmitEmpty("is_coalesced", e.IsCoalesced) + if e.ECN != logging.ECNUnsupported { + enc.StringKey("ecn", ecn(e.ECN).String()) + } enc.StringKeyOmitEmpty("trigger", e.Trigger) } @@ -516,6 +524,20 @@ func (e eventCongestionStateUpdated) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("new", e.state.String()) } +type eventECNStateUpdated struct { + state logging.ECNState + trigger logging.ECNStateTrigger +} + +func (e eventECNStateUpdated) Category() category { return categoryRecovery } +func (e eventECNStateUpdated) Name() string { return "ecn_state_updated" } +func (e eventECNStateUpdated) IsNil() bool { return false } + +func (e eventECNStateUpdated) MarshalJSONObject(enc *gojay.Encoder) { + enc.StringKey("new", ecnState(e.state).String()) + enc.StringKeyOmitEmpty("trigger", ecnStateTrigger(e.trigger).String()) +} + type eventGeneric struct { name string msg string diff --git a/vendor/github.com/quic-go/quic-go/qlog/qlog.go b/vendor/github.com/quic-go/quic-go/qlog/qlog.go index 4c480e260..7801b6463 100644 --- a/vendor/github.com/quic-go/quic-go/qlog/qlog.go +++ b/vendor/github.com/quic-go/quic-go/qlog/qlog.go @@ -63,11 +63,9 @@ type connectionTracer struct { lastMetrics *metrics } -var _ logging.ConnectionTracer = &connectionTracer{} - // NewConnectionTracer creates a new tracer to record a qlog for a connection. -func NewConnectionTracer(w io.WriteCloser, p protocol.Perspective, odcid protocol.ConnectionID) logging.ConnectionTracer { - t := &connectionTracer{ +func NewConnectionTracer(w io.WriteCloser, p protocol.Perspective, odcid protocol.ConnectionID) *logging.ConnectionTracer { + t := connectionTracer{ w: w, perspective: p, odcid: odcid, @@ -76,7 +74,84 @@ func NewConnectionTracer(w io.WriteCloser, p protocol.Perspective, odcid protoco referenceTime: time.Now(), } go t.run() - return t + return &logging.ConnectionTracer{ + StartedConnection: func(local, remote net.Addr, srcConnID, destConnID logging.ConnectionID) { + t.StartedConnection(local, remote, srcConnID, destConnID) + }, + NegotiatedVersion: func(chosen logging.VersionNumber, clientVersions, serverVersions []logging.VersionNumber) { + t.NegotiatedVersion(chosen, clientVersions, serverVersions) + }, + ClosedConnection: func(e error) { t.ClosedConnection(e) }, + SentTransportParameters: func(tp *wire.TransportParameters) { t.SentTransportParameters(tp) }, + ReceivedTransportParameters: func(tp *wire.TransportParameters) { t.ReceivedTransportParameters(tp) }, + RestoredTransportParameters: func(tp *wire.TransportParameters) { t.RestoredTransportParameters(tp) }, + SentLongHeaderPacket: func(hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, ack *logging.AckFrame, frames []logging.Frame) { + t.SentLongHeaderPacket(hdr, size, ecn, ack, frames) + }, + SentShortHeaderPacket: func(hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, ack *logging.AckFrame, frames []logging.Frame) { + t.SentShortHeaderPacket(hdr, size, ecn, ack, frames) + }, + ReceivedLongHeaderPacket: func(hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { + t.ReceivedLongHeaderPacket(hdr, size, ecn, frames) + }, + ReceivedShortHeaderPacket: func(hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { + t.ReceivedShortHeaderPacket(hdr, size, ecn, frames) + }, + ReceivedRetry: func(hdr *wire.Header) { + t.ReceivedRetry(hdr) + }, + ReceivedVersionNegotiationPacket: func(dest, src logging.ArbitraryLenConnectionID, versions []logging.VersionNumber) { + t.ReceivedVersionNegotiationPacket(dest, src, versions) + }, + BufferedPacket: func(pt logging.PacketType, size protocol.ByteCount) { + t.BufferedPacket(pt, size) + }, + DroppedPacket: func(pt logging.PacketType, size protocol.ByteCount, reason logging.PacketDropReason) { + t.DroppedPacket(pt, size, reason) + }, + UpdatedMetrics: func(rttStats *utils.RTTStats, cwnd, bytesInFlight protocol.ByteCount, packetsInFlight int) { + t.UpdatedMetrics(rttStats, cwnd, bytesInFlight, packetsInFlight) + }, + LostPacket: func(encLevel protocol.EncryptionLevel, pn protocol.PacketNumber, lossReason logging.PacketLossReason) { + t.LostPacket(encLevel, pn, lossReason) + }, + UpdatedCongestionState: func(state logging.CongestionState) { + t.UpdatedCongestionState(state) + }, + UpdatedPTOCount: func(value uint32) { + t.UpdatedPTOCount(value) + }, + UpdatedKeyFromTLS: func(encLevel protocol.EncryptionLevel, pers protocol.Perspective) { + t.UpdatedKeyFromTLS(encLevel, pers) + }, + UpdatedKey: func(generation protocol.KeyPhase, remote bool) { + t.UpdatedKey(generation, remote) + }, + DroppedEncryptionLevel: func(encLevel protocol.EncryptionLevel) { + t.DroppedEncryptionLevel(encLevel) + }, + DroppedKey: func(generation protocol.KeyPhase) { + t.DroppedKey(generation) + }, + SetLossTimer: func(tt logging.TimerType, encLevel protocol.EncryptionLevel, timeout time.Time) { + t.SetLossTimer(tt, encLevel, timeout) + }, + LossTimerExpired: func(tt logging.TimerType, encLevel protocol.EncryptionLevel) { + t.LossTimerExpired(tt, encLevel) + }, + LossTimerCanceled: func() { + t.LossTimerCanceled() + }, + ECNStateUpdated: func(state logging.ECNState, trigger logging.ECNStateTrigger) { + t.ECNStateUpdated(state, trigger) + }, + Debug: func(name, msg string) { + t.Debug(name, msg) + }, + Close: func() { + t.Close() + }, + } } func (t *connectionTracer) run() { @@ -253,15 +328,33 @@ func (t *connectionTracer) toTransportParameters(tp *wire.TransportParameters) * } } -func (t *connectionTracer) SentLongHeaderPacket(hdr *logging.ExtendedHeader, packetSize logging.ByteCount, ack *logging.AckFrame, frames []logging.Frame) { - t.sentPacket(*transformLongHeader(hdr), packetSize, hdr.Length, ack, frames) +func (t *connectionTracer) SentLongHeaderPacket( + hdr *logging.ExtendedHeader, + size logging.ByteCount, + ecn logging.ECN, + ack *logging.AckFrame, + frames []logging.Frame, +) { + t.sentPacket(*transformLongHeader(hdr), size, hdr.Length, ecn, ack, frames) } -func (t *connectionTracer) SentShortHeaderPacket(hdr *logging.ShortHeader, packetSize logging.ByteCount, ack *logging.AckFrame, frames []logging.Frame) { - t.sentPacket(*transformShortHeader(hdr), packetSize, 0, ack, frames) +func (t *connectionTracer) SentShortHeaderPacket( + hdr *logging.ShortHeader, + size logging.ByteCount, + ecn logging.ECN, + ack *logging.AckFrame, + frames []logging.Frame, +) { + t.sentPacket(*transformShortHeader(hdr), size, 0, ecn, ack, frames) } -func (t *connectionTracer) sentPacket(hdr gojay.MarshalerJSONObject, packetSize, payloadLen logging.ByteCount, ack *logging.AckFrame, frames []logging.Frame) { +func (t *connectionTracer) sentPacket( + hdr gojay.MarshalerJSONObject, + size, payloadLen logging.ByteCount, + ecn logging.ECN, + ack *logging.AckFrame, + frames []logging.Frame, +) { numFrames := len(frames) if ack != nil { numFrames++ @@ -276,14 +369,15 @@ func (t *connectionTracer) sentPacket(hdr gojay.MarshalerJSONObject, packetSize, t.mutex.Lock() t.recordEvent(time.Now(), &eventPacketSent{ Header: hdr, - Length: packetSize, + Length: size, PayloadLength: payloadLen, + ECN: ecn, Frames: fs, }) t.mutex.Unlock() } -func (t *connectionTracer) ReceivedLongHeaderPacket(hdr *logging.ExtendedHeader, packetSize logging.ByteCount, frames []logging.Frame) { +func (t *connectionTracer) ReceivedLongHeaderPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { fs := make([]frame, len(frames)) for i, f := range frames { fs[i] = frame{Frame: f} @@ -292,14 +386,15 @@ func (t *connectionTracer) ReceivedLongHeaderPacket(hdr *logging.ExtendedHeader, t.mutex.Lock() t.recordEvent(time.Now(), &eventPacketReceived{ Header: header, - Length: packetSize, + Length: size, PayloadLength: hdr.Length, + ECN: ecn, Frames: fs, }) t.mutex.Unlock() } -func (t *connectionTracer) ReceivedShortHeaderPacket(hdr *logging.ShortHeader, packetSize logging.ByteCount, frames []logging.Frame) { +func (t *connectionTracer) ReceivedShortHeaderPacket(hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { fs := make([]frame, len(frames)) for i, f := range frames { fs[i] = frame{Frame: f} @@ -308,8 +403,9 @@ func (t *connectionTracer) ReceivedShortHeaderPacket(hdr *logging.ShortHeader, p t.mutex.Lock() t.recordEvent(time.Now(), &eventPacketReceived{ Header: header, - Length: packetSize, - PayloadLength: packetSize - wire.ShortHeaderLen(hdr.DestConnectionID, hdr.PacketNumberLen), + Length: size, + PayloadLength: size - wire.ShortHeaderLen(hdr.DestConnectionID, hdr.PacketNumberLen), + ECN: ecn, Frames: fs, }) t.mutex.Unlock() @@ -482,6 +578,12 @@ func (t *connectionTracer) LossTimerCanceled() { t.mutex.Unlock() } +func (t *connectionTracer) ECNStateUpdated(state logging.ECNState, trigger logging.ECNStateTrigger) { + t.mutex.Lock() + t.recordEvent(time.Now(), &eventECNStateUpdated{state: state, trigger: trigger}) + t.mutex.Unlock() +} + func (t *connectionTracer) Debug(name, msg string) { t.mutex.Lock() t.recordEvent(time.Now(), &eventGeneric{ diff --git a/vendor/github.com/quic-go/quic-go/qlog/types.go b/vendor/github.com/quic-go/quic-go/qlog/types.go index c47ad481e..e0c0b2434 100644 --- a/vendor/github.com/quic-go/quic-go/qlog/types.go +++ b/vendor/github.com/quic-go/quic-go/qlog/types.go @@ -312,3 +312,61 @@ func (s congestionState) String() string { return "unknown congestion state" } } + +type ecn logging.ECN + +func (e ecn) String() string { + //nolint:exhaustive // The unsupported value is never logged. + switch logging.ECN(e) { + case logging.ECTNot: + return "Not-ECT" + case logging.ECT0: + return "ECT(0)" + case logging.ECT1: + return "ECT(1)" + case logging.ECNCE: + return "CE" + default: + return "unknown ECN" + } +} + +type ecnState logging.ECNState + +func (e ecnState) String() string { + switch logging.ECNState(e) { + case logging.ECNStateTesting: + return "testing" + case logging.ECNStateUnknown: + return "unknown" + case logging.ECNStateCapable: + return "capable" + case logging.ECNStateFailed: + return "failed" + default: + return "unknown ECN state" + } +} + +type ecnStateTrigger logging.ECNStateTrigger + +func (e ecnStateTrigger) String() string { + switch logging.ECNStateTrigger(e) { + case logging.ECNTriggerNoTrigger: + return "" + case logging.ECNFailedNoECNCounts: + return "ACK doesn't contain ECN marks" + case logging.ECNFailedDecreasedECNCounts: + return "ACK decreases ECN counts" + case logging.ECNFailedLostAllTestingPackets: + return "all ECN testing packets declared lost" + case logging.ECNFailedMoreECNCountsThanSent: + return "ACK contains more ECN counts than ECN-marked packets sent" + case logging.ECNFailedTooFewECNCounts: + return "ACK contains fewer new ECN counts than acknowledged ECN-marked packets" + case logging.ECNFailedManglingDetected: + return "ECN mangling detected" + default: + return "unknown ECN state trigger" + } +} diff --git a/vendor/github.com/quic-go/quic-go/send_conn.go b/vendor/github.com/quic-go/quic-go/send_conn.go index 4e7007fad..4fda14691 100644 --- a/vendor/github.com/quic-go/quic-go/send_conn.go +++ b/vendor/github.com/quic-go/quic-go/send_conn.go @@ -1,15 +1,15 @@ package quic import ( - "math" "net" "github.com/quic-go/quic-go/internal/protocol" + "github.com/quic-go/quic-go/internal/utils" ) // A sendConn allows sending using a simple Write() on a non-connected packet conn. type sendConn interface { - Write(b []byte, size protocol.ByteCount) error + Write(b []byte, gsoSize uint16, ecn protocol.ECN) error Close() error LocalAddr() net.Addr RemoteAddr() net.Addr @@ -20,61 +20,72 @@ type sendConn interface { type sconn struct { rawConn + localAddr net.Addr remoteAddr net.Addr - info packetInfo - oob []byte + + logger utils.Logger + + packetInfoOOB []byte + // If GSO enabled, and we receive a GSO error for this remote address, GSO is disabled. + gotGSOError bool } var _ sendConn = &sconn{} -func newSendConn(c rawConn, remote net.Addr) *sconn { - sc := &sconn{ - rawConn: c, - remoteAddr: remote, +func newSendConn(c rawConn, remote net.Addr, info packetInfo, logger utils.Logger) *sconn { + localAddr := c.LocalAddr() + if info.addr.IsValid() { + if udpAddr, ok := localAddr.(*net.UDPAddr); ok { + addrCopy := *udpAddr + addrCopy.IP = info.addr.AsSlice() + localAddr = &addrCopy + } } - if c.capabilities().GSO { - // add 32 bytes, so we can add the UDP_SEGMENT msg - sc.oob = make([]byte, 0, 32) - } - return sc -} -func newSendConnWithPacketInfo(c rawConn, remote net.Addr, info packetInfo) *sconn { oob := info.OOB() - if c.capabilities().GSO { - // add 32 bytes, so we can add the UDP_SEGMENT msg - l := len(oob) - oob = append(oob, make([]byte, 32)...) - oob = oob[:l] - } + // increase oob slice capacity, so we can add the UDP_SEGMENT and ECN control messages without allocating + l := len(oob) + oob = append(oob, make([]byte, 64)...)[:l] return &sconn{ - rawConn: c, - remoteAddr: remote, - info: info, - oob: oob, + rawConn: c, + localAddr: localAddr, + remoteAddr: remote, + packetInfoOOB: oob, + logger: logger, } } -func (c *sconn) Write(p []byte, size protocol.ByteCount) error { - if size > math.MaxUint16 { - panic("size overflow") +func (c *sconn) Write(p []byte, gsoSize uint16, ecn protocol.ECN) error { + _, err := c.WritePacket(p, c.remoteAddr, c.packetInfoOOB, gsoSize, ecn) + if err != nil && isGSOError(err) { + // disable GSO for future calls + c.gotGSOError = true + if c.logger.Debug() { + c.logger.Debugf("GSO failed when sending to %s", c.remoteAddr) + } + // send out the packets one by one + for len(p) > 0 { + l := len(p) + if l > int(gsoSize) { + l = int(gsoSize) + } + if _, err := c.WritePacket(p[:l], c.remoteAddr, c.packetInfoOOB, 0, ecn); err != nil { + return err + } + p = p[l:] + } + return nil } - _, err := c.WritePacket(p, uint16(size), c.remoteAddr, c.oob) return err } -func (c *sconn) RemoteAddr() net.Addr { - return c.remoteAddr +func (c *sconn) capabilities() connCapabilities { + capabilities := c.rawConn.capabilities() + if capabilities.GSO { + capabilities.GSO = !c.gotGSOError + } + return capabilities } -func (c *sconn) LocalAddr() net.Addr { - addr := c.rawConn.LocalAddr() - if c.info.addr.IsValid() { - if udpAddr, ok := addr.(*net.UDPAddr); ok { - addrCopy := *udpAddr - addrCopy.IP = c.info.addr.AsSlice() - addr = &addrCopy - } - } - return addr -} +func (c *sconn) RemoteAddr() net.Addr { return c.remoteAddr } +func (c *sconn) LocalAddr() net.Addr { return c.localAddr } diff --git a/vendor/github.com/quic-go/quic-go/send_queue.go b/vendor/github.com/quic-go/quic-go/send_queue.go index ab7a45ca8..bde023348 100644 --- a/vendor/github.com/quic-go/quic-go/send_queue.go +++ b/vendor/github.com/quic-go/quic-go/send_queue.go @@ -3,7 +3,7 @@ package quic import "github.com/quic-go/quic-go/internal/protocol" type sender interface { - Send(p *packetBuffer, packetSize protocol.ByteCount) + Send(p *packetBuffer, gsoSize uint16, ecn protocol.ECN) Run() error WouldBlock() bool Available() <-chan struct{} @@ -11,8 +11,9 @@ type sender interface { } type queueEntry struct { - buf *packetBuffer - size protocol.ByteCount + buf *packetBuffer + gsoSize uint16 + ecn protocol.ECN } type sendQueue struct { @@ -40,9 +41,9 @@ func newSendQueue(conn sendConn) sender { // Send sends out a packet. It's guaranteed to not block. // Callers need to make sure that there's actually space in the send queue by calling WouldBlock. // Otherwise Send will panic. -func (h *sendQueue) Send(p *packetBuffer, size protocol.ByteCount) { +func (h *sendQueue) Send(p *packetBuffer, gsoSize uint16, ecn protocol.ECN) { select { - case h.queue <- queueEntry{buf: p, size: size}: + case h.queue <- queueEntry{buf: p, gsoSize: gsoSize, ecn: ecn}: // clear available channel if we've reached capacity if len(h.queue) == sendQueueCapacity { select { @@ -77,12 +78,12 @@ func (h *sendQueue) Run() error { // make sure that all queued packets are actually sent out shouldClose = true case e := <-h.queue: - if err := h.conn.Write(e.buf.Data, e.size); err != nil { + if err := h.conn.Write(e.buf.Data, e.gsoSize, e.ecn); err != nil { // This additional check enables: // 1. Checking for "datagram too large" message from the kernel, as such, // 2. Path MTU discovery,and // 3. Eventual detection of loss PingFrame. - if !isMsgSizeErr(err) { + if !isSendMsgSizeErr(err) { return err } } diff --git a/vendor/github.com/quic-go/quic-go/send_stream.go b/vendor/github.com/quic-go/quic-go/send_stream.go index 62ebe2ea5..4113d9f0c 100644 --- a/vendor/github.com/quic-go/quic-go/send_stream.go +++ b/vendor/github.com/quic-go/quic-go/send_stream.go @@ -30,7 +30,7 @@ type sendStream struct { retransmissionQueue []*wire.StreamFrame ctx context.Context - ctxCancel context.CancelFunc + ctxCancel context.CancelCauseFunc streamID protocol.StreamID sender streamSender @@ -71,7 +71,7 @@ func newSendStream( writeChan: make(chan struct{}, 1), writeOnce: make(chan struct{}, 1), // cap: 1, to protect against concurrent use of Write } - s.ctx, s.ctxCancel = context.WithCancel(context.Background()) + s.ctx, s.ctxCancel = context.WithCancelCause(context.Background()) return s } @@ -366,7 +366,7 @@ func (s *sendStream) Close() error { s.mutex.Unlock() return fmt.Errorf("close called for canceled stream %d", s.streamID) } - s.ctxCancel() + s.ctxCancel(nil) s.finishedWriting = true s.mutex.Unlock() @@ -385,8 +385,8 @@ func (s *sendStream) cancelWriteImpl(errorCode qerr.StreamErrorCode, remote bool s.mutex.Unlock() return } - s.ctxCancel() s.cancelWriteErr = &StreamError{StreamID: s.streamID, ErrorCode: errorCode, Remote: remote} + s.ctxCancel(s.cancelWriteErr) s.numOutstandingFrames = 0 s.retransmissionQueue = nil newlyCompleted := s.isNewlyCompleted() @@ -435,7 +435,7 @@ func (s *sendStream) SetWriteDeadline(t time.Time) error { // The peer will NOT be informed about this: the stream is closed without sending a FIN or RST. func (s *sendStream) closeForShutdown(err error) { s.mutex.Lock() - s.ctxCancel() + s.ctxCancel(err) s.closeForShutdownErr = err s.mutex.Unlock() s.signalWrite() diff --git a/vendor/github.com/quic-go/quic-go/server.go b/vendor/github.com/quic-go/quic-go/server.go index 0f8219e3a..119289b39 100644 --- a/vendor/github.com/quic-go/quic-go/server.go +++ b/vendor/github.com/quic-go/quic-go/server.go @@ -2,7 +2,6 @@ package quic import ( "context" - "crypto/rand" "crypto/tls" "errors" "fmt" @@ -59,7 +58,8 @@ type zeroRTTQueue struct { type baseServer struct { mutex sync.Mutex - acceptEarlyConns bool + disableVersionNegotiation bool + acceptEarlyConns bool tlsConf *tls.Config config *Config @@ -67,6 +67,7 @@ type baseServer struct { conn rawConn tokenGenerator *handshake.TokenGenerator + maxTokenAge time.Duration connIDGenerator ConnectionIDGenerator connHandler packetHandlerManager @@ -92,7 +93,7 @@ type baseServer struct { *tls.Config, *handshake.TokenGenerator, bool, /* client address validated by an address validation token */ - logging.ConnectionTracer, + *logging.ConnectionTracer, uint64, utils.Logger, protocol.VersionNumber, @@ -108,7 +109,7 @@ type baseServer struct { connQueue chan quicConn connQueueLen int32 // to be used as an atomic - tracer logging.Tracer + tracer *logging.Tracer logger utils.Logger } @@ -224,32 +225,33 @@ func newServer( connIDGenerator ConnectionIDGenerator, tlsConf *tls.Config, config *Config, - tracer logging.Tracer, + tracer *logging.Tracer, onClose func(), + tokenGeneratorKey TokenGeneratorKey, + maxTokenAge time.Duration, + disableVersionNegotiation bool, acceptEarly bool, -) (*baseServer, error) { - tokenGenerator, err := handshake.NewTokenGenerator(rand.Reader) - if err != nil { - return nil, err - } +) *baseServer { s := &baseServer{ - conn: conn, - tlsConf: tlsConf, - config: config, - tokenGenerator: tokenGenerator, - connIDGenerator: connIDGenerator, - connHandler: connHandler, - connQueue: make(chan quicConn), - errorChan: make(chan struct{}), - running: make(chan struct{}), - receivedPackets: make(chan receivedPacket, protocol.MaxServerUnprocessedPackets), - versionNegotiationQueue: make(chan receivedPacket, 4), - invalidTokenQueue: make(chan receivedPacket, 4), - newConn: newConnection, - tracer: tracer, - logger: utils.DefaultLogger.WithPrefix("server"), - acceptEarlyConns: acceptEarly, - onClose: onClose, + conn: conn, + tlsConf: tlsConf, + config: config, + tokenGenerator: handshake.NewTokenGenerator(tokenGeneratorKey), + maxTokenAge: maxTokenAge, + connIDGenerator: connIDGenerator, + connHandler: connHandler, + connQueue: make(chan quicConn), + errorChan: make(chan struct{}), + running: make(chan struct{}), + receivedPackets: make(chan receivedPacket, protocol.MaxServerUnprocessedPackets), + versionNegotiationQueue: make(chan receivedPacket, 4), + invalidTokenQueue: make(chan receivedPacket, 4), + newConn: newConnection, + tracer: tracer, + logger: utils.DefaultLogger.WithPrefix("server"), + acceptEarlyConns: acceptEarly, + disableVersionNegotiation: disableVersionNegotiation, + onClose: onClose, } if acceptEarly { s.zeroRTTQueues = map[protocol.ConnectionID]*zeroRTTQueue{} @@ -257,7 +259,7 @@ func newServer( go s.run() go s.runSendQueue() s.logger.Debugf("Listening for %s connections on %s", conn.LocalAddr().Network(), conn.LocalAddr().String()) - return s, nil + return s } func (s *baseServer) run() { @@ -350,7 +352,7 @@ func (s *baseServer) handlePacket(p receivedPacket) { case s.receivedPackets <- p: default: s.logger.Debugf("Dropping packet from %s (%d bytes). Server receive queue full.", p.remoteAddr, p.Size()) - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropDOSPrevention) } } @@ -363,7 +365,7 @@ func (s *baseServer) handlePacketImpl(p receivedPacket) bool /* is the buffer st if wire.IsVersionNegotiationPacket(p.data) { s.logger.Debugf("Dropping Version Negotiation packet.") - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropUnexpectedPacket) } return false @@ -376,20 +378,20 @@ func (s *baseServer) handlePacketImpl(p receivedPacket) bool /* is the buffer st // drop the packet if we failed to parse the protocol version if err != nil { s.logger.Debugf("Dropping a packet with an unknown version") - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket) } return false } // send a Version Negotiation Packet if the client is speaking a different protocol version if !protocol.IsSupportedVersion(s.config.Versions, v) { - if s.config.DisableVersionNegotiationPackets { + if s.disableVersionNegotiation { return false } if p.Size() < protocol.MinUnknownVersionPacketSize { s.logger.Debugf("Dropping a packet with an unsupported version number %d that is too small (%d bytes)", v, p.Size()) - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket) } return false @@ -399,7 +401,7 @@ func (s *baseServer) handlePacketImpl(p receivedPacket) bool /* is the buffer st if wire.Is0RTTPacket(p.data) { if !s.acceptEarlyConns { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropUnexpectedPacket) } return false @@ -411,7 +413,7 @@ func (s *baseServer) handlePacketImpl(p receivedPacket) bool /* is the buffer st // The header will then be parsed again. hdr, _, _, err := wire.ParsePacket(p.data) if err != nil { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) } s.logger.Debugf("Error parsing packet: %s", err) @@ -419,7 +421,7 @@ func (s *baseServer) handlePacketImpl(p receivedPacket) bool /* is the buffer st } if hdr.Type == protocol.PacketTypeInitial && p.Size() < protocol.MinInitialPacketSize { s.logger.Debugf("Dropping a packet that is too small to be a valid Initial (%d bytes)", p.Size()) - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket) } return false @@ -430,7 +432,7 @@ func (s *baseServer) handlePacketImpl(p receivedPacket) bool /* is the buffer st // There's little point in sending a Stateless Reset, since the client // might not have received the token yet. s.logger.Debugf("Dropping long header packet of type %s (%d bytes)", hdr.Type, len(p.data)) - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropUnexpectedPacket) } return false @@ -449,7 +451,7 @@ func (s *baseServer) handlePacketImpl(p receivedPacket) bool /* is the buffer st func (s *baseServer) handle0RTTPacket(p receivedPacket) bool { connID, err := wire.ParseConnectionID(p.data, 0) if err != nil { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropHeaderParseError) } return false @@ -463,7 +465,7 @@ func (s *baseServer) handle0RTTPacket(p receivedPacket) bool { if q, ok := s.zeroRTTQueues[connID]; ok { if len(q.packets) >= protocol.Max0RTTQueueLen { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropDOSPrevention) } return false @@ -473,7 +475,7 @@ func (s *baseServer) handle0RTTPacket(p receivedPacket) bool { } if len(s.zeroRTTQueues) >= protocol.Max0RTTQueues { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropDOSPrevention) } return false @@ -501,7 +503,7 @@ func (s *baseServer) cleanupZeroRTTQueues(now time.Time) { continue } for _, p := range q.packets { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketType0RTT, p.Size(), logging.PacketDropDOSPrevention) } p.buffer.Release() @@ -525,10 +527,10 @@ func (s *baseServer) validateToken(token *handshake.Token, addr net.Addr) bool { if !token.ValidateRemoteAddr(addr) { return false } - if !token.IsRetryToken && time.Since(token.SentTime) > s.config.MaxTokenAge { + if !token.IsRetryToken && time.Since(token.SentTime) > s.maxTokenAge { return false } - if token.IsRetryToken && time.Since(token.SentTime) > s.config.MaxRetryTokenAge { + if token.IsRetryToken && time.Since(token.SentTime) > s.config.maxRetryTokenAge() { return false } return true @@ -537,7 +539,7 @@ func (s *baseServer) validateToken(token *handshake.Token, addr net.Addr) bool { func (s *baseServer) handleInitialImpl(p receivedPacket, hdr *wire.Header) error { if len(hdr.Token) == 0 && hdr.DestConnectionID.Len() < protocol.MinConnectionIDLenInitial { p.buffer.Release() - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket) } return errors.New("too short connection ID") @@ -622,7 +624,7 @@ func (s *baseServer) handleInitialImpl(p receivedPacket, hdr *wire.Header) error } config = populateConfig(conf) } - var tracer logging.ConnectionTracer + var tracer *logging.ConnectionTracer if config.Tracer != nil { // Use the same connection ID that is passed to the client's GetLogWriter callback. connID := hdr.DestConnectionID @@ -632,7 +634,7 @@ func (s *baseServer) handleInitialImpl(p receivedPacket, hdr *wire.Header) error tracer = config.Tracer(context.WithValue(context.Background(), ConnectionTracingKey, tracingID), protocol.PerspectiveServer, connID) } conn = s.newConn( - newSendConnWithPacketInfo(s.conn, p.remoteAddr, p.info), + newSendConn(s.conn, p.remoteAddr, p.info, s.logger), s.connHandler, origDestConnID, retrySrcConnID, @@ -739,10 +741,10 @@ func (s *baseServer) sendRetry(remoteAddr net.Addr, hdr *wire.Header, info packe // append the Retry integrity tag tag := handshake.GetRetryIntegrityTag(buf.Data, hdr.DestConnectionID, hdr.Version) buf.Data = append(buf.Data, tag[:]...) - if s.tracer != nil { + if s.tracer != nil && s.tracer.SentPacket != nil { s.tracer.SentPacket(remoteAddr, &replyHdr.Header, protocol.ByteCount(len(buf.Data)), nil) } - _, err = s.conn.WritePacket(buf.Data, uint16(len(buf.Data)), remoteAddr, info.OOB()) + _, err = s.conn.WritePacket(buf.Data, remoteAddr, info.OOB(), 0, protocol.ECNUnsupported) return err } @@ -760,7 +762,7 @@ func (s *baseServer) maybeSendInvalidToken(p receivedPacket) { hdr, _, _, err := wire.ParsePacket(p.data) if err != nil { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) } s.logger.Debugf("Error parsing packet: %s", err) @@ -775,14 +777,14 @@ func (s *baseServer) maybeSendInvalidToken(p receivedPacket) { // Only send INVALID_TOKEN if we can unprotect the packet. // This makes sure that we won't send it for packets that were corrupted. if err != nil { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropHeaderParseError) } return } hdrLen := extHdr.ParsedLen() if _, err := opener.Open(data[hdrLen:hdrLen], data[hdrLen:], extHdr.PacketNumber, data[:hdrLen]); err != nil { - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropPayloadDecryptError) } return @@ -838,10 +840,10 @@ func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer han replyHdr.Log(s.logger) wire.LogFrame(s.logger, ccf, true) - if s.tracer != nil { + if s.tracer != nil && s.tracer.SentPacket != nil { s.tracer.SentPacket(remoteAddr, &replyHdr.Header, protocol.ByteCount(len(b.Data)), []logging.Frame{ccf}) } - _, err = s.conn.WritePacket(b.Data, uint16(len(b.Data)), remoteAddr, info.OOB()) + _, err = s.conn.WritePacket(b.Data, remoteAddr, info.OOB(), 0, protocol.ECNUnsupported) return err } @@ -867,7 +869,7 @@ func (s *baseServer) maybeSendVersionNegotiationPacket(p receivedPacket) { _, src, dest, err := wire.ParseArbitraryLenConnectionIDs(p.data) if err != nil { // should never happen s.logger.Debugf("Dropping a packet with an unknown version for which we failed to parse connection IDs") - if s.tracer != nil { + if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket) } return @@ -876,10 +878,10 @@ func (s *baseServer) maybeSendVersionNegotiationPacket(p receivedPacket) { s.logger.Debugf("Client offered version %s, sending Version Negotiation", v) data := wire.ComposeVersionNegotiation(dest, src, s.config.Versions) - if s.tracer != nil { + if s.tracer != nil && s.tracer.SentVersionNegotiationPacket != nil { s.tracer.SentVersionNegotiationPacket(p.remoteAddr, src, dest, s.config.Versions) } - if _, err := s.conn.WritePacket(data, uint16(len(data)), p.remoteAddr, p.info.OOB()); err != nil { + if _, err := s.conn.WritePacket(data, p.remoteAddr, p.info.OOB(), 0, protocol.ECNUnsupported); err != nil { s.logger.Debugf("Error sending Version Negotiation: %s", err) } } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn.go b/vendor/github.com/quic-go/quic-go/sys_conn.go index c68477c31..71cc46070 100644 --- a/vendor/github.com/quic-go/quic-go/sys_conn.go +++ b/vendor/github.com/quic-go/quic-go/sys_conn.go @@ -1,7 +1,6 @@ package quic import ( - "fmt" "log" "net" "os" @@ -27,27 +26,7 @@ type OOBCapablePacketConn interface { var _ OOBCapablePacketConn = &net.UDPConn{} -// OptimizeConn takes a net.PacketConn and attempts to enable various optimizations that will improve QUIC performance: -// 1. It enables the Don't Fragment (DF) bit on the IP header. -// This is required to run DPLPMTUD (Path MTU Discovery, RFC 8899). -// 2. It enables reading of the ECN bits from the IP header. -// This allows the remote node to speed up its loss detection and recovery. -// 3. It uses batched syscalls (recvmmsg) to more efficiently receive packets from the socket. -// 4. It uses Generic Segmentation Offload (GSO) to efficiently send batches of packets (on Linux). -// -// In order for this to work, the connection needs to implement the OOBCapablePacketConn interface (as a *net.UDPConn does). -// -// It's only necessary to call this function explicitly if the application calls WriteTo -// after passing the connection to the Transport. -func OptimizeConn(c net.PacketConn) (net.PacketConn, error) { - return wrapConn(c) -} - -func wrapConn(pc net.PacketConn) (interface { - net.PacketConn - rawConn -}, error, -) { +func wrapConn(pc net.PacketConn) (rawConn, error) { if err := setReceiveBuffer(pc); err != nil { if !strings.Contains(err.Error(), "use of closed network connection") { setBufferWarningOnce.Do(func() { @@ -125,9 +104,12 @@ func (c *basicConn) ReadPacket() (receivedPacket, error) { }, nil } -func (c *basicConn) WritePacket(b []byte, packetSize uint16, addr net.Addr, _ []byte) (n int, err error) { - if uint16(len(b)) != packetSize { - panic(fmt.Sprintf("inconsistent length. got: %d. expected %d", packetSize, len(b))) +func (c *basicConn) WritePacket(b []byte, addr net.Addr, _ []byte, gsoSize uint16, ecn protocol.ECN) (n int, err error) { + if gsoSize != 0 { + panic("cannot use GSO with a basicConn") + } + if ecn != protocol.ECNUnsupported { + panic("cannot use ECN with a basicConn") } return c.PacketConn.WriteTo(b, addr) } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_df.go b/vendor/github.com/quic-go/quic-go/sys_conn_df.go index a21894122..0db61509d 100644 --- a/vendor/github.com/quic-go/quic-go/sys_conn_df.go +++ b/vendor/github.com/quic-go/quic-go/sys_conn_df.go @@ -1,4 +1,4 @@ -//go:build !linux && !windows +//go:build !linux && !windows && !darwin package quic @@ -11,7 +11,12 @@ func setDF(syscall.RawConn) (bool, error) { return false, nil } -func isMsgSizeErr(err error) bool { +func isSendMsgSizeErr(err error) bool { + // to be implemented for more specific platforms + return false +} + +func isRecvMsgSizeErr(err error) bool { // to be implemented for more specific platforms return false } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_df_darwin.go b/vendor/github.com/quic-go/quic-go/sys_conn_df_darwin.go new file mode 100644 index 000000000..b51cd8f1a --- /dev/null +++ b/vendor/github.com/quic-go/quic-go/sys_conn_df_darwin.go @@ -0,0 +1,74 @@ +//go:build darwin + +package quic + +import ( + "errors" + "strconv" + "strings" + "syscall" + + "golang.org/x/sys/unix" + + "github.com/quic-go/quic-go/internal/utils" +) + +func setDF(rawConn syscall.RawConn) (bool, error) { + // Setting DF bit is only supported from macOS11 + // https://github.com/chromium/chromium/blob/117.0.5881.2/net/socket/udp_socket_posix.cc#L555 + if supportsDF, err := isAtLeastMacOS11(); !supportsDF || err != nil { + return false, err + } + + // Enabling IP_DONTFRAG will force the kernel to return "sendto: message too long" + // and the datagram will not be fragmented + var errDFIPv4, errDFIPv6 error + if err := rawConn.Control(func(fd uintptr) { + errDFIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_DONTFRAG, 1) + errDFIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_DONTFRAG, 1) + }); err != nil { + return false, err + } + switch { + case errDFIPv4 == nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4 and IPv6.") + case errDFIPv4 == nil && errDFIPv6 != nil: + utils.DefaultLogger.Debugf("Setting DF for IPv4.") + case errDFIPv4 != nil && errDFIPv6 == nil: + utils.DefaultLogger.Debugf("Setting DF for IPv6.") + // On macOS, the syscall for setting DF bit for IPv4 fails on dual-stack listeners. + // Treat the connection as not having DF enabled, even though the DF bit will be set + // when used for IPv6. + // See https://github.com/quic-go/quic-go/issues/3793 for details. + return false, nil + case errDFIPv4 != nil && errDFIPv6 != nil: + return false, errors.New("setting DF failed for both IPv4 and IPv6") + } + return true, nil +} + +func isSendMsgSizeErr(err error) bool { + return errors.Is(err, unix.EMSGSIZE) +} + +func isRecvMsgSizeErr(error) bool { return false } + +func isAtLeastMacOS11() (bool, error) { + uname := &unix.Utsname{} + err := unix.Uname(uname) + if err != nil { + return false, err + } + + release := string(uname.Release[:]) + if idx := strings.Index(release, "."); idx != -1 { + version, err := strconv.Atoi(release[:idx]) + if err != nil { + return false, err + } + // Darwin version 20 is macOS version 11 + // https://en.wikipedia.org/wiki/Darwin_(operating_system)#Darwin_20_onwards + return version >= 20, nil + } + return false, nil +} diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_df_linux.go b/vendor/github.com/quic-go/quic-go/sys_conn_df_linux.go index 92f7e725e..f09eaa5df 100644 --- a/vendor/github.com/quic-go/quic-go/sys_conn_df_linux.go +++ b/vendor/github.com/quic-go/quic-go/sys_conn_df_linux.go @@ -4,22 +4,13 @@ package quic import ( "errors" - "log" - "os" - "strconv" "syscall" - "unsafe" "golang.org/x/sys/unix" "github.com/quic-go/quic-go/internal/utils" ) -// UDP_SEGMENT controls GSO (Generic Segmentation Offload) -// -//nolint:stylecheck -const UDP_SEGMENT = 103 - func setDF(rawConn syscall.RawConn) (bool, error) { // Enabling IP_MTU_DISCOVER will force the kernel to return "sendto: message too long" // and the datagram will not be fragmented @@ -43,41 +34,9 @@ func setDF(rawConn syscall.RawConn) (bool, error) { return true, nil } -func maybeSetGSO(rawConn syscall.RawConn) bool { - enable, _ := strconv.ParseBool(os.Getenv("QUIC_GO_ENABLE_GSO")) - if !enable { - return false - } - - var setErr error - if err := rawConn.Control(func(fd uintptr) { - setErr = unix.SetsockoptInt(int(fd), syscall.IPPROTO_UDP, UDP_SEGMENT, 1) - }); err != nil { - setErr = err - } - if setErr != nil { - log.Println("failed to enable GSO") - return false - } - return true -} - -func isMsgSizeErr(err error) bool { +func isSendMsgSizeErr(err error) bool { // https://man7.org/linux/man-pages/man7/udp.7.html return errors.Is(err, unix.EMSGSIZE) } -func appendUDPSegmentSizeMsg(b []byte, size uint16) []byte { - startLen := len(b) - const dataLen = 2 // payload is a uint16 - b = append(b, make([]byte, unix.CmsgSpace(dataLen))...) - h := (*unix.Cmsghdr)(unsafe.Pointer(&b[startLen])) - h.Level = syscall.IPPROTO_UDP - h.Type = UDP_SEGMENT - h.SetLen(unix.CmsgLen(dataLen)) - - // UnixRights uses the private `data` method, but I *think* this achieves the same goal. - offset := startLen + unix.CmsgSpace(0) - *(*uint16)(unsafe.Pointer(&b[offset])) = size - return b -} +func isRecvMsgSizeErr(error) bool { return false } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_df_windows.go b/vendor/github.com/quic-go/quic-go/sys_conn_df_windows.go index a7f1351a7..e27635ec9 100644 --- a/vendor/github.com/quic-go/quic-go/sys_conn_df_windows.go +++ b/vendor/github.com/quic-go/quic-go/sys_conn_df_windows.go @@ -43,7 +43,12 @@ func setDF(rawConn syscall.RawConn) (bool, error) { return true, nil } -func isMsgSizeErr(err error) bool { +func isSendMsgSizeErr(err error) bool { + // https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2 + return errors.Is(err, windows.WSAEMSGSIZE) +} + +func isRecvMsgSizeErr(err error) bool { // https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2 return errors.Is(err, windows.WSAEMSGSIZE) } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_helper_darwin.go b/vendor/github.com/quic-go/quic-go/sys_conn_helper_darwin.go index 7ad5f3af1..d761072f2 100644 --- a/vendor/github.com/quic-go/quic-go/sys_conn_helper_darwin.go +++ b/vendor/github.com/quic-go/quic-go/sys_conn_helper_darwin.go @@ -2,20 +2,35 @@ package quic -import "golang.org/x/sys/unix" +import ( + "encoding/binary" + "net/netip" + "syscall" -const msgTypeIPTOS = unix.IP_RECVTOS - -const ( - ipv4RECVPKTINFO = unix.IP_RECVPKTINFO - ipv6RECVPKTINFO = 0x3d + "golang.org/x/sys/unix" ) const ( - msgTypeIPv4PKTINFO = unix.IP_PKTINFO - msgTypeIPv6PKTINFO = 0x2e + msgTypeIPTOS = unix.IP_RECVTOS + ipv4PKTINFO = unix.IP_RECVPKTINFO ) +const ecnIPv4DataLen = 4 + // ReadBatch only returns a single packet on OSX, // see https://godoc.org/golang.org/x/net/ipv4#PacketConn.ReadBatch. const batchSize = 1 + +func parseIPv4PktInfo(body []byte) (ip netip.Addr, ifIndex uint32, ok bool) { + // struct in_pktinfo { + // unsigned int ipi_ifindex; /* Interface index */ + // struct in_addr ipi_spec_dst; /* Local address */ + // struct in_addr ipi_addr; /* Header Destination address */ + // }; + if len(body) != 12 { + return netip.Addr{}, 0, false + } + return netip.AddrFrom4(*(*[4]byte)(body[8:12])), binary.LittleEndian.Uint32(body), true +} + +func isGSOSupported(syscall.RawConn) bool { return false } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_helper_freebsd.go b/vendor/github.com/quic-go/quic-go/sys_conn_helper_freebsd.go index 8d16d0b91..a53ca2eae 100644 --- a/vendor/github.com/quic-go/quic-go/sys_conn_helper_freebsd.go +++ b/vendor/github.com/quic-go/quic-go/sys_conn_helper_freebsd.go @@ -2,20 +2,30 @@ package quic -import "golang.org/x/sys/unix" +import ( + "net/netip" + "syscall" + + "golang.org/x/sys/unix" +) const ( msgTypeIPTOS = unix.IP_RECVTOS + ipv4PKTINFO = 0x7 ) -const ( - ipv4RECVPKTINFO = 0x7 - ipv6RECVPKTINFO = 0x24 -) - -const ( - msgTypeIPv4PKTINFO = 0x7 - msgTypeIPv6PKTINFO = 0x2e -) +const ecnIPv4DataLen = 1 const batchSize = 8 + +func parseIPv4PktInfo(body []byte) (ip netip.Addr, _ uint32, ok bool) { + // struct in_pktinfo { + // struct in_addr ipi_addr; /* Header Destination address */ + // }; + if len(body) != 4 { + return netip.Addr{}, 0, false + } + return netip.AddrFrom4(*(*[4]byte)(body)), 0, true +} + +func isGSOSupported(syscall.RawConn) bool { return false } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go b/vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go index 2c12233cb..310c83741 100644 --- a/vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go +++ b/vendor/github.com/quic-go/quic-go/sys_conn_helper_linux.go @@ -3,22 +3,23 @@ package quic import ( + "encoding/binary" + "errors" + "net/netip" + "os" + "strconv" "syscall" + "unsafe" "golang.org/x/sys/unix" ) -const msgTypeIPTOS = unix.IP_TOS - const ( - ipv4RECVPKTINFO = unix.IP_PKTINFO - ipv6RECVPKTINFO = unix.IPV6_RECVPKTINFO + msgTypeIPTOS = unix.IP_TOS + ipv4PKTINFO = unix.IP_PKTINFO ) -const ( - msgTypeIPv4PKTINFO = unix.IP_PKTINFO - msgTypeIPv6PKTINFO = unix.IPV6_PKTINFO -) +const ecnIPv4DataLen = 1 const batchSize = 8 // needs to smaller than MaxUint8 (otherwise the type of oobConn.readPos has to be changed) @@ -41,3 +42,58 @@ func forceSetSendBuffer(c syscall.RawConn, bytes int) error { } return serr } + +func parseIPv4PktInfo(body []byte) (ip netip.Addr, ifIndex uint32, ok bool) { + // struct in_pktinfo { + // unsigned int ipi_ifindex; /* Interface index */ + // struct in_addr ipi_spec_dst; /* Local address */ + // struct in_addr ipi_addr; /* Header Destination address */ + // }; + if len(body) != 12 { + return netip.Addr{}, 0, false + } + return netip.AddrFrom4(*(*[4]byte)(body[8:12])), binary.LittleEndian.Uint32(body), true +} + +// isGSOSupported tests if the kernel supports GSO. +// Sending with GSO might still fail later on, if the interface doesn't support it (see isGSOError). +func isGSOSupported(conn syscall.RawConn) bool { + disabled, err := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_GSO")) + if err == nil && disabled { + return false + } + var serr error + if err := conn.Control(func(fd uintptr) { + _, serr = unix.GetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_SEGMENT) + }); err != nil { + return false + } + return serr == nil +} + +func appendUDPSegmentSizeMsg(b []byte, size uint16) []byte { + startLen := len(b) + const dataLen = 2 // payload is a uint16 + b = append(b, make([]byte, unix.CmsgSpace(dataLen))...) + h := (*unix.Cmsghdr)(unsafe.Pointer(&b[startLen])) + h.Level = syscall.IPPROTO_UDP + h.Type = unix.UDP_SEGMENT + h.SetLen(unix.CmsgLen(dataLen)) + + // UnixRights uses the private `data` method, but I *think* this achieves the same goal. + offset := startLen + unix.CmsgSpace(0) + *(*uint16)(unsafe.Pointer(&b[offset])) = size + return b +} + +func isGSOError(err error) bool { + var serr *os.SyscallError + if errors.As(err, &serr) { + // EIO is returned by udp_send_skb() if the device driver does not have tx checksums enabled, + // which is a hard requirement of UDP_SEGMENT. See: + // https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man7/udp.7?id=806eabd74910447f21005160e90957bde4db0183#n228 + // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/ipv4/udp.c?h=v6.2&id=c9c3395d5e3dcc6daee66c6908354d47bf98cb0c#n942 + return serr.Err == unix.EIO + } + return false +} diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_helper_nonlinux.go b/vendor/github.com/quic-go/quic-go/sys_conn_helper_nonlinux.go index 80b795c33..cace82d5d 100644 --- a/vendor/github.com/quic-go/quic-go/sys_conn_helper_nonlinux.go +++ b/vendor/github.com/quic-go/quic-go/sys_conn_helper_nonlinux.go @@ -4,3 +4,6 @@ package quic func forceSetReceiveBuffer(c any, bytes int) error { return nil } func forceSetSendBuffer(c any, bytes int) error { return nil } + +func appendUDPSegmentSizeMsg([]byte, uint16) []byte { return nil } +func isGSOError(error) bool { return false } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_no_gso.go b/vendor/github.com/quic-go/quic-go/sys_conn_no_gso.go deleted file mode 100644 index 6f6a8c917..000000000 --- a/vendor/github.com/quic-go/quic-go/sys_conn_no_gso.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build darwin || freebsd - -package quic - -import "syscall" - -func maybeSetGSO(_ syscall.RawConn) bool { return false } -func appendUDPSegmentSizeMsg(_ []byte, _ uint16) []byte { return nil } diff --git a/vendor/github.com/quic-go/quic-go/sys_conn_oob.go b/vendor/github.com/quic-go/quic-go/sys_conn_oob.go index fc28f94b7..71a037d5a 100644 --- a/vendor/github.com/quic-go/quic-go/sys_conn_oob.go +++ b/vendor/github.com/quic-go/quic-go/sys_conn_oob.go @@ -5,11 +5,15 @@ package quic import ( "encoding/binary" "errors" - "fmt" + "log" "net" "net/netip" + "os" + "strconv" + "sync" "syscall" "time" + "unsafe" "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" @@ -55,6 +59,11 @@ func inspectWriteBuffer(c syscall.RawConn) (int, error) { return size, serr } +func isECNDisabled() bool { + disabled, err := strconv.ParseBool(os.Getenv("QUIC_GO_DISABLE_ECN")) + return err == nil && disabled +} + type oobConn struct { OOBCapablePacketConn batchConn batchConn @@ -87,8 +96,8 @@ func newConn(c OOBCapablePacketConn, supportsDF bool) (*oobConn, error) { errECNIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_RECVTCLASS, 1) if needsPacketInfo { - errPIIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, ipv4RECVPKTINFO, 1) - errPIIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, ipv6RECVPKTINFO, 1) + errPIIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, ipv4PKTINFO, 1) + errPIIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_RECVPKTINFO, 1) } }); err != nil { return nil, err @@ -126,10 +135,6 @@ func newConn(c OOBCapablePacketConn, supportsDF bool) (*oobConn, error) { bc = ipv4.NewPacketConn(c) } - // Try enabling GSO. - // This will only succeed on Linux, and only for kernels > 4.18. - supportsGSO := maybeSetGSO(rawConn) - msgs := make([]ipv4.Message, batchSize) for i := range msgs { // preallocate the [][]byte @@ -140,15 +145,20 @@ func newConn(c OOBCapablePacketConn, supportsDF bool) (*oobConn, error) { batchConn: bc, messages: msgs, readPos: batchSize, + cap: connCapabilities{ + DF: supportsDF, + GSO: isGSOSupported(rawConn), + ECN: !isECNDisabled(), + }, } - oobConn.cap.DF = supportsDF - oobConn.cap.GSO = supportsGSO for i := 0; i < batchSize; i++ { oobConn.messages[i].OOB = make([]byte, oobBufferSize) } return oobConn, nil } +var invalidCmsgOnceV4, invalidCmsgOnceV6 sync.Once + func (c *oobConn) ReadPacket() (receivedPacket, error) { if len(c.messages) == int(c.readPos) { // all messages read. Read the next batch of messages. c.messages = c.messages[:batchSize] @@ -187,39 +197,37 @@ func (c *oobConn) ReadPacket() (receivedPacket, error) { if hdr.Level == unix.IPPROTO_IP { switch hdr.Type { case msgTypeIPTOS: - p.ecn = protocol.ECN(body[0] & ecnMask) - case msgTypeIPv4PKTINFO: - // struct in_pktinfo { - // unsigned int ipi_ifindex; /* Interface index */ - // struct in_addr ipi_spec_dst; /* Local address */ - // struct in_addr ipi_addr; /* Header Destination - // address */ - // }; - var ip [4]byte - if len(body) == 12 { - copy(ip[:], body[8:12]) - p.info.ifIndex = binary.LittleEndian.Uint32(body) - } else if len(body) == 4 { - // FreeBSD - copy(ip[:], body) + p.ecn = protocol.ParseECNHeaderBits(body[0] & ecnMask) + case ipv4PKTINFO: + ip, ifIndex, ok := parseIPv4PktInfo(body) + if ok { + p.info.addr = ip + p.info.ifIndex = ifIndex + } else { + invalidCmsgOnceV4.Do(func() { + log.Printf("Received invalid IPv4 packet info control message: %+x. "+ + "This should never occur, please open a new issue and include details about the architecture.", body) + }) } - p.info.addr = netip.AddrFrom4(ip) } } if hdr.Level == unix.IPPROTO_IPV6 { switch hdr.Type { case unix.IPV6_TCLASS: - p.ecn = protocol.ECN(body[0] & ecnMask) - case msgTypeIPv6PKTINFO: + p.ecn = protocol.ParseECNHeaderBits(body[0] & ecnMask) + case unix.IPV6_PKTINFO: // struct in6_pktinfo { // struct in6_addr ipi6_addr; /* src/dst IPv6 address */ // unsigned int ipi6_ifindex; /* send/recv interface index */ // }; if len(body) == 20 { - var ip [16]byte - copy(ip[:], body[:16]) - p.info.addr = netip.AddrFrom16(ip) + p.info.addr = netip.AddrFrom16(*(*[16]byte)(body[:16])) p.info.ifIndex = binary.LittleEndian.Uint32(body[16:]) + } else { + invalidCmsgOnceV6.Do(func() { + log.Printf("Received invalid IPv6 packet info control message: %+x. "+ + "This should never occur, please open a new issue and include details about the architecture.", body) + }) } } } @@ -228,25 +236,28 @@ func (c *oobConn) ReadPacket() (receivedPacket, error) { return p, nil } -// WriteTo (re)implements the net.PacketConn method. -// This is needed for users who call OptimizeConn to be able to send (non-QUIC) packets on the underlying connection. -// With GSO enabled, this would otherwise not be needed, as the kernel requires the UDP_SEGMENT message to be set. -func (c *oobConn) WriteTo(p []byte, addr net.Addr) (int, error) { - return c.WritePacket(p, uint16(len(p)), addr, nil) -} - // WritePacket writes a new packet. -// If the connection supports GSO (and we activated GSO support before), -// it appends the UDP_SEGMENT size message to oob. -// Callers are advised to make sure that oob has a sufficient capacity, -// such that appending the UDP_SEGMENT size message doesn't cause an allocation. -func (c *oobConn) WritePacket(b []byte, packetSize uint16, addr net.Addr, oob []byte) (n int, err error) { - if c.cap.GSO { - oob = appendUDPSegmentSizeMsg(oob, packetSize) - } else if uint16(len(b)) != packetSize { - panic(fmt.Sprintf("inconsistent length. got: %d. expected %d", packetSize, len(b))) +func (c *oobConn) WritePacket(b []byte, addr net.Addr, packetInfoOOB []byte, gsoSize uint16, ecn protocol.ECN) (int, error) { + oob := packetInfoOOB + if gsoSize > 0 { + if !c.capabilities().GSO { + panic("GSO disabled") + } + oob = appendUDPSegmentSizeMsg(oob, gsoSize) } - n, _, err = c.OOBCapablePacketConn.WriteMsgUDP(b, oob, addr.(*net.UDPAddr)) + if ecn != protocol.ECNUnsupported { + if !c.capabilities().ECN { + panic("tried to send a ECN-marked packet although ECN is disabled") + } + if remoteUDPAddr, ok := addr.(*net.UDPAddr); ok { + if remoteUDPAddr.IP.To4() != nil { + oob = appendIPv4ECNMsg(oob, ecn) + } else { + oob = appendIPv6ECNMsg(oob, ecn) + } + } + } + n, _, err := c.OOBCapablePacketConn.WriteMsgUDP(b, oob, addr.(*net.UDPAddr)) return n, err } @@ -289,3 +300,32 @@ func (info *packetInfo) OOB() []byte { } return nil } + +func appendIPv4ECNMsg(b []byte, val protocol.ECN) []byte { + startLen := len(b) + b = append(b, make([]byte, unix.CmsgSpace(ecnIPv4DataLen))...) + h := (*unix.Cmsghdr)(unsafe.Pointer(&b[startLen])) + h.Level = syscall.IPPROTO_IP + h.Type = unix.IP_TOS + h.SetLen(unix.CmsgLen(ecnIPv4DataLen)) + + // UnixRights uses the private `data` method, but I *think* this achieves the same goal. + offset := startLen + unix.CmsgSpace(0) + b[offset] = val.ToHeaderBits() + return b +} + +func appendIPv6ECNMsg(b []byte, val protocol.ECN) []byte { + startLen := len(b) + const dataLen = 4 + b = append(b, make([]byte, unix.CmsgSpace(dataLen))...) + h := (*unix.Cmsghdr)(unsafe.Pointer(&b[startLen])) + h.Level = syscall.IPPROTO_IPV6 + h.Type = unix.IPV6_TCLASS + h.SetLen(unix.CmsgLen(dataLen)) + + // UnixRights uses the private `data` method, but I *think* this achieves the same goal. + offset := startLen + unix.CmsgSpace(0) + b[offset] = val.ToHeaderBits() + return b +} diff --git a/vendor/github.com/quic-go/quic-go/tools.go b/vendor/github.com/quic-go/quic-go/tools.go index e848317f1..d00ce748d 100644 --- a/vendor/github.com/quic-go/quic-go/tools.go +++ b/vendor/github.com/quic-go/quic-go/tools.go @@ -3,6 +3,6 @@ package quic import ( - _ "github.com/golang/mock/mockgen" _ "github.com/onsi/ginkgo/v2/ginkgo" + _ "go.uber.org/mock/mockgen" ) diff --git a/vendor/github.com/quic-go/quic-go/transport.go b/vendor/github.com/quic-go/quic-go/transport.go index 57fdbfb4e..60d44a43e 100644 --- a/vendor/github.com/quic-go/quic-go/transport.go +++ b/vendor/github.com/quic-go/quic-go/transport.go @@ -7,15 +7,17 @@ import ( "errors" "net" "sync" + "sync/atomic" "time" - "github.com/quic-go/quic-go/internal/wire" - "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" + "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/logging" ) +var errListenerAlreadySet = errors.New("listener already set") + // The Transport is the central point to manage incoming and outgoing QUIC connections. // QUIC demultiplexes connections based on their QUIC Connection IDs, not based on the 4-tuple. // This means that a single UDP socket can be used for listening for incoming connections, as well as @@ -26,9 +28,16 @@ type Transport struct { // A single net.PacketConn can only be handled by one Transport. // Bad things will happen if passed to multiple Transports. // - // If not done by the user, the connection is passed through OptimizeConn to enable a number of optimizations. - // After passing the connection to the Transport, it's invalid to call ReadFrom on the connection. - // Calling WriteTo is only valid on the connection returned by OptimizeConn. + // A number of optimizations will be enabled if the connections implements the OOBCapablePacketConn interface, + // as a *net.UDPConn does. + // 1. It enables the Don't Fragment (DF) bit on the IP header. + // This is required to run DPLPMTUD (Path MTU Discovery, RFC 8899). + // 2. It enables reading of the ECN bits from the IP header. + // This allows the remote node to speed up its loss detection and recovery. + // 3. It uses batched syscalls (recvmmsg) to more efficiently receive packets from the socket. + // 4. It uses Generic Segmentation Offload (GSO) to efficiently send batches of packets (on Linux). + // + // After passing the connection to the Transport, it's invalid to call ReadFrom or WriteTo on the connection. Conn net.PacketConn // The length of the connection ID in bytes. @@ -50,8 +59,26 @@ type Transport struct { // See section 10.3 of RFC 9000 for details. StatelessResetKey *StatelessResetKey + // The TokenGeneratorKey is used to encrypt session resumption tokens. + // If no key is configured, a random key will be generated. + // If multiple servers are authoritative for the same domain, they should use the same key, + // see section 8.1.3 of RFC 9000 for details. + TokenGeneratorKey *TokenGeneratorKey + + // MaxTokenAge is the maximum age of the resumption token presented during the handshake. + // These tokens allow skipping address resumption when resuming a QUIC connection, + // and are especially useful when using 0-RTT. + // If not set, it defaults to 24 hours. + // See section 8.1.3 of RFC 9000 for details. + MaxTokenAge time.Duration + + // DisableVersionNegotiationPackets disables the sending of Version Negotiation packets. + // This can be useful if version information is exchanged out-of-band. + // It has no effect for clients. + DisableVersionNegotiationPackets bool + // A Tracer traces events that don't belong to a single QUIC connection. - Tracer logging.Tracer + Tracer *logging.Tracer handlerMap packetHandlerManager @@ -66,7 +93,7 @@ type Transport struct { // If no ConnectionIDGenerator is set, this is set to a default. connIDGenerator ConnectionIDGenerator - server unknownPacketHandler + server *baseServer conn rawConn @@ -78,6 +105,9 @@ type Transport struct { createdConn bool isSingleUse bool // was created for a single server or client, i.e. by calling quic.Listen or quic.Dial + readingNonQUICPackets atomic.Bool + nonQUICPackets chan receivedPacket + logger utils.Logger } @@ -85,28 +115,10 @@ type Transport struct { // There can only be a single listener on any net.PacketConn. // Listen may only be called again after the current Listener was closed. func (t *Transport) Listen(tlsConf *tls.Config, conf *Config) (*Listener, error) { - if tlsConf == nil { - return nil, errors.New("quic: tls.Config not set") - } - if err := validateConfig(conf); err != nil { - return nil, err - } - - t.mutex.Lock() - defer t.mutex.Unlock() - - if t.server != nil { - return nil, errListenerAlreadySet - } - conf = populateServerConfig(conf) - if err := t.init(true); err != nil { - return nil, err - } - s, err := newServer(t.conn, t.handlerMap, t.connIDGenerator, tlsConf, conf, t.Tracer, t.closeServer, false) + s, err := t.createServer(tlsConf, conf, false) if err != nil { return nil, err } - t.server = s return &Listener{baseServer: s}, nil } @@ -114,6 +126,14 @@ func (t *Transport) Listen(tlsConf *tls.Config, conf *Config) (*Listener, error) // There can only be a single listener on any net.PacketConn. // Listen may only be called again after the current Listener was closed. func (t *Transport) ListenEarly(tlsConf *tls.Config, conf *Config) (*EarlyListener, error) { + s, err := t.createServer(tlsConf, conf, true) + if err != nil { + return nil, err + } + return &EarlyListener{baseServer: s}, nil +} + +func (t *Transport) createServer(tlsConf *tls.Config, conf *Config, allow0RTT bool) (*baseServer, error) { if tlsConf == nil { return nil, errors.New("quic: tls.Config not set") } @@ -128,53 +148,56 @@ func (t *Transport) ListenEarly(tlsConf *tls.Config, conf *Config) (*EarlyListen return nil, errListenerAlreadySet } conf = populateServerConfig(conf) - if err := t.init(true); err != nil { - return nil, err - } - s, err := newServer(t.conn, t.handlerMap, t.connIDGenerator, tlsConf, conf, t.Tracer, t.closeServer, true) - if err != nil { + if err := t.init(false); err != nil { return nil, err } + s := newServer( + t.conn, + t.handlerMap, + t.connIDGenerator, + tlsConf, + conf, + t.Tracer, + t.closeServer, + *t.TokenGeneratorKey, + t.MaxTokenAge, + t.DisableVersionNegotiationPackets, + allow0RTT, + ) t.server = s - return &EarlyListener{baseServer: s}, nil + return s, nil } // Dial dials a new connection to a remote host (not using 0-RTT). func (t *Transport) Dial(ctx context.Context, addr net.Addr, tlsConf *tls.Config, conf *Config) (Connection, error) { - if err := validateConfig(conf); err != nil { - return nil, err - } - conf = populateConfig(conf) - if err := t.init(false); err != nil { - return nil, err - } - var onClose func() - if t.isSingleUse { - onClose = func() { t.Close() } - } - return dial(ctx, newSendConn(t.conn, addr), t.connIDGenerator, t.handlerMap, tlsConf, conf, onClose, false) + return t.dial(ctx, addr, "", tlsConf, conf, false) } // DialEarly dials a new connection, attempting to use 0-RTT if possible. func (t *Transport) DialEarly(ctx context.Context, addr net.Addr, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) { + return t.dial(ctx, addr, "", tlsConf, conf, true) +} + +func (t *Transport) dial(ctx context.Context, addr net.Addr, host string, tlsConf *tls.Config, conf *Config, use0RTT bool) (EarlyConnection, error) { if err := validateConfig(conf); err != nil { return nil, err } conf = populateConfig(conf) - if err := t.init(false); err != nil { + if err := t.init(t.isSingleUse); err != nil { return nil, err } var onClose func() if t.isSingleUse { onClose = func() { t.Close() } } - return dial(ctx, newSendConn(t.conn, addr), t.connIDGenerator, t.handlerMap, tlsConf, conf, onClose, true) + tlsConf = tlsConf.Clone() + tlsConf.MinVersion = tls.VersionTLS13 + setTLSConfigServerName(tlsConf, addr, host) + return dial(ctx, newSendConn(t.conn, addr, packetInfo{}, utils.DefaultLogger), t.connIDGenerator, t.handlerMap, tlsConf, conf, onClose, use0RTT) } -func (t *Transport) init(isServer bool) error { +func (t *Transport) init(allowZeroLengthConnIDs bool) error { t.initOnce.Do(func() { - getMultiplexer().AddConn(t.Conn) - var conn rawConn if c, ok := t.Conn.(rawConn); ok { conn = c @@ -186,7 +209,6 @@ func (t *Transport) init(isServer bool) error { return } } - t.conn = conn t.logger = utils.DefaultLogger // TODO: make this configurable t.conn = conn @@ -195,25 +217,42 @@ func (t *Transport) init(isServer bool) error { t.closeQueue = make(chan closePacket, 4) t.statelessResetQueue = make(chan receivedPacket, 4) + if t.TokenGeneratorKey == nil { + var key TokenGeneratorKey + if _, err := rand.Read(key[:]); err != nil { + t.initErr = err + return + } + t.TokenGeneratorKey = &key + } if t.ConnectionIDGenerator != nil { t.connIDGenerator = t.ConnectionIDGenerator t.connIDLen = t.ConnectionIDGenerator.ConnectionIDLen() } else { connIDLen := t.ConnectionIDLength - if t.ConnectionIDLength == 0 && (!t.isSingleUse || isServer) { + if t.ConnectionIDLength == 0 && !allowZeroLengthConnIDs { connIDLen = protocol.DefaultConnectionIDLength } t.connIDLen = connIDLen t.connIDGenerator = &protocol.DefaultConnectionIDGenerator{ConnLen: t.connIDLen} } + getMultiplexer().AddConn(t.Conn) go t.listen(conn) go t.runSendQueue() }) return t.initErr } +// WriteTo sends a packet on the underlying connection. +func (t *Transport) WriteTo(b []byte, addr net.Addr) (int, error) { + if err := t.init(false); err != nil { + return 0, err + } + return t.conn.WritePacket(b, addr, nil, 0, protocol.ECNUnsupported) +} + func (t *Transport) enqueueClosePacket(p closePacket) { select { case t.closeQueue <- p: @@ -229,7 +268,7 @@ func (t *Transport) runSendQueue() { case <-t.listening: return case p := <-t.closeQueue: - t.conn.WritePacket(p.payload, uint16(len(p.payload)), p.addr, p.info.OOB()) + t.conn.WritePacket(p.payload, p.addr, p.info.OOB(), 0, protocol.ECNUnsupported) case p := <-t.statelessResetQueue: t.sendStatelessReset(p) } @@ -312,6 +351,10 @@ func (t *Transport) listen(conn rawConn) { continue } if err != nil { + // Windows returns an error when receiving a UDP datagram that doesn't fit into the provided buffer. + if isRecvMsgSizeErr(err) { + continue + } t.close(err) return } @@ -320,10 +363,17 @@ func (t *Transport) listen(conn rawConn) { } func (t *Transport) handlePacket(p receivedPacket) { + if len(p.data) == 0 { + return + } + if !wire.IsPotentialQUICPacket(p.data[0]) && !wire.IsLongHeaderPacket(p.data[0]) { + t.handleNonQUICPacket(p) + return + } connID, err := wire.ParseConnectionID(p.data, t.connIDLen) if err != nil { t.logger.Debugf("error parsing connection ID on packet from %s: %s", p.remoteAddr, err) - if t.Tracer != nil { + if t.Tracer != nil && t.Tracer.DroppedPacket != nil { t.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) } p.buffer.MaybeRelease() @@ -386,7 +436,7 @@ func (t *Transport) sendStatelessReset(p receivedPacket) { rand.Read(data) data[0] = (data[0] & 0x7f) | 0x40 data = append(data, token[:]...) - if _, err := t.conn.WritePacket(data, uint16(len(data)), p.remoteAddr, p.info.OOB()); err != nil { + if _, err := t.conn.WritePacket(data, p.remoteAddr, p.info.OOB(), 0, protocol.ECNUnsupported); err != nil { t.logger.Debugf("Error sending Stateless Reset to %s: %s", p.remoteAddr, err) } } @@ -408,3 +458,61 @@ func (t *Transport) maybeHandleStatelessReset(data []byte) bool { } return false } + +func (t *Transport) handleNonQUICPacket(p receivedPacket) { + // Strictly speaking, this is racy, + // but we only care about receiving packets at some point after ReadNonQUICPacket has been called. + if !t.readingNonQUICPackets.Load() { + return + } + select { + case t.nonQUICPackets <- p: + default: + if t.Tracer != nil && t.Tracer.DroppedPacket != nil { + t.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropDOSPrevention) + } + } +} + +const maxQueuedNonQUICPackets = 32 + +// ReadNonQUICPacket reads non-QUIC packets received on the underlying connection. +// The detection logic is very simple: Any packet that has the first and second bit of the packet set to 0. +// Note that this is stricter than the detection logic defined in RFC 9443. +func (t *Transport) ReadNonQUICPacket(ctx context.Context, b []byte) (int, net.Addr, error) { + if err := t.init(false); err != nil { + return 0, nil, err + } + if !t.readingNonQUICPackets.Load() { + t.nonQUICPackets = make(chan receivedPacket, maxQueuedNonQUICPackets) + t.readingNonQUICPackets.Store(true) + } + select { + case <-ctx.Done(): + return 0, nil, ctx.Err() + case p := <-t.nonQUICPackets: + n := copy(b, p.data) + return n, p.remoteAddr, nil + case <-t.listening: + return 0, nil, errors.New("closed") + } +} + +func setTLSConfigServerName(tlsConf *tls.Config, addr net.Addr, host string) { + // If no ServerName is set, infer the ServerName from the host we're connecting to. + if tlsConf.ServerName != "" { + return + } + if host == "" { + if udpAddr, ok := addr.(*net.UDPAddr); ok { + tlsConf.ServerName = udpAddr.IP.String() + return + } + } + h, _, err := net.SplitHostPort(host) + if err != nil { // This happens if the host doesn't contain a port number. + tlsConf.ServerName = host + return + } + tlsConf.ServerName = h +} diff --git a/vendor/github.com/quic-go/webtransport-go/client.go b/vendor/github.com/quic-go/webtransport-go/client.go index ffcb7497d..8efe4d75a 100644 --- a/vendor/github.com/quic-go/webtransport-go/client.go +++ b/vendor/github.com/quic-go/webtransport-go/client.go @@ -92,7 +92,7 @@ func (d *Dialer) Dial(ctx context.Context, urlStr string, reqHdr http.Header) (* if reqHdr == nil { reqHdr = http.Header{} } - reqHdr.Add(webTransportDraftOfferHeaderKey, "1") + reqHdr.Set(webTransportDraftOfferHeaderKey, "1") req := &http.Request{ Method: http.MethodConnect, Header: reqHdr, diff --git a/vendor/github.com/quic-go/webtransport-go/errors.go b/vendor/github.com/quic-go/webtransport-go/errors.go index 9929513e4..108627f6a 100644 --- a/vendor/github.com/quic-go/webtransport-go/errors.go +++ b/vendor/github.com/quic-go/webtransport-go/errors.go @@ -8,14 +8,14 @@ import ( ) // StreamErrorCode is an error code used for stream termination. -type StreamErrorCode uint8 +type StreamErrorCode uint32 // SessionErrorCode is an error code for session termination. type SessionErrorCode uint32 const ( firstErrorCode = 0x52e4a40fa8db - lastErrorCode = 0x52e4a40fa9e2 + lastErrorCode = 0x52e5ac983162 ) func webtransportCodeToHTTPCode(n StreamErrorCode) quic.StreamErrorCode { diff --git a/vendor/github.com/quic-go/webtransport-go/version.json b/vendor/github.com/quic-go/webtransport-go/version.json index ef97c9ca1..42c14d1be 100644 --- a/vendor/github.com/quic-go/webtransport-go/version.json +++ b/vendor/github.com/quic-go/webtransport-go/version.json @@ -1,3 +1,3 @@ { - "version": "v0.5.3" + "version": "v0.6.0" } diff --git a/vendor/github.com/status-im/rendezvous/server/server.go b/vendor/github.com/status-im/rendezvous/server/server.go index ef452e5f4..f6559c46e 100644 --- a/vendor/github.com/status-im/rendezvous/server/server.go +++ b/vendor/github.com/status-im/rendezvous/server/server.go @@ -154,7 +154,7 @@ func (srv *Server) startListener() error { } } }) - addr, err := ma.NewMultiaddr(fmt.Sprintf("/ethv4/%s", h.ID().Pretty())) + addr, err := ma.NewMultiaddr(fmt.Sprintf("/ethv4/%s", h.ID())) if err != nil { return err } diff --git a/vendor/github.com/stretchr/objx/README.md b/vendor/github.com/stretchr/objx/README.md index 246660b21..78dc1f8b0 100644 --- a/vendor/github.com/stretchr/objx/README.md +++ b/vendor/github.com/stretchr/objx/README.md @@ -4,20 +4,20 @@ [![Maintainability](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/maintainability)](https://codeclimate.com/github/stretchr/objx/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/test_coverage)](https://codeclimate.com/github/stretchr/objx/test_coverage) [![Sourcegraph](https://sourcegraph.com/github.com/stretchr/objx/-/badge.svg)](https://sourcegraph.com/github.com/stretchr/objx) -[![GoDoc](https://godoc.org/github.com/stretchr/objx?status.svg)](https://godoc.org/github.com/stretchr/objx) +[![GoDoc](https://pkg.go.dev/badge/github.com/stretchr/objx?utm_source=godoc)](https://pkg.go.dev/github.com/stretchr/objx) Objx - Go package for dealing with maps, slices, JSON and other data. Get started: - Install Objx with [one line of code](#installation), or [update it with another](#staying-up-to-date) -- Check out the API Documentation http://godoc.org/github.com/stretchr/objx +- Check out the API Documentation http://pkg.go.dev/github.com/stretchr/objx ## Overview Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes a powerful `Get` method (among others) that allows you to easily and quickly get access to data within the map, without having to worry too much about type assertions, missing data, default values etc. ### Pattern -Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going: +Objx uses a predictable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going: m, err := objx.FromJSON(json) @@ -74,7 +74,7 @@ To update Objx to the latest version, run: go get -u github.com/stretchr/objx ### Supported go versions -We support the lastest three major Go versions, which are 1.10, 1.11 and 1.12 at the moment. +We currently support the three recent major Go versions. ## Contributing Please feel free to submit issues, fork the repository and send pull requests! diff --git a/vendor/github.com/stretchr/objx/Taskfile.yml b/vendor/github.com/stretchr/objx/Taskfile.yml index 7746f516d..8a79e8d67 100644 --- a/vendor/github.com/stretchr/objx/Taskfile.yml +++ b/vendor/github.com/stretchr/objx/Taskfile.yml @@ -1,7 +1,4 @@ -version: '2' - -env: - GOFLAGS: -mod=vendor +version: '3' tasks: default: diff --git a/vendor/github.com/stretchr/objx/accessors.go b/vendor/github.com/stretchr/objx/accessors.go index 4c6045588..72f1d1c1c 100644 --- a/vendor/github.com/stretchr/objx/accessors.go +++ b/vendor/github.com/stretchr/objx/accessors.go @@ -14,17 +14,17 @@ const ( // For example, `location.address.city` PathSeparator string = "." - // arrayAccesRegexString is the regex used to extract the array number + // arrayAccessRegexString is the regex used to extract the array number // from the access path - arrayAccesRegexString = `^(.+)\[([0-9]+)\]$` + arrayAccessRegexString = `^(.+)\[([0-9]+)\]$` // mapAccessRegexString is the regex used to extract the map key // from the access path mapAccessRegexString = `^([^\[]*)\[([^\]]+)\](.*)$` ) -// arrayAccesRegex is the compiled arrayAccesRegexString -var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString) +// arrayAccessRegex is the compiled arrayAccessRegexString +var arrayAccessRegex = regexp.MustCompile(arrayAccessRegexString) // mapAccessRegex is the compiled mapAccessRegexString var mapAccessRegex = regexp.MustCompile(mapAccessRegexString) @@ -37,11 +37,11 @@ var mapAccessRegex = regexp.MustCompile(mapAccessRegexString) // // Get can only operate directly on map[string]interface{} and []interface. // -// Example +// # Example // // To access the title of the third chapter of the second book, do: // -// o.Get("books[1].chapters[2].title") +// o.Get("books[1].chapters[2].title") func (m Map) Get(selector string) *Value { rawObj := access(m, selector, nil, false) return &Value{data: rawObj} @@ -52,26 +52,26 @@ func (m Map) Get(selector string) *Value { // // Set can only operate directly on map[string]interface{} and []interface // -// Example +// # Example // // To set the title of the third chapter of the second book, do: // -// o.Set("books[1].chapters[2].title","Time to Go") +// o.Set("books[1].chapters[2].title","Time to Go") func (m Map) Set(selector string, value interface{}) Map { access(m, selector, value, true) return m } -// getIndex returns the index, which is hold in s by two braches. -// It also returns s withour the index part, e.g. name[1] will return (1, name). +// getIndex returns the index, which is hold in s by two branches. +// It also returns s without the index part, e.g. name[1] will return (1, name). // If no index is found, -1 is returned func getIndex(s string) (int, string) { - arrayMatches := arrayAccesRegex.FindStringSubmatch(s) + arrayMatches := arrayAccessRegex.FindStringSubmatch(s) if len(arrayMatches) > 0 { // Get the key into the map selector := arrayMatches[1] // Get the index into the array at the key - // We know this cannt fail because arrayMatches[2] is an int for sure + // We know this can't fail because arrayMatches[2] is an int for sure index, _ := strconv.Atoi(arrayMatches[2]) return index, selector } diff --git a/vendor/github.com/stretchr/objx/conversions.go b/vendor/github.com/stretchr/objx/conversions.go index 080aa46e4..01c63d7d3 100644 --- a/vendor/github.com/stretchr/objx/conversions.go +++ b/vendor/github.com/stretchr/objx/conversions.go @@ -15,7 +15,7 @@ import ( const SignatureSeparator = "_" // URLValuesSliceKeySuffix is the character that is used to -// specify a suffic for slices parsed by URLValues. +// specify a suffix for slices parsed by URLValues. // If the suffix is set to "[i]", then the index of the slice // is used in place of i // Ex: Suffix "[]" would have the form a[]=b&a[]=c @@ -30,7 +30,7 @@ const ( ) // SetURLValuesSliceKeySuffix sets the character that is used to -// specify a suffic for slices parsed by URLValues. +// specify a suffix for slices parsed by URLValues. // If the suffix is set to "[i]", then the index of the slice // is used in place of i // Ex: Suffix "[]" would have the form a[]=b&a[]=c diff --git a/vendor/github.com/stretchr/objx/doc.go b/vendor/github.com/stretchr/objx/doc.go index 6d6af1a83..b170af74b 100644 --- a/vendor/github.com/stretchr/objx/doc.go +++ b/vendor/github.com/stretchr/objx/doc.go @@ -1,19 +1,19 @@ /* -Objx - Go package for dealing with maps, slices, JSON and other data. +Package objx provides utilities for dealing with maps, slices, JSON and other data. -Overview +# Overview Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes a powerful `Get` method (among others) that allows you to easily and quickly get access to data within the map, without having to worry too much about type assertions, missing data, default values etc. -Pattern +# Pattern -Objx uses a preditable pattern to make access data from within `map[string]interface{}` easy. +Objx uses a predictable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going: - m, err := objx.FromJSON(json) + m, err := objx.FromJSON(json) NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, the rest will be optimistic and try to figure things out without panicking. @@ -21,46 +21,46 @@ the rest will be optimistic and try to figure things out without panicking. Use `Get` to access the value you're interested in. You can use dot and array notation too: - m.Get("places[0].latlng") + m.Get("places[0].latlng") Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. - if m.Get("code").IsStr() { // Your code... } + if m.Get("code").IsStr() { // Your code... } Or you can just assume the type, and use one of the strong type methods to extract the real value: - m.Get("code").Int() + m.Get("code").Int() If there's no value there (or if it's the wrong type) then a default value will be returned, or you can be explicit about the default value. - Get("code").Int(-1) + Get("code").Int(-1) If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, manipulating and selecting that data. You can find out more by exploring the index below. -Reading data +# Reading data A simple example of how to use Objx: - // Use MustFromJSON to make an objx.Map from some JSON - m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) + // Use MustFromJSON to make an objx.Map from some JSON + m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) - // Get the details - name := m.Get("name").Str() - age := m.Get("age").Int() + // Get the details + name := m.Get("name").Str() + age := m.Get("age").Int() - // Get their nickname (or use their name if they don't have one) - nickname := m.Get("nickname").Str(name) + // Get their nickname (or use their name if they don't have one) + nickname := m.Get("nickname").Str(name) -Ranging +# Ranging Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For example, to `range` the data, do what you would expect: - m := objx.MustFromJSON(json) - for key, value := range m { - // Your code... - } + m := objx.MustFromJSON(json) + for key, value := range m { + // Your code... + } */ package objx diff --git a/vendor/github.com/stretchr/objx/map.go b/vendor/github.com/stretchr/objx/map.go index a64712a08..ab9f9ae67 100644 --- a/vendor/github.com/stretchr/objx/map.go +++ b/vendor/github.com/stretchr/objx/map.go @@ -47,17 +47,16 @@ func New(data interface{}) Map { // // The arguments follow a key, value pattern. // -// // Returns nil if any key argument is non-string or if there are an odd number of arguments. // -// Example +// # Example // // To easily create Maps: // -// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true)) +// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true)) // -// // creates an Map equivalent to -// m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}} +// // creates an Map equivalent to +// m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}} func MSI(keyAndValuePairs ...interface{}) Map { newMap := Map{} keyAndValuePairsLen := len(keyAndValuePairs) diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go index b774da88d..4d4b4aad6 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -28,6 +28,8 @@ var ( uint32Type = reflect.TypeOf(uint32(1)) uint64Type = reflect.TypeOf(uint64(1)) + uintptrType = reflect.TypeOf(uintptr(1)) + float32Type = reflect.TypeOf(float32(1)) float64Type = reflect.TypeOf(float64(1)) @@ -308,11 +310,11 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { case reflect.Struct: { // All structs enter here. We're not interested in most types. - if !canConvert(obj1Value, timeType) { + if !obj1Value.CanConvert(timeType) { break } - // time.Time can compared! + // time.Time can be compared! timeObj1, ok := obj1.(time.Time) if !ok { timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time) @@ -328,7 +330,7 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { case reflect.Slice: { // We only care about the []byte type. - if !canConvert(obj1Value, bytesType) { + if !obj1Value.CanConvert(bytesType) { break } @@ -345,6 +347,26 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { return CompareType(bytes.Compare(bytesObj1, bytesObj2)), true } + case reflect.Uintptr: + { + uintptrObj1, ok := obj1.(uintptr) + if !ok { + uintptrObj1 = obj1Value.Convert(uintptrType).Interface().(uintptr) + } + uintptrObj2, ok := obj2.(uintptr) + if !ok { + uintptrObj2 = obj2Value.Convert(uintptrType).Interface().(uintptr) + } + if uintptrObj1 > uintptrObj2 { + return compareGreater, true + } + if uintptrObj1 == uintptrObj2 { + return compareEqual, true + } + if uintptrObj1 < uintptrObj2 { + return compareLess, true + } + } } return compareEqual, false diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go b/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go deleted file mode 100644 index da867903e..000000000 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare_can_convert.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build go1.17 -// +build go1.17 - -// TODO: once support for Go 1.16 is dropped, this file can be -// merged/removed with assertion_compare_go1.17_test.go and -// assertion_compare_legacy.go - -package assert - -import "reflect" - -// Wrapper around reflect.Value.CanConvert, for compatibility -// reasons. -func canConvert(value reflect.Value, to reflect.Type) bool { - return value.CanConvert(to) -} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go b/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go deleted file mode 100644 index 1701af2a3..000000000 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare_legacy.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build !go1.17 -// +build !go1.17 - -// TODO: once support for Go 1.16 is dropped, this file can be -// merged/removed with assertion_compare_go1.17_test.go and -// assertion_compare_can_convert.go - -package assert - -import "reflect" - -// Older versions of Go does not have the reflect.Value.CanConvert -// method. -func canConvert(value reflect.Value, to reflect.Type) bool { - return false -} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index 84dbd6c79..3ddab109a 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -1,7 +1,4 @@ -/* -* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen -* THIS FILE MUST NOT BE EDITED BY HAND - */ +// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. package assert @@ -107,7 +104,7 @@ func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...) } -// EqualValuesf asserts that two objects are equal or convertable to the same types +// EqualValuesf asserts that two objects are equal or convertible to the same types // and equal. // // assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") @@ -616,6 +613,16 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...) } +// NotImplementsf asserts that an object does not implement the specified interface. +// +// assert.NotImplementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +func NotImplementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotImplements(t, interfaceObject, object, append([]interface{}{msg}, args...)...) +} + // NotNilf asserts that the specified object is not nil. // // assert.NotNilf(t, err, "error message %s", "formatted") @@ -660,10 +667,12 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...) } -// NotSubsetf asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubsetf asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") +// assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -747,10 +756,11 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg return Same(t, expected, actual, append([]interface{}{msg}, args...)...) } -// Subsetf asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subsetf asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") +// assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index b1d94aec5..a84e09bd4 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -1,7 +1,4 @@ -/* -* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen -* THIS FILE MUST NOT BE EDITED BY HAND - */ +// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. package assert @@ -189,7 +186,7 @@ func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface return EqualExportedValuesf(a.t, expected, actual, msg, args...) } -// EqualValues asserts that two objects are equal or convertable to the same types +// EqualValues asserts that two objects are equal or convertible to the same types // and equal. // // a.EqualValues(uint32(123), int32(123)) @@ -200,7 +197,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn return EqualValues(a.t, expected, actual, msgAndArgs...) } -// EqualValuesf asserts that two objects are equal or convertable to the same types +// EqualValuesf asserts that two objects are equal or convertible to the same types // and equal. // // a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") @@ -1221,6 +1218,26 @@ func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...in return NotErrorIsf(a.t, err, target, msg, args...) } +// NotImplements asserts that an object does not implement the specified interface. +// +// a.NotImplements((*MyInterface)(nil), new(MyObject)) +func (a *Assertions) NotImplements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotImplements(a.t, interfaceObject, object, msgAndArgs...) +} + +// NotImplementsf asserts that an object does not implement the specified interface. +// +// a.NotImplementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +func (a *Assertions) NotImplementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotImplementsf(a.t, interfaceObject, object, msg, args...) +} + // NotNil asserts that the specified object is not nil. // // a.NotNil(err) @@ -1309,10 +1326,12 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri return NotSamef(a.t, expected, actual, msg, args...) } -// NotSubset asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubset asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2]) +// a.NotSubset({"x": 1, "y": 2}, {"z": 3}) func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1320,10 +1339,12 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs return NotSubset(a.t, list, subset, msgAndArgs...) } -// NotSubsetf asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubsetf asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") +// a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1483,10 +1504,11 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, return Samef(a.t, expected, actual, msg, args...) } -// Subset asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subset asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2]) +// a.Subset({"x": 1, "y": 2}, {"x": 1}) func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1494,10 +1516,11 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... return Subset(a.t, list, subset, msgAndArgs...) } -// Subsetf asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subsetf asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") +// a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index a55d1bba9..0b7570f21 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -19,7 +19,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/pmezard/go-difflib/difflib" - yaml "gopkg.in/yaml.v3" + "gopkg.in/yaml.v3" ) //go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl" @@ -110,7 +110,12 @@ func copyExportedFields(expected interface{}) interface{} { return result.Interface() case reflect.Array, reflect.Slice: - result := reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len()) + var result reflect.Value + if expectedKind == reflect.Array { + result = reflect.New(reflect.ArrayOf(expectedValue.Len(), expectedType.Elem())).Elem() + } else { + result = reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len()) + } for i := 0; i < expectedValue.Len(); i++ { index := expectedValue.Index(i) if isNil(index) { @@ -140,6 +145,8 @@ func copyExportedFields(expected interface{}) interface{} { // structures. // // This function does no assertion of any kind. +// +// Deprecated: Use [EqualExportedValues] instead. func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { expectedCleaned := copyExportedFields(expected) actualCleaned := copyExportedFields(actual) @@ -153,17 +160,40 @@ func ObjectsAreEqualValues(expected, actual interface{}) bool { return true } - actualType := reflect.TypeOf(actual) - if actualType == nil { + expectedValue := reflect.ValueOf(expected) + actualValue := reflect.ValueOf(actual) + if !expectedValue.IsValid() || !actualValue.IsValid() { return false } - expectedValue := reflect.ValueOf(expected) - if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { - // Attempt comparison after type conversion - return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual) + + expectedType := expectedValue.Type() + actualType := actualValue.Type() + if !expectedType.ConvertibleTo(actualType) { + return false } - return false + if !isNumericType(expectedType) || !isNumericType(actualType) { + // Attempt comparison after type conversion + return reflect.DeepEqual( + expectedValue.Convert(actualType).Interface(), actual, + ) + } + + // If BOTH values are numeric, there are chances of false positives due + // to overflow or underflow. So, we need to make sure to always convert + // the smaller type to a larger type before comparing. + if expectedType.Size() >= actualType.Size() { + return actualValue.Convert(expectedType).Interface() == expected + } + + return expectedValue.Convert(actualType).Interface() == actual +} + +// isNumericType returns true if the type is one of: +// int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, +// float32, float64, complex64, complex128 +func isNumericType(t reflect.Type) bool { + return t.Kind() >= reflect.Int && t.Kind() <= reflect.Complex128 } /* CallerInfo is necessary because the assert functions use the testing object @@ -266,7 +296,7 @@ func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { // Aligns the provided message so that all lines after the first line start at the same location as the first line. // Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab). -// The longestLabelLen parameter specifies the length of the longest label in the output (required becaues this is the +// The longestLabelLen parameter specifies the length of the longest label in the output (required because this is the // basis on which the alignment occurs). func indentMessageLines(message string, longestLabelLen int) string { outBuf := new(bytes.Buffer) @@ -382,6 +412,25 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg return true } +// NotImplements asserts that an object does not implement the specified interface. +// +// assert.NotImplements(t, (*MyInterface)(nil), new(MyObject)) +func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + interfaceType := reflect.TypeOf(interfaceObject).Elem() + + if object == nil { + return Fail(t, fmt.Sprintf("Cannot check if nil does not implement %v", interfaceType), msgAndArgs...) + } + if reflect.TypeOf(object).Implements(interfaceType) { + return Fail(t, fmt.Sprintf("%T implements %v", object, interfaceType), msgAndArgs...) + } + + return true +} + // IsType asserts that the specified objects are of the same type. func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { @@ -496,7 +545,7 @@ func samePointers(first, second interface{}) bool { // representations appropriate to be presented to the user. // // If the values are not of like type, the returned strings will be prefixed -// with the type name, and the value will be enclosed in parenthesis similar +// with the type name, and the value will be enclosed in parentheses similar // to a type conversion in the Go grammar. func formatUnequalValues(expected, actual interface{}) (e string, a string) { if reflect.TypeOf(expected) != reflect.TypeOf(actual) { @@ -523,7 +572,7 @@ func truncatingFormat(data interface{}) string { return value } -// EqualValues asserts that two objects are equal or convertable to the same types +// EqualValues asserts that two objects are equal or convertible to the same types // and equal. // // assert.EqualValues(t, uint32(123), int32(123)) @@ -566,12 +615,19 @@ func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs .. return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) } + if aType.Kind() == reflect.Ptr { + aType = aType.Elem() + } + if bType.Kind() == reflect.Ptr { + bType = bType.Elem() + } + if aType.Kind() != reflect.Struct { - return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...) + return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...) } if bType.Kind() != reflect.Struct { - return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...) + return Fail(t, fmt.Sprintf("Types expected to both be struct or pointer to struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...) } expected = copyExportedFields(expected) @@ -620,17 +676,6 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return Fail(t, "Expected value not to be nil.", msgAndArgs...) } -// containsKind checks if a specified kind in the slice of kinds. -func containsKind(kinds []reflect.Kind, kind reflect.Kind) bool { - for i := 0; i < len(kinds); i++ { - if kind == kinds[i] { - return true - } - } - - return false -} - // isNil checks if a specified object is nil or not, without Failing. func isNil(object interface{}) bool { if object == nil { @@ -638,16 +683,13 @@ func isNil(object interface{}) bool { } value := reflect.ValueOf(object) - kind := value.Kind() - isNilableKind := containsKind( - []reflect.Kind{ - reflect.Chan, reflect.Func, - reflect.Interface, reflect.Map, - reflect.Ptr, reflect.Slice, reflect.UnsafePointer}, - kind) + switch value.Kind() { + case + reflect.Chan, reflect.Func, + reflect.Interface, reflect.Map, + reflect.Ptr, reflect.Slice, reflect.UnsafePointer: - if isNilableKind && value.IsNil() { - return true + return value.IsNil() } return false @@ -731,16 +773,14 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { } -// getLen try to get length of object. -// return (false, 0) if impossible. -func getLen(x interface{}) (ok bool, length int) { +// getLen tries to get the length of an object. +// It returns (0, false) if impossible. +func getLen(x interface{}) (length int, ok bool) { v := reflect.ValueOf(x) defer func() { - if e := recover(); e != nil { - ok = false - } + ok = recover() == nil }() - return true, v.Len() + return v.Len(), true } // Len asserts that the specified object has specific length. @@ -751,13 +791,13 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) if h, ok := t.(tHelper); ok { h.Helper() } - ok, l := getLen(object) + l, ok := getLen(object) if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...) + return Fail(t, fmt.Sprintf("\"%v\" could not be applied builtin len()", object), msgAndArgs...) } if l != length { - return Fail(t, fmt.Sprintf("\"%s\" should have %d item(s), but has %d", object, length, l), msgAndArgs...) + return Fail(t, fmt.Sprintf("\"%v\" should have %d item(s), but has %d", object, length, l), msgAndArgs...) } return true } @@ -919,10 +959,11 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) } -// Subset asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subset asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2]) +// assert.Subset(t, {"x": 1, "y": 2}, {"x": 1}) func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -975,10 +1016,12 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok return true } -// NotSubset asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubset asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2]) +// assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1439,7 +1482,7 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd h.Helper() } if math.IsNaN(epsilon) { - return Fail(t, "epsilon must not be NaN") + return Fail(t, "epsilon must not be NaN", msgAndArgs...) } actualEpsilon, err := calcRelativeError(expected, actual) if err != nil { @@ -1458,19 +1501,26 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m if h, ok := t.(tHelper); ok { h.Helper() } - if expected == nil || actual == nil || - reflect.TypeOf(actual).Kind() != reflect.Slice || - reflect.TypeOf(expected).Kind() != reflect.Slice { + + if expected == nil || actual == nil { return Fail(t, "Parameters must be slice", msgAndArgs...) } - actualSlice := reflect.ValueOf(actual) expectedSlice := reflect.ValueOf(expected) + actualSlice := reflect.ValueOf(actual) - for i := 0; i < actualSlice.Len(); i++ { - result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon) - if !result { - return result + if expectedSlice.Type().Kind() != reflect.Slice { + return Fail(t, "Expected value must be slice", msgAndArgs...) + } + + expectedLen := expectedSlice.Len() + if !IsType(t, expected, actual) || !Len(t, actual, expectedLen) { + return false + } + + for i := 0; i < expectedLen; i++ { + if !InEpsilon(t, expectedSlice.Index(i).Interface(), actualSlice.Index(i).Interface(), epsilon, "at index %d", i) { + return false } } @@ -1870,23 +1920,18 @@ func (c *CollectT) Errorf(format string, args ...interface{}) { } // FailNow panics. -func (c *CollectT) FailNow() { +func (*CollectT) FailNow() { panic("Assertion failed") } -// Reset clears the collected errors. -func (c *CollectT) Reset() { - c.errors = nil +// Deprecated: That was a method for internal usage that should not have been published. Now just panics. +func (*CollectT) Reset() { + panic("Reset() is deprecated") } -// Copy copies the collected errors to the supplied t. -func (c *CollectT) Copy(t TestingT) { - if tt, ok := t.(tHelper); ok { - tt.Helper() - } - for _, err := range c.errors { - t.Errorf("%v", err) - } +// Deprecated: That was a method for internal usage that should not have been published. Now just panics. +func (*CollectT) Copy(TestingT) { + panic("Copy() is deprecated") } // EventuallyWithT asserts that given condition will be met in waitFor time, @@ -1912,8 +1957,8 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time h.Helper() } - collect := new(CollectT) - ch := make(chan bool, 1) + var lastFinishedTickErrs []error + ch := make(chan []error, 1) timer := time.NewTimer(waitFor) defer timer.Stop() @@ -1924,19 +1969,25 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time for tick := ticker.C; ; { select { case <-timer.C: - collect.Copy(t) + for _, err := range lastFinishedTickErrs { + t.Errorf("%v", err) + } return Fail(t, "Condition never satisfied", msgAndArgs...) case <-tick: tick = nil - collect.Reset() go func() { + collect := new(CollectT) + defer func() { + ch <- collect.errors + }() condition(collect) - ch <- len(collect.errors) == 0 }() - case v := <-ch: - if v { + case errs := <-ch: + if len(errs) == 0 { return true } + // Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached. + lastFinishedTickErrs = errs tick = ticker.C } } diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go index d8038c28a..861ed4b7c 100644 --- a/vendor/github.com/stretchr/testify/assert/http_assertions.go +++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go @@ -12,7 +12,7 @@ import ( // an error if building a new request fails. func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) { w := httptest.NewRecorder() - req, err := http.NewRequest(method, url, nil) + req, err := http.NewRequest(method, url, http.NoBody) if err != nil { return -1, err } @@ -32,12 +32,12 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value } code, err := httpCode(handler, method, url, values) if err != nil { - Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) + Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) } isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent if !isSuccessCode { - Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code)) + Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) } return isSuccessCode @@ -54,12 +54,12 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu } code, err := httpCode(handler, method, url, values) if err != nil { - Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) + Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) } isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect if !isRedirectCode { - Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code)) + Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) } return isRedirectCode @@ -76,12 +76,12 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values } code, err := httpCode(handler, method, url, values) if err != nil { - Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) + Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) } isErrorCode := code >= http.StatusBadRequest if !isErrorCode { - Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code)) + Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) } return isErrorCode @@ -98,12 +98,12 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, va } code, err := httpCode(handler, method, url, values) if err != nil { - Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err)) + Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) } successful := code == statuscode if !successful { - Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code)) + Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code), msgAndArgs...) } return successful @@ -113,7 +113,10 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, va // empty string if building a new request fails. func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { w := httptest.NewRecorder() - req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) + if len(values) > 0 { + url += "?" + values.Encode() + } + req, err := http.NewRequest(method, url, http.NoBody) if err != nil { return "" } @@ -135,7 +138,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, contains := strings.Contains(body, fmt.Sprint(str)) if !contains { - Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) + Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...) } return contains @@ -155,7 +158,7 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url strin contains := strings.Contains(body, fmt.Sprint(str)) if contains { - Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) + Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...) } return !contains diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go index f4b42e44f..213bde2ea 100644 --- a/vendor/github.com/stretchr/testify/mock/mock.go +++ b/vendor/github.com/stretchr/testify/mock/mock.go @@ -18,6 +18,9 @@ import ( "github.com/stretchr/testify/assert" ) +// regex for GCCGO functions +var gccgoRE = regexp.MustCompile(`\.pN\d+_`) + // TestingT is an interface wrapper around *testing.T type TestingT interface { Logf(format string, args ...interface{}) @@ -111,7 +114,7 @@ func (c *Call) Return(returnArguments ...interface{}) *Call { return c } -// Panic specifies if the functon call should fail and the panic message +// Panic specifies if the function call should fail and the panic message // // Mock.On("DoSomething").Panic("test panic") func (c *Call) Panic(msg string) *Call { @@ -123,21 +126,21 @@ func (c *Call) Panic(msg string) *Call { return c } -// Once indicates that that the mock should only return the value once. +// Once indicates that the mock should only return the value once. // // Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once() func (c *Call) Once() *Call { return c.Times(1) } -// Twice indicates that that the mock should only return the value twice. +// Twice indicates that the mock should only return the value twice. // // Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice() func (c *Call) Twice() *Call { return c.Times(2) } -// Times indicates that that the mock should only return the indicated number +// Times indicates that the mock should only return the indicated number // of times. // // Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5) @@ -455,9 +458,8 @@ func (m *Mock) Called(arguments ...interface{}) Arguments { // For Ex: github_com_docker_libkv_store_mock.WatchTree.pN39_github_com_docker_libkv_store_mock.Mock // uses interface information unlike golang github.com/docker/libkv/store/mock.(*Mock).WatchTree // With GCCGO we need to remove interface information starting from pN
. - re := regexp.MustCompile("\\.pN\\d+_") - if re.MatchString(functionPath) { - functionPath = re.Split(functionPath, -1)[0] + if gccgoRE.MatchString(functionPath) { + functionPath = gccgoRE.Split(functionPath, -1)[0] } parts := strings.Split(functionPath, ".") functionName := parts[len(parts)-1] @@ -474,7 +476,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen found, call := m.findExpectedCall(methodName, arguments...) if found < 0 { - // expected call found but it has already been called with repeatable times + // expected call found, but it has already been called with repeatable times if call != nil { m.mutex.Unlock() m.fail("\nassert: mock: The method has been called over %d times.\n\tEither do one more Mock.On(\"%s\").Return(...), or remove extra call.\n\tThis call was unexpected:\n\t\t%s\n\tat: %s", call.totalCalls, methodName, callString(methodName, arguments, true), assert.CallerInfo()) @@ -563,7 +565,7 @@ func (m *Mock) MethodCalled(methodName string, arguments ...interface{}) Argumen Assertions */ -type assertExpectationser interface { +type assertExpectationiser interface { AssertExpectations(TestingT) bool } @@ -580,7 +582,7 @@ func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool { t.Logf("Deprecated mock.AssertExpectationsForObjects(myMock.Mock) use mock.AssertExpectationsForObjects(myMock)") obj = m } - m := obj.(assertExpectationser) + m := obj.(assertExpectationiser) if !m.AssertExpectations(t) { t.Logf("Expectations didn't match for Mock: %+v", reflect.TypeOf(m)) return false @@ -592,6 +594,9 @@ func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool { // AssertExpectations asserts that everything specified with On and Return was // in fact called as expected. Calls may have occurred in any order. func (m *Mock) AssertExpectations(t TestingT) bool { + if s, ok := t.(interface{ Skipped() bool }); ok && s.Skipped() { + return true + } if h, ok := t.(tHelper); ok { h.Helper() } @@ -606,8 +611,8 @@ func (m *Mock) AssertExpectations(t TestingT) bool { satisfied, reason := m.checkExpectation(expectedCall) if !satisfied { failedExpectations++ + t.Logf(reason) } - t.Logf(reason) } if failedExpectations != 0 { @@ -758,25 +763,33 @@ const ( Anything = "mock.Anything" ) -// AnythingOfTypeArgument is a string that contains the type of an argument +// AnythingOfTypeArgument contains the type of an argument // for use when type checking. Used in Diff and Assert. -type AnythingOfTypeArgument string +// +// Deprecated: this is an implementation detail that must not be used. Use [AnythingOfType] instead. +type AnythingOfTypeArgument = anythingOfTypeArgument -// AnythingOfType returns an AnythingOfTypeArgument object containing the -// name of the type to check for. Used in Diff and Assert. +// anythingOfTypeArgument is a string that contains the type of an argument +// for use when type checking. Used in Diff and Assert. +type anythingOfTypeArgument string + +// AnythingOfType returns a special value containing the +// name of the type to check for. The type name will be matched against the type name returned by [reflect.Type.String]. +// +// Used in Diff and Assert. // // For example: // // Assert(t, AnythingOfType("string"), AnythingOfType("int")) func AnythingOfType(t string) AnythingOfTypeArgument { - return AnythingOfTypeArgument(t) + return anythingOfTypeArgument(t) } // IsTypeArgument is a struct that contains the type of an argument // for use when type checking. This is an alternative to AnythingOfType. // Used in Diff and Assert. type IsTypeArgument struct { - t interface{} + t reflect.Type } // IsType returns an IsTypeArgument object containing the type to check for. @@ -786,7 +799,7 @@ type IsTypeArgument struct { // For example: // Assert(t, IsType(""), IsType(0)) func IsType(t interface{}) *IsTypeArgument { - return &IsTypeArgument{t: t} + return &IsTypeArgument{t: reflect.TypeOf(t)} } // FunctionalOptionsArgument is a struct that contains the type and value of an functional option argument @@ -950,53 +963,55 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { differences++ output = fmt.Sprintf("%s\t%d: FAIL: %s not matched by %s\n", output, i, actualFmt, matcher) } - } else if reflect.TypeOf(expected) == reflect.TypeOf((*AnythingOfTypeArgument)(nil)).Elem() { - // type checking - if reflect.TypeOf(actual).Name() != string(expected.(AnythingOfTypeArgument)) && reflect.TypeOf(actual).String() != string(expected.(AnythingOfTypeArgument)) { - // not match - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt) - } - } else if reflect.TypeOf(expected) == reflect.TypeOf((*IsTypeArgument)(nil)) { - t := expected.(*IsTypeArgument).t - if reflect.TypeOf(t) != reflect.TypeOf(actual) { - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, reflect.TypeOf(t).Name(), reflect.TypeOf(actual).Name(), actualFmt) - } - } else if reflect.TypeOf(expected) == reflect.TypeOf((*FunctionalOptionsArgument)(nil)) { - t := expected.(*FunctionalOptionsArgument).value + } else { + switch expected := expected.(type) { + case anythingOfTypeArgument: + // type checking + if reflect.TypeOf(actual).Name() != string(expected) && reflect.TypeOf(actual).String() != string(expected) { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt) + } + case *IsTypeArgument: + actualT := reflect.TypeOf(actual) + if actualT != expected.t { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt) + } + case *FunctionalOptionsArgument: + t := expected.value - var name string - tValue := reflect.ValueOf(t) - if tValue.Len() > 0 { - name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() - } + var name string + tValue := reflect.ValueOf(t) + if tValue.Len() > 0 { + name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() + } - tName := reflect.TypeOf(t).Name() - if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 { - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt) - } else { - if ef, af := assertOpts(t, actual); ef == "" && af == "" { + tName := reflect.TypeOf(t).Name() + if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt) + } else { + if ef, af := assertOpts(t, actual); ef == "" && af == "" { + // match + output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName) + } else { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, af, ef) + } + } + + default: + if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) { // match - output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName) + output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt) } else { // not match differences++ - output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, af, ef) + output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt) } } - } else { - // normal checking - - if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) { - // match - output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, actualFmt, expectedFmt) - } else { - // not match - differences++ - output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, actualFmt, expectedFmt) - } } } diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index 63f852147..506a82f80 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -1,7 +1,4 @@ -/* -* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen -* THIS FILE MUST NOT BE EDITED BY HAND - */ +// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. package require @@ -235,7 +232,7 @@ func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, t.FailNow() } -// EqualValues asserts that two objects are equal or convertable to the same types +// EqualValues asserts that two objects are equal or convertible to the same types // and equal. // // assert.EqualValues(t, uint32(123), int32(123)) @@ -249,7 +246,7 @@ func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArg t.FailNow() } -// EqualValuesf asserts that two objects are equal or convertable to the same types +// EqualValuesf asserts that two objects are equal or convertible to the same types // and equal. // // assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") @@ -1546,6 +1543,32 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf t.FailNow() } +// NotImplements asserts that an object does not implement the specified interface. +// +// assert.NotImplements(t, (*MyInterface)(nil), new(MyObject)) +func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotImplements(t, interfaceObject, object, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotImplementsf asserts that an object does not implement the specified interface. +// +// assert.NotImplementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +func NotImplementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotImplementsf(t, interfaceObject, object, msg, args...) { + return + } + t.FailNow() +} + // NotNil asserts that the specified object is not nil. // // assert.NotNil(t, err) @@ -1658,10 +1681,12 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, t.FailNow() } -// NotSubset asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubset asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2]) +// assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1672,10 +1697,12 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i t.FailNow() } -// NotSubsetf asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubsetf asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") +// assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1880,10 +1907,11 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg t.FailNow() } -// Subset asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subset asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2]) +// assert.Subset(t, {"x": 1, "y": 2}, {"x": 1}) func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1894,10 +1922,11 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte t.FailNow() } -// Subsetf asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subsetf asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") +// assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index 3b5b09330..eee8310a5 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -1,7 +1,4 @@ -/* -* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen -* THIS FILE MUST NOT BE EDITED BY HAND - */ +// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. package require @@ -190,7 +187,7 @@ func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface EqualExportedValuesf(a.t, expected, actual, msg, args...) } -// EqualValues asserts that two objects are equal or convertable to the same types +// EqualValues asserts that two objects are equal or convertible to the same types // and equal. // // a.EqualValues(uint32(123), int32(123)) @@ -201,7 +198,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn EqualValues(a.t, expected, actual, msgAndArgs...) } -// EqualValuesf asserts that two objects are equal or convertable to the same types +// EqualValuesf asserts that two objects are equal or convertible to the same types // and equal. // // a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") @@ -1222,6 +1219,26 @@ func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...in NotErrorIsf(a.t, err, target, msg, args...) } +// NotImplements asserts that an object does not implement the specified interface. +// +// a.NotImplements((*MyInterface)(nil), new(MyObject)) +func (a *Assertions) NotImplements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotImplements(a.t, interfaceObject, object, msgAndArgs...) +} + +// NotImplementsf asserts that an object does not implement the specified interface. +// +// a.NotImplementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +func (a *Assertions) NotImplementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotImplementsf(a.t, interfaceObject, object, msg, args...) +} + // NotNil asserts that the specified object is not nil. // // a.NotNil(err) @@ -1310,10 +1327,12 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri NotSamef(a.t, expected, actual, msg, args...) } -// NotSubset asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubset asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2]) +// a.NotSubset({"x": 1, "y": 2}, {"z": 3}) func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1321,10 +1340,12 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs NotSubset(a.t, list, subset, msgAndArgs...) } -// NotSubsetf asserts that the specified list(array, slice...) contains not all -// elements given in the specified subset(array, slice...). +// NotSubsetf asserts that the specified list(array, slice...) or map does NOT +// contain all elements given in the specified subset list(array, slice...) or +// map. // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") +// a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1484,10 +1505,11 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, Samef(a.t, expected, actual, msg, args...) } -// Subset asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subset asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2]) +// a.Subset({"x": 1, "y": 2}, {"x": 1}) func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1495,10 +1517,11 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... Subset(a.t, list, subset, msgAndArgs...) } -// Subsetf asserts that the specified list(array, slice...) contains all -// elements given in the specified subset(array, slice...). +// Subsetf asserts that the specified list(array, slice...) or map contains all +// elements given in the specified subset list(array, slice...) or map. // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") +// a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/suite/suite.go b/vendor/github.com/stretchr/testify/suite/suite.go index 8b4202d89..18443a91c 100644 --- a/vendor/github.com/stretchr/testify/suite/suite.go +++ b/vendor/github.com/stretchr/testify/suite/suite.go @@ -58,7 +58,7 @@ func (suite *Suite) Require() *require.Assertions { suite.mu.Lock() defer suite.mu.Unlock() if suite.require == nil { - suite.require = require.New(suite.T()) + panic("'Require' must not be called before 'Run' or 'SetT'") } return suite.require } @@ -72,17 +72,19 @@ func (suite *Suite) Assert() *assert.Assertions { suite.mu.Lock() defer suite.mu.Unlock() if suite.Assertions == nil { - suite.Assertions = assert.New(suite.T()) + panic("'Assert' must not be called before 'Run' or 'SetT'") } return suite.Assertions } func recoverAndFailOnPanic(t *testing.T) { + t.Helper() r := recover() failOnPanic(t, r) } func failOnPanic(t *testing.T, r interface{}) { + t.Helper() if r != nil { t.Errorf("test panicked: %v\n%s", r, debug.Stack()) t.FailNow() @@ -96,19 +98,20 @@ func failOnPanic(t *testing.T, r interface{}) { func (suite *Suite) Run(name string, subtest func()) bool { oldT := suite.T() - if setupSubTest, ok := suite.s.(SetupSubTest); ok { - setupSubTest.SetupSubTest() - } - - defer func() { - suite.SetT(oldT) - if tearDownSubTest, ok := suite.s.(TearDownSubTest); ok { - tearDownSubTest.TearDownSubTest() - } - }() - return oldT.Run(name, func(t *testing.T) { suite.SetT(t) + defer suite.SetT(oldT) + + defer recoverAndFailOnPanic(t) + + if setupSubTest, ok := suite.s.(SetupSubTest); ok { + setupSubTest.SetupSubTest() + } + + if tearDownSubTest, ok := suite.s.(TearDownSubTest); ok { + defer tearDownSubTest.TearDownSubTest() + } + subtest() }) } @@ -164,6 +167,8 @@ func Run(t *testing.T, suite TestingSuite) { suite.SetT(t) defer recoverAndFailOnPanic(t) defer func() { + t.Helper() + r := recover() if stats != nil { diff --git a/vendor/github.com/urfave/cli/v2/.gitignore b/vendor/github.com/urfave/cli/v2/.gitignore index d7d26b1fc..1ef91a60b 100644 --- a/vendor/github.com/urfave/cli/v2/.gitignore +++ b/vendor/github.com/urfave/cli/v2/.gitignore @@ -4,6 +4,7 @@ .*envrc .envrc .idea +# goimports is installed here if not available /.local/ /site/ coverage.txt diff --git a/vendor/github.com/urfave/cli/v2/.golangci.yaml b/vendor/github.com/urfave/cli/v2/.golangci.yaml new file mode 100644 index 000000000..89b6e8661 --- /dev/null +++ b/vendor/github.com/urfave/cli/v2/.golangci.yaml @@ -0,0 +1,4 @@ +# https://golangci-lint.run/usage/configuration/ +linters: + enable: + - misspell diff --git a/vendor/github.com/urfave/cli/v2/app.go b/vendor/github.com/urfave/cli/v2/app.go index 4b6675a2d..af072e769 100644 --- a/vendor/github.com/urfave/cli/v2/app.go +++ b/vendor/github.com/urfave/cli/v2/app.go @@ -23,8 +23,8 @@ var ( fmt.Sprintf("See %s", appActionDeprecationURL), 2) ignoreFlagPrefix = "test." // this is to ignore test flags when adding flags from other packages - SuggestFlag SuggestFlagFunc = suggestFlag - SuggestCommand SuggestCommandFunc = suggestCommand + SuggestFlag SuggestFlagFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest + SuggestCommand SuggestCommandFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate ) @@ -39,6 +39,8 @@ type App struct { Usage string // Text to override the USAGE section of help UsageText string + // Whether this command supports arguments + Args bool // Description of the program argument format. ArgsUsage string // Version of the program @@ -227,8 +229,6 @@ func (a *App) Setup() { a.separator.disabled = true } - var newCommands []*Command - for _, c := range a.Commands { cname := c.Name if c.HelpName != "" { @@ -237,9 +237,7 @@ func (a *App) Setup() { c.separator = a.separator c.HelpName = fmt.Sprintf("%s %s", a.HelpName, cname) c.flagCategories = newFlagCategoriesFromFlags(c.Flags) - newCommands = append(newCommands, c) } - a.Commands = newCommands if a.Command(helpCommand.Name) == nil && !a.HideHelp { if !a.HideHelpCommand { @@ -329,14 +327,24 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { a.rootCommand = a.newRootCommand() cCtx.Command = a.rootCommand + if err := checkDuplicatedCmds(a.rootCommand); err != nil { + return err + } return a.rootCommand.Run(cCtx, arguments...) } -// This is a stub function to keep public API unchanged from old code -// -// Deprecated: use App.Run or App.RunContext +// RunAsSubcommand is for legacy/compatibility purposes only. New code should only +// use App.RunContext. This function is slated to be removed in v3. func (a *App) RunAsSubcommand(ctx *Context) (err error) { - return a.RunContext(ctx.Context, ctx.Args().Slice()) + a.Setup() + + cCtx := NewContext(a, nil, ctx) + cCtx.shellComplete = ctx.shellComplete + + a.rootCommand = a.newRootCommand() + cCtx.Command = a.rootCommand + + return a.rootCommand.Run(cCtx, ctx.Args().Slice()...) } func (a *App) suggestFlagFromError(err error, command string) (string, error) { @@ -356,6 +364,9 @@ func (a *App) suggestFlagFromError(err error, command string) (string, error) { hideHelp = hideHelp || cmd.HideHelp } + if SuggestFlag == nil { + return "", err + } suggestion := SuggestFlag(flags, flag, hideHelp) if len(suggestion) == 0 { return "", err @@ -448,30 +459,6 @@ func (a *App) handleExitCoder(cCtx *Context, err error) { } } -func (a *App) commandNames() []string { - var cmdNames []string - - for _, cmd := range a.Commands { - cmdNames = append(cmdNames, cmd.Names()...) - } - - return cmdNames -} - -func (a *App) validCommandName(checkCmdName string) bool { - valid := false - allCommandNames := a.commandNames() - - for _, cmdName := range allCommandNames { - if checkCmdName == cmdName { - valid = true - break - } - } - - return valid -} - func (a *App) argsWithDefaultCommand(oldArgs Args) Args { if a.DefaultCommand != "" { rawArgs := append([]string{a.DefaultCommand}, oldArgs.Slice()...) diff --git a/vendor/github.com/urfave/cli/v2/category.go b/vendor/github.com/urfave/cli/v2/category.go index ccc043c25..0986fffca 100644 --- a/vendor/github.com/urfave/cli/v2/category.go +++ b/vendor/github.com/urfave/cli/v2/category.go @@ -104,17 +104,17 @@ func newFlagCategoriesFromFlags(fs []Flag) FlagCategories { var categorized bool for _, fl := range fs { if cf, ok := fl.(CategorizableFlag); ok { - if cat := cf.GetCategory(); cat != "" { + if cat := cf.GetCategory(); cat != "" && cf.IsVisible() { fc.AddFlag(cat, cf) categorized = true } } } - if categorized == true { + if categorized { for _, fl := range fs { if cf, ok := fl.(CategorizableFlag); ok { - if cf.GetCategory() == "" { + if cf.GetCategory() == "" && cf.IsVisible() { fc.AddFlag("", fl) } } diff --git a/vendor/github.com/urfave/cli/v2/command.go b/vendor/github.com/urfave/cli/v2/command.go index f978b4a43..472c1ff44 100644 --- a/vendor/github.com/urfave/cli/v2/command.go +++ b/vendor/github.com/urfave/cli/v2/command.go @@ -20,6 +20,8 @@ type Command struct { UsageText string // A longer explanation of how the command works Description string + // Whether this command supports arguments + Args bool // A short description of the arguments of this command ArgsUsage string // The category the command is part of @@ -130,14 +132,12 @@ func (c *Command) setup(ctx *Context) { } sort.Sort(c.categories.(*commandCategories)) - var newCmds []*Command for _, scmd := range c.Subcommands { if scmd.HelpName == "" { scmd.HelpName = fmt.Sprintf("%s %s", c.HelpName, scmd.Name) } - newCmds = append(newCmds, scmd) + scmd.separator = c.separator } - c.Subcommands = newCmds if c.BashComplete == nil { c.BashComplete = DefaultCompleteWithFlags(c) @@ -148,6 +148,9 @@ func (c *Command) Run(cCtx *Context, arguments ...string) (err error) { if !c.isRoot { c.setup(cCtx) + if err := checkDuplicatedCmds(c); err != nil { + return err + } } a := args(arguments) @@ -403,3 +406,16 @@ func hasCommand(commands []*Command, command *Command) bool { return false } + +func checkDuplicatedCmds(parent *Command) error { + seen := make(map[string]struct{}) + for _, c := range parent.Subcommands { + for _, name := range c.Names() { + if _, exists := seen[name]; exists { + return fmt.Errorf("parent command [%s] has duplicated subcommand name or alias: %s", parent.Name, name) + } + seen[name] = struct{}{} + } + } + return nil +} diff --git a/vendor/github.com/urfave/cli/v2/context.go b/vendor/github.com/urfave/cli/v2/context.go index dbf50e495..8dd476521 100644 --- a/vendor/github.com/urfave/cli/v2/context.go +++ b/vendor/github.com/urfave/cli/v2/context.go @@ -56,6 +56,7 @@ func (cCtx *Context) Set(name, value string) error { // IsSet determines if the flag was actually set func (cCtx *Context) IsSet(name string) bool { + if fs := cCtx.lookupFlagSet(name); fs != nil { isSet := false fs.Visit(func(f *flag.Flag) { @@ -72,7 +73,23 @@ func (cCtx *Context) IsSet(name string) bool { return false } - return f.IsSet() + if f.IsSet() { + return true + } + + // now redo flagset search on aliases + aliases := f.Names() + fs.Visit(func(f *flag.Flag) { + for _, alias := range aliases { + if f.Name == alias { + isSet = true + } + } + }) + + if isSet { + return true + } } return false @@ -127,7 +144,7 @@ func (cCtx *Context) Lineage() []*Context { return lineage } -// Count returns the num of occurences of this flag +// Count returns the num of occurrences of this flag func (cCtx *Context) Count(name string) int { if fs := cCtx.lookupFlagSet(name); fs != nil { if cf, ok := fs.Lookup(name).Value.(Countable); ok { @@ -204,9 +221,10 @@ func (cCtx *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr { var flagPresent bool var flagName string - for _, key := range f.Names() { - flagName = key + flagNames := f.Names() + flagName = flagNames[0] + for _, key := range flagNames { if cCtx.IsSet(strings.TrimSpace(key)) { flagPresent = true } diff --git a/vendor/github.com/urfave/cli/v2/docs.go b/vendor/github.com/urfave/cli/v2/docs.go index 8b1c9c8a2..6cd0624ae 100644 --- a/vendor/github.com/urfave/cli/v2/docs.go +++ b/vendor/github.com/urfave/cli/v2/docs.go @@ -153,9 +153,14 @@ func prepareFlags( // flagDetails returns a string containing the flags metadata func flagDetails(flag DocGenerationFlag) string { description := flag.GetUsage() - value := flag.GetValue() - if value != "" { - description += " (default: " + value + ")" + if flag.TakesValue() { + defaultText := flag.GetDefaultText() + if defaultText == "" { + defaultText = flag.GetValue() + } + if defaultText != "" { + description += " (default: " + defaultText + ")" + } } return ": " + description } diff --git a/vendor/github.com/urfave/cli/v2/flag_bool.go b/vendor/github.com/urfave/cli/v2/flag_bool.go index f64d1cd92..01862ea76 100644 --- a/vendor/github.com/urfave/cli/v2/flag_bool.go +++ b/vendor/github.com/urfave/cli/v2/flag_bool.go @@ -52,7 +52,7 @@ func (b *boolValue) String() string { func (b *boolValue) IsBoolFlag() bool { return true } func (b *boolValue) Count() int { - if b.count != nil { + if b.count != nil && *b.count > 0 { return *b.count } return 0 @@ -84,7 +84,10 @@ func (f *BoolFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return fmt.Sprintf("%v", f.defaultValue) + if f.defaultValueSet { + return fmt.Sprintf("%v", f.defaultValue) + } + return fmt.Sprintf("%v", f.Value) } // GetEnvVars returns the env vars for this flag @@ -105,6 +108,7 @@ func (f *BoolFlag) RunAction(c *Context) error { func (f *BoolFlag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value + f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { @@ -130,6 +134,11 @@ func (f *BoolFlag) Apply(set *flag.FlagSet) error { if count == nil { count = new(int) } + + // since count will be incremented for each alias as well + // subtract number of aliases from overall count + *count -= len(f.Aliases) + if dest == nil { dest = new(bool) } diff --git a/vendor/github.com/urfave/cli/v2/flag_duration.go b/vendor/github.com/urfave/cli/v2/flag_duration.go index eac4cb833..e600cc30a 100644 --- a/vendor/github.com/urfave/cli/v2/flag_duration.go +++ b/vendor/github.com/urfave/cli/v2/flag_duration.go @@ -32,7 +32,10 @@ func (f *DurationFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return f.defaultValue.String() + if f.defaultValueSet { + return f.defaultValue.String() + } + return f.Value.String() } // GetEnvVars returns the env vars for this flag @@ -44,6 +47,7 @@ func (f *DurationFlag) GetEnvVars() []string { func (f *DurationFlag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value + f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { diff --git a/vendor/github.com/urfave/cli/v2/flag_float64.go b/vendor/github.com/urfave/cli/v2/flag_float64.go index bce26c195..6a4de5c88 100644 --- a/vendor/github.com/urfave/cli/v2/flag_float64.go +++ b/vendor/github.com/urfave/cli/v2/flag_float64.go @@ -32,7 +32,10 @@ func (f *Float64Flag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return f.GetValue() + if f.defaultValueSet { + return fmt.Sprintf("%v", f.defaultValue) + } + return fmt.Sprintf("%v", f.Value) } // GetEnvVars returns the env vars for this flag @@ -42,6 +45,9 @@ func (f *Float64Flag) GetEnvVars() []string { // Apply populates the flag given the flag set and environment func (f *Float64Flag) Apply(set *flag.FlagSet) error { + f.defaultValue = f.Value + f.defaultValueSet = true + if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { valFloat, err := strconv.ParseFloat(val, 64) diff --git a/vendor/github.com/urfave/cli/v2/flag_generic.go b/vendor/github.com/urfave/cli/v2/flag_generic.go index 4f9ac0a7f..7528c934c 100644 --- a/vendor/github.com/urfave/cli/v2/flag_generic.go +++ b/vendor/github.com/urfave/cli/v2/flag_generic.go @@ -53,9 +53,15 @@ func (f *GenericFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - if f.defaultValue != nil { - return f.defaultValue.String() + val := f.Value + if f.defaultValueSet { + val = f.defaultValue } + + if val != nil { + return val.String() + } + return "" } @@ -70,6 +76,7 @@ func (f *GenericFlag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it if f.Value != nil { f.defaultValue = &stringGeneric{value: f.Value.String()} + f.defaultValueSet = true } if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { @@ -117,13 +124,8 @@ func (cCtx *Context) Generic(name string) interface{} { } func lookupGeneric(name string, set *flag.FlagSet) interface{} { - f := set.Lookup(name) - if f != nil { - parsed, err := f.Value, error(nil) - if err != nil { - return nil - } - return parsed + if f := set.Lookup(name); f != nil { + return f.Value } return nil } diff --git a/vendor/github.com/urfave/cli/v2/flag_int.go b/vendor/github.com/urfave/cli/v2/flag_int.go index d681270a2..750e7ebfc 100644 --- a/vendor/github.com/urfave/cli/v2/flag_int.go +++ b/vendor/github.com/urfave/cli/v2/flag_int.go @@ -32,7 +32,10 @@ func (f *IntFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return fmt.Sprintf("%d", f.defaultValue) + if f.defaultValueSet { + return fmt.Sprintf("%d", f.defaultValue) + } + return fmt.Sprintf("%d", f.Value) } // GetEnvVars returns the env vars for this flag @@ -44,6 +47,7 @@ func (f *IntFlag) GetEnvVars() []string { func (f *IntFlag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value + f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { diff --git a/vendor/github.com/urfave/cli/v2/flag_int64.go b/vendor/github.com/urfave/cli/v2/flag_int64.go index 6f8c8d27d..688c26716 100644 --- a/vendor/github.com/urfave/cli/v2/flag_int64.go +++ b/vendor/github.com/urfave/cli/v2/flag_int64.go @@ -32,7 +32,10 @@ func (f *Int64Flag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return fmt.Sprintf("%d", f.defaultValue) + if f.defaultValueSet { + return fmt.Sprintf("%d", f.defaultValue) + } + return fmt.Sprintf("%d", f.Value) } // GetEnvVars returns the env vars for this flag @@ -44,6 +47,7 @@ func (f *Int64Flag) GetEnvVars() []string { func (f *Int64Flag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value + f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { diff --git a/vendor/github.com/urfave/cli/v2/flag_path.go b/vendor/github.com/urfave/cli/v2/flag_path.go index 6434d3224..76cb35248 100644 --- a/vendor/github.com/urfave/cli/v2/flag_path.go +++ b/vendor/github.com/urfave/cli/v2/flag_path.go @@ -33,10 +33,14 @@ func (f *PathFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - if f.defaultValue == "" { - return f.defaultValue + val := f.Value + if f.defaultValueSet { + val = f.defaultValue } - return fmt.Sprintf("%q", f.defaultValue) + if val == "" { + return val + } + return fmt.Sprintf("%q", val) } // GetEnvVars returns the env vars for this flag @@ -48,6 +52,7 @@ func (f *PathFlag) GetEnvVars() []string { func (f *PathFlag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value + f.defaultValueSet = true if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { f.Value = val @@ -90,13 +95,8 @@ func (cCtx *Context) Path(name string) string { } func lookupPath(name string, set *flag.FlagSet) string { - f := set.Lookup(name) - if f != nil { - parsed, err := f.Value.String(), error(nil) - if err != nil { - return "" - } - return parsed + if f := set.Lookup(name); f != nil { + return f.Value.String() } return "" } diff --git a/vendor/github.com/urfave/cli/v2/flag_string.go b/vendor/github.com/urfave/cli/v2/flag_string.go index 3050086e8..0f73e0621 100644 --- a/vendor/github.com/urfave/cli/v2/flag_string.go +++ b/vendor/github.com/urfave/cli/v2/flag_string.go @@ -31,10 +31,15 @@ func (f *StringFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - if f.defaultValue == "" { - return f.defaultValue + val := f.Value + if f.defaultValueSet { + val = f.defaultValue } - return fmt.Sprintf("%q", f.defaultValue) + + if val == "" { + return val + } + return fmt.Sprintf("%q", val) } // GetEnvVars returns the env vars for this flag @@ -46,6 +51,7 @@ func (f *StringFlag) GetEnvVars() []string { func (f *StringFlag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value + f.defaultValueSet = true if val, _, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { f.Value = val @@ -87,10 +93,8 @@ func (cCtx *Context) String(name string) string { } func lookupString(name string, set *flag.FlagSet) string { - f := set.Lookup(name) - if f != nil { - parsed := f.Value.String() - return parsed + if f := set.Lookup(name); f != nil { + return f.Value.String() } return "" } diff --git a/vendor/github.com/urfave/cli/v2/flag_timestamp.go b/vendor/github.com/urfave/cli/v2/flag_timestamp.go index fa0671fe4..b90123087 100644 --- a/vendor/github.com/urfave/cli/v2/flag_timestamp.go +++ b/vendor/github.com/urfave/cli/v2/flag_timestamp.go @@ -120,8 +120,13 @@ func (f *TimestampFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - if f.defaultValue != nil && f.defaultValue.timestamp != nil { - return f.defaultValue.timestamp.String() + val := f.Value + if f.defaultValueSet { + val = f.defaultValue + } + + if val != nil && val.timestamp != nil { + return val.timestamp.String() } return "" @@ -144,11 +149,7 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error { f.Value.SetLocation(f.Timezone) f.defaultValue = f.Value.clone() - - if f.Destination != nil { - f.Destination.SetLayout(f.Layout) - f.Destination.SetLocation(f.Timezone) - } + f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if err := f.Value.Set(val); err != nil { @@ -157,6 +158,10 @@ func (f *TimestampFlag) Apply(set *flag.FlagSet) error { f.HasBeenSet = true } + if f.Destination != nil { + *f.Destination = *f.Value + } + for _, name := range f.Names() { if f.Destination != nil { set.Var(f.Destination, name, f.Usage) diff --git a/vendor/github.com/urfave/cli/v2/flag_uint.go b/vendor/github.com/urfave/cli/v2/flag_uint.go index d11aa0a89..8d5b85458 100644 --- a/vendor/github.com/urfave/cli/v2/flag_uint.go +++ b/vendor/github.com/urfave/cli/v2/flag_uint.go @@ -25,6 +25,7 @@ func (f *UintFlag) GetCategory() string { func (f *UintFlag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value + f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { @@ -69,7 +70,10 @@ func (f *UintFlag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return fmt.Sprintf("%d", f.defaultValue) + if f.defaultValueSet { + return fmt.Sprintf("%d", f.defaultValue) + } + return fmt.Sprintf("%d", f.Value) } // GetEnvVars returns the env vars for this flag diff --git a/vendor/github.com/urfave/cli/v2/flag_uint64.go b/vendor/github.com/urfave/cli/v2/flag_uint64.go index ea73100a9..c356e533b 100644 --- a/vendor/github.com/urfave/cli/v2/flag_uint64.go +++ b/vendor/github.com/urfave/cli/v2/flag_uint64.go @@ -25,6 +25,7 @@ func (f *Uint64Flag) GetCategory() string { func (f *Uint64Flag) Apply(set *flag.FlagSet) error { // set default value so that environment wont be able to overwrite it f.defaultValue = f.Value + f.defaultValueSet = true if val, source, found := flagFromEnvOrFile(f.EnvVars, f.FilePath); found { if val != "" { @@ -69,7 +70,10 @@ func (f *Uint64Flag) GetDefaultText() string { if f.DefaultText != "" { return f.DefaultText } - return fmt.Sprintf("%d", f.defaultValue) + if f.defaultValueSet { + return fmt.Sprintf("%d", f.defaultValue) + } + return fmt.Sprintf("%d", f.Value) } // GetEnvVars returns the env vars for this flag diff --git a/vendor/github.com/urfave/cli/v2/flag_uint64_slice.go b/vendor/github.com/urfave/cli/v2/flag_uint64_slice.go index e845dd525..d34201868 100644 --- a/vendor/github.com/urfave/cli/v2/flag_uint64_slice.go +++ b/vendor/github.com/urfave/cli/v2/flag_uint64_slice.go @@ -190,6 +190,15 @@ func (f *Uint64SliceFlag) Get(ctx *Context) []uint64 { return ctx.Uint64Slice(f.Name) } +// RunAction executes flag action if set +func (f *Uint64SliceFlag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.Uint64Slice(f.Name)) + } + + return nil +} + // Uint64Slice looks up the value of a local Uint64SliceFlag, returns // nil if not found func (cCtx *Context) Uint64Slice(name string) []uint64 { diff --git a/vendor/github.com/urfave/cli/v2/flag_uint_slice.go b/vendor/github.com/urfave/cli/v2/flag_uint_slice.go index d2aed480d..4dc13e126 100644 --- a/vendor/github.com/urfave/cli/v2/flag_uint_slice.go +++ b/vendor/github.com/urfave/cli/v2/flag_uint_slice.go @@ -201,6 +201,15 @@ func (f *UintSliceFlag) Get(ctx *Context) []uint { return ctx.UintSlice(f.Name) } +// RunAction executes flag action if set +func (f *UintSliceFlag) RunAction(c *Context) error { + if f.Action != nil { + return f.Action(c, c.UintSlice(f.Name)) + } + + return nil +} + // UintSlice looks up the value of a local UintSliceFlag, returns // nil if not found func (cCtx *Context) UintSlice(name string) []uint { diff --git a/vendor/github.com/urfave/cli/v2/godoc-current.txt b/vendor/github.com/urfave/cli/v2/godoc-current.txt index bd5e1defc..4b620feeb 100644 --- a/vendor/github.com/urfave/cli/v2/godoc-current.txt +++ b/vendor/github.com/urfave/cli/v2/godoc-current.txt @@ -27,15 +27,15 @@ application: VARIABLES var ( - SuggestFlag SuggestFlagFunc = suggestFlag - SuggestCommand SuggestCommandFunc = suggestCommand + SuggestFlag SuggestFlagFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest + SuggestCommand SuggestCommandFunc = nil // initialized in suggestions.go unless built with urfave_cli_no_suggest SuggestDidYouMeanTemplate string = suggestDidYouMeanTemplate ) var AppHelpTemplate = `NAME: {{template "helpNameTemplate" .}} USAGE: - {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} + {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}{{if .Args}}[arguments...]{{end}}{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} VERSION: {{.Version}}{{end}}{{end}}{{if .Description}} @@ -136,12 +136,12 @@ var SubcommandHelpTemplate = `NAME: {{template "helpNameTemplate" .}} USAGE: - {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} + {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}{{if .Args}}[arguments...]{{end}}{{end}}{{end}}{{if .Description}} DESCRIPTION: {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} -COMMANDS:{{template "visibleCommandTemplate" .}}{{end}}{{if .VisibleFlagCategories}} +COMMANDS:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}} OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} @@ -253,6 +253,8 @@ type App struct { Usage string // Text to override the USAGE section of help UsageText string + // Whether this command supports arguments + Args bool // Description of the program argument format. ArgsUsage string // Version of the program @@ -357,9 +359,8 @@ func (a *App) RunAndExitOnError() code in the cli.ExitCoder func (a *App) RunAsSubcommand(ctx *Context) (err error) - This is a stub function to keep public API unchanged from old code - - Deprecated: use App.Run or App.RunContext + RunAsSubcommand is for legacy/compatibility purposes only. New code should + only use App.RunContext. This function is slated to be removed in v3. func (a *App) RunContext(ctx context.Context, arguments []string) (err error) RunContext is like Run except it takes a Context that will be passed to @@ -524,6 +525,8 @@ type Command struct { UsageText string // A longer explanation of how the command works Description string + // Whether this command supports arguments + Args bool // A short description of the arguments of this command ArgsUsage string // The category the command is part of @@ -650,7 +653,7 @@ func (cCtx *Context) Bool(name string) bool Bool looks up the value of a local BoolFlag, returns false if not found func (cCtx *Context) Count(name string) int - Count returns the num of occurences of this flag + Count returns the num of occurrences of this flag func (cCtx *Context) Duration(name string) time.Duration Duration looks up the value of a local DurationFlag, returns 0 if not found @@ -2143,6 +2146,9 @@ func (f *Uint64SliceFlag) IsVisible() bool func (f *Uint64SliceFlag) Names() []string Names returns the names of the flag +func (f *Uint64SliceFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *Uint64SliceFlag) String() string String returns a readable representation of this value (for usage defaults) @@ -2308,6 +2314,9 @@ func (f *UintSliceFlag) IsVisible() bool func (f *UintSliceFlag) Names() []string Names returns the names of the flag +func (f *UintSliceFlag) RunAction(c *Context) error + RunAction executes flag action if set + func (f *UintSliceFlag) String() string String returns a readable representation of this value (for usage defaults) diff --git a/vendor/github.com/urfave/cli/v2/help.go b/vendor/github.com/urfave/cli/v2/help.go index c7b8f55a5..640e29045 100644 --- a/vendor/github.com/urfave/cli/v2/help.go +++ b/vendor/github.com/urfave/cli/v2/help.go @@ -42,6 +42,7 @@ var helpCommand = &Command{ // 3 $ app foo // 4 $ app help foo // 5 $ app foo help + // 6 $ app foo -h (with no other sub-commands nor flags defined) // Case 4. when executing a help command set the context to parent // to allow resolution of subsequent args. This will transform @@ -68,7 +69,7 @@ var helpCommand = &Command{ } // Case 3, 5 - if (len(cCtx.Command.Subcommands) == 1 && !cCtx.Command.HideHelp) || + if (len(cCtx.Command.Subcommands) == 1 && !cCtx.Command.HideHelp && !cCtx.Command.HideHelpCommand) || (len(cCtx.Command.Subcommands) == 0 && cCtx.Command.HideHelp) { templ := cCtx.Command.CustomHelpTemplate if templ == "" { @@ -77,6 +78,8 @@ var helpCommand = &Command{ HelpPrinter(cCtx.App.Writer, templ, cCtx.Command) return nil } + + // Case 6, handling incorporated in the callee itself return ShowSubcommandHelp(cCtx) }, } @@ -206,9 +209,15 @@ func printFlagSuggestions(lastArg string, flags []Flag, writer io.Writer) { func DefaultCompleteWithFlags(cmd *Command) func(cCtx *Context) { return func(cCtx *Context) { - if len(os.Args) > 2 { - lastArg := os.Args[len(os.Args)-2] + var lastArg string + // TODO: This shouldnt depend on os.Args rather it should + // depend on root arguments passed to App + if len(os.Args) > 2 { + lastArg = os.Args[len(os.Args)-2] + } + + if lastArg != "" { if strings.HasPrefix(lastArg, "-") { if cmd != nil { printFlagSuggestions(lastArg, cmd.Flags, cCtx.App.Writer) @@ -269,7 +278,7 @@ func ShowCommandHelp(ctx *Context, command string) error { if ctx.App.CommandNotFound == nil { errMsg := fmt.Sprintf("No help topic for '%v'", command) - if ctx.App.Suggest { + if ctx.App.Suggest && SuggestCommand != nil { if suggestion := SuggestCommand(ctx.Command.Subcommands, command); suggestion != "" { errMsg += ". " + suggestion } @@ -292,8 +301,12 @@ func ShowSubcommandHelp(cCtx *Context) error { if cCtx == nil { return nil } - - HelpPrinter(cCtx.App.Writer, SubcommandHelpTemplate, cCtx.Command) + // use custom template when provided (fixes #1703) + templ := SubcommandHelpTemplate + if cCtx.Command != nil && cCtx.Command.CustomHelpTemplate != "" { + templ = cCtx.Command.CustomHelpTemplate + } + HelpPrinter(cCtx.App.Writer, templ, cCtx.Command) return nil } @@ -363,17 +376,26 @@ func printHelpCustom(out io.Writer, templ string, data interface{}, customFuncs w := tabwriter.NewWriter(out, 1, 8, 2, ' ', 0) t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) - t.New("helpNameTemplate").Parse(helpNameTemplate) - t.New("usageTemplate").Parse(usageTemplate) - t.New("descriptionTemplate").Parse(descriptionTemplate) - t.New("visibleCommandTemplate").Parse(visibleCommandTemplate) - t.New("copyrightTemplate").Parse(copyrightTemplate) - t.New("versionTemplate").Parse(versionTemplate) - t.New("visibleFlagCategoryTemplate").Parse(visibleFlagCategoryTemplate) - t.New("visibleFlagTemplate").Parse(visibleFlagTemplate) - t.New("visibleGlobalFlagCategoryTemplate").Parse(strings.Replace(visibleFlagCategoryTemplate, "OPTIONS", "GLOBAL OPTIONS", -1)) - t.New("authorsTemplate").Parse(authorsTemplate) - t.New("visibleCommandCategoryTemplate").Parse(visibleCommandCategoryTemplate) + templates := map[string]string{ + "helpNameTemplate": helpNameTemplate, + "usageTemplate": usageTemplate, + "descriptionTemplate": descriptionTemplate, + "visibleCommandTemplate": visibleCommandTemplate, + "copyrightTemplate": copyrightTemplate, + "versionTemplate": versionTemplate, + "visibleFlagCategoryTemplate": visibleFlagCategoryTemplate, + "visibleFlagTemplate": visibleFlagTemplate, + "visibleGlobalFlagCategoryTemplate": strings.Replace(visibleFlagCategoryTemplate, "OPTIONS", "GLOBAL OPTIONS", -1), + "authorsTemplate": authorsTemplate, + "visibleCommandCategoryTemplate": visibleCommandCategoryTemplate, + } + for name, value := range templates { + if _, err := t.New(name).Parse(value); err != nil { + if os.Getenv("CLI_TEMPLATE_ERROR_DEBUG") != "" { + _, _ = fmt.Fprintf(ErrWriter, "CLI TEMPLATE ERROR: %#v\n", err) + } + } + } err := t.Execute(w, data) if err != nil { @@ -402,6 +424,9 @@ func checkVersion(cCtx *Context) bool { } func checkHelp(cCtx *Context) bool { + if HelpFlag == nil { + return false + } found := false for _, name := range HelpFlag.Names() { if cCtx.Bool(name) { @@ -413,24 +438,6 @@ func checkHelp(cCtx *Context) bool { return found } -func checkCommandHelp(c *Context, name string) bool { - if c.Bool("h") || c.Bool("help") { - _ = ShowCommandHelp(c, name) - return true - } - - return false -} - -func checkSubcommandHelp(cCtx *Context) bool { - if cCtx.Bool("h") || cCtx.Bool("help") { - _ = ShowSubcommandHelp(cCtx) - return true - } - - return false -} - func checkShellCompleteFlag(a *App, arguments []string) (bool, []string) { if !a.EnableBashCompletion { return false, arguments diff --git a/vendor/github.com/urfave/cli/v2/mkdocs-requirements.txt b/vendor/github.com/urfave/cli/v2/mkdocs-reqs.txt similarity index 100% rename from vendor/github.com/urfave/cli/v2/mkdocs-requirements.txt rename to vendor/github.com/urfave/cli/v2/mkdocs-reqs.txt diff --git a/vendor/github.com/urfave/cli/v2/mkdocs.yml b/vendor/github.com/urfave/cli/v2/mkdocs.yml index 02657b9ed..f7bfd419e 100644 --- a/vendor/github.com/urfave/cli/v2/mkdocs.yml +++ b/vendor/github.com/urfave/cli/v2/mkdocs.yml @@ -1,7 +1,7 @@ # NOTE: the mkdocs dependencies will need to be installed out of # band until this whole thing gets more automated: # -# pip install -r mkdocs-requirements.txt +# pip install -r mkdocs-reqs.txt # site_name: urfave/cli diff --git a/vendor/github.com/urfave/cli/v2/sliceflag.go b/vendor/github.com/urfave/cli/v2/sliceflag.go index 7dea3576a..b2ca59041 100644 --- a/vendor/github.com/urfave/cli/v2/sliceflag.go +++ b/vendor/github.com/urfave/cli/v2/sliceflag.go @@ -1,6 +1,3 @@ -//go:build go1.18 -// +build go1.18 - package cli import ( diff --git a/vendor/github.com/urfave/cli/v2/sliceflag_pre18.go b/vendor/github.com/urfave/cli/v2/sliceflag_pre18.go deleted file mode 100644 index 1173ae740..000000000 --- a/vendor/github.com/urfave/cli/v2/sliceflag_pre18.go +++ /dev/null @@ -1,10 +0,0 @@ -//go:build !go1.18 -// +build !go1.18 - -package cli - -import ( - "flag" -) - -func unwrapFlagValue(v flag.Value) flag.Value { return v } diff --git a/vendor/github.com/urfave/cli/v2/suggestions.go b/vendor/github.com/urfave/cli/v2/suggestions.go index 87fa905dd..9d2b7a81e 100644 --- a/vendor/github.com/urfave/cli/v2/suggestions.go +++ b/vendor/github.com/urfave/cli/v2/suggestions.go @@ -1,3 +1,6 @@ +//go:build !urfave_cli_no_suggest +// +build !urfave_cli_no_suggest + package cli import ( @@ -6,6 +9,11 @@ import ( "github.com/xrash/smetrics" ) +func init() { + SuggestFlag = suggestFlag + SuggestCommand = suggestCommand +} + func jaroWinkler(a, b string) float64 { // magic values are from https://github.com/xrash/smetrics/blob/039620a656736e6ad994090895784a7af15e0b80/jaro-winkler.go#L8 const ( @@ -21,7 +29,7 @@ func suggestFlag(flags []Flag, provided string, hideHelp bool) string { for _, flag := range flags { flagNames := flag.Names() - if !hideHelp { + if !hideHelp && HelpFlag != nil { flagNames = append(flagNames, HelpFlag.Names()...) } for _, name := range flagNames { diff --git a/vendor/github.com/urfave/cli/v2/template.go b/vendor/github.com/urfave/cli/v2/template.go index b565ba61e..5748f4c20 100644 --- a/vendor/github.com/urfave/cli/v2/template.go +++ b/vendor/github.com/urfave/cli/v2/template.go @@ -1,7 +1,7 @@ package cli var helpNameTemplate = `{{$v := offset .HelpName 6}}{{wrap .HelpName 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}}` -var usageTemplate = `{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}` +var usageTemplate = `{{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}}{{if .ArgsUsage}}{{.ArgsUsage}}{{else}}{{if .Args}} [arguments...]{{end}}{{end}}{{end}}` var descriptionTemplate = `{{wrap .Description 3}}` var authorsTemplate = `{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}: {{range $index, $author := .Authors}}{{if $index}} @@ -35,7 +35,7 @@ var AppHelpTemplate = `NAME: {{template "helpNameTemplate" .}} USAGE: - {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} + {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}{{if .Args}}[arguments...]{{end}}{{end}}{{end}}{{if .Version}}{{if not .HideVersion}} VERSION: {{.Version}}{{end}}{{end}}{{if .Description}} @@ -83,12 +83,12 @@ var SubcommandHelpTemplate = `NAME: {{template "helpNameTemplate" .}} USAGE: - {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} + {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}{{if .Args}}[arguments...]{{end}}{{end}}{{end}}{{if .Description}} DESCRIPTION: {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} -COMMANDS:{{template "visibleCommandTemplate" .}}{{end}}{{if .VisibleFlagCategories}} +COMMANDS:{{template "visibleCommandCategoryTemplate" .}}{{end}}{{if .VisibleFlagCategories}} OPTIONS:{{template "visibleFlagCategoryTemplate" .}}{{else if .VisibleFlags}} diff --git a/vendor/github.com/urfave/cli/v2/zz_generated.flags.go b/vendor/github.com/urfave/cli/v2/zz_generated.flags.go index 73e771451..f2fc8c88b 100644 --- a/vendor/github.com/urfave/cli/v2/zz_generated.flags.go +++ b/vendor/github.com/urfave/cli/v2/zz_generated.flags.go @@ -23,7 +23,8 @@ type Float64SliceFlag struct { Aliases []string EnvVars []string - defaultValue *Float64Slice + defaultValue *Float64Slice + defaultValueSet bool separator separatorSpec @@ -69,7 +70,8 @@ type GenericFlag struct { Aliases []string EnvVars []string - defaultValue Generic + defaultValue Generic + defaultValueSet bool TakesFile bool @@ -120,7 +122,8 @@ type Int64SliceFlag struct { Aliases []string EnvVars []string - defaultValue *Int64Slice + defaultValue *Int64Slice + defaultValueSet bool separator separatorSpec @@ -166,7 +169,8 @@ type IntSliceFlag struct { Aliases []string EnvVars []string - defaultValue *IntSlice + defaultValue *IntSlice + defaultValueSet bool separator separatorSpec @@ -212,7 +216,8 @@ type PathFlag struct { Aliases []string EnvVars []string - defaultValue Path + defaultValue Path + defaultValueSet bool TakesFile bool @@ -263,7 +268,8 @@ type StringSliceFlag struct { Aliases []string EnvVars []string - defaultValue *StringSlice + defaultValue *StringSlice + defaultValueSet bool separator separatorSpec @@ -313,7 +319,8 @@ type TimestampFlag struct { Aliases []string EnvVars []string - defaultValue *Timestamp + defaultValue *Timestamp + defaultValueSet bool Layout string @@ -366,7 +373,8 @@ type Uint64SliceFlag struct { Aliases []string EnvVars []string - defaultValue *Uint64Slice + defaultValue *Uint64Slice + defaultValueSet bool separator separatorSpec @@ -412,7 +420,8 @@ type UintSliceFlag struct { Aliases []string EnvVars []string - defaultValue *UintSlice + defaultValue *UintSlice + defaultValueSet bool separator separatorSpec @@ -458,7 +467,8 @@ type BoolFlag struct { Aliases []string EnvVars []string - defaultValue bool + defaultValue bool + defaultValueSet bool Count *int @@ -511,7 +521,8 @@ type Float64Flag struct { Aliases []string EnvVars []string - defaultValue float64 + defaultValue float64 + defaultValueSet bool Action func(*Context, float64) error } @@ -560,7 +571,8 @@ type IntFlag struct { Aliases []string EnvVars []string - defaultValue int + defaultValue int + defaultValueSet bool Base int @@ -611,7 +623,8 @@ type Int64Flag struct { Aliases []string EnvVars []string - defaultValue int64 + defaultValue int64 + defaultValueSet bool Base int @@ -662,7 +675,8 @@ type StringFlag struct { Aliases []string EnvVars []string - defaultValue string + defaultValue string + defaultValueSet bool TakesFile bool @@ -713,7 +727,8 @@ type DurationFlag struct { Aliases []string EnvVars []string - defaultValue time.Duration + defaultValue time.Duration + defaultValueSet bool Action func(*Context, time.Duration) error } @@ -762,7 +777,8 @@ type UintFlag struct { Aliases []string EnvVars []string - defaultValue uint + defaultValue uint + defaultValueSet bool Base int @@ -813,7 +829,8 @@ type Uint64Flag struct { Aliases []string EnvVars []string - defaultValue uint64 + defaultValue uint64 + defaultValueSet bool Base int diff --git a/vendor/github.com/waku-org/go-discover/discover/table.go b/vendor/github.com/waku-org/go-discover/discover/table.go index c91b8b856..af77294ef 100644 --- a/vendor/github.com/waku-org/go-discover/discover/table.go +++ b/vendor/github.com/waku-org/go-discover/discover/table.go @@ -193,7 +193,9 @@ func (tab *Table) setFallbackNodes(nodes []*enode.Node) error { return fmt.Errorf("bad bootstrap node %q: %v", n, err) } } + tab.mutex.Lock() tab.nursery = wrapNodes(nodes) + tab.mutex.Unlock() return nil } @@ -305,7 +307,9 @@ func (tab *Table) doRefresh(done chan struct{}) { func (tab *Table) loadSeedNodes() { seeds := wrapNodes(tab.db.QuerySeeds(seedCount, seedMaxAge)) + tab.mutex.Lock() seeds = append(seeds, tab.nursery...) + tab.mutex.Unlock() for i := range seeds { seed := seeds[i] age := log.Lazy{Fn: func() interface{} { return time.Since(tab.db.LastPongReceived(seed.ID(), seed.IP())) }} diff --git a/vendor/github.com/waku-org/go-libp2p-rendezvous/svc.go b/vendor/github.com/waku-org/go-libp2p-rendezvous/svc.go index f2dfa9457..64a5eed64 100644 --- a/vendor/github.com/waku-org/go-libp2p-rendezvous/svc.go +++ b/vendor/github.com/waku-org/go-libp2p-rendezvous/svc.go @@ -32,7 +32,7 @@ func (rz *RendezvousService) handleStream(s inet.Stream) { defer s.Reset() pid := s.Conn().RemotePeer() - log.Debugf("New stream from %s", pid.Pretty()) + log.Debugf("New stream from %s", pid.String()) r := pbio.NewDelimitedReader(s, inet.MessageSizeMax) w := pbio.NewDelimitedWriter(s) diff --git a/vendor/github.com/waku-org/go-waku/logging/logging.go b/vendor/github.com/waku-org/go-waku/logging/logging.go index fb0295ae3..7e872eefc 100644 --- a/vendor/github.com/waku-org/go-waku/logging/logging.go +++ b/vendor/github.com/waku-org/go-waku/logging/logging.go @@ -15,7 +15,8 @@ import ( "github.com/ethereum/go-ethereum/p2p/enode" "github.com/libp2p/go-libp2p/core/peer" "github.com/multiformats/go-multiaddr" - "github.com/waku-org/go-waku/waku/v2/protocol/store/pb" + "github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb" + wpb "github.com/waku-org/go-waku/waku/v2/protocol/pb" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) @@ -128,6 +129,10 @@ func (bytes hexBytes) String() string { return hexutil.Encode(bytes) } +func Hash(hash wpb.MessageHash) zap.Field { + return zap.Stringer("hash", hash) +} + // ENode creates a field for ENR node. func ENode(key string, node *enode.Node) zap.Field { return zap.Stringer(key, node) diff --git a/vendor/github.com/waku-org/go-waku/waku/persistence/store.go b/vendor/github.com/waku-org/go-waku/waku/persistence/store.go index 574d1877d..10540c7ce 100644 --- a/vendor/github.com/waku-org/go-waku/waku/persistence/store.go +++ b/vendor/github.com/waku-org/go-waku/waku/persistence/store.go @@ -11,8 +11,8 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/waku-org/go-waku/waku/v2/protocol" + "github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb" wpb "github.com/waku-org/go-waku/waku/v2/protocol/pb" - "github.com/waku-org/go-waku/waku/v2/protocol/store/pb" "github.com/waku-org/go-waku/waku/v2/timesource" "go.uber.org/zap" "google.golang.org/protobuf/proto" @@ -317,8 +317,10 @@ func (d *DBStore) Put(env *protocol.Envelope) error { storedAt = env.Index().ReceiverTime } + hash := env.Hash() + start := time.Now() - _, err = stmt.Exec(env.Index().Digest, env.Hash(), storedAt, env.Message().GetTimestamp(), env.Message().ContentTopic, env.PubsubTopic(), env.Message().Payload, env.Message().GetVersion()) + _, err = stmt.Exec(env.Index().Digest, hash[:], storedAt, env.Message().GetTimestamp(), env.Message().ContentTopic, env.PubsubTopic(), env.Message().Payload, env.Message().GetVersion()) if err != nil { return err } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/dnsdisc/resolver.go b/vendor/github.com/waku-org/go-waku/waku/v2/dnsdisc/resolver.go index 8985b33b9..3cb12a1ac 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/dnsdisc/resolver.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/dnsdisc/resolver.go @@ -14,7 +14,7 @@ func GetResolver(ctx context.Context, nameserver string) *net.Resolver { return &net.Resolver{ PreferGo: true, - Dial: func(ctx context.Context, network, address string) (net.Conn, error) { + Dial: func(_ context.Context, network, address string) (net.Conn, error) { d := net.Dialer{} return d.DialContext(ctx, network, net.JoinHostPort(nameserver, "53")) }, diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/node/connectedness.go b/vendor/github.com/waku-org/go-waku/waku/v2/node/connectedness.go index 7aa509609..5a0f89fe2 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/node/connectedness.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/node/connectedness.go @@ -9,10 +9,6 @@ import ( "github.com/libp2p/go-libp2p/core/protocol" "github.com/multiformats/go-multiaddr" "github.com/waku-org/go-waku/logging" - "github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter" - "github.com/waku-org/go-waku/waku/v2/protocol/lightpush" - "github.com/waku-org/go-waku/waku/v2/protocol/relay" - "github.com/waku-org/go-waku/waku/v2/protocol/store" "go.uber.org/zap" wps "github.com/waku-org/go-waku/waku/v2/peerstore" @@ -21,14 +17,6 @@ import ( // PeerStatis is a map of peer IDs to supported protocols type PeerStats map[peer.ID][]protocol.ID -// ConnStatus is used to indicate if the node is online, has access to history -// and also see the list of peers the node is aware of -type ConnStatus struct { - IsOnline bool - HasHistory bool - Peers PeerStats -} - type PeerConnection struct { PeerID peer.ID Connected bool @@ -112,15 +100,6 @@ func (c ConnectionNotifier) ClosedStream(n network.Network, s network.Stream) { func (c ConnectionNotifier) Close() { } -func (w *WakuNode) sendConnStatus() { - isOnline, hasHistory := w.Status() - if w.connStatusChan != nil { - connStatus := ConnStatus{IsOnline: isOnline, HasHistory: hasHistory, Peers: w.PeerStats()} - w.connStatusChan <- connStatus - } - -} - func (w *WakuNode) connectednessListener(ctx context.Context) { defer w.wg.Done() @@ -128,53 +107,7 @@ func (w *WakuNode) connectednessListener(ctx context.Context) { select { case <-ctx.Done(): return - case <-w.protocolEventSub.Out(): - case <-w.identificationEventSub.Out(): case <-w.connectionNotif.DisconnectChan: } - w.sendConnStatus() } } - -// Status returns the current status of the node (online or not) -// and if the node has access to history nodes or not -func (w *WakuNode) Status() (isOnline bool, hasHistory bool) { - hasRelay := false - hasLightPush := false - hasStore := false - hasFilter := false - - for _, peer := range w.host.Network().Peers() { - protocols, err := w.host.Peerstore().GetProtocols(peer) - if err != nil { - w.log.Warn("reading peer protocols", logging.HostID("peer", peer), zap.Error(err)) - } - - for _, protocol := range protocols { - if !hasRelay && protocol == relay.WakuRelayID_v200 { - hasRelay = true - } - if !hasLightPush && protocol == lightpush.LightPushID_v20beta1 { - hasLightPush = true - } - if !hasStore && protocol == store.StoreID_v20beta4 { - hasStore = true - } - if !hasFilter && protocol == legacy_filter.FilterID_v20beta1 { - hasFilter = true - } - } - } - - if hasStore { - hasHistory = true - } - - if w.opts.enableFilterLightNode && !w.opts.enableRelay { - isOnline = hasLightPush && hasFilter - } else { - isOnline = hasRelay || hasLightPush && (hasStore || hasFilter) - } - - return -} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/node/wakunode2.go b/vendor/github.com/waku-org/go-waku/waku/v2/node/wakunode2.go index 578d4cdd9..323958e58 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/node/wakunode2.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/node/wakunode2.go @@ -11,6 +11,7 @@ import ( golog "github.com/ipfs/go-log/v2" "github.com/libp2p/go-libp2p" "go.uber.org/zap" + "golang.org/x/exp/maps" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p/enode" @@ -35,7 +36,7 @@ import ( wakuprotocol "github.com/waku-org/go-waku/waku/v2/protocol" "github.com/waku-org/go-waku/waku/v2/protocol/enr" "github.com/waku-org/go-waku/waku/v2/protocol/filter" - "github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter" + "github.com/waku-org/go-waku/waku/v2/protocol/legacy_store" "github.com/waku-org/go-waku/waku/v2/protocol/lightpush" "github.com/waku-org/go-waku/waku/v2/protocol/metadata" "github.com/waku-org/go-waku/waku/v2/protocol/pb" @@ -59,7 +60,7 @@ type Peer struct { PubsubTopics []string `json:"pubsubTopics"` } -type storeFactory func(w *WakuNode) store.Store +type storeFactory func(w *WakuNode) legacy_store.Store type byte32 = [32]byte @@ -98,10 +99,10 @@ type WakuNode struct { peerExchange Service rendezvous Service metadata Service - legacyFilter ReceptorService filterFullNode ReceptorService filterLightNode Service - store ReceptorService + legacyStore ReceptorService + store *store.WakuStore rlnRelay RLNRelay wakuFlag enr.WakuEnrBitfield @@ -111,11 +112,9 @@ type WakuNode struct { bcaster relay.Broadcaster - connectionNotif ConnectionNotifier - protocolEventSub event.Subscription - identificationEventSub event.Subscription - addressChangesSub event.Subscription - enrChangeCh chan struct{} + connectionNotif ConnectionNotifier + addressChangesSub event.Subscription + enrChangeCh chan struct{} keepAliveMutex sync.Mutex keepAliveFails map[peer.ID]int @@ -123,17 +122,13 @@ type WakuNode struct { cancel context.CancelFunc wg *sync.WaitGroup - // Channel passed to WakuNode constructor - // receiving connection status notifications - connStatusChan chan<- ConnStatus - storeFactory storeFactory peermanager *peermanager.PeerManager } -func defaultStoreFactory(w *WakuNode) store.Store { - return store.NewWakuStore(w.opts.messageProvider, w.peermanager, w.timesource, w.opts.prometheusReg, w.log) +func defaultStoreFactory(w *WakuNode) legacy_store.Store { + return legacy_store.NewWakuStore(w.opts.messageProvider, w.peermanager, w.timesource, w.opts.prometheusReg, w.log) } // New is used to instantiate a WakuNode using a set of WakuNodeOptions @@ -199,7 +194,7 @@ func New(opts ...WakuNodeOption) (*WakuNode, error) { w.log = params.logger.Named("node2") w.wg = &sync.WaitGroup{} w.keepAliveFails = make(map[peer.ID]int) - w.wakuFlag = enr.NewWakuEnrBitfield(w.opts.enableLightPush, w.opts.enableLegacyFilter, w.opts.enableStore, w.opts.enableRelay) + w.wakuFlag = enr.NewWakuEnrBitfield(w.opts.enableLightPush, w.opts.enableFilterFullNode, w.opts.enableStore, w.opts.enableRelay) w.circuitRelayNodes = make(chan peer.AddrInfo) w.metrics = newMetrics(params.prometheusReg) @@ -257,10 +252,11 @@ func New(opts ...WakuNodeOption) (*WakuNode, error) { w.log.Error("creating localnode", zap.Error(err)) } - w.metadata = metadata.NewWakuMetadata(w.opts.clusterID, w.localNode, w.log) + metadata := metadata.NewWakuMetadata(w.opts.clusterID, w.localNode, w.log) + w.metadata = metadata //Initialize peer manager. - w.peermanager = peermanager.NewPeerManager(w.opts.maxPeerConnections, w.opts.peerStoreCapacity, w.log) + w.peermanager = peermanager.NewPeerManager(w.opts.maxPeerConnections, w.opts.peerStoreCapacity, metadata, w.log) w.peerConnector, err = peermanager.NewPeerConnectionStrategy(w.peermanager, discoveryConnectTimeout, w.log) if err != nil { @@ -291,22 +287,22 @@ func New(opts ...WakuNodeOption) (*WakuNode, error) { } } - w.opts.legacyFilterOpts = append(w.opts.legacyFilterOpts, legacy_filter.WithPeerManager(w.peermanager)) w.opts.filterOpts = append(w.opts.filterOpts, filter.WithPeerManager(w.peermanager)) - w.legacyFilter = legacy_filter.NewWakuFilter(w.bcaster, w.opts.isLegacyFilterFullNode, w.timesource, w.opts.prometheusReg, w.log, w.opts.legacyFilterOpts...) w.filterFullNode = filter.NewWakuFilterFullNode(w.timesource, w.opts.prometheusReg, w.log, w.opts.filterOpts...) w.filterLightNode = filter.NewWakuFilterLightNode(w.bcaster, w.peermanager, w.timesource, w.opts.prometheusReg, w.log) w.lightPush = lightpush.NewWakuLightPush(w.Relay(), w.peermanager, w.opts.prometheusReg, w.log) + w.store = store.NewWakuStore(w.peermanager, w.timesource, w.log) + if params.storeFactory != nil { w.storeFactory = params.storeFactory } else { w.storeFactory = defaultStoreFactory } - if params.connStatusC != nil { - w.connStatusChan = params.connStatusC + if params.topicHealthNotifCh != nil { + w.peermanager.TopicHealthNotifCh = params.topicHealthNotifCh } return w, nil @@ -324,13 +320,13 @@ func (w *WakuNode) watchMultiaddressChanges(ctx context.Context) { case <-ctx.Done(): return case <-first: - addr := utils.MultiAddrFromSet(addrsSet) + addr := maps.Values(addrsSet) w.log.Info("listening", logging.MultiAddrs("multiaddr", addr...)) case <-w.addressChangesSub.Out(): newAddrs := utils.MultiAddrSet(w.ListenAddresses()...) if !utils.MultiAddrSetEquals(addrsSet, newAddrs) { addrsSet = newAddrs - addrs := utils.MultiAddrFromSet(addrsSet) + addrs := maps.Values(addrsSet) w.log.Info("listening addresses update received", logging.MultiAddrs("multiaddr", addrs...)) err := w.setupENR(ctx, addrs) if err != nil { @@ -343,7 +339,7 @@ func (w *WakuNode) watchMultiaddressChanges(ctx context.Context) { // Start initializes all the protocols that were setup in the WakuNode func (w *WakuNode) Start(ctx context.Context) error { - connGater := peermanager.NewConnectionGater(w.log) + connGater := peermanager.NewConnectionGater(w.opts.maxConnectionsPerIP, w.log) ctx, cancel := context.WithCancel(ctx) w.cancel = cancel @@ -363,14 +359,6 @@ func (w *WakuNode) Start(ctx context.Context) error { w.host = host - if w.protocolEventSub, err = host.EventBus().Subscribe(new(event.EvtPeerProtocolsUpdated)); err != nil { - return err - } - - if w.identificationEventSub, err = host.EventBus().Subscribe(new(event.EvtPeerIdentificationCompleted)); err != nil { - return err - } - if w.addressChangesSub, err = host.EventBus().Subscribe(new(event.EvtLocalAddressesUpdated)); err != nil { return err } @@ -438,8 +426,8 @@ func (w *WakuNode) Start(ctx context.Context) error { w.registerAndMonitorReachability(ctx) } - w.store = w.storeFactory(w) - w.store.SetHost(host) + w.legacyStore = w.storeFactory(w) + w.legacyStore.SetHost(host) if w.opts.enableStore { sub := w.bcaster.RegisterForAll() err := w.startStore(ctx, sub) @@ -449,6 +437,8 @@ func (w *WakuNode) Start(ctx context.Context) error { w.log.Info("Subscribing store to broadcaster") } + w.store.SetHost(host) + w.lightPush.SetHost(host) if w.opts.enableLightPush { if err := w.lightPush.Start(ctx); err != nil { @@ -456,16 +446,6 @@ func (w *WakuNode) Start(ctx context.Context) error { } } - w.legacyFilter.SetHost(host) - if w.opts.enableLegacyFilter { - sub := w.bcaster.RegisterForAll() - err := w.legacyFilter.Start(ctx, sub) - if err != nil { - return err - } - w.log.Info("Subscribing filter to broadcaster") - } - w.filterFullNode.SetHost(host) if w.opts.enableFilterFullNode { sub := w.bcaster.RegisterForAll() @@ -518,16 +498,13 @@ func (w *WakuNode) Stop() { w.bcaster.Stop() defer w.connectionNotif.Close() - defer w.protocolEventSub.Close() - defer w.identificationEventSub.Close() defer w.addressChangesSub.Close() w.host.Network().StopNotify(w.connectionNotif) w.relay.Stop() w.lightPush.Stop() - w.store.Stop() - w.legacyFilter.Stop() + w.legacyStore.Stop() w.filterFullNode.Stop() w.filterLightNode.Stop() @@ -561,7 +538,7 @@ func (w *WakuNode) Host() host.Host { // ID returns the base58 encoded ID from the host func (w *WakuNode) ID() string { - return w.host.ID().Pretty() + return w.host.ID().String() } func (w *WakuNode) watchENRChanges(ctx context.Context) { @@ -612,17 +589,14 @@ func (w *WakuNode) Relay() *relay.WakuRelay { return nil } -// Store is used to access any operation related to Waku Store protocol -func (w *WakuNode) Store() store.Store { - return w.store.(store.Store) +// LegacyStore is used to access any operation related to Waku Store protocol +func (w *WakuNode) LegacyStore() legacy_store.Store { + return w.legacyStore.(legacy_store.Store) } -// LegacyFilter is used to access any operation related to Waku LegacyFilter protocol -func (w *WakuNode) LegacyFilter() *legacy_filter.WakuFilter { - if result, ok := w.legacyFilter.(*legacy_filter.WakuFilter); ok { - return result - } - return nil +// Store is used to access any operation related to Waku Store protocol +func (w *WakuNode) Store() *store.WakuStore { + return w.store } // FilterLightnode is used to access any operation related to Waku Filter protocol Full node feature @@ -704,7 +678,7 @@ func (w *WakuNode) mountDiscV5() error { } func (w *WakuNode) startStore(ctx context.Context, sub *relay.Subscription) error { - err := w.store.Start(ctx, sub) + err := w.legacyStore.Start(ctx, sub) if err != nil { w.log.Error("starting store", zap.Error(err)) return err diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/node/wakuoptions.go b/vendor/github.com/waku-org/go-waku/waku/v2/node/wakuoptions.go index dd6d9958e..60608f779 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/node/wakuoptions.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/node/wakuoptions.go @@ -12,12 +12,12 @@ import ( "github.com/ethereum/go-ethereum/p2p/enode" logging "github.com/ipfs/go-log/v2" "github.com/libp2p/go-libp2p" + mplex "github.com/libp2p/go-libp2p-mplex" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/config" "github.com/libp2p/go-libp2p/core/crypto" "github.com/libp2p/go-libp2p/core/peerstore" basichost "github.com/libp2p/go-libp2p/p2p/host/basic" - "github.com/libp2p/go-libp2p/p2p/muxer/mplex" "github.com/libp2p/go-libp2p/p2p/muxer/yamux" "github.com/libp2p/go-libp2p/p2p/net/connmgr" quic "github.com/libp2p/go-libp2p/p2p/transport/quic" @@ -26,10 +26,10 @@ import ( "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" "github.com/prometheus/client_golang/prometheus" + "github.com/waku-org/go-waku/waku/v2/peermanager" "github.com/waku-org/go-waku/waku/v2/protocol/filter" - "github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter" + "github.com/waku-org/go-waku/waku/v2/protocol/legacy_store" "github.com/waku-org/go-waku/waku/v2/protocol/pb" - "github.com/waku-org/go-waku/waku/v2/protocol/store" "github.com/waku-org/go-waku/waku/v2/rendezvous" "github.com/waku-org/go-waku/waku/v2/timesource" "github.com/waku-org/go-waku/waku/v2/utils" @@ -37,23 +37,26 @@ import ( "go.uber.org/zap/zapcore" ) -// Default userAgent -const userAgent string = "go-waku" +// Default UserAgent +const UserAgent string = "go-waku" // Default minRelayPeersToPublish const defaultMinRelayPeersToPublish = 0 +const DefaultMaxConnectionsPerIP = 5 + type WakuNodeParameters struct { - hostAddr *net.TCPAddr - clusterID uint16 - dns4Domain string - advertiseAddrs []multiaddr.Multiaddr - multiAddr []multiaddr.Multiaddr - addressFactory basichost.AddrsFactory - privKey *ecdsa.PrivateKey - libP2POpts []libp2p.Option - peerstore peerstore.Peerstore - prometheusReg prometheus.Registerer + hostAddr *net.TCPAddr + maxConnectionsPerIP int + clusterID uint16 + dns4Domain string + advertiseAddrs []multiaddr.Multiaddr + multiAddr []multiaddr.Multiaddr + addressFactory basichost.AddrsFactory + privKey *ecdsa.PrivateKey + libP2POpts []libp2p.Option + peerstore peerstore.Peerstore + prometheusReg prometheus.Registerer circuitRelayMinInterval time.Duration circuitRelayBootDelay time.Duration @@ -70,20 +73,17 @@ type WakuNodeParameters struct { logger *zap.Logger logLevel logging.LogLevel - enableRelay bool - enableLegacyFilter bool - isLegacyFilterFullNode bool - enableFilterLightNode bool - enableFilterFullNode bool - legacyFilterOpts []legacy_filter.Option - filterOpts []filter.Option - pubsubOpts []pubsub.Option + enableRelay bool + enableFilterLightNode bool + enableFilterFullNode bool + filterOpts []filter.Option + pubsubOpts []pubsub.Option minRelayPeersToPublish int maxMsgSizeBytes int enableStore bool - messageProvider store.MessageProvider + messageProvider legacy_store.MessageProvider enableRendezvousPoint bool rendezvousDB *rendezvous.DB @@ -112,8 +112,8 @@ type WakuNodeParameters struct { enableLightPush bool - connStatusC chan<- ConnStatus - connNotifCh chan<- PeerConnection + connNotifCh chan<- PeerConnection + topicHealthNotifCh chan<- peermanager.TopicHealthStatus storeFactory storeFactory } @@ -124,6 +124,7 @@ type WakuNodeOption func(*WakuNodeParameters) error var DefaultWakuNodeOptions = []WakuNodeOption{ WithPrometheusRegisterer(prometheus.NewRegistry()), WithMaxPeerConnections(50), + WithMaxConnectionsPerIP(DefaultMaxConnectionsPerIP), WithCircuitRelayParams(2*time.Second, 3*time.Minute), } @@ -151,6 +152,7 @@ func (w WakuNodeParameters) AddressFactory() basichost.AddrsFactory { func WithLogger(l *zap.Logger) WakuNodeOption { return func(params *WakuNodeParameters) error { params.logger = l + logging.SetAllLoggers(logging.LogLevel(l.Level())) logging.SetPrimaryCore(l.Core()) return nil } @@ -304,6 +306,14 @@ func WithClusterID(clusterID uint16) WakuNodeOption { } } +// WithMaxConnectionsPerIP sets the max number of allowed peers from the same IP +func WithMaxConnectionsPerIP(limit int) WakuNodeOption { + return func(params *WakuNodeParameters) error { + params.maxConnectionsPerIP = limit + return nil + } +} + // WithNTP is used to use ntp for any operation that requires obtaining time // A list of ntp servers can be passed but if none is specified, some defaults // will be used @@ -399,17 +409,6 @@ func WithPeerExchange() WakuNodeOption { } } -// WithLegacyWakuFilter enables the legacy Waku Filter protocol. This WakuNodeOption -// accepts a list of WakuFilter gossipsub options to setup the protocol -func WithLegacyWakuFilter(fullnode bool, filterOpts ...legacy_filter.Option) WakuNodeOption { - return func(params *WakuNodeParameters) error { - params.enableLegacyFilter = true - params.isLegacyFilterFullNode = fullnode - params.legacyFilterOpts = filterOpts - return nil - } -} - // WithWakuFilter enables the Waku Filter V2 protocol for lightnode functionality func WithWakuFilterLightNode() WakuNodeOption { return func(params *WakuNodeParameters) error { @@ -449,7 +448,7 @@ func WithWakuStoreFactory(factory storeFactory) WakuNodeOption { // WithMessageProvider is a WakuNodeOption that sets the MessageProvider // used to store and retrieve persisted messages -func WithMessageProvider(s store.MessageProvider) WakuNodeOption { +func WithMessageProvider(s legacy_store.MessageProvider) WakuNodeOption { return func(params *WakuNodeParameters) error { if s == nil { return errors.New("message provider can't be nil") @@ -476,16 +475,6 @@ func WithKeepAlive(t time.Duration) WakuNodeOption { } } -// WithConnectionStatusChannel is a WakuNodeOption used to set a channel where the -// connection status changes will be pushed to. It's useful to identify when peer -// connections and disconnections occur -func WithConnectionStatusChannel(connStatus chan ConnStatus) WakuNodeOption { - return func(params *WakuNodeParameters) error { - params.connStatusC = connStatus - return nil - } -} - func WithConnectionNotification(ch chan<- PeerConnection) WakuNodeOption { return func(params *WakuNodeParameters) error { params.connNotifCh = ch @@ -553,6 +542,13 @@ func WithCircuitRelayParams(minInterval time.Duration, bootDelay time.Duration) } } +func WithTopicHealthStatusChannel(ch chan<- peermanager.TopicHealthStatus) WakuNodeOption { + return func(params *WakuNodeParameters) error { + params.topicHealthNotifCh = ch + return nil + } +} + // Default options used in the libp2p node var DefaultLibP2POptions = []libp2p.Option{ libp2p.ChainOptions( @@ -560,7 +556,7 @@ var DefaultLibP2POptions = []libp2p.Option{ libp2p.Transport(quic.NewTransport), libp2p.Transport(libp2pwebtransport.New), ), - libp2p.UserAgent(userAgent), + libp2p.UserAgent(UserAgent), libp2p.ChainOptions( libp2p.Muxer("/yamux/1.0.0", yamux.DefaultTransport), libp2p.Muxer("/mplex/6.7.0", mplex.DefaultTransport), diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/connection_gater.go b/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/connection_gater.go index 5b9b761bd..d08008139 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/connection_gater.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/connection_gater.go @@ -16,19 +16,21 @@ import ( // the number of connections per IP address type ConnectionGater struct { sync.Mutex - logger *zap.Logger - limiter map[string]int + logger *zap.Logger + limiter map[string]int + maxConnsPerIP int } -const maxConnsPerIP = 10 - // NewConnectionGater creates a new instance of ConnectionGater -func NewConnectionGater(logger *zap.Logger) *ConnectionGater { +func NewConnectionGater(maxConnsPerIP int, logger *zap.Logger) *ConnectionGater { c := &ConnectionGater{ - logger: logger.Named("connection-gater"), - limiter: make(map[string]int), + logger: logger.Named("connection-gater"), + maxConnsPerIP: maxConnsPerIP, + limiter: make(map[string]int), } + c.logger.Info("configured settings", zap.Int("maxConnsPerIP", maxConnsPerIP)) + return c } @@ -103,7 +105,7 @@ func (c *ConnectionGater) validateInboundConn(addr multiaddr.Multiaddr) bool { c.Lock() defer c.Unlock() - if currConnections := c.limiter[ip.String()]; currConnections+1 > maxConnsPerIP { + if currConnections := c.limiter[ip.String()]; c.maxConnsPerIP > 0 && currConnections+1 > c.maxConnsPerIP { return false } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_connector.go b/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_connector.go index ece035398..8dc8e6038 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_connector.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_connector.go @@ -127,10 +127,10 @@ func (c *PeerConnectionStrategy) consumeSubscription(s subscription) { triggerImmediateConnection := false //Not connecting to peer as soon as it is discovered, // rather expecting this to be pushed from PeerManager based on the need. - if len(c.host.Network().Peers()) < waku_proto.GossipSubOptimalFullMeshSize { + if len(c.host.Network().Peers()) < waku_proto.GossipSubDMin { triggerImmediateConnection = true } - c.logger.Debug("adding discovered peer", logging.HostID("peer", p.AddrInfo.ID)) + c.logger.Debug("adding discovered peer", logging.HostID("peerID", p.AddrInfo.ID)) c.pm.AddDiscoveredPeer(p, triggerImmediateConnection) case <-time.After(1 * time.Second): @@ -198,13 +198,18 @@ func (c *PeerConnectionStrategy) canDialPeer(pi peer.AddrInfo) bool { tv := val.(*connCacheData) now := time.Now() if now.Before(tv.nextTry) { + c.logger.Debug("Skipping connecting to peer due to backoff strategy", + zap.Time("currentTime", now), zap.Time("until", tv.nextTry)) return false } - + c.logger.Debug("Proceeding with connecting to peer", + zap.Time("currentTime", now), zap.Time("nextTry", tv.nextTry)) tv.nextTry = now.Add(tv.strat.Delay()) } else { cachedPeer = &connCacheData{strat: c.backoff()} cachedPeer.nextTry = time.Now().Add(cachedPeer.strat.Delay()) + c.logger.Debug("Initializing connectionCache for peer ", + logging.HostID("peerID", pi.ID), zap.Time("until", cachedPeer.nextTry)) c.cache.Add(pi.ID, cachedPeer) } return true diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_discovery.go b/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_discovery.go index c64391462..d1cedac29 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_discovery.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_discovery.go @@ -112,7 +112,7 @@ func (pm *PeerManager) discoverPeersByPubsubTopics(pubsubTopics []string, proto for _, shardInfo := range shardsInfo { err = pm.DiscoverAndConnectToPeers(ctx, shardInfo.ClusterID, shardInfo.ShardIDs[0], proto, maxCount) if err != nil { - pm.logger.Error("failed to discover and conenct to peers", zap.Error(err)) + pm.logger.Error("failed to discover and connect to peers", zap.Error(err)) } } } else { diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_manager.go b/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_manager.go index 85806d05f..d1af8ce3c 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_manager.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_manager.go @@ -20,15 +20,43 @@ import ( wps "github.com/waku-org/go-waku/waku/v2/peerstore" waku_proto "github.com/waku-org/go-waku/waku/v2/protocol" wenr "github.com/waku-org/go-waku/waku/v2/protocol/enr" + "github.com/waku-org/go-waku/waku/v2/protocol/metadata" "github.com/waku-org/go-waku/waku/v2/protocol/relay" "github.com/waku-org/go-waku/waku/v2/service" "go.uber.org/zap" ) +type TopicHealth int + +const ( + UnHealthy = iota + MinimallyHealthy = 1 + SufficientlyHealthy = 2 +) + +func (t TopicHealth) String() string { + switch t { + case UnHealthy: + return "UnHealthy" + case MinimallyHealthy: + return "MinimallyHealthy" + case SufficientlyHealthy: + return "SufficientlyHealthy" + default: + return "" + } +} + +type TopicHealthStatus struct { + Topic string + Health TopicHealth +} + // NodeTopicDetails stores pubSubTopic related data like topicHandle for the node. type NodeTopicDetails struct { - topic *pubsub.Topic + topic *pubsub.Topic + healthStatus TopicHealth } // WakuProtoInfo holds protocol specific info @@ -41,6 +69,7 @@ type WakuProtoInfo struct { // PeerManager applies various controls and manage connections towards peers. type PeerManager struct { peerConnector *PeerConnectionStrategy + metadata *metadata.WakuMetadata maxPeers int maxRelayPeers int logger *zap.Logger @@ -54,6 +83,7 @@ type PeerManager struct { subRelayTopics map[string]*NodeTopicDetails discoveryService *discv5.DiscoveryV5 wakuprotoToENRFieldMap map[protocol.ID]WakuProtoInfo + TopicHealthNotifCh chan<- TopicHealthStatus } // PeerSelection provides various options based on which Peer is selected from a list of peers. @@ -87,8 +117,59 @@ func inAndOutRelayPeers(relayPeers int) (int, int) { return relayPeers - outRelayPeers, outRelayPeers } +// checkAndUpdateTopicHealth finds health of specified topic and updates and notifies of the same. +// Also returns the healthyPeerCount +func (pm *PeerManager) checkAndUpdateTopicHealth(topic *NodeTopicDetails) int { + healthyPeerCount := 0 + for _, p := range topic.topic.ListPeers() { + if pm.host.Network().Connectedness(p) == network.Connected { + pThreshold, err := pm.host.Peerstore().(wps.WakuPeerstore).Score(p) + if err == nil { + if pThreshold < relay.PeerPublishThreshold { + pm.logger.Debug("peer score below publish threshold", logging.HostID("peer", p), zap.Float64("score", pThreshold)) + } else { + healthyPeerCount++ + } + } else { + pm.logger.Warn("failed to fetch peer score ", zap.Error(err), logging.HostID("peer", p)) + //For now considering peer as healthy if we can't fetch score. + healthyPeerCount++ + } + } + } + //Update topic's health + oldHealth := topic.healthStatus + if healthyPeerCount < 1 { //Ideally this check should be done with minPeersForRelay, but leaving it as is for now. + topic.healthStatus = UnHealthy + } else if healthyPeerCount < waku_proto.GossipSubDMin { + topic.healthStatus = MinimallyHealthy + } else { + topic.healthStatus = SufficientlyHealthy + } + + if oldHealth != topic.healthStatus { + //Check old health, and if there is a change notify of the same. + pm.logger.Debug("topic health has changed", zap.String("pubsubtopic", topic.topic.String()), zap.Stringer("health", topic.healthStatus)) + pm.TopicHealthNotifCh <- TopicHealthStatus{topic.topic.String(), topic.healthStatus} + } + return healthyPeerCount +} + +// TopicHealth can be used to fetch health of a specific pubsubTopic. +// Returns error if topic is not found. +func (pm *PeerManager) TopicHealth(pubsubTopic string) (TopicHealth, error) { + pm.topicMutex.RLock() + defer pm.topicMutex.RUnlock() + + topicDetails, ok := pm.subRelayTopics[pubsubTopic] + if !ok { + return UnHealthy, errors.New("topic not found") + } + return topicDetails.healthStatus, nil +} + // NewPeerManager creates a new peerManager instance. -func NewPeerManager(maxConnections int, maxPeers int, logger *zap.Logger) *PeerManager { +func NewPeerManager(maxConnections int, maxPeers int, metadata *metadata.WakuMetadata, logger *zap.Logger) *PeerManager { maxRelayPeers, _ := relayAndServicePeers(maxConnections) inRelayPeersTarget, outRelayPeersTarget := inAndOutRelayPeers(maxRelayPeers) @@ -99,6 +180,7 @@ func NewPeerManager(maxConnections int, maxPeers int, logger *zap.Logger) *PeerM pm := &PeerManager{ logger: logger.Named("peer-manager"), + metadata: metadata, maxRelayPeers: maxRelayPeers, InRelayPeersTarget: inRelayPeersTarget, OutRelayPeersTarget: outRelayPeersTarget, @@ -212,26 +294,22 @@ func (pm *PeerManager) ensureMinRelayConnsPerTopic() { // @cammellos reported that ListPeers returned an invalid number of // peers. This will ensure that the peers returned by this function // match those peers that are currently connected - curPeerLen := 0 - for _, p := range topicInst.topic.ListPeers() { - if pm.host.Network().Connectedness(p) == network.Connected { - curPeerLen++ - } - } - if curPeerLen < waku_proto.GossipSubOptimalFullMeshSize { - pm.logger.Debug("subscribed topic is unhealthy, initiating more connections to maintain health", + + curPeerLen := pm.checkAndUpdateTopicHealth(topicInst) + if curPeerLen < waku_proto.GossipSubDMin { + pm.logger.Debug("subscribed topic is not sufficiently healthy, initiating more connections to maintain health", zap.String("pubSubTopic", topicStr), zap.Int("connectedPeerCount", curPeerLen), - zap.Int("optimumPeers", waku_proto.GossipSubOptimalFullMeshSize)) + zap.Int("optimumPeers", waku_proto.GossipSubDMin)) //Find not connected peers. notConnectedPeers := pm.getNotConnectedPers(topicStr) if notConnectedPeers.Len() == 0 { pm.logger.Debug("could not find any peers in peerstore to connect to, discovering more", zap.String("pubSubTopic", topicStr)) - pm.discoverPeersByPubsubTopics([]string{topicStr}, relay.WakuRelayID_v200, pm.ctx, 2) + go pm.discoverPeersByPubsubTopics([]string{topicStr}, relay.WakuRelayID_v200, pm.ctx, 2) continue } pm.logger.Debug("connecting to eligible peers in peerstore", zap.String("pubSubTopic", topicStr)) //Connect to eligible peers. - numPeersToConnect := waku_proto.GossipSubOptimalFullMeshSize - curPeerLen + numPeersToConnect := waku_proto.GossipSubDMin - curPeerLen if numPeersToConnect > notConnectedPeers.Len() { numPeersToConnect = notConnectedPeers.Len() diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_selection.go b/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_selection.go index bfaab97a4..2b4c3807d 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_selection.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/peer_selection.go @@ -3,7 +3,6 @@ package peermanager import ( "context" "errors" - "math/rand" "sync" "time" @@ -14,8 +13,11 @@ import ( wps "github.com/waku-org/go-waku/waku/v2/peerstore" waku_proto "github.com/waku-org/go-waku/waku/v2/protocol" "go.uber.org/zap" + "golang.org/x/exp/maps" ) +type peerSet map[peer.ID]struct{} + // SelectPeerByContentTopic is used to return a random peer that supports a given protocol for given contentTopic. // If a list of specific peers is passed, the peer will be chosen from that list assuming // it supports the chosen protocol and contentTopic, otherwise it will chose a peer from the service slot. @@ -30,7 +32,11 @@ func (pm *PeerManager) SelectPeerByContentTopics(proto protocol.ID, contentTopic } pubsubTopics = append(pubsubTopics, pubsubTopic) } - return pm.SelectPeer(PeerSelectionCriteria{PubsubTopics: pubsubTopics, Proto: proto, SpecificPeers: specificPeers}) + peers, err := pm.SelectPeers(PeerSelectionCriteria{PubsubTopics: pubsubTopics, Proto: proto, SpecificPeers: specificPeers}) + if err != nil { + return "", err + } + return peers[0], nil } // SelectRandomPeer is used to return a random peer that supports a given protocol. @@ -38,65 +44,107 @@ func (pm *PeerManager) SelectPeerByContentTopics(proto protocol.ID, contentTopic // it supports the chosen protocol, otherwise it will chose a peer from the service slot. // If a peer cannot be found in the service slot, a peer will be selected from node peerstore // if pubSubTopic is specified, peer is selected from list that support the pubSubTopic -func (pm *PeerManager) SelectRandomPeer(criteria PeerSelectionCriteria) (peer.ID, error) { +func (pm *PeerManager) SelectRandom(criteria PeerSelectionCriteria) (peer.IDSlice, error) { // @TODO We need to be more strategic about which peers we dial. Right now we just set one on the service. // Ideally depending on the query and our set of peers we take a subset of ideal peers. // This will require us to check for various factors such as: // - which topics they track // - latency? - - peerID, err := pm.selectServicePeer(criteria.Proto, criteria.PubsubTopics, criteria.Ctx, criteria.SpecificPeers...) - if err == nil { - return peerID, nil + peerIDs, err := pm.selectServicePeer(criteria) + if err == nil && len(peerIDs) == criteria.MaxPeers { + return maps.Keys(peerIDs), nil } else if !errors.Is(err, ErrNoPeersAvailable) { pm.logger.Debug("could not retrieve random peer from slot", zap.String("protocol", string(criteria.Proto)), zap.Strings("pubsubTopics", criteria.PubsubTopics), zap.Error(err)) - return "", err + return nil, err + } else if len(peerIDs) == 0 { + peerIDs = make(peerSet) } - // if not found in serviceSlots or proto == WakuRelayIDv200 filteredPeers, err := pm.FilterPeersByProto(criteria.SpecificPeers, criteria.Proto) if err != nil { - return "", err + return nil, err } if len(criteria.PubsubTopics) > 0 { filteredPeers = pm.host.Peerstore().(wps.WakuPeerstore).PeersByPubSubTopics(criteria.PubsubTopics, filteredPeers...) } - return selectRandomPeer(filteredPeers, pm.logger) + randomPeers, err := selectRandomPeers(filteredPeers, criteria.MaxPeers-len(peerIDs)) + if err != nil && len(peerIDs) == 0 { + return nil, err + } + + for tmpPeer := range randomPeers { + peerIDs[tmpPeer] = struct{}{} + } + return maps.Keys(peerIDs), nil } -func (pm *PeerManager) selectServicePeer(proto protocol.ID, pubsubTopics []string, ctx context.Context, specificPeers ...peer.ID) (peer.ID, error) { - var peerID peer.ID +func getRandom(filter peerSet, count int) (peerSet, error) { + i := 0 + selectedPeers := make(peerSet) + for pID := range filter { + //Map's iterator in golang works using randomness and hence not random function is being used. + selectedPeers[pID] = struct{}{} + i++ + if i == count { + break + } + } + if len(selectedPeers) == 0 { + return nil, ErrNoPeersAvailable + } + return selectedPeers, nil +} + +// selects count random peers from list of peers +func selectRandomPeers(peers peer.IDSlice, count int) (peerSet, error) { + filteredPeerMap := peerSliceToMap(peers) + return getRandom(filteredPeerMap, count) +} + +func peerSliceToMap(peers peer.IDSlice) peerSet { + peerSet := make(peerSet, peers.Len()) + for _, peer := range peers { + peerSet[peer] = struct{}{} + } + return peerSet +} + +func (pm *PeerManager) selectServicePeer(criteria PeerSelectionCriteria) (peerSet, error) { + peers := make(peerSet) var err error for retryCnt := 0; retryCnt < 1; retryCnt++ { //Try to fetch from serviceSlot - if slot := pm.serviceSlots.getPeers(proto); slot != nil { - if len(pubsubTopics) == 0 || (len(pubsubTopics) == 1 && pubsubTopics[0] == "") { - return slot.getRandom() + if slot := pm.serviceSlots.getPeers(criteria.Proto); slot != nil { + if len(criteria.PubsubTopics) == 0 || (len(criteria.PubsubTopics) == 1 && criteria.PubsubTopics[0] == "") { + return slot.getRandom(criteria.MaxPeers) } else { //PubsubTopic based selection keys := make([]peer.ID, 0, len(slot.m)) for i := range slot.m { keys = append(keys, i) } - selectedPeers := pm.host.Peerstore().(wps.WakuPeerstore).PeersByPubSubTopics(pubsubTopics, keys...) - peerID, err = selectRandomPeer(selectedPeers, pm.logger) - if err == nil { - return peerID, nil + selectedPeers := pm.host.Peerstore().(wps.WakuPeerstore).PeersByPubSubTopics(criteria.PubsubTopics, keys...) + tmpPeers, err := selectRandomPeers(selectedPeers, criteria.MaxPeers) + for tmpPeer := range tmpPeers { + peers[tmpPeer] = struct{}{} + } + if err == nil && len(peers) == criteria.MaxPeers { + return peers, nil } else { - pm.logger.Debug("discovering peers by pubsubTopic", zap.Strings("pubsubTopics", pubsubTopics)) + pm.logger.Debug("discovering peers by pubsubTopic", zap.Strings("pubsubTopics", criteria.PubsubTopics)) //Trigger on-demand discovery for this topic and connect to peer immediately. //For now discover atleast 1 peer for the criteria - pm.discoverPeersByPubsubTopics(pubsubTopics, proto, ctx, 1) + pm.discoverPeersByPubsubTopics(criteria.PubsubTopics, criteria.Proto, criteria.Ctx, 1) //Try to fetch peers again. continue } } } } - if peerID == "" { + if len(peers) == 0 { pm.logger.Debug("could not retrieve random peer from slot", zap.Error(err)) } - return "", ErrNoPeersAvailable + return peers, ErrNoPeersAvailable } // PeerSelectionCriteria is the selection Criteria that is used by PeerManager to select peers. @@ -105,20 +153,28 @@ type PeerSelectionCriteria struct { Proto protocol.ID PubsubTopics []string SpecificPeers peer.IDSlice + MaxPeers int Ctx context.Context } -// SelectPeer selects a peer based on selectionType specified. +// SelectPeers selects a peer based on selectionType specified. // Context is required only in case of selectionType set to LowestRTT -func (pm *PeerManager) SelectPeer(criteria PeerSelectionCriteria) (peer.ID, error) { - +func (pm *PeerManager) SelectPeers(criteria PeerSelectionCriteria) (peer.IDSlice, error) { + if criteria.MaxPeers == 0 { + criteria.MaxPeers = 1 + } switch criteria.SelectionType { case Automatic: - return pm.SelectRandomPeer(criteria) + return pm.SelectRandom(criteria) case LowestRTT: - return pm.SelectPeerWithLowestRTT(criteria) + peerID, err := pm.SelectPeerWithLowestRTT(criteria) + if err != nil { + return nil, err + } + //TODO: Update this once peer Ping cache PR is merged into this code. + return []peer.ID{peerID}, nil default: - return "", errors.New("unknown peer selection type specified") + return nil, errors.New("unknown peer selection type specified") } } @@ -198,17 +254,6 @@ func (pm *PeerManager) SelectPeerWithLowestRTT(criteria PeerSelectionCriteria) ( } } -// selectRandomPeer selects randomly a peer from the list of peers passed. -func selectRandomPeer(peers peer.IDSlice, log *zap.Logger) (peer.ID, error) { - if len(peers) >= 1 { - peerID := peers[rand.Intn(len(peers))] - // TODO: proper heuristic here that compares peer scores and selects "best" one. For now a random peer for the given protocol is returned - return peerID, nil // nolint: gosec - } - - return "", ErrNoPeersAvailable -} - // FilterPeersByProto filters list of peers that support specified protocols. // If specificPeers is nil, all peers in the host's peerStore are considered for filtering. func (pm *PeerManager) FilterPeersByProto(specificPeers peer.IDSlice, proto ...protocol.ID) (peer.IDSlice, error) { diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/service_slot.go b/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/service_slot.go index df63b7ac2..a5673df3f 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/service_slot.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/service_slot.go @@ -19,14 +19,10 @@ func newPeerMap() *peerMap { } } -func (pm *peerMap) getRandom() (peer.ID, error) { +func (pm *peerMap) getRandom(count int) (peerSet, error) { pm.mu.RLock() defer pm.mu.RUnlock() - for pID := range pm.m { - return pID, nil - } - return "", ErrNoPeersAvailable - + return getRandom(pm.m, count) } func (pm *peerMap) remove(pID peer.ID) { diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/topic_event_handler.go b/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/topic_event_handler.go index 9e66ec0fc..60e1f759b 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/topic_event_handler.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/peermanager/topic_event_handler.go @@ -2,6 +2,7 @@ package peermanager import ( "context" + "time" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/event" @@ -30,7 +31,7 @@ func (pm *PeerManager) handleNewRelayTopicSubscription(pubsubTopic string, topic //Nothing to be done, as we are already subscribed to this topic. return } - pm.subRelayTopics[pubsubTopic] = &NodeTopicDetails{topicInst} + pm.subRelayTopics[pubsubTopic] = &NodeTopicDetails{topicInst, UnHealthy} //Check how many relay peers we are connected to that subscribe to this topic, if less than D find peers in peerstore and connect. //If no peers in peerStore, trigger discovery for this topic? relevantPeersForPubSubTopic := pm.host.Peerstore().(*wps.WakuPeerstoreImpl).PeersByPubSubTopic(pubsubTopic) @@ -44,7 +45,9 @@ func (pm *PeerManager) handleNewRelayTopicSubscription(pubsubTopic string, topic } } - if connectedPeers >= waku_proto.GossipSubOptimalFullMeshSize { //TODO: Use a config rather than hard-coding. + pm.checkAndUpdateTopicHealth(pm.subRelayTopics[pubsubTopic]) + + if connectedPeers >= waku_proto.GossipSubDMin { //TODO: Use a config rather than hard-coding. // Should we use optimal number or define some sort of a config for the node to choose from? // A desktop node may choose this to be 4-6, whereas a service node may choose this to be 8-12 based on resources it has // or bandwidth it can support. @@ -58,10 +61,10 @@ func (pm *PeerManager) handleNewRelayTopicSubscription(pubsubTopic string, topic numPeersToConnect := notConnectedPeers.Len() - connectedPeers if numPeersToConnect < 0 { numPeersToConnect = notConnectedPeers.Len() - } else if numPeersToConnect-connectedPeers > waku_proto.GossipSubOptimalFullMeshSize { - numPeersToConnect = waku_proto.GossipSubOptimalFullMeshSize - connectedPeers + } else if numPeersToConnect-connectedPeers > waku_proto.GossipSubDMin { + numPeersToConnect = waku_proto.GossipSubDMin - connectedPeers } - if numPeersToConnect+connectedPeers < waku_proto.GossipSubOptimalFullMeshSize { + if numPeersToConnect+connectedPeers < waku_proto.GossipSubDMin { triggerDiscovery = true } //For now all peers are being given same priority, @@ -118,17 +121,38 @@ func (pm *PeerManager) handlerPeerTopicEvent(peerEvt relay.EvtPeerTopic) { wps := pm.host.Peerstore().(*wps.WakuPeerstoreImpl) peerID := peerEvt.PeerID if peerEvt.State == relay.PEER_JOINED { + if pm.metadata != nil { + rs, err := pm.metadata.RelayShard() + if err != nil { + pm.logger.Error("could not obtain the cluster and shards of wakunode", zap.Error(err)) + } else if rs != nil && rs.ClusterID != 0 { + ctx, cancel := context.WithTimeout(pm.ctx, 7*time.Second) + defer cancel() + if err := pm.metadata.DisconnectPeerOnShardMismatch(ctx, peerEvt.PeerID); err != nil { + return + } + } + } + err := wps.AddPubSubTopic(peerID, peerEvt.PubsubTopic) if err != nil { pm.logger.Error("failed to add pubSubTopic for peer", logging.HostID("peerID", peerID), zap.String("topic", peerEvt.PubsubTopic), zap.Error(err)) } + + pm.topicMutex.RLock() + defer pm.topicMutex.RUnlock() + pm.checkAndUpdateTopicHealth(pm.subRelayTopics[peerEvt.PubsubTopic]) + } else if peerEvt.State == relay.PEER_LEFT { err := wps.RemovePubSubTopic(peerID, peerEvt.PubsubTopic) if err != nil { pm.logger.Error("failed to remove pubSubTopic for peer", logging.HostID("peerID", peerID), zap.Error(err)) } + pm.topicMutex.RLock() + defer pm.topicMutex.RUnlock() + pm.checkAndUpdateTopicHealth(pm.subRelayTopics[peerEvt.PubsubTopic]) } else { pm.logger.Error("unknown peer event received", zap.Int("eventState", int(peerEvt.State))) } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/peerstore/waku_peer_store.go b/vendor/github.com/waku-org/go-waku/waku/v2/peerstore/waku_peer_store.go index 5495a577f..402d265ad 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/peerstore/waku_peer_store.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/peerstore/waku_peer_store.go @@ -28,6 +28,7 @@ const peerOrigin = "origin" const peerENR = "enr" const peerDirection = "direction" const peerPubSubTopics = "pubSubTopics" +const peerScore = "score" // ConnectionFailures contains connection failure information towards all peers type ConnectionFailures struct { @@ -61,6 +62,9 @@ type WakuPeerstore interface { SetPubSubTopics(p peer.ID, topics []string) error PeersByPubSubTopics(pubSubTopics []string, specificPeers ...peer.ID) peer.IDSlice PeersByPubSubTopic(pubSubTopic string, specificPeers ...peer.ID) peer.IDSlice + + SetScore(peer.ID, float64) error + Score(peer.ID) (float64, error) } // NewWakuPeerstore creates a new WakuPeerStore object @@ -88,6 +92,21 @@ func (ps *WakuPeerstoreImpl) Origin(p peer.ID) (Origin, error) { return result.(Origin), nil } +// SetScore sets score for a specific peer. +func (ps *WakuPeerstoreImpl) SetScore(p peer.ID, score float64) error { + return ps.peerStore.Put(p, peerScore, score) +} + +// Score fetches the peerScore for a specific peer. +func (ps *WakuPeerstoreImpl) Score(p peer.ID) (float64, error) { + result, err := ps.peerStore.Get(p, peerScore) + if err != nil { + return -1, err + } + + return result.(float64), nil +} + // PeersByOrigin returns the list of peers for a specific origin func (ps *WakuPeerstoreImpl) PeersByOrigin(expectedOrigin Origin) peer.IDSlice { var result peer.IDSlice diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/enr/enr.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/enr/enr.go index 7f8d9e56a..b5b7bbd90 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/enr/enr.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/enr/enr.go @@ -113,7 +113,7 @@ func Multiaddress(node *enode.Node) (peer.ID, []multiaddr.Multiaddr, error) { return "", nil, fmt.Errorf("invalid multiaddress field length") } - hostInfoStr := fmt.Sprintf("/p2p/%s", peerID.Pretty()) + hostInfoStr := fmt.Sprintf("/p2p/%s", peerID.String()) _, pID := peer.SplitAddr(addr) if pID != "" && pID != peerID { // Addresses in the ENR that contain a p2p component are circuit relay addr diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/envelope.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/envelope.go index 6fd4edbc9..5ca5a77fa 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/envelope.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/envelope.go @@ -2,8 +2,8 @@ package protocol import ( "github.com/waku-org/go-waku/waku/v2/hash" + "github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb" wpb "github.com/waku-org/go-waku/waku/v2/protocol/pb" - "github.com/waku-org/go-waku/waku/v2/protocol/store/pb" ) // Envelope contains information about the pubsub topic of a WakuMessage @@ -11,7 +11,7 @@ import ( // protobuffer type Envelope struct { msg *wpb.WakuMessage - hash []byte + hash wpb.MessageHash index *pb.Index } @@ -44,7 +44,7 @@ func (e *Envelope) PubsubTopic() string { } // Hash returns a 32 byte hash calculated from the WakuMessage bytes -func (e *Envelope) Hash() []byte { +func (e *Envelope) Hash() wpb.MessageHash { return e.hash } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/client.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/client.go index b6fc3092f..cad12dc6f 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/client.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/client.go @@ -38,6 +38,7 @@ const FilterPushID_v20beta1 = libp2pProtocol.ID("/vac/waku/filter-push/2.0.0-bet var ( ErrNoPeersAvailable = errors.New("no suitable remote peers") ErrSubscriptionNotFound = errors.New("subscription not found") + ErrNoPeersSpecified = errors.New("no peers specified to unsubscribe") ) type WakuFilterLightNode struct { @@ -211,10 +212,10 @@ func (wf *WakuFilterLightNode) notify(remotePeerID peer.ID, pubsubTopic string, wf.subscriptions.Notify(remotePeerID, envelope) } -func (wf *WakuFilterLightNode) request(ctx context.Context, params *FilterSubscribeParameters, - reqType pb.FilterSubscribeRequest_FilterSubscribeType, contentFilter protocol.ContentFilter) error { +func (wf *WakuFilterLightNode) request(ctx context.Context, requestID []byte, + reqType pb.FilterSubscribeRequest_FilterSubscribeType, contentFilter protocol.ContentFilter, peer peer.ID) error { request := &pb.FilterSubscribeRequest{ - RequestId: hex.EncodeToString(params.requestID), + RequestId: hex.EncodeToString(requestID), FilterSubscribeType: reqType, PubsubTopic: &contentFilter.PubsubTopic, ContentTopics: contentFilter.ContentTopicsList(), @@ -225,9 +226,9 @@ func (wf *WakuFilterLightNode) request(ctx context.Context, params *FilterSubscr return err } - logger := wf.log.With(logging.HostID("peerID", params.selectedPeer)) + logger := wf.log.With(logging.HostID("peerID", peer)) - stream, err := wf.h.NewStream(ctx, params.selectedPeer, FilterSubscribeID_v20beta1) + stream, err := wf.h.NewStream(ctx, peer, FilterSubscribeID_v20beta1) if err != nil { wf.metrics.RecordError(dialFailure) return err @@ -276,11 +277,7 @@ func (wf *WakuFilterLightNode) request(ctx context.Context, params *FilterSubscr if filterSubscribeResponse.StatusCode != http.StatusOK { wf.metrics.RecordError(errorResponse) - errMessage := "" - if filterSubscribeResponse.StatusDesc != nil { - errMessage = *filterSubscribeResponse.StatusDesc - } - err := NewFilterError(int(filterSubscribeResponse.StatusCode), errMessage) + err := NewFilterError(int(filterSubscribeResponse.StatusCode), filterSubscribeResponse.GetStatusDesc()) return &err } @@ -314,15 +311,19 @@ func (wf *WakuFilterLightNode) handleFilterSubscribeOptions(ctx context.Context, return nil, nil, err } wf.pm.Connect(pData) - params.selectedPeer = pData.AddrInfo.ID + params.selectedPeers = append(params.selectedPeers, pData.AddrInfo.ID) } - if params.pm != nil && params.selectedPeer == "" { - params.selectedPeer, err = wf.pm.SelectPeer( + if params.pm != nil { + + peerCount := params.maxPeers - len(params.selectedPeers) + + params.selectedPeers, err = wf.pm.SelectPeers( peermanager.PeerSelectionCriteria{ SelectionType: params.peerSelectionType, Proto: FilterSubscribeID_v20beta1, PubsubTopics: maps.Keys(pubSubTopicMap), SpecificPeers: params.preferredPeers, + MaxPeers: peerCount, Ctx: ctx, }, ) @@ -352,21 +353,22 @@ func (wf *WakuFilterLightNode) Subscribe(ctx context.Context, contentFilter prot failedContentTopics := []string{} subscriptions := make([]*subscription.SubscriptionDetails, 0) for pubSubTopic, cTopics := range pubSubTopicMap { - var selectedPeer peer.ID - if params.pm != nil && params.selectedPeer == "" { - selectedPeer, err = wf.pm.SelectPeer( + var selectedPeers peer.IDSlice + if params.pm != nil && len(params.selectedPeers) < params.maxPeers { + selectedPeers, err = wf.pm.SelectPeers( peermanager.PeerSelectionCriteria{ SelectionType: params.peerSelectionType, Proto: FilterSubscribeID_v20beta1, PubsubTopics: []string{pubSubTopic}, SpecificPeers: params.preferredPeers, + MaxPeers: params.maxPeers - params.selectedPeers.Len(), Ctx: ctx, }, ) } else { - selectedPeer = params.selectedPeer + selectedPeers = params.selectedPeers } - if selectedPeer == "" { + if len(selectedPeers) == 0 { wf.metrics.RecordError(peerNotFoundFailure) wf.log.Error("selecting peer", zap.String("pubSubTopic", pubSubTopic), zap.Strings("contentTopics", cTopics), zap.Error(err)) @@ -379,20 +381,22 @@ func (wf *WakuFilterLightNode) Subscribe(ctx context.Context, contentFilter prot cFilter.ContentTopics = protocol.NewContentTopicSet(cTopics...) paramsCopy := params.Copy() - paramsCopy.selectedPeer = selectedPeer - err := wf.request( - ctx, - paramsCopy, - pb.FilterSubscribeRequest_SUBSCRIBE, - cFilter, - ) - if err != nil { - wf.log.Error("Failed to subscribe", zap.String("pubSubTopic", pubSubTopic), zap.Strings("contentTopics", cTopics), - zap.Error(err)) - failedContentTopics = append(failedContentTopics, cTopics...) - continue + paramsCopy.selectedPeers = selectedPeers + for _, peer := range selectedPeers { + err := wf.request( + ctx, + params.requestID, + pb.FilterSubscribeRequest_SUBSCRIBE, + cFilter, + peer) + if err != nil { + wf.log.Error("Failed to subscribe", zap.String("pubSubTopic", pubSubTopic), zap.Strings("contentTopics", cTopics), + zap.Error(err)) + failedContentTopics = append(failedContentTopics, cTopics...) + continue + } + subscriptions = append(subscriptions, wf.subscriptions.NewSubscription(peer, cFilter)) } - subscriptions = append(subscriptions, wf.subscriptions.NewSubscription(selectedPeer, cFilter)) } if len(failedContentTopics) > 0 { @@ -427,7 +431,6 @@ func (wf *WakuFilterLightNode) getUnsubscribeParameters(opts ...FilterSubscribeO return nil, err } } - return params, nil } @@ -448,9 +451,10 @@ func (wf *WakuFilterLightNode) Ping(ctx context.Context, peerID peer.ID, opts .. return wf.request( ctx, - &FilterSubscribeParameters{selectedPeer: peerID, requestID: params.requestID}, + params.requestID, pb.FilterSubscribeRequest_SUBSCRIBER_PING, - protocol.ContentFilter{}) + protocol.ContentFilter{}, + peerID) } func (wf *WakuFilterLightNode) IsSubscriptionAlive(ctx context.Context, subscription *subscription.SubscriptionDetails) error { @@ -463,7 +467,7 @@ func (wf *WakuFilterLightNode) IsSubscriptionAlive(ctx context.Context, subscrip return wf.Ping(ctx, subscription.PeerID) } -// Unsubscribe is used to stop receiving messages from a peer that match a content filter +// Unsubscribe is used to stop receiving messages from specified peers for the content filter func (wf *WakuFilterLightNode) Unsubscribe(ctx context.Context, contentFilter protocol.ContentFilter, opts ...FilterSubscribeOption) (*WakuFilterPushResult, error) { wf.RLock() defer wf.RUnlock() @@ -475,7 +479,7 @@ func (wf *WakuFilterLightNode) Unsubscribe(ctx context.Context, contentFilter pr return nil, errors.New("at least one content topic is required") } - if slices.Contains[string](contentFilter.ContentTopicsList(), "") { + if slices.Contains(contentFilter.ContentTopicsList(), "") { return nil, errors.New("one or more content topics specified is empty") } @@ -487,7 +491,6 @@ func (wf *WakuFilterLightNode) Unsubscribe(ctx context.Context, contentFilter pr if err != nil { return nil, err } - pubSubTopicMap, err := protocol.ContentFilterToPubSubTopicMap(contentFilter) if err != nil { return nil, err @@ -495,16 +498,33 @@ func (wf *WakuFilterLightNode) Unsubscribe(ctx context.Context, contentFilter pr result := &WakuFilterPushResult{} for pTopic, cTopics := range pubSubTopicMap { cFilter := protocol.NewContentFilter(pTopic, cTopics...) - - peers := make(map[peer.ID]struct{}) - subs := wf.subscriptions.GetSubscription(params.selectedPeer, cFilter) + var subs []*subscription.SubscriptionDetails + if params.selectedPeers.Len() == 0 { + subs = wf.subscriptions.GetAllSubscriptions() + if len(subs) == 0 { + result.Add(WakuFilterPushError{ + Err: ErrSubscriptionNotFound, + PeerID: "", + }) + continue + } + } + for _, peer := range params.selectedPeers { + subsForPeer := wf.subscriptions.GetSubscriptionsForPeer(peer, cFilter) + if len(subsForPeer) == 0 { + result.Add(WakuFilterPushError{ + Err: ErrSubscriptionNotFound, + PeerID: peer, + }) + continue + } + subs = append(subs, subsForPeer...) + } if len(subs) == 0 { - result.Add(WakuFilterPushError{ - Err: ErrSubscriptionNotFound, - PeerID: params.selectedPeer, - }) + //No subscriptions found for this filter continue } + peers := make(map[peer.ID]struct{}) for _, sub := range subs { sub.Remove(cTopics...) peers[sub.PeerID] = struct{}{} @@ -520,7 +540,7 @@ func (wf *WakuFilterLightNode) Unsubscribe(ctx context.Context, contentFilter pr params.wg.Done() } }() - err := wf.unsubscribeFromServer(ctx, &FilterSubscribeParameters{selectedPeer: peerID, requestID: params.requestID}, cFilter) + err := wf.unsubscribeFromServer(ctx, params.requestID, peerID, cFilter) if params.wg != nil { result.Add(WakuFilterPushError{ @@ -539,7 +559,7 @@ func (wf *WakuFilterLightNode) Unsubscribe(ctx context.Context, contentFilter pr } func (wf *WakuFilterLightNode) Subscriptions() []*subscription.SubscriptionDetails { - subs := wf.subscriptions.GetSubscription("", protocol.ContentFilter{}) + subs := wf.subscriptions.GetAllSubscriptions() return subs } @@ -571,8 +591,7 @@ func (wf *WakuFilterLightNode) UnsubscribeWithSubscription(ctx context.Context, if !wf.subscriptions.Has(sub.PeerID, sub.ContentFilter) { // Last sub for this [peer, contentFilter] pair - params.selectedPeer = sub.PeerID - err = wf.unsubscribeFromServer(ctx, params, sub.ContentFilter) + err = wf.unsubscribeFromServer(ctx, params.requestID, sub.PeerID, sub.ContentFilter) result.Add(WakuFilterPushError{ Err: err, PeerID: sub.PeerID, @@ -582,14 +601,14 @@ func (wf *WakuFilterLightNode) UnsubscribeWithSubscription(ctx context.Context, } -func (wf *WakuFilterLightNode) unsubscribeFromServer(ctx context.Context, params *FilterSubscribeParameters, cFilter protocol.ContentFilter) error { - err := wf.request(ctx, params, pb.FilterSubscribeRequest_UNSUBSCRIBE, cFilter) +func (wf *WakuFilterLightNode) unsubscribeFromServer(ctx context.Context, requestID []byte, peer peer.ID, cFilter protocol.ContentFilter) error { + err := wf.request(ctx, requestID, pb.FilterSubscribeRequest_UNSUBSCRIBE, cFilter, peer) if err != nil { ferr, ok := err.(*FilterError) if ok && ferr.Code == http.StatusNotFound { - wf.log.Warn("peer does not have a subscription", logging.HostID("peerID", params.selectedPeer), zap.Error(err)) + wf.log.Warn("peer does not have a subscription", logging.HostID("peerID", peer), zap.Error(err)) } else { - wf.log.Error("could not unsubscribe from peer", logging.HostID("peerID", params.selectedPeer), zap.Error(err)) + wf.log.Error("could not unsubscribe from peer", logging.HostID("peerID", peer), zap.Error(err)) } } @@ -606,12 +625,25 @@ func (wf *WakuFilterLightNode) unsubscribeAll(ctx context.Context, opts ...Filte result := &WakuFilterPushResult{} peers := make(map[peer.ID]struct{}) - subs := wf.subscriptions.GetSubscription(params.selectedPeer, protocol.ContentFilter{}) - if len(subs) == 0 && params.selectedPeer != "" { - result.Add(WakuFilterPushError{ - Err: err, - PeerID: params.selectedPeer, - }) + var subs []*subscription.SubscriptionDetails + if params.selectedPeers.Len() == 0 { + subs = wf.subscriptions.GetAllSubscriptions() + if len(subs) == 0 { + return result, nil + } + } + for _, peer := range params.selectedPeers { + pSubs := wf.subscriptions.GetSubscriptionsForPeer(peer, protocol.ContentFilter{}) + if len(pSubs) == 0 { + result.Add(WakuFilterPushError{ + Err: ErrSubscriptionNotFound, + PeerID: peer, + }) + continue + } + subs = append(subs, pSubs...) + } + if len(subs) == 0 { return result, ErrSubscriptionNotFound } for _, sub := range subs { @@ -630,13 +662,11 @@ func (wf *WakuFilterLightNode) unsubscribeAll(ctx context.Context, opts ...Filte _ = recover() }() - paramsCopy := params.Copy() - paramsCopy.selectedPeer = peerID err := wf.request( ctx, - params, + params.requestID, pb.FilterSubscribeRequest_UNSUBSCRIBE_ALL, - protocol.ContentFilter{}) + protocol.ContentFilter{}, peerID) if err != nil { wf.log.Error("could not unsubscribe from peer", logging.HostID("peerID", peerID), zap.Error(err)) } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/options.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/options.go index 93c5326b9..afb9f2f1e 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/options.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/options.go @@ -15,8 +15,8 @@ import ( func (old *FilterSubscribeParameters) Copy() *FilterSubscribeParameters { return &FilterSubscribeParameters{ - selectedPeer: old.selectedPeer, - requestID: old.requestID, + selectedPeers: old.selectedPeers, + requestID: old.requestID, } } @@ -35,10 +35,11 @@ func WithPingRequestId(requestId []byte) FilterPingOption { type ( FilterSubscribeParameters struct { - selectedPeer peer.ID + selectedPeers peer.IDSlice peerAddr multiaddr.Multiaddr peerSelectionType peermanager.PeerSelection preferredPeers peer.IDSlice + maxPeers int requestID []byte log *zap.Logger @@ -72,7 +73,7 @@ func WithTimeout(timeout time.Duration) Option { // Note that this option is mutually exclusive to WithPeerAddr, only one of them can be used. func WithPeer(p peer.ID) FilterSubscribeOption { return func(params *FilterSubscribeParameters) error { - params.selectedPeer = p + params.selectedPeers = append(params.selectedPeers, p) if params.peerAddr != nil { return errors.New("peerAddr and peerId options are mutually exclusive") } @@ -86,13 +87,20 @@ func WithPeer(p peer.ID) FilterSubscribeOption { func WithPeerAddr(pAddr multiaddr.Multiaddr) FilterSubscribeOption { return func(params *FilterSubscribeParameters) error { params.peerAddr = pAddr - if params.selectedPeer != "" { + if len(params.selectedPeers) != 0 { return errors.New("peerAddr and peerId options are mutually exclusive") } return nil } } +func WithMaxPeersPerContentFilter(numPeers int) FilterSubscribeOption { + return func(params *FilterSubscribeParameters) error { + params.maxPeers = numPeers + return nil + } +} + // WithAutomaticPeerSelection is an option used to randomly select a peer from the peer store. // If a list of specific peers is passed, the peer will be chosen from that list assuming it // supports the chosen protocol, otherwise it will chose a peer from the node peerstore @@ -186,7 +194,7 @@ func WithPeerManager(pm *peermanager.PeerManager) Option { func DefaultOptions() []Option { return []Option{ - WithTimeout(24 * time.Hour), + WithTimeout(5 * time.Minute), WithMaxSubscribers(DefaultMaxSubscriptions), } } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/pb/validation.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/pb/validation.go index 2e0d450dd..ba761f915 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/pb/validation.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/pb/validation.go @@ -7,7 +7,7 @@ import ( "golang.org/x/exp/slices" ) -const MaxContentTopicsPerRequest = 30 +const MaxContentTopicsPerRequest = 100 var ( errMissingRequestID = errors.New("missing RequestId field") @@ -32,7 +32,7 @@ func (x *FilterSubscribeRequest) Validate() error { return errNoContentTopics } - if slices.Contains[string](x.ContentTopics, "") { + if slices.Contains(x.ContentTopics, "") { return errEmptyContentTopics } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/server.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/server.go index cea5efab6..aa18839b4 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/server.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/server.go @@ -82,6 +82,8 @@ func (wf *WakuFilterFullNode) start(sub *relay.Subscription) error { wf.WaitGroup().Add(1) go wf.filterListener(wf.Context()) + wf.subscriptions.Start(wf.Context()) + wf.log.Info("filter-subscriber protocol started") return nil } @@ -155,9 +157,11 @@ func (wf *WakuFilterFullNode) reply(ctx context.Context, stream network.Stream, } func (wf *WakuFilterFullNode) ping(ctx context.Context, stream network.Stream, request *pb.FilterSubscribeRequest) { - exists := wf.subscriptions.Has(stream.Conn().RemotePeer()) + peerID := stream.Conn().RemotePeer() + exists := wf.subscriptions.Has(peerID) if exists { + wf.subscriptions.Refresh(peerID) wf.reply(ctx, stream, request, http.StatusOK) } else { wf.reply(ctx, stream, request, http.StatusNotFound, peerHasNoSubscription) @@ -218,7 +222,7 @@ func (wf *WakuFilterFullNode) filterListener(ctx context.Context) { handle := func(envelope *protocol.Envelope) error { msg := envelope.Message() pubsubTopic := envelope.PubsubTopic() - logger := utils.MessagesLogger("filter").With(logging.HexBytes("hash", envelope.Hash()), + logger := utils.MessagesLogger("filter").With(logging.Hash(envelope.Hash()), zap.String("pubsubTopic", envelope.PubsubTopic()), zap.String("contentTopic", envelope.Message().ContentTopic), ) @@ -265,7 +269,6 @@ func (wf *WakuFilterFullNode) pushMessage(ctx context.Context, logger *zap.Logge stream, err := wf.h.NewStream(ctx, peerID, FilterPushID_v20beta1) if err != nil { - wf.subscriptions.FlagAsFailure(peerID) if errors.Is(context.DeadlineExceeded, err) { wf.metrics.RecordError(pushTimeoutFailure) } else { @@ -284,7 +287,6 @@ func (wf *WakuFilterFullNode) pushMessage(ctx context.Context, logger *zap.Logge wf.metrics.RecordError(writeResponseFailure) } logger.Error("pushing messages to peer", zap.Error(err)) - wf.subscriptions.FlagAsFailure(peerID) if err := stream.Reset(); err != nil { wf.log.Error("resetting connection", zap.Error(err)) } @@ -293,8 +295,6 @@ func (wf *WakuFilterFullNode) pushMessage(ctx context.Context, logger *zap.Logge stream.Close() - wf.subscriptions.FlagAsSuccess(peerID) - logger.Debug("message pushed succesfully") return nil diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/subscribers_map.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/subscribers_map.go index 80a364e11..faa5700ca 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/subscribers_map.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/filter/subscribers_map.go @@ -1,6 +1,7 @@ package filter import ( + "context" "encoding/hex" "errors" "sync" @@ -17,14 +18,15 @@ type PubsubTopics map[string]protocol.ContentTopicSet // pubsubTopic => contentT var errNotFound = errors.New("not found") +const cleanupInterval = time.Minute + type SubscribersMap struct { sync.RWMutex items map[peer.ID]PubsubTopics interestMap map[string]PeerSet // key: sha256(pubsubTopic-contentTopic) => peers - timeout time.Duration - failedPeers map[peer.ID]time.Time + lastSeen map[peer.ID]time.Time } func NewSubscribersMap(timeout time.Duration) *SubscribersMap { @@ -32,23 +34,29 @@ func NewSubscribersMap(timeout time.Duration) *SubscribersMap { items: make(map[peer.ID]PubsubTopics), interestMap: make(map[string]PeerSet), timeout: timeout, - failedPeers: make(map[peer.ID]time.Time), + lastSeen: make(map[peer.ID]time.Time), } } +func (sub *SubscribersMap) Start(ctx context.Context) { + go sub.cleanUp(ctx, cleanupInterval) +} + func (sub *SubscribersMap) Clear() { sub.Lock() defer sub.Unlock() sub.items = make(map[peer.ID]PubsubTopics) sub.interestMap = make(map[string]PeerSet) - sub.failedPeers = make(map[peer.ID]time.Time) + sub.lastSeen = make(map[peer.ID]time.Time) } func (sub *SubscribersMap) Set(peerID peer.ID, pubsubTopic string, contentTopics []string) { sub.Lock() defer sub.Unlock() + sub.lastSeen[peerID] = time.Now() + pubsubTopicMap, ok := sub.items[peerID] if !ok { pubsubTopicMap = make(PubsubTopics) @@ -105,6 +113,10 @@ func (sub *SubscribersMap) Delete(peerID peer.ID, pubsubTopic string, contentTop return errNotFound } + // Updating first the lastSeen since this is a valid activity + // (it will still get deleted if all content topics are removed) + sub.lastSeen[peerID] = time.Now() + // Removing content topics individually for _, c := range contentTopics { c := c @@ -123,6 +135,7 @@ func (sub *SubscribersMap) Delete(peerID peer.ID, pubsubTopic string, contentTop if len(sub.items[peerID]) == 0 { delete(sub.items, peerID) + delete(sub.lastSeen, peerID) } return nil @@ -142,6 +155,7 @@ func (sub *SubscribersMap) deleteAll(peerID peer.ID) error { } delete(sub.items, peerID) + delete(sub.lastSeen, peerID) return nil } @@ -158,6 +172,7 @@ func (sub *SubscribersMap) RemoveAll() { defer sub.Unlock() sub.items = make(map[peer.ID]PubsubTopics) + sub.lastSeen = make(map[peer.ID]time.Time) } func (sub *SubscribersMap) Count() int { @@ -213,34 +228,31 @@ func getKey(pubsubTopic string, contentTopic string) string { } -func (sub *SubscribersMap) IsFailedPeer(peerID peer.ID) bool { - sub.RLock() - defer sub.RUnlock() - _, ok := sub.failedPeers[peerID] - return ok -} - -func (sub *SubscribersMap) FlagAsSuccess(peerID peer.ID) { +func (sub *SubscribersMap) Refresh(peerID peer.ID) { sub.Lock() defer sub.Unlock() - _, ok := sub.failedPeers[peerID] - if ok { - delete(sub.failedPeers, peerID) - } + sub.lastSeen[peerID] = time.Now() } -func (sub *SubscribersMap) FlagAsFailure(peerID peer.ID) { - sub.Lock() - defer sub.Unlock() +func (sub *SubscribersMap) cleanUp(ctx context.Context, cleanupInterval time.Duration) { + t := time.NewTicker(cleanupInterval) + defer t.Stop() - lastFailure, ok := sub.failedPeers[peerID] - if ok { - elapsedTime := time.Since(lastFailure) - if elapsedTime < sub.timeout { - _ = sub.deleteAll(peerID) + for { + select { + case <-ctx.Done(): + return + case <-t.C: + sub.Lock() + for peerID, lastSeen := range sub.lastSeen { + elapsedTime := time.Since(lastSeen) + if elapsedTime < sub.timeout { + _ = sub.deleteAll(peerID) + } + + } + sub.Unlock() } - } else { - sub.failedPeers[peerID] = time.Now() } } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/filter_map.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/filter_map.go deleted file mode 100644 index f71e35a6d..000000000 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/filter_map.go +++ /dev/null @@ -1,115 +0,0 @@ -package legacy_filter - -import ( - "sync" - - "github.com/waku-org/go-waku/waku/v2/protocol" - "github.com/waku-org/go-waku/waku/v2/protocol/pb" - "github.com/waku-org/go-waku/waku/v2/protocol/relay" - "github.com/waku-org/go-waku/waku/v2/timesource" -) - -type FilterMap struct { - sync.RWMutex - timesource timesource.Timesource - items map[string]Filter - broadcaster relay.Broadcaster -} - -type FilterMapItem struct { - Key string - Value Filter -} - -func NewFilterMap(broadcaster relay.Broadcaster, timesource timesource.Timesource) *FilterMap { - return &FilterMap{ - timesource: timesource, - items: make(map[string]Filter), - broadcaster: broadcaster, - } -} - -func (fm *FilterMap) Set(key string, value Filter) { - fm.Lock() - defer fm.Unlock() - - fm.items[key] = value -} - -func (fm *FilterMap) Get(key string) (Filter, bool) { - fm.Lock() - defer fm.Unlock() - - value, ok := fm.items[key] - - return value, ok -} - -func (fm *FilterMap) Delete(key string) { - fm.Lock() - defer fm.Unlock() - - _, ok := fm.items[key] - if !ok { - return - } - - close(fm.items[key].Chan) - delete(fm.items, key) -} - -func (fm *FilterMap) RemoveAll() { - fm.Lock() - defer fm.Unlock() - - for k, v := range fm.items { - close(v.Chan) - delete(fm.items, k) - } -} - -func (fm *FilterMap) Items() <-chan FilterMapItem { - c := make(chan FilterMapItem) - - f := func() { - fm.RLock() - defer fm.RUnlock() - - for k, v := range fm.items { - c <- FilterMapItem{k, v} - } - close(c) - } - go f() - - return c -} - -// Notify is used to push a received message from a filter subscription to -// any content filter registered on this node and to the broadcast subscribers -func (fm *FilterMap) Notify(msg *pb.WakuMessage, requestID string) { - fm.RLock() - defer fm.RUnlock() - - filter, ok := fm.items[requestID] - if !ok { - // We do this because the key for the filter is set to the requestID received from the filter protocol. - // This means we do not need to check the content filter explicitly as all MessagePushs already contain - // the requestID of the coresponding filter. - return - } - - envelope := protocol.NewEnvelope(msg, fm.timesource.Now().UnixNano(), filter.Topic) - - // Broadcasting message so it's stored - fm.broadcaster.Submit(envelope) - - // TODO: In case of no topics we should either trigger here for all messages, - // or we should not allow such filter to exist in the first place. - for _, contentTopic := range filter.ContentFilters { - if msg.ContentTopic == contentTopic { - filter.Chan <- envelope - break - } - } -} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/filter_subscribers.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/filter_subscribers.go deleted file mode 100644 index d3bd6862e..000000000 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/filter_subscribers.go +++ /dev/null @@ -1,167 +0,0 @@ -package legacy_filter - -import ( - "sync" - "time" - - "github.com/libp2p/go-libp2p/core/peer" - "github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/pb" -) - -type Subscriber struct { - peer peer.ID - requestID string - filter *pb.FilterRequest // @TODO MAKE THIS A SEQUENCE AGAIN? -} - -func (sub Subscriber) HasContentTopic(topic string) bool { - if len(sub.filter.ContentFilters) == 0 { - return true // When the subscriber has no specific ContentTopic filter - } - - for _, filter := range sub.filter.ContentFilters { - if filter.ContentTopic == topic { - return true - } - } - return false -} - -type Subscribers struct { - sync.RWMutex - subscribers []Subscriber - timeout time.Duration - failedPeers map[peer.ID]time.Time -} - -func NewSubscribers(timeout time.Duration) *Subscribers { - return &Subscribers{ - timeout: timeout, - failedPeers: make(map[peer.ID]time.Time), - } -} - -func (sub *Subscribers) Clear() { - sub.Lock() - defer sub.Unlock() - - sub.subscribers = nil - sub.failedPeers = make(map[peer.ID]time.Time) -} - -func (sub *Subscribers) Append(s Subscriber) int { - sub.Lock() - defer sub.Unlock() - - sub.subscribers = append(sub.subscribers, s) - return len(sub.subscribers) -} - -func (sub *Subscribers) Items(contentTopic *string) <-chan Subscriber { - c := make(chan Subscriber) - - f := func() { - sub.RLock() - defer sub.RUnlock() - for _, s := range sub.subscribers { - if contentTopic == nil || s.HasContentTopic(*contentTopic) { - c <- s - } - } - close(c) - } - go f() - - return c -} - -func (sub *Subscribers) Length() int { - sub.RLock() - defer sub.RUnlock() - - return len(sub.subscribers) -} - -func (sub *Subscribers) IsFailedPeer(peerID peer.ID) bool { - sub.RLock() - defer sub.RUnlock() - _, ok := sub.failedPeers[peerID] - return ok -} - -func (sub *Subscribers) FlagAsSuccess(peerID peer.ID) { - sub.Lock() - defer sub.Unlock() - - _, ok := sub.failedPeers[peerID] - if ok { - delete(sub.failedPeers, peerID) - } -} - -func (sub *Subscribers) FlagAsFailure(peerID peer.ID) { - sub.Lock() - defer sub.Unlock() - - lastFailure, ok := sub.failedPeers[peerID] - if ok { - elapsedTime := time.Since(lastFailure) - if elapsedTime > sub.timeout { - var tmpSubs []Subscriber - for _, s := range sub.subscribers { - if s.peer != peerID { - tmpSubs = append(tmpSubs, s) - } - } - sub.subscribers = tmpSubs - - delete(sub.failedPeers, peerID) - } - } else { - sub.failedPeers[peerID] = time.Now() - } -} - -// RemoveContentFilters removes a set of content filters registered for an specific peer -func (sub *Subscribers) RemoveContentFilters(peerID peer.ID, requestID string, contentFilters []*pb.FilterRequest_ContentFilter) { - sub.Lock() - defer sub.Unlock() - - var peerIdsToRemove []peer.ID - - for subIndex, subscriber := range sub.subscribers { - if subscriber.peer != peerID || subscriber.requestID != requestID { - continue - } - - // make sure we delete the content filter - // if no more topics are left - for _, contentFilter := range contentFilters { - subCfs := subscriber.filter.ContentFilters - for i, cf := range subCfs { - if cf.ContentTopic == contentFilter.ContentTopic { - l := len(subCfs) - 1 - subCfs[i] = subCfs[l] - subscriber.filter.ContentFilters = subCfs[:l] - } - } - sub.subscribers[subIndex] = subscriber - } - - if len(subscriber.filter.ContentFilters) == 0 { - peerIdsToRemove = append(peerIdsToRemove, subscriber.peer) - } - } - - // make sure we delete the subscriber - // if no more content filters left - for _, peerID := range peerIdsToRemove { - for i, s := range sub.subscribers { - if s.peer == peerID && s.requestID == requestID { - l := len(sub.subscribers) - 1 - sub.subscribers[i] = sub.subscribers[l] - sub.subscribers = sub.subscribers[:l] - } - } - } -} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/metrics.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/metrics.go deleted file mode 100644 index abb598228..000000000 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/metrics.go +++ /dev/null @@ -1,75 +0,0 @@ -package legacy_filter - -import ( - "github.com/libp2p/go-libp2p/p2p/metricshelper" - "github.com/prometheus/client_golang/prometheus" -) - -var filterMessages = prometheus.NewCounter( - prometheus.CounterOpts{ - Name: "legacy_filter_messages", - Help: "The number of messages received via legacy filter protocol", - }) - -var filterErrors = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "legacy_filter_errors", - Help: "The distribution of the legacy filter protocol errors", - }, - []string{"error_type"}, -) - -var filterSubscribers = prometheus.NewGauge( - prometheus.GaugeOpts{ - Name: "legacy_filter_subscriptions", - Help: "The number of legacy filter subscribers", - }) - -var collectors = []prometheus.Collector{ - filterMessages, - filterErrors, - filterSubscribers, -} - -// Metrics exposes the functions required to update prometheus metrics for legacy filter protocol -type Metrics interface { - RecordMessages(num int) - RecordSubscribers(num int) - RecordError(err metricsErrCategory) -} - -type metricsImpl struct { - reg prometheus.Registerer -} - -func newMetrics(reg prometheus.Registerer) Metrics { - metricshelper.RegisterCollectors(reg, collectors...) - return &metricsImpl{ - reg: reg, - } -} - -// RecordMessage is used to increase the counter for the number of messages received via waku filter -func (m *metricsImpl) RecordMessages(num int) { - filterMessages.Add(float64(num)) -} - -type metricsErrCategory string - -var ( - decodeRPCFailure metricsErrCategory = "decode_rpc_failure" - dialFailure metricsErrCategory = "dial_failure" - pushWriteError metricsErrCategory = "push_write_error" - peerNotFoundFailure metricsErrCategory = "peer_not_found_failure" - writeRequestFailure metricsErrCategory = "write_request_failure" -) - -// RecordError increases the counter for different error types -func (m *metricsImpl) RecordError(err metricsErrCategory) { - filterErrors.WithLabelValues(string(err)).Inc() -} - -// RecordSubscribers track the current number of filter subscribers -func (m *metricsImpl) RecordSubscribers(num int) { - filterSubscribers.Set(float64(num)) -} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/pb/generate.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/pb/generate.go deleted file mode 100644 index 45524a8c4..000000000 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/pb/generate.go +++ /dev/null @@ -1,5 +0,0 @@ -package pb - -//go:generate mv ./../../waku-proto/waku/filter/v2beta1/filter.proto ./../../waku-proto/waku/filter/v2beta1/legacy_filter.proto -//go:generate protoc -I./../../waku-proto/waku/filter/v2beta1/. -I./../../waku-proto/ --go_opt=paths=source_relative --go_opt=Mlegacy_filter.proto=github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/pb --go_opt=Mwaku/message/v1/message.proto=github.com/waku-org/go-waku/waku/v2/protocol/pb --go_out=. ./../../waku-proto/waku/filter/v2beta1/legacy_filter.proto -//go:generate mv ./../../waku-proto/waku/filter/v2beta1/legacy_filter.proto ./../../waku-proto/waku/filter/v2beta1/filter.proto diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/pb/legacy_filter.pb.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/pb/legacy_filter.pb.go deleted file mode 100644 index 9bbb181af..000000000 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/pb/legacy_filter.pb.go +++ /dev/null @@ -1,394 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.31.0 -// protoc v4.24.4 -// source: legacy_filter.proto - -// 12/WAKU2-FILTER rfc: https://rfc.vac.dev/spec/12/ -// Protocol identifier: /vac/waku/filter/2.0.0-beta1 - -package pb - -import ( - pb "github.com/waku-org/go-waku/waku/v2/protocol/pb" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type FilterRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Subscribe bool `protobuf:"varint,1,opt,name=subscribe,proto3" json:"subscribe,omitempty"` - Topic string `protobuf:"bytes,2,opt,name=topic,proto3" json:"topic,omitempty"` - ContentFilters []*FilterRequest_ContentFilter `protobuf:"bytes,3,rep,name=content_filters,json=contentFilters,proto3" json:"content_filters,omitempty"` -} - -func (x *FilterRequest) Reset() { - *x = FilterRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_legacy_filter_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FilterRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FilterRequest) ProtoMessage() {} - -func (x *FilterRequest) ProtoReflect() protoreflect.Message { - mi := &file_legacy_filter_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FilterRequest.ProtoReflect.Descriptor instead. -func (*FilterRequest) Descriptor() ([]byte, []int) { - return file_legacy_filter_proto_rawDescGZIP(), []int{0} -} - -func (x *FilterRequest) GetSubscribe() bool { - if x != nil { - return x.Subscribe - } - return false -} - -func (x *FilterRequest) GetTopic() string { - if x != nil { - return x.Topic - } - return "" -} - -func (x *FilterRequest) GetContentFilters() []*FilterRequest_ContentFilter { - if x != nil { - return x.ContentFilters - } - return nil -} - -type MessagePush struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Messages []*pb.WakuMessage `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` -} - -func (x *MessagePush) Reset() { - *x = MessagePush{} - if protoimpl.UnsafeEnabled { - mi := &file_legacy_filter_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *MessagePush) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MessagePush) ProtoMessage() {} - -func (x *MessagePush) ProtoReflect() protoreflect.Message { - mi := &file_legacy_filter_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use MessagePush.ProtoReflect.Descriptor instead. -func (*MessagePush) Descriptor() ([]byte, []int) { - return file_legacy_filter_proto_rawDescGZIP(), []int{1} -} - -func (x *MessagePush) GetMessages() []*pb.WakuMessage { - if x != nil { - return x.Messages - } - return nil -} - -type FilterRpc struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - Request *FilterRequest `protobuf:"bytes,2,opt,name=request,proto3,oneof" json:"request,omitempty"` - Push *MessagePush `protobuf:"bytes,3,opt,name=push,proto3,oneof" json:"push,omitempty"` -} - -func (x *FilterRpc) Reset() { - *x = FilterRpc{} - if protoimpl.UnsafeEnabled { - mi := &file_legacy_filter_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FilterRpc) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FilterRpc) ProtoMessage() {} - -func (x *FilterRpc) ProtoReflect() protoreflect.Message { - mi := &file_legacy_filter_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FilterRpc.ProtoReflect.Descriptor instead. -func (*FilterRpc) Descriptor() ([]byte, []int) { - return file_legacy_filter_proto_rawDescGZIP(), []int{2} -} - -func (x *FilterRpc) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -func (x *FilterRpc) GetRequest() *FilterRequest { - if x != nil { - return x.Request - } - return nil -} - -func (x *FilterRpc) GetPush() *MessagePush { - if x != nil { - return x.Push - } - return nil -} - -type FilterRequest_ContentFilter struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ContentTopic string `protobuf:"bytes,1,opt,name=content_topic,json=contentTopic,proto3" json:"content_topic,omitempty"` -} - -func (x *FilterRequest_ContentFilter) Reset() { - *x = FilterRequest_ContentFilter{} - if protoimpl.UnsafeEnabled { - mi := &file_legacy_filter_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FilterRequest_ContentFilter) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FilterRequest_ContentFilter) ProtoMessage() {} - -func (x *FilterRequest_ContentFilter) ProtoReflect() protoreflect.Message { - mi := &file_legacy_filter_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FilterRequest_ContentFilter.ProtoReflect.Descriptor instead. -func (*FilterRequest_ContentFilter) Descriptor() ([]byte, []int) { - return file_legacy_filter_proto_rawDescGZIP(), []int{0, 0} -} - -func (x *FilterRequest_ContentFilter) GetContentTopic() string { - if x != nil { - return x.ContentTopic - } - return "" -} - -var File_legacy_filter_proto protoreflect.FileDescriptor - -var file_legacy_filter_proto_rawDesc = []byte{ - 0x0a, 0x13, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x77, 0x61, 0x6b, 0x75, 0x2e, 0x66, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1d, 0x77, 0x61, 0x6b, 0x75, - 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd4, 0x01, 0x0a, 0x0d, 0x46, 0x69, - 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, - 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, - 0x59, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x77, 0x61, 0x6b, 0x75, 0x2e, - 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x46, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x0e, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x1a, 0x34, 0x0a, 0x0d, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x70, 0x69, 0x63, - 0x22, 0x47, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x75, 0x73, 0x68, 0x12, - 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x77, 0x61, 0x6b, 0x75, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x6b, 0x75, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, - 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x22, 0xbd, 0x01, 0x0a, 0x09, 0x46, 0x69, - 0x6c, 0x74, 0x65, 0x72, 0x52, 0x70, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x41, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x77, 0x61, 0x6b, 0x75, 0x2e, 0x66, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x46, 0x69, - 0x6c, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x07, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x04, 0x70, 0x75, 0x73, - 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x77, 0x61, 0x6b, 0x75, 0x2e, 0x66, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x75, 0x73, 0x68, 0x48, 0x01, 0x52, 0x04, 0x70, 0x75, 0x73, - 0x68, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, -} - -var ( - file_legacy_filter_proto_rawDescOnce sync.Once - file_legacy_filter_proto_rawDescData = file_legacy_filter_proto_rawDesc -) - -func file_legacy_filter_proto_rawDescGZIP() []byte { - file_legacy_filter_proto_rawDescOnce.Do(func() { - file_legacy_filter_proto_rawDescData = protoimpl.X.CompressGZIP(file_legacy_filter_proto_rawDescData) - }) - return file_legacy_filter_proto_rawDescData -} - -var file_legacy_filter_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_legacy_filter_proto_goTypes = []interface{}{ - (*FilterRequest)(nil), // 0: waku.filter.v2beta1.FilterRequest - (*MessagePush)(nil), // 1: waku.filter.v2beta1.MessagePush - (*FilterRpc)(nil), // 2: waku.filter.v2beta1.FilterRpc - (*FilterRequest_ContentFilter)(nil), // 3: waku.filter.v2beta1.FilterRequest.ContentFilter - (*pb.WakuMessage)(nil), // 4: waku.message.v1.WakuMessage -} -var file_legacy_filter_proto_depIdxs = []int32{ - 3, // 0: waku.filter.v2beta1.FilterRequest.content_filters:type_name -> waku.filter.v2beta1.FilterRequest.ContentFilter - 4, // 1: waku.filter.v2beta1.MessagePush.messages:type_name -> waku.message.v1.WakuMessage - 0, // 2: waku.filter.v2beta1.FilterRpc.request:type_name -> waku.filter.v2beta1.FilterRequest - 1, // 3: waku.filter.v2beta1.FilterRpc.push:type_name -> waku.filter.v2beta1.MessagePush - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name -} - -func init() { file_legacy_filter_proto_init() } -func file_legacy_filter_proto_init() { - if File_legacy_filter_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_legacy_filter_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FilterRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_legacy_filter_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MessagePush); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_legacy_filter_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FilterRpc); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_legacy_filter_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FilterRequest_ContentFilter); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_legacy_filter_proto_msgTypes[2].OneofWrappers = []interface{}{} - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_legacy_filter_proto_rawDesc, - NumEnums: 0, - NumMessages: 4, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_legacy_filter_proto_goTypes, - DependencyIndexes: file_legacy_filter_proto_depIdxs, - MessageInfos: file_legacy_filter_proto_msgTypes, - }.Build() - File_legacy_filter_proto = out.File - file_legacy_filter_proto_rawDesc = nil - file_legacy_filter_proto_goTypes = nil - file_legacy_filter_proto_depIdxs = nil -} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/waku_filter.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/waku_filter.go deleted file mode 100644 index 0d87f7182..000000000 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/waku_filter.go +++ /dev/null @@ -1,468 +0,0 @@ -package legacy_filter - -import ( - "context" - "encoding/hex" - "errors" - "math" - - "github.com/libp2p/go-libp2p/core/host" - "github.com/libp2p/go-libp2p/core/network" - "github.com/libp2p/go-libp2p/core/peer" - libp2pProtocol "github.com/libp2p/go-libp2p/core/protocol" - "github.com/libp2p/go-msgio/pbio" - "github.com/prometheus/client_golang/prometheus" - "github.com/waku-org/go-waku/logging" - "github.com/waku-org/go-waku/waku/v2/peermanager" - "github.com/waku-org/go-waku/waku/v2/protocol" - "github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/pb" - wpb "github.com/waku-org/go-waku/waku/v2/protocol/pb" - "github.com/waku-org/go-waku/waku/v2/protocol/relay" - "github.com/waku-org/go-waku/waku/v2/service" - "github.com/waku-org/go-waku/waku/v2/timesource" - "go.uber.org/zap" - "golang.org/x/sync/errgroup" -) - -var ( - ErrNoPeersAvailable = errors.New("no suitable remote peers") -) - -type ( - Filter struct { - filterID string - PeerID peer.ID - Topic string - ContentFilters []string - Chan chan *protocol.Envelope - } - - ContentFilter struct { - Topic string - ContentTopics []string - } - - FilterSubscription struct { - RequestID string - Peer peer.ID - } - - WakuFilter struct { - *service.CommonService - h host.Host - pm *peermanager.PeerManager - isFullNode bool - msgSub *relay.Subscription - metrics Metrics - log *zap.Logger - - filters *FilterMap - subscribers *Subscribers - } -) - -// FilterID_v20beta1 is the current Waku Filter protocol identifier -const FilterID_v20beta1 = libp2pProtocol.ID("/vac/waku/filter/2.0.0-beta1") - -// NewWakuRelay returns a new instance of Waku Filter struct setup according to the chosen parameter and options -func NewWakuFilter(broadcaster relay.Broadcaster, isFullNode bool, timesource timesource.Timesource, reg prometheus.Registerer, log *zap.Logger, opts ...Option) *WakuFilter { - wf := new(WakuFilter) - wf.log = log.Named("filter").With(zap.Bool("fullNode", isFullNode)) - - params := new(FilterParameters) - optList := DefaultOptions() - optList = append(optList, opts...) - for _, opt := range optList { - opt(params) - } - - wf.isFullNode = isFullNode - wf.CommonService = service.NewCommonService() - wf.filters = NewFilterMap(broadcaster, timesource) - wf.subscribers = NewSubscribers(params.Timeout) - wf.metrics = newMetrics(reg) - - return wf -} - -// Sets the host to be able to mount or consume a protocol -func (wf *WakuFilter) SetHost(h host.Host) { - wf.h = h -} - -func (wf *WakuFilter) Start(ctx context.Context, sub *relay.Subscription) error { - return wf.CommonService.Start(ctx, func() error { - return wf.start(sub) - }) -} - -func (wf *WakuFilter) start(sub *relay.Subscription) error { - wf.h.SetStreamHandlerMatch(FilterID_v20beta1, protocol.PrefixTextMatch(string(FilterID_v20beta1)), wf.onRequest(wf.Context())) - wf.msgSub = sub - wf.WaitGroup().Add(1) - go wf.filterListener(wf.Context()) - wf.log.Info("filter protocol started") - return nil -} -func (wf *WakuFilter) onRequest(ctx context.Context) func(network.Stream) { - return func(stream network.Stream) { - peerID := stream.Conn().RemotePeer() - logger := wf.log.With(logging.HostID("peer", peerID)) - - filterRPCRequest := &pb.FilterRpc{} - - reader := pbio.NewDelimitedReader(stream, math.MaxInt32) - - err := reader.ReadMsg(filterRPCRequest) - if err != nil { - wf.metrics.RecordError(decodeRPCFailure) - logger.Error("reading request", zap.Error(err)) - if err := stream.Reset(); err != nil { - wf.log.Error("resetting connection", zap.Error(err)) - } - return - } - - logger.Info("received request") - - if filterRPCRequest.Push != nil && len(filterRPCRequest.Push.Messages) > 0 { - // We're on a light node. - // This is a message push coming from a full node. - for _, message := range filterRPCRequest.Push.Messages { - wf.filters.Notify(message, filterRPCRequest.RequestId) // Trigger filter handlers on a light node - } - - logger.Info("received a message push", zap.Int("messages", len(filterRPCRequest.Push.Messages))) - wf.metrics.RecordMessages(len(filterRPCRequest.Push.Messages)) - } else if filterRPCRequest.Request != nil && wf.isFullNode { - // We're on a full node. - // This is a filter request coming from a light node. - if filterRPCRequest.Request.Subscribe { - subscriber := Subscriber{peer: stream.Conn().RemotePeer(), requestID: filterRPCRequest.RequestId, filter: filterRPCRequest.Request} - if subscriber.filter.Topic == "" { // @TODO: review if empty topic is possible - subscriber.filter.Topic = relay.DefaultWakuTopic - } - - subscribersLen := wf.subscribers.Append(subscriber) - - logger.Info("adding subscriber") - wf.metrics.RecordSubscribers(subscribersLen) - } else { - wf.subscribers.RemoveContentFilters(peerID, filterRPCRequest.RequestId, filterRPCRequest.Request.ContentFilters) - - logger.Info("removing subscriber") - wf.metrics.RecordSubscribers(wf.subscribers.Length()) - } - } else { - logger.Error("can't serve request") - if err := stream.Reset(); err != nil { - wf.log.Error("resetting connection", zap.Error(err)) - } - return - } - - stream.Close() - } -} - -func (wf *WakuFilter) pushMessage(ctx context.Context, subscriber Subscriber, msg *wpb.WakuMessage) error { - pushRPC := &pb.FilterRpc{RequestId: subscriber.requestID, Push: &pb.MessagePush{Messages: []*wpb.WakuMessage{msg}}} - logger := wf.log.With(logging.HostID("peer", subscriber.peer)) - - stream, err := wf.h.NewStream(ctx, subscriber.peer, FilterID_v20beta1) - if err != nil { - wf.subscribers.FlagAsFailure(subscriber.peer) - logger.Error("opening peer stream", zap.Error(err)) - wf.metrics.RecordError(dialFailure) - return err - } - - writer := pbio.NewDelimitedWriter(stream) - err = writer.WriteMsg(pushRPC) - if err != nil { - logger.Error("pushing messages to peer", zap.Error(err)) - wf.subscribers.FlagAsFailure(subscriber.peer) - wf.metrics.RecordError(pushWriteError) - if err := stream.Reset(); err != nil { - wf.log.Error("resetting connection", zap.Error(err)) - } - return nil - } - - stream.Close() - - wf.subscribers.FlagAsSuccess(subscriber.peer) - return nil -} - -func (wf *WakuFilter) filterListener(ctx context.Context) { - defer wf.WaitGroup().Done() - - // This function is invoked for each message received - // on the full node in context of Waku2-Filter - handle := func(envelope *protocol.Envelope) error { // async - msg := envelope.Message() - pubsubTopic := envelope.PubsubTopic() - logger := wf.log.With(zap.Stringer("message", msg)) - g := new(errgroup.Group) - // Each subscriber is a light node that earlier on invoked - // a FilterRequest on this node - for subscriber := range wf.subscribers.Items(&(msg.ContentTopic)) { - logger := logger.With(logging.HostID("subscriber", subscriber.peer)) - subscriber := subscriber // https://golang.org/doc/faq#closures_and_goroutines - if subscriber.filter.Topic != pubsubTopic { - logger.Info("pubsub topic mismatch", - zap.String("subscriberTopic", subscriber.filter.Topic), - zap.String("messageTopic", pubsubTopic)) - continue - } - - // Do a message push to light node - logger.Info("pushing message to light node", zap.String("contentTopic", msg.ContentTopic)) - g.Go(func() (err error) { - err = wf.pushMessage(ctx, subscriber, msg) - if err != nil { - logger.Error("pushing message", zap.Error(err)) - } - return err - }) - } - - return g.Wait() - } - - for m := range wf.msgSub.Ch { - if err := handle(m); err != nil { - wf.log.Error("handling message", zap.Error(err)) - } - } -} - -// Having a FilterRequest struct, -// select a peer with filter support, dial it, -// and submit FilterRequest wrapped in FilterRPC -func (wf *WakuFilter) requestSubscription(ctx context.Context, filter ContentFilter, opts ...FilterSubscribeOption) (subscription *FilterSubscription, err error) { - params := new(FilterSubscribeParameters) - params.log = wf.log - params.host = wf.h - - optList := DefaultSubscribtionOptions() - optList = append(optList, opts...) - for _, opt := range optList { - opt(params) - } - if wf.pm != nil && params.selectedPeer == "" { - params.selectedPeer, _ = wf.pm.SelectPeer( - peermanager.PeerSelectionCriteria{ - SelectionType: params.peerSelectionType, - Proto: FilterID_v20beta1, - PubsubTopics: []string{filter.Topic}, - SpecificPeers: params.preferredPeers, - Ctx: ctx, - }, - ) - } - if params.selectedPeer == "" { - wf.metrics.RecordError(peerNotFoundFailure) - return nil, ErrNoPeersAvailable - } - - var contentFilters []*pb.FilterRequest_ContentFilter - for _, ct := range filter.ContentTopics { - contentFilters = append(contentFilters, &pb.FilterRequest_ContentFilter{ContentTopic: ct}) - } - - request := &pb.FilterRequest{ - Subscribe: true, - Topic: filter.Topic, - ContentFilters: contentFilters, - } - - stream, err := wf.h.NewStream(ctx, params.selectedPeer, FilterID_v20beta1) - if err != nil { - wf.metrics.RecordError(dialFailure) - return - } - - // This is the only successful path to subscription - requestID := hex.EncodeToString(protocol.GenerateRequestID()) - - writer := pbio.NewDelimitedWriter(stream) - filterRPC := &pb.FilterRpc{RequestId: requestID, Request: request} - wf.log.Debug("sending filterRPC", zap.Stringer("rpc", filterRPC)) - err = writer.WriteMsg(filterRPC) - if err != nil { - wf.metrics.RecordError(writeRequestFailure) - wf.log.Error("sending filterRPC", zap.Error(err)) - if err := stream.Reset(); err != nil { - wf.log.Error("resetting connection", zap.Error(err)) - } - return - } - - stream.Close() - - subscription = new(FilterSubscription) - subscription.Peer = params.selectedPeer - subscription.RequestID = requestID - - return -} - -// Unsubscribe is used to stop receiving messages from a peer that match a content filter -func (wf *WakuFilter) Unsubscribe(ctx context.Context, contentFilter ContentFilter, peer peer.ID) error { - stream, err := wf.h.NewStream(ctx, peer, FilterID_v20beta1) - if err != nil { - wf.metrics.RecordError(dialFailure) - return err - } - - // This is the only successful path to subscription - id := protocol.GenerateRequestID() - - var contentFilters []*pb.FilterRequest_ContentFilter - for _, ct := range contentFilter.ContentTopics { - contentFilters = append(contentFilters, &pb.FilterRequest_ContentFilter{ContentTopic: ct}) - } - - request := &pb.FilterRequest{ - Subscribe: false, - Topic: contentFilter.Topic, - ContentFilters: contentFilters, - } - - writer := pbio.NewDelimitedWriter(stream) - filterRPC := &pb.FilterRpc{RequestId: hex.EncodeToString(id), Request: request} - err = writer.WriteMsg(filterRPC) - if err != nil { - wf.metrics.RecordError(writeRequestFailure) - if err := stream.Reset(); err != nil { - wf.log.Error("resetting connection", zap.Error(err)) - } - return err - } - - stream.Close() - - return nil -} - -// Stop unmounts the filter protocol -func (wf *WakuFilter) Stop() { - wf.CommonService.Stop(func() { - wf.msgSub.Unsubscribe() - - wf.h.RemoveStreamHandler(FilterID_v20beta1) - wf.filters.RemoveAll() - wf.subscribers.Clear() - }) -} - -// Subscribe setups a subscription to receive messages that match a specific content filter -func (wf *WakuFilter) Subscribe(ctx context.Context, f ContentFilter, opts ...FilterSubscribeOption) (filterID string, theFilter Filter, err error) { - // TODO: check if there's an existing pubsub topic that uses the same peer. If so, reuse filter, and return same channel and filterID - - // Registers for messages that match a specific filter. Triggers the handler whenever a message is received. - // ContentFilterChan takes MessagePush structs - remoteSubs, err := wf.requestSubscription(ctx, f, opts...) - if err != nil || remoteSubs.RequestID == "" { - // Failed to subscribe - wf.log.Error("requesting subscription", zap.Error(err)) - return - } - - // Register handler for filter, whether remote subscription succeeded or not - - filterID = remoteSubs.RequestID - theFilter = Filter{ - filterID: filterID, - PeerID: remoteSubs.Peer, - Topic: f.Topic, - ContentFilters: f.ContentTopics, - Chan: make(chan *protocol.Envelope, 1024), // To avoid blocking - } - wf.filters.Set(filterID, theFilter) - - return -} - -// UnsubscribeFilterByID removes a subscription to a filter node completely -// using using a filter. It also closes the filter channel -func (wf *WakuFilter) UnsubscribeByFilter(ctx context.Context, filter Filter) error { - err := wf.UnsubscribeFilterByID(ctx, filter.filterID) - if err != nil { - close(filter.Chan) - } - return err -} - -// UnsubscribeFilterByID removes a subscription to a filter node completely -// using the filterID returned when the subscription was created -func (wf *WakuFilter) UnsubscribeFilterByID(ctx context.Context, filterID string) error { - - var f Filter - var ok bool - - if f, ok = wf.filters.Get(filterID); !ok { - return errors.New("filter not found") - } - - cf := ContentFilter{ - Topic: f.Topic, - ContentTopics: f.ContentFilters, - } - - err := wf.Unsubscribe(ctx, cf, f.PeerID) - if err != nil { - return err - } - - wf.filters.Delete(filterID) - - return nil -} - -// Unsubscribe filter removes content topics from a filter subscription. If all -// the contentTopics are removed the subscription is dropped completely -func (wf *WakuFilter) UnsubscribeFilter(ctx context.Context, cf ContentFilter) error { - // Remove local filter - idsToRemove := make(map[string]struct{}) - for filterMapItem := range wf.filters.Items() { - f := filterMapItem.Value - id := filterMapItem.Key - - if f.Topic != cf.Topic { - continue - } - - // Send message to full node in order to unsubscribe - err := wf.Unsubscribe(ctx, cf, f.PeerID) - if err != nil { - return err - } - - // Iterate filter entries to remove matching content topics - // make sure we delete the content filter - // if no more topics are left - for _, cfToDelete := range cf.ContentTopics { - for i, cf := range f.ContentFilters { - if cf == cfToDelete { - l := len(f.ContentFilters) - 1 - f.ContentFilters[l], f.ContentFilters[i] = f.ContentFilters[i], f.ContentFilters[l] - f.ContentFilters = f.ContentFilters[:l] - break - } - - } - if len(f.ContentFilters) == 0 { - idsToRemove[id] = struct{}{} - } - } - } - - for rID := range idsToRemove { - wf.filters.Delete(rID) - } - - return nil -} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/waku_filter_option.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/waku_filter_option.go deleted file mode 100644 index 4b103a367..000000000 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/waku_filter_option.go +++ /dev/null @@ -1,79 +0,0 @@ -package legacy_filter - -import ( - "time" - - "github.com/libp2p/go-libp2p/core/host" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/waku-org/go-waku/waku/v2/peermanager" - "go.uber.org/zap" -) - -type ( - FilterSubscribeParameters struct { - host host.Host - selectedPeer peer.ID - peerSelectionType peermanager.PeerSelection - preferredPeers peer.IDSlice - log *zap.Logger - } - - FilterSubscribeOption func(*FilterSubscribeParameters) - - FilterParameters struct { - Timeout time.Duration - pm *peermanager.PeerManager - } - - Option func(*FilterParameters) -) - -func WithTimeout(timeout time.Duration) Option { - return func(params *FilterParameters) { - params.Timeout = timeout - } -} - -func WithPeerManager(pm *peermanager.PeerManager) Option { - return func(params *FilterParameters) { - params.pm = pm - } -} - -func WithPeer(p peer.ID) FilterSubscribeOption { - return func(params *FilterSubscribeParameters) { - params.selectedPeer = p - } -} - -// WithAutomaticPeerSelection is an option used to randomly select a peer from the peer store. -// If a list of specific peers is passed, the peer will be chosen from that list assuming it -// supports the chosen protocol, otherwise it will chose a peer from the node peerstore -func WithAutomaticPeerSelection(fromThesePeers ...peer.ID) FilterSubscribeOption { - return func(params *FilterSubscribeParameters) { - params.peerSelectionType = peermanager.Automatic - params.preferredPeers = fromThesePeers - } -} - -// WithFastestPeerSelection is an option used to select a peer from the peer store -// with the lowest ping If a list of specific peers is passed, the peer will be chosen -// from that list assuming it supports the chosen protocol, otherwise it will chose a -// peer from the node peerstore -func WithFastestPeerSelection(fromThesePeers ...peer.ID) FilterSubscribeOption { - return func(params *FilterSubscribeParameters) { - params.peerSelectionType = peermanager.LowestRTT - } -} - -func DefaultOptions() []Option { - return []Option{ - WithTimeout(24 * time.Hour), - } -} - -func DefaultSubscribtionOptions() []FilterSubscribeOption { - return []FilterSubscribeOption{ - WithAutomaticPeerSelection(), - } -} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/metrics.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/metrics.go similarity index 98% rename from vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/metrics.go rename to vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/metrics.go index d9083ec66..99ff87a06 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/metrics.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/metrics.go @@ -1,4 +1,4 @@ -package store +package legacy_store import ( "github.com/libp2p/go-libp2p/p2p/metricshelper" diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb/generate.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb/generate.go new file mode 100644 index 000000000..eb2537ebd --- /dev/null +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb/generate.go @@ -0,0 +1,3 @@ +package pb + +//go:generate protoc -I./../../waku-proto/waku/store/v2beta4//. -I./../../waku-proto/ --go_opt=paths=source_relative --go_opt=Mstore.proto=github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb --go_opt=Mwaku/message/v1/message.proto=github.com/waku-org/go-waku/waku/v2/protocol/pb --go_out=. ./../../waku-proto/waku/store/v2beta4/store.proto diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/store.pb.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb/store.pb.go similarity index 100% rename from vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/store.pb.go rename to vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb/store.pb.go diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb/validation.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb/validation.go new file mode 100644 index 000000000..740b58086 --- /dev/null +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb/validation.go @@ -0,0 +1,68 @@ +package pb + +import ( + "errors" +) + +// MaxContentFilters is the maximum number of allowed content filters in a query +const MaxContentFilters = 10 + +var ( + errMissingRequestID = errors.New("missing RequestId field") + errMissingQuery = errors.New("missing Query field") + errRequestIDMismatch = errors.New("requestID in response does not match request") + errMaxContentFilters = errors.New("exceeds the maximum number of content filters allowed") + errEmptyContentTopics = errors.New("one or more content topics specified is empty") +) + +func (x *HistoryQuery) Validate() error { + if len(x.ContentFilters) > MaxContentFilters { + return errMaxContentFilters + } + + for _, m := range x.ContentFilters { + if m.ContentTopic == "" { + return errEmptyContentTopics + } + } + + return nil +} + +func (x *HistoryRPC) ValidateQuery() error { + if x.RequestId == "" { + return errMissingRequestID + } + + if x.Query == nil { + return errMissingQuery + } + + return x.Query.Validate() +} + +func (x *HistoryResponse) Validate() error { + for _, m := range x.Messages { + if err := m.Validate(); err != nil { + return err + } + } + + return nil +} + +func (x *HistoryRPC) ValidateResponse(requestID string) error { + if x.RequestId == "" { + return errMissingRequestID + } + + if x.RequestId != requestID { + return errRequestIDMismatch + } + + if x.Response != nil { + return x.Response.Validate() + } + + return nil +} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/waku_store_client.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/waku_store_client.go similarity index 96% rename from vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/waku_store_client.go rename to vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/waku_store_client.go index 9e5687971..b02cd92e3 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/waku_store_client.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/waku_store_client.go @@ -1,4 +1,4 @@ -package store +package legacy_store import ( "context" @@ -15,8 +15,8 @@ import ( "github.com/waku-org/go-waku/waku/v2/peermanager" "github.com/waku-org/go-waku/waku/v2/peerstore" "github.com/waku-org/go-waku/waku/v2/protocol" + "github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb" wpb "github.com/waku-org/go-waku/waku/v2/protocol/pb" - "github.com/waku-org/go-waku/waku/v2/protocol/store/pb" ) type Query struct { @@ -275,6 +275,10 @@ func (store *WakuStore) localQuery(historyQuery *pb.HistoryRPC) (*pb.HistoryResp return historyResponseRPC.Response, nil } +func (store *WakuStore) isLocalQuery(p *HistoryRequestParameters) bool { + return p.localQuery && store.started +} + func (store *WakuStore) Query(ctx context.Context, query Query, opts ...HistoryRequestOption) (*Result, error) { params := new(HistoryRequestParameters) params.s = store @@ -288,7 +292,7 @@ func (store *WakuStore) Query(ctx context.Context, query Query, opts ...HistoryR } } - if !params.localQuery { + if !store.isLocalQuery(params) { pubsubTopics := []string{} if query.PubsubTopic == "" { for _, cTopic := range query.ContentTopics { @@ -312,8 +316,7 @@ func (store *WakuStore) Query(ctx context.Context, query Query, opts ...HistoryR params.selectedPeer = pData.AddrInfo.ID } if store.pm != nil && params.selectedPeer == "" { - var err error - params.selectedPeer, err = store.pm.SelectPeer( + selectedPeers, err := store.pm.SelectPeers( peermanager.PeerSelectionCriteria{ SelectionType: params.peerSelectionType, Proto: StoreID_v20beta4, @@ -325,6 +328,7 @@ func (store *WakuStore) Query(ctx context.Context, query Query, opts ...HistoryR if err != nil { return nil, err } + params.selectedPeer = selectedPeers[0] } } @@ -343,7 +347,7 @@ func (store *WakuStore) Query(ctx context.Context, query Query, opts ...HistoryR historyRequest.Query.ContentFilters = append(historyRequest.Query.ContentFilters, &pb.ContentFilter{ContentTopic: cf}) } - if !params.localQuery && params.selectedPeer == "" { + if !store.isLocalQuery(params) && params.selectedPeer == "" { store.metrics.RecordError(peerNotFoundFailure) return nil, ErrNoPeersAvailable } @@ -373,7 +377,7 @@ func (store *WakuStore) Query(ctx context.Context, query Query, opts ...HistoryR var response *pb.HistoryResponse - if params.localQuery { + if store.isLocalQuery(params) { response, err = store.localQuery(historyRequest) } else { response, err = store.queryFrom(ctx, historyRequest, params.selectedPeer) diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/waku_store_common.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/waku_store_common.go similarity index 95% rename from vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/waku_store_common.go rename to vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/waku_store_common.go index 82e369cfd..f1edbbc25 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/waku_store_common.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/waku_store_common.go @@ -1,4 +1,4 @@ -package store +package legacy_store import ( "context" @@ -67,8 +67,5 @@ func NewWakuStore(p MessageProvider, pm *peermanager.PeerManager, timesource tim wakuStore.pm = pm wakuStore.metrics = newMetrics(reg) - if pm != nil { - pm.RegisterWakuProtocol(StoreID_v20beta4, StoreENRField) - } return wakuStore } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/waku_store_protocol.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/waku_store_protocol.go similarity index 98% rename from vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/waku_store_protocol.go rename to vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/waku_store_protocol.go index 76e3983fe..16eabe8f0 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/waku_store_protocol.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/waku_store_protocol.go @@ -1,4 +1,4 @@ -package store +package legacy_store import ( "context" @@ -17,9 +17,9 @@ import ( "github.com/waku-org/go-waku/logging" "github.com/waku-org/go-waku/waku/persistence" "github.com/waku-org/go-waku/waku/v2/protocol" + "github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb" wpb "github.com/waku-org/go-waku/waku/v2/protocol/pb" "github.com/waku-org/go-waku/waku/v2/protocol/relay" - "github.com/waku-org/go-waku/waku/v2/protocol/store/pb" "github.com/waku-org/go-waku/waku/v2/timesource" ) @@ -119,6 +119,10 @@ func (store *WakuStore) Start(ctx context.Context, sub *relay.Subscription) erro return err } + if store.pm != nil { + store.pm.RegisterWakuProtocol(StoreID_v20beta4, StoreENRField) + } + store.started = true store.ctx, store.cancel = context.WithCancel(ctx) store.MsgC = sub diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/lightpush/metrics.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/lightpush/metrics.go index 8938bcd7c..8769ba72f 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/lightpush/metrics.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/lightpush/metrics.go @@ -53,6 +53,7 @@ var ( writeRequestFailure metricsErrCategory = "write_request_failure" writeResponseFailure metricsErrCategory = "write_response_failure" dialFailure metricsErrCategory = "dial_failure" + rateLimitFailure metricsErrCategory = "ratelimit_failure" messagePushFailure metricsErrCategory = "message_push_failure" requestBodyFailure metricsErrCategory = "request_failure" responseBodyFailure metricsErrCategory = "response_body_failure" diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/lightpush/waku_lightpush.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/lightpush/waku_lightpush.go index 322b4d9c3..f2b98bb37 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/lightpush/waku_lightpush.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/lightpush/waku_lightpush.go @@ -9,6 +9,7 @@ import ( "github.com/libp2p/go-libp2p/core/host" "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" libp2pProtocol "github.com/libp2p/go-libp2p/core/protocol" "github.com/libp2p/go-msgio/pbio" "github.com/prometheus/client_golang/prometheus" @@ -21,6 +22,7 @@ import ( "github.com/waku-org/go-waku/waku/v2/protocol/relay" "github.com/waku-org/go-waku/waku/v2/utils" "go.uber.org/zap" + "golang.org/x/time/rate" ) // LightPushID_v20beta1 is the current Waku LightPush protocol identifier @@ -36,6 +38,7 @@ var ( type WakuLightPush struct { h host.Host relay *relay.WakuRelay + limiter *rate.Limiter cancel context.CancelFunc pm *peermanager.PeerManager metrics Metrics @@ -46,17 +49,20 @@ type WakuLightPush struct { // NewWakuLightPush returns a new instance of Waku Lightpush struct // Takes an optional peermanager if WakuLightPush is being created along with WakuNode. // If using libp2p host, then pass peermanager as nil -func NewWakuLightPush(relay *relay.WakuRelay, pm *peermanager.PeerManager, reg prometheus.Registerer, log *zap.Logger) *WakuLightPush { +func NewWakuLightPush(relay *relay.WakuRelay, pm *peermanager.PeerManager, reg prometheus.Registerer, log *zap.Logger, opts ...Option) *WakuLightPush { wakuLP := new(WakuLightPush) wakuLP.relay = relay wakuLP.log = log.Named("lightpush") wakuLP.pm = pm wakuLP.metrics = newMetrics(reg) - if pm != nil { - wakuLP.pm.RegisterWakuProtocol(LightPushID_v20beta1, LightPushENRField) + params := &LightpushParameters{} + for _, opt := range opts { + opt(params) } + wakuLP.limiter = params.limiter + return wakuLP } @@ -71,6 +77,10 @@ func (wakuLP *WakuLightPush) Start(ctx context.Context) error { return errors.New("relay is required, without it, it is only a client and cannot be started") } + if wakuLP.pm != nil { + wakuLP.pm.RegisterWakuProtocol(LightPushID_v20beta1, LightPushENRField) + } + ctx, cancel := context.WithCancel(ctx) wakuLP.cancel = cancel @@ -90,6 +100,18 @@ func (wakuLP *WakuLightPush) onRequest(ctx context.Context) func(network.Stream) logger := wakuLP.log.With(logging.HostID("peer", stream.Conn().RemotePeer())) requestPushRPC := &pb.PushRpc{} + responsePushRPC := &pb.PushRpc{ + Response: &pb.PushResponse{}, + } + + if wakuLP.limiter != nil && !wakuLP.limiter.Allow() { + wakuLP.metrics.RecordError(rateLimitFailure) + responseMsg := "exceeds the rate limit" + responsePushRPC.Response.Info = &responseMsg + wakuLP.reply(stream, responsePushRPC, logger) + return + } + reader := pbio.NewDelimitedReader(stream, math.MaxInt32) err := reader.ReadMsg(requestPushRPC) @@ -102,13 +124,9 @@ func (wakuLP *WakuLightPush) onRequest(ctx context.Context) func(network.Stream) return } - responsePushRPC := &pb.PushRpc{ - RequestId: requestPushRPC.RequestId, - Response: &pb.PushResponse{}, - } - + responsePushRPC.RequestId = requestPushRPC.RequestId if err := requestPushRPC.ValidateRequest(); err != nil { - responseMsg := err.Error() + responseMsg := "invalid request: " + err.Error() responsePushRPC.Response.Info = &responseMsg wakuLP.metrics.RecordError(requestBodyFailure) wakuLP.reply(stream, responsePushRPC, logger) @@ -133,7 +151,6 @@ func (wakuLP *WakuLightPush) onRequest(ctx context.Context) func(network.Stream) wakuLP.metrics.RecordError(messagePushFailure) responseMsg := fmt.Sprintf("Could not publish message: %s", err.Error()) responsePushRPC.Response.Info = &responseMsg - return } else { responsePushRPC.Response.IsSuccess = true responseMsg := "OK" @@ -169,7 +186,7 @@ func (wakuLP *WakuLightPush) reply(stream network.Stream, responsePushRPC *pb.Pu } // request sends a message via lightPush protocol to either a specified peer or peer that is selected. -func (wakuLP *WakuLightPush) request(ctx context.Context, req *pb.PushRequest, params *lightPushParameters) (*pb.PushResponse, error) { +func (wakuLP *WakuLightPush) request(ctx context.Context, req *pb.PushRequest, params *lightPushRequestParameters) (*pb.PushResponse, error) { if params == nil { return nil, errors.New("lightpush params are mandatory") } @@ -187,6 +204,9 @@ func (wakuLP *WakuLightPush) request(ctx context.Context, req *pb.PushRequest, p return nil, err } pushRequestRPC := &pb.PushRpc{RequestId: hex.EncodeToString(params.requestID), Request: req} + if err = pushRequestRPC.ValidateRequest(); err != nil { + return nil, err + } writer := pbio.NewDelimitedWriter(stream) reader := pbio.NewDelimitedReader(stream, math.MaxInt32) @@ -216,7 +236,7 @@ func (wakuLP *WakuLightPush) request(ctx context.Context, req *pb.PushRequest, p if err = pushResponseRPC.ValidateResponse(pushRequestRPC.RequestId); err != nil { wakuLP.metrics.RecordError(responseBodyFailure) - return nil, err + return nil, fmt.Errorf("invalid response: %w", err) } return pushResponseRPC.Response, nil @@ -232,8 +252,8 @@ func (wakuLP *WakuLightPush) Stop() { wakuLP.h.RemoveStreamHandler(LightPushID_v20beta1) } -func (wakuLP *WakuLightPush) handleOpts(ctx context.Context, message *wpb.WakuMessage, opts ...Option) (*lightPushParameters, error) { - params := new(lightPushParameters) +func (wakuLP *WakuLightPush) handleOpts(ctx context.Context, message *wpb.WakuMessage, opts ...RequestOption) (*lightPushRequestParameters, error) { + params := new(lightPushRequestParameters) params.host = wakuLP.h params.log = wakuLP.log params.pm = wakuLP.pm @@ -264,7 +284,9 @@ func (wakuLP *WakuLightPush) handleOpts(ctx context.Context, message *wpb.WakuMe } if params.pm != nil && params.selectedPeer == "" { - params.selectedPeer, err = wakuLP.pm.SelectPeer( + var selectedPeers peer.IDSlice + //TODO: update this to work with multiple peer selection + selectedPeers, err = wakuLP.pm.SelectPeers( peermanager.PeerSelectionCriteria{ SelectionType: params.peerSelectionType, Proto: LightPushID_v20beta1, @@ -273,6 +295,10 @@ func (wakuLP *WakuLightPush) handleOpts(ctx context.Context, message *wpb.WakuMe Ctx: ctx, }, ) + if err == nil { + params.selectedPeer = selectedPeers[0] + } + } if params.selectedPeer == "" { if err != nil { @@ -287,14 +313,14 @@ func (wakuLP *WakuLightPush) handleOpts(ctx context.Context, message *wpb.WakuMe // Publish is used to broadcast a WakuMessage to the pubSubTopic (which is derived from the // contentTopic) via lightpush protocol. If auto-sharding is not to be used, then the // `WithPubSubTopic` option should be provided to publish the message to an specific pubSubTopic -func (wakuLP *WakuLightPush) Publish(ctx context.Context, message *wpb.WakuMessage, opts ...Option) ([]byte, error) { +func (wakuLP *WakuLightPush) Publish(ctx context.Context, message *wpb.WakuMessage, opts ...RequestOption) (wpb.MessageHash, error) { if message == nil { - return nil, errors.New("message can't be null") + return wpb.MessageHash{}, errors.New("message can't be null") } params, err := wakuLP.handleOpts(ctx, message, opts...) if err != nil { - return nil, err + return wpb.MessageHash{}, err } req := new(pb.PushRequest) req.Message = message @@ -307,12 +333,12 @@ func (wakuLP *WakuLightPush) Publish(ctx context.Context, message *wpb.WakuMessa response, err := wakuLP.request(ctx, req, params) if err != nil { logger.Error("could not publish message", zap.Error(err)) - return nil, err + return wpb.MessageHash{}, err } if response.IsSuccess { hash := message.Hash(params.pubsubTopic) - utils.MessagesLogger("lightpush").Debug("waku.lightpush published", logging.HexBytes("hash", hash)) + utils.MessagesLogger("lightpush").Debug("waku.lightpush published", logging.HexBytes("hash", hash[:])) return hash, nil } @@ -321,5 +347,5 @@ func (wakuLP *WakuLightPush) Publish(ctx context.Context, message *wpb.WakuMessa errMsg = *response.Info } - return nil, errors.New(errMsg) + return wpb.MessageHash{}, errors.New(errMsg) } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/lightpush/waku_lightpush_option.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/lightpush/waku_lightpush_option.go index caf87c0c0..6ec258990 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/lightpush/waku_lightpush_option.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/lightpush/waku_lightpush_option.go @@ -10,9 +10,23 @@ import ( "github.com/waku-org/go-waku/waku/v2/protocol" "github.com/waku-org/go-waku/waku/v2/protocol/relay" "go.uber.org/zap" + "golang.org/x/time/rate" ) -type lightPushParameters struct { +type LightpushParameters struct { + limiter *rate.Limiter +} + +type Option func(*LightpushParameters) + +// WithRateLimiter is an option used to specify a rate limiter for requests received in lightpush protocol +func WithRateLimiter(r rate.Limit, b int) Option { + return func(params *LightpushParameters) { + params.limiter = rate.NewLimiter(r, b) + } +} + +type lightPushRequestParameters struct { host host.Host peerAddr multiaddr.Multiaddr selectedPeer peer.ID @@ -24,12 +38,12 @@ type lightPushParameters struct { pubsubTopic string } -// Option is the type of options accepted when performing LightPush protocol requests -type Option func(*lightPushParameters) error +// RequestOption is the type of options accepted when performing LightPush protocol requests +type RequestOption func(*lightPushRequestParameters) error // WithPeer is an option used to specify the peerID to push a waku message to -func WithPeer(p peer.ID) Option { - return func(params *lightPushParameters) error { +func WithPeer(p peer.ID) RequestOption { + return func(params *lightPushRequestParameters) error { params.selectedPeer = p if params.peerAddr != nil { return errors.New("peerAddr and peerId options are mutually exclusive") @@ -41,8 +55,8 @@ func WithPeer(p peer.ID) Option { // WithPeerAddr is an option used to specify a peerAddress // This new peer will be added to peerStore. // Note that this option is mutually exclusive to WithPeerAddr, only one of them can be used. -func WithPeerAddr(pAddr multiaddr.Multiaddr) Option { - return func(params *lightPushParameters) error { +func WithPeerAddr(pAddr multiaddr.Multiaddr) RequestOption { + return func(params *lightPushRequestParameters) error { params.peerAddr = pAddr if params.selectedPeer != "" { return errors.New("peerAddr and peerId options are mutually exclusive") @@ -55,8 +69,8 @@ func WithPeerAddr(pAddr multiaddr.Multiaddr) Option { // to push a waku message to. If a list of specific peers is passed, the peer will be chosen // from that list assuming it supports the chosen protocol, otherwise it will chose a peer // from the node peerstore -func WithAutomaticPeerSelection(fromThesePeers ...peer.ID) Option { - return func(params *lightPushParameters) error { +func WithAutomaticPeerSelection(fromThesePeers ...peer.ID) RequestOption { + return func(params *lightPushRequestParameters) error { params.peerSelectionType = peermanager.Automatic params.preferredPeers = fromThesePeers return nil @@ -67,24 +81,24 @@ func WithAutomaticPeerSelection(fromThesePeers ...peer.ID) Option { // with the lowest ping. If a list of specific peers is passed, the peer will be chosen // from that list assuming it supports the chosen protocol, otherwise it will chose a peer // from the node peerstore -func WithFastestPeerSelection(fromThesePeers ...peer.ID) Option { - return func(params *lightPushParameters) error { +func WithFastestPeerSelection(fromThesePeers ...peer.ID) RequestOption { + return func(params *lightPushRequestParameters) error { params.peerSelectionType = peermanager.LowestRTT return nil } } // WithPubSubTopic is used to specify the pubsub topic on which a WakuMessage will be broadcasted -func WithPubSubTopic(pubsubTopic string) Option { - return func(params *lightPushParameters) error { +func WithPubSubTopic(pubsubTopic string) RequestOption { + return func(params *lightPushRequestParameters) error { params.pubsubTopic = pubsubTopic return nil } } // WithDefaultPubsubTopic is used to indicate that the message should be broadcasted in the default pubsub topic -func WithDefaultPubsubTopic() Option { - return func(params *lightPushParameters) error { +func WithDefaultPubsubTopic() RequestOption { + return func(params *lightPushRequestParameters) error { params.pubsubTopic = relay.DefaultWakuTopic return nil } @@ -92,8 +106,8 @@ func WithDefaultPubsubTopic() Option { // WithRequestID is an option to set a specific request ID to be used when // publishing a message -func WithRequestID(requestID []byte) Option { - return func(params *lightPushParameters) error { +func WithRequestID(requestID []byte) RequestOption { + return func(params *lightPushRequestParameters) error { params.requestID = requestID return nil } @@ -101,16 +115,16 @@ func WithRequestID(requestID []byte) Option { // WithAutomaticRequestID is an option to automatically generate a request ID // when publishing a message -func WithAutomaticRequestID() Option { - return func(params *lightPushParameters) error { +func WithAutomaticRequestID() RequestOption { + return func(params *lightPushRequestParameters) error { params.requestID = protocol.GenerateRequestID() return nil } } // DefaultOptions are the default options to be used when using the lightpush protocol -func DefaultOptions(host host.Host) []Option { - return []Option{ +func DefaultOptions(host host.Host) []RequestOption { + return []RequestOption{ WithAutomaticRequestID(), WithAutomaticPeerSelection(), } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/metadata/pb/waku_metadata.pb.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/metadata/pb/waku_metadata.pb.go index 9aed0d57c..ddd30f601 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/metadata/pb/waku_metadata.pb.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/metadata/pb/waku_metadata.pb.go @@ -28,7 +28,13 @@ type WakuMetadataRequest struct { unknownFields protoimpl.UnknownFields ClusterId *uint32 `protobuf:"varint,1,opt,name=cluster_id,json=clusterId,proto3,oneof" json:"cluster_id,omitempty"` - Shards []uint32 `protobuf:"varint,2,rep,packed,name=shards,proto3" json:"shards,omitempty"` + Shards []uint32 `protobuf:"varint,3,rep,packed,name=shards,proto3" json:"shards,omitempty"` + // Starting from nwaku v0.26, if field 3 contains no data, it will attempt to + // decode this field first assuming it's a packed field, and if that fails, + // attempt to decode as an unpacked field + // + // Deprecated: Marked as deprecated in waku_metadata.proto. + ShardsDeprecated []uint32 `protobuf:"varint,2,rep,name=shards_deprecated,json=shardsDeprecated,proto3" json:"shards_deprecated,omitempty"` } func (x *WakuMetadataRequest) Reset() { @@ -77,13 +83,27 @@ func (x *WakuMetadataRequest) GetShards() []uint32 { return nil } +// Deprecated: Marked as deprecated in waku_metadata.proto. +func (x *WakuMetadataRequest) GetShardsDeprecated() []uint32 { + if x != nil { + return x.ShardsDeprecated + } + return nil +} + type WakuMetadataResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields ClusterId *uint32 `protobuf:"varint,1,opt,name=cluster_id,json=clusterId,proto3,oneof" json:"cluster_id,omitempty"` - Shards []uint32 `protobuf:"varint,2,rep,packed,name=shards,proto3" json:"shards,omitempty"` + Shards []uint32 `protobuf:"varint,3,rep,packed,name=shards,proto3" json:"shards,omitempty"` + // Starting from nwaku v0.26, if field 3 contains no data, it will attempt to + // decode this field first assuming it's a packed field, and if that fails, + // attempt to decode as an unpacked field + // + // Deprecated: Marked as deprecated in waku_metadata.proto. + ShardsDeprecated []uint32 `protobuf:"varint,2,rep,name=shards_deprecated,json=shardsDeprecated,proto3" json:"shards_deprecated,omitempty"` } func (x *WakuMetadataResponse) Reset() { @@ -132,25 +152,39 @@ func (x *WakuMetadataResponse) GetShards() []uint32 { return nil } +// Deprecated: Marked as deprecated in waku_metadata.proto. +func (x *WakuMetadataResponse) GetShardsDeprecated() []uint32 { + if x != nil { + return x.ShardsDeprecated + } + return nil +} + var File_waku_metadata_proto protoreflect.FileDescriptor var file_waku_metadata_proto_rawDesc = []byte{ 0x0a, 0x13, 0x77, 0x61, 0x6b, 0x75, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x77, 0x61, 0x6b, 0x75, 0x2e, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x22, 0x60, 0x0a, 0x13, 0x57, 0x61, 0x6b, 0x75, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, - 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x48, 0x00, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x88, - 0x01, 0x01, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0d, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x22, 0x61, 0x0a, 0x14, 0x57, 0x61, 0x6b, - 0x75, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x22, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, - 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x42, 0x0d, 0x0a, - 0x0b, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x31, 0x22, 0x93, 0x01, 0x0a, 0x13, 0x57, 0x61, 0x6b, 0x75, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x22, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, + 0x88, 0x01, 0x01, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x12, 0x31, 0x0a, 0x11, 0x73, + 0x68, 0x61, 0x72, 0x64, 0x73, 0x5f, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x42, 0x04, 0x10, 0x00, 0x18, 0x01, 0x52, 0x10, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x42, 0x0d, + 0x0a, 0x0b, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x22, 0x94, 0x01, + 0x0a, 0x14, 0x57, 0x61, 0x6b, 0x75, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x09, 0x63, 0x6c, + 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, + 0x61, 0x72, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x73, 0x68, 0x61, 0x72, + 0x64, 0x73, 0x12, 0x31, 0x0a, 0x11, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x5f, 0x64, 0x65, 0x70, + 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0d, 0x42, 0x04, 0x10, + 0x00, 0x18, 0x01, 0x52, 0x10, 0x73, 0x68, 0x61, 0x72, 0x64, 0x73, 0x44, 0x65, 0x70, 0x72, 0x65, + 0x63, 0x61, 0x74, 0x65, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/metadata/waku_metadata.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/metadata/waku_metadata.go index 71c0dcc09..228f4487f 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/metadata/waku_metadata.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/metadata/waku_metadata.go @@ -4,6 +4,8 @@ import ( "context" "errors" "math" + "sync" + "time" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/libp2p/go-libp2p/core/host" @@ -32,6 +34,9 @@ type WakuMetadata struct { clusterID uint16 localnode *enode.LocalNode + peerShardsMutex sync.RWMutex + peerShards map[peer.ID][]uint16 + log *zap.Logger } @@ -63,16 +68,22 @@ func (wakuM *WakuMetadata) Start(ctx context.Context) error { wakuM.ctx = ctx wakuM.cancel = cancel + wakuM.peerShards = make(map[peer.ID][]uint16) + + wakuM.h.SetStreamHandlerMatch(MetadataID_v1, protocol.PrefixTextMatch(string(MetadataID_v1)), wakuM.onRequest(ctx)) wakuM.h.Network().Notify(wakuM) - wakuM.h.SetStreamHandlerMatch(MetadataID_v1, protocol.PrefixTextMatch(string(MetadataID_v1)), wakuM.onRequest(ctx)) wakuM.log.Info("metadata protocol started") return nil } -func (wakuM *WakuMetadata) getClusterAndShards() (*uint32, []uint32, error) { - shard, err := enr.RelaySharding(wakuM.localnode.Node().Record()) +func (wakuM *WakuMetadata) RelayShard() (*protocol.RelayShards, error) { + return enr.RelaySharding(wakuM.localnode.Node().Record()) +} + +func (wakuM *WakuMetadata) ClusterAndShards() (*uint32, []uint32, error) { + shard, err := wakuM.RelayShard() if err != nil { return nil, nil, err } @@ -98,7 +109,7 @@ func (wakuM *WakuMetadata) Request(ctx context.Context, peerID peer.ID) (*protoc return nil, err } - clusterID, shards, err := wakuM.getClusterAndShards() + clusterID, shards, err := wakuM.ClusterAndShards() if err != nil { if err := stream.Reset(); err != nil { wakuM.log.Error("resetting connection", zap.Error(err)) @@ -109,6 +120,8 @@ func (wakuM *WakuMetadata) Request(ctx context.Context, peerID peer.ID) (*protoc request := &pb.WakuMetadataRequest{} request.ClusterId = clusterID request.Shards = shards + // TODO: remove with nwaku 0.28 deployment + request.ShardsDeprecated = shards // nolint: staticcheck writer := pbio.NewDelimitedWriter(stream) reader := pbio.NewDelimitedReader(stream, math.MaxInt32) @@ -140,8 +153,15 @@ func (wakuM *WakuMetadata) Request(ctx context.Context, peerID peer.ID) (*protoc rClusterID := uint16(*response.ClusterId) var rShardIDs []uint16 - for _, i := range response.Shards { - rShardIDs = append(rShardIDs, uint16(i)) + if len(response.Shards) != 0 { + for _, i := range response.Shards { + rShardIDs = append(rShardIDs, uint16(i)) + } + } else { + // TODO: remove with nwaku 0.28 deployment + for _, i := range response.ShardsDeprecated { // nolint: staticcheck + rShardIDs = append(rShardIDs, uint16(i)) + } } rs, err := protocol.NewRelayShards(rClusterID, rShardIDs...) @@ -171,12 +191,14 @@ func (wakuM *WakuMetadata) onRequest(ctx context.Context) func(network.Stream) { response := new(pb.WakuMetadataResponse) - clusterID, shards, err := wakuM.getClusterAndShards() + clusterID, shards, err := wakuM.ClusterAndShards() if err != nil { logger.Error("obtaining shard info", zap.Error(err)) } else { response.ClusterId = clusterID response.Shards = shards + // TODO: remove with nwaku 0.28 deployment + response.ShardsDeprecated = shards // nolint: staticcheck } err = writer.WriteMsg(response) @@ -241,11 +263,79 @@ func (wakuM *WakuMetadata) Connected(n network.Network, cc network.Conn) { if shard.ClusterID != wakuM.clusterID { wakuM.disconnectPeer(peerID, errors.New("different clusterID reported")) + return } + + // Store shards so they're used to verify if a relay peer supports the same shards we do + wakuM.peerShardsMutex.Lock() + defer wakuM.peerShardsMutex.Unlock() + wakuM.peerShards[peerID] = shard.ShardIDs }() } // Disconnected is called when a connection closed func (wakuM *WakuMetadata) Disconnected(n network.Network, cc network.Conn) { - // Do nothing + // We no longer need the shard info for that peer + wakuM.peerShardsMutex.Lock() + defer wakuM.peerShardsMutex.Unlock() + delete(wakuM.peerShards, cc.RemotePeer()) +} + +func (wakuM *WakuMetadata) GetPeerShards(ctx context.Context, peerID peer.ID) ([]uint16, error) { + // Already connected and we got the shard info, return immediatly + wakuM.peerShardsMutex.RLock() + shards, ok := wakuM.peerShards[peerID] + wakuM.peerShardsMutex.RUnlock() + if ok { + return shards, nil + } + + // Shard info pending. Let's wait + t := time.NewTicker(200 * time.Millisecond) + defer t.Stop() + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-t.C: + wakuM.peerShardsMutex.RLock() + shards, ok := wakuM.peerShards[peerID] + wakuM.peerShardsMutex.RUnlock() + if ok { + return shards, nil + } + } + } +} + +func (wakuM *WakuMetadata) disconnect(peerID peer.ID) { + wakuM.h.Peerstore().RemovePeer(peerID) + err := wakuM.h.Network().ClosePeer(peerID) + if err != nil { + wakuM.log.Error("disconnecting peer", logging.HostID("peerID", peerID), zap.Error(err)) + } +} + +func (wakuM *WakuMetadata) DisconnectPeerOnShardMismatch(ctx context.Context, peerID peer.ID) error { + peerShards, err := wakuM.GetPeerShards(ctx, peerID) + if err != nil { + wakuM.log.Error("could not obtain peer shards", zap.Error(err), logging.HostID("peerID", peerID)) + wakuM.disconnect(peerID) + return err + } + + rs, err := wakuM.RelayShard() + if err != nil { + wakuM.log.Error("could not obtain shards", zap.Error(err)) + wakuM.disconnect(peerID) + return err + } + + if !rs.ContainsAnyShard(rs.ClusterID, peerShards) { + wakuM.log.Info("shard mismatch", logging.HostID("peerID", peerID), zap.Uint16("clusterID", rs.ClusterID), zap.Uint16s("ourShardIDs", rs.ShardIDs), zap.Uint16s("theirShardIDs", peerShards)) + wakuM.disconnect(peerID) + return errors.New("shard mismatch") + } + + return nil } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/pb/utils.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/pb/utils.go index 70de2e0d5..6c306e2b6 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/pb/utils.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/pb/utils.go @@ -9,9 +9,28 @@ import ( "go.uber.org/zap/zapcore" ) +// MessageHash represents an unique identifier for a message within a pubsub topic +type MessageHash [32]byte + +func (h MessageHash) String() string { + return hexutil.Encode(h[:]) +} + +func (h MessageHash) Bytes() []byte { + return h[:] +} + +// ToMessageHash converts a byte slice into a MessageHash +func ToMessageHash(b []byte) MessageHash { + var result MessageHash + copy(result[:], b) + return result +} + // Hash calculates the hash of a waku message -func (msg *WakuMessage) Hash(pubsubTopic string) []byte { - return hash.SHA256([]byte(pubsubTopic), msg.Payload, []byte(msg.ContentTopic), msg.Meta, toBytes(msg.GetTimestamp())) +func (msg *WakuMessage) Hash(pubsubTopic string) MessageHash { + hash := hash.SHA256([]byte(pubsubTopic), msg.Payload, []byte(msg.ContentTopic), msg.Meta, toBytes(msg.GetTimestamp())) + return ToMessageHash(hash) } func toBytes(i int64) []byte { @@ -22,7 +41,7 @@ func toBytes(i int64) []byte { func (msg *WakuMessage) LogFields(pubsubTopic string) []zapcore.Field { return []zapcore.Field{ - zap.String("hash", hexutil.Encode(msg.Hash(pubsubTopic))), + zap.Stringer("hash", msg.Hash(pubsubTopic)), zap.String("pubsubTopic", pubsubTopic), zap.String("contentTopic", msg.ContentTopic), zap.Int64("timestamp", msg.GetTimestamp()), diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/client.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/client.go index 2d8564fb9..915ce75fd 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/client.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/client.go @@ -12,14 +12,15 @@ import ( "github.com/libp2p/go-msgio/pbio" "github.com/waku-org/go-waku/waku/v2/peermanager" "github.com/waku-org/go-waku/waku/v2/peerstore" + "github.com/waku-org/go-waku/waku/v2/protocol" wenr "github.com/waku-org/go-waku/waku/v2/protocol/enr" "github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/pb" "github.com/waku-org/go-waku/waku/v2/service" "go.uber.org/zap" ) -func (wakuPX *WakuPeerExchange) Request(ctx context.Context, numPeers int, opts ...PeerExchangeOption) error { - params := new(PeerExchangeParameters) +func (wakuPX *WakuPeerExchange) Request(ctx context.Context, numPeers int, opts ...RequestOption) error { + params := new(PeerExchangeRequestParameters) params.host = wakuPX.h params.log = wakuPX.log params.pm = wakuPX.pm @@ -43,11 +44,16 @@ func (wakuPX *WakuPeerExchange) Request(ctx context.Context, numPeers int, opts } if params.pm != nil && params.selectedPeer == "" { - var err error - params.selectedPeer, err = wakuPX.pm.SelectPeer( + pubsubTopics := []string{} + if params.clusterID != 0 { + pubsubTopics = append(pubsubTopics, + protocol.NewStaticShardingPubsubTopic(uint16(params.clusterID), uint16(params.shard)).String()) + } + selectedPeers, err := wakuPX.pm.SelectPeers( peermanager.PeerSelectionCriteria{ SelectionType: params.peerSelectionType, Proto: PeerExchangeID_v20alpha1, + PubsubTopics: pubsubTopics, SpecificPeers: params.preferredPeers, Ctx: ctx, }, @@ -55,6 +61,7 @@ func (wakuPX *WakuPeerExchange) Request(ctx context.Context, numPeers int, opts if err != nil { return err } + params.selectedPeer = selectedPeers[0] } if params.selectedPeer == "" { wakuPX.metrics.RecordError(dialFailure) @@ -93,10 +100,10 @@ func (wakuPX *WakuPeerExchange) Request(ctx context.Context, numPeers int, opts stream.Close() - return wakuPX.handleResponse(ctx, responseRPC.Response) + return wakuPX.handleResponse(ctx, responseRPC.Response, params) } -func (wakuPX *WakuPeerExchange) handleResponse(ctx context.Context, response *pb.PeerExchangeResponse) error { +func (wakuPX *WakuPeerExchange) handleResponse(ctx context.Context, response *pb.PeerExchangeResponse, params *PeerExchangeRequestParameters) error { var discoveredPeers []struct { addrInfo peer.AddrInfo enr *enode.Node @@ -112,6 +119,16 @@ func (wakuPX *WakuPeerExchange) handleResponse(ctx context.Context, response *pb return err } + if params.clusterID != 0 { + wakuPX.log.Debug("clusterID is non zero, filtering by shard") + rs, err := wenr.RelaySharding(enrRecord) + if err != nil || rs == nil || !rs.Contains(uint16(params.clusterID), uint16(params.shard)) { + wakuPX.log.Debug("peer doesn't matches filter", zap.Int("shard", params.shard)) + continue + } + wakuPX.log.Debug("peer matches filter", zap.Int("shard", params.shard)) + } + enodeRecord, err := enode.New(enode.ValidSchemes, enrRecord) if err != nil { wakuPX.log.Error("creating enode record", zap.Error(err)) diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/metrics.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/metrics.go index 1a98949a9..4d55db1d4 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/metrics.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/metrics.go @@ -39,6 +39,7 @@ var ( decodeRPCFailure metricsErrCategory = "decode_rpc_failure" pxFailure metricsErrCategory = "px_failure" dialFailure metricsErrCategory = "dial_failure" + rateLimitFailure metricsErrCategory = "ratelimit_failure" ) // RecordError increases the counter for different error types diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/protocol.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/protocol.go index 3f4a15739..a70b34f61 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/protocol.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/protocol.go @@ -20,6 +20,7 @@ import ( "github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/pb" "github.com/waku-org/go-waku/waku/v2/service" "go.uber.org/zap" + "golang.org/x/time/rate" ) // PeerExchangeID_v20alpha1 is the current Waku Peer Exchange protocol identifier @@ -47,12 +48,13 @@ type WakuPeerExchange struct { peerConnector PeerConnector enrCache *enrCache + limiter *rate.Limiter } // NewWakuPeerExchange returns a new instance of WakuPeerExchange struct // Takes an optional peermanager if WakuPeerExchange is being created along with WakuNode. // If using libp2p host, then pass peermanager as nil -func NewWakuPeerExchange(disc *discv5.DiscoveryV5, peerConnector PeerConnector, pm *peermanager.PeerManager, reg prometheus.Registerer, log *zap.Logger) (*WakuPeerExchange, error) { +func NewWakuPeerExchange(disc *discv5.DiscoveryV5, peerConnector PeerConnector, pm *peermanager.PeerManager, reg prometheus.Registerer, log *zap.Logger, opts ...Option) (*WakuPeerExchange, error) { wakuPX := new(WakuPeerExchange) wakuPX.disc = disc wakuPX.metrics = newMetrics(reg) @@ -62,6 +64,12 @@ func NewWakuPeerExchange(disc *discv5.DiscoveryV5, peerConnector PeerConnector, wakuPX.pm = pm wakuPX.CommonService = service.NewCommonService() + params := &PeerExchangeParameters{} + for _, opt := range opts { + opt(params) + } + + wakuPX.limiter = params.limiter return wakuPX, nil } @@ -87,6 +95,14 @@ func (wakuPX *WakuPeerExchange) start() error { func (wakuPX *WakuPeerExchange) onRequest() func(network.Stream) { return func(stream network.Stream) { logger := wakuPX.log.With(logging.HostID("peer", stream.Conn().RemotePeer())) + + if wakuPX.limiter != nil && !wakuPX.limiter.Allow() { + wakuPX.metrics.RecordError(rateLimitFailure) + wakuPX.log.Error("exceeds the rate limit") + // TODO: peer exchange protocol should contain an err field + return + } + requestRPC := &pb.PeerExchangeRPC{} reader := pbio.NewDelimitedReader(stream, math.MaxInt32) err := reader.ReadMsg(requestRPC) diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/waku_peer_exchange_option.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/waku_peer_exchange_option.go index 55702ad52..c08988091 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/waku_peer_exchange_option.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange/waku_peer_exchange_option.go @@ -8,9 +8,23 @@ import ( "github.com/multiformats/go-multiaddr" "github.com/waku-org/go-waku/waku/v2/peermanager" "go.uber.org/zap" + "golang.org/x/time/rate" ) type PeerExchangeParameters struct { + limiter *rate.Limiter +} + +type Option func(*PeerExchangeParameters) + +// WithRateLimiter is an option used to specify a rate limiter for requests received in lightpush protocol +func WithRateLimiter(r rate.Limit, b int) Option { + return func(params *PeerExchangeParameters) { + params.limiter = rate.NewLimiter(r, b) + } +} + +type PeerExchangeRequestParameters struct { host host.Host selectedPeer peer.ID peerAddr multiaddr.Multiaddr @@ -18,13 +32,15 @@ type PeerExchangeParameters struct { preferredPeers peer.IDSlice pm *peermanager.PeerManager log *zap.Logger + shard int + clusterID int } -type PeerExchangeOption func(*PeerExchangeParameters) error +type RequestOption func(*PeerExchangeRequestParameters) error // WithPeer is an option used to specify the peerID to fetch peers from -func WithPeer(p peer.ID) PeerExchangeOption { - return func(params *PeerExchangeParameters) error { +func WithPeer(p peer.ID) RequestOption { + return func(params *PeerExchangeRequestParameters) error { params.selectedPeer = p if params.peerAddr != nil { return errors.New("peerAddr and peerId options are mutually exclusive") @@ -36,8 +52,8 @@ func WithPeer(p peer.ID) PeerExchangeOption { // WithPeerAddr is an option used to specify a peerAddress to fetch peers from // This new peer will be added to peerStore. // Note that this option is mutually exclusive to WithPeerAddr, only one of them can be used. -func WithPeerAddr(pAddr multiaddr.Multiaddr) PeerExchangeOption { - return func(params *PeerExchangeParameters) error { +func WithPeerAddr(pAddr multiaddr.Multiaddr) RequestOption { + return func(params *PeerExchangeRequestParameters) error { params.peerAddr = pAddr if params.selectedPeer != "" { return errors.New("peerAddr and peerId options are mutually exclusive") @@ -51,8 +67,8 @@ func WithPeerAddr(pAddr multiaddr.Multiaddr) PeerExchangeOption { // from that list assuming it supports the chosen protocol, otherwise it will chose a peer // from the node peerstore // Note: this option can only be used if WakuNode is initialized which internally intializes the peerManager -func WithAutomaticPeerSelection(fromThesePeers ...peer.ID) PeerExchangeOption { - return func(params *PeerExchangeParameters) error { +func WithAutomaticPeerSelection(fromThesePeers ...peer.ID) RequestOption { + return func(params *PeerExchangeRequestParameters) error { params.peerSelectionType = peermanager.Automatic params.preferredPeers = fromThesePeers return nil @@ -63,8 +79,8 @@ func WithAutomaticPeerSelection(fromThesePeers ...peer.ID) PeerExchangeOption { // with the lowest ping. If a list of specific peers is passed, the peer will be chosen // from that list assuming it supports the chosen protocol, otherwise it will chose a peer // from the node peerstore -func WithFastestPeerSelection(fromThesePeers ...peer.ID) PeerExchangeOption { - return func(params *PeerExchangeParameters) error { +func WithFastestPeerSelection(fromThesePeers ...peer.ID) RequestOption { + return func(params *PeerExchangeRequestParameters) error { params.peerSelectionType = peermanager.LowestRTT params.preferredPeers = fromThesePeers return nil @@ -72,8 +88,17 @@ func WithFastestPeerSelection(fromThesePeers ...peer.ID) PeerExchangeOption { } // DefaultOptions are the default options to be used when using the lightpush protocol -func DefaultOptions(host host.Host) []PeerExchangeOption { - return []PeerExchangeOption{ +func DefaultOptions(host host.Host) []RequestOption { + return []RequestOption{ WithAutomaticPeerSelection(), } } + +// Use this if you want to filter peers by specific shards +func FilterByShard(clusterID int, shard int) RequestOption { + return func(params *PeerExchangeRequestParameters) error { + params.shard = shard + params.clusterID = clusterID + return nil + } +} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/config.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/config.go index 16799863e..f0f41f800 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/config.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/config.go @@ -40,6 +40,8 @@ func msgIDFn(pmsg *pubsub_pb.Message) string { return string(hash.SHA256(pmsg.Data)) } +const PeerPublishThreshold = -1000 + func (w *WakuRelay) setDefaultPeerScoreParams() { w.peerScoreParams = &pubsub.PeerScoreParams{ Topics: make(map[string]*pubsub.TopicScoreParams), @@ -59,10 +61,10 @@ func (w *WakuRelay) setDefaultPeerScoreParams() { BehaviourPenaltyDecay: 0.986, } w.peerScoreThresholds = &pubsub.PeerScoreThresholds{ - GossipThreshold: -100, // no gossip is sent to peers below this score - PublishThreshold: -1000, // no self-published msgs are sent to peers below this score - GraylistThreshold: -10000, // used to trigger disconnections + ignore peer if below this score - OpportunisticGraftThreshold: 0, // grafts better peers if the mesh median score drops below this. unset. + GossipThreshold: -100, // no gossip is sent to peers below this score + PublishThreshold: PeerPublishThreshold, // no self-published msgs are sent to peers below this score + GraylistThreshold: -10000, // used to trigger disconnections + ignore peer if below this score + OpportunisticGraftThreshold: 0, // grafts better peers if the mesh median score drops below this. unset. } } @@ -72,11 +74,11 @@ func (w *WakuRelay) defaultPubsubOptions() []pubsub.Option { cfg.PruneBackoff = time.Minute cfg.UnsubscribeBackoff = 5 * time.Second cfg.GossipFactor = 0.25 - cfg.D = waku_proto.GossipSubOptimalFullMeshSize + cfg.D = waku_proto.GossipSubDMin cfg.Dlo = 4 cfg.Dhi = 8 cfg.Dout = 3 - cfg.Dlazy = waku_proto.GossipSubOptimalFullMeshSize + cfg.Dlazy = waku_proto.GossipSubDMin cfg.HeartbeatInterval = time.Second cfg.HistoryLength = 6 cfg.HistoryGossip = 3 diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/metrics.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/metrics.go index b642c4970..4a10a0a9a 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/metrics.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/metrics.go @@ -61,7 +61,7 @@ func (m *metricsImpl) RecordMessage(envelope *waku_proto.Envelope) { messageSize.Observe(payloadSizeInKb) pubsubTopic := envelope.PubsubTopic() messages.WithLabelValues(pubsubTopic).Inc() - m.log.Debug("waku.relay received", zap.String("pubsubTopic", pubsubTopic), logging.HexBytes("hash", envelope.Hash()), zap.Int64("receivedTime", envelope.Index().ReceiverTime), zap.Int("payloadSizeBytes", payloadSizeInBytes)) + m.log.Debug("waku.relay received", zap.String("pubsubTopic", pubsubTopic), logging.Hash(envelope.Hash()), zap.Int64("receivedTime", envelope.Index().ReceiverTime), zap.Int("payloadSizeBytes", payloadSizeInBytes)) }() } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/subscription.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/subscription.go index a7bb6a362..fc1df406e 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/subscription.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/subscription.go @@ -30,7 +30,7 @@ func (s *Subscription) Submit(ctx context.Context, msg *protocol.Envelope) { // - if contentFilter doesn't have a contentTopic // - if contentFilter has contentTopics and it matches with message if !s.noConsume && (len(s.contentFilter.ContentTopicsList()) == 0 || - (len(s.contentFilter.ContentTopicsList()) > 0 && slices.Contains[string](s.contentFilter.ContentTopicsList(), msg.Message().ContentTopic))) { + (len(s.contentFilter.ContentTopicsList()) > 0 && slices.Contains(s.contentFilter.ContentTopicsList(), msg.Message().ContentTopic))) { select { case <-ctx.Done(): return diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/waku_relay.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/waku_relay.go index 060f161c5..d2ec331bf 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/waku_relay.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/relay/waku_relay.go @@ -16,6 +16,7 @@ import ( pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/waku-org/go-waku/logging" + wps "github.com/waku-org/go-waku/waku/v2/peerstore" waku_proto "github.com/waku-org/go-waku/waku/v2/protocol" "github.com/waku-org/go-waku/waku/v2/protocol/pb" "github.com/waku-org/go-waku/waku/v2/service" @@ -114,6 +115,7 @@ func (w *WakuRelay) peerScoreInspector(peerScoresSnapshots map[peer.ID]*pubsub.P w.log.Error("could not disconnect peer", logging.HostID("peer", pid), zap.Error(err)) } } + _ = w.host.Peerstore().(wps.WakuPeerstore).SetScore(pid, snap.Score) } } @@ -253,19 +255,19 @@ func (w *WakuRelay) subscribeToPubsubTopic(topic string) (*pubsubTopicSubscripti // Publish is used to broadcast a WakuMessage to a pubsub topic. The pubsubTopic is derived from contentTopic // specified in the message via autosharding. To publish to a specific pubsubTopic, the `WithPubSubTopic` option should // be provided -func (w *WakuRelay) Publish(ctx context.Context, message *pb.WakuMessage, opts ...PublishOption) ([]byte, error) { +func (w *WakuRelay) Publish(ctx context.Context, message *pb.WakuMessage, opts ...PublishOption) (pb.MessageHash, error) { // Publish a `WakuMessage` to a PubSub topic. if w.pubsub == nil { - return nil, errors.New("PubSub hasn't been set") + return pb.MessageHash{}, errors.New("PubSub hasn't been set") } if message == nil { - return nil, errors.New("message can't be null") + return pb.MessageHash{}, errors.New("message can't be null") } err := message.Validate() if err != nil { - return nil, err + return pb.MessageHash{}, err } params := new(publishParameters) @@ -276,39 +278,39 @@ func (w *WakuRelay) Publish(ctx context.Context, message *pb.WakuMessage, opts . if params.pubsubTopic == "" { params.pubsubTopic, err = waku_proto.GetPubSubTopicFromContentTopic(message.ContentTopic) if err != nil { - return nil, err + return pb.MessageHash{}, err } } if !w.EnoughPeersToPublishToTopic(params.pubsubTopic) { - return nil, errors.New("not enough peers to publish") + return pb.MessageHash{}, errors.New("not enough peers to publish") } - w.topicsMutex.RLock() - defer w.topicsMutex.RUnlock() + w.topicsMutex.Lock() + defer w.topicsMutex.Unlock() pubSubTopic, err := w.upsertTopic(params.pubsubTopic) if err != nil { - return nil, err + return pb.MessageHash{}, err } out, err := proto.Marshal(message) if err != nil { - return nil, err + return pb.MessageHash{}, err } if len(out) > w.relayParams.maxMsgSizeBytes { - return nil, errors.New("message size exceeds gossipsub max message size") + return pb.MessageHash{}, errors.New("message size exceeds gossipsub max message size") } err = pubSubTopic.Publish(ctx, out) if err != nil { - return nil, err + return pb.MessageHash{}, err } hash := message.Hash(params.pubsubTopic) - w.logMessages.Debug("waku.relay published", zap.String("pubsubTopic", params.pubsubTopic), logging.HexBytes("hash", hash), zap.Int64("publishTime", w.timesource.Now().UnixNano()), zap.Int("payloadSizeBytes", len(message.Payload))) + w.logMessages.Debug("waku.relay published", zap.String("pubsubTopic", params.pubsubTopic), logging.Hash(hash), zap.Int64("publishTime", w.timesource.Now().UnixNano()), zap.Int("payloadSizeBytes", len(message.Payload))) return hash, nil } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go index 419709840..36a0807c4 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/rln/group_manager/dynamic/dynamic.go @@ -73,11 +73,13 @@ func (gm *DynamicGroupManager) handler(events []*contracts.RLNMemberRegistered, } eventsPerBlock = append(eventsPerBlock, event) toInsertTable.Set(event.Raw.BlockNumber, eventsPerBlock) - - if event.Raw.BlockNumber > lastBlockProcessed { - lastBlockProcessed = event.Raw.BlockNumber - } } + if event.Raw.BlockNumber > lastBlockProcessed { + lastBlockProcessed = event.Raw.BlockNumber + } + } + if len(events) == 0 { + lastBlockProcessed = latestProcessBlock } err := gm.RemoveMembers(toRemoveTable) diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/rln/waku_rln_relay.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/rln/waku_rln_relay.go index ad7bc368c..29d5d65ac 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/rln/waku_rln_relay.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/rln/waku_rln_relay.go @@ -108,8 +108,9 @@ func (rlnRelay *WakuRLNRelay) ValidateMessage(msg *pb.WakuMessage, optionalTime msgProof, err := BytesToRateLimitProof(msg.RateLimitProof) if err != nil { - rlnRelay.log.Debug("invalid message: could not extract proof", zap.Error(err)) + rlnRelay.log.Debug("invalid message: could not extract proof") rlnRelay.metrics.RecordInvalidMessage(proofExtractionErr) + return validationError, err } if msgProof == nil { @@ -146,9 +147,9 @@ func (rlnRelay *WakuRLNRelay) ValidateMessage(msg *pb.WakuMessage, optionalTime start := time.Now() valid, err := rlnRelay.verifyProof(msg, msgProof) if err != nil { - rlnRelay.log.Debug("could not verify proof", zap.Error(err)) + rlnRelay.log.Debug("could not verify proof") rlnRelay.metrics.RecordError(proofVerificationErr) - return invalidMessage, nil + return validationError, err } rlnRelay.metrics.RecordProofVerification(time.Since(start)) @@ -234,7 +235,7 @@ func (rlnRelay *WakuRLNRelay) Validator( hash := msg.Hash(topic) log := rlnRelay.log.With( - logging.HexBytes("hash", hash), + logging.HexBytes("hash", hash[:]), zap.String("pubsubTopic", topic), zap.String("contentTopic", msg.ContentTopic), ) @@ -270,7 +271,7 @@ func (rlnRelay *WakuRLNRelay) Validator( return false default: - log.Debug("unhandled validation result", zap.Int("validationResult", int(validationRes))) + log.Error("unhandled validation result", zap.Int("validationResult", int(validationRes))) return false } } diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/shard.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/shard.go index 66ec5fdcb..9ef30840f 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/shard.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/shard.go @@ -4,7 +4,6 @@ import ( "encoding/binary" "errors" "fmt" - "math" "strings" "github.com/waku-org/go-waku/waku/v2/hash" @@ -20,7 +19,6 @@ const ClusterIndex = 1 const GenerationZeroShardsCount = 8 var ( - ErrTooManyShards = errors.New("too many shards") ErrInvalidShard = errors.New("invalid shard") ErrInvalidShardCount = errors.New("invalid shard count") ErrExpected130Bytes = errors.New("invalid data: expected 130 bytes") @@ -32,10 +30,6 @@ type RelayShards struct { } func NewRelayShards(clusterID uint16, shardIDs ...uint16) (RelayShards, error) { - if len(shardIDs) > math.MaxUint8 { - return RelayShards{}, ErrTooManyShards - } - shardIDSet := make(map[uint16]struct{}) for _, index := range shardIDs { if index > MaxShardIndex { @@ -64,21 +58,27 @@ func (rs RelayShards) Topics() []WakuPubSubTopic { return result } -func (rs RelayShards) Contains(cluster uint16, index uint16) bool { +func (rs RelayShards) ContainsAnyShard(cluster uint16, indexes []uint16) bool { if rs.ClusterID != cluster { return false } found := false - for _, idx := range rs.ShardIDs { - if idx == index { - found = true + for _, rsIdx := range rs.ShardIDs { + for _, idx := range indexes { + if rsIdx == idx { + return true + } } } return found } +func (rs RelayShards) Contains(cluster uint16, index uint16) bool { + return rs.ContainsAnyShard(cluster, []uint16{index}) +} + func (rs RelayShards) ContainsShardPubsubTopic(topic WakuPubSubTopic) bool { if shardedTopic, err := ToShardPubsubTopic(topic); err != nil { return false @@ -136,10 +136,6 @@ func (rs RelayShards) ContainsTopic(topic string) bool { } func (rs RelayShards) ShardList() ([]byte, error) { - if len(rs.ShardIDs) > math.MaxUint8 { - return nil, ErrTooManyShards - } - var result []byte result = binary.BigEndian.AppendUint16(result, rs.ClusterID) @@ -268,3 +264,11 @@ func GeneratePubsubToContentTopicMap(pubsubTopic string, contentTopics []string) } return pubSubTopicMap, nil } + +func ShardsToTopics(clusterId int, shards []int) []string { + pubsubTopics := make([]string, len(shards)) + for i, shard := range shards { + pubsubTopics[i] = NewStaticShardingPubsubTopic(uint16(clusterId), uint16(shard)).String() + } + return pubsubTopics +} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/client.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/client.go new file mode 100644 index 000000000..9eb3da40a --- /dev/null +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/client.go @@ -0,0 +1,290 @@ +package store + +import ( + "context" + "encoding/hex" + "errors" + "fmt" + "math" + + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/peer" + libp2pProtocol "github.com/libp2p/go-libp2p/core/protocol" + "github.com/libp2p/go-msgio/pbio" + "github.com/waku-org/go-waku/logging" + "github.com/waku-org/go-waku/waku/v2/peermanager" + "github.com/waku-org/go-waku/waku/v2/peerstore" + "github.com/waku-org/go-waku/waku/v2/protocol" + wpb "github.com/waku-org/go-waku/waku/v2/protocol/pb" + "github.com/waku-org/go-waku/waku/v2/protocol/store/pb" + "github.com/waku-org/go-waku/waku/v2/timesource" + "go.uber.org/zap" + "google.golang.org/protobuf/proto" +) + +// StoreQueryID_v300 is the Store protocol v3 identifier +const StoreQueryID_v300 = libp2pProtocol.ID("/vac/waku/store-query/3.0.0") +const StoreENRField = uint8(1 << 1) + +// MaxPageSize is the maximum number of waku messages to return per page +const MaxPageSize = 100 + +// DefaultPageSize is the default number of waku messages per page +const DefaultPageSize = 20 + +const ok = uint32(200) + +var ( + + // ErrNoPeersAvailable is returned when there are no store peers in the peer store + // that could be used to retrieve message history + ErrNoPeersAvailable = errors.New("no suitable remote peers") + ErrMustSelectPeer = errors.New("a peer ID or multiaddress is required when checking for message hashes") +) + +// StoreError represents an error code returned by a storenode +type StoreError struct { + Code int + Message string +} + +// NewStoreError creates a new instance of StoreError +func NewStoreError(code int, message string) StoreError { + return StoreError{ + Code: code, + Message: message, + } +} + +const errorStringFmt = "%d - %s" + +// Error returns a string with the error message +func (e *StoreError) Error() string { + return fmt.Sprintf(errorStringFmt, e.Code, e.Message) +} + +// WakuStore represents an instance of a store client +type WakuStore struct { + h host.Host + timesource timesource.Timesource + log *zap.Logger + pm *peermanager.PeerManager +} + +// NewWakuStore is used to instantiate a StoreV3 client +func NewWakuStore(pm *peermanager.PeerManager, timesource timesource.Timesource, log *zap.Logger) *WakuStore { + s := new(WakuStore) + s.log = log.Named("store-client") + s.timesource = timesource + s.pm = pm + + if pm != nil { + pm.RegisterWakuProtocol(StoreQueryID_v300, StoreENRField) + } + + return s +} + +// Sets the host to be able to mount or consume a protocol +func (s *WakuStore) SetHost(h host.Host) { + s.h = h +} + +// Request is used to send a store query. This function requires understanding how to prepare a store query +// and most of the time you can use `Query`, `QueryByHash` and `Exists` instead, as they provide +// a simpler API +func (s *WakuStore) Request(ctx context.Context, criteria Criteria, opts ...RequestOption) (*Result, error) { + params := new(Parameters) + + optList := DefaultOptions() + optList = append(optList, opts...) + for _, opt := range optList { + err := opt(params) + if err != nil { + return nil, err + } + } + + filterCriteria, isFilterCriteria := criteria.(FilterCriteria) + + var pubsubTopics []string + if isFilterCriteria { + pubsubTopics = append(pubsubTopics, filterCriteria.PubsubTopic) + } + + //Add Peer to peerstore. + if s.pm != nil && params.peerAddr != nil { + pData, err := s.pm.AddPeer(params.peerAddr, peerstore.Static, pubsubTopics, StoreQueryID_v300) + if err != nil { + return nil, err + } + s.pm.Connect(pData) + params.selectedPeer = pData.AddrInfo.ID + } + + if s.pm != nil && params.selectedPeer == "" { + if isFilterCriteria { + selectedPeers, err := s.pm.SelectPeers( + peermanager.PeerSelectionCriteria{ + SelectionType: params.peerSelectionType, + Proto: StoreQueryID_v300, + PubsubTopics: []string{filterCriteria.PubsubTopic}, + SpecificPeers: params.preferredPeers, + Ctx: ctx, + }, + ) + if err != nil { + return nil, err + } + params.selectedPeer = selectedPeers[0] + } else { + return nil, ErrMustSelectPeer + } + } + + if params.selectedPeer == "" { + return nil, ErrNoPeersAvailable + } + + pageLimit := params.pageLimit + if pageLimit == 0 { + pageLimit = DefaultPageSize + } else if pageLimit > uint64(MaxPageSize) { + pageLimit = MaxPageSize + } + + storeRequest := &pb.StoreQueryRequest{ + RequestId: hex.EncodeToString(params.requestID), + IncludeData: params.includeData, + PaginationForward: params.forward, + PaginationLimit: proto.Uint64(pageLimit), + } + + criteria.PopulateStoreRequest(storeRequest) + + if params.cursor != nil { + storeRequest.PaginationCursor = params.cursor + } + + err := storeRequest.Validate() + if err != nil { + return nil, err + } + + response, err := s.queryFrom(ctx, storeRequest, params.selectedPeer) + if err != nil { + return nil, err + } + + result := &Result{ + store: s, + messages: response.Messages, + storeRequest: storeRequest, + peerID: params.selectedPeer, + cursor: response.PaginationCursor, + } + + return result, nil +} + +// Query retrieves all the messages that match a criteria. Use the options to indicate whether to return the message themselves or not. +func (s *WakuStore) Query(ctx context.Context, criteria FilterCriteria, opts ...RequestOption) (*Result, error) { + return s.Request(ctx, criteria, opts...) +} + +// Query retrieves all the messages with specific message hashes +func (s *WakuStore) QueryByHash(ctx context.Context, messageHashes []wpb.MessageHash, opts ...RequestOption) (*Result, error) { + return s.Request(ctx, MessageHashCriteria{messageHashes}, opts...) +} + +// Exists is an utility function to determine if a message exists. For checking the presence of more than one message, use QueryByHash +// and pass the option WithReturnValues(false). You will have to iterate the results and check whether the full list of messages contains +// the list of messages to verify +func (s *WakuStore) Exists(ctx context.Context, messageHash wpb.MessageHash, opts ...RequestOption) (bool, error) { + opts = append(opts, IncludeData(false)) + result, err := s.Request(ctx, MessageHashCriteria{MessageHashes: []wpb.MessageHash{messageHash}}, opts...) + if err != nil { + return false, err + } + + return len(result.messages) != 0, nil +} + +func (s *WakuStore) next(ctx context.Context, r *Result) (*Result, error) { + if r.IsComplete() { + return &Result{ + store: s, + started: true, + messages: []*pb.WakuMessageKeyValue{}, + cursor: nil, + storeRequest: r.storeRequest, + peerID: r.PeerID(), + }, nil + } + + storeRequest := proto.Clone(r.storeRequest).(*pb.StoreQueryRequest) + storeRequest.RequestId = hex.EncodeToString(protocol.GenerateRequestID()) + storeRequest.PaginationCursor = r.Cursor() + + response, err := s.queryFrom(ctx, storeRequest, r.PeerID()) + if err != nil { + return nil, err + } + + result := &Result{ + started: true, + store: s, + messages: response.Messages, + storeRequest: storeRequest, + peerID: r.PeerID(), + cursor: response.PaginationCursor, + } + + return result, nil + +} + +func (s *WakuStore) queryFrom(ctx context.Context, storeRequest *pb.StoreQueryRequest, selectedPeer peer.ID) (*pb.StoreQueryResponse, error) { + logger := s.log.With(logging.HostID("peer", selectedPeer)) + logger.Info("sending store request") + + stream, err := s.h.NewStream(ctx, selectedPeer, StoreQueryID_v300) + if err != nil { + logger.Error("creating stream to peer", zap.Error(err)) + return nil, err + } + + writer := pbio.NewDelimitedWriter(stream) + reader := pbio.NewDelimitedReader(stream, math.MaxInt32) + + err = writer.WriteMsg(storeRequest) + if err != nil { + logger.Error("writing request", zap.Error(err)) + if err := stream.Reset(); err != nil { + s.log.Error("resetting connection", zap.Error(err)) + } + return nil, err + } + + storeResponse := &pb.StoreQueryResponse{RequestId: storeRequest.RequestId} + err = reader.ReadMsg(storeResponse) + if err != nil { + logger.Error("reading response", zap.Error(err)) + if err := stream.Reset(); err != nil { + s.log.Error("resetting connection", zap.Error(err)) + } + return nil, err + } + + stream.Close() + + if err := storeResponse.Validate(storeRequest.RequestId); err != nil { + return nil, err + } + + if storeResponse.GetStatusCode() != ok { + err := NewStoreError(int(storeResponse.GetStatusCode()), storeResponse.GetStatusDesc()) + return nil, &err + } + return storeResponse, nil +} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/criteria.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/criteria.go new file mode 100644 index 000000000..f62de764c --- /dev/null +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/criteria.go @@ -0,0 +1,36 @@ +package store + +import ( + "github.com/waku-org/go-waku/waku/v2/protocol" + wpb "github.com/waku-org/go-waku/waku/v2/protocol/pb" + "github.com/waku-org/go-waku/waku/v2/protocol/store/pb" + "google.golang.org/protobuf/proto" +) + +type Criteria interface { + PopulateStoreRequest(request *pb.StoreQueryRequest) +} + +type FilterCriteria struct { + protocol.ContentFilter + TimeStart *int64 + TimeEnd *int64 +} + +func (f FilterCriteria) PopulateStoreRequest(request *pb.StoreQueryRequest) { + request.ContentTopics = f.ContentTopicsList() + request.PubsubTopic = proto.String(f.PubsubTopic) + request.TimeStart = f.TimeStart + request.TimeEnd = f.TimeEnd +} + +type MessageHashCriteria struct { + MessageHashes []wpb.MessageHash +} + +func (m MessageHashCriteria) PopulateStoreRequest(request *pb.StoreQueryRequest) { + request.MessageHashes = make([][]byte, len(m.MessageHashes)) + for i := range m.MessageHashes { + request.MessageHashes[i] = m.MessageHashes[i][:] + } +} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/options.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/options.go new file mode 100644 index 000000000..b38afd53a --- /dev/null +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/options.go @@ -0,0 +1,126 @@ +package store + +import ( + "errors" + + "github.com/libp2p/go-libp2p/core/peer" + "github.com/multiformats/go-multiaddr" + "github.com/waku-org/go-waku/waku/v2/peermanager" + "github.com/waku-org/go-waku/waku/v2/protocol" +) + +type Parameters struct { + selectedPeer peer.ID + peerAddr multiaddr.Multiaddr + peerSelectionType peermanager.PeerSelection + preferredPeers peer.IDSlice + requestID []byte + cursor []byte + pageLimit uint64 + forward bool + includeData bool +} + +type RequestOption func(*Parameters) error + +// WithPeer is an option used to specify the peerID to request the message history. +// Note that this option is mutually exclusive to WithPeerAddr, only one of them can be used. +func WithPeer(p peer.ID) RequestOption { + return func(params *Parameters) error { + params.selectedPeer = p + if params.peerAddr != nil { + return errors.New("WithPeer and WithPeerAddr options are mutually exclusive") + } + return nil + } +} + +// WithPeerAddr is an option used to specify a peerAddress to request the message history. +// This new peer will be added to peerStore. +// Note that this option is mutually exclusive to WithPeerAddr, only one of them can be used. +func WithPeerAddr(pAddr multiaddr.Multiaddr) RequestOption { + return func(params *Parameters) error { + params.peerAddr = pAddr + if params.selectedPeer != "" { + return errors.New("WithPeerAddr and WithPeer options are mutually exclusive") + } + return nil + } +} + +// WithAutomaticPeerSelection is an option used to randomly select a peer from the peer store +// to request the message history. If a list of specific peers is passed, the peer will be chosen +// from that list assuming it supports the chosen protocol, otherwise it will chose a peer +// from the node peerstore +// Note: This option is avaiable only with peerManager +func WithAutomaticPeerSelection(fromThesePeers ...peer.ID) RequestOption { + return func(params *Parameters) error { + params.peerSelectionType = peermanager.Automatic + params.preferredPeers = fromThesePeers + return nil + } +} + +// WithFastestPeerSelection is an option used to select a peer from the peer store +// with the lowest ping. If a list of specific peers is passed, the peer will be chosen +// from that list assuming it supports the chosen protocol, otherwise it will chose a peer +// from the node peerstore +// Note: This option is avaiable only with peerManager +func WithFastestPeerSelection(fromThesePeers ...peer.ID) RequestOption { + return func(params *Parameters) error { + params.peerSelectionType = peermanager.LowestRTT + return nil + } +} + +// WithRequestID is an option to set a specific request ID to be used when +// creating a store request +func WithRequestID(requestID []byte) RequestOption { + return func(params *Parameters) error { + params.requestID = requestID + return nil + } +} + +// WithAutomaticRequestID is an option to automatically generate a request ID +// when creating a store request +func WithAutomaticRequestID() RequestOption { + return func(params *Parameters) error { + params.requestID = protocol.GenerateRequestID() + return nil + } +} + +func WithCursor(cursor []byte) RequestOption { + return func(params *Parameters) error { + params.cursor = cursor + return nil + } +} + +// WithPaging is an option used to specify the order and maximum number of records to return +func WithPaging(forward bool, limit uint64) RequestOption { + return func(params *Parameters) error { + params.forward = forward + params.pageLimit = limit + return nil + } +} + +// IncludeData is an option used to indicate whether you want to return the message content or not +func IncludeData(v bool) RequestOption { + return func(params *Parameters) error { + params.includeData = v + return nil + } +} + +// Default options to be used when querying a store node for results +func DefaultOptions() []RequestOption { + return []RequestOption{ + WithAutomaticRequestID(), + WithAutomaticPeerSelection(), + WithPaging(true, DefaultPageSize), + IncludeData(true), + } +} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/generate.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/generate.go index 31462bdac..c7e957821 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/generate.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/generate.go @@ -1,3 +1,3 @@ package pb -//go:generate protoc -I./../../waku-proto/waku/store/v2beta4//. -I./../../waku-proto/ --go_opt=paths=source_relative --go_opt=Mstore.proto=github.com/waku-org/go-waku/waku/v2/protocol/store/pb --go_opt=Mwaku/message/v1/message.proto=github.com/waku-org/go-waku/waku/v2/protocol/pb --go_out=. ./../../waku-proto/waku/store/v2beta4/store.proto +//go:generate protoc -I. -I./../../waku-proto/ --go_opt=paths=source_relative --go_opt=Mstorev3.proto=github.com/waku-org/go-waku/waku/v2/protocol/store/pb --go_opt=Mwaku/message/v1/message.proto=github.com/waku-org/go-waku/waku/v2/protocol/pb --go_out=. ./storev3.proto diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/hash.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/hash.go new file mode 100644 index 000000000..600b5cafb --- /dev/null +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/hash.go @@ -0,0 +1,9 @@ +package pb + +import ( + pb "github.com/waku-org/go-waku/waku/v2/protocol/pb" +) + +func (x *WakuMessageKeyValue) WakuMessageHash() pb.MessageHash { + return pb.ToMessageHash(x.MessageHash) +} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/storev3.pb.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/storev3.pb.go new file mode 100644 index 000000000..10f90e0b1 --- /dev/null +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/storev3.pb.go @@ -0,0 +1,445 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v3.21.12 +// source: storev3.proto + +// Protocol identifier: /vac/waku/store/3.0.0 + +package pb + +import ( + pb "github.com/waku-org/go-waku/waku/v2/protocol/pb" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type WakuMessageKeyValue struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MessageHash []byte `protobuf:"bytes,1,opt,name=message_hash,json=messageHash,proto3,oneof" json:"message_hash,omitempty"` // Globally unique key for a Waku Message + Message *pb.WakuMessage `protobuf:"bytes,2,opt,name=message,proto3,oneof" json:"message,omitempty"` // Full message content as value +} + +func (x *WakuMessageKeyValue) Reset() { + *x = WakuMessageKeyValue{} + if protoimpl.UnsafeEnabled { + mi := &file_storev3_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WakuMessageKeyValue) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WakuMessageKeyValue) ProtoMessage() {} + +func (x *WakuMessageKeyValue) ProtoReflect() protoreflect.Message { + mi := &file_storev3_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WakuMessageKeyValue.ProtoReflect.Descriptor instead. +func (*WakuMessageKeyValue) Descriptor() ([]byte, []int) { + return file_storev3_proto_rawDescGZIP(), []int{0} +} + +func (x *WakuMessageKeyValue) GetMessageHash() []byte { + if x != nil { + return x.MessageHash + } + return nil +} + +func (x *WakuMessageKeyValue) GetMessage() *pb.WakuMessage { + if x != nil { + return x.Message + } + return nil +} + +type StoreQueryRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + IncludeData bool `protobuf:"varint,2,opt,name=include_data,json=includeData,proto3" json:"include_data,omitempty"` // Response should include full message content + // Filter criteria for content-filtered queries + PubsubTopic *string `protobuf:"bytes,10,opt,name=pubsub_topic,json=pubsubTopic,proto3,oneof" json:"pubsub_topic,omitempty"` + ContentTopics []string `protobuf:"bytes,11,rep,name=content_topics,json=contentTopics,proto3" json:"content_topics,omitempty"` + TimeStart *int64 `protobuf:"zigzag64,12,opt,name=time_start,json=timeStart,proto3,oneof" json:"time_start,omitempty"` + TimeEnd *int64 `protobuf:"zigzag64,13,opt,name=time_end,json=timeEnd,proto3,oneof" json:"time_end,omitempty"` + // List of key criteria for lookup queries + MessageHashes [][]byte `protobuf:"bytes,20,rep,name=message_hashes,json=messageHashes,proto3" json:"message_hashes,omitempty"` // Message hashes (keys) to lookup + // Pagination info. 50 Reserved + PaginationCursor []byte `protobuf:"bytes,51,opt,name=pagination_cursor,json=paginationCursor,proto3,oneof" json:"pagination_cursor,omitempty"` // Message hash (key) from where to start query (exclusive) + PaginationForward bool `protobuf:"varint,52,opt,name=pagination_forward,json=paginationForward,proto3" json:"pagination_forward,omitempty"` + PaginationLimit *uint64 `protobuf:"varint,53,opt,name=pagination_limit,json=paginationLimit,proto3,oneof" json:"pagination_limit,omitempty"` +} + +func (x *StoreQueryRequest) Reset() { + *x = StoreQueryRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_storev3_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StoreQueryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StoreQueryRequest) ProtoMessage() {} + +func (x *StoreQueryRequest) ProtoReflect() protoreflect.Message { + mi := &file_storev3_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StoreQueryRequest.ProtoReflect.Descriptor instead. +func (*StoreQueryRequest) Descriptor() ([]byte, []int) { + return file_storev3_proto_rawDescGZIP(), []int{1} +} + +func (x *StoreQueryRequest) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *StoreQueryRequest) GetIncludeData() bool { + if x != nil { + return x.IncludeData + } + return false +} + +func (x *StoreQueryRequest) GetPubsubTopic() string { + if x != nil && x.PubsubTopic != nil { + return *x.PubsubTopic + } + return "" +} + +func (x *StoreQueryRequest) GetContentTopics() []string { + if x != nil { + return x.ContentTopics + } + return nil +} + +func (x *StoreQueryRequest) GetTimeStart() int64 { + if x != nil && x.TimeStart != nil { + return *x.TimeStart + } + return 0 +} + +func (x *StoreQueryRequest) GetTimeEnd() int64 { + if x != nil && x.TimeEnd != nil { + return *x.TimeEnd + } + return 0 +} + +func (x *StoreQueryRequest) GetMessageHashes() [][]byte { + if x != nil { + return x.MessageHashes + } + return nil +} + +func (x *StoreQueryRequest) GetPaginationCursor() []byte { + if x != nil { + return x.PaginationCursor + } + return nil +} + +func (x *StoreQueryRequest) GetPaginationForward() bool { + if x != nil { + return x.PaginationForward + } + return false +} + +func (x *StoreQueryRequest) GetPaginationLimit() uint64 { + if x != nil && x.PaginationLimit != nil { + return *x.PaginationLimit + } + return 0 +} + +type StoreQueryResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + StatusCode *uint32 `protobuf:"varint,10,opt,name=status_code,json=statusCode,proto3,oneof" json:"status_code,omitempty"` + StatusDesc *string `protobuf:"bytes,11,opt,name=status_desc,json=statusDesc,proto3,oneof" json:"status_desc,omitempty"` + Messages []*WakuMessageKeyValue `protobuf:"bytes,20,rep,name=messages,proto3" json:"messages,omitempty"` + PaginationCursor []byte `protobuf:"bytes,51,opt,name=pagination_cursor,json=paginationCursor,proto3,oneof" json:"pagination_cursor,omitempty"` +} + +func (x *StoreQueryResponse) Reset() { + *x = StoreQueryResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_storev3_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StoreQueryResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StoreQueryResponse) ProtoMessage() {} + +func (x *StoreQueryResponse) ProtoReflect() protoreflect.Message { + mi := &file_storev3_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StoreQueryResponse.ProtoReflect.Descriptor instead. +func (*StoreQueryResponse) Descriptor() ([]byte, []int) { + return file_storev3_proto_rawDescGZIP(), []int{2} +} + +func (x *StoreQueryResponse) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *StoreQueryResponse) GetStatusCode() uint32 { + if x != nil && x.StatusCode != nil { + return *x.StatusCode + } + return 0 +} + +func (x *StoreQueryResponse) GetStatusDesc() string { + if x != nil && x.StatusDesc != nil { + return *x.StatusDesc + } + return "" +} + +func (x *StoreQueryResponse) GetMessages() []*WakuMessageKeyValue { + if x != nil { + return x.Messages + } + return nil +} + +func (x *StoreQueryResponse) GetPaginationCursor() []byte { + if x != nil { + return x.PaginationCursor + } + return nil +} + +var File_storev3_proto protoreflect.FileDescriptor + +var file_storev3_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x76, 0x33, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x0d, 0x77, 0x61, 0x6b, 0x75, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x33, 0x1a, 0x1d, + 0x77, 0x61, 0x6b, 0x75, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2f, 0x76, 0x31, 0x2f, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x97, 0x01, + 0x0a, 0x13, 0x57, 0x61, 0x6b, 0x75, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x26, 0x0a, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0b, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x61, 0x73, 0x68, 0x88, 0x01, 0x01, 0x12, 0x3b, 0x0a, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, + 0x2e, 0x77, 0x61, 0x6b, 0x75, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x57, 0x61, 0x6b, 0x75, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x01, 0x52, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x42, 0x0a, 0x0a, 0x08, 0x5f, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xf8, 0x03, 0x0a, 0x11, 0x53, 0x74, 0x6f, 0x72, + 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, + 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0b, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x26, 0x0a, 0x0c, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x54, + 0x6f, 0x70, 0x69, 0x63, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x73, 0x12, 0x22, + 0x0a, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x12, 0x48, 0x01, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x88, + 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x0d, + 0x20, 0x01, 0x28, 0x12, 0x48, 0x02, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x45, 0x6e, 0x64, 0x88, + 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x68, 0x61, + 0x73, 0x68, 0x65, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0d, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x11, 0x70, 0x61, 0x67, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x33, + 0x20, 0x01, 0x28, 0x0c, 0x48, 0x03, 0x52, 0x10, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x43, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x2d, 0x0a, 0x12, 0x70, + 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, + 0x64, 0x18, 0x34, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x12, 0x2e, 0x0a, 0x10, 0x70, 0x61, + 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x35, + 0x20, 0x01, 0x28, 0x04, 0x48, 0x04, 0x52, 0x0f, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x70, + 0x75, 0x62, 0x73, 0x75, 0x62, 0x5f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x5f, 0x65, 0x6e, 0x64, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x70, 0x61, 0x67, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x42, 0x13, 0x0a, + 0x11, 0x5f, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x22, 0xa7, 0x02, 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, + 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x43, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12, 0x24, + 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x44, 0x65, 0x73, + 0x63, 0x88, 0x01, 0x01, 0x12, 0x3e, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, + 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x77, 0x61, 0x6b, 0x75, 0x2e, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x33, 0x2e, 0x57, 0x61, 0x6b, 0x75, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x11, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18, 0x33, 0x20, 0x01, 0x28, 0x0c, 0x48, + 0x02, 0x52, 0x10, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x75, 0x72, + 0x73, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x70, 0x61, 0x67, 0x69, 0x6e, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_storev3_proto_rawDescOnce sync.Once + file_storev3_proto_rawDescData = file_storev3_proto_rawDesc +) + +func file_storev3_proto_rawDescGZIP() []byte { + file_storev3_proto_rawDescOnce.Do(func() { + file_storev3_proto_rawDescData = protoimpl.X.CompressGZIP(file_storev3_proto_rawDescData) + }) + return file_storev3_proto_rawDescData +} + +var file_storev3_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_storev3_proto_goTypes = []interface{}{ + (*WakuMessageKeyValue)(nil), // 0: waku.store.v3.WakuMessageKeyValue + (*StoreQueryRequest)(nil), // 1: waku.store.v3.StoreQueryRequest + (*StoreQueryResponse)(nil), // 2: waku.store.v3.StoreQueryResponse + (*pb.WakuMessage)(nil), // 3: waku.message.v1.WakuMessage +} +var file_storev3_proto_depIdxs = []int32{ + 3, // 0: waku.store.v3.WakuMessageKeyValue.message:type_name -> waku.message.v1.WakuMessage + 0, // 1: waku.store.v3.StoreQueryResponse.messages:type_name -> waku.store.v3.WakuMessageKeyValue + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_storev3_proto_init() } +func file_storev3_proto_init() { + if File_storev3_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_storev3_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*WakuMessageKeyValue); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_storev3_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StoreQueryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_storev3_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StoreQueryResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_storev3_proto_msgTypes[0].OneofWrappers = []interface{}{} + file_storev3_proto_msgTypes[1].OneofWrappers = []interface{}{} + file_storev3_proto_msgTypes[2].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_storev3_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_storev3_proto_goTypes, + DependencyIndexes: file_storev3_proto_depIdxs, + MessageInfos: file_storev3_proto_msgTypes, + }.Build() + File_storev3_proto = out.File + file_storev3_proto_rawDesc = nil + file_storev3_proto_goTypes = nil + file_storev3_proto_depIdxs = nil +} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/storev3.proto b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/storev3.proto new file mode 100644 index 000000000..c828a713f --- /dev/null +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/storev3.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; + +// Protocol identifier: /vac/waku/store/3.0.0 +package waku.store.v3; + +import "waku/message/v1/message.proto"; + +message WakuMessageKeyValue { + optional bytes message_hash = 1; // Globally unique key for a Waku Message + optional waku.message.v1.WakuMessage message = 2; // Full message content as value +} + +message StoreQueryRequest { + string request_id = 1; + bool include_data = 2; // Response should include full message content + + // Filter criteria for content-filtered queries + optional string pubsub_topic = 10; + repeated string content_topics = 11; + optional sint64 time_start = 12; + optional sint64 time_end = 13; + + // List of key criteria for lookup queries + repeated bytes message_hashes = 20; // Message hashes (keys) to lookup + + // Pagination info. 50 Reserved + optional bytes pagination_cursor = 51; // Message hash (key) from where to start query (exclusive) + bool pagination_forward = 52; + optional uint64 pagination_limit = 53; +} + +message StoreQueryResponse { + string request_id = 1; + + optional uint32 status_code = 10; + optional string status_desc = 11; + + repeated WakuMessageKeyValue messages = 20; + + optional bytes pagination_cursor = 51; +} \ No newline at end of file diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/validation.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/validation.go index 740b58086..40bdfade6 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/validation.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/pb/validation.go @@ -4,44 +4,70 @@ import ( "errors" ) -// MaxContentFilters is the maximum number of allowed content filters in a query -const MaxContentFilters = 10 +// MaxContentTopics is the maximum number of allowed contenttopics in a query +const MaxContentTopics = 10 var ( - errMissingRequestID = errors.New("missing RequestId field") - errMissingQuery = errors.New("missing Query field") - errRequestIDMismatch = errors.New("requestID in response does not match request") - errMaxContentFilters = errors.New("exceeds the maximum number of content filters allowed") - errEmptyContentTopics = errors.New("one or more content topics specified is empty") + errMissingRequestID = errors.New("missing RequestId field") + errMessageHashOtherFields = errors.New("cannot use MessageHashes with ContentTopics/PubsubTopic") + errRequestIDMismatch = errors.New("requestID in response does not match request") + errMaxContentTopics = errors.New("exceeds the maximum number of ContentTopics allowed") + errEmptyContentTopic = errors.New("one or more content topics specified is empty") + errMissingPubsubTopic = errors.New("missing PubsubTopic field") + errMissingContentTopics = errors.New("missing ContentTopics field") + errMissingStatusCode = errors.New("missing StatusCode field") + errInvalidTimeRange = errors.New("invalid time range") + errInvalidMessageHash = errors.New("invalid message hash") ) -func (x *HistoryQuery) Validate() error { - if len(x.ContentFilters) > MaxContentFilters { - return errMaxContentFilters - } - - for _, m := range x.ContentFilters { - if m.ContentTopic == "" { - return errEmptyContentTopics - } - } - - return nil -} - -func (x *HistoryRPC) ValidateQuery() error { +func (x *StoreQueryRequest) Validate() error { if x.RequestId == "" { return errMissingRequestID } - if x.Query == nil { - return errMissingQuery - } + if len(x.MessageHashes) != 0 { + if len(x.ContentTopics) != 0 || x.GetPubsubTopic() != "" { + return errMessageHashOtherFields + } - return x.Query.Validate() + for _, x := range x.MessageHashes { + if len(x) != 32 { + return errInvalidMessageHash + } + } + } else { + if x.GetPubsubTopic() == "" { + return errMissingPubsubTopic + } + + if len(x.ContentTopics) == 0 { + return errMissingContentTopics + } else if len(x.ContentTopics) > MaxContentTopics { + return errMaxContentTopics + } else { + for _, m := range x.ContentTopics { + if m == "" { + return errEmptyContentTopic + } + } + } + + if x.GetTimeStart() > 0 && x.GetTimeEnd() > 0 && x.GetTimeStart() > x.GetTimeEnd() { + return errInvalidTimeRange + } + } + return nil } -func (x *HistoryResponse) Validate() error { +func (x *StoreQueryResponse) Validate(requestID string) error { + if x.RequestId != "" && x.RequestId != requestID { + return errRequestIDMismatch + } + + if x.StatusCode == nil { + return errMissingStatusCode + } + for _, m := range x.Messages { if err := m.Validate(); err != nil { return err @@ -51,17 +77,13 @@ func (x *HistoryResponse) Validate() error { return nil } -func (x *HistoryRPC) ValidateResponse(requestID string) error { - if x.RequestId == "" { - return errMissingRequestID +func (x *WakuMessageKeyValue) Validate() error { + if len(x.MessageHash) != 32 { + return errInvalidMessageHash } - if x.RequestId != requestID { - return errRequestIDMismatch - } - - if x.Response != nil { - return x.Response.Validate() + if x.Message != nil { + return x.Message.Validate() } return nil diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/result.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/result.go new file mode 100644 index 000000000..be67671ec --- /dev/null +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/store/result.go @@ -0,0 +1,68 @@ +package store + +import ( + "context" + + "github.com/libp2p/go-libp2p/core/peer" + "github.com/waku-org/go-waku/waku/v2/protocol/store/pb" +) + +// Result represents a valid response from a store node +type Result struct { + started bool + messages []*pb.WakuMessageKeyValue + store *WakuStore + storeRequest *pb.StoreQueryRequest + cursor []byte + peerID peer.ID +} + +func (r *Result) Cursor() []byte { + return r.cursor +} + +func (r *Result) IsComplete() bool { + return r.cursor == nil +} + +func (r *Result) PeerID() peer.ID { + return r.peerID +} + +func (r *Result) Query() *pb.StoreQueryRequest { + return r.storeRequest +} + +func (r *Result) PubsubTopic() string { + return r.storeRequest.GetPubsubTopic() +} + +func (r *Result) Next(ctx context.Context) (bool, error) { + if !r.started { + r.started = true + return len(r.messages) != 0, nil + } + + if r.IsComplete() { + r.cursor = nil + r.messages = nil + return false, nil + } + + newResult, err := r.store.next(ctx, r) + if err != nil { + return false, err + } + + r.cursor = newResult.cursor + r.messages = newResult.messages + + return !r.IsComplete(), nil +} + +func (r *Result) Messages() []*pb.WakuMessageKeyValue { + if !r.started { + return nil + } + return r.messages +} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/subscription/subscriptions_map.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/subscription/subscriptions_map.go index 540fe8076..a538912e7 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/subscription/subscriptions_map.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/subscription/subscriptions_map.go @@ -29,7 +29,7 @@ func NewSubscriptionMap(logger *zap.Logger) *SubscriptionsMap { } func (m *SubscriptionsMap) Count() int { - m.RLock() + m.RLock() defer m.RUnlock() return len(m.items) } @@ -199,7 +199,7 @@ func iterateSubscriptionSet(logger *zap.Logger, subscriptions SubscriptionSet, e } } -func (m *SubscriptionsMap) GetSubscription(peerID peer.ID, contentFilter protocol.ContentFilter) []*SubscriptionDetails { +func (m *SubscriptionsMap) GetSubscriptionsForPeer(peerID peer.ID, contentFilter protocol.ContentFilter) []*SubscriptionDetails { m.RLock() defer m.RUnlock() @@ -217,3 +217,7 @@ func (m *SubscriptionsMap) GetSubscription(peerID peer.ID, contentFilter protoco } return output } + +func (m *SubscriptionsMap) GetAllSubscriptions() []*SubscriptionDetails { + return m.GetSubscriptionsForPeer("", protocol.ContentFilter{}) +} diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/utils.go b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/utils.go index eaf820936..c30b7f9c3 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/protocol/utils.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/protocol/utils.go @@ -6,7 +6,7 @@ import ( "github.com/libp2p/go-libp2p/core/protocol" ) -const GossipSubOptimalFullMeshSize = 6 +const GossipSubDMin = 4 // FulltextMatch is the default matching function used for checking if a peer // supports a protocol or not diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/rendezvous/db.go b/vendor/github.com/waku-org/go-waku/waku/v2/rendezvous/db.go index 883c1f0c5..1d751fede 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/rendezvous/db.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/rendezvous/db.go @@ -165,7 +165,7 @@ func (db *DB) prepareStmts() error { } func (db *DB) Register(p peer.ID, ns string, signedPeerRecord []byte, ttl int) (uint64, error) { - pid := p.Pretty() + pid := p.String() expire := time.Now().Unix() + int64(ttl) tx, err := db.db.Begin() @@ -202,7 +202,7 @@ func (db *DB) Register(p peer.ID, ns string, signedPeerRecord []byte, ttl int) ( } func (db *DB) CountRegistrations(p peer.ID) (int, error) { - pid := p.Pretty() + pid := p.String() row := db.countPeerRegistrations.QueryRow(pid) @@ -213,7 +213,7 @@ func (db *DB) CountRegistrations(p peer.ID) (int, error) { } func (db *DB) Unregister(p peer.ID, ns string) error { - pid := p.Pretty() + pid := p.String() var err error diff --git a/vendor/github.com/waku-org/go-waku/waku/v2/utils/multiaddr.go b/vendor/github.com/waku-org/go-waku/waku/v2/utils/multiaddr.go index 8ba7630c9..350612163 100644 --- a/vendor/github.com/waku-org/go-waku/waku/v2/utils/multiaddr.go +++ b/vendor/github.com/waku-org/go-waku/waku/v2/utils/multiaddr.go @@ -9,7 +9,7 @@ import ( // EncapsulatePeerID takes a peer.ID and adds a p2p component to all multiaddresses it receives func EncapsulatePeerID(peerID peer.ID, addrs ...multiaddr.Multiaddr) []multiaddr.Multiaddr { - hostInfo, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/p2p/%s", peerID.Pretty())) + hostInfo, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/p2p/%s", peerID.String())) var result []multiaddr.Multiaddr for _, addr := range addrs { result = append(result, addr.Encapsulate(hostInfo)) @@ -17,6 +17,19 @@ func EncapsulatePeerID(peerID peer.ID, addrs ...multiaddr.Multiaddr) []multiaddr return result } +func MultiAddrSetEquals(m1 map[string]multiaddr.Multiaddr, m2 map[string]multiaddr.Multiaddr) bool { + if len(m1) != len(m2) { + return false + } + for k1, v1 := range m1 { + v2, ok := m2[k1] + if !ok || !v1.Equal(v2) { + return false + } + } + return true +} + func MultiAddrSet(addr ...multiaddr.Multiaddr) map[string]multiaddr.Multiaddr { r := make(map[string]multiaddr.Multiaddr) for _, a := range addr { @@ -24,26 +37,3 @@ func MultiAddrSet(addr ...multiaddr.Multiaddr) map[string]multiaddr.Multiaddr { } return r } - -func MultiAddrSetEquals(m1 map[string]multiaddr.Multiaddr, m2 map[string]multiaddr.Multiaddr) bool { - if len(m1) != len(m2) { - return false - } - - for k := range m1 { - _, ok := m2[k] - if !ok { - return false - } - } - - return true -} - -func MultiAddrFromSet(m map[string]multiaddr.Multiaddr) []multiaddr.Multiaddr { - var r []multiaddr.Multiaddr - for _, v := range m { - r = append(r, v) - } - return r -} diff --git a/vendor/github.com/xrash/smetrics/soundex.go b/vendor/github.com/xrash/smetrics/soundex.go index a2ad034d5..18c3aef72 100644 --- a/vendor/github.com/xrash/smetrics/soundex.go +++ b/vendor/github.com/xrash/smetrics/soundex.go @@ -6,36 +6,58 @@ import ( // The Soundex encoding. It is a phonetic algorithm that considers how the words sound in English. Soundex maps a string to a 4-byte code consisting of the first letter of the original string and three numbers. Strings that sound similar should map to the same code. func Soundex(s string) string { - m := map[byte]string{ - 'B': "1", 'P': "1", 'F': "1", 'V': "1", - 'C': "2", 'S': "2", 'K': "2", 'G': "2", 'J': "2", 'Q': "2", 'X': "2", 'Z': "2", - 'D': "3", 'T': "3", - 'L': "4", - 'M': "5", 'N': "5", - 'R': "6", - } + b := strings.Builder{} + b.Grow(4) - s = strings.ToUpper(s) - - r := string(s[0]) p := s[0] - for i := 1; i < len(s) && len(r) < 4; i++ { + if p <= 'z' && p >= 'a' { + p -= 32 // convert to uppercase + } + b.WriteByte(p) + + n := 0 + for i := 1; i < len(s); i++ { c := s[i] - if (c < 'A' || c > 'Z') || (c == p) { + if c <= 'z' && c >= 'a' { + c -= 32 // convert to uppercase + } else if c < 'A' || c > 'Z' { + continue + } + + if c == p { continue } p = c - if n, ok := m[c]; ok { - r += n + switch c { + case 'B', 'P', 'F', 'V': + c = '1' + case 'C', 'S', 'K', 'G', 'J', 'Q', 'X', 'Z': + c = '2' + case 'D', 'T': + c = '3' + case 'L': + c = '4' + case 'M', 'N': + c = '5' + case 'R': + c = '6' + default: + continue + } + + b.WriteByte(c) + n++ + if n == 3 { + break } } - for i := len(r); i < 4; i++ { - r += "0" + for i := n; i < 3; i++ { + b.WriteByte('0') } - return r + return b.String() } diff --git a/vendor/go.uber.org/dig/CHANGELOG.md b/vendor/go.uber.org/dig/CHANGELOG.md index 2989c1bba..f191bc5ae 100644 --- a/vendor/go.uber.org/dig/CHANGELOG.md +++ b/vendor/go.uber.org/dig/CHANGELOG.md @@ -4,13 +4,28 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.17.1] - 2023-10-19 +### Added +- Suggestions for value vs. pointer elements for slice and array types. + +### Fixed +- An issue where value group values were not getting decorated + by decorators within the same module when using dig.Export(true). +- A typo in docs. +- An issue where false positives in cycle detection were occurring + when providing to a child scope. + +Thanks to @paullen and @lcarilla for their contributions to this release. + +[1.17.1]: https://github.com/uber-go/dig/compare/v1.17.0...v1.17.1 + ## [1.17.0] - 2023-05-02 ### Added - Allow using `dig.As` with `dig.Group`. - Add `FillInvokeInfo` Option and `InvokeInfo` struct to help extract the types requested by an `Invoke` statement. - To get visibility into constructor and decorator calls, introduce - `WithCallback` Option to provide callback functions. + `WithProviderCallback` and `WithDecoratorCallback` Options to provide callback functions. [1.17.0]: https://github.com/uber-go/dig/compare/v1.16.1...v1.17.0 diff --git a/vendor/go.uber.org/dig/constructor.go b/vendor/go.uber.org/dig/constructor.go index 208659d98..034c41c22 100644 --- a/vendor/go.uber.org/dig/constructor.go +++ b/vendor/go.uber.org/dig/constructor.go @@ -129,6 +129,11 @@ func (n *constructorNode) CType() reflect.Type { return n.ctype } func (n *constructorNode) Order(s *Scope) int { return n.orders[s] } func (n *constructorNode) OrigScope() *Scope { return n.origS } +// CopyOrder copies the order for the given parent scope to the given child scope. +func (n *constructorNode) CopyOrder(parent, child *Scope) { + n.orders[child] = n.orders[parent] +} + func (n *constructorNode) String() string { return fmt.Sprintf("deps: %v, ctor: %v", n.paramList, n.ctype) } diff --git a/vendor/go.uber.org/dig/doc.go b/vendor/go.uber.org/dig/doc.go index 3f9ab85c1..77b01d045 100644 --- a/vendor/go.uber.org/dig/doc.go +++ b/vendor/go.uber.org/dig/doc.go @@ -207,7 +207,7 @@ // // func NewUserGateway(p UserGatewayParams, log *log.Logger) (*UserGateway, error) { // if p.Cache == nil { -// log.Print("Logging disabled") +// log.Print("Caching disabled") // } // // ... // } diff --git a/vendor/go.uber.org/dig/error.go b/vendor/go.uber.org/dig/error.go index 24c5685be..8aba9ee5a 100644 --- a/vendor/go.uber.org/dig/error.go +++ b/vendor/go.uber.org/dig/error.go @@ -414,6 +414,28 @@ func newErrMissingTypes(c containerStore, k key) errMissingTypes { suggestions = append(suggestions, k.t.Elem()) } + if k.t.Kind() == reflect.Slice { + // Maybe the user meant a slice of pointers while we have the slice of elements + suggestions = append(suggestions, reflect.SliceOf(reflect.PtrTo(k.t.Elem()))) + + // Maybe the user meant a slice of elements while we have the slice of pointers + sliceElement := k.t.Elem() + if sliceElement.Kind() == reflect.Ptr { + suggestions = append(suggestions, reflect.SliceOf(sliceElement.Elem())) + } + } + + if k.t.Kind() == reflect.Array { + // Maybe the user meant an array of pointers while we have the array of elements + suggestions = append(suggestions, reflect.ArrayOf(k.t.Len(), reflect.PtrTo(k.t.Elem()))) + + // Maybe the user meant an array of elements while we have the array of pointers + arrayElement := k.t.Elem() + if arrayElement.Kind() == reflect.Ptr { + suggestions = append(suggestions, reflect.ArrayOf(k.t.Len(), arrayElement.Elem())) + } + } + knownTypes := c.knownTypes() if k.t.Kind() == reflect.Interface { // Maybe we have an implementation of the interface. diff --git a/vendor/go.uber.org/dig/param.go b/vendor/go.uber.org/dig/param.go index d584fc237..ca0689759 100644 --- a/vendor/go.uber.org/dig/param.go +++ b/vendor/go.uber.org/dig/param.go @@ -606,7 +606,7 @@ func (pt paramGroupedSlice) callGroupProviders(c containerStore) (int, error) { providers := c.getGroupProviders(pt.Group, pt.Type.Elem()) itemCount += len(providers) for _, n := range providers { - if err := n.Call(c); err != nil { + if err := n.Call(n.OrigScope()); err != nil { return 0, errParamGroupFailed{ CtorID: n.ID(), Key: key{group: pt.Group, t: pt.Type.Elem()}, diff --git a/vendor/go.uber.org/dig/scope.go b/vendor/go.uber.org/dig/scope.go index 216cf18a1..dc0c7ac5b 100644 --- a/vendor/go.uber.org/dig/scope.go +++ b/vendor/go.uber.org/dig/scope.go @@ -121,7 +121,12 @@ func (s *Scope) Scope(name string, opts ...ScopeOption) *Scope { child.recoverFromPanics = s.recoverFromPanics // child copies the parent's graph nodes. - child.gh.nodes = append(child.gh.nodes, s.gh.nodes...) + for _, node := range s.gh.nodes { + child.gh.nodes = append(child.gh.nodes, node) + if ctrNode, ok := node.Wrapped.(*constructorNode); ok { + ctrNode.CopyOrder(s, child) + } + } for _, opt := range opts { opt.noScopeOption() diff --git a/vendor/go.uber.org/dig/version.go b/vendor/go.uber.org/dig/version.go index 0b55dc929..7c92d4ab0 100644 --- a/vendor/go.uber.org/dig/version.go +++ b/vendor/go.uber.org/dig/version.go @@ -21,4 +21,4 @@ package dig // Version of the library. -const Version = "1.17.0" +const Version = "1.17.1" diff --git a/vendor/go.uber.org/fx/CHANGELOG.md b/vendor/go.uber.org/fx/CHANGELOG.md index e3a746f22..7c43848c4 100644 --- a/vendor/go.uber.org/fx/CHANGELOG.md +++ b/vendor/go.uber.org/fx/CHANGELOG.md @@ -10,6 +10,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.20.1](https://github.com/uber-go/fx/compare/v1.20.0...v1.20.1) - 2023-10-17 + +### Added +- Provided, Decorated, Supplied, and Replaced events now include a trace + of module locations through which the option was given to the App. +- wasi support. + ## [1.20.0](https://github.com/uber-go/fx/compare/v1.19.3...v1.20.0) - 2023-06-12 ### Added diff --git a/vendor/go.uber.org/fx/app.go b/vendor/go.uber.org/fx/app.go index d524a5e98..e07bd5bdc 100644 --- a/vendor/go.uber.org/fx/app.go +++ b/vendor/go.uber.org/fx/app.go @@ -432,7 +432,8 @@ func New(opts ...Option) *App { // user gave us. For the last case, however, we need to fall // back to what was provided to fx.Logger if fx.WithLogger // fails. - log: logger, + log: logger, + trace: []string{fxreflect.CallerStack(1, 2)[0].String()}, } app.modules = append(app.modules, app.root) diff --git a/vendor/go.uber.org/fx/app_wasm.go b/vendor/go.uber.org/fx/app_wasm.go index 2468fed7e..5e2ca8d01 100644 --- a/vendor/go.uber.org/fx/app_wasm.go +++ b/vendor/go.uber.org/fx/app_wasm.go @@ -18,8 +18,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -//go:build js && wasm -// +build js,wasm +//go:build (js && wasm) || (wasip1 && wasm) +// +build js,wasm wasip1,wasm package fx diff --git a/vendor/go.uber.org/fx/fxevent/event.go b/vendor/go.uber.org/fx/fxevent/event.go index c87f9a303..7babcd4a0 100644 --- a/vendor/go.uber.org/fx/fxevent/event.go +++ b/vendor/go.uber.org/fx/fxevent/event.go @@ -113,6 +113,9 @@ type Supplied struct { // StackTrace is the stack trace of the call to Supply. StackTrace []string + // ModuleTrace contains the module locations through which this value was added. + ModuleTrace []string + // ModuleName is the name of the module in which the value was added to. ModuleName string @@ -129,6 +132,9 @@ type Provided struct { // StackTrace is the stack trace of where the constructor was provided to Fx. StackTrace []string + // ModuleTrace contains the module locations through which this was provided to Fx. + ModuleTrace []string + // OutputTypeNames is a list of names of types that are produced by // this constructor. OutputTypeNames []string @@ -152,6 +158,9 @@ type Replaced struct { // StackTrace is the stack trace of the call to Replace. StackTrace []string + // ModuleTrace contains the module locations through which this value was added. + ModuleTrace []string + // ModuleName is the name of the module in which the value was added to. ModuleName string @@ -168,6 +177,9 @@ type Decorated struct { // StackTrace is the stack trace of where the decorator was given to Fx. StackTrace []string + // ModuleTrace contains the module locations through which this value was added. + ModuleTrace []string + // ModuleName is the name of the module in which the value was added to. ModuleName string diff --git a/vendor/go.uber.org/fx/fxevent/zap.go b/vendor/go.uber.org/fx/fxevent/zap.go index c304ea058..613eb8653 100644 --- a/vendor/go.uber.org/fx/fxevent/zap.go +++ b/vendor/go.uber.org/fx/fxevent/zap.go @@ -105,12 +105,14 @@ func (l *ZapLogger) LogEvent(event Event) { l.logError("error encountered while applying options", zap.String("type", e.TypeName), zap.Strings("stacktrace", e.StackTrace), + zap.Strings("moduletrace", e.ModuleTrace), moduleField(e.ModuleName), zap.Error(e.Err)) } else { l.logEvent("supplied", zap.String("type", e.TypeName), zap.Strings("stacktrace", e.StackTrace), + zap.Strings("moduletrace", e.ModuleTrace), moduleField(e.ModuleName), ) } @@ -119,6 +121,7 @@ func (l *ZapLogger) LogEvent(event Event) { l.logEvent("provided", zap.String("constructor", e.ConstructorName), zap.Strings("stacktrace", e.StackTrace), + zap.Strings("moduletrace", e.ModuleTrace), moduleField(e.ModuleName), zap.String("type", rtype), maybeBool("private", e.Private), @@ -128,12 +131,14 @@ func (l *ZapLogger) LogEvent(event Event) { l.logError("error encountered while applying options", moduleField(e.ModuleName), zap.Strings("stacktrace", e.StackTrace), + zap.Strings("moduletrace", e.ModuleTrace), zap.Error(e.Err)) } case *Replaced: for _, rtype := range e.OutputTypeNames { l.logEvent("replaced", zap.Strings("stacktrace", e.StackTrace), + zap.Strings("moduletrace", e.ModuleTrace), moduleField(e.ModuleName), zap.String("type", rtype), ) @@ -141,6 +146,7 @@ func (l *ZapLogger) LogEvent(event Event) { if e.Err != nil { l.logError("error encountered while replacing", zap.Strings("stacktrace", e.StackTrace), + zap.Strings("moduletrace", e.ModuleTrace), moduleField(e.ModuleName), zap.Error(e.Err)) } @@ -149,6 +155,7 @@ func (l *ZapLogger) LogEvent(event Event) { l.logEvent("decorated", zap.String("decorator", e.DecoratorName), zap.Strings("stacktrace", e.StackTrace), + zap.Strings("moduletrace", e.ModuleTrace), moduleField(e.ModuleName), zap.String("type", rtype), ) @@ -156,6 +163,7 @@ func (l *ZapLogger) LogEvent(event Event) { if e.Err != nil { l.logError("error encountered while applying options", zap.Strings("stacktrace", e.StackTrace), + zap.Strings("moduletrace", e.ModuleTrace), moduleField(e.ModuleName), zap.Error(e.Err)) } diff --git a/vendor/go.uber.org/fx/module.go b/vendor/go.uber.org/fx/module.go index d678c4dc6..bb4f74484 100644 --- a/vendor/go.uber.org/fx/module.go +++ b/vendor/go.uber.org/fx/module.go @@ -45,15 +45,17 @@ type container interface { // place. For more information, see [Decorate], [Replace], or [Invoke]. func Module(name string, opts ...Option) Option { mo := moduleOption{ - name: name, - options: opts, + name: name, + location: fxreflect.CallerStack(1, 2)[0], + options: opts, } return mo } type moduleOption struct { - name string - options []Option + name string + location fxreflect.Frame + options []Option } func (o moduleOption) String() string { @@ -68,9 +70,13 @@ func (o moduleOption) apply(mod *module) { // module. // 2. Apply child Options on the new module. // 3. Append it to the parent module. + + // Create trace as parent's trace with this module's location pre-pended. + trace := append([]string{fmt.Sprintf("%v (%v)", o.location, o.name)}, mod.trace...) newModule := &module{ name: o.name, parent: mod, + trace: trace, app: mod.app, } for _, opt := range o.options { @@ -82,6 +88,7 @@ func (o moduleOption) apply(mod *module) { type module struct { parent *module name string + trace []string scope scope provides []provide invokes []invoke @@ -177,6 +184,7 @@ func (m *module) provide(p provide) { m.log.LogEvent(&fxevent.Provided{ ConstructorName: funcName, StackTrace: p.Stack.Strings(), + ModuleTrace: append([]string{p.Stack[0].String()}, m.trace...), ModuleName: m.name, OutputTypeNames: outputNames, Err: m.app.err, @@ -202,10 +210,11 @@ func (m *module) supply(p provide) { } m.log.LogEvent(&fxevent.Supplied{ - TypeName: typeName, - StackTrace: p.Stack.Strings(), - ModuleName: m.name, - Err: m.app.err, + TypeName: typeName, + StackTrace: p.Stack.Strings(), + ModuleTrace: append([]string{p.Stack[0].String()}, m.trace...), + ModuleName: m.name, + Err: m.app.err, }) } @@ -329,6 +338,7 @@ func (m *module) decorate(d decorator) (err error) { m.log.LogEvent(&fxevent.Decorated{ DecoratorName: funcName, StackTrace: d.Stack.Strings(), + ModuleTrace: append([]string{d.Stack[0].String()}, m.trace...), ModuleName: m.name, OutputTypeNames: outputNames, Err: err, @@ -354,6 +364,7 @@ func (m *module) replace(d decorator) error { m.log.LogEvent(&fxevent.Replaced{ ModuleName: m.name, StackTrace: d.Stack.Strings(), + ModuleTrace: append([]string{d.Stack[0].String()}, m.trace...), OutputTypeNames: []string{typeName}, Err: err, }) diff --git a/vendor/go.uber.org/fx/version.go b/vendor/go.uber.org/fx/version.go index 943cd1a2c..15934d2ce 100644 --- a/vendor/go.uber.org/fx/version.go +++ b/vendor/go.uber.org/fx/version.go @@ -21,4 +21,4 @@ package fx // Version is exported for runtime compatibility checks. -const Version = "1.20.0" +const Version = "1.20.1" diff --git a/vendor/go.uber.org/mock/AUTHORS b/vendor/go.uber.org/mock/AUTHORS new file mode 100644 index 000000000..660b8ccc8 --- /dev/null +++ b/vendor/go.uber.org/mock/AUTHORS @@ -0,0 +1,12 @@ +# This is the official list of GoMock authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +Alex Reece +Google Inc. diff --git a/vendor/go.uber.org/mock/CONTRIBUTORS b/vendor/go.uber.org/mock/CONTRIBUTORS new file mode 100644 index 000000000..def849cab --- /dev/null +++ b/vendor/go.uber.org/mock/CONTRIBUTORS @@ -0,0 +1,37 @@ +# This is the official list of people who can contribute (and typically +# have contributed) code to the gomock repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# The submission process automatically checks to make sure +# that people submitting code are listed in this file (by email address). +# +# Names should be added to this file only after verifying that +# the individual or the individual's organization has agreed to +# the appropriate Contributor License Agreement, found here: +# +# http://code.google.com/legal/individual-cla-v1.0.html +# http://code.google.com/legal/corporate-cla-v1.0.html +# +# The agreement for individuals can be filled out on the web. +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file, depending on whether the +# individual or corporate CLA was used. + +# Names should be added to this file like so: +# Name +# +# An entry with two email addresses specifies that the +# first address should be used in the submit logs and +# that the second address should be recognized as the +# same person when interacting with Rietveld. + +# Please keep the list sorted. + +Aaron Jacobs +Alex Reece +David Symonds +Ryan Barrett diff --git a/vendor/go.uber.org/mock/LICENSE b/vendor/go.uber.org/mock/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/vendor/go.uber.org/mock/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/go.uber.org/mock/mockgen/generic_go118.go b/vendor/go.uber.org/mock/mockgen/generic_go118.go new file mode 100644 index 000000000..635402dc5 --- /dev/null +++ b/vendor/go.uber.org/mock/mockgen/generic_go118.go @@ -0,0 +1,116 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build go1.18 +// +build go1.18 + +package main + +import ( + "fmt" + "go/ast" + "strings" + + "go.uber.org/mock/mockgen/model" +) + +func getTypeSpecTypeParams(ts *ast.TypeSpec) []*ast.Field { + if ts == nil || ts.TypeParams == nil { + return nil + } + return ts.TypeParams.List +} + +func (p *fileParser) parseGenericType(pkg string, typ ast.Expr, tps map[string]model.Type) (model.Type, error) { + switch v := typ.(type) { + case *ast.IndexExpr: + m, err := p.parseType(pkg, v.X, tps) + if err != nil { + return nil, err + } + nm, ok := m.(*model.NamedType) + if !ok { + return m, nil + } + t, err := p.parseType(pkg, v.Index, tps) + if err != nil { + return nil, err + } + nm.TypeParams = &model.TypeParametersType{TypeParameters: []model.Type{t}} + return m, nil + case *ast.IndexListExpr: + m, err := p.parseType(pkg, v.X, tps) + if err != nil { + return nil, err + } + nm, ok := m.(*model.NamedType) + if !ok { + return m, nil + } + var ts []model.Type + for _, expr := range v.Indices { + t, err := p.parseType(pkg, expr, tps) + if err != nil { + return nil, err + } + ts = append(ts, t) + } + nm.TypeParams = &model.TypeParametersType{TypeParameters: ts} + return m, nil + } + return nil, nil +} + +func getIdentTypeParams(decl any) string { + if decl == nil { + return "" + } + ts, ok := decl.(*ast.TypeSpec) + if !ok { + return "" + } + if ts.TypeParams == nil || len(ts.TypeParams.List) == 0 { + return "" + } + var sb strings.Builder + sb.WriteString("[") + for i, v := range ts.TypeParams.List { + if i != 0 { + sb.WriteString(", ") + } + sb.WriteString(v.Names[0].Name) + } + sb.WriteString("]") + return sb.String() +} + +func (p *fileParser) parseGenericMethod(field *ast.Field, it *namedInterface, iface *model.Interface, pkg string, tps map[string]model.Type) ([]*model.Method, error) { + var indices []ast.Expr + var typ ast.Expr + switch v := field.Type.(type) { + case *ast.IndexExpr: + indices = []ast.Expr{v.Index} + typ = v.X + case *ast.IndexListExpr: + indices = v.Indices + typ = v.X + default: + return nil, fmt.Errorf("don't know how to mock method of type %T", field.Type) + } + + nf := &ast.Field{ + Doc: field.Comment, + Names: field.Names, + Type: typ, + Tag: field.Tag, + Comment: field.Comment, + } + + it.embeddedInstTypeParams = indices + + return p.parseMethod(nf, it, iface, pkg, tps) +} diff --git a/vendor/go.uber.org/mock/mockgen/generic_notgo118.go b/vendor/go.uber.org/mock/mockgen/generic_notgo118.go new file mode 100644 index 000000000..8a779c8b2 --- /dev/null +++ b/vendor/go.uber.org/mock/mockgen/generic_notgo118.go @@ -0,0 +1,41 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !go1.18 +// +build !go1.18 + +package main + +import ( + "fmt" + "go/ast" + + "go.uber.org/mock/mockgen/model" +) + +func getTypeSpecTypeParams(ts *ast.TypeSpec) []*ast.Field { + return nil +} + +func (p *fileParser) parseGenericType(pkg string, typ ast.Expr, tps map[string]model.Type) (model.Type, error) { + return nil, nil +} + +func getIdentTypeParams(decl any) string { + return "" +} + +func (p *fileParser) parseGenericMethod(field *ast.Field, it *namedInterface, iface *model.Interface, pkg string, tps map[string]model.Type) ([]*model.Method, error) { + return nil, fmt.Errorf("don't know how to mock method of type %T", field.Type) +} diff --git a/vendor/github.com/golang/mock/mockgen/mockgen.go b/vendor/go.uber.org/mock/mockgen/mockgen.go similarity index 64% rename from vendor/github.com/golang/mock/mockgen/mockgen.go rename to vendor/go.uber.org/mock/mockgen/mockgen.go index 50487070e..feb747fac 100644 --- a/vendor/github.com/golang/mock/mockgen/mockgen.go +++ b/vendor/go.uber.org/mock/mockgen/mockgen.go @@ -21,11 +21,11 @@ package main import ( "bytes" "encoding/json" + "errors" "flag" "fmt" "go/token" "io" - "io/ioutil" "log" "os" "os/exec" @@ -36,14 +36,14 @@ import ( "strings" "unicode" - "github.com/golang/mock/mockgen/model" - "golang.org/x/mod/modfile" toolsimports "golang.org/x/tools/imports" + + "go.uber.org/mock/mockgen/model" ) const ( - gomockImportPath = "github.com/golang/mock/gomock" + gomockImportPath = "go.uber.org/mock/gomock" ) var ( @@ -53,13 +53,19 @@ var ( ) var ( - source = flag.String("source", "", "(source mode) Input Go source file; enables source mode.") - destination = flag.String("destination", "", "Output file; defaults to stdout.") - mockNames = flag.String("mock_names", "", "Comma-separated interfaceName=mockName pairs of explicit mock names to use. Mock names default to 'Mock'+ interfaceName suffix.") - packageOut = flag.String("package", "", "Package of the generated code; defaults to the package of the input with a 'mock_' prefix.") - selfPackage = flag.String("self_package", "", "The full package import path for the generated code. The purpose of this flag is to prevent import cycles in the generated code by trying to include its own package. This can happen if the mock's package is set to one of its inputs (usually the main one) and the output is stdio so mockgen cannot detect the final output package. Setting this flag will then tell mockgen which import to exclude.") - writePkgComment = flag.Bool("write_package_comment", true, "Writes package documentation comment (godoc) if true.") - copyrightFile = flag.String("copyright_file", "", "Copyright file used to add copyright header") + source = flag.String("source", "", "(source mode) Input Go source file; enables source mode.") + destination = flag.String("destination", "", "Output file; defaults to stdout.") + mockNames = flag.String("mock_names", "", "Comma-separated interfaceName=mockName pairs of explicit mock names to use. Mock names default to 'Mock'+ interfaceName suffix.") + packageOut = flag.String("package", "", "Package of the generated code; defaults to the package of the input with a 'mock_' prefix.") + selfPackage = flag.String("self_package", "", "The full package import path for the generated code. The purpose of this flag is to prevent import cycles in the generated code by trying to include its own package. This can happen if the mock's package is set to one of its inputs (usually the main one) and the output is stdio so mockgen cannot detect the final output package. Setting this flag will then tell mockgen which import to exclude.") + writePkgComment = flag.Bool("write_package_comment", true, "Writes package documentation comment (godoc) if true.") + writeSourceComment = flag.Bool("write_source_comment", true, "Writes original file (source mode) or interface names (reflect mode) comment if true.") + writeGenerateDirective = flag.Bool("write_generate_directive", false, "Add //go:generate directive to regenerate the mock") + copyrightFile = flag.String("copyright_file", "", "Copyright file used to add copyright header") + typed = flag.Bool("typed", false, "Generate Type-safe 'Return', 'Do', 'DoAndReturn' function") + imports = flag.String("imports", "", "(source mode) Comma-separated name=path pairs of explicit imports to use.") + auxFiles = flag.String("aux_files", "", "(source mode) Comma-separated pkg=path pairs of auxiliary Go source files.") + excludeInterfaces = flag.String("exclude_interfaces", "", "Comma-separated names of interfaces to be excluded") debugParser = flag.Bool("debug_parser", false, "Print out parser results only.") showVersion = flag.Bool("version", false, "Print version.") @@ -107,19 +113,6 @@ func main() { return } - dst := os.Stdout - if len(*destination) > 0 { - if err := os.MkdirAll(filepath.Dir(*destination), os.ModePerm); err != nil { - log.Fatalf("Unable to create directory: %v", err) - } - f, err := os.Create(*destination) - if err != nil { - log.Fatalf("Failed opening destination file: %v", err) - } - defer f.Close() - dst = f - } - outputPackageName := *packageOut if outputPackageName == "" { // pkg.Name in reflect mode is the base name of the import path, @@ -161,7 +154,7 @@ func main() { g.mockNames = parseMockNames(*mockNames) } if *copyrightFile != "" { - header, err := ioutil.ReadFile(*copyrightFile) + header, err := os.ReadFile(*copyrightFile) if err != nil { log.Fatalf("Failed reading copyright file: %v", err) } @@ -171,7 +164,27 @@ func main() { if err := g.Generate(pkg, outputPackageName, outputPackagePath); err != nil { log.Fatalf("Failed generating mock: %v", err) } - if _, err := dst.Write(g.Output()); err != nil { + output := g.Output() + dst := os.Stdout + if len(*destination) > 0 { + if err := os.MkdirAll(filepath.Dir(*destination), os.ModePerm); err != nil { + log.Fatalf("Unable to create directory: %v", err) + } + existing, err := os.ReadFile(*destination) + if err != nil && !errors.Is(err, os.ErrNotExist) { + log.Fatalf("Failed reading pre-exiting destination file: %v", err) + } + if len(existing) == len(output) && bytes.Equal(existing, output) { + return + } + f, err := os.Create(*destination) + if err != nil { + log.Fatalf("Failed opening destination file: %v", err) + } + defer f.Close() + dst = f + } + if _, err := dst.Write(output); err != nil { log.Fatalf("Failed writing to destination: %v", err) } } @@ -188,6 +201,24 @@ func parseMockNames(names string) map[string]string { return mocksMap } +func parseExcludeInterfaces(names string) map[string]struct{} { + splitNames := strings.Split(names, ",") + namesSet := make(map[string]struct{}, len(splitNames)) + for _, name := range splitNames { + if name == "" { + continue + } + + namesSet[name] = struct{}{} + } + + if len(namesSet) == 0 { + return nil + } + + return namesSet +} + func usage() { _, _ = io.WriteString(os.Stderr, usageText) flag.PrintDefaults() @@ -222,7 +253,7 @@ type generator struct { packageMap map[string]string // map from import path to package name } -func (g *generator) p(format string, args ...interface{}) { +func (g *generator) p(format string, args ...any) { fmt.Fprintf(&g.buf, g.indent+format+"\n", args...) } @@ -274,12 +305,17 @@ func (g *generator) Generate(pkg *model.Package, outputPkgName string, outputPac } g.p("// Code generated by MockGen. DO NOT EDIT.") - if g.filename != "" { - g.p("// Source: %v", g.filename) - } else { - g.p("// Source: %v (interfaces: %v)", g.srcPackage, g.srcInterfaces) + if *writeSourceComment { + if g.filename != "" { + g.p("// Source: %v", g.filename) + } else { + g.p("// Source: %v (interfaces: %v)", g.srcPackage, g.srcInterfaces) + } } - g.p("") + g.p("//") + g.p("// Generated by this command:") + // only log the name of the executable, not the full path + g.p("// %v", strings.Join(append([]string{filepath.Base(os.Args[0])}, os.Args[1:]...), " ")) // Get all required imports, and generate unique names for them all. im := pkg.Imports() @@ -305,6 +341,16 @@ func (g *generator) Generate(pkg *model.Package, outputPkgName string, outputPac packagesName := createPackageMap(sortedPaths) + definedImports := make(map[string]string, len(im)) + if *imports != "" { + for _, kv := range strings.Split(*imports, ",") { + eq := strings.Index(kv, "=") + if k, v := kv[:eq], kv[eq+1:]; k != "." { + definedImports[v] = k + } + } + } + g.packageMap = make(map[string]string, len(im)) localNames := make(map[string]bool, len(im)) for _, pth := range sortedPaths { @@ -316,9 +362,14 @@ func (g *generator) Generate(pkg *model.Package, outputPkgName string, outputPac // Local names for an imported package can usually be the basename of the import path. // A couple of situations don't permit that, such as duplicate local names // (e.g. importing "html/template" and "text/template"), or where the basename is - // a keyword (e.g. "foo/case"). + // a keyword (e.g. "foo/case") or when defining a name for that by using the -imports flag. // try base0, base1, ... pkgName := base + + if _, ok := definedImports[base]; ok { + pkgName = definedImports[base] + } + i := 0 for localNames[pkgName] || token.Lookup(pkgName).IsKeyword() { pkgName = base + strconv.Itoa(i) @@ -353,6 +404,10 @@ func (g *generator) Generate(pkg *model.Package, outputPkgName string, outputPac g.out() g.p(")") + if *writeGenerateDirective { + g.p("//go:generate %v", strings.Join(os.Args, " ")) + } + for _, intf := range pkg.Interfaces { if err := g.GenerateMockInterface(intf, outputPackagePath); err != nil { return err @@ -371,32 +426,58 @@ func (g *generator) mockName(typeName string) string { return "Mock" + typeName } +// formattedTypeParams returns a long and short form of type param info used for +// printing. If analyzing a interface with type param [I any, O any] the result +// will be: +// "[I any, O any]", "[I, O]" +func (g *generator) formattedTypeParams(it *model.Interface, pkgOverride string) (string, string) { + if len(it.TypeParams) == 0 { + return "", "" + } + var long, short strings.Builder + long.WriteString("[") + short.WriteString("[") + for i, v := range it.TypeParams { + if i != 0 { + long.WriteString(", ") + short.WriteString(", ") + } + long.WriteString(v.Name) + short.WriteString(v.Name) + long.WriteString(fmt.Sprintf(" %s", v.Type.String(g.packageMap, pkgOverride))) + } + long.WriteString("]") + short.WriteString("]") + return long.String(), short.String() +} + func (g *generator) GenerateMockInterface(intf *model.Interface, outputPackagePath string) error { mockType := g.mockName(intf.Name) + longTp, shortTp := g.formattedTypeParams(intf, outputPackagePath) g.p("") g.p("// %v is a mock of %v interface.", mockType, intf.Name) - g.p("type %v struct {", mockType) + g.p("type %v%v struct {", mockType, longTp) g.in() g.p("ctrl *gomock.Controller") - g.p("recorder *%vMockRecorder", mockType) + g.p("recorder *%vMockRecorder%v", mockType, shortTp) g.out() g.p("}") g.p("") g.p("// %vMockRecorder is the mock recorder for %v.", mockType, mockType) - g.p("type %vMockRecorder struct {", mockType) + g.p("type %vMockRecorder%v struct {", mockType, longTp) g.in() - g.p("mock *%v", mockType) + g.p("mock *%v%v", mockType, shortTp) g.out() g.p("}") g.p("") g.p("// New%v creates a new mock instance.", mockType) - g.p("func New%v(ctrl *gomock.Controller) *%v {", mockType, mockType) + g.p("func New%v%v(ctrl *gomock.Controller) *%v%v {", mockType, longTp, mockType, shortTp) g.in() - g.p("mock := &%v{ctrl: ctrl}", mockType) - g.p("mock.recorder = &%vMockRecorder{mock}", mockType) + g.p("mock := &%v%v{ctrl: ctrl}", mockType, shortTp) + g.p("mock.recorder = &%vMockRecorder%v{mock}", mockType, shortTp) g.p("return mock") g.out() g.p("}") @@ -404,13 +485,13 @@ func (g *generator) GenerateMockInterface(intf *model.Interface, outputPackagePa // XXX: possible name collision here if someone has EXPECT in their interface. g.p("// EXPECT returns an object that allows the caller to indicate expected use.") - g.p("func (m *%v) EXPECT() *%vMockRecorder {", mockType, mockType) + g.p("func (m *%v%v) EXPECT() *%vMockRecorder%v {", mockType, shortTp, mockType, shortTp) g.in() g.p("return m.recorder") g.out() g.p("}") - g.GenerateMockMethods(mockType, intf, outputPackagePath) + g.GenerateMockMethods(mockType, intf, outputPackagePath, longTp, shortTp, *typed) return nil } @@ -421,13 +502,17 @@ func (b byMethodName) Len() int { return len(b) } func (b byMethodName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } func (b byMethodName) Less(i, j int) bool { return b[i].Name < b[j].Name } -func (g *generator) GenerateMockMethods(mockType string, intf *model.Interface, pkgOverride string) { +func (g *generator) GenerateMockMethods(mockType string, intf *model.Interface, pkgOverride, longTp, shortTp string, typed bool) { sort.Sort(byMethodName(intf.Methods)) for _, m := range intf.Methods { g.p("") - _ = g.GenerateMockMethod(mockType, m, pkgOverride) + _ = g.GenerateMockMethod(mockType, m, pkgOverride, shortTp) g.p("") - _ = g.GenerateMockRecorderMethod(mockType, m) + _ = g.GenerateMockRecorderMethod(intf, mockType, m, shortTp, typed) + if typed { + g.p("") + _ = g.GenerateMockReturnCallMethod(intf, m, pkgOverride, longTp, shortTp) + } } } @@ -446,9 +531,9 @@ func makeArgString(argNames, argTypes []string) string { // GenerateMockMethod generates a mock method implementation. // If non-empty, pkgOverride is the package in which unqualified types reside. -func (g *generator) GenerateMockMethod(mockType string, m *model.Method, pkgOverride string) error { - argNames := g.getArgNames(m) - argTypes := g.getArgTypes(m, pkgOverride) +func (g *generator) GenerateMockMethod(mockType string, m *model.Method, pkgOverride, shortTp string) error { + argNames := g.getArgNames(m, true /* in */) + argTypes := g.getArgTypes(m, pkgOverride, true /* in */) argString := makeArgString(argNames, argTypes) rets := make([]string, len(m.Out)) @@ -467,7 +552,7 @@ func (g *generator) GenerateMockMethod(mockType string, m *model.Method, pkgOver idRecv := ia.allocateIdentifier("m") g.p("// %v mocks base method.", m.Name) - g.p("func (%v *%v) %v(%v)%v {", idRecv, mockType, m.Name, argString, retString) + g.p("func (%v *%v%v) %v(%v)%v {", idRecv, mockType, shortTp, m.Name, argString, retString) g.in() g.p("%s.ctrl.T.Helper()", idRecv) @@ -477,11 +562,11 @@ func (g *generator) GenerateMockMethod(mockType string, m *model.Method, pkgOver callArgs = ", " + strings.Join(argNames, ", ") } } else { - // Non-trivial. The generated code must build a []interface{}, + // Non-trivial. The generated code must build a []any, // but the variadic argument may be any type. idVarArgs := ia.allocateIdentifier("varargs") idVArg := ia.allocateIdentifier("a") - g.p("%s := []interface{}{%s}", idVarArgs, strings.Join(argNames[:len(argNames)-1], ", ")) + g.p("%s := []any{%s}", idVarArgs, strings.Join(argNames[:len(argNames)-1], ", ")) g.p("for _, %s := range %s {", idVArg, argNames[len(argNames)-1]) g.in() g.p("%s = append(%s, %s)", idVarArgs, idVarArgs, idVArg) @@ -511,8 +596,8 @@ func (g *generator) GenerateMockMethod(mockType string, m *model.Method, pkgOver return nil } -func (g *generator) GenerateMockRecorderMethod(mockType string, m *model.Method) error { - argNames := g.getArgNames(m) +func (g *generator) GenerateMockRecorderMethod(intf *model.Interface, mockType string, m *model.Method, shortTp string, typed bool) error { + argNames := g.getArgNames(m, true) var argString string if m.Variadic == nil { @@ -521,21 +606,26 @@ func (g *generator) GenerateMockRecorderMethod(mockType string, m *model.Method) argString = strings.Join(argNames[:len(argNames)-1], ", ") } if argString != "" { - argString += " interface{}" + argString += " any" } if m.Variadic != nil { if argString != "" { argString += ", " } - argString += fmt.Sprintf("%s ...interface{}", argNames[len(argNames)-1]) + argString += fmt.Sprintf("%s ...any", argNames[len(argNames)-1]) } ia := newIdentifierAllocator(argNames) idRecv := ia.allocateIdentifier("mr") g.p("// %v indicates an expected call of %v.", m.Name, m.Name) - g.p("func (%s *%vMockRecorder) %v(%v) *gomock.Call {", idRecv, mockType, m.Name, argString) + if typed { + g.p("func (%s *%vMockRecorder%v) %v(%v) *%s%sCall%s {", idRecv, mockType, shortTp, m.Name, argString, intf.Name, m.Name, shortTp) + } else { + g.p("func (%s *%vMockRecorder%v) %v(%v) *gomock.Call {", idRecv, mockType, shortTp, m.Name, argString) + } + g.in() g.p("%s.mock.ctrl.T.Helper()", idRecv) @@ -551,42 +641,121 @@ func (g *generator) GenerateMockRecorderMethod(mockType string, m *model.Method) } else { // Hard: create a temporary slice. idVarArgs := ia.allocateIdentifier("varargs") - g.p("%s := append([]interface{}{%s}, %s...)", + g.p("%s := append([]any{%s}, %s...)", idVarArgs, strings.Join(argNames[:len(argNames)-1], ", "), argNames[len(argNames)-1]) callArgs = ", " + idVarArgs + "..." } } - g.p(`return %s.mock.ctrl.RecordCallWithMethodType(%s.mock, "%s", reflect.TypeOf((*%s)(nil).%s)%s)`, idRecv, idRecv, m.Name, mockType, m.Name, callArgs) + if typed { + g.p(`call := %s.mock.ctrl.RecordCallWithMethodType(%s.mock, "%s", reflect.TypeOf((*%s%s)(nil).%s)%s)`, idRecv, idRecv, m.Name, mockType, shortTp, m.Name, callArgs) + g.p(`return &%s%sCall%s{Call: call}`, intf.Name, m.Name, shortTp) + } else { + g.p(`return %s.mock.ctrl.RecordCallWithMethodType(%s.mock, "%s", reflect.TypeOf((*%s%s)(nil).%s)%s)`, idRecv, idRecv, m.Name, mockType, shortTp, m.Name, callArgs) + } g.out() g.p("}") return nil } -func (g *generator) getArgNames(m *model.Method) []string { - argNames := make([]string, len(m.In)) - for i, p := range m.In { +func (g *generator) GenerateMockReturnCallMethod(intf *model.Interface, m *model.Method, pkgOverride, longTp, shortTp string) error { + argNames := g.getArgNames(m, true /* in */) + retNames := g.getArgNames(m, false /* out */) + argTypes := g.getArgTypes(m, pkgOverride, true /* in */) + retTypes := g.getArgTypes(m, pkgOverride, false /* out */) + argString := strings.Join(argTypes, ", ") + + rets := make([]string, len(m.Out)) + for i, p := range m.Out { + rets[i] = p.Type.String(g.packageMap, pkgOverride) + } + + var retString string + switch { + case len(rets) == 1: + retString = " " + rets[0] + case len(rets) > 1: + retString = " (" + strings.Join(rets, ", ") + ")" + } + + ia := newIdentifierAllocator(argNames) + idRecv := ia.allocateIdentifier("c") + + recvStructName := intf.Name + m.Name + + g.p("// %s%sCall wrap *gomock.Call", intf.Name, m.Name) + g.p("type %s%sCall%s struct{", intf.Name, m.Name, longTp) + g.in() + g.p("*gomock.Call") + g.out() + g.p("}") + + g.p("// Return rewrite *gomock.Call.Return") + g.p("func (%s *%sCall%s) Return(%v) *%sCall%s {", idRecv, recvStructName, shortTp, makeArgString(retNames, retTypes), recvStructName, shortTp) + g.in() + var retArgs string + if len(retNames) > 0 { + retArgs = strings.Join(retNames, ", ") + } + g.p(`%s.Call = %v.Call.Return(%v)`, idRecv, idRecv, retArgs) + g.p("return %s", idRecv) + g.out() + g.p("}") + + g.p("// Do rewrite *gomock.Call.Do") + g.p("func (%s *%sCall%s) Do(f func(%v)%v) *%sCall%s {", idRecv, recvStructName, shortTp, argString, retString, recvStructName, shortTp) + g.in() + g.p(`%s.Call = %v.Call.Do(f)`, idRecv, idRecv) + g.p("return %s", idRecv) + g.out() + g.p("}") + + g.p("// DoAndReturn rewrite *gomock.Call.DoAndReturn") + g.p("func (%s *%sCall%s) DoAndReturn(f func(%v)%v) *%sCall%s {", idRecv, recvStructName, shortTp, argString, retString, recvStructName, shortTp) + g.in() + g.p(`%s.Call = %v.Call.DoAndReturn(f)`, idRecv, idRecv) + g.p("return %s", idRecv) + g.out() + g.p("}") + return nil +} + +func (g *generator) getArgNames(m *model.Method, in bool) []string { + var params []*model.Parameter + if in { + params = m.In + } else { + params = m.Out + } + argNames := make([]string, len(params)) + for i, p := range params { name := p.Name if name == "" || name == "_" { name = fmt.Sprintf("arg%d", i) } argNames[i] = name } - if m.Variadic != nil { + if m.Variadic != nil && in { name := m.Variadic.Name if name == "" { - name = fmt.Sprintf("arg%d", len(m.In)) + name = fmt.Sprintf("arg%d", len(params)) } argNames = append(argNames, name) } return argNames } -func (g *generator) getArgTypes(m *model.Method, pkgOverride string) []string { - argTypes := make([]string, len(m.In)) - for i, p := range m.In { +func (g *generator) getArgTypes(m *model.Method, pkgOverride string, in bool) []string { + var params []*model.Parameter + if in { + params = m.In + } else { + params = m.Out + } + argTypes := make([]string, len(params)) + for i, p := range params { argTypes[i] = p.Type.String(g.packageMap, pkgOverride) } if m.Variadic != nil { @@ -670,7 +839,7 @@ func parsePackageImport(srcDir string) (string, error) { if moduleMode != "off" { currentDir := srcDir for { - dat, err := ioutil.ReadFile(filepath.Join(currentDir, "go.mod")) + dat, err := os.ReadFile(filepath.Join(currentDir, "go.mod")) if os.IsNotExist(err) { if currentDir == filepath.Dir(currentDir) { // at the root diff --git a/vendor/github.com/golang/mock/mockgen/model/model.go b/vendor/go.uber.org/mock/mockgen/model/model.go similarity index 87% rename from vendor/github.com/golang/mock/mockgen/model/model.go rename to vendor/go.uber.org/mock/mockgen/model/model.go index 2c6a62ceb..e2dde5385 100644 --- a/vendor/github.com/golang/mock/mockgen/model/model.go +++ b/vendor/go.uber.org/mock/mockgen/model/model.go @@ -24,7 +24,7 @@ import ( ) // pkgPath is the importable path for package model -const pkgPath = "github.com/golang/mock/mockgen/model" +const pkgPath = "go.uber.org/mock/mockgen/model" // Package is a Go package. It may be a subset. type Package struct { @@ -47,14 +47,18 @@ func (pkg *Package) Imports() map[string]bool { im := make(map[string]bool) for _, intf := range pkg.Interfaces { intf.addImports(im) + for _, tp := range intf.TypeParams { + tp.Type.addImports(im) + } } return im } // Interface is a Go interface. type Interface struct { - Name string - Methods []*Method + Name string + Methods []*Method + TypeParams []*Parameter } // Print writes the interface name and its methods. @@ -143,12 +147,14 @@ type Type interface { } func init() { - gob.Register(&ArrayType{}) - gob.Register(&ChanType{}) - gob.Register(&FuncType{}) - gob.Register(&MapType{}) - gob.Register(&NamedType{}) - gob.Register(&PointerType{}) + // Call gob.RegisterName with pkgPath as prefix to avoid conflicting with + // github.com/golang/mock/mockgen/model 's registration. + gob.RegisterName(pkgPath+".ArrayType", &ArrayType{}) + gob.RegisterName(pkgPath+".ChanType", &ChanType{}) + gob.RegisterName(pkgPath+".FuncType", &FuncType{}) + gob.RegisterName(pkgPath+".MapType", &MapType{}) + gob.RegisterName(pkgPath+".NamedType", &NamedType{}) + gob.RegisterName(pkgPath+".PointerType", &PointerType{}) // Call gob.RegisterName to make sure it has the consistent name registered // for both gob decoder and encoder. @@ -156,7 +162,7 @@ func init() { // For a non-pointer type, gob.Register will try to get package full path by // calling rt.PkgPath() for a name to register. If your project has vendor // directory, it is possible that PkgPath will get a path like this: - // ../../../vendor/github.com/golang/mock/mockgen/model + // ../../../vendor/go.uber.org/mock/mockgen/model gob.RegisterName(pkgPath+".PredeclaredType", PredeclaredType("")) } @@ -259,26 +265,28 @@ func (mt *MapType) addImports(im map[string]bool) { // NamedType is an exported type in a package. type NamedType struct { - Package string // may be empty - Type string + Package string // may be empty + Type string + TypeParams *TypeParametersType } func (nt *NamedType) String(pm map[string]string, pkgOverride string) string { if pkgOverride == nt.Package { - return nt.Type + return nt.Type + nt.TypeParams.String(pm, pkgOverride) } prefix := pm[nt.Package] if prefix != "" { - return prefix + "." + nt.Type + return prefix + "." + nt.Type + nt.TypeParams.String(pm, pkgOverride) } - return nt.Type + return nt.Type + nt.TypeParams.String(pm, pkgOverride) } func (nt *NamedType) addImports(im map[string]bool) { if nt.Package != "" { im[nt.Package] = true } + nt.TypeParams.addImports(im) } // PointerType is a pointer to another type. @@ -297,6 +305,36 @@ type PredeclaredType string func (pt PredeclaredType) String(map[string]string, string) string { return string(pt) } func (pt PredeclaredType) addImports(map[string]bool) {} +// TypeParametersType contains type paramters for a NamedType. +type TypeParametersType struct { + TypeParameters []Type +} + +func (tp *TypeParametersType) String(pm map[string]string, pkgOverride string) string { + if tp == nil || len(tp.TypeParameters) == 0 { + return "" + } + var sb strings.Builder + sb.WriteString("[") + for i, v := range tp.TypeParameters { + if i != 0 { + sb.WriteString(", ") + } + sb.WriteString(v.String(pm, pkgOverride)) + } + sb.WriteString("]") + return sb.String() +} + +func (tp *TypeParametersType) addImports(im map[string]bool) { + if tp == nil { + return + } + for _, v := range tp.TypeParameters { + v.addImports(im) + } +} + // The following code is intended to be called by the program generated by ../reflect.go. // InterfaceFromInterfaceType returns a pointer to an interface for the @@ -431,7 +469,7 @@ func typeFromType(t reflect.Type) (Type, error) { case reflect.Interface: // Two special interfaces. if t.NumMethod() == 0 { - return PredeclaredType("interface{}"), nil + return PredeclaredType("any"), nil } if t == errorType { return PredeclaredType("error"), nil diff --git a/vendor/github.com/golang/mock/mockgen/parse.go b/vendor/go.uber.org/mock/mockgen/parse.go similarity index 63% rename from vendor/github.com/golang/mock/mockgen/parse.go rename to vendor/go.uber.org/mock/mockgen/parse.go index bf6902cd5..952140998 100644 --- a/vendor/github.com/golang/mock/mockgen/parse.go +++ b/vendor/go.uber.org/mock/mockgen/parse.go @@ -18,7 +18,6 @@ package main import ( "errors" - "flag" "fmt" "go/ast" "go/build" @@ -26,19 +25,14 @@ import ( "go/parser" "go/token" "go/types" - "io/ioutil" "log" + "os" "path" "path/filepath" "strconv" "strings" - "github.com/golang/mock/mockgen/model" -) - -var ( - imports = flag.String("imports", "", "(source mode) Comma-separated name=path pairs of explicit imports to use.") - auxFiles = flag.String("aux_files", "", "(source mode) Comma-separated pkg=path pairs of auxiliary Go source files.") + "go.uber.org/mock/mockgen/model" ) // sourceMode generates mocks via source file. @@ -62,8 +56,8 @@ func sourceMode(source string) (*model.Package, error) { p := &fileParser{ fileSet: fs, imports: make(map[string]importedPackage), - importedInterfaces: make(map[string]map[string]*ast.InterfaceType), - auxInterfaces: make(map[string]map[string]*ast.InterfaceType), + importedInterfaces: newInterfaceCache(), + auxInterfaces: newInterfaceCache(), srcDir: srcDir, } @@ -81,6 +75,10 @@ func sourceMode(source string) (*model.Package, error) { } } + if *excludeInterfaces != "" { + p.excludeNamesSet = parseExcludeInterfaces(*excludeInterfaces) + } + // Handle -aux_files. if err := p.parseAuxFiles(*auxFiles); err != nil { return nil, err @@ -127,21 +125,55 @@ func (d duplicateImport) Error() string { func (d duplicateImport) Path() string { log.Fatal(d.Error()); return "" } func (d duplicateImport) Parser() *fileParser { log.Fatal(d.Error()); return nil } -type fileParser struct { - fileSet *token.FileSet - imports map[string]importedPackage // package name => imported package - importedInterfaces map[string]map[string]*ast.InterfaceType // package (or "") => name => interface - - auxFiles []*ast.File - auxInterfaces map[string]map[string]*ast.InterfaceType // package (or "") => name => interface - - srcDir string +type interfaceCache struct { + m map[string]map[string]*namedInterface } -func (p *fileParser) errorf(pos token.Pos, format string, args ...interface{}) error { +func newInterfaceCache() *interfaceCache { + return &interfaceCache{ + m: make(map[string]map[string]*namedInterface), + } +} + +func (i *interfaceCache) Set(pkg, name string, it *namedInterface) { + if _, ok := i.m[pkg]; !ok { + i.m[pkg] = make(map[string]*namedInterface) + } + i.m[pkg][name] = it +} + +func (i *interfaceCache) Get(pkg, name string) *namedInterface { + if _, ok := i.m[pkg]; !ok { + return nil + } + return i.m[pkg][name] +} + +func (i *interfaceCache) GetASTIface(pkg, name string) *ast.InterfaceType { + if _, ok := i.m[pkg]; !ok { + return nil + } + it, ok := i.m[pkg][name] + if !ok { + return nil + } + return it.it +} + +type fileParser struct { + fileSet *token.FileSet + imports map[string]importedPackage // package name => imported package + importedInterfaces *interfaceCache + auxFiles []*ast.File + auxInterfaces *interfaceCache + srcDir string + excludeNamesSet map[string]struct{} +} + +func (p *fileParser) errorf(pos token.Pos, format string, args ...any) error { ps := p.fileSet.Position(pos) format = "%s:%d:%d: " + format - args = append([]interface{}{ps.Filename, ps.Line, ps.Column}, args...) + args = append([]any{ps.Filename, ps.Line, ps.Column}, args...) return fmt.Errorf(format, args...) } @@ -168,11 +200,8 @@ func (p *fileParser) parseAuxFiles(auxFiles string) error { } func (p *fileParser) addAuxInterfacesFromFile(pkg string, file *ast.File) { - if _, ok := p.auxInterfaces[pkg]; !ok { - p.auxInterfaces[pkg] = make(map[string]*ast.InterfaceType) - } for ni := range iterInterfaces(file) { - p.auxInterfaces[pkg][ni.name.Name] = ni.it + p.auxInterfaces.Set(pkg, ni.name.Name, ni) } } @@ -199,7 +228,10 @@ func (p *fileParser) parseFile(importPath string, file *ast.File) (*model.Packag var is []*model.Interface for ni := range iterInterfaces(file) { - i, err := p.parseInterface(ni.name.String(), importPath, ni.it) + if _, ok := p.excludeNamesSet[ni.name.String()]; ok { + continue + } + i, err := p.parseInterface(ni.name.String(), importPath, ni) if err != nil { return nil, err } @@ -219,8 +251,8 @@ func (p *fileParser) parsePackage(path string) (*fileParser, error) { newP := &fileParser{ fileSet: token.NewFileSet(), imports: make(map[string]importedPackage), - importedInterfaces: make(map[string]map[string]*ast.InterfaceType), - auxInterfaces: make(map[string]map[string]*ast.InterfaceType), + importedInterfaces: newInterfaceCache(), + auxInterfaces: newInterfaceCache(), srcDir: p.srcDir, } @@ -233,11 +265,8 @@ func (p *fileParser) parsePackage(path string) (*fileParser, error) { for _, pkg := range pkgs { file := ast.MergePackageFiles(pkg, ast.FilterFuncDuplicates|ast.FilterUnassociatedComments|ast.FilterImportDuplicates) - if _, ok := newP.importedInterfaces[path]; !ok { - newP.importedInterfaces[path] = make(map[string]*ast.InterfaceType) - } for ni := range iterInterfaces(file) { - newP.importedInterfaces[path][ni.name.Name] = ni.it + newP.importedInterfaces.Set(path, ni.name.Name, ni) } imports, _ := importsOfFile(file) for pkgName, pkgI := range imports { @@ -247,9 +276,77 @@ func (p *fileParser) parsePackage(path string) (*fileParser, error) { return newP, nil } -func (p *fileParser) parseInterface(name, pkg string, it *ast.InterfaceType) (*model.Interface, error) { +func (p *fileParser) constructInstParams(pkg string, params []*ast.Field, instParams []model.Type, embeddedInstParams []ast.Expr, tps map[string]model.Type) ([]model.Type, error) { + pm := make(map[string]int) + var i int + for _, v := range params { + for _, n := range v.Names { + pm[n.Name] = i + instParams = append(instParams, model.PredeclaredType(n.Name)) + i++ + } + } + + var runtimeInstParams []model.Type + for _, instParam := range embeddedInstParams { + switch t := instParam.(type) { + case *ast.Ident: + if idx, ok := pm[t.Name]; ok { + runtimeInstParams = append(runtimeInstParams, instParams[idx]) + continue + } + } + modelType, err := p.parseType(pkg, instParam, tps) + if err != nil { + return nil, err + } + runtimeInstParams = append(runtimeInstParams, modelType) + } + + return runtimeInstParams, nil +} + +func (p *fileParser) constructTps(it *namedInterface) (tps map[string]model.Type) { + tps = make(map[string]model.Type) + n := 0 + for _, tp := range it.typeParams { + for _, tm := range tp.Names { + tps[tm.Name] = nil + if len(it.instTypes) != 0 { + tps[tm.Name] = it.instTypes[n] + n++ + } + } + } + return tps +} + +// parseInterface loads interface specified by pkg and name, parses it and returns +// a new model with the parsed. +func (p *fileParser) parseInterface(name, pkg string, it *namedInterface) (*model.Interface, error) { iface := &model.Interface{Name: name} - for _, field := range it.Methods.List { + tps := p.constructTps(it) + tp, err := p.parseFieldList(pkg, it.typeParams, tps) + if err != nil { + return nil, fmt.Errorf("unable to parse interface type parameters: %v", name) + } + + iface.TypeParams = tp + for _, field := range it.it.Methods.List { + var methods []*model.Method + if methods, err = p.parseMethod(field, it, iface, pkg, tps); err != nil { + return nil, err + } + for _, m := range methods { + iface.AddMethod(m) + } + } + return iface, nil +} + +func (p *fileParser) parseMethod(field *ast.Field, it *namedInterface, iface *model.Interface, pkg string, tps map[string]model.Type) ([]*model.Method, error) { + // {} for git diff + { switch v := field.Type.(type) { case *ast.FuncType: if nn := len(field.Names); nn != 1 { @@ -259,37 +356,55 @@ func (p *fileParser) parseInterface(name, pkg string, it *ast.InterfaceType) (*m Name: field.Names[0].String(), } var err error - m.In, m.Variadic, m.Out, err = p.parseFunc(pkg, v) + m.In, m.Variadic, m.Out, err = p.parseFunc(pkg, v, tps) if err != nil { return nil, err } - iface.AddMethod(m) + return []*model.Method{m}, nil case *ast.Ident: // Embedded interface in this package. - embeddedIfaceType := p.auxInterfaces[pkg][v.String()] + embeddedIfaceType := p.auxInterfaces.Get(pkg, v.String()) if embeddedIfaceType == nil { - embeddedIfaceType = p.importedInterfaces[pkg][v.String()] + embeddedIfaceType = p.importedInterfaces.Get(pkg, v.String()) } var embeddedIface *model.Interface if embeddedIfaceType != nil { var err error + embeddedIfaceType.instTypes, err = p.constructInstParams(pkg, it.typeParams, it.instTypes, it.embeddedInstTypeParams, tps) + if err != nil { + return nil, err + } embeddedIface, err = p.parseInterface(v.String(), pkg, embeddedIfaceType) if err != nil { return nil, err } + } else { // This is built-in error interface. if v.String() == model.ErrorInterface.Name { embeddedIface = &model.ErrorInterface } else { - return nil, p.errorf(v.Pos(), "unknown embedded interface %s", v.String()) + ip, err := p.parsePackage(pkg) + if err != nil { + return nil, p.errorf(v.Pos(), "could not parse package %s: %v", pkg, err) + } + + if embeddedIfaceType = ip.importedInterfaces.Get(pkg, v.String()); embeddedIfaceType == nil { + return nil, p.errorf(v.Pos(), "unknown embedded interface %s.%s", pkg, v.String()) + } + + embeddedIfaceType.instTypes, err = p.constructInstParams(pkg, it.typeParams, it.instTypes, it.embeddedInstTypeParams, tps) + if err != nil { + return nil, err + } + embeddedIface, err = ip.parseInterface(v.String(), pkg, embeddedIfaceType) + if err != nil { + return nil, err + } } } - // Copy the methods. - for _, m := range embeddedIface.Methods { - iface.AddMethod(m) - } + return embeddedIface.Methods, nil case *ast.SelectorExpr: // Embedded interface in another package. filePkg, sel := v.X.(*ast.Ident).String(), v.Sel.String() @@ -300,8 +415,12 @@ func (p *fileParser) parseInterface(name, pkg string, it *ast.InterfaceType) (*m var embeddedIface *model.Interface var err error - embeddedIfaceType := p.auxInterfaces[filePkg][sel] + embeddedIfaceType := p.auxInterfaces.Get(filePkg, sel) if embeddedIfaceType != nil { + embeddedIfaceType.instTypes, err = p.constructInstParams(pkg, it.typeParams, it.instTypes, it.embeddedInstTypeParams, tps) + if err != nil { + return nil, err + } embeddedIface, err = p.parseInterface(sel, filePkg, embeddedIfaceType) if err != nil { return nil, err @@ -320,46 +439,47 @@ func (p *fileParser) parseInterface(name, pkg string, it *ast.InterfaceType) (*m parser: parser, } } - if embeddedIfaceType = parser.importedInterfaces[path][sel]; embeddedIfaceType == nil { + if embeddedIfaceType = parser.importedInterfaces.Get(path, sel); embeddedIfaceType == nil { return nil, p.errorf(v.Pos(), "unknown embedded interface %s.%s", path, sel) } + + embeddedIfaceType.instTypes, err = p.constructInstParams(pkg, it.typeParams, it.instTypes, it.embeddedInstTypeParams, tps) + if err != nil { + return nil, err + } embeddedIface, err = parser.parseInterface(sel, path, embeddedIfaceType) if err != nil { return nil, err } } - // Copy the methods. // TODO: apply shadowing rules. - for _, m := range embeddedIface.Methods { - iface.AddMethod(m) - } + return embeddedIface.Methods, nil default: - return nil, fmt.Errorf("don't know how to mock method of type %T", field.Type) + return p.parseGenericMethod(field, it, iface, pkg, tps) } } - return iface, nil } -func (p *fileParser) parseFunc(pkg string, f *ast.FuncType) (inParam []*model.Parameter, variadic *model.Parameter, outParam []*model.Parameter, err error) { +func (p *fileParser) parseFunc(pkg string, f *ast.FuncType, tps map[string]model.Type) (inParam []*model.Parameter, variadic *model.Parameter, outParam []*model.Parameter, err error) { if f.Params != nil { regParams := f.Params.List if isVariadic(f) { n := len(regParams) varParams := regParams[n-1:] regParams = regParams[:n-1] - vp, err := p.parseFieldList(pkg, varParams) + vp, err := p.parseFieldList(pkg, varParams, tps) if err != nil { return nil, nil, nil, p.errorf(varParams[0].Pos(), "failed parsing variadic argument: %v", err) } variadic = vp[0] } - inParam, err = p.parseFieldList(pkg, regParams) + inParam, err = p.parseFieldList(pkg, regParams, tps) if err != nil { return nil, nil, nil, p.errorf(f.Pos(), "failed parsing arguments: %v", err) } } if f.Results != nil { - outParam, err = p.parseFieldList(pkg, f.Results.List) + outParam, err = p.parseFieldList(pkg, f.Results.List, tps) if err != nil { return nil, nil, nil, p.errorf(f.Pos(), "failed parsing returns: %v", err) } @@ -367,7 +487,7 @@ func (p *fileParser) parseFunc(pkg string, f *ast.FuncType) (inParam []*model.Pa return } -func (p *fileParser) parseFieldList(pkg string, fields []*ast.Field) ([]*model.Parameter, error) { +func (p *fileParser) parseFieldList(pkg string, fields []*ast.Field, tps map[string]model.Type) ([]*model.Parameter, error) { nf := 0 for _, f := range fields { nn := len(f.Names) @@ -382,7 +502,7 @@ func (p *fileParser) parseFieldList(pkg string, fields []*ast.Field) ([]*model.P ps := make([]*model.Parameter, nf) i := 0 // destination index for _, f := range fields { - t, err := p.parseType(pkg, f.Type) + t, err := p.parseType(pkg, f.Type, tps) if err != nil { return nil, err } @@ -401,44 +521,27 @@ func (p *fileParser) parseFieldList(pkg string, fields []*ast.Field) ([]*model.P return ps, nil } -func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) { +func (p *fileParser) parseType(pkg string, typ ast.Expr, tps map[string]model.Type) (model.Type, error) { switch v := typ.(type) { case *ast.ArrayType: ln := -1 if v.Len != nil { - var value string - switch val := v.Len.(type) { - case (*ast.BasicLit): - value = val.Value - case (*ast.Ident): - // when the length is a const defined locally - value = val.Obj.Decl.(*ast.ValueSpec).Values[0].(*ast.BasicLit).Value - case (*ast.SelectorExpr): - // when the length is a const defined in an external package - usedPkg, err := importer.Default().Import(fmt.Sprintf("%s", val.X)) - if err != nil { - return nil, p.errorf(v.Len.Pos(), "unknown package in array length: %v", err) - } - ev, err := types.Eval(token.NewFileSet(), usedPkg, token.NoPos, val.Sel.Name) - if err != nil { - return nil, p.errorf(v.Len.Pos(), "unknown constant in array length: %v", err) - } - value = ev.Value.String() + value, err := p.parseArrayLength(v.Len) + if err != nil { + return nil, err } - - x, err := strconv.Atoi(value) + ln, err = strconv.Atoi(value) if err != nil { return nil, p.errorf(v.Len.Pos(), "bad array size: %v", err) } - ln = x } - t, err := p.parseType(pkg, v.Elt) + t, err := p.parseType(pkg, v.Elt, tps) if err != nil { return nil, err } return &model.ArrayType{Len: ln, Type: t}, nil case *ast.ChanType: - t, err := p.parseType(pkg, v.Value) + t, err := p.parseType(pkg, v.Value, tps) if err != nil { return nil, err } @@ -452,15 +555,16 @@ func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) { return &model.ChanType{Dir: dir, Type: t}, nil case *ast.Ellipsis: // assume we're parsing a variadic argument - return p.parseType(pkg, v.Elt) + return p.parseType(pkg, v.Elt, tps) case *ast.FuncType: - in, variadic, out, err := p.parseFunc(pkg, v) + in, variadic, out, err := p.parseFunc(pkg, v, tps) if err != nil { return nil, err } return &model.FuncType{In: in, Out: out, Variadic: variadic}, nil case *ast.Ident: - if v.IsExported() { + it, ok := tps[v.Name] + if v.IsExported() && !ok { // `pkg` may be an aliased imported pkg // if so, patch the import w/ the fully qualified import maybeImportedPkg, ok := p.imports[pkg] @@ -470,20 +574,22 @@ func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) { // assume type in this package return &model.NamedType{Package: pkg, Type: v.Name}, nil } - + if ok && it != nil { + return it, nil + } // assume predeclared type return model.PredeclaredType(v.Name), nil case *ast.InterfaceType: if v.Methods != nil && len(v.Methods.List) > 0 { return nil, p.errorf(v.Pos(), "can't handle non-empty unnamed interface types") } - return model.PredeclaredType("interface{}"), nil + return model.PredeclaredType("any"), nil case *ast.MapType: - key, err := p.parseType(pkg, v.Key) + key, err := p.parseType(pkg, v.Key, tps) if err != nil { return nil, err } - value, err := p.parseType(pkg, v.Value) + value, err := p.parseType(pkg, v.Value, tps) if err != nil { return nil, err } @@ -496,7 +602,7 @@ func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) { } return &model.NamedType{Package: pkg.Path(), Type: v.Sel.String()}, nil case *ast.StarExpr: - t, err := p.parseType(pkg, v.X) + t, err := p.parseType(pkg, v.X, tps) if err != nil { return nil, err } @@ -507,12 +613,61 @@ func (p *fileParser) parseType(pkg string, typ ast.Expr) (model.Type, error) { } return model.PredeclaredType("struct{}"), nil case *ast.ParenExpr: - return p.parseType(pkg, v.X) + return p.parseType(pkg, v.X, tps) + default: + mt, err := p.parseGenericType(pkg, typ, tps) + if err != nil { + return nil, err + } + if mt == nil { + break + } + return mt, nil } return nil, fmt.Errorf("don't know how to parse type %T", typ) } +func (p *fileParser) parseArrayLength(expr ast.Expr) (string, error) { + switch val := expr.(type) { + case (*ast.BasicLit): + return val.Value, nil + case (*ast.Ident): + // when the length is a const defined locally + return val.Obj.Decl.(*ast.ValueSpec).Values[0].(*ast.BasicLit).Value, nil + case (*ast.SelectorExpr): + // when the length is a const defined in an external package + usedPkg, err := importer.Default().Import(fmt.Sprintf("%s", val.X)) + if err != nil { + return "", p.errorf(expr.Pos(), "unknown package in array length: %v", err) + } + ev, err := types.Eval(token.NewFileSet(), usedPkg, token.NoPos, val.Sel.Name) + if err != nil { + return "", p.errorf(expr.Pos(), "unknown constant in array length: %v", err) + } + return ev.Value.String(), nil + case (*ast.ParenExpr): + return p.parseArrayLength(val.X) + case (*ast.BinaryExpr): + x, err := p.parseArrayLength(val.X) + if err != nil { + return "", err + } + y, err := p.parseArrayLength(val.Y) + if err != nil { + return "", err + } + biExpr := fmt.Sprintf("%s%v%s", x, val.Op, y) + tv, err := types.Eval(token.NewFileSet(), nil, token.NoPos, biExpr) + if err != nil { + return "", p.errorf(expr.Pos(), "invalid expression in array length: %v", err) + } + return tv.Value.String(), nil + default: + return "", p.errorf(expr.Pos(), "invalid expression in array length: %v", val) + } +} + // importsOfFile returns a map of package name to import path // of the imports in file. func importsOfFile(file *ast.File) (normalImports map[string]importedPackage, dotImports []string) { @@ -575,13 +730,16 @@ func importsOfFile(file *ast.File) (normalImports map[string]importedPackage, do } type namedInterface struct { - name *ast.Ident - it *ast.InterfaceType + name *ast.Ident + it *ast.InterfaceType + typeParams []*ast.Field + embeddedInstTypeParams []ast.Expr + instTypes []model.Type } // Create an iterator over all interfaces in file. -func iterInterfaces(file *ast.File) <-chan namedInterface { - ch := make(chan namedInterface) +func iterInterfaces(file *ast.File) <-chan *namedInterface { + ch := make(chan *namedInterface) go func() { for _, decl := range file.Decls { gd, ok := decl.(*ast.GenDecl) @@ -598,7 +756,7 @@ func iterInterfaces(file *ast.File) <-chan namedInterface { continue } - ch <- namedInterface{ts.Name, it} + ch <- &namedInterface{name: ts.Name, it: it, typeParams: getTypeSpecTypeParams(ts)} } } close(ch) @@ -618,7 +776,7 @@ func isVariadic(f *ast.FuncType) bool { // packageNameOfDir get package import path via dir func packageNameOfDir(srcDir string) (string, error) { - files, err := ioutil.ReadDir(srcDir) + files, err := os.ReadDir(srcDir) if err != nil { log.Fatal(err) } diff --git a/vendor/github.com/golang/mock/mockgen/reflect.go b/vendor/go.uber.org/mock/mockgen/reflect.go similarity index 93% rename from vendor/github.com/golang/mock/mockgen/reflect.go rename to vendor/go.uber.org/mock/mockgen/reflect.go index e24efce0b..8f519f6a5 100644 --- a/vendor/github.com/golang/mock/mockgen/reflect.go +++ b/vendor/go.uber.org/mock/mockgen/reflect.go @@ -23,7 +23,6 @@ import ( "fmt" "go/build" "io" - "io/ioutil" "log" "os" "os/exec" @@ -32,7 +31,7 @@ import ( "strings" "text/template" - "github.com/golang/mock/mockgen/model" + "go.uber.org/mock/mockgen/model" ) var ( @@ -92,7 +91,7 @@ func writeProgram(importPath string, symbols []string) ([]byte, error) { // run the given program and parse the output as a model.Package. func run(program string) (*model.Package, error) { - f, err := ioutil.TempFile("", "") + f, err := os.CreateTemp("", "") if err != nil { return nil, err } @@ -133,7 +132,7 @@ func run(program string) (*model.Package, error) { // parses the output as a model.Package. func runInDir(program []byte, dir string) (*model.Package, error) { // We use TempDir instead of TempFile so we can control the filename. - tmpDir, err := ioutil.TempDir(dir, "gomock_reflect_") + tmpDir, err := os.MkdirTemp(dir, "gomock_reflect_") if err != nil { return nil, err } @@ -149,7 +148,7 @@ func runInDir(program []byte, dir string) (*model.Package, error) { progBinary += ".exe" } - if err := ioutil.WriteFile(filepath.Join(tmpDir, progSource), program, 0600); err != nil { + if err := os.WriteFile(filepath.Join(tmpDir, progSource), program, 0600); err != nil { return nil, err } @@ -169,8 +168,8 @@ func runInDir(program []byte, dir string) (*model.Package, error) { if err := cmd.Run(); err != nil { sErr := buf.String() if strings.Contains(sErr, `cannot find package "."`) && - strings.Contains(sErr, "github.com/golang/mock/mockgen/model") { - fmt.Fprint(os.Stderr, "Please reference the steps in the README to fix this error:\n\thttps://github.com/golang/mock#reflect-vendoring-error.") + strings.Contains(sErr, "go.uber.org/mock/mockgen/model") { + fmt.Fprint(os.Stderr, "Please reference the steps in the README to fix this error:\n\thttps://go.uber.org/mock#reflect-vendoring-error.\n") return nil, err } return nil, err @@ -198,7 +197,7 @@ import ( "path" "reflect" - "github.com/golang/mock/mockgen/model" + "go.uber.org/mock/mockgen/model" pkg_ {{printf "%q" .ImportPath}} ) diff --git a/vendor/github.com/golang/mock/mockgen/version.1.12.go b/vendor/go.uber.org/mock/mockgen/version.go similarity index 94% rename from vendor/github.com/golang/mock/mockgen/version.1.12.go rename to vendor/go.uber.org/mock/mockgen/version.go index ad121ae63..6db160ac2 100644 --- a/vendor/github.com/golang/mock/mockgen/version.1.12.go +++ b/vendor/go.uber.org/mock/mockgen/version.go @@ -1,4 +1,4 @@ -// Copyright 2019 Google LLC +// Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,9 +11,6 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// - -// +build go1.12 package main @@ -31,5 +28,4 @@ func printModuleVersion() { "GO111MODULE=on when running 'go get' in order to use specific " + "version of the binary.") } - } diff --git a/vendor/go.uber.org/zap/.golangci.yml b/vendor/go.uber.org/zap/.golangci.yml new file mode 100644 index 000000000..2346df135 --- /dev/null +++ b/vendor/go.uber.org/zap/.golangci.yml @@ -0,0 +1,77 @@ +output: + # Make output more digestible with quickfix in vim/emacs/etc. + sort-results: true + print-issued-lines: false + +linters: + # We'll track the golangci-lint default linters manually + # instead of letting them change without our control. + disable-all: true + enable: + # golangci-lint defaults: + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + - unused + + # Our own extras: + - gofumpt + - nolintlint # lints nolint directives + - revive + +linters-settings: + govet: + # These govet checks are disabled by default, but they're useful. + enable: + - niliness + - reflectvaluecompare + - sortslice + - unusedwrite + + errcheck: + exclude-functions: + # These methods can not fail. + # They operate on an in-memory buffer. + - (*go.uber.org/zap/buffer.Buffer).Write + - (*go.uber.org/zap/buffer.Buffer).WriteByte + - (*go.uber.org/zap/buffer.Buffer).WriteString + + - (*go.uber.org/zap/zapio.Writer).Close + - (*go.uber.org/zap/zapio.Writer).Sync + - (*go.uber.org/zap/zapio.Writer).Write + # Write to zapio.Writer cannot fail, + # so io.WriteString on it cannot fail. + - io.WriteString(*go.uber.org/zap/zapio.Writer) + + # Writing a plain string to a fmt.State cannot fail. + - io.WriteString(fmt.State) + +issues: + # Print all issues reported by all linters. + max-issues-per-linter: 0 + max-same-issues: 0 + + # Don't ignore some of the issues that golangci-lint considers okay. + # This includes documenting all exported entities. + exclude-use-default: false + + exclude-rules: + # Don't warn on unused parameters. + # Parameter names are useful; replacing them with '_' is undesirable. + - linters: [revive] + text: 'unused-parameter: parameter \S+ seems to be unused, consider removing or renaming it as _' + + # staticcheck already has smarter checks for empty blocks. + # revive's empty-block linter has false positives. + # For example, as of writing this, the following is not allowed. + # for foo() { } + - linters: [revive] + text: 'empty-block: this block is empty, you can remove it' + + # Ignore logger.Sync() errcheck failures in example_test.go + # since those are intended to be uncomplicated examples. + - linters: [errcheck] + path: example_test.go + text: 'Error return value of `logger.Sync` is not checked' diff --git a/vendor/go.uber.org/zap/.readme.tmpl b/vendor/go.uber.org/zap/.readme.tmpl index 92aa65d66..4fea3027a 100644 --- a/vendor/go.uber.org/zap/.readme.tmpl +++ b/vendor/go.uber.org/zap/.readme.tmpl @@ -1,7 +1,15 @@ # :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] +
+ Blazing fast, structured, leveled logging in Go. +![Zap logo](assets/logo.png) + +[![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] + +
+ ## Installation `go get -u go.uber.org/zap` @@ -92,7 +100,7 @@ standard.
-Released under the [MIT License](LICENSE.txt). +Released under the [MIT License](LICENSE). 1 In particular, keep in mind that we may be benchmarking against slightly older versions of other packages. Versions are diff --git a/vendor/go.uber.org/zap/CHANGELOG.md b/vendor/go.uber.org/zap/CHANGELOG.md index 0db1f9f15..6d6cd5f4d 100644 --- a/vendor/go.uber.org/zap/CHANGELOG.md +++ b/vendor/go.uber.org/zap/CHANGELOG.md @@ -1,7 +1,55 @@ # Changelog All notable changes to this project will be documented in this file. -This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 1.27.0 (20 Feb 2024) +Enhancements: +* [#1378][]: Add `WithLazy` method for `SugaredLogger`. +* [#1399][]: zaptest: Add `NewTestingWriter` for customizing TestingWriter with more flexibility than `NewLogger`. +* [#1406][]: Add `Log`, `Logw`, `Logln` methods for `SugaredLogger`. +* [#1416][]: Add `WithPanicHook` option for testing panic logs. + +Thanks to @defval, @dimmo, @arxeiss, and @MKrupauskas for their contributions to this release. + +[#1378]: https://github.com/uber-go/zap/pull/1378 +[#1399]: https://github.com/uber-go/zap/pull/1399 +[#1406]: https://github.com/uber-go/zap/pull/1406 +[#1416]: https://github.com/uber-go/zap/pull/1416 + +## 1.26.0 (14 Sep 2023) +Enhancements: +* [#1297][]: Add Dict as a Field. +* [#1319][]: Add `WithLazy` method to `Logger` which lazily evaluates the structured +context. +* [#1350][]: String encoding is much (~50%) faster now. + +Thanks to @hhk7734, @jquirke, and @cdvr1993 for their contributions to this release. + +[#1297]: https://github.com/uber-go/zap/pull/1297 +[#1319]: https://github.com/uber-go/zap/pull/1319 +[#1350]: https://github.com/uber-go/zap/pull/1350 + +## 1.25.0 (1 Aug 2023) + +This release contains several improvements including performance, API additions, +and two new experimental packages whose APIs are unstable and may change in the +future. + +Enhancements: +* [#1246][]: Add `zap/exp/zapslog` package for integration with slog. +* [#1273][]: Add `Name` to `Logger` which returns the Logger's name if one is set. +* [#1281][]: Add `zap/exp/expfield` package which contains helper methods +`Str` and `Strs` for constructing String-like zap.Fields. +* [#1310][]: Reduce stack size on `Any`. + +Thanks to @knight42, @dzakaammar, @bcspragu, and @rexywork for their contributions +to this release. + +[#1246]: https://github.com/uber-go/zap/pull/1246 +[#1273]: https://github.com/uber-go/zap/pull/1273 +[#1281]: https://github.com/uber-go/zap/pull/1281 +[#1310]: https://github.com/uber-go/zap/pull/1310 ## 1.24.0 (30 Nov 2022) @@ -27,7 +75,6 @@ Enhancements: [#1147]: https://github.com/uber-go/zap/pull/1147 [#1155]: https://github.com/uber-go/zap/pull/1155 - ## 1.22.0 (8 Aug 2022) Enhancements: @@ -176,6 +223,16 @@ Enhancements: Thanks to @ash2k, @FMLS, @jimmystewpot, @Oncilla, @tsoslow, @tylitianrui, @withshubh, and @wziww for their contributions to this release. +[#865]: https://github.com/uber-go/zap/pull/865 +[#867]: https://github.com/uber-go/zap/pull/867 +[#881]: https://github.com/uber-go/zap/pull/881 +[#903]: https://github.com/uber-go/zap/pull/903 +[#912]: https://github.com/uber-go/zap/pull/912 +[#913]: https://github.com/uber-go/zap/pull/913 +[#928]: https://github.com/uber-go/zap/pull/928 +[#931]: https://github.com/uber-go/zap/pull/931 +[#936]: https://github.com/uber-go/zap/pull/936 + ## 1.16.0 (1 Sep 2020) Bugfixes: @@ -197,6 +254,17 @@ Enhancements: Thanks to @SteelPhase, @tmshn, @lixingwang, @wyxloading, @moul, @segevfiner, @andy-retailnext and @jcorbin for their contributions to this release. +[#629]: https://github.com/uber-go/zap/pull/629 +[#697]: https://github.com/uber-go/zap/pull/697 +[#828]: https://github.com/uber-go/zap/pull/828 +[#835]: https://github.com/uber-go/zap/pull/835 +[#843]: https://github.com/uber-go/zap/pull/843 +[#844]: https://github.com/uber-go/zap/pull/844 +[#852]: https://github.com/uber-go/zap/pull/852 +[#854]: https://github.com/uber-go/zap/pull/854 +[#861]: https://github.com/uber-go/zap/pull/861 +[#862]: https://github.com/uber-go/zap/pull/862 + ## 1.15.0 (23 Apr 2020) Bugfixes: @@ -213,6 +281,11 @@ Enhancements: Thanks to @danielbprice for their contributions to this release. +[#804]: https://github.com/uber-go/zap/pull/804 +[#812]: https://github.com/uber-go/zap/pull/812 +[#806]: https://github.com/uber-go/zap/pull/806 +[#813]: https://github.com/uber-go/zap/pull/813 + ## 1.14.1 (14 Mar 2020) Bugfixes: @@ -225,6 +298,10 @@ Bugfixes: Thanks to @YashishDua for their contributions to this release. +[#791]: https://github.com/uber-go/zap/pull/791 +[#795]: https://github.com/uber-go/zap/pull/795 +[#799]: https://github.com/uber-go/zap/pull/799 + ## 1.14.0 (20 Feb 2020) Enhancements: @@ -235,6 +312,11 @@ Enhancements: Thanks to @caibirdme for their contributions to this release. +[#771]: https://github.com/uber-go/zap/pull/771 +[#773]: https://github.com/uber-go/zap/pull/773 +[#775]: https://github.com/uber-go/zap/pull/775 +[#786]: https://github.com/uber-go/zap/pull/786 + ## 1.13.0 (13 Nov 2019) Enhancements: @@ -243,11 +325,15 @@ Enhancements: Thanks to @jbizzle for their contributions to this release. +[#758]: https://github.com/uber-go/zap/pull/758 + ## 1.12.0 (29 Oct 2019) Enhancements: * [#751][]: Migrate to Go modules. +[#751]: https://github.com/uber-go/zap/pull/751 + ## 1.11.0 (21 Oct 2019) Enhancements: @@ -256,6 +342,9 @@ Enhancements: Thanks to @juicemia, @uhthomas for their contributions to this release. +[#725]: https://github.com/uber-go/zap/pull/725 +[#736]: https://github.com/uber-go/zap/pull/736 + ## 1.10.0 (29 Apr 2019) Bugfixes: @@ -273,13 +362,21 @@ Enhancements: Thanks to @iaroslav-ciupin, @lelenanam, @joa, @NWilson for their contributions to this release. -## v1.9.1 (06 Aug 2018) +[#657]: https://github.com/uber-go/zap/pull/657 +[#706]: https://github.com/uber-go/zap/pull/706 +[#610]: https://github.com/uber-go/zap/pull/610 +[#675]: https://github.com/uber-go/zap/pull/675 +[#704]: https://github.com/uber-go/zap/pull/704 + +## 1.9.1 (06 Aug 2018) Bugfixes: * [#614][]: MapObjectEncoder should not ignore empty slices. -## v1.9.0 (19 Jul 2018) +[#614]: https://github.com/uber-go/zap/pull/614 + +## 1.9.0 (19 Jul 2018) Enhancements: * [#602][]: Reduce number of allocations when logging with reflection. @@ -288,7 +385,11 @@ Enhancements: Thanks to @nfarah86, @AlekSi, @JeanMertz, @philippgille, @etsangsplk, and @dimroc for their contributions to this release. -## v1.8.0 (13 Apr 2018) +[#602]: https://github.com/uber-go/zap/pull/602 +[#572]: https://github.com/uber-go/zap/pull/572 +[#606]: https://github.com/uber-go/zap/pull/606 + +## 1.8.0 (13 Apr 2018) Enhancements: * [#508][]: Make log level configurable when redirecting the standard @@ -301,19 +402,28 @@ Bugfixes: Thanks to @DiSiqueira and @djui for their contributions to this release. -## v1.7.1 (25 Sep 2017) +[#508]: https://github.com/uber-go/zap/pull/508 +[#518]: https://github.com/uber-go/zap/pull/518 +[#577]: https://github.com/uber-go/zap/pull/577 +[#574]: https://github.com/uber-go/zap/pull/574 + +## 1.7.1 (25 Sep 2017) Bugfixes: * [#504][]: Store strings when using AddByteString with the map encoder. -## v1.7.0 (21 Sep 2017) +[#504]: https://github.com/uber-go/zap/pull/504 + +## 1.7.0 (21 Sep 2017) Enhancements: * [#487][]: Add `NewStdLogAt`, which extends `NewStdLog` by allowing the user to specify the level of the logged messages. -## v1.6.0 (30 Aug 2017) +[#487]: https://github.com/uber-go/zap/pull/487 + +## 1.6.0 (30 Aug 2017) Enhancements: @@ -321,7 +431,10 @@ Enhancements: * [#490][]: Add a `ContextMap` method to observer logs for simpler field validation in tests. -## v1.5.0 (22 Jul 2017) +[#490]: https://github.com/uber-go/zap/pull/490 +[#491]: https://github.com/uber-go/zap/pull/491 + +## 1.5.0 (22 Jul 2017) Enhancements: @@ -334,7 +447,12 @@ Bugfixes: Thanks to @richard-tunein and @pavius for their contributions to this release. -## v1.4.1 (08 Jun 2017) +[#477]: https://github.com/uber-go/zap/pull/477 +[#465]: https://github.com/uber-go/zap/pull/465 +[#460]: https://github.com/uber-go/zap/pull/460 +[#470]: https://github.com/uber-go/zap/pull/470 + +## 1.4.1 (08 Jun 2017) This release fixes two bugs. @@ -343,7 +461,10 @@ Bugfixes: * [#435][]: Support a variety of case conventions when unmarshaling levels. * [#444][]: Fix a panic in the observer. -## v1.4.0 (12 May 2017) +[#435]: https://github.com/uber-go/zap/pull/435 +[#444]: https://github.com/uber-go/zap/pull/444 + +## 1.4.0 (12 May 2017) This release adds a few small features and is fully backward-compatible. @@ -355,7 +476,11 @@ Enhancements: * [#431][]: Make `zap.AtomicLevel` implement `fmt.Stringer`, which makes a variety of operations a bit simpler. -## v1.3.0 (25 Apr 2017) +[#424]: https://github.com/uber-go/zap/pull/424 +[#425]: https://github.com/uber-go/zap/pull/425 +[#431]: https://github.com/uber-go/zap/pull/431 + +## 1.3.0 (25 Apr 2017) This release adds an enhancement to zap's testing helpers as well as the ability to marshal an AtomicLevel. It is fully backward-compatible. @@ -366,7 +491,10 @@ Enhancements: particularly useful when testing the `SugaredLogger`. * [#416][]: Make `AtomicLevel` implement `encoding.TextMarshaler`. -## v1.2.0 (13 Apr 2017) +[#415]: https://github.com/uber-go/zap/pull/415 +[#416]: https://github.com/uber-go/zap/pull/416 + +## 1.2.0 (13 Apr 2017) This release adds a gRPC compatibility wrapper. It is fully backward-compatible. @@ -375,7 +503,9 @@ Enhancements: * [#402][]: Add a `zapgrpc` package that wraps zap's Logger and implements `grpclog.Logger`. -## v1.1.0 (31 Mar 2017) +[#402]: https://github.com/uber-go/zap/pull/402 + +## 1.1.0 (31 Mar 2017) This release fixes two bugs and adds some enhancements to zap's testing helpers. It is fully backward-compatible. @@ -392,7 +522,11 @@ Enhancements: Thanks to @moitias for contributing to this release. -## v1.0.0 (14 Mar 2017) +[#385]: https://github.com/uber-go/zap/pull/385 +[#396]: https://github.com/uber-go/zap/pull/396 +[#386]: https://github.com/uber-go/zap/pull/386 + +## 1.0.0 (14 Mar 2017) This is zap's first stable release. All exported APIs are now final, and no further breaking changes will be made in the 1.x release series. Anyone using a @@ -437,7 +571,21 @@ Enhancements: Thanks to @suyash, @htrendev, @flisky, @Ulexus, and @skipor for their contributions to this release. -## v1.0.0-rc.3 (7 Mar 2017) +[#366]: https://github.com/uber-go/zap/pull/366 +[#364]: https://github.com/uber-go/zap/pull/364 +[#371]: https://github.com/uber-go/zap/pull/371 +[#362]: https://github.com/uber-go/zap/pull/362 +[#369]: https://github.com/uber-go/zap/pull/369 +[#347]: https://github.com/uber-go/zap/pull/347 +[#373]: https://github.com/uber-go/zap/pull/373 +[#348]: https://github.com/uber-go/zap/pull/348 +[#327]: https://github.com/uber-go/zap/pull/327 +[#376]: https://github.com/uber-go/zap/pull/376 +[#346]: https://github.com/uber-go/zap/pull/346 +[#365]: https://github.com/uber-go/zap/pull/365 +[#372]: https://github.com/uber-go/zap/pull/372 + +## 1.0.0-rc.3 (7 Mar 2017) This is the third release candidate for zap's stable release. There are no breaking changes. @@ -458,7 +606,12 @@ Enhancements: Thanks to @ansel1 and @suyash for their contributions to this release. -## v1.0.0-rc.2 (21 Feb 2017) +[#339]: https://github.com/uber-go/zap/pull/339 +[#307]: https://github.com/uber-go/zap/pull/307 +[#353]: https://github.com/uber-go/zap/pull/353 +[#311]: https://github.com/uber-go/zap/pull/311 + +## 1.0.0-rc.2 (21 Feb 2017) This is the second release candidate for zap's stable release. It includes two breaking changes. @@ -495,7 +648,16 @@ Enhancements: Thanks to @skipor and @chapsuk for their contributions to this release. -## v1.0.0-rc.1 (14 Feb 2017) +[#316]: https://github.com/uber-go/zap/pull/316 +[#309]: https://github.com/uber-go/zap/pull/309 +[#317]: https://github.com/uber-go/zap/pull/317 +[#321]: https://github.com/uber-go/zap/pull/321 +[#325]: https://github.com/uber-go/zap/pull/325 +[#333]: https://github.com/uber-go/zap/pull/333 +[#326]: https://github.com/uber-go/zap/pull/326 +[#300]: https://github.com/uber-go/zap/pull/300 + +## 1.0.0-rc.1 (14 Feb 2017) This is the first release candidate for zap's stable release. There are multiple breaking changes and improvements from the pre-release version. Most notably: @@ -515,7 +677,7 @@ breaking changes and improvements from the pre-release version. Most notably: * Sampling is more accurate, and doesn't depend on the standard library's shared timer heap. -## v0.1.0-beta.1 (6 Feb 2017) +## 0.1.0-beta.1 (6 Feb 2017) This is a minor version, tagged to allow users to pin to the pre-1.0 APIs and upgrade at their leisure. Since this is the first tagged release, there are no @@ -523,95 +685,3 @@ backward compatibility concerns and all functionality is new. Early zap adopters should pin to the 0.1.x minor version until they're ready to upgrade to the upcoming stable release. - -[#316]: https://github.com/uber-go/zap/pull/316 -[#309]: https://github.com/uber-go/zap/pull/309 -[#317]: https://github.com/uber-go/zap/pull/317 -[#321]: https://github.com/uber-go/zap/pull/321 -[#325]: https://github.com/uber-go/zap/pull/325 -[#333]: https://github.com/uber-go/zap/pull/333 -[#326]: https://github.com/uber-go/zap/pull/326 -[#300]: https://github.com/uber-go/zap/pull/300 -[#339]: https://github.com/uber-go/zap/pull/339 -[#307]: https://github.com/uber-go/zap/pull/307 -[#353]: https://github.com/uber-go/zap/pull/353 -[#311]: https://github.com/uber-go/zap/pull/311 -[#366]: https://github.com/uber-go/zap/pull/366 -[#364]: https://github.com/uber-go/zap/pull/364 -[#371]: https://github.com/uber-go/zap/pull/371 -[#362]: https://github.com/uber-go/zap/pull/362 -[#369]: https://github.com/uber-go/zap/pull/369 -[#347]: https://github.com/uber-go/zap/pull/347 -[#373]: https://github.com/uber-go/zap/pull/373 -[#348]: https://github.com/uber-go/zap/pull/348 -[#327]: https://github.com/uber-go/zap/pull/327 -[#376]: https://github.com/uber-go/zap/pull/376 -[#346]: https://github.com/uber-go/zap/pull/346 -[#365]: https://github.com/uber-go/zap/pull/365 -[#372]: https://github.com/uber-go/zap/pull/372 -[#385]: https://github.com/uber-go/zap/pull/385 -[#396]: https://github.com/uber-go/zap/pull/396 -[#386]: https://github.com/uber-go/zap/pull/386 -[#402]: https://github.com/uber-go/zap/pull/402 -[#415]: https://github.com/uber-go/zap/pull/415 -[#416]: https://github.com/uber-go/zap/pull/416 -[#424]: https://github.com/uber-go/zap/pull/424 -[#425]: https://github.com/uber-go/zap/pull/425 -[#431]: https://github.com/uber-go/zap/pull/431 -[#435]: https://github.com/uber-go/zap/pull/435 -[#444]: https://github.com/uber-go/zap/pull/444 -[#477]: https://github.com/uber-go/zap/pull/477 -[#465]: https://github.com/uber-go/zap/pull/465 -[#460]: https://github.com/uber-go/zap/pull/460 -[#470]: https://github.com/uber-go/zap/pull/470 -[#487]: https://github.com/uber-go/zap/pull/487 -[#490]: https://github.com/uber-go/zap/pull/490 -[#491]: https://github.com/uber-go/zap/pull/491 -[#504]: https://github.com/uber-go/zap/pull/504 -[#508]: https://github.com/uber-go/zap/pull/508 -[#518]: https://github.com/uber-go/zap/pull/518 -[#577]: https://github.com/uber-go/zap/pull/577 -[#574]: https://github.com/uber-go/zap/pull/574 -[#602]: https://github.com/uber-go/zap/pull/602 -[#572]: https://github.com/uber-go/zap/pull/572 -[#606]: https://github.com/uber-go/zap/pull/606 -[#614]: https://github.com/uber-go/zap/pull/614 -[#657]: https://github.com/uber-go/zap/pull/657 -[#706]: https://github.com/uber-go/zap/pull/706 -[#610]: https://github.com/uber-go/zap/pull/610 -[#675]: https://github.com/uber-go/zap/pull/675 -[#704]: https://github.com/uber-go/zap/pull/704 -[#725]: https://github.com/uber-go/zap/pull/725 -[#736]: https://github.com/uber-go/zap/pull/736 -[#751]: https://github.com/uber-go/zap/pull/751 -[#758]: https://github.com/uber-go/zap/pull/758 -[#771]: https://github.com/uber-go/zap/pull/771 -[#773]: https://github.com/uber-go/zap/pull/773 -[#775]: https://github.com/uber-go/zap/pull/775 -[#786]: https://github.com/uber-go/zap/pull/786 -[#791]: https://github.com/uber-go/zap/pull/791 -[#795]: https://github.com/uber-go/zap/pull/795 -[#799]: https://github.com/uber-go/zap/pull/799 -[#804]: https://github.com/uber-go/zap/pull/804 -[#812]: https://github.com/uber-go/zap/pull/812 -[#806]: https://github.com/uber-go/zap/pull/806 -[#813]: https://github.com/uber-go/zap/pull/813 -[#629]: https://github.com/uber-go/zap/pull/629 -[#697]: https://github.com/uber-go/zap/pull/697 -[#828]: https://github.com/uber-go/zap/pull/828 -[#835]: https://github.com/uber-go/zap/pull/835 -[#843]: https://github.com/uber-go/zap/pull/843 -[#844]: https://github.com/uber-go/zap/pull/844 -[#852]: https://github.com/uber-go/zap/pull/852 -[#854]: https://github.com/uber-go/zap/pull/854 -[#861]: https://github.com/uber-go/zap/pull/861 -[#862]: https://github.com/uber-go/zap/pull/862 -[#865]: https://github.com/uber-go/zap/pull/865 -[#867]: https://github.com/uber-go/zap/pull/867 -[#881]: https://github.com/uber-go/zap/pull/881 -[#903]: https://github.com/uber-go/zap/pull/903 -[#912]: https://github.com/uber-go/zap/pull/912 -[#913]: https://github.com/uber-go/zap/pull/913 -[#928]: https://github.com/uber-go/zap/pull/928 -[#931]: https://github.com/uber-go/zap/pull/931 -[#936]: https://github.com/uber-go/zap/pull/936 diff --git a/vendor/go.uber.org/zap/LICENSE.txt b/vendor/go.uber.org/zap/LICENSE similarity index 100% rename from vendor/go.uber.org/zap/LICENSE.txt rename to vendor/go.uber.org/zap/LICENSE diff --git a/vendor/go.uber.org/zap/Makefile b/vendor/go.uber.org/zap/Makefile index 9b1bc3b0e..eb1cee53b 100644 --- a/vendor/go.uber.org/zap/Makefile +++ b/vendor/go.uber.org/zap/Makefile @@ -1,50 +1,51 @@ -export GOBIN ?= $(shell pwd)/bin +# Directory containing the Makefile. +PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) -GOLINT = $(GOBIN)/golint -STATICCHECK = $(GOBIN)/staticcheck +export GOBIN ?= $(PROJECT_ROOT)/bin +export PATH := $(GOBIN):$(PATH) + +GOVULNCHECK = $(GOBIN)/govulncheck BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem # Directories containing independent Go modules. -# -# We track coverage only for the main module. -MODULE_DIRS = . ./benchmarks ./zapgrpc/internal/test +MODULE_DIRS = . ./exp ./benchmarks ./zapgrpc/internal/test -# Many Go tools take file globs or directories as arguments instead of packages. -GO_FILES := $(shell \ - find . '(' -path '*/.*' -o -path './vendor' ')' -prune \ - -o -name '*.go' -print | cut -b3-) +# Directories that we want to track coverage for. +COVER_DIRS = . ./exp .PHONY: all all: lint test .PHONY: lint -lint: $(GOLINT) $(STATICCHECK) - @rm -rf lint.log - @echo "Checking formatting..." - @gofmt -d -s $(GO_FILES) 2>&1 | tee lint.log - @echo "Checking vet..." - @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go vet ./... 2>&1) &&) true | tee -a lint.log - @echo "Checking lint..." - @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(GOLINT) ./... 2>&1) &&) true | tee -a lint.log - @echo "Checking staticcheck..." - @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(STATICCHECK) ./... 2>&1) &&) true | tee -a lint.log - @echo "Checking for unresolved FIXMEs..." - @git grep -i fixme | grep -v -e Makefile | tee -a lint.log - @echo "Checking for license headers..." - @./checklicense.sh | tee -a lint.log - @[ ! -s lint.log ] - @echo "Checking 'go mod tidy'..." - @make tidy - @if ! git diff --quiet; then \ - echo "'go mod tidy' resulted in changes or working tree is dirty:"; \ - git --no-pager diff; \ - fi +lint: golangci-lint tidy-lint license-lint -$(GOLINT): - cd tools && go install golang.org/x/lint/golint +.PHONY: golangci-lint +golangci-lint: + @$(foreach mod,$(MODULE_DIRS), \ + (cd $(mod) && \ + echo "[lint] golangci-lint: $(mod)" && \ + golangci-lint run --path-prefix $(mod)) &&) true -$(STATICCHECK): - cd tools && go install honnef.co/go/tools/cmd/staticcheck +.PHONY: tidy +tidy: + @$(foreach dir,$(MODULE_DIRS), \ + (cd $(dir) && go mod tidy) &&) true + +.PHONY: tidy-lint +tidy-lint: + @$(foreach mod,$(MODULE_DIRS), \ + (cd $(mod) && \ + echo "[lint] tidy: $(mod)" && \ + go mod tidy && \ + git diff --exit-code -- go.mod go.sum) &&) true + + +.PHONY: license-lint +license-lint: + ./checklicense.sh + +$(GOVULNCHECK): + cd tools && go install golang.org/x/vuln/cmd/govulncheck .PHONY: test test: @@ -52,8 +53,10 @@ test: .PHONY: cover cover: - go test -race -coverprofile=cover.out -coverpkg=./... ./... - go tool cover -html=cover.out -o cover.html + @$(foreach dir,$(COVER_DIRS), ( \ + cd $(dir) && \ + go test -race -coverprofile=cover.out -coverpkg=./... ./... \ + && go tool cover -html=cover.out -o cover.html) &&) true .PHONY: bench BENCH ?= . @@ -68,6 +71,6 @@ updatereadme: rm -f README.md cat .readme.tmpl | go run internal/readme/readme.go > README.md -.PHONY: tidy -tidy: - @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go mod tidy) &&) true +.PHONY: vulncheck +vulncheck: $(GOVULNCHECK) + $(GOVULNCHECK) ./... diff --git a/vendor/go.uber.org/zap/README.md b/vendor/go.uber.org/zap/README.md index a553a428c..a17035cb6 100644 --- a/vendor/go.uber.org/zap/README.md +++ b/vendor/go.uber.org/zap/README.md @@ -1,7 +1,16 @@ -# :zap: zap [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] +# :zap: zap + + +
Blazing fast, structured, leveled logging in Go. +![Zap logo](assets/logo.png) + +[![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] + +
+ ## Installation `go get -u go.uber.org/zap` @@ -54,7 +63,7 @@ and make many small allocations. Put differently, using `encoding/json` and Zap takes a different approach. It includes a reflection-free, zero-allocation JSON encoder, and the base `Logger` strives to avoid serialization overhead and allocations wherever possible. By building the high-level `SugaredLogger` -on that foundation, zap lets users _choose_ when they need to count every +on that foundation, zap lets users *choose* when they need to count every allocation and when they'd prefer a more familiar, loosely typed API. As measured by its own [benchmarking suite][], not only is zap more performant @@ -64,40 +73,46 @@ id="anchor-versions">[1](#footnote-versions) Log a message and 10 fields: -| Package | Time | Time % to zap | Objects Allocated | -| :------------------ | :---------: | :-----------: | :---------------: | -| :zap: zap | 2900 ns/op | +0% | 5 allocs/op | -| :zap: zap (sugared) | 3475 ns/op | +20% | 10 allocs/op | -| zerolog | 10639 ns/op | +267% | 32 allocs/op | -| go-kit | 14434 ns/op | +398% | 59 allocs/op | -| logrus | 17104 ns/op | +490% | 81 allocs/op | -| apex/log | 32424 ns/op | +1018% | 66 allocs/op | -| log15 | 33579 ns/op | +1058% | 76 allocs/op | +| Package | Time | Time % to zap | Objects Allocated | +| :------ | :--: | :-----------: | :---------------: | +| :zap: zap | 656 ns/op | +0% | 5 allocs/op +| :zap: zap (sugared) | 935 ns/op | +43% | 10 allocs/op +| zerolog | 380 ns/op | -42% | 1 allocs/op +| go-kit | 2249 ns/op | +243% | 57 allocs/op +| slog (LogAttrs) | 2479 ns/op | +278% | 40 allocs/op +| slog | 2481 ns/op | +278% | 42 allocs/op +| apex/log | 9591 ns/op | +1362% | 63 allocs/op +| log15 | 11393 ns/op | +1637% | 75 allocs/op +| logrus | 11654 ns/op | +1677% | 79 allocs/op Log a message with a logger that already has 10 fields of context: -| Package | Time | Time % to zap | Objects Allocated | -| :------------------ | :---------: | :-----------: | :---------------: | -| :zap: zap | 373 ns/op | +0% | 0 allocs/op | -| :zap: zap (sugared) | 452 ns/op | +21% | 1 allocs/op | -| zerolog | 288 ns/op | -23% | 0 allocs/op | -| go-kit | 11785 ns/op | +3060% | 58 allocs/op | -| logrus | 19629 ns/op | +5162% | 70 allocs/op | -| log15 | 21866 ns/op | +5762% | 72 allocs/op | -| apex/log | 30890 ns/op | +8182% | 55 allocs/op | +| Package | Time | Time % to zap | Objects Allocated | +| :------ | :--: | :-----------: | :---------------: | +| :zap: zap | 67 ns/op | +0% | 0 allocs/op +| :zap: zap (sugared) | 84 ns/op | +25% | 1 allocs/op +| zerolog | 35 ns/op | -48% | 0 allocs/op +| slog | 193 ns/op | +188% | 0 allocs/op +| slog (LogAttrs) | 200 ns/op | +199% | 0 allocs/op +| go-kit | 2460 ns/op | +3572% | 56 allocs/op +| log15 | 9038 ns/op | +13390% | 70 allocs/op +| apex/log | 9068 ns/op | +13434% | 53 allocs/op +| logrus | 10521 ns/op | +15603% | 68 allocs/op Log a static string, without any context or `printf`-style templating: -| Package | Time | Time % to zap | Objects Allocated | -| :------------------ | :--------: | :-----------: | :---------------: | -| :zap: zap | 381 ns/op | +0% | 0 allocs/op | -| :zap: zap (sugared) | 410 ns/op | +8% | 1 allocs/op | -| zerolog | 369 ns/op | -3% | 0 allocs/op | -| standard library | 385 ns/op | +1% | 2 allocs/op | -| go-kit | 606 ns/op | +59% | 11 allocs/op | -| logrus | 1730 ns/op | +354% | 25 allocs/op | -| apex/log | 1998 ns/op | +424% | 7 allocs/op | -| log15 | 4546 ns/op | +1093% | 22 allocs/op | +| Package | Time | Time % to zap | Objects Allocated | +| :------ | :--: | :-----------: | :---------------: | +| :zap: zap | 63 ns/op | +0% | 0 allocs/op +| :zap: zap (sugared) | 81 ns/op | +29% | 1 allocs/op +| zerolog | 32 ns/op | -49% | 0 allocs/op +| standard library | 124 ns/op | +97% | 1 allocs/op +| slog | 196 ns/op | +211% | 0 allocs/op +| slog (LogAttrs) | 200 ns/op | +217% | 0 allocs/op +| go-kit | 213 ns/op | +238% | 9 allocs/op +| apex/log | 771 ns/op | +1124% | 5 allocs/op +| logrus | 1439 ns/op | +2184% | 23 allocs/op +| log15 | 2069 ns/op | +3184% | 20 allocs/op ## Development Status: Stable @@ -117,7 +132,7 @@ standard.
-Released under the [MIT License](LICENSE.txt). +Released under the [MIT License](LICENSE). 1 In particular, keep in mind that we may be benchmarking against slightly older versions of other packages. Versions are @@ -131,3 +146,4 @@ pinned in the [benchmarks/go.mod][] file. [↩](#anchor-versions) [cov]: https://codecov.io/gh/uber-go/zap [benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks [benchmarks/go.mod]: https://github.com/uber-go/zap/blob/master/benchmarks/go.mod + diff --git a/vendor/go.uber.org/zap/array.go b/vendor/go.uber.org/zap/array.go index 5be3704a3..abfccb566 100644 --- a/vendor/go.uber.org/zap/array.go +++ b/vendor/go.uber.org/zap/array.go @@ -21,6 +21,7 @@ package zap import ( + "fmt" "time" "go.uber.org/zap/zapcore" @@ -94,11 +95,137 @@ func Int8s(key string, nums []int8) Field { return Array(key, int8s(nums)) } +// Objects constructs a field with the given key, holding a list of the +// provided objects that can be marshaled by Zap. +// +// Note that these objects must implement zapcore.ObjectMarshaler directly. +// That is, if you're trying to marshal a []Request, the MarshalLogObject +// method must be declared on the Request type, not its pointer (*Request). +// If it's on the pointer, use ObjectValues. +// +// Given an object that implements MarshalLogObject on the value receiver, you +// can log a slice of those objects with Objects like so: +// +// type Author struct{ ... } +// func (a Author) MarshalLogObject(enc zapcore.ObjectEncoder) error +// +// var authors []Author = ... +// logger.Info("loading article", zap.Objects("authors", authors)) +// +// Similarly, given a type that implements MarshalLogObject on its pointer +// receiver, you can log a slice of pointers to that object with Objects like +// so: +// +// type Request struct{ ... } +// func (r *Request) MarshalLogObject(enc zapcore.ObjectEncoder) error +// +// var requests []*Request = ... +// logger.Info("sending requests", zap.Objects("requests", requests)) +// +// If instead, you have a slice of values of such an object, use the +// ObjectValues constructor. +// +// var requests []Request = ... +// logger.Info("sending requests", zap.ObjectValues("requests", requests)) +func Objects[T zapcore.ObjectMarshaler](key string, values []T) Field { + return Array(key, objects[T](values)) +} + +type objects[T zapcore.ObjectMarshaler] []T + +func (os objects[T]) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for _, o := range os { + if err := arr.AppendObject(o); err != nil { + return err + } + } + return nil +} + +// ObjectMarshalerPtr is a constraint that specifies that the given type +// implements zapcore.ObjectMarshaler on a pointer receiver. +type ObjectMarshalerPtr[T any] interface { + *T + zapcore.ObjectMarshaler +} + +// ObjectValues constructs a field with the given key, holding a list of the +// provided objects, where pointers to these objects can be marshaled by Zap. +// +// Note that pointers to these objects must implement zapcore.ObjectMarshaler. +// That is, if you're trying to marshal a []Request, the MarshalLogObject +// method must be declared on the *Request type, not the value (Request). +// If it's on the value, use Objects. +// +// Given an object that implements MarshalLogObject on the pointer receiver, +// you can log a slice of those objects with ObjectValues like so: +// +// type Request struct{ ... } +// func (r *Request) MarshalLogObject(enc zapcore.ObjectEncoder) error +// +// var requests []Request = ... +// logger.Info("sending requests", zap.ObjectValues("requests", requests)) +// +// If instead, you have a slice of pointers of such an object, use the Objects +// field constructor. +// +// var requests []*Request = ... +// logger.Info("sending requests", zap.Objects("requests", requests)) +func ObjectValues[T any, P ObjectMarshalerPtr[T]](key string, values []T) Field { + return Array(key, objectValues[T, P](values)) +} + +type objectValues[T any, P ObjectMarshalerPtr[T]] []T + +func (os objectValues[T, P]) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for i := range os { + // It is necessary for us to explicitly reference the "P" type. + // We cannot simply pass "&os[i]" to AppendObject because its type + // is "*T", which the type system does not consider as + // implementing ObjectMarshaler. + // Only the type "P" satisfies ObjectMarshaler, which we have + // to convert "*T" to explicitly. + var p P = &os[i] + if err := arr.AppendObject(p); err != nil { + return err + } + } + return nil +} + // Strings constructs a field that carries a slice of strings. func Strings(key string, ss []string) Field { return Array(key, stringArray(ss)) } +// Stringers constructs a field with the given key, holding a list of the +// output provided by the value's String method +// +// Given an object that implements String on the value receiver, you +// can log a slice of those objects with Objects like so: +// +// type Request struct{ ... } +// func (a Request) String() string +// +// var requests []Request = ... +// logger.Info("sending requests", zap.Stringers("requests", requests)) +// +// Note that these objects must implement fmt.Stringer directly. +// That is, if you're trying to marshal a []Request, the String method +// must be declared on the Request type, not its pointer (*Request). +func Stringers[T fmt.Stringer](key string, values []T) Field { + return Array(key, stringers[T](values)) +} + +type stringers[T fmt.Stringer] []T + +func (os stringers[T]) MarshalLogArray(arr zapcore.ArrayEncoder) error { + for _, o := range os { + arr.AppendString(o.String()) + } + return nil +} + // Times constructs a field that carries a slice of time.Times. func Times(key string, ts []time.Time) Field { return Array(key, times(ts)) diff --git a/vendor/go.uber.org/zap/array_go118.go b/vendor/go.uber.org/zap/array_go118.go deleted file mode 100644 index d0d2c49d6..000000000 --- a/vendor/go.uber.org/zap/array_go118.go +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright (c) 2022 Uber Technologies, Inc. -// -// 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. - -//go:build go1.18 -// +build go1.18 - -package zap - -import ( - "fmt" - - "go.uber.org/zap/zapcore" -) - -// Objects constructs a field with the given key, holding a list of the -// provided objects that can be marshaled by Zap. -// -// Note that these objects must implement zapcore.ObjectMarshaler directly. -// That is, if you're trying to marshal a []Request, the MarshalLogObject -// method must be declared on the Request type, not its pointer (*Request). -// If it's on the pointer, use ObjectValues. -// -// Given an object that implements MarshalLogObject on the value receiver, you -// can log a slice of those objects with Objects like so: -// -// type Author struct{ ... } -// func (a Author) MarshalLogObject(enc zapcore.ObjectEncoder) error -// -// var authors []Author = ... -// logger.Info("loading article", zap.Objects("authors", authors)) -// -// Similarly, given a type that implements MarshalLogObject on its pointer -// receiver, you can log a slice of pointers to that object with Objects like -// so: -// -// type Request struct{ ... } -// func (r *Request) MarshalLogObject(enc zapcore.ObjectEncoder) error -// -// var requests []*Request = ... -// logger.Info("sending requests", zap.Objects("requests", requests)) -// -// If instead, you have a slice of values of such an object, use the -// ObjectValues constructor. -// -// var requests []Request = ... -// logger.Info("sending requests", zap.ObjectValues("requests", requests)) -func Objects[T zapcore.ObjectMarshaler](key string, values []T) Field { - return Array(key, objects[T](values)) -} - -type objects[T zapcore.ObjectMarshaler] []T - -func (os objects[T]) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for _, o := range os { - if err := arr.AppendObject(o); err != nil { - return err - } - } - return nil -} - -// ObjectMarshalerPtr is a constraint that specifies that the given type -// implements zapcore.ObjectMarshaler on a pointer receiver. -type ObjectMarshalerPtr[T any] interface { - *T - zapcore.ObjectMarshaler -} - -// ObjectValues constructs a field with the given key, holding a list of the -// provided objects, where pointers to these objects can be marshaled by Zap. -// -// Note that pointers to these objects must implement zapcore.ObjectMarshaler. -// That is, if you're trying to marshal a []Request, the MarshalLogObject -// method must be declared on the *Request type, not the value (Request). -// If it's on the value, use Objects. -// -// Given an object that implements MarshalLogObject on the pointer receiver, -// you can log a slice of those objects with ObjectValues like so: -// -// type Request struct{ ... } -// func (r *Request) MarshalLogObject(enc zapcore.ObjectEncoder) error -// -// var requests []Request = ... -// logger.Info("sending requests", zap.ObjectValues("requests", requests)) -// -// If instead, you have a slice of pointers of such an object, use the Objects -// field constructor. -// -// var requests []*Request = ... -// logger.Info("sending requests", zap.Objects("requests", requests)) -func ObjectValues[T any, P ObjectMarshalerPtr[T]](key string, values []T) Field { - return Array(key, objectValues[T, P](values)) -} - -type objectValues[T any, P ObjectMarshalerPtr[T]] []T - -func (os objectValues[T, P]) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for i := range os { - // It is necessary for us to explicitly reference the "P" type. - // We cannot simply pass "&os[i]" to AppendObject because its type - // is "*T", which the type system does not consider as - // implementing ObjectMarshaler. - // Only the type "P" satisfies ObjectMarshaler, which we have - // to convert "*T" to explicitly. - var p P = &os[i] - if err := arr.AppendObject(p); err != nil { - return err - } - } - return nil -} - -// Stringers constructs a field with the given key, holding a list of the -// output provided by the value's String method -// -// Given an object that implements String on the value receiver, you -// can log a slice of those objects with Objects like so: -// -// type Request struct{ ... } -// func (a Request) String() string -// -// var requests []Request = ... -// logger.Info("sending requests", zap.Stringers("requests", requests)) -// -// Note that these objects must implement fmt.Stringer directly. -// That is, if you're trying to marshal a []Request, the String method -// must be declared on the Request type, not its pointer (*Request). -func Stringers[T fmt.Stringer](key string, values []T) Field { - return Array(key, stringers[T](values)) -} - -type stringers[T fmt.Stringer] []T - -func (os stringers[T]) MarshalLogArray(arr zapcore.ArrayEncoder) error { - for _, o := range os { - arr.AppendString(o.String()) - } - return nil -} diff --git a/vendor/go.uber.org/zap/buffer/buffer.go b/vendor/go.uber.org/zap/buffer/buffer.go index 9e929cd98..0b8540c21 100644 --- a/vendor/go.uber.org/zap/buffer/buffer.go +++ b/vendor/go.uber.org/zap/buffer/buffer.go @@ -42,6 +42,11 @@ func (b *Buffer) AppendByte(v byte) { b.bs = append(b.bs, v) } +// AppendBytes writes the given slice of bytes to the Buffer. +func (b *Buffer) AppendBytes(v []byte) { + b.bs = append(b.bs, v...) +} + // AppendString writes a string to the Buffer. func (b *Buffer) AppendString(s string) { b.bs = append(b.bs, s...) diff --git a/vendor/go.uber.org/zap/buffer/pool.go b/vendor/go.uber.org/zap/buffer/pool.go index 8fb3e202c..846323360 100644 --- a/vendor/go.uber.org/zap/buffer/pool.go +++ b/vendor/go.uber.org/zap/buffer/pool.go @@ -20,25 +20,29 @@ package buffer -import "sync" +import ( + "go.uber.org/zap/internal/pool" +) // A Pool is a type-safe wrapper around a sync.Pool. type Pool struct { - p *sync.Pool + p *pool.Pool[*Buffer] } // NewPool constructs a new Pool. func NewPool() Pool { - return Pool{p: &sync.Pool{ - New: func() interface{} { - return &Buffer{bs: make([]byte, 0, _size)} - }, - }} + return Pool{ + p: pool.New(func() *Buffer { + return &Buffer{ + bs: make([]byte, 0, _size), + } + }), + } } // Get retrieves a Buffer from the pool, creating one if necessary. func (p Pool) Get() *Buffer { - buf := p.p.Get().(*Buffer) + buf := p.p.Get() buf.Reset() buf.pool = p return buf diff --git a/vendor/go.uber.org/zap/config.go b/vendor/go.uber.org/zap/config.go index ee6096766..e76e4e64f 100644 --- a/vendor/go.uber.org/zap/config.go +++ b/vendor/go.uber.org/zap/config.go @@ -95,6 +95,32 @@ type Config struct { // NewProductionEncoderConfig returns an opinionated EncoderConfig for // production environments. +// +// Messages encoded with this configuration will be JSON-formatted +// and will have the following keys by default: +// +// - "level": The logging level (e.g. "info", "error"). +// - "ts": The current time in number of seconds since the Unix epoch. +// - "msg": The message passed to the log statement. +// - "caller": If available, a short path to the file and line number +// where the log statement was issued. +// The logger configuration determines whether this field is captured. +// - "stacktrace": If available, a stack trace from the line +// where the log statement was issued. +// The logger configuration determines whether this field is captured. +// +// By default, the following formats are used for different types: +// +// - Time is formatted as floating-point number of seconds since the Unix +// epoch. +// - Duration is formatted as floating-point number of seconds. +// +// You may change these by setting the appropriate fields in the returned +// object. +// For example, use the following to change the time encoding format: +// +// cfg := zap.NewProductionEncoderConfig() +// cfg.EncodeTime = zapcore.ISO8601TimeEncoder func NewProductionEncoderConfig() zapcore.EncoderConfig { return zapcore.EncoderConfig{ TimeKey: "ts", @@ -112,11 +138,22 @@ func NewProductionEncoderConfig() zapcore.EncoderConfig { } } -// NewProductionConfig is a reasonable production logging configuration. -// Logging is enabled at InfoLevel and above. +// NewProductionConfig builds a reasonable default production logging +// configuration. +// Logging is enabled at InfoLevel and above, and uses a JSON encoder. +// Logs are written to standard error. +// Stacktraces are included on logs of ErrorLevel and above. +// DPanicLevel logs will not panic, but will write a stacktrace. // -// It uses a JSON encoder, writes to standard error, and enables sampling. -// Stacktraces are automatically included on logs of ErrorLevel and above. +// Sampling is enabled at 100:100 by default, +// meaning that after the first 100 log entries +// with the same level and message in the same second, +// it will log every 100th entry +// with the same level and message in the same second. +// You may disable this behavior by setting Sampling to nil. +// +// See [NewProductionEncoderConfig] for information +// on the default encoder configuration. func NewProductionConfig() Config { return Config{ Level: NewAtomicLevelAt(InfoLevel), @@ -134,6 +171,32 @@ func NewProductionConfig() Config { // NewDevelopmentEncoderConfig returns an opinionated EncoderConfig for // development environments. +// +// Messages encoded with this configuration will use Zap's console encoder +// intended to print human-readable output. +// It will print log messages with the following information: +// +// - The log level (e.g. "INFO", "ERROR"). +// - The time in ISO8601 format (e.g. "2017-01-01T12:00:00Z"). +// - The message passed to the log statement. +// - If available, a short path to the file and line number +// where the log statement was issued. +// The logger configuration determines whether this field is captured. +// - If available, a stacktrace from the line +// where the log statement was issued. +// The logger configuration determines whether this field is captured. +// +// By default, the following formats are used for different types: +// +// - Time is formatted in ISO8601 format (e.g. "2017-01-01T12:00:00Z"). +// - Duration is formatted as a string (e.g. "1.234s"). +// +// You may change these by setting the appropriate fields in the returned +// object. +// For example, use the following to change the time encoding format: +// +// cfg := zap.NewDevelopmentEncoderConfig() +// cfg.EncodeTime = zapcore.ISO8601TimeEncoder func NewDevelopmentEncoderConfig() zapcore.EncoderConfig { return zapcore.EncoderConfig{ // Keys can be anything except the empty string. @@ -152,12 +215,15 @@ func NewDevelopmentEncoderConfig() zapcore.EncoderConfig { } } -// NewDevelopmentConfig is a reasonable development logging configuration. -// Logging is enabled at DebugLevel and above. +// NewDevelopmentConfig builds a reasonable default development logging +// configuration. +// Logging is enabled at DebugLevel and above, and uses a console encoder. +// Logs are written to standard error. +// Stacktraces are included on logs of WarnLevel and above. +// DPanicLevel logs will panic. // -// It enables development mode (which makes DPanicLevel logs panic), uses a -// console encoder, writes to standard error, and disables sampling. -// Stacktraces are automatically included on logs of WarnLevel and above. +// See [NewDevelopmentEncoderConfig] for information +// on the default encoder configuration. func NewDevelopmentConfig() Config { return Config{ Level: NewAtomicLevelAt(DebugLevel), diff --git a/vendor/go.uber.org/zap/error.go b/vendor/go.uber.org/zap/error.go index 65982a51e..45f7b838d 100644 --- a/vendor/go.uber.org/zap/error.go +++ b/vendor/go.uber.org/zap/error.go @@ -21,14 +21,13 @@ package zap import ( - "sync" - + "go.uber.org/zap/internal/pool" "go.uber.org/zap/zapcore" ) -var _errArrayElemPool = sync.Pool{New: func() interface{} { +var _errArrayElemPool = pool.New(func() *errArrayElem { return &errArrayElem{} -}} +}) // Error is shorthand for the common idiom NamedError("error", err). func Error(err error) Field { @@ -60,11 +59,14 @@ func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error { // potentially an "errorVerbose" attribute, we need to wrap it in a // type that implements LogObjectMarshaler. To prevent this from // allocating, pool the wrapper type. - elem := _errArrayElemPool.Get().(*errArrayElem) + elem := _errArrayElemPool.Get() elem.error = errs[i] - arr.AppendObject(elem) + err := arr.AppendObject(elem) elem.error = nil _errArrayElemPool.Put(elem) + if err != nil { + return err + } } return nil } diff --git a/vendor/go.uber.org/zap/field.go b/vendor/go.uber.org/zap/field.go index bbb745db5..6743930b8 100644 --- a/vendor/go.uber.org/zap/field.go +++ b/vendor/go.uber.org/zap/field.go @@ -25,6 +25,7 @@ import ( "math" "time" + "go.uber.org/zap/internal/stacktrace" "go.uber.org/zap/zapcore" ) @@ -374,7 +375,7 @@ func StackSkip(key string, skip int) Field { // from expanding the zapcore.Field union struct to include a byte slice. Since // taking a stacktrace is already so expensive (~10us), the extra allocation // is okay. - return String(key, takeStacktrace(skip+1)) // skip StackSkip + return String(key, stacktrace.Take(skip+1)) // skip StackSkip } // Duration constructs a field with the given key and value. The encoder @@ -410,6 +411,65 @@ func Inline(val zapcore.ObjectMarshaler) Field { } } +// Dict constructs a field containing the provided key-value pairs. +// It acts similar to [Object], but with the fields specified as arguments. +func Dict(key string, val ...Field) Field { + return dictField(key, val) +} + +// We need a function with the signature (string, T) for zap.Any. +func dictField(key string, val []Field) Field { + return Object(key, dictObject(val)) +} + +type dictObject []Field + +func (d dictObject) MarshalLogObject(enc zapcore.ObjectEncoder) error { + for _, f := range d { + f.AddTo(enc) + } + return nil +} + +// We discovered an issue where zap.Any can cause a performance degradation +// when used in new goroutines. +// +// This happens because the compiler assigns 4.8kb (one zap.Field per arm of +// switch statement) of stack space for zap.Any when it takes the form: +// +// switch v := v.(type) { +// case string: +// return String(key, v) +// case int: +// return Int(key, v) +// // ... +// default: +// return Reflect(key, v) +// } +// +// To avoid this, we use the type switch to assign a value to a single local variable +// and then call a function on it. +// The local variable is just a function reference so it doesn't allocate +// when converted to an interface{}. +// +// A fair bit of experimentation went into this. +// See also: +// +// - https://github.com/uber-go/zap/pull/1301 +// - https://github.com/uber-go/zap/pull/1303 +// - https://github.com/uber-go/zap/pull/1304 +// - https://github.com/uber-go/zap/pull/1305 +// - https://github.com/uber-go/zap/pull/1308 +// +// See https://github.com/golang/go/issues/62077 for upstream issue. +type anyFieldC[T any] func(string, T) Field + +func (f anyFieldC[T]) Any(key string, val any) Field { + v, _ := val.(T) + // val is guaranteed to be a T, except when it's nil. + return f(key, v) +} + // Any takes a key and an arbitrary value and chooses the best way to represent // them as a field, falling back to a reflection-based approach only if // necessary. @@ -418,132 +478,138 @@ func Inline(val zapcore.ObjectMarshaler) Field { // them. To minimize surprises, []byte values are treated as binary blobs, byte // values are treated as uint8, and runes are always treated as integers. func Any(key string, value interface{}) Field { - switch val := value.(type) { + var c interface{ Any(string, any) Field } + + switch value.(type) { case zapcore.ObjectMarshaler: - return Object(key, val) + c = anyFieldC[zapcore.ObjectMarshaler](Object) case zapcore.ArrayMarshaler: - return Array(key, val) + c = anyFieldC[zapcore.ArrayMarshaler](Array) + case []Field: + c = anyFieldC[[]Field](dictField) case bool: - return Bool(key, val) + c = anyFieldC[bool](Bool) case *bool: - return Boolp(key, val) + c = anyFieldC[*bool](Boolp) case []bool: - return Bools(key, val) + c = anyFieldC[[]bool](Bools) case complex128: - return Complex128(key, val) + c = anyFieldC[complex128](Complex128) case *complex128: - return Complex128p(key, val) + c = anyFieldC[*complex128](Complex128p) case []complex128: - return Complex128s(key, val) + c = anyFieldC[[]complex128](Complex128s) case complex64: - return Complex64(key, val) + c = anyFieldC[complex64](Complex64) case *complex64: - return Complex64p(key, val) + c = anyFieldC[*complex64](Complex64p) case []complex64: - return Complex64s(key, val) + c = anyFieldC[[]complex64](Complex64s) case float64: - return Float64(key, val) + c = anyFieldC[float64](Float64) case *float64: - return Float64p(key, val) + c = anyFieldC[*float64](Float64p) case []float64: - return Float64s(key, val) + c = anyFieldC[[]float64](Float64s) case float32: - return Float32(key, val) + c = anyFieldC[float32](Float32) case *float32: - return Float32p(key, val) + c = anyFieldC[*float32](Float32p) case []float32: - return Float32s(key, val) + c = anyFieldC[[]float32](Float32s) case int: - return Int(key, val) + c = anyFieldC[int](Int) case *int: - return Intp(key, val) + c = anyFieldC[*int](Intp) case []int: - return Ints(key, val) + c = anyFieldC[[]int](Ints) case int64: - return Int64(key, val) + c = anyFieldC[int64](Int64) case *int64: - return Int64p(key, val) + c = anyFieldC[*int64](Int64p) case []int64: - return Int64s(key, val) + c = anyFieldC[[]int64](Int64s) case int32: - return Int32(key, val) + c = anyFieldC[int32](Int32) case *int32: - return Int32p(key, val) + c = anyFieldC[*int32](Int32p) case []int32: - return Int32s(key, val) + c = anyFieldC[[]int32](Int32s) case int16: - return Int16(key, val) + c = anyFieldC[int16](Int16) case *int16: - return Int16p(key, val) + c = anyFieldC[*int16](Int16p) case []int16: - return Int16s(key, val) + c = anyFieldC[[]int16](Int16s) case int8: - return Int8(key, val) + c = anyFieldC[int8](Int8) case *int8: - return Int8p(key, val) + c = anyFieldC[*int8](Int8p) case []int8: - return Int8s(key, val) + c = anyFieldC[[]int8](Int8s) case string: - return String(key, val) + c = anyFieldC[string](String) case *string: - return Stringp(key, val) + c = anyFieldC[*string](Stringp) case []string: - return Strings(key, val) + c = anyFieldC[[]string](Strings) case uint: - return Uint(key, val) + c = anyFieldC[uint](Uint) case *uint: - return Uintp(key, val) + c = anyFieldC[*uint](Uintp) case []uint: - return Uints(key, val) + c = anyFieldC[[]uint](Uints) case uint64: - return Uint64(key, val) + c = anyFieldC[uint64](Uint64) case *uint64: - return Uint64p(key, val) + c = anyFieldC[*uint64](Uint64p) case []uint64: - return Uint64s(key, val) + c = anyFieldC[[]uint64](Uint64s) case uint32: - return Uint32(key, val) + c = anyFieldC[uint32](Uint32) case *uint32: - return Uint32p(key, val) + c = anyFieldC[*uint32](Uint32p) case []uint32: - return Uint32s(key, val) + c = anyFieldC[[]uint32](Uint32s) case uint16: - return Uint16(key, val) + c = anyFieldC[uint16](Uint16) case *uint16: - return Uint16p(key, val) + c = anyFieldC[*uint16](Uint16p) case []uint16: - return Uint16s(key, val) + c = anyFieldC[[]uint16](Uint16s) case uint8: - return Uint8(key, val) + c = anyFieldC[uint8](Uint8) case *uint8: - return Uint8p(key, val) + c = anyFieldC[*uint8](Uint8p) case []byte: - return Binary(key, val) + c = anyFieldC[[]byte](Binary) case uintptr: - return Uintptr(key, val) + c = anyFieldC[uintptr](Uintptr) case *uintptr: - return Uintptrp(key, val) + c = anyFieldC[*uintptr](Uintptrp) case []uintptr: - return Uintptrs(key, val) + c = anyFieldC[[]uintptr](Uintptrs) case time.Time: - return Time(key, val) + c = anyFieldC[time.Time](Time) case *time.Time: - return Timep(key, val) + c = anyFieldC[*time.Time](Timep) case []time.Time: - return Times(key, val) + c = anyFieldC[[]time.Time](Times) case time.Duration: - return Duration(key, val) + c = anyFieldC[time.Duration](Duration) case *time.Duration: - return Durationp(key, val) + c = anyFieldC[*time.Duration](Durationp) case []time.Duration: - return Durations(key, val) + c = anyFieldC[[]time.Duration](Durations) case error: - return NamedError(key, val) + c = anyFieldC[error](NamedError) case []error: - return Errors(key, val) + c = anyFieldC[[]error](Errors) case fmt.Stringer: - return Stringer(key, val) + c = anyFieldC[fmt.Stringer](Stringer) default: - return Reflect(key, val) + c = anyFieldC[any](Reflect) } + + return c.Any(key, value) } diff --git a/vendor/go.uber.org/zap/http_handler.go b/vendor/go.uber.org/zap/http_handler.go index 632b6831a..2be8f6515 100644 --- a/vendor/go.uber.org/zap/http_handler.go +++ b/vendor/go.uber.org/zap/http_handler.go @@ -69,6 +69,13 @@ import ( // // curl -X PUT localhost:8080/log/level -H "Content-Type: application/json" -d '{"level":"debug"}' func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if err := lvl.serveHTTP(w, r); err != nil { + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintf(w, "internal error: %v", err) + } +} + +func (lvl AtomicLevel) serveHTTP(w http.ResponseWriter, r *http.Request) error { type errorResponse struct { Error string `json:"error"` } @@ -80,19 +87,20 @@ func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: - enc.Encode(payload{Level: lvl.Level()}) + return enc.Encode(payload{Level: lvl.Level()}) + case http.MethodPut: requestedLvl, err := decodePutRequest(r.Header.Get("Content-Type"), r) if err != nil { w.WriteHeader(http.StatusBadRequest) - enc.Encode(errorResponse{Error: err.Error()}) - return + return enc.Encode(errorResponse{Error: err.Error()}) } lvl.SetLevel(requestedLvl) - enc.Encode(payload{Level: lvl.Level()}) + return enc.Encode(payload{Level: lvl.Level()}) + default: w.WriteHeader(http.StatusMethodNotAllowed) - enc.Encode(errorResponse{ + return enc.Encode(errorResponse{ Error: "Only GET and PUT are supported.", }) } @@ -129,5 +137,4 @@ func decodePutJSON(body io.Reader) (zapcore.Level, error) { return 0, errors.New("must specify logging level") } return *pld.Level, nil - } diff --git a/vendor/go.uber.org/zap/internal/level_enabler.go b/vendor/go.uber.org/zap/internal/level_enabler.go index 5f3e3f1b9..40bfed81e 100644 --- a/vendor/go.uber.org/zap/internal/level_enabler.go +++ b/vendor/go.uber.org/zap/internal/level_enabler.go @@ -18,6 +18,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +// Package internal and its subpackages hold types and functionality +// that are not part of Zap's public API. package internal import "go.uber.org/zap/zapcore" diff --git a/vendor/go.uber.org/zap/internal/pool/pool.go b/vendor/go.uber.org/zap/internal/pool/pool.go new file mode 100644 index 000000000..60e9d2c43 --- /dev/null +++ b/vendor/go.uber.org/zap/internal/pool/pool.go @@ -0,0 +1,58 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// 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. + +// Package pool provides internal pool utilities. +package pool + +import ( + "sync" +) + +// A Pool is a generic wrapper around [sync.Pool] to provide strongly-typed +// object pooling. +// +// Note that SA6002 (ref: https://staticcheck.io/docs/checks/#SA6002) will +// not be detected, so all internal pool use must take care to only store +// pointer types. +type Pool[T any] struct { + pool sync.Pool +} + +// New returns a new [Pool] for T, and will use fn to construct new Ts when +// the pool is empty. +func New[T any](fn func() T) *Pool[T] { + return &Pool[T]{ + pool: sync.Pool{ + New: func() any { + return fn() + }, + }, + } +} + +// Get gets a T from the pool, or creates a new one if the pool is empty. +func (p *Pool[T]) Get() T { + return p.pool.Get().(T) +} + +// Put returns x into the pool. +func (p *Pool[T]) Put(x T) { + p.pool.Put(x) +} diff --git a/vendor/go.uber.org/zap/stacktrace.go b/vendor/go.uber.org/zap/internal/stacktrace/stack.go similarity index 73% rename from vendor/go.uber.org/zap/stacktrace.go rename to vendor/go.uber.org/zap/internal/stacktrace/stack.go index 817a3bde8..82af7551f 100644 --- a/vendor/go.uber.org/zap/stacktrace.go +++ b/vendor/go.uber.org/zap/internal/stacktrace/stack.go @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Uber Technologies, Inc. +// Copyright (c) 2023 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -18,25 +18,26 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -package zap +// Package stacktrace provides support for gathering stack traces +// efficiently. +package stacktrace import ( "runtime" - "sync" "go.uber.org/zap/buffer" "go.uber.org/zap/internal/bufferpool" + "go.uber.org/zap/internal/pool" ) -var _stacktracePool = sync.Pool{ - New: func() interface{} { - return &stacktrace{ - storage: make([]uintptr, 64), - } - }, -} +var _stackPool = pool.New(func() *Stack { + return &Stack{ + storage: make([]uintptr, 64), + } +}) -type stacktrace struct { +// Stack is a captured stack trace. +type Stack struct { pcs []uintptr // program counters; always a subslice of storage frames *runtime.Frames @@ -50,30 +51,30 @@ type stacktrace struct { storage []uintptr } -// stacktraceDepth specifies how deep of a stack trace should be captured. -type stacktraceDepth int +// Depth specifies how deep of a stack trace should be captured. +type Depth int const ( - // stacktraceFirst captures only the first frame. - stacktraceFirst stacktraceDepth = iota + // First captures only the first frame. + First Depth = iota - // stacktraceFull captures the entire call stack, allocating more + // Full captures the entire call stack, allocating more // storage for it if needed. - stacktraceFull + Full ) -// captureStacktrace captures a stack trace of the specified depth, skipping +// Capture captures a stack trace of the specified depth, skipping // the provided number of frames. skip=0 identifies the caller of -// captureStacktrace. +// Capture. // // The caller must call Free on the returned stacktrace after using it. -func captureStacktrace(skip int, depth stacktraceDepth) *stacktrace { - stack := _stacktracePool.Get().(*stacktrace) +func Capture(skip int, depth Depth) *Stack { + stack := _stackPool.Get() switch depth { - case stacktraceFirst: + case First: stack.pcs = stack.storage[:1] - case stacktraceFull: + case Full: stack.pcs = stack.storage } @@ -87,7 +88,7 @@ func captureStacktrace(skip int, depth stacktraceDepth) *stacktrace { // runtime.Callers truncates the recorded stacktrace if there is no // room in the provided slice. For the full stack trace, keep expanding // storage until there are fewer frames than there is room. - if depth == stacktraceFull { + if depth == Full { pcs := stack.pcs for numFrames == len(pcs) { pcs = make([]uintptr, len(pcs)*2) @@ -109,50 +110,54 @@ func captureStacktrace(skip int, depth stacktraceDepth) *stacktrace { // Free releases resources associated with this stacktrace // and returns it back to the pool. -func (st *stacktrace) Free() { +func (st *Stack) Free() { st.frames = nil st.pcs = nil - _stacktracePool.Put(st) + _stackPool.Put(st) } // Count reports the total number of frames in this stacktrace. // Count DOES NOT change as Next is called. -func (st *stacktrace) Count() int { +func (st *Stack) Count() int { return len(st.pcs) } // Next returns the next frame in the stack trace, // and a boolean indicating whether there are more after it. -func (st *stacktrace) Next() (_ runtime.Frame, more bool) { +func (st *Stack) Next() (_ runtime.Frame, more bool) { return st.frames.Next() } -func takeStacktrace(skip int) string { - stack := captureStacktrace(skip+1, stacktraceFull) +// Take returns a string representation of the current stacktrace. +// +// skip is the number of frames to skip before recording the stack trace. +// skip=0 identifies the caller of Take. +func Take(skip int) string { + stack := Capture(skip+1, Full) defer stack.Free() buffer := bufferpool.Get() defer buffer.Free() - stackfmt := newStackFormatter(buffer) + stackfmt := NewFormatter(buffer) stackfmt.FormatStack(stack) return buffer.String() } -// stackFormatter formats a stack trace into a readable string representation. -type stackFormatter struct { +// Formatter formats a stack trace into a readable string representation. +type Formatter struct { b *buffer.Buffer nonEmpty bool // whehther we've written at least one frame already } -// newStackFormatter builds a new stackFormatter. -func newStackFormatter(b *buffer.Buffer) stackFormatter { - return stackFormatter{b: b} +// NewFormatter builds a new Formatter. +func NewFormatter(b *buffer.Buffer) Formatter { + return Formatter{b: b} } // FormatStack formats all remaining frames in the provided stacktrace -- minus // the final runtime.main/runtime.goexit frame. -func (sf *stackFormatter) FormatStack(stack *stacktrace) { +func (sf *Formatter) FormatStack(stack *Stack) { // Note: On the last iteration, frames.Next() returns false, with a valid // frame, but we ignore this frame. The last frame is a runtime frame which // adds noise, since it's only either runtime.main or runtime.goexit. @@ -162,7 +167,7 @@ func (sf *stackFormatter) FormatStack(stack *stacktrace) { } // FormatFrame formats the given frame. -func (sf *stackFormatter) FormatFrame(frame runtime.Frame) { +func (sf *Formatter) FormatFrame(frame runtime.Frame) { if sf.nonEmpty { sf.b.AppendByte('\n') } diff --git a/vendor/go.uber.org/zap/level.go b/vendor/go.uber.org/zap/level.go index db951e19a..155b208bd 100644 --- a/vendor/go.uber.org/zap/level.go +++ b/vendor/go.uber.org/zap/level.go @@ -21,7 +21,8 @@ package zap import ( - "go.uber.org/atomic" + "sync/atomic" + "go.uber.org/zap/internal" "go.uber.org/zap/zapcore" ) @@ -76,9 +77,9 @@ var _ internal.LeveledEnabler = AtomicLevel{} // NewAtomicLevel creates an AtomicLevel with InfoLevel and above logging // enabled. func NewAtomicLevel() AtomicLevel { - return AtomicLevel{ - l: atomic.NewInt32(int32(InfoLevel)), - } + lvl := AtomicLevel{l: new(atomic.Int32)} + lvl.l.Store(int32(InfoLevel)) + return lvl } // NewAtomicLevelAt is a convenience function that creates an AtomicLevel diff --git a/vendor/go.uber.org/zap/logger.go b/vendor/go.uber.org/zap/logger.go index cd44030d1..c4d300323 100644 --- a/vendor/go.uber.org/zap/logger.go +++ b/vendor/go.uber.org/zap/logger.go @@ -27,6 +27,7 @@ import ( "strings" "go.uber.org/zap/internal/bufferpool" + "go.uber.org/zap/internal/stacktrace" "go.uber.org/zap/zapcore" ) @@ -42,6 +43,7 @@ type Logger struct { development bool addCaller bool + onPanic zapcore.CheckWriteHook // default is WriteThenPanic onFatal zapcore.CheckWriteHook // default is WriteThenFatal name string @@ -173,7 +175,8 @@ func (log *Logger) WithOptions(opts ...Option) *Logger { } // With creates a child logger and adds structured context to it. Fields added -// to the child don't affect the parent, and vice versa. +// to the child don't affect the parent, and vice versa. Any fields that +// require evaluation (such as Objects) are evaluated upon invocation of With. func (log *Logger) With(fields ...Field) *Logger { if len(fields) == 0 { return log @@ -183,6 +186,28 @@ func (log *Logger) With(fields ...Field) *Logger { return l } +// WithLazy creates a child logger and adds structured context to it lazily. +// +// The fields are evaluated only if the logger is further chained with [With] +// or is written to with any of the log level methods. +// Until that occurs, the logger may retain references to objects inside the fields, +// and logging will reflect the state of an object at the time of logging, +// not the time of WithLazy(). +// +// WithLazy provides a worthwhile performance optimization for contextual loggers +// when the likelihood of using the child logger is low, +// such as error paths and rarely taken branches. +// +// Similar to [With], fields added to the child don't affect the parent, and vice versa. +func (log *Logger) WithLazy(fields ...Field) *Logger { + if len(fields) == 0 { + return log + } + return log.WithOptions(WrapCore(func(core zapcore.Core) zapcore.Core { + return zapcore.NewLazyWith(core, fields) + })) +} + // Level reports the minimum enabled level for this logger. // // For NopLoggers, this is [zapcore.InvalidLevel]. @@ -199,6 +224,8 @@ func (log *Logger) Check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { // Log logs a message at the specified level. The message includes any fields // passed at the log site, as well as any fields accumulated on the logger. +// Any Fields that require evaluation (such as Objects) are evaluated upon +// invocation of Log. func (log *Logger) Log(lvl zapcore.Level, msg string, fields ...Field) { if ce := log.check(lvl, msg); ce != nil { ce.Write(fields...) @@ -281,9 +308,15 @@ func (log *Logger) Core() zapcore.Core { return log.core } +// Name returns the Logger's underlying name, +// or an empty string if the logger is unnamed. +func (log *Logger) Name() string { + return log.name +} + func (log *Logger) clone() *Logger { - copy := *log - return © + clone := *log + return &clone } func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { @@ -313,27 +346,12 @@ func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { // Set up any required terminal behavior. switch ent.Level { case zapcore.PanicLevel: - ce = ce.After(ent, zapcore.WriteThenPanic) + ce = ce.After(ent, terminalHookOverride(zapcore.WriteThenPanic, log.onPanic)) case zapcore.FatalLevel: - onFatal := log.onFatal - // nil or WriteThenNoop will lead to continued execution after - // a Fatal log entry, which is unexpected. For example, - // - // f, err := os.Open(..) - // if err != nil { - // log.Fatal("cannot open", zap.Error(err)) - // } - // fmt.Println(f.Name()) - // - // The f.Name() will panic if we continue execution after the - // log.Fatal. - if onFatal == nil || onFatal == zapcore.WriteThenNoop { - onFatal = zapcore.WriteThenFatal - } - ce = ce.After(ent, onFatal) + ce = ce.After(ent, terminalHookOverride(zapcore.WriteThenFatal, log.onFatal)) case zapcore.DPanicLevel: if log.development { - ce = ce.After(ent, zapcore.WriteThenPanic) + ce = ce.After(ent, terminalHookOverride(zapcore.WriteThenPanic, log.onPanic)) } } @@ -354,17 +372,17 @@ func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { // Adding the caller or stack trace requires capturing the callers of // this function. We'll share information between these two. - stackDepth := stacktraceFirst + stackDepth := stacktrace.First if addStack { - stackDepth = stacktraceFull + stackDepth = stacktrace.Full } - stack := captureStacktrace(log.callerSkip+callerSkipOffset, stackDepth) + stack := stacktrace.Capture(log.callerSkip+callerSkipOffset, stackDepth) defer stack.Free() if stack.Count() == 0 { if log.addCaller { fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", ent.Time.UTC()) - log.errorOutput.Sync() + _ = log.errorOutput.Sync() } return ce } @@ -385,7 +403,7 @@ func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { buffer := bufferpool.Get() defer buffer.Free() - stackfmt := newStackFormatter(buffer) + stackfmt := stacktrace.NewFormatter(buffer) // We've already extracted the first frame, so format that // separately and defer to stackfmt for the rest. @@ -398,3 +416,20 @@ func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { return ce } + +func terminalHookOverride(defaultHook, override zapcore.CheckWriteHook) zapcore.CheckWriteHook { + // A nil or WriteThenNoop hook will lead to continued execution after + // a Panic or Fatal log entry, which is unexpected. For example, + // + // f, err := os.Open(..) + // if err != nil { + // log.Fatal("cannot open", zap.Error(err)) + // } + // fmt.Println(f.Name()) + // + // The f.Name() will panic if we continue execution after the log.Fatal. + if override == nil || override == zapcore.WriteThenNoop { + return defaultHook + } + return override +} diff --git a/vendor/go.uber.org/zap/options.go b/vendor/go.uber.org/zap/options.go index c4f3bca3d..43d357ac9 100644 --- a/vendor/go.uber.org/zap/options.go +++ b/vendor/go.uber.org/zap/options.go @@ -132,6 +132,21 @@ func IncreaseLevel(lvl zapcore.LevelEnabler) Option { }) } +// WithPanicHook sets a CheckWriteHook to run on Panic/DPanic logs. +// Zap will call this hook after writing a log statement with a Panic/DPanic level. +// +// For example, the following builds a logger that will exit the current +// goroutine after writing a Panic/DPanic log message, but it will not start a panic. +// +// zap.New(core, zap.WithPanicHook(zapcore.WriteThenGoexit)) +// +// This is useful for testing Panic/DPanic log output. +func WithPanicHook(hook zapcore.CheckWriteHook) Option { + return optionFunc(func(log *Logger) { + log.onPanic = hook + }) +} + // OnFatal sets the action to take on fatal logs. // // Deprecated: Use [WithFatalHook] instead. diff --git a/vendor/go.uber.org/zap/sink.go b/vendor/go.uber.org/zap/sink.go index 478c9a10f..499772a00 100644 --- a/vendor/go.uber.org/zap/sink.go +++ b/vendor/go.uber.org/zap/sink.go @@ -66,7 +66,8 @@ func newSinkRegistry() *sinkRegistry { factories: make(map[string]func(*url.URL) (Sink, error)), openFile: os.OpenFile, } - sr.RegisterSink(schemeFile, sr.newFileSinkFromURL) + // Infallible operation: the registry is empty, so we can't have a conflict. + _ = sr.RegisterSink(schemeFile, sr.newFileSinkFromURL) return sr } @@ -154,7 +155,7 @@ func (sr *sinkRegistry) newFileSinkFromPath(path string) (Sink, error) { case "stderr": return nopCloserSink{os.Stderr}, nil } - return sr.openFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) + return sr.openFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o666) } func normalizeScheme(s string) (string, error) { diff --git a/vendor/go.uber.org/zap/sugar.go b/vendor/go.uber.org/zap/sugar.go index ac387b3e4..8904cd087 100644 --- a/vendor/go.uber.org/zap/sugar.go +++ b/vendor/go.uber.org/zap/sugar.go @@ -115,6 +115,21 @@ func (s *SugaredLogger) With(args ...interface{}) *SugaredLogger { return &SugaredLogger{base: s.base.With(s.sweetenFields(args)...)} } +// WithLazy adds a variadic number of fields to the logging context lazily. +// The fields are evaluated only if the logger is further chained with [With] +// or is written to with any of the log level methods. +// Until that occurs, the logger may retain references to objects inside the fields, +// and logging will reflect the state of an object at the time of logging, +// not the time of WithLazy(). +// +// Similar to [With], fields added to the child don't affect the parent, +// and vice versa. Also, the keys in key-value pairs should be strings. In development, +// passing a non-string key panics, while in production it logs an error and skips the pair. +// Passing an orphaned key has the same behavior. +func (s *SugaredLogger) WithLazy(args ...interface{}) *SugaredLogger { + return &SugaredLogger{base: s.base.WithLazy(s.sweetenFields(args)...)} +} + // Level reports the minimum enabled level for this logger. // // For NopLoggers, this is [zapcore.InvalidLevel]. @@ -122,78 +137,110 @@ func (s *SugaredLogger) Level() zapcore.Level { return zapcore.LevelOf(s.base.core) } -// Debug uses fmt.Sprint to construct and log a message. +// Log logs the provided arguments at provided level. +// Spaces are added between arguments when neither is a string. +func (s *SugaredLogger) Log(lvl zapcore.Level, args ...interface{}) { + s.log(lvl, "", args, nil) +} + +// Debug logs the provided arguments at [DebugLevel]. +// Spaces are added between arguments when neither is a string. func (s *SugaredLogger) Debug(args ...interface{}) { s.log(DebugLevel, "", args, nil) } -// Info uses fmt.Sprint to construct and log a message. +// Info logs the provided arguments at [InfoLevel]. +// Spaces are added between arguments when neither is a string. func (s *SugaredLogger) Info(args ...interface{}) { s.log(InfoLevel, "", args, nil) } -// Warn uses fmt.Sprint to construct and log a message. +// Warn logs the provided arguments at [WarnLevel]. +// Spaces are added between arguments when neither is a string. func (s *SugaredLogger) Warn(args ...interface{}) { s.log(WarnLevel, "", args, nil) } -// Error uses fmt.Sprint to construct and log a message. +// Error logs the provided arguments at [ErrorLevel]. +// Spaces are added between arguments when neither is a string. func (s *SugaredLogger) Error(args ...interface{}) { s.log(ErrorLevel, "", args, nil) } -// DPanic uses fmt.Sprint to construct and log a message. In development, the -// logger then panics. (See DPanicLevel for details.) +// DPanic logs the provided arguments at [DPanicLevel]. +// In development, the logger then panics. (See [DPanicLevel] for details.) +// Spaces are added between arguments when neither is a string. func (s *SugaredLogger) DPanic(args ...interface{}) { s.log(DPanicLevel, "", args, nil) } -// Panic uses fmt.Sprint to construct and log a message, then panics. +// Panic constructs a message with the provided arguments and panics. +// Spaces are added between arguments when neither is a string. func (s *SugaredLogger) Panic(args ...interface{}) { s.log(PanicLevel, "", args, nil) } -// Fatal uses fmt.Sprint to construct and log a message, then calls os.Exit. +// Fatal constructs a message with the provided arguments and calls os.Exit. +// Spaces are added between arguments when neither is a string. func (s *SugaredLogger) Fatal(args ...interface{}) { s.log(FatalLevel, "", args, nil) } -// Debugf uses fmt.Sprintf to log a templated message. +// Logf formats the message according to the format specifier +// and logs it at provided level. +func (s *SugaredLogger) Logf(lvl zapcore.Level, template string, args ...interface{}) { + s.log(lvl, template, args, nil) +} + +// Debugf formats the message according to the format specifier +// and logs it at [DebugLevel]. func (s *SugaredLogger) Debugf(template string, args ...interface{}) { s.log(DebugLevel, template, args, nil) } -// Infof uses fmt.Sprintf to log a templated message. +// Infof formats the message according to the format specifier +// and logs it at [InfoLevel]. func (s *SugaredLogger) Infof(template string, args ...interface{}) { s.log(InfoLevel, template, args, nil) } -// Warnf uses fmt.Sprintf to log a templated message. +// Warnf formats the message according to the format specifier +// and logs it at [WarnLevel]. func (s *SugaredLogger) Warnf(template string, args ...interface{}) { s.log(WarnLevel, template, args, nil) } -// Errorf uses fmt.Sprintf to log a templated message. +// Errorf formats the message according to the format specifier +// and logs it at [ErrorLevel]. func (s *SugaredLogger) Errorf(template string, args ...interface{}) { s.log(ErrorLevel, template, args, nil) } -// DPanicf uses fmt.Sprintf to log a templated message. In development, the -// logger then panics. (See DPanicLevel for details.) +// DPanicf formats the message according to the format specifier +// and logs it at [DPanicLevel]. +// In development, the logger then panics. (See [DPanicLevel] for details.) func (s *SugaredLogger) DPanicf(template string, args ...interface{}) { s.log(DPanicLevel, template, args, nil) } -// Panicf uses fmt.Sprintf to log a templated message, then panics. +// Panicf formats the message according to the format specifier +// and panics. func (s *SugaredLogger) Panicf(template string, args ...interface{}) { s.log(PanicLevel, template, args, nil) } -// Fatalf uses fmt.Sprintf to log a templated message, then calls os.Exit. +// Fatalf formats the message according to the format specifier +// and calls os.Exit. func (s *SugaredLogger) Fatalf(template string, args ...interface{}) { s.log(FatalLevel, template, args, nil) } +// Logw logs a message with some additional context. The variadic key-value +// pairs are treated as they are in With. +func (s *SugaredLogger) Logw(lvl zapcore.Level, msg string, keysAndValues ...interface{}) { + s.log(lvl, msg, nil, keysAndValues) +} + // Debugw logs a message with some additional context. The variadic key-value // pairs are treated as they are in With. // @@ -241,38 +288,51 @@ func (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) { s.log(FatalLevel, msg, nil, keysAndValues) } -// Debugln uses fmt.Sprintln to construct and log a message. +// Logln logs a message at provided level. +// Spaces are always added between arguments. +func (s *SugaredLogger) Logln(lvl zapcore.Level, args ...interface{}) { + s.logln(lvl, args, nil) +} + +// Debugln logs a message at [DebugLevel]. +// Spaces are always added between arguments. func (s *SugaredLogger) Debugln(args ...interface{}) { s.logln(DebugLevel, args, nil) } -// Infoln uses fmt.Sprintln to construct and log a message. +// Infoln logs a message at [InfoLevel]. +// Spaces are always added between arguments. func (s *SugaredLogger) Infoln(args ...interface{}) { s.logln(InfoLevel, args, nil) } -// Warnln uses fmt.Sprintln to construct and log a message. +// Warnln logs a message at [WarnLevel]. +// Spaces are always added between arguments. func (s *SugaredLogger) Warnln(args ...interface{}) { s.logln(WarnLevel, args, nil) } -// Errorln uses fmt.Sprintln to construct and log a message. +// Errorln logs a message at [ErrorLevel]. +// Spaces are always added between arguments. func (s *SugaredLogger) Errorln(args ...interface{}) { s.logln(ErrorLevel, args, nil) } -// DPanicln uses fmt.Sprintln to construct and log a message. In development, the -// logger then panics. (See DPanicLevel for details.) +// DPanicln logs a message at [DPanicLevel]. +// In development, the logger then panics. (See [DPanicLevel] for details.) +// Spaces are always added between arguments. func (s *SugaredLogger) DPanicln(args ...interface{}) { s.logln(DPanicLevel, args, nil) } -// Panicln uses fmt.Sprintln to construct and log a message, then panics. +// Panicln logs a message at [PanicLevel] and panics. +// Spaces are always added between arguments. func (s *SugaredLogger) Panicln(args ...interface{}) { s.logln(PanicLevel, args, nil) } -// Fatalln uses fmt.Sprintln to construct and log a message, then calls os.Exit. +// Fatalln logs a message at [FatalLevel] and calls os.Exit. +// Spaces are always added between arguments. func (s *SugaredLogger) Fatalln(args ...interface{}) { s.logln(FatalLevel, args, nil) } diff --git a/vendor/go.uber.org/zap/writer.go b/vendor/go.uber.org/zap/writer.go index f08728e1e..06768c679 100644 --- a/vendor/go.uber.org/zap/writer.go +++ b/vendor/go.uber.org/zap/writer.go @@ -48,21 +48,21 @@ import ( // os.Stdout and os.Stderr. When specified without a scheme, relative file // paths also work. func Open(paths ...string) (zapcore.WriteSyncer, func(), error) { - writers, close, err := open(paths) + writers, closeAll, err := open(paths) if err != nil { return nil, nil, err } writer := CombineWriteSyncers(writers...) - return writer, close, nil + return writer, closeAll, nil } func open(paths []string) ([]zapcore.WriteSyncer, func(), error) { writers := make([]zapcore.WriteSyncer, 0, len(paths)) closers := make([]io.Closer, 0, len(paths)) - close := func() { + closeAll := func() { for _, c := range closers { - c.Close() + _ = c.Close() } } @@ -77,11 +77,11 @@ func open(paths []string) ([]zapcore.WriteSyncer, func(), error) { closers = append(closers, sink) } if openErr != nil { - close() + closeAll() return nil, nil, openErr } - return writers, close, nil + return writers, closeAll, nil } // CombineWriteSyncers is a utility that combines multiple WriteSyncers into a diff --git a/vendor/go.uber.org/zap/zapcore/console_encoder.go b/vendor/go.uber.org/zap/zapcore/console_encoder.go index 1aa5dc364..cc2b4e07b 100644 --- a/vendor/go.uber.org/zap/zapcore/console_encoder.go +++ b/vendor/go.uber.org/zap/zapcore/console_encoder.go @@ -22,20 +22,20 @@ package zapcore import ( "fmt" - "sync" "go.uber.org/zap/buffer" "go.uber.org/zap/internal/bufferpool" + "go.uber.org/zap/internal/pool" ) -var _sliceEncoderPool = sync.Pool{ - New: func() interface{} { - return &sliceArrayEncoder{elems: make([]interface{}, 0, 2)} - }, -} +var _sliceEncoderPool = pool.New(func() *sliceArrayEncoder { + return &sliceArrayEncoder{ + elems: make([]interface{}, 0, 2), + } +}) func getSliceEncoder() *sliceArrayEncoder { - return _sliceEncoderPool.Get().(*sliceArrayEncoder) + return _sliceEncoderPool.Get() } func putSliceEncoder(e *sliceArrayEncoder) { @@ -77,7 +77,7 @@ func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, // If this ever becomes a performance bottleneck, we can implement // ArrayEncoder for our plain-text format. arr := getSliceEncoder() - if c.TimeKey != "" && c.EncodeTime != nil { + if c.TimeKey != "" && c.EncodeTime != nil && !ent.Time.IsZero() { c.EncodeTime(ent.Time, arr) } if c.LevelKey != "" && c.EncodeLevel != nil { diff --git a/vendor/go.uber.org/zap/zapcore/core.go b/vendor/go.uber.org/zap/zapcore/core.go index 9dfd64051..776e93f6f 100644 --- a/vendor/go.uber.org/zap/zapcore/core.go +++ b/vendor/go.uber.org/zap/zapcore/core.go @@ -102,9 +102,9 @@ func (c *ioCore) Write(ent Entry, fields []Field) error { return err } if ent.Level > ErrorLevel { - // Since we may be crashing the program, sync the output. Ignore Sync - // errors, pending a clean solution to issue #370. - c.Sync() + // Since we may be crashing the program, sync the output. + // Ignore Sync errors, pending a clean solution to issue #370. + _ = c.Sync() } return nil } diff --git a/vendor/go.uber.org/zap/zapcore/encoder.go b/vendor/go.uber.org/zap/zapcore/encoder.go index 5769ff3e4..044625415 100644 --- a/vendor/go.uber.org/zap/zapcore/encoder.go +++ b/vendor/go.uber.org/zap/zapcore/encoder.go @@ -37,6 +37,9 @@ const DefaultLineEnding = "\n" const OmitKey = "" // A LevelEncoder serializes a Level to a primitive type. +// +// This function must make exactly one call +// to a PrimitiveArrayEncoder's Append* method. type LevelEncoder func(Level, PrimitiveArrayEncoder) // LowercaseLevelEncoder serializes a Level to a lowercase string. For example, @@ -90,6 +93,9 @@ func (e *LevelEncoder) UnmarshalText(text []byte) error { } // A TimeEncoder serializes a time.Time to a primitive type. +// +// This function must make exactly one call +// to a PrimitiveArrayEncoder's Append* method. type TimeEncoder func(time.Time, PrimitiveArrayEncoder) // EpochTimeEncoder serializes a time.Time to a floating-point number of seconds @@ -219,6 +225,9 @@ func (e *TimeEncoder) UnmarshalJSON(data []byte) error { } // A DurationEncoder serializes a time.Duration to a primitive type. +// +// This function must make exactly one call +// to a PrimitiveArrayEncoder's Append* method. type DurationEncoder func(time.Duration, PrimitiveArrayEncoder) // SecondsDurationEncoder serializes a time.Duration to a floating-point number of seconds elapsed. @@ -262,6 +271,9 @@ func (e *DurationEncoder) UnmarshalText(text []byte) error { } // A CallerEncoder serializes an EntryCaller to a primitive type. +// +// This function must make exactly one call +// to a PrimitiveArrayEncoder's Append* method. type CallerEncoder func(EntryCaller, PrimitiveArrayEncoder) // FullCallerEncoder serializes a caller in /full/path/to/package/file:line @@ -292,6 +304,9 @@ func (e *CallerEncoder) UnmarshalText(text []byte) error { // A NameEncoder serializes a period-separated logger name to a primitive // type. +// +// This function must make exactly one call +// to a PrimitiveArrayEncoder's Append* method. type NameEncoder func(string, PrimitiveArrayEncoder) // FullNameEncoder serializes the logger name as-is. diff --git a/vendor/go.uber.org/zap/zapcore/entry.go b/vendor/go.uber.org/zap/zapcore/entry.go index 9d326e95e..459a5d7ce 100644 --- a/vendor/go.uber.org/zap/zapcore/entry.go +++ b/vendor/go.uber.org/zap/zapcore/entry.go @@ -24,25 +24,23 @@ import ( "fmt" "runtime" "strings" - "sync" "time" "go.uber.org/multierr" "go.uber.org/zap/internal/bufferpool" "go.uber.org/zap/internal/exit" + "go.uber.org/zap/internal/pool" ) -var ( - _cePool = sync.Pool{New: func() interface{} { - // Pre-allocate some space for cores. - return &CheckedEntry{ - cores: make([]Core, 4), - } - }} -) +var _cePool = pool.New(func() *CheckedEntry { + // Pre-allocate some space for cores. + return &CheckedEntry{ + cores: make([]Core, 4), + } +}) func getCheckedEntry() *CheckedEntry { - ce := _cePool.Get().(*CheckedEntry) + ce := _cePool.Get() ce.reset() return ce } @@ -244,7 +242,7 @@ func (ce *CheckedEntry) Write(fields ...Field) { // CheckedEntry is being used after it was returned to the pool, // the message may be an amalgamation from multiple call sites. fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n", ce.Time, ce.Entry) - ce.ErrorOutput.Sync() + _ = ce.ErrorOutput.Sync() // ignore error } return } @@ -256,7 +254,7 @@ func (ce *CheckedEntry) Write(fields ...Field) { } if err != nil && ce.ErrorOutput != nil { fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n", ce.Time, err) - ce.ErrorOutput.Sync() + _ = ce.ErrorOutput.Sync() // ignore error } hook := ce.after diff --git a/vendor/go.uber.org/zap/zapcore/error.go b/vendor/go.uber.org/zap/zapcore/error.go index 06359907a..c40df1326 100644 --- a/vendor/go.uber.org/zap/zapcore/error.go +++ b/vendor/go.uber.org/zap/zapcore/error.go @@ -23,7 +23,8 @@ package zapcore import ( "fmt" "reflect" - "sync" + + "go.uber.org/zap/internal/pool" ) // Encodes the given error into fields of an object. A field with the given @@ -97,15 +98,18 @@ func (errs errArray) MarshalLogArray(arr ArrayEncoder) error { } el := newErrArrayElem(errs[i]) - arr.AppendObject(el) + err := arr.AppendObject(el) el.Free() + if err != nil { + return err + } } return nil } -var _errArrayElemPool = sync.Pool{New: func() interface{} { +var _errArrayElemPool = pool.New(func() *errArrayElem { return &errArrayElem{} -}} +}) // Encodes any error into a {"error": ...} re-using the same errors logic. // @@ -113,7 +117,7 @@ var _errArrayElemPool = sync.Pool{New: func() interface{} { type errArrayElem struct{ err error } func newErrArrayElem(err error) *errArrayElem { - e := _errArrayElemPool.Get().(*errArrayElem) + e := _errArrayElemPool.Get() e.err = err return e } diff --git a/vendor/go.uber.org/zap/zapcore/field.go b/vendor/go.uber.org/zap/zapcore/field.go index 95bdb0a12..308c9781e 100644 --- a/vendor/go.uber.org/zap/zapcore/field.go +++ b/vendor/go.uber.org/zap/zapcore/field.go @@ -47,7 +47,7 @@ const ( ByteStringType // Complex128Type indicates that the field carries a complex128. Complex128Type - // Complex64Type indicates that the field carries a complex128. + // Complex64Type indicates that the field carries a complex64. Complex64Type // DurationType indicates that the field carries a time.Duration. DurationType diff --git a/vendor/go.uber.org/zap/zapcore/json_encoder.go b/vendor/go.uber.org/zap/zapcore/json_encoder.go index 3921c5cd3..9685169b2 100644 --- a/vendor/go.uber.org/zap/zapcore/json_encoder.go +++ b/vendor/go.uber.org/zap/zapcore/json_encoder.go @@ -23,24 +23,20 @@ package zapcore import ( "encoding/base64" "math" - "sync" "time" "unicode/utf8" "go.uber.org/zap/buffer" "go.uber.org/zap/internal/bufferpool" + "go.uber.org/zap/internal/pool" ) // For JSON-escaping; see jsonEncoder.safeAddString below. const _hex = "0123456789abcdef" -var _jsonPool = sync.Pool{New: func() interface{} { +var _jsonPool = pool.New(func() *jsonEncoder { return &jsonEncoder{} -}} - -func getJSONEncoder() *jsonEncoder { - return _jsonPool.Get().(*jsonEncoder) -} +}) func putJSONEncoder(enc *jsonEncoder) { if enc.reflectBuf != nil { @@ -354,7 +350,7 @@ func (enc *jsonEncoder) Clone() Encoder { } func (enc *jsonEncoder) clone() *jsonEncoder { - clone := getJSONEncoder() + clone := _jsonPool.Get() clone.EncoderConfig = enc.EncoderConfig clone.spaced = enc.spaced clone.openNamespaces = enc.openNamespaces @@ -376,7 +372,7 @@ func (enc *jsonEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, final.AppendString(ent.Level.String()) } } - if final.TimeKey != "" { + if final.TimeKey != "" && !ent.Time.IsZero() { final.AddTime(final.TimeKey, ent.Time) } if ent.LoggerName != "" && final.NameKey != "" { @@ -490,73 +486,98 @@ func (enc *jsonEncoder) appendFloat(val float64, bitSize int) { // Unlike the standard library's encoder, it doesn't attempt to protect the // user from browser vulnerabilities or JSONP-related problems. func (enc *jsonEncoder) safeAddString(s string) { - for i := 0; i < len(s); { - if enc.tryAddRuneSelf(s[i]) { - i++ - continue - } - r, size := utf8.DecodeRuneInString(s[i:]) - if enc.tryAddRuneError(r, size) { - i++ - continue - } - enc.buf.AppendString(s[i : i+size]) - i += size - } + safeAppendStringLike( + (*buffer.Buffer).AppendString, + utf8.DecodeRuneInString, + enc.buf, + s, + ) } // safeAddByteString is no-alloc equivalent of safeAddString(string(s)) for s []byte. func (enc *jsonEncoder) safeAddByteString(s []byte) { + safeAppendStringLike( + (*buffer.Buffer).AppendBytes, + utf8.DecodeRune, + enc.buf, + s, + ) +} + +// safeAppendStringLike is a generic implementation of safeAddString and safeAddByteString. +// It appends a string or byte slice to the buffer, escaping all special characters. +func safeAppendStringLike[S []byte | string]( + // appendTo appends this string-like object to the buffer. + appendTo func(*buffer.Buffer, S), + // decodeRune decodes the next rune from the string-like object + // and returns its value and width in bytes. + decodeRune func(S) (rune, int), + buf *buffer.Buffer, + s S, +) { + // The encoding logic below works by skipping over characters + // that can be safely copied as-is, + // until a character is found that needs special handling. + // At that point, we copy everything we've seen so far, + // and then handle that special character. + // + // last is the index of the last byte that was copied to the buffer. + last := 0 for i := 0; i < len(s); { - if enc.tryAddRuneSelf(s[i]) { - i++ - continue - } - r, size := utf8.DecodeRune(s[i:]) - if enc.tryAddRuneError(r, size) { - i++ - continue - } - enc.buf.Write(s[i : i+size]) - i += size - } -} + if s[i] >= utf8.RuneSelf { + // Character >= RuneSelf may be part of a multi-byte rune. + // They need to be decoded before we can decide how to handle them. + r, size := decodeRune(s[i:]) + if r != utf8.RuneError || size != 1 { + // No special handling required. + // Skip over this rune and continue. + i += size + continue + } -// tryAddRuneSelf appends b if it is valid UTF-8 character represented in a single byte. -func (enc *jsonEncoder) tryAddRuneSelf(b byte) bool { - if b >= utf8.RuneSelf { - return false - } - if 0x20 <= b && b != '\\' && b != '"' { - enc.buf.AppendByte(b) - return true - } - switch b { - case '\\', '"': - enc.buf.AppendByte('\\') - enc.buf.AppendByte(b) - case '\n': - enc.buf.AppendByte('\\') - enc.buf.AppendByte('n') - case '\r': - enc.buf.AppendByte('\\') - enc.buf.AppendByte('r') - case '\t': - enc.buf.AppendByte('\\') - enc.buf.AppendByte('t') - default: - // Encode bytes < 0x20, except for the escape sequences above. - enc.buf.AppendString(`\u00`) - enc.buf.AppendByte(_hex[b>>4]) - enc.buf.AppendByte(_hex[b&0xF]) - } - return true -} + // Invalid UTF-8 sequence. + // Replace it with the Unicode replacement character. + appendTo(buf, s[last:i]) + buf.AppendString(`\ufffd`) -func (enc *jsonEncoder) tryAddRuneError(r rune, size int) bool { - if r == utf8.RuneError && size == 1 { - enc.buf.AppendString(`\ufffd`) - return true + i++ + last = i + } else { + // Character < RuneSelf is a single-byte UTF-8 rune. + if s[i] >= 0x20 && s[i] != '\\' && s[i] != '"' { + // No escaping necessary. + // Skip over this character and continue. + i++ + continue + } + + // This character needs to be escaped. + appendTo(buf, s[last:i]) + switch s[i] { + case '\\', '"': + buf.AppendByte('\\') + buf.AppendByte(s[i]) + case '\n': + buf.AppendByte('\\') + buf.AppendByte('n') + case '\r': + buf.AppendByte('\\') + buf.AppendByte('r') + case '\t': + buf.AppendByte('\\') + buf.AppendByte('t') + default: + // Encode bytes < 0x20, except for the escape sequences above. + buf.AppendString(`\u00`) + buf.AppendByte(_hex[s[i]>>4]) + buf.AppendByte(_hex[s[i]&0xF]) + } + + i++ + last = i + } } - return false + + // add remaining + appendTo(buf, s[last:]) } diff --git a/vendor/go.uber.org/zap/zapcore/lazy_with.go b/vendor/go.uber.org/zap/zapcore/lazy_with.go new file mode 100644 index 000000000..05288d6a8 --- /dev/null +++ b/vendor/go.uber.org/zap/zapcore/lazy_with.go @@ -0,0 +1,54 @@ +// Copyright (c) 2023 Uber Technologies, Inc. +// +// 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. + +package zapcore + +import "sync" + +type lazyWithCore struct { + Core + sync.Once + fields []Field +} + +// NewLazyWith wraps a Core with a "lazy" Core that will only encode fields if +// the logger is written to (or is further chained in a lon-lazy manner). +func NewLazyWith(core Core, fields []Field) Core { + return &lazyWithCore{ + Core: core, + fields: fields, + } +} + +func (d *lazyWithCore) initOnce() { + d.Once.Do(func() { + d.Core = d.Core.With(d.fields) + }) +} + +func (d *lazyWithCore) With(fields []Field) Core { + d.initOnce() + return d.Core.With(fields) +} + +func (d *lazyWithCore) Check(e Entry, ce *CheckedEntry) *CheckedEntry { + d.initOnce() + return d.Core.Check(e, ce) +} diff --git a/vendor/go.uber.org/zap/zapcore/sampler.go b/vendor/go.uber.org/zap/zapcore/sampler.go index dc518055a..b7c093a4f 100644 --- a/vendor/go.uber.org/zap/zapcore/sampler.go +++ b/vendor/go.uber.org/zap/zapcore/sampler.go @@ -21,9 +21,8 @@ package zapcore import ( + "sync/atomic" "time" - - "go.uber.org/atomic" ) const ( @@ -66,16 +65,16 @@ func (c *counter) IncCheckReset(t time.Time, tick time.Duration) uint64 { tn := t.UnixNano() resetAfter := c.resetAt.Load() if resetAfter > tn { - return c.counter.Inc() + return c.counter.Add(1) } c.counter.Store(1) newResetAfter := tn + tick.Nanoseconds() - if !c.resetAt.CAS(resetAfter, newResetAfter) { + if !c.resetAt.CompareAndSwap(resetAfter, newResetAfter) { // We raced with another goroutine trying to reset, and it also reset // the counter to 1, so we need to reincrement the counter. - return c.counter.Inc() + return c.counter.Add(1) } return 1 diff --git a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go index 56bfaaa17..199c21d27 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go +++ b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.7 && amd64 && gc && !purego -// +build go1.7,amd64,gc,!purego +//go:build amd64 && gc && !purego package blake2b diff --git a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s index 4b9daa18d..9ae8206c2 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s +++ b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.7 && amd64 && gc && !purego -// +build go1.7,amd64,gc,!purego +//go:build amd64 && gc && !purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.go b/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.go deleted file mode 100644 index 5fa1b3284..000000000 --- a/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.7 && amd64 && gc && !purego -// +build !go1.7,amd64,gc,!purego - -package blake2b - -import "golang.org/x/sys/cpu" - -func init() { - useSSE4 = cpu.X86.HasSSE41 -} - -//go:noescape -func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) - -func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) { - if useSSE4 { - hashBlocksSSE4(h, c, flag, blocks) - } else { - hashBlocksGeneric(h, c, flag, blocks) - } -} diff --git a/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.s b/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.s index ae75eb9af..adfac00c1 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.s +++ b/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && gc && !purego -// +build amd64,gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/blake2b/blake2b_ref.go b/vendor/golang.org/x/crypto/blake2b/blake2b_ref.go index b0137cdf0..6e28668cd 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2b_ref.go +++ b/vendor/golang.org/x/crypto/blake2b/blake2b_ref.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !amd64 || purego || !gc -// +build !amd64 purego !gc package blake2b diff --git a/vendor/golang.org/x/crypto/blake2b/register.go b/vendor/golang.org/x/crypto/blake2b/register.go index 9d8633963..54e446e1d 100644 --- a/vendor/golang.org/x/crypto/blake2b/register.go +++ b/vendor/golang.org/x/crypto/blake2b/register.go @@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.9 -// +build go1.9 - package blake2b import ( diff --git a/vendor/golang.org/x/crypto/blake2s/blake2s_386.go b/vendor/golang.org/x/crypto/blake2s/blake2s_386.go index b4463fb4d..97f629617 100644 --- a/vendor/golang.org/x/crypto/blake2s/blake2s_386.go +++ b/vendor/golang.org/x/crypto/blake2s/blake2s_386.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 && gc && !purego -// +build 386,gc,!purego package blake2s diff --git a/vendor/golang.org/x/crypto/blake2s/blake2s_386.s b/vendor/golang.org/x/crypto/blake2s/blake2s_386.s index 603d00ca3..919c02654 100644 --- a/vendor/golang.org/x/crypto/blake2s/blake2s_386.s +++ b/vendor/golang.org/x/crypto/blake2s/blake2s_386.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build 386 && gc && !purego -// +build 386,gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/blake2s/blake2s_amd64.go b/vendor/golang.org/x/crypto/blake2s/blake2s_amd64.go index becdaa120..8a7310254 100644 --- a/vendor/golang.org/x/crypto/blake2s/blake2s_amd64.go +++ b/vendor/golang.org/x/crypto/blake2s/blake2s_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && gc && !purego -// +build amd64,gc,!purego package blake2s diff --git a/vendor/golang.org/x/crypto/blake2s/blake2s_amd64.s b/vendor/golang.org/x/crypto/blake2s/blake2s_amd64.s index e9df7a7c2..fe4b818a3 100644 --- a/vendor/golang.org/x/crypto/blake2s/blake2s_amd64.s +++ b/vendor/golang.org/x/crypto/blake2s/blake2s_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && gc && !purego -// +build amd64,gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/blake2s/blake2s_ref.go b/vendor/golang.org/x/crypto/blake2s/blake2s_ref.go index 799dba0c4..38ce8e283 100644 --- a/vendor/golang.org/x/crypto/blake2s/blake2s_ref.go +++ b/vendor/golang.org/x/crypto/blake2s/blake2s_ref.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (!amd64 && !386) || !gc || purego -// +build !amd64,!386 !gc purego package blake2s diff --git a/vendor/golang.org/x/crypto/blake2s/register.go b/vendor/golang.org/x/crypto/blake2s/register.go index ef79ff3c6..3156148a4 100644 --- a/vendor/golang.org/x/crypto/blake2s/register.go +++ b/vendor/golang.org/x/crypto/blake2s/register.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.9 -// +build go1.9 package blake2s diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_arm64.go b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.go index 94c71ac1a..661ea132e 100644 --- a/vendor/golang.org/x/crypto/chacha20/chacha_arm64.go +++ b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.11 && gc && !purego -// +build go1.11,gc,!purego +//go:build gc && !purego package chacha20 diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s index 63cae9e6f..7dd2638e8 100644 --- a/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s +++ b/vendor/golang.org/x/crypto/chacha20/chacha_arm64.s @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.11 && gc && !purego -// +build go1.11,gc,!purego +//go:build gc && !purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go b/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go index 025b49897..db42e6676 100644 --- a/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go +++ b/vendor/golang.org/x/crypto/chacha20/chacha_noasm.go @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (!arm64 && !s390x && !ppc64le) || (arm64 && !go1.11) || !gc || purego -// +build !arm64,!s390x,!ppc64le arm64,!go1.11 !gc purego +//go:build (!arm64 && !s390x && !ppc64le) || !gc || purego package chacha20 diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go index da420b2e9..3a4287f99 100644 --- a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go +++ b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego package chacha20 diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s index 5c0fed26f..66aebae25 100644 --- a/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s +++ b/vendor/golang.org/x/crypto/chacha20/chacha_ppc64le.s @@ -20,7 +20,6 @@ // due to the calling conventions and initialization of constants. //go:build gc && !purego -// +build gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go index 4652247b8..683ccfd1c 100644 --- a/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go +++ b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego package chacha20 diff --git a/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s index f3ef5a019..1eda91a3d 100644 --- a/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s +++ b/vendor/golang.org/x/crypto/chacha20/chacha_s390x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego #include "go_asm.h" #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go index 0c408c570..50695a14f 100644 --- a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go +++ b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego package chacha20poly1305 diff --git a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s index 867c181a1..731d2ac6d 100644 --- a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s +++ b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s @@ -5,7 +5,6 @@ // This file was originally from https://golang.org/cl/24717 by Vlad Krasnov of CloudFlare. //go:build gc && !purego -// +build gc,!purego #include "textflag.h" // General register allocation @@ -184,11 +183,31 @@ GLOBL ·andMask<>(SB), (NOPTR+RODATA), $240 #define shiftD1Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xd2; BYTE $0x04 // PALIGNR $4, X10, X10 #define shiftD2Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xdb; BYTE $0x04 // PALIGNR $4, X11, X11 #define shiftD3Right BYTE $0x66; BYTE $0x45; BYTE $0x0f; BYTE $0x3a; BYTE $0x0f; BYTE $0xff; BYTE $0x04 // PALIGNR $4, X15, X15 + // Some macros + +// ROL rotates the uint32s in register R left by N bits, using temporary T. +#define ROL(N, R, T) \ + MOVO R, T; PSLLL $(N), T; PSRLL $(32-(N)), R; PXOR T, R + +// ROL16 rotates the uint32s in register R left by 16, using temporary T if needed. +#ifdef GOAMD64_v2 +#define ROL16(R, T) PSHUFB ·rol16<>(SB), R +#else +#define ROL16(R, T) ROL(16, R, T) +#endif + +// ROL8 rotates the uint32s in register R left by 8, using temporary T if needed. +#ifdef GOAMD64_v2 +#define ROL8(R, T) PSHUFB ·rol8<>(SB), R +#else +#define ROL8(R, T) ROL(8, R, T) +#endif + #define chachaQR(A, B, C, D, T) \ - PADDD B, A; PXOR A, D; PSHUFB ·rol16<>(SB), D \ + PADDD B, A; PXOR A, D; ROL16(D, T) \ PADDD D, C; PXOR C, B; MOVO B, T; PSLLL $12, T; PSRLL $20, B; PXOR T, B \ - PADDD B, A; PXOR A, D; PSHUFB ·rol8<>(SB), D \ + PADDD B, A; PXOR A, D; ROL8(D, T) \ PADDD D, C; PXOR C, B; MOVO B, T; PSLLL $7, T; PSRLL $25, B; PXOR T, B #define chachaQR_AVX2(A, B, C, D, T) \ diff --git a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go index f832b33d4..34e6ab1df 100644 --- a/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go +++ b/vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !amd64 || !gc || purego -// +build !amd64 !gc purego package chacha20poly1305 diff --git a/vendor/golang.org/x/crypto/cryptobyte/asn1.go b/vendor/golang.org/x/crypto/cryptobyte/asn1.go index 6fc2838a3..2492f796a 100644 --- a/vendor/golang.org/x/crypto/cryptobyte/asn1.go +++ b/vendor/golang.org/x/crypto/cryptobyte/asn1.go @@ -733,13 +733,14 @@ func (s *String) ReadOptionalASN1OctetString(out *[]byte, outPresent *bool, tag return true } -// ReadOptionalASN1Boolean sets *out to the value of the next ASN.1 BOOLEAN or, -// if the next bytes are not an ASN.1 BOOLEAN, to the value of defaultValue. -// It reports whether the operation was successful. -func (s *String) ReadOptionalASN1Boolean(out *bool, defaultValue bool) bool { +// ReadOptionalASN1Boolean attempts to read an optional ASN.1 BOOLEAN +// explicitly tagged with tag into out and advances. If no element with a +// matching tag is present, it sets "out" to defaultValue instead. It reports +// whether the read was successful. +func (s *String) ReadOptionalASN1Boolean(out *bool, tag asn1.Tag, defaultValue bool) bool { var present bool var child String - if !s.ReadOptionalASN1(&child, &present, asn1.BOOLEAN) { + if !s.ReadOptionalASN1(&child, &present, tag) { return false } @@ -748,7 +749,7 @@ func (s *String) ReadOptionalASN1Boolean(out *bool, defaultValue bool) bool { return true } - return s.ReadASN1Boolean(out) + return child.ReadASN1Boolean(out) } func (s *String) readASN1(out *String, outTag *asn1.Tag, skipHeader bool) bool { diff --git a/vendor/golang.org/x/crypto/cryptobyte/builder.go b/vendor/golang.org/x/crypto/cryptobyte/builder.go index c05ac7d16..cf254f5f1 100644 --- a/vendor/golang.org/x/crypto/cryptobyte/builder.go +++ b/vendor/golang.org/x/crypto/cryptobyte/builder.go @@ -95,6 +95,11 @@ func (b *Builder) AddUint32(v uint32) { b.add(byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) } +// AddUint48 appends a big-endian, 48-bit value to the byte string. +func (b *Builder) AddUint48(v uint64) { + b.add(byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) +} + // AddUint64 appends a big-endian, 64-bit value to the byte string. func (b *Builder) AddUint64(v uint64) { b.add(byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) diff --git a/vendor/golang.org/x/crypto/cryptobyte/string.go b/vendor/golang.org/x/crypto/cryptobyte/string.go index 0531a3d6f..10692a8a3 100644 --- a/vendor/golang.org/x/crypto/cryptobyte/string.go +++ b/vendor/golang.org/x/crypto/cryptobyte/string.go @@ -81,6 +81,17 @@ func (s *String) ReadUint32(out *uint32) bool { return true } +// ReadUint48 decodes a big-endian, 48-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint48(out *uint64) bool { + v := s.read(6) + if v == nil { + return false + } + *out = uint64(v[0])<<40 | uint64(v[1])<<32 | uint64(v[2])<<24 | uint64(v[3])<<16 | uint64(v[4])<<8 | uint64(v[5]) + return true +} + // ReadUint64 decodes a big-endian, 64-bit value into out and advances over it. // It reports whether the read was successful. func (s *String) ReadUint64(out *uint64) bool { diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go index edcf163c4..70c541692 100644 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.go @@ -1,7 +1,6 @@ // Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. //go:build amd64 && gc && !purego -// +build amd64,gc,!purego package field diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s index 293f013c9..60817acc4 100644 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64.s @@ -1,7 +1,6 @@ // Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT. //go:build amd64 && gc && !purego -// +build amd64,gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go index ddb6c9b8f..9da280d1d 100644 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_amd64_noasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !amd64 || !gc || purego -// +build !amd64 !gc purego package field diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go index af459ef51..075fe9b92 100644 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 && gc && !purego -// +build arm64,gc,!purego package field diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s index 5c91e4589..3126a4341 100644 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build arm64 && gc && !purego -// +build arm64,gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go index 234a5b2e5..fc029ac12 100644 --- a/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go +++ b/vendor/golang.org/x/crypto/curve25519/internal/field/fe_arm64_noasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !arm64 || !gc || purego -// +build !arm64 !gc purego package field diff --git a/vendor/golang.org/x/crypto/hkdf/hkdf.go b/vendor/golang.org/x/crypto/hkdf/hkdf.go index dda3f143b..f4ded5fee 100644 --- a/vendor/golang.org/x/crypto/hkdf/hkdf.go +++ b/vendor/golang.org/x/crypto/hkdf/hkdf.go @@ -56,7 +56,9 @@ func (f *hkdf) Read(p []byte) (int, error) { // Fill the rest of the buffer for len(p) > 0 { - f.expander.Reset() + if f.counter > 1 { + f.expander.Reset() + } f.expander.Write(f.prev) f.expander.Write(f.info) f.expander.Write([]byte{f.counter}) diff --git a/vendor/golang.org/x/crypto/internal/alias/alias.go b/vendor/golang.org/x/crypto/internal/alias/alias.go index 69c17f822..551ff0c35 100644 --- a/vendor/golang.org/x/crypto/internal/alias/alias.go +++ b/vendor/golang.org/x/crypto/internal/alias/alias.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !purego -// +build !purego // Package alias implements memory aliasing tests. package alias diff --git a/vendor/golang.org/x/crypto/internal/alias/alias_purego.go b/vendor/golang.org/x/crypto/internal/alias/alias_purego.go index 4775b0a43..6fe61b5c6 100644 --- a/vendor/golang.org/x/crypto/internal/alias/alias_purego.go +++ b/vendor/golang.org/x/crypto/internal/alias/alias_purego.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build purego -// +build purego // Package alias implements memory aliasing tests. package alias diff --git a/vendor/golang.org/x/crypto/internal/poly1305/bits_compat.go b/vendor/golang.org/x/crypto/internal/poly1305/bits_compat.go deleted file mode 100644 index 45b5c966b..000000000 --- a/vendor/golang.org/x/crypto/internal/poly1305/bits_compat.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.13 -// +build !go1.13 - -package poly1305 - -// Generic fallbacks for the math/bits intrinsics, copied from -// src/math/bits/bits.go. They were added in Go 1.12, but Add64 and Sum64 had -// variable time fallbacks until Go 1.13. - -func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) { - sum = x + y + carry - carryOut = ((x & y) | ((x | y) &^ sum)) >> 63 - return -} - -func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) { - diff = x - y - borrow - borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63 - return -} - -func bitsMul64(x, y uint64) (hi, lo uint64) { - const mask32 = 1<<32 - 1 - x0 := x & mask32 - x1 := x >> 32 - y0 := y & mask32 - y1 := y >> 32 - w0 := x0 * y0 - t := x1*y0 + w0>>32 - w1 := t & mask32 - w2 := t >> 32 - w1 += x0 * y1 - hi = x1*y1 + w2 + w1>>32 - lo = x * y - return -} diff --git a/vendor/golang.org/x/crypto/internal/poly1305/bits_go1.13.go b/vendor/golang.org/x/crypto/internal/poly1305/bits_go1.13.go deleted file mode 100644 index ed52b3418..000000000 --- a/vendor/golang.org/x/crypto/internal/poly1305/bits_go1.13.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.13 -// +build go1.13 - -package poly1305 - -import "math/bits" - -func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) { - return bits.Add64(x, y, carry) -} - -func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) { - return bits.Sub64(x, y, borrow) -} - -func bitsMul64(x, y uint64) (hi, lo uint64) { - return bits.Mul64(x, y) -} diff --git a/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go b/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go index f184b67d9..333da285b 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (!amd64 && !ppc64le && !s390x) || !gc || purego -// +build !amd64,!ppc64le,!s390x !gc purego package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.go index 6d522333f..164cd47d3 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s index 1d74f0f88..e0d3c6475 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go index e041da5ea..ec2202bd7 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_generic.go @@ -7,7 +7,10 @@ package poly1305 -import "encoding/binary" +import ( + "encoding/binary" + "math/bits" +) // Poly1305 [RFC 7539] is a relatively simple algorithm: the authentication tag // for a 64 bytes message is approximately @@ -114,13 +117,13 @@ type uint128 struct { } func mul64(a, b uint64) uint128 { - hi, lo := bitsMul64(a, b) + hi, lo := bits.Mul64(a, b) return uint128{lo, hi} } func add128(a, b uint128) uint128 { - lo, c := bitsAdd64(a.lo, b.lo, 0) - hi, c := bitsAdd64(a.hi, b.hi, c) + lo, c := bits.Add64(a.lo, b.lo, 0) + hi, c := bits.Add64(a.hi, b.hi, c) if c != 0 { panic("poly1305: unexpected overflow") } @@ -155,8 +158,8 @@ func updateGeneric(state *macState, msg []byte) { // hide leading zeroes. For full chunks, that's 1 << 128, so we can just // add 1 to the most significant (2¹²⁸) limb, h2. if len(msg) >= TagSize { - h0, c = bitsAdd64(h0, binary.LittleEndian.Uint64(msg[0:8]), 0) - h1, c = bitsAdd64(h1, binary.LittleEndian.Uint64(msg[8:16]), c) + h0, c = bits.Add64(h0, binary.LittleEndian.Uint64(msg[0:8]), 0) + h1, c = bits.Add64(h1, binary.LittleEndian.Uint64(msg[8:16]), c) h2 += c + 1 msg = msg[TagSize:] @@ -165,8 +168,8 @@ func updateGeneric(state *macState, msg []byte) { copy(buf[:], msg) buf[len(msg)] = 1 - h0, c = bitsAdd64(h0, binary.LittleEndian.Uint64(buf[0:8]), 0) - h1, c = bitsAdd64(h1, binary.LittleEndian.Uint64(buf[8:16]), c) + h0, c = bits.Add64(h0, binary.LittleEndian.Uint64(buf[0:8]), 0) + h1, c = bits.Add64(h1, binary.LittleEndian.Uint64(buf[8:16]), c) h2 += c msg = nil @@ -219,9 +222,9 @@ func updateGeneric(state *macState, msg []byte) { m3 := h2r1 t0 := m0.lo - t1, c := bitsAdd64(m1.lo, m0.hi, 0) - t2, c := bitsAdd64(m2.lo, m1.hi, c) - t3, _ := bitsAdd64(m3.lo, m2.hi, c) + t1, c := bits.Add64(m1.lo, m0.hi, 0) + t2, c := bits.Add64(m2.lo, m1.hi, c) + t3, _ := bits.Add64(m3.lo, m2.hi, c) // Now we have the result as 4 64-bit limbs, and we need to reduce it // modulo 2¹³⁰ - 5. The special shape of this Crandall prime lets us do @@ -243,14 +246,14 @@ func updateGeneric(state *macState, msg []byte) { // To add c * 5 to h, we first add cc = c * 4, and then add (cc >> 2) = c. - h0, c = bitsAdd64(h0, cc.lo, 0) - h1, c = bitsAdd64(h1, cc.hi, c) + h0, c = bits.Add64(h0, cc.lo, 0) + h1, c = bits.Add64(h1, cc.hi, c) h2 += c cc = shiftRightBy2(cc) - h0, c = bitsAdd64(h0, cc.lo, 0) - h1, c = bitsAdd64(h1, cc.hi, c) + h0, c = bits.Add64(h0, cc.lo, 0) + h1, c = bits.Add64(h1, cc.hi, c) h2 += c // h2 is at most 3 + 1 + 1 = 5, making the whole of h at most @@ -287,9 +290,9 @@ func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) { // in constant time, we compute t = h - (2¹³⁰ - 5), and select h as the // result if the subtraction underflows, and t otherwise. - hMinusP0, b := bitsSub64(h0, p0, 0) - hMinusP1, b := bitsSub64(h1, p1, b) - _, b = bitsSub64(h2, p2, b) + hMinusP0, b := bits.Sub64(h0, p0, 0) + hMinusP1, b := bits.Sub64(h1, p1, b) + _, b = bits.Sub64(h2, p2, b) // h = h if h < p else h - p h0 = select64(b, h0, hMinusP0) @@ -301,8 +304,8 @@ func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) { // // by just doing a wide addition with the 128 low bits of h and discarding // the overflow. - h0, c := bitsAdd64(h0, s[0], 0) - h1, _ = bitsAdd64(h1, s[1], c) + h0, c := bits.Add64(h0, s[0], 0) + h1, _ = bits.Add64(h1, s[1], c) binary.LittleEndian.PutUint64(out[0:8], h0) binary.LittleEndian.PutUint64(out[8:16], h1) diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go index 4a069941a..4aec4874b 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s index 58422aad2..d2ca5deeb 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_ppc64le.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go index ec9596688..e1d033a49 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s index aa9e0494c..0fe3a7c21 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_s390x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go index c400dfcf7..e76b44fe5 100644 --- a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go +++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && !purego && gc -// +build amd64,!purego,gc package salsa diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.s b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.s index c08927720..fcce0234b 100644 --- a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.s +++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && !purego && gc -// +build amd64,!purego,gc // This code was translated into a form compatible with 6a from the public // domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html diff --git a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_noasm.go b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_noasm.go index 4392cc1ac..9448760f2 100644 --- a/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_noasm.go +++ b/vendor/golang.org/x/crypto/salsa20/salsa/salsa20_noasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !amd64 || purego || !gc -// +build !amd64 purego !gc package salsa diff --git a/vendor/golang.org/x/crypto/sha3/hashes_generic.go b/vendor/golang.org/x/crypto/sha3/hashes_generic.go index c74fc20fc..fe8c84793 100644 --- a/vendor/golang.org/x/crypto/sha3/hashes_generic.go +++ b/vendor/golang.org/x/crypto/sha3/hashes_generic.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !gc || purego || !s390x -// +build !gc purego !s390x package sha3 diff --git a/vendor/golang.org/x/crypto/sha3/keccakf.go b/vendor/golang.org/x/crypto/sha3/keccakf.go index e5faa375c..ce48b1dd3 100644 --- a/vendor/golang.org/x/crypto/sha3/keccakf.go +++ b/vendor/golang.org/x/crypto/sha3/keccakf.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !amd64 || purego || !gc -// +build !amd64 purego !gc package sha3 diff --git a/vendor/golang.org/x/crypto/sha3/keccakf_amd64.go b/vendor/golang.org/x/crypto/sha3/keccakf_amd64.go index 248a38241..b908696be 100644 --- a/vendor/golang.org/x/crypto/sha3/keccakf_amd64.go +++ b/vendor/golang.org/x/crypto/sha3/keccakf_amd64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && !purego && gc -// +build amd64,!purego,gc package sha3 diff --git a/vendor/golang.org/x/crypto/sha3/keccakf_amd64.s b/vendor/golang.org/x/crypto/sha3/keccakf_amd64.s index 4cfa54383..1f5393886 100644 --- a/vendor/golang.org/x/crypto/sha3/keccakf_amd64.s +++ b/vendor/golang.org/x/crypto/sha3/keccakf_amd64.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && !purego && gc -// +build amd64,!purego,gc // This code was translated into a form compatible with 6a from the public // domain sources at https://github.com/gvanas/KeccakCodePackage @@ -320,9 +319,9 @@ MOVQ rDi, _si(oState); \ MOVQ rDo, _so(oState) \ -// func keccakF1600(state *[25]uint64) +// func keccakF1600(a *[25]uint64) TEXT ·keccakF1600(SB), 0, $200-8 - MOVQ state+0(FP), rpState + MOVQ a+0(FP), rpState // Convert the user state into an internal state NOTQ _be(rpState) diff --git a/vendor/golang.org/x/crypto/sha3/register.go b/vendor/golang.org/x/crypto/sha3/register.go index 8b4453aac..addfd5049 100644 --- a/vendor/golang.org/x/crypto/sha3/register.go +++ b/vendor/golang.org/x/crypto/sha3/register.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.4 -// +build go1.4 package sha3 diff --git a/vendor/golang.org/x/crypto/sha3/sha3.go b/vendor/golang.org/x/crypto/sha3/sha3.go index fa182beb4..4884d172a 100644 --- a/vendor/golang.org/x/crypto/sha3/sha3.go +++ b/vendor/golang.org/x/crypto/sha3/sha3.go @@ -121,11 +121,11 @@ func (d *state) padAndPermute(dsbyte byte) { copyOut(d, d.buf) } -// Write absorbs more data into the hash's state. It produces an error -// if more data is written to the ShakeHash after writing +// Write absorbs more data into the hash's state. It panics if any +// output has already been read. func (d *state) Write(p []byte) (written int, err error) { if d.state != spongeAbsorbing { - panic("sha3: write to sponge after read") + panic("sha3: Write after Read") } if d.buf == nil { d.buf = d.storage.asBytes()[:0] @@ -182,12 +182,16 @@ func (d *state) Read(out []byte) (n int, err error) { } // Sum applies padding to the hash state and then squeezes out the desired -// number of output bytes. +// number of output bytes. It panics if any output has already been read. func (d *state) Sum(in []byte) []byte { + if d.state != spongeAbsorbing { + panic("sha3: Sum after Read") + } + // Make a copy of the original hash so that caller can keep writing // and summing. dup := d.clone() - hash := make([]byte, dup.outputLen) + hash := make([]byte, dup.outputLen, 64) // explicit cap to allow stack allocation dup.Read(hash) return append(in, hash...) } diff --git a/vendor/golang.org/x/crypto/sha3/sha3_s390x.go b/vendor/golang.org/x/crypto/sha3/sha3_s390x.go index 63a3edb4c..d861bca52 100644 --- a/vendor/golang.org/x/crypto/sha3/sha3_s390x.go +++ b/vendor/golang.org/x/crypto/sha3/sha3_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego package sha3 @@ -49,7 +48,7 @@ type asmState struct { buf []byte // care must be taken to ensure cap(buf) is a multiple of rate rate int // equivalent to block size storage [3072]byte // underlying storage for buf - outputLen int // output length if fixed, 0 if not + outputLen int // output length for full security function code // KIMD/KLMD function code state spongeDirection // whether the sponge is absorbing or squeezing } @@ -72,8 +71,10 @@ func newAsmState(function code) *asmState { s.outputLen = 64 case shake_128: s.rate = 168 + s.outputLen = 32 case shake_256: s.rate = 136 + s.outputLen = 64 default: panic("sha3: unrecognized function code") } @@ -108,7 +109,7 @@ func (s *asmState) resetBuf() { // It never returns an error. func (s *asmState) Write(b []byte) (int, error) { if s.state != spongeAbsorbing { - panic("sha3: write to sponge after read") + panic("sha3: Write after Read") } length := len(b) for len(b) > 0 { @@ -192,8 +193,8 @@ func (s *asmState) Read(out []byte) (n int, err error) { // Sum appends the current hash to b and returns the resulting slice. // It does not change the underlying hash state. func (s *asmState) Sum(b []byte) []byte { - if s.outputLen == 0 { - panic("sha3: cannot call Sum on SHAKE functions") + if s.state != spongeAbsorbing { + panic("sha3: Sum after Read") } // Copy the state to preserve the original. diff --git a/vendor/golang.org/x/crypto/sha3/sha3_s390x.s b/vendor/golang.org/x/crypto/sha3/sha3_s390x.s index a0e051b04..826b862c7 100644 --- a/vendor/golang.org/x/crypto/sha3/sha3_s390x.s +++ b/vendor/golang.org/x/crypto/sha3/sha3_s390x.s @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build gc && !purego -// +build gc,!purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/sha3/shake.go b/vendor/golang.org/x/crypto/sha3/shake.go index d7be2954a..bb6998402 100644 --- a/vendor/golang.org/x/crypto/sha3/shake.go +++ b/vendor/golang.org/x/crypto/sha3/shake.go @@ -17,26 +17,25 @@ package sha3 import ( "encoding/binary" + "hash" "io" ) -// ShakeHash defines the interface to hash functions that -// support arbitrary-length output. +// ShakeHash defines the interface to hash functions that support +// arbitrary-length output. When used as a plain [hash.Hash], it +// produces minimum-length outputs that provide full-strength generic +// security. type ShakeHash interface { - // Write absorbs more data into the hash's state. It panics if input is - // written to it after output has been read from it. - io.Writer + hash.Hash // Read reads more output from the hash; reading affects the hash's // state. (ShakeHash.Read is thus very different from Hash.Sum) - // It never returns an error. + // It never returns an error, but subsequent calls to Write or Sum + // will panic. io.Reader // Clone returns a copy of the ShakeHash in its current state. Clone() ShakeHash - - // Reset resets the ShakeHash to its initial state. - Reset() } // cSHAKE specific context @@ -81,8 +80,8 @@ func leftEncode(value uint64) []byte { return b[i-1:] } -func newCShake(N, S []byte, rate int, dsbyte byte) ShakeHash { - c := cshakeState{state: &state{rate: rate, dsbyte: dsbyte}} +func newCShake(N, S []byte, rate, outputLen int, dsbyte byte) ShakeHash { + c := cshakeState{state: &state{rate: rate, outputLen: outputLen, dsbyte: dsbyte}} // leftEncode returns max 9 bytes c.initBlock = make([]byte, 0, 9*2+len(N)+len(S)) @@ -119,7 +118,7 @@ func NewShake128() ShakeHash { if h := newShake128Asm(); h != nil { return h } - return &state{rate: rate128, dsbyte: dsbyteShake} + return &state{rate: rate128, outputLen: 32, dsbyte: dsbyteShake} } // NewShake256 creates a new SHAKE256 variable-output-length ShakeHash. @@ -129,7 +128,7 @@ func NewShake256() ShakeHash { if h := newShake256Asm(); h != nil { return h } - return &state{rate: rate256, dsbyte: dsbyteShake} + return &state{rate: rate256, outputLen: 64, dsbyte: dsbyteShake} } // NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash, @@ -142,7 +141,7 @@ func NewCShake128(N, S []byte) ShakeHash { if len(N) == 0 && len(S) == 0 { return NewShake128() } - return newCShake(N, S, rate128, dsbyteCShake) + return newCShake(N, S, rate128, 32, dsbyteCShake) } // NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash, @@ -155,7 +154,7 @@ func NewCShake256(N, S []byte) ShakeHash { if len(N) == 0 && len(S) == 0 { return NewShake256() } - return newCShake(N, S, rate256, dsbyteCShake) + return newCShake(N, S, rate256, 64, dsbyteCShake) } // ShakeSum128 writes an arbitrary-length digest of data into hash. diff --git a/vendor/golang.org/x/crypto/sha3/shake_generic.go b/vendor/golang.org/x/crypto/sha3/shake_generic.go index 5c0710ef9..8d31cf5be 100644 --- a/vendor/golang.org/x/crypto/sha3/shake_generic.go +++ b/vendor/golang.org/x/crypto/sha3/shake_generic.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !gc || purego || !s390x -// +build !gc purego !s390x package sha3 diff --git a/vendor/golang.org/x/crypto/sha3/xor.go b/vendor/golang.org/x/crypto/sha3/xor.go index 59c8eb941..7337cca88 100644 --- a/vendor/golang.org/x/crypto/sha3/xor.go +++ b/vendor/golang.org/x/crypto/sha3/xor.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build (!amd64 && !386 && !ppc64le) || purego -// +build !amd64,!386,!ppc64le purego package sha3 diff --git a/vendor/golang.org/x/crypto/sha3/xor_unaligned.go b/vendor/golang.org/x/crypto/sha3/xor_unaligned.go index 1ce606246..870e2d16e 100644 --- a/vendor/golang.org/x/crypto/sha3/xor_unaligned.go +++ b/vendor/golang.org/x/crypto/sha3/xor_unaligned.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (amd64 || 386 || ppc64le) && !purego -// +build amd64 386 ppc64le -// +build !purego package sha3 diff --git a/vendor/golang.org/x/exp/rand/exp.go b/vendor/golang.org/x/exp/rand/exp.go new file mode 100644 index 000000000..083867276 --- /dev/null +++ b/vendor/golang.org/x/exp/rand/exp.go @@ -0,0 +1,221 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +import ( + "math" +) + +/* + * Exponential distribution + * + * See "The Ziggurat Method for Generating Random Variables" + * (Marsaglia & Tsang, 2000) + * http://www.jstatsoft.org/v05/i08/paper [pdf] + */ + +const ( + re = 7.69711747013104972 +) + +// ExpFloat64 returns an exponentially distributed float64 in the range +// (0, +math.MaxFloat64] with an exponential distribution whose rate parameter +// (lambda) is 1 and whose mean is 1/lambda (1). +// To produce a distribution with a different rate parameter, +// callers can adjust the output using: +// +// sample = ExpFloat64() / desiredRateParameter +func (r *Rand) ExpFloat64() float64 { + for { + j := r.Uint32() + i := j & 0xFF + x := float64(j) * float64(we[i]) + if j < ke[i] { + return x + } + if i == 0 { + return re - math.Log(r.Float64()) + } + if fe[i]+float32(r.Float64())*(fe[i-1]-fe[i]) < float32(math.Exp(-x)) { + return x + } + } +} + +var ke = [256]uint32{ + 0xe290a139, 0x0, 0x9beadebc, 0xc377ac71, 0xd4ddb990, + 0xde893fb8, 0xe4a8e87c, 0xe8dff16a, 0xebf2deab, 0xee49a6e8, + 0xf0204efd, 0xf19bdb8e, 0xf2d458bb, 0xf3da104b, 0xf4b86d78, + 0xf577ad8a, 0xf61de83d, 0xf6afb784, 0xf730a573, 0xf7a37651, + 0xf80a5bb6, 0xf867189d, 0xf8bb1b4f, 0xf9079062, 0xf94d70ca, + 0xf98d8c7d, 0xf9c8928a, 0xf9ff175b, 0xfa319996, 0xfa6085f8, + 0xfa8c3a62, 0xfab5084e, 0xfadb36c8, 0xfaff0410, 0xfb20a6ea, + 0xfb404fb4, 0xfb5e2951, 0xfb7a59e9, 0xfb95038c, 0xfbae44ba, + 0xfbc638d8, 0xfbdcf892, 0xfbf29a30, 0xfc0731df, 0xfc1ad1ed, + 0xfc2d8b02, 0xfc3f6c4d, 0xfc5083ac, 0xfc60ddd1, 0xfc708662, + 0xfc7f8810, 0xfc8decb4, 0xfc9bbd62, 0xfca9027c, 0xfcb5c3c3, + 0xfcc20864, 0xfccdd70a, 0xfcd935e3, 0xfce42ab0, 0xfceebace, + 0xfcf8eb3b, 0xfd02c0a0, 0xfd0c3f59, 0xfd156b7b, 0xfd1e48d6, + 0xfd26daff, 0xfd2f2552, 0xfd372af7, 0xfd3eeee5, 0xfd4673e7, + 0xfd4dbc9e, 0xfd54cb85, 0xfd5ba2f2, 0xfd62451b, 0xfd68b415, + 0xfd6ef1da, 0xfd750047, 0xfd7ae120, 0xfd809612, 0xfd8620b4, + 0xfd8b8285, 0xfd90bcf5, 0xfd95d15e, 0xfd9ac10b, 0xfd9f8d36, + 0xfda43708, 0xfda8bf9e, 0xfdad2806, 0xfdb17141, 0xfdb59c46, + 0xfdb9a9fd, 0xfdbd9b46, 0xfdc170f6, 0xfdc52bd8, 0xfdc8ccac, + 0xfdcc542d, 0xfdcfc30b, 0xfdd319ef, 0xfdd6597a, 0xfdd98245, + 0xfddc94e5, 0xfddf91e6, 0xfde279ce, 0xfde54d1f, 0xfde80c52, + 0xfdeab7de, 0xfded5034, 0xfdefd5be, 0xfdf248e3, 0xfdf4aa06, + 0xfdf6f984, 0xfdf937b6, 0xfdfb64f4, 0xfdfd818d, 0xfdff8dd0, + 0xfe018a08, 0xfe03767a, 0xfe05536c, 0xfe07211c, 0xfe08dfc9, + 0xfe0a8fab, 0xfe0c30fb, 0xfe0dc3ec, 0xfe0f48b1, 0xfe10bf76, + 0xfe122869, 0xfe1383b4, 0xfe14d17c, 0xfe1611e7, 0xfe174516, + 0xfe186b2a, 0xfe19843e, 0xfe1a9070, 0xfe1b8fd6, 0xfe1c8289, + 0xfe1d689b, 0xfe1e4220, 0xfe1f0f26, 0xfe1fcfbc, 0xfe2083ed, + 0xfe212bc3, 0xfe21c745, 0xfe225678, 0xfe22d95f, 0xfe234ffb, + 0xfe23ba4a, 0xfe241849, 0xfe2469f2, 0xfe24af3c, 0xfe24e81e, + 0xfe25148b, 0xfe253474, 0xfe2547c7, 0xfe254e70, 0xfe25485a, + 0xfe25356a, 0xfe251586, 0xfe24e88f, 0xfe24ae64, 0xfe2466e1, + 0xfe2411df, 0xfe23af34, 0xfe233eb4, 0xfe22c02c, 0xfe22336b, + 0xfe219838, 0xfe20ee58, 0xfe20358c, 0xfe1f6d92, 0xfe1e9621, + 0xfe1daef0, 0xfe1cb7ac, 0xfe1bb002, 0xfe1a9798, 0xfe196e0d, + 0xfe1832fd, 0xfe16e5fe, 0xfe15869d, 0xfe141464, 0xfe128ed3, + 0xfe10f565, 0xfe0f478c, 0xfe0d84b1, 0xfe0bac36, 0xfe09bd73, + 0xfe07b7b5, 0xfe059a40, 0xfe03644c, 0xfe011504, 0xfdfeab88, + 0xfdfc26e9, 0xfdf98629, 0xfdf6c83b, 0xfdf3ec01, 0xfdf0f04a, + 0xfdedd3d1, 0xfdea953d, 0xfde7331e, 0xfde3abe9, 0xfddffdfb, + 0xfddc2791, 0xfdd826cd, 0xfdd3f9a8, 0xfdcf9dfc, 0xfdcb1176, + 0xfdc65198, 0xfdc15bb3, 0xfdbc2ce2, 0xfdb6c206, 0xfdb117be, + 0xfdab2a63, 0xfda4f5fd, 0xfd9e7640, 0xfd97a67a, 0xfd908192, + 0xfd8901f2, 0xfd812182, 0xfd78d98e, 0xfd7022bb, 0xfd66f4ed, + 0xfd5d4732, 0xfd530f9c, 0xfd48432b, 0xfd3cd59a, 0xfd30b936, + 0xfd23dea4, 0xfd16349e, 0xfd07a7a3, 0xfcf8219b, 0xfce7895b, + 0xfcd5c220, 0xfcc2aadb, 0xfcae1d5e, 0xfc97ed4e, 0xfc7fe6d4, + 0xfc65ccf3, 0xfc495762, 0xfc2a2fc8, 0xfc07ee19, 0xfbe213c1, + 0xfbb8051a, 0xfb890078, 0xfb5411a5, 0xfb180005, 0xfad33482, + 0xfa839276, 0xfa263b32, 0xf9b72d1c, 0xf930a1a2, 0xf889f023, + 0xf7b577d2, 0xf69c650c, 0xf51530f0, 0xf2cb0e3c, 0xeeefb15d, + 0xe6da6ecf, +} +var we = [256]float32{ + 2.0249555e-09, 1.486674e-11, 2.4409617e-11, 3.1968806e-11, + 3.844677e-11, 4.4228204e-11, 4.9516443e-11, 5.443359e-11, + 5.905944e-11, 6.344942e-11, 6.7643814e-11, 7.1672945e-11, + 7.556032e-11, 7.932458e-11, 8.298079e-11, 8.654132e-11, + 9.0016515e-11, 9.3415074e-11, 9.674443e-11, 1.0001099e-10, + 1.03220314e-10, 1.06377254e-10, 1.09486115e-10, 1.1255068e-10, + 1.1557435e-10, 1.1856015e-10, 1.2151083e-10, 1.2442886e-10, + 1.2731648e-10, 1.3017575e-10, 1.3300853e-10, 1.3581657e-10, + 1.3860142e-10, 1.4136457e-10, 1.4410738e-10, 1.4683108e-10, + 1.4953687e-10, 1.5222583e-10, 1.54899e-10, 1.5755733e-10, + 1.6020171e-10, 1.6283301e-10, 1.6545203e-10, 1.6805951e-10, + 1.7065617e-10, 1.732427e-10, 1.7581973e-10, 1.7838787e-10, + 1.8094774e-10, 1.8349985e-10, 1.8604476e-10, 1.8858298e-10, + 1.9111498e-10, 1.9364126e-10, 1.9616223e-10, 1.9867835e-10, + 2.0119004e-10, 2.0369768e-10, 2.0620168e-10, 2.087024e-10, + 2.1120022e-10, 2.136955e-10, 2.1618855e-10, 2.1867974e-10, + 2.2116936e-10, 2.2365775e-10, 2.261452e-10, 2.2863202e-10, + 2.311185e-10, 2.3360494e-10, 2.360916e-10, 2.3857874e-10, + 2.4106667e-10, 2.4355562e-10, 2.4604588e-10, 2.485377e-10, + 2.5103128e-10, 2.5352695e-10, 2.560249e-10, 2.585254e-10, + 2.6102867e-10, 2.6353494e-10, 2.6604446e-10, 2.6855745e-10, + 2.7107416e-10, 2.7359479e-10, 2.761196e-10, 2.7864877e-10, + 2.8118255e-10, 2.8372119e-10, 2.8626485e-10, 2.888138e-10, + 2.9136826e-10, 2.939284e-10, 2.9649452e-10, 2.9906677e-10, + 3.016454e-10, 3.0423064e-10, 3.0682268e-10, 3.0942177e-10, + 3.1202813e-10, 3.1464195e-10, 3.1726352e-10, 3.19893e-10, + 3.2253064e-10, 3.251767e-10, 3.2783135e-10, 3.3049485e-10, + 3.3316744e-10, 3.3584938e-10, 3.3854083e-10, 3.4124212e-10, + 3.4395342e-10, 3.46675e-10, 3.4940711e-10, 3.5215003e-10, + 3.5490397e-10, 3.5766917e-10, 3.6044595e-10, 3.6323455e-10, + 3.660352e-10, 3.6884823e-10, 3.7167386e-10, 3.745124e-10, + 3.773641e-10, 3.802293e-10, 3.8310827e-10, 3.860013e-10, + 3.8890866e-10, 3.918307e-10, 3.9476775e-10, 3.9772008e-10, + 4.0068804e-10, 4.0367196e-10, 4.0667217e-10, 4.09689e-10, + 4.1272286e-10, 4.1577405e-10, 4.1884296e-10, 4.2192994e-10, + 4.250354e-10, 4.281597e-10, 4.313033e-10, 4.3446652e-10, + 4.3764986e-10, 4.408537e-10, 4.4407847e-10, 4.4732465e-10, + 4.5059267e-10, 4.5388301e-10, 4.571962e-10, 4.6053267e-10, + 4.6389292e-10, 4.6727755e-10, 4.70687e-10, 4.741219e-10, + 4.7758275e-10, 4.810702e-10, 4.845848e-10, 4.8812715e-10, + 4.9169796e-10, 4.9529775e-10, 4.989273e-10, 5.0258725e-10, + 5.0627835e-10, 5.100013e-10, 5.1375687e-10, 5.1754584e-10, + 5.21369e-10, 5.2522725e-10, 5.2912136e-10, 5.330522e-10, + 5.370208e-10, 5.4102806e-10, 5.45075e-10, 5.491625e-10, + 5.532918e-10, 5.5746385e-10, 5.616799e-10, 5.6594107e-10, + 5.7024857e-10, 5.746037e-10, 5.7900773e-10, 5.834621e-10, + 5.8796823e-10, 5.925276e-10, 5.971417e-10, 6.018122e-10, + 6.065408e-10, 6.113292e-10, 6.1617933e-10, 6.2109295e-10, + 6.260722e-10, 6.3111916e-10, 6.3623595e-10, 6.4142497e-10, + 6.4668854e-10, 6.5202926e-10, 6.5744976e-10, 6.6295286e-10, + 6.6854156e-10, 6.742188e-10, 6.79988e-10, 6.858526e-10, + 6.9181616e-10, 6.978826e-10, 7.04056e-10, 7.103407e-10, + 7.167412e-10, 7.2326256e-10, 7.2990985e-10, 7.366886e-10, + 7.4360473e-10, 7.5066453e-10, 7.5787476e-10, 7.6524265e-10, + 7.7277595e-10, 7.80483e-10, 7.883728e-10, 7.9645507e-10, + 8.047402e-10, 8.1323964e-10, 8.219657e-10, 8.309319e-10, + 8.401528e-10, 8.496445e-10, 8.594247e-10, 8.6951274e-10, + 8.799301e-10, 8.9070046e-10, 9.018503e-10, 9.134092e-10, + 9.254101e-10, 9.378904e-10, 9.508923e-10, 9.644638e-10, + 9.786603e-10, 9.935448e-10, 1.0091913e-09, 1.025686e-09, + 1.0431306e-09, 1.0616465e-09, 1.08138e-09, 1.1025096e-09, + 1.1252564e-09, 1.1498986e-09, 1.1767932e-09, 1.206409e-09, + 1.2393786e-09, 1.276585e-09, 1.3193139e-09, 1.3695435e-09, + 1.4305498e-09, 1.508365e-09, 1.6160854e-09, 1.7921248e-09, +} +var fe = [256]float32{ + 1, 0.9381437, 0.90046996, 0.87170434, 0.8477855, 0.8269933, + 0.8084217, 0.7915276, 0.77595687, 0.7614634, 0.7478686, + 0.7350381, 0.72286767, 0.71127474, 0.70019263, 0.6895665, + 0.67935055, 0.6695063, 0.66000086, 0.65080583, 0.6418967, + 0.63325197, 0.6248527, 0.6166822, 0.60872537, 0.60096896, + 0.5934009, 0.58601034, 0.5787874, 0.57172304, 0.5648092, + 0.5580383, 0.5514034, 0.5448982, 0.5385169, 0.53225386, + 0.5261042, 0.52006316, 0.5141264, 0.50828975, 0.5025495, + 0.496902, 0.49134386, 0.485872, 0.48048335, 0.4751752, + 0.46994483, 0.46478975, 0.45970762, 0.45469615, 0.44975325, + 0.44487688, 0.44006512, 0.43531612, 0.43062815, 0.42599955, + 0.42142874, 0.4169142, 0.41245446, 0.40804818, 0.403694, + 0.3993907, 0.39513698, 0.39093173, 0.38677382, 0.38266218, + 0.37859577, 0.37457356, 0.37059465, 0.3666581, 0.362763, + 0.35890847, 0.35509375, 0.351318, 0.3475805, 0.34388044, + 0.34021714, 0.3365899, 0.33299807, 0.32944095, 0.32591796, + 0.3224285, 0.3189719, 0.31554767, 0.31215525, 0.30879408, + 0.3054636, 0.3021634, 0.29889292, 0.2956517, 0.29243928, + 0.28925523, 0.28609908, 0.28297043, 0.27986884, 0.27679393, + 0.2737453, 0.2707226, 0.2677254, 0.26475343, 0.26180625, + 0.25888354, 0.25598502, 0.2531103, 0.25025907, 0.24743107, + 0.24462597, 0.24184346, 0.23908329, 0.23634516, 0.23362878, + 0.23093392, 0.2282603, 0.22560766, 0.22297576, 0.22036438, + 0.21777324, 0.21520215, 0.21265087, 0.21011916, 0.20760682, + 0.20511365, 0.20263945, 0.20018397, 0.19774707, 0.19532852, + 0.19292815, 0.19054577, 0.1881812, 0.18583426, 0.18350479, + 0.1811926, 0.17889754, 0.17661946, 0.17435817, 0.17211354, + 0.1698854, 0.16767362, 0.16547804, 0.16329853, 0.16113494, + 0.15898713, 0.15685499, 0.15473837, 0.15263714, 0.15055119, + 0.14848037, 0.14642459, 0.14438373, 0.14235765, 0.14034624, + 0.13834943, 0.13636707, 0.13439907, 0.13244532, 0.13050574, + 0.1285802, 0.12666863, 0.12477092, 0.12288698, 0.12101672, + 0.119160056, 0.1173169, 0.115487166, 0.11367077, 0.11186763, + 0.11007768, 0.10830083, 0.10653701, 0.10478614, 0.10304816, + 0.101323, 0.09961058, 0.09791085, 0.09622374, 0.09454919, + 0.09288713, 0.091237515, 0.08960028, 0.087975375, 0.08636274, + 0.08476233, 0.083174095, 0.081597984, 0.08003395, 0.07848195, + 0.076941945, 0.07541389, 0.07389775, 0.072393484, 0.07090106, + 0.069420435, 0.06795159, 0.066494495, 0.06504912, 0.063615434, + 0.062193416, 0.060783047, 0.059384305, 0.057997175, + 0.05662164, 0.05525769, 0.053905312, 0.052564494, 0.051235236, + 0.049917534, 0.048611384, 0.047316793, 0.046033762, 0.0447623, + 0.043502413, 0.042254124, 0.041017443, 0.039792392, + 0.038578995, 0.037377283, 0.036187284, 0.035009038, + 0.033842582, 0.032687962, 0.031545233, 0.030414443, 0.02929566, + 0.02818895, 0.027094385, 0.026012046, 0.024942026, 0.023884421, + 0.022839336, 0.021806888, 0.020787204, 0.019780423, 0.0187867, + 0.0178062, 0.016839107, 0.015885621, 0.014945968, 0.014020392, + 0.013109165, 0.012212592, 0.011331013, 0.01046481, 0.009614414, + 0.008780315, 0.007963077, 0.0071633533, 0.006381906, + 0.0056196423, 0.0048776558, 0.004157295, 0.0034602648, + 0.0027887989, 0.0021459677, 0.0015362998, 0.0009672693, + 0.00045413437, +} diff --git a/vendor/golang.org/x/exp/rand/normal.go b/vendor/golang.org/x/exp/rand/normal.go new file mode 100644 index 000000000..b66da3a81 --- /dev/null +++ b/vendor/golang.org/x/exp/rand/normal.go @@ -0,0 +1,156 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +import ( + "math" +) + +/* + * Normal distribution + * + * See "The Ziggurat Method for Generating Random Variables" + * (Marsaglia & Tsang, 2000) + * http://www.jstatsoft.org/v05/i08/paper [pdf] + */ + +const ( + rn = 3.442619855899 +) + +func absInt32(i int32) uint32 { + if i < 0 { + return uint32(-i) + } + return uint32(i) +} + +// NormFloat64 returns a normally distributed float64 in the range +// [-math.MaxFloat64, +math.MaxFloat64] with +// standard normal distribution (mean = 0, stddev = 1). +// To produce a different normal distribution, callers can +// adjust the output using: +// +// sample = NormFloat64() * desiredStdDev + desiredMean +func (r *Rand) NormFloat64() float64 { + for { + j := int32(r.Uint32()) // Possibly negative + i := j & 0x7F + x := float64(j) * float64(wn[i]) + if absInt32(j) < kn[i] { + // This case should be hit better than 99% of the time. + return x + } + + if i == 0 { + // This extra work is only required for the base strip. + for { + x = -math.Log(r.Float64()) * (1.0 / rn) + y := -math.Log(r.Float64()) + if y+y >= x*x { + break + } + } + if j > 0 { + return rn + x + } + return -rn - x + } + if fn[i]+float32(r.Float64())*(fn[i-1]-fn[i]) < float32(math.Exp(-.5*x*x)) { + return x + } + } +} + +var kn = [128]uint32{ + 0x76ad2212, 0x0, 0x600f1b53, 0x6ce447a6, 0x725b46a2, + 0x7560051d, 0x774921eb, 0x789a25bd, 0x799045c3, 0x7a4bce5d, + 0x7adf629f, 0x7b5682a6, 0x7bb8a8c6, 0x7c0ae722, 0x7c50cce7, + 0x7c8cec5b, 0x7cc12cd6, 0x7ceefed2, 0x7d177e0b, 0x7d3b8883, + 0x7d5bce6c, 0x7d78dd64, 0x7d932886, 0x7dab0e57, 0x7dc0dd30, + 0x7dd4d688, 0x7de73185, 0x7df81cea, 0x7e07c0a3, 0x7e163efa, + 0x7e23b587, 0x7e303dfd, 0x7e3beec2, 0x7e46db77, 0x7e51155d, + 0x7e5aabb3, 0x7e63abf7, 0x7e6c222c, 0x7e741906, 0x7e7b9a18, + 0x7e82adfa, 0x7e895c63, 0x7e8fac4b, 0x7e95a3fb, 0x7e9b4924, + 0x7ea0a0ef, 0x7ea5b00d, 0x7eaa7ac3, 0x7eaf04f3, 0x7eb3522a, + 0x7eb765a5, 0x7ebb4259, 0x7ebeeafd, 0x7ec2620a, 0x7ec5a9c4, + 0x7ec8c441, 0x7ecbb365, 0x7ece78ed, 0x7ed11671, 0x7ed38d62, + 0x7ed5df12, 0x7ed80cb4, 0x7eda175c, 0x7edc0005, 0x7eddc78e, + 0x7edf6ebf, 0x7ee0f647, 0x7ee25ebe, 0x7ee3a8a9, 0x7ee4d473, + 0x7ee5e276, 0x7ee6d2f5, 0x7ee7a620, 0x7ee85c10, 0x7ee8f4cd, + 0x7ee97047, 0x7ee9ce59, 0x7eea0eca, 0x7eea3147, 0x7eea3568, + 0x7eea1aab, 0x7ee9e071, 0x7ee98602, 0x7ee90a88, 0x7ee86d08, + 0x7ee7ac6a, 0x7ee6c769, 0x7ee5bc9c, 0x7ee48a67, 0x7ee32efc, + 0x7ee1a857, 0x7edff42f, 0x7ede0ffa, 0x7edbf8d9, 0x7ed9ab94, + 0x7ed7248d, 0x7ed45fae, 0x7ed1585c, 0x7ece095f, 0x7eca6ccb, + 0x7ec67be2, 0x7ec22eee, 0x7ebd7d1a, 0x7eb85c35, 0x7eb2c075, + 0x7eac9c20, 0x7ea5df27, 0x7e9e769f, 0x7e964c16, 0x7e8d44ba, + 0x7e834033, 0x7e781728, 0x7e6b9933, 0x7e5d8a1a, 0x7e4d9ded, + 0x7e3b737a, 0x7e268c2f, 0x7e0e3ff5, 0x7df1aa5d, 0x7dcf8c72, + 0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a, + 0x7ba90bdc, 0x7a722176, 0x77d664e5, +} +var wn = [128]float32{ + 1.7290405e-09, 1.2680929e-10, 1.6897518e-10, 1.9862688e-10, + 2.2232431e-10, 2.4244937e-10, 2.601613e-10, 2.7611988e-10, + 2.9073963e-10, 3.042997e-10, 3.1699796e-10, 3.289802e-10, + 3.4035738e-10, 3.5121603e-10, 3.616251e-10, 3.7164058e-10, + 3.8130857e-10, 3.9066758e-10, 3.9975012e-10, 4.08584e-10, + 4.1719309e-10, 4.2559822e-10, 4.338176e-10, 4.418672e-10, + 4.497613e-10, 4.5751258e-10, 4.651324e-10, 4.7263105e-10, + 4.8001775e-10, 4.87301e-10, 4.944885e-10, 5.015873e-10, + 5.0860405e-10, 5.155446e-10, 5.2241467e-10, 5.2921934e-10, + 5.359635e-10, 5.426517e-10, 5.4928817e-10, 5.5587696e-10, + 5.624219e-10, 5.6892646e-10, 5.753941e-10, 5.818282e-10, + 5.882317e-10, 5.946077e-10, 6.00959e-10, 6.072884e-10, + 6.135985e-10, 6.19892e-10, 6.2617134e-10, 6.3243905e-10, + 6.386974e-10, 6.449488e-10, 6.511956e-10, 6.5744005e-10, + 6.6368433e-10, 6.699307e-10, 6.7618144e-10, 6.824387e-10, + 6.8870465e-10, 6.949815e-10, 7.012715e-10, 7.075768e-10, + 7.1389966e-10, 7.202424e-10, 7.266073e-10, 7.329966e-10, + 7.394128e-10, 7.4585826e-10, 7.5233547e-10, 7.58847e-10, + 7.653954e-10, 7.719835e-10, 7.7861395e-10, 7.852897e-10, + 7.920138e-10, 7.987892e-10, 8.0561924e-10, 8.125073e-10, + 8.194569e-10, 8.2647167e-10, 8.3355556e-10, 8.407127e-10, + 8.479473e-10, 8.55264e-10, 8.6266755e-10, 8.7016316e-10, + 8.777562e-10, 8.8545243e-10, 8.932582e-10, 9.0117996e-10, + 9.09225e-10, 9.174008e-10, 9.2571584e-10, 9.341788e-10, + 9.427997e-10, 9.515889e-10, 9.605579e-10, 9.697193e-10, + 9.790869e-10, 9.88676e-10, 9.985036e-10, 1.0085882e-09, + 1.0189509e-09, 1.0296151e-09, 1.0406069e-09, 1.0519566e-09, + 1.063698e-09, 1.0758702e-09, 1.0885183e-09, 1.1016947e-09, + 1.1154611e-09, 1.1298902e-09, 1.1450696e-09, 1.1611052e-09, + 1.1781276e-09, 1.1962995e-09, 1.2158287e-09, 1.2369856e-09, + 1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09, + 1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09, +} +var fn = [128]float32{ + 1, 0.9635997, 0.9362827, 0.9130436, 0.89228165, 0.87324303, + 0.8555006, 0.8387836, 0.8229072, 0.8077383, 0.793177, + 0.7791461, 0.7655842, 0.7524416, 0.73967725, 0.7272569, + 0.7151515, 0.7033361, 0.69178915, 0.68049186, 0.6694277, + 0.658582, 0.6479418, 0.63749546, 0.6272325, 0.6171434, + 0.6072195, 0.5974532, 0.58783704, 0.5783647, 0.56903, + 0.5598274, 0.5507518, 0.54179835, 0.5329627, 0.52424055, + 0.5156282, 0.50712204, 0.49871865, 0.49041483, 0.48220766, + 0.4740943, 0.46607214, 0.4581387, 0.45029163, 0.44252872, + 0.43484783, 0.427247, 0.41972435, 0.41227803, 0.40490642, + 0.39760786, 0.3903808, 0.3832238, 0.37613547, 0.36911446, + 0.3621595, 0.35526937, 0.34844297, 0.34167916, 0.33497685, + 0.3283351, 0.3217529, 0.3152294, 0.30876362, 0.30235484, + 0.29600215, 0.28970486, 0.2834622, 0.2772735, 0.27113807, + 0.2650553, 0.25902456, 0.2530453, 0.24711695, 0.241239, + 0.23541094, 0.22963232, 0.2239027, 0.21822165, 0.21258877, + 0.20700371, 0.20146611, 0.19597565, 0.19053204, 0.18513499, + 0.17978427, 0.17447963, 0.1692209, 0.16400786, 0.15884037, + 0.15371831, 0.14864157, 0.14361008, 0.13862377, 0.13368265, + 0.12878671, 0.12393598, 0.119130544, 0.11437051, 0.10965602, + 0.104987256, 0.10036444, 0.095787846, 0.0912578, 0.08677467, + 0.0823389, 0.077950984, 0.073611505, 0.06932112, 0.06508058, + 0.06089077, 0.056752663, 0.0526674, 0.048636295, 0.044660863, + 0.040742867, 0.03688439, 0.033087887, 0.029356318, + 0.025693292, 0.022103304, 0.018592102, 0.015167298, + 0.011839478, 0.008624485, 0.005548995, 0.0026696292, +} diff --git a/vendor/golang.org/x/exp/rand/rand.go b/vendor/golang.org/x/exp/rand/rand.go new file mode 100644 index 000000000..ee6161bc6 --- /dev/null +++ b/vendor/golang.org/x/exp/rand/rand.go @@ -0,0 +1,372 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package rand implements pseudo-random number generators. +// +// Random numbers are generated by a Source. Top-level functions, such as +// Float64 and Int, use a default shared Source that produces a deterministic +// sequence of values each time a program is run. Use the Seed function to +// initialize the default Source if different behavior is required for each run. +// The default Source, a LockedSource, is safe for concurrent use by multiple +// goroutines, but Sources created by NewSource are not. However, Sources are small +// and it is reasonable to have a separate Source for each goroutine, seeded +// differently, to avoid locking. +// +// For random numbers suitable for security-sensitive work, see the crypto/rand +// package. +package rand + +import "sync" + +// A Source represents a source of uniformly-distributed +// pseudo-random int64 values in the range [0, 1<<64). +type Source interface { + Uint64() uint64 + Seed(seed uint64) +} + +// NewSource returns a new pseudo-random Source seeded with the given value. +func NewSource(seed uint64) Source { + var rng PCGSource + rng.Seed(seed) + return &rng +} + +// A Rand is a source of random numbers. +type Rand struct { + src Source + + // readVal contains remainder of 64-bit integer used for bytes + // generation during most recent Read call. + // It is saved so next Read call can start where the previous + // one finished. + readVal uint64 + // readPos indicates the number of low-order bytes of readVal + // that are still valid. + readPos int8 +} + +// New returns a new Rand that uses random values from src +// to generate other random values. +func New(src Source) *Rand { + return &Rand{src: src} +} + +// Seed uses the provided seed value to initialize the generator to a deterministic state. +// Seed should not be called concurrently with any other Rand method. +func (r *Rand) Seed(seed uint64) { + if lk, ok := r.src.(*LockedSource); ok { + lk.seedPos(seed, &r.readPos) + return + } + + r.src.Seed(seed) + r.readPos = 0 +} + +// Uint64 returns a pseudo-random 64-bit integer as a uint64. +func (r *Rand) Uint64() uint64 { return r.src.Uint64() } + +// Int63 returns a non-negative pseudo-random 63-bit integer as an int64. +func (r *Rand) Int63() int64 { return int64(r.src.Uint64() &^ (1 << 63)) } + +// Uint32 returns a pseudo-random 32-bit value as a uint32. +func (r *Rand) Uint32() uint32 { return uint32(r.Uint64() >> 32) } + +// Int31 returns a non-negative pseudo-random 31-bit integer as an int32. +func (r *Rand) Int31() int32 { return int32(r.Uint64() >> 33) } + +// Int returns a non-negative pseudo-random int. +func (r *Rand) Int() int { + u := uint(r.Uint64()) + return int(u << 1 >> 1) // clear sign bit. +} + +const maxUint64 = (1 << 64) - 1 + +// Uint64n returns, as a uint64, a pseudo-random number in [0,n). +// It is guaranteed more uniform than taking a Source value mod n +// for any n that is not a power of 2. +func (r *Rand) Uint64n(n uint64) uint64 { + if n&(n-1) == 0 { // n is power of two, can mask + if n == 0 { + panic("invalid argument to Uint64n") + } + return r.Uint64() & (n - 1) + } + // If n does not divide v, to avoid bias we must not use + // a v that is within maxUint64%n of the top of the range. + v := r.Uint64() + if v > maxUint64-n { // Fast check. + ceiling := maxUint64 - maxUint64%n + for v >= ceiling { + v = r.Uint64() + } + } + + return v % n +} + +// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n). +// It panics if n <= 0. +func (r *Rand) Int63n(n int64) int64 { + if n <= 0 { + panic("invalid argument to Int63n") + } + return int64(r.Uint64n(uint64(n))) +} + +// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n). +// It panics if n <= 0. +func (r *Rand) Int31n(n int32) int32 { + if n <= 0 { + panic("invalid argument to Int31n") + } + // TODO: Avoid some 64-bit ops to make it more efficient on 32-bit machines. + return int32(r.Uint64n(uint64(n))) +} + +// Intn returns, as an int, a non-negative pseudo-random number in [0,n). +// It panics if n <= 0. +func (r *Rand) Intn(n int) int { + if n <= 0 { + panic("invalid argument to Intn") + } + // TODO: Avoid some 64-bit ops to make it more efficient on 32-bit machines. + return int(r.Uint64n(uint64(n))) +} + +// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0). +func (r *Rand) Float64() float64 { + // There is one bug in the value stream: r.Int63() may be so close + // to 1<<63 that the division rounds up to 1.0, and we've guaranteed + // that the result is always less than 1.0. + // + // We tried to fix this by mapping 1.0 back to 0.0, but since float64 + // values near 0 are much denser than near 1, mapping 1 to 0 caused + // a theoretically significant overshoot in the probability of returning 0. + // Instead of that, if we round up to 1, just try again. + // Getting 1 only happens 1/2⁵³ of the time, so most clients + // will not observe it anyway. +again: + f := float64(r.Uint64n(1<<53)) / (1 << 53) + if f == 1.0 { + goto again // resample; this branch is taken O(never) + } + return f +} + +// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0). +func (r *Rand) Float32() float32 { + // We do not want to return 1.0. + // This only happens 1/2²⁴ of the time (plus the 1/2⁵³ of the time in Float64). +again: + f := float32(r.Float64()) + if f == 1 { + goto again // resample; this branch is taken O(very rarely) + } + return f +} + +// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n). +func (r *Rand) Perm(n int) []int { + m := make([]int, n) + // In the following loop, the iteration when i=0 always swaps m[0] with m[0]. + // A change to remove this useless iteration is to assign 1 to i in the init + // statement. But Perm also effects r. Making this change will affect + // the final state of r. So this change can't be made for compatibility + // reasons for Go 1. + for i := 0; i < n; i++ { + j := r.Intn(i + 1) + m[i] = m[j] + m[j] = i + } + return m +} + +// Shuffle pseudo-randomizes the order of elements. +// n is the number of elements. Shuffle panics if n < 0. +// swap swaps the elements with indexes i and j. +func (r *Rand) Shuffle(n int, swap func(i, j int)) { + if n < 0 { + panic("invalid argument to Shuffle") + } + + // Fisher-Yates shuffle: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle + // Shuffle really ought not be called with n that doesn't fit in 32 bits. + // Not only will it take a very long time, but with 2³¹! possible permutations, + // there's no way that any PRNG can have a big enough internal state to + // generate even a minuscule percentage of the possible permutations. + // Nevertheless, the right API signature accepts an int n, so handle it as best we can. + i := n - 1 + for ; i > 1<<31-1-1; i-- { + j := int(r.Int63n(int64(i + 1))) + swap(i, j) + } + for ; i > 0; i-- { + j := int(r.Int31n(int32(i + 1))) + swap(i, j) + } +} + +// Read generates len(p) random bytes and writes them into p. It +// always returns len(p) and a nil error. +// Read should not be called concurrently with any other Rand method unless +// the underlying source is a LockedSource. +func (r *Rand) Read(p []byte) (n int, err error) { + if lk, ok := r.src.(*LockedSource); ok { + return lk.Read(p, &r.readVal, &r.readPos) + } + return read(p, r.src, &r.readVal, &r.readPos) +} + +func read(p []byte, src Source, readVal *uint64, readPos *int8) (n int, err error) { + pos := *readPos + val := *readVal + rng, _ := src.(*PCGSource) + for n = 0; n < len(p); n++ { + if pos == 0 { + if rng != nil { + val = rng.Uint64() + } else { + val = src.Uint64() + } + pos = 8 + } + p[n] = byte(val) + val >>= 8 + pos-- + } + *readPos = pos + *readVal = val + return +} + +/* + * Top-level convenience functions + */ + +var globalRand = New(&LockedSource{src: *NewSource(1).(*PCGSource)}) + +// Type assert that globalRand's source is a LockedSource whose src is a PCGSource. +var _ PCGSource = globalRand.src.(*LockedSource).src + +// Seed uses the provided seed value to initialize the default Source to a +// deterministic state. If Seed is not called, the generator behaves as +// if seeded by Seed(1). +// Seed, unlike the Rand.Seed method, is safe for concurrent use. +func Seed(seed uint64) { globalRand.Seed(seed) } + +// Int63 returns a non-negative pseudo-random 63-bit integer as an int64 +// from the default Source. +func Int63() int64 { return globalRand.Int63() } + +// Uint32 returns a pseudo-random 32-bit value as a uint32 +// from the default Source. +func Uint32() uint32 { return globalRand.Uint32() } + +// Uint64 returns a pseudo-random 64-bit value as a uint64 +// from the default Source. +func Uint64() uint64 { return globalRand.Uint64() } + +// Int31 returns a non-negative pseudo-random 31-bit integer as an int32 +// from the default Source. +func Int31() int32 { return globalRand.Int31() } + +// Int returns a non-negative pseudo-random int from the default Source. +func Int() int { return globalRand.Int() } + +// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n) +// from the default Source. +// It panics if n <= 0. +func Int63n(n int64) int64 { return globalRand.Int63n(n) } + +// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n) +// from the default Source. +// It panics if n <= 0. +func Int31n(n int32) int32 { return globalRand.Int31n(n) } + +// Intn returns, as an int, a non-negative pseudo-random number in [0,n) +// from the default Source. +// It panics if n <= 0. +func Intn(n int) int { return globalRand.Intn(n) } + +// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0) +// from the default Source. +func Float64() float64 { return globalRand.Float64() } + +// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0) +// from the default Source. +func Float32() float32 { return globalRand.Float32() } + +// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n) +// from the default Source. +func Perm(n int) []int { return globalRand.Perm(n) } + +// Shuffle pseudo-randomizes the order of elements using the default Source. +// n is the number of elements. Shuffle panics if n < 0. +// swap swaps the elements with indexes i and j. +func Shuffle(n int, swap func(i, j int)) { globalRand.Shuffle(n, swap) } + +// Read generates len(p) random bytes from the default Source and +// writes them into p. It always returns len(p) and a nil error. +// Read, unlike the Rand.Read method, is safe for concurrent use. +func Read(p []byte) (n int, err error) { return globalRand.Read(p) } + +// NormFloat64 returns a normally distributed float64 in the range +// [-math.MaxFloat64, +math.MaxFloat64] with +// standard normal distribution (mean = 0, stddev = 1) +// from the default Source. +// To produce a different normal distribution, callers can +// adjust the output using: +// +// sample = NormFloat64() * desiredStdDev + desiredMean +func NormFloat64() float64 { return globalRand.NormFloat64() } + +// ExpFloat64 returns an exponentially distributed float64 in the range +// (0, +math.MaxFloat64] with an exponential distribution whose rate parameter +// (lambda) is 1 and whose mean is 1/lambda (1) from the default Source. +// To produce a distribution with a different rate parameter, +// callers can adjust the output using: +// +// sample = ExpFloat64() / desiredRateParameter +func ExpFloat64() float64 { return globalRand.ExpFloat64() } + +// LockedSource is an implementation of Source that is concurrency-safe. +// A Rand using a LockedSource is safe for concurrent use. +// +// The zero value of LockedSource is valid, but should be seeded before use. +type LockedSource struct { + lk sync.Mutex + src PCGSource +} + +func (s *LockedSource) Uint64() (n uint64) { + s.lk.Lock() + n = s.src.Uint64() + s.lk.Unlock() + return +} + +func (s *LockedSource) Seed(seed uint64) { + s.lk.Lock() + s.src.Seed(seed) + s.lk.Unlock() +} + +// seedPos implements Seed for a LockedSource without a race condiiton. +func (s *LockedSource) seedPos(seed uint64, readPos *int8) { + s.lk.Lock() + s.src.Seed(seed) + *readPos = 0 + s.lk.Unlock() +} + +// Read implements Read for a LockedSource. +func (s *LockedSource) Read(p []byte, readVal *uint64, readPos *int8) (n int, err error) { + s.lk.Lock() + n, err = read(p, &s.src, readVal, readPos) + s.lk.Unlock() + return +} diff --git a/vendor/golang.org/x/exp/rand/rng.go b/vendor/golang.org/x/exp/rand/rng.go new file mode 100644 index 000000000..9b79108c7 --- /dev/null +++ b/vendor/golang.org/x/exp/rand/rng.go @@ -0,0 +1,91 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rand + +import ( + "encoding/binary" + "io" + "math/bits" +) + +// PCGSource is an implementation of a 64-bit permuted congruential +// generator as defined in +// +// PCG: A Family of Simple Fast Space-Efficient Statistically Good +// Algorithms for Random Number Generation +// Melissa E. O’Neill, Harvey Mudd College +// http://www.pcg-random.org/pdf/toms-oneill-pcg-family-v1.02.pdf +// +// The generator here is the congruential generator PCG XSL RR 128/64 (LCG) +// as found in the software available at http://www.pcg-random.org/. +// It has period 2^128 with 128 bits of state, producing 64-bit values. +// Is state is represented by two uint64 words. +type PCGSource struct { + low uint64 + high uint64 +} + +const ( + maxUint32 = (1 << 32) - 1 + + multiplier = 47026247687942121848144207491837523525 + mulHigh = multiplier >> 64 + mulLow = multiplier & maxUint64 + + increment = 117397592171526113268558934119004209487 + incHigh = increment >> 64 + incLow = increment & maxUint64 + + // TODO: Use these? + initializer = 245720598905631564143578724636268694099 + initHigh = initializer >> 64 + initLow = initializer & maxUint64 +) + +// Seed uses the provided seed value to initialize the generator to a deterministic state. +func (pcg *PCGSource) Seed(seed uint64) { + pcg.low = seed + pcg.high = seed // TODO: What is right? +} + +// Uint64 returns a pseudo-random 64-bit unsigned integer as a uint64. +func (pcg *PCGSource) Uint64() uint64 { + pcg.multiply() + pcg.add() + // XOR high and low 64 bits together and rotate right by high 6 bits of state. + return bits.RotateLeft64(pcg.high^pcg.low, -int(pcg.high>>58)) +} + +func (pcg *PCGSource) add() { + var carry uint64 + pcg.low, carry = bits.Add64(pcg.low, incLow, 0) + pcg.high, _ = bits.Add64(pcg.high, incHigh, carry) +} + +func (pcg *PCGSource) multiply() { + hi, lo := bits.Mul64(pcg.low, mulLow) + hi += pcg.high * mulLow + hi += pcg.low * mulHigh + pcg.low = lo + pcg.high = hi +} + +// MarshalBinary returns the binary representation of the current state of the generator. +func (pcg *PCGSource) MarshalBinary() ([]byte, error) { + var buf [16]byte + binary.BigEndian.PutUint64(buf[:8], pcg.high) + binary.BigEndian.PutUint64(buf[8:], pcg.low) + return buf[:], nil +} + +// UnmarshalBinary sets the state of the generator to the state represented in data. +func (pcg *PCGSource) UnmarshalBinary(data []byte) error { + if len(data) < 16 { + return io.ErrUnexpectedEOF + } + pcg.low = binary.BigEndian.Uint64(data[8:]) + pcg.high = binary.BigEndian.Uint64(data[:8]) + return nil +} diff --git a/vendor/golang.org/x/exp/rand/zipf.go b/vendor/golang.org/x/exp/rand/zipf.go new file mode 100644 index 000000000..f04c814eb --- /dev/null +++ b/vendor/golang.org/x/exp/rand/zipf.go @@ -0,0 +1,77 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// W.Hormann, G.Derflinger: +// "Rejection-Inversion to Generate Variates +// from Monotone Discrete Distributions" +// http://eeyore.wu-wien.ac.at/papers/96-04-04.wh-der.ps.gz + +package rand + +import "math" + +// A Zipf generates Zipf distributed variates. +type Zipf struct { + r *Rand + imax float64 + v float64 + q float64 + s float64 + oneminusQ float64 + oneminusQinv float64 + hxm float64 + hx0minusHxm float64 +} + +func (z *Zipf) h(x float64) float64 { + return math.Exp(z.oneminusQ*math.Log(z.v+x)) * z.oneminusQinv +} + +func (z *Zipf) hinv(x float64) float64 { + return math.Exp(z.oneminusQinv*math.Log(z.oneminusQ*x)) - z.v +} + +// NewZipf returns a Zipf variate generator. +// The generator generates values k ∈ [0, imax] +// such that P(k) is proportional to (v + k) ** (-s). +// Requirements: s > 1 and v >= 1. +func NewZipf(r *Rand, s float64, v float64, imax uint64) *Zipf { + z := new(Zipf) + if s <= 1.0 || v < 1 { + return nil + } + z.r = r + z.imax = float64(imax) + z.v = v + z.q = s + z.oneminusQ = 1.0 - z.q + z.oneminusQinv = 1.0 / z.oneminusQ + z.hxm = z.h(z.imax + 0.5) + z.hx0minusHxm = z.h(0.5) - math.Exp(math.Log(z.v)*(-z.q)) - z.hxm + z.s = 1 - z.hinv(z.h(1.5)-math.Exp(-z.q*math.Log(z.v+1.0))) + return z +} + +// Uint64 returns a value drawn from the Zipf distribution described +// by the Zipf object. +func (z *Zipf) Uint64() uint64 { + if z == nil { + panic("rand: nil Zipf") + } + k := 0.0 + + for { + r := z.r.Float64() // r on [0,1] + ur := z.hxm + r*z.hx0minusHxm + x := z.hinv(ur) + k = math.Floor(x + 0.5) + if k-x <= z.s { + break + } + if ur >= z.h(k+0.5)-math.Exp(-math.Log(k+z.v)*z.q) { + break + } + } + return uint64(k) +} diff --git a/vendor/golang.org/x/exp/slices/cmp.go b/vendor/golang.org/x/exp/slices/cmp.go new file mode 100644 index 000000000..fbf1934a0 --- /dev/null +++ b/vendor/golang.org/x/exp/slices/cmp.go @@ -0,0 +1,44 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package slices + +import "golang.org/x/exp/constraints" + +// min is a version of the predeclared function from the Go 1.21 release. +func min[T constraints.Ordered](a, b T) T { + if a < b || isNaN(a) { + return a + } + return b +} + +// max is a version of the predeclared function from the Go 1.21 release. +func max[T constraints.Ordered](a, b T) T { + if a > b || isNaN(a) { + return a + } + return b +} + +// cmpLess is a copy of cmp.Less from the Go 1.21 release. +func cmpLess[T constraints.Ordered](x, y T) bool { + return (isNaN(x) && !isNaN(y)) || x < y +} + +// cmpCompare is a copy of cmp.Compare from the Go 1.21 release. +func cmpCompare[T constraints.Ordered](x, y T) int { + xNaN := isNaN(x) + yNaN := isNaN(y) + if xNaN && yNaN { + return 0 + } + if xNaN || x < y { + return -1 + } + if yNaN || x > y { + return +1 + } + return 0 +} diff --git a/vendor/golang.org/x/exp/slices/slices.go b/vendor/golang.org/x/exp/slices/slices.go index 8a7cf20db..5e8158bba 100644 --- a/vendor/golang.org/x/exp/slices/slices.go +++ b/vendor/golang.org/x/exp/slices/slices.go @@ -3,23 +3,20 @@ // license that can be found in the LICENSE file. // Package slices defines various functions useful with slices of any type. -// Unless otherwise specified, these functions all apply to the elements -// of a slice at index 0 <= i < len(s). -// -// Note that the less function in IsSortedFunc, SortFunc, SortStableFunc requires a -// strict weak ordering (https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings), -// or the sorting may fail to sort correctly. A common case is when sorting slices of -// floating-point numbers containing NaN values. package slices -import "golang.org/x/exp/constraints" +import ( + "unsafe" + + "golang.org/x/exp/constraints" +) // Equal reports whether two slices are equal: the same length and all // elements equal. If the lengths are different, Equal returns false. // Otherwise, the elements are compared in increasing index order, and the // comparison stops at the first unequal pair. // Floating point NaNs are not considered equal. -func Equal[E comparable](s1, s2 []E) bool { +func Equal[S ~[]E, E comparable](s1, s2 S) bool { if len(s1) != len(s2) { return false } @@ -31,12 +28,12 @@ func Equal[E comparable](s1, s2 []E) bool { return true } -// EqualFunc reports whether two slices are equal using a comparison +// EqualFunc reports whether two slices are equal using an equality // function on each pair of elements. If the lengths are different, // EqualFunc returns false. Otherwise, the elements are compared in // increasing index order, and the comparison stops at the first index // for which eq returns false. -func EqualFunc[E1, E2 any](s1 []E1, s2 []E2, eq func(E1, E2) bool) bool { +func EqualFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, eq func(E1, E2) bool) bool { if len(s1) != len(s2) { return false } @@ -49,45 +46,37 @@ func EqualFunc[E1, E2 any](s1 []E1, s2 []E2, eq func(E1, E2) bool) bool { return true } -// Compare compares the elements of s1 and s2. -// The elements are compared sequentially, starting at index 0, +// Compare compares the elements of s1 and s2, using [cmp.Compare] on each pair +// of elements. The elements are compared sequentially, starting at index 0, // until one element is not equal to the other. // The result of comparing the first non-matching elements is returned. // If both slices are equal until one of them ends, the shorter slice is // considered less than the longer one. // The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2. -// Comparisons involving floating point NaNs are ignored. -func Compare[E constraints.Ordered](s1, s2 []E) int { - s2len := len(s2) +func Compare[S ~[]E, E constraints.Ordered](s1, s2 S) int { for i, v1 := range s1 { - if i >= s2len { + if i >= len(s2) { return +1 } v2 := s2[i] - switch { - case v1 < v2: - return -1 - case v1 > v2: - return +1 + if c := cmpCompare(v1, v2); c != 0 { + return c } } - if len(s1) < s2len { + if len(s1) < len(s2) { return -1 } return 0 } -// CompareFunc is like Compare but uses a comparison function -// on each pair of elements. The elements are compared in increasing -// index order, and the comparisons stop after the first time cmp -// returns non-zero. +// CompareFunc is like [Compare] but uses a custom comparison function on each +// pair of elements. // The result is the first non-zero result of cmp; if cmp always // returns 0 the result is 0 if len(s1) == len(s2), -1 if len(s1) < len(s2), // and +1 if len(s1) > len(s2). -func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int { - s2len := len(s2) +func CompareFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, cmp func(E1, E2) int) int { for i, v1 := range s1 { - if i >= s2len { + if i >= len(s2) { return +1 } v2 := s2[i] @@ -95,7 +84,7 @@ func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int { return c } } - if len(s1) < s2len { + if len(s1) < len(s2) { return -1 } return 0 @@ -103,7 +92,7 @@ func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int { // Index returns the index of the first occurrence of v in s, // or -1 if not present. -func Index[E comparable](s []E, v E) int { +func Index[S ~[]E, E comparable](s S, v E) int { for i := range s { if v == s[i] { return i @@ -114,7 +103,7 @@ func Index[E comparable](s []E, v E) int { // IndexFunc returns the first index i satisfying f(s[i]), // or -1 if none do. -func IndexFunc[E any](s []E, f func(E) bool) int { +func IndexFunc[S ~[]E, E any](s S, f func(E) bool) int { for i := range s { if f(s[i]) { return i @@ -124,39 +113,104 @@ func IndexFunc[E any](s []E, f func(E) bool) int { } // Contains reports whether v is present in s. -func Contains[E comparable](s []E, v E) bool { +func Contains[S ~[]E, E comparable](s S, v E) bool { return Index(s, v) >= 0 } // ContainsFunc reports whether at least one // element e of s satisfies f(e). -func ContainsFunc[E any](s []E, f func(E) bool) bool { +func ContainsFunc[S ~[]E, E any](s S, f func(E) bool) bool { return IndexFunc(s, f) >= 0 } // Insert inserts the values v... into s at index i, // returning the modified slice. -// In the returned slice r, r[i] == v[0]. +// The elements at s[i:] are shifted up to make room. +// In the returned slice r, r[i] == v[0], +// and r[i+len(v)] == value originally at r[i]. // Insert panics if i is out of range. // This function is O(len(s) + len(v)). func Insert[S ~[]E, E any](s S, i int, v ...E) S { - tot := len(s) + len(v) - if tot <= cap(s) { - s2 := s[:tot] - copy(s2[i+len(v):], s[i:]) + m := len(v) + if m == 0 { + return s + } + n := len(s) + if i == n { + return append(s, v...) + } + if n+m > cap(s) { + // Use append rather than make so that we bump the size of + // the slice up to the next storage class. + // This is what Grow does but we don't call Grow because + // that might copy the values twice. + s2 := append(s[:i], make(S, n+m-i)...) copy(s2[i:], v) + copy(s2[i+m:], s[i:]) return s2 } - s2 := make(S, tot) - copy(s2, s[:i]) - copy(s2[i:], v) - copy(s2[i+len(v):], s[i:]) - return s2 + s = s[:n+m] + + // before: + // s: aaaaaaaabbbbccccccccdddd + // ^ ^ ^ ^ + // i i+m n n+m + // after: + // s: aaaaaaaavvvvbbbbcccccccc + // ^ ^ ^ ^ + // i i+m n n+m + // + // a are the values that don't move in s. + // v are the values copied in from v. + // b and c are the values from s that are shifted up in index. + // d are the values that get overwritten, never to be seen again. + + if !overlaps(v, s[i+m:]) { + // Easy case - v does not overlap either the c or d regions. + // (It might be in some of a or b, or elsewhere entirely.) + // The data we copy up doesn't write to v at all, so just do it. + + copy(s[i+m:], s[i:]) + + // Now we have + // s: aaaaaaaabbbbbbbbcccccccc + // ^ ^ ^ ^ + // i i+m n n+m + // Note the b values are duplicated. + + copy(s[i:], v) + + // Now we have + // s: aaaaaaaavvvvbbbbcccccccc + // ^ ^ ^ ^ + // i i+m n n+m + // That's the result we want. + return s + } + + // The hard case - v overlaps c or d. We can't just shift up + // the data because we'd move or clobber the values we're trying + // to insert. + // So instead, write v on top of d, then rotate. + copy(s[n:], v) + + // Now we have + // s: aaaaaaaabbbbccccccccvvvv + // ^ ^ ^ ^ + // i i+m n n+m + + rotateRight(s[i:], m) + + // Now we have + // s: aaaaaaaavvvvbbbbcccccccc + // ^ ^ ^ ^ + // i i+m n n+m + // That's the result we want. + return s } // Delete removes the elements s[i:j] from s, returning the modified slice. // Delete panics if s[i:j] is not a valid slice of s. -// Delete modifies the contents of the slice s; it does not create a new slice. // Delete is O(len(s)-j), so if many items must be deleted, it is better to // make a single call deleting them all together than to delete one at a time. // Delete might not modify the elements s[len(s)-(j-i):len(s)]. If those @@ -175,39 +229,106 @@ func Delete[S ~[]E, E any](s S, i, j int) S { // zeroing those elements so that objects they reference can be garbage // collected. func DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S { + i := IndexFunc(s, del) + if i == -1 { + return s + } // Don't start copying elements until we find one to delete. - for i, v := range s { - if del(v) { - j := i - for i++; i < len(s); i++ { - v = s[i] - if !del(v) { - s[j] = v - j++ - } - } - return s[:j] + for j := i + 1; j < len(s); j++ { + if v := s[j]; !del(v) { + s[i] = v + i++ } } - return s + return s[:i] } // Replace replaces the elements s[i:j] by the given v, and returns the // modified slice. Replace panics if s[i:j] is not a valid slice of s. func Replace[S ~[]E, E any](s S, i, j int, v ...E) S { _ = s[i:j] // verify that i:j is a valid subslice + + if i == j { + return Insert(s, i, v...) + } + if j == len(s) { + return append(s[:i], v...) + } + tot := len(s[:i]) + len(v) + len(s[j:]) - if tot <= cap(s) { - s2 := s[:tot] - copy(s2[i+len(v):], s[j:]) + if tot > cap(s) { + // Too big to fit, allocate and copy over. + s2 := append(s[:i], make(S, tot-i)...) // See Insert copy(s2[i:], v) + copy(s2[i+len(v):], s[j:]) return s2 } - s2 := make(S, tot) - copy(s2, s[:i]) - copy(s2[i:], v) - copy(s2[i+len(v):], s[j:]) - return s2 + + r := s[:tot] + + if i+len(v) <= j { + // Easy, as v fits in the deleted portion. + copy(r[i:], v) + if i+len(v) != j { + copy(r[i+len(v):], s[j:]) + } + return r + } + + // We are expanding (v is bigger than j-i). + // The situation is something like this: + // (example has i=4,j=8,len(s)=16,len(v)=6) + // s: aaaaxxxxbbbbbbbbyy + // ^ ^ ^ ^ + // i j len(s) tot + // a: prefix of s + // x: deleted range + // b: more of s + // y: area to expand into + + if !overlaps(r[i+len(v):], v) { + // Easy, as v is not clobbered by the first copy. + copy(r[i+len(v):], s[j:]) + copy(r[i:], v) + return r + } + + // This is a situation where we don't have a single place to which + // we can copy v. Parts of it need to go to two different places. + // We want to copy the prefix of v into y and the suffix into x, then + // rotate |y| spots to the right. + // + // v[2:] v[:2] + // | | + // s: aaaavvvvbbbbbbbbvv + // ^ ^ ^ ^ + // i j len(s) tot + // + // If either of those two destinations don't alias v, then we're good. + y := len(v) - (j - i) // length of y portion + + if !overlaps(r[i:j], v) { + copy(r[i:j], v[y:]) + copy(r[len(s):], v[:y]) + rotateRight(r[i:], y) + return r + } + if !overlaps(r[len(s):], v) { + copy(r[len(s):], v[:y]) + copy(r[i:j], v[y:]) + rotateRight(r[i:], y) + return r + } + + // Now we know that v overlaps both x and y. + // That means that the entirety of b is *inside* v. + // So we don't need to preserve b at all; instead we + // can copy v first, then copy the b part of v out of + // v to the right destination. + k := startIdx(v, s[j:]) + copy(r[i:], v) + copy(r[i+len(v):], r[i+k:]) + return r } // Clone returns a copy of the slice. @@ -222,7 +343,8 @@ func Clone[S ~[]E, E any](s S) S { // Compact replaces consecutive runs of equal elements with a single copy. // This is like the uniq command found on Unix. -// Compact modifies the contents of the slice s; it does not create a new slice. +// Compact modifies the contents of the slice s and returns the modified slice, +// which may have a smaller length. // When Compact discards m elements in total, it might not modify the elements // s[len(s)-m:len(s)]. If those elements contain pointers you might consider // zeroing those elements so that objects they reference can be garbage collected. @@ -242,7 +364,8 @@ func Compact[S ~[]E, E comparable](s S) S { return s[:i] } -// CompactFunc is like Compact but uses a comparison function. +// CompactFunc is like [Compact] but uses an equality function to compare elements. +// For runs of elements that compare equal, CompactFunc keeps the first one. func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S { if len(s) < 2 { return s @@ -280,3 +403,97 @@ func Grow[S ~[]E, E any](s S, n int) S { func Clip[S ~[]E, E any](s S) S { return s[:len(s):len(s)] } + +// Rotation algorithm explanation: +// +// rotate left by 2 +// start with +// 0123456789 +// split up like this +// 01 234567 89 +// swap first 2 and last 2 +// 89 234567 01 +// join first parts +// 89234567 01 +// recursively rotate first left part by 2 +// 23456789 01 +// join at the end +// 2345678901 +// +// rotate left by 8 +// start with +// 0123456789 +// split up like this +// 01 234567 89 +// swap first 2 and last 2 +// 89 234567 01 +// join last parts +// 89 23456701 +// recursively rotate second part left by 6 +// 89 01234567 +// join at the end +// 8901234567 + +// TODO: There are other rotate algorithms. +// This algorithm has the desirable property that it moves each element exactly twice. +// The triple-reverse algorithm is simpler and more cache friendly, but takes more writes. +// The follow-cycles algorithm can be 1-write but it is not very cache friendly. + +// rotateLeft rotates b left by n spaces. +// s_final[i] = s_orig[i+r], wrapping around. +func rotateLeft[E any](s []E, r int) { + for r != 0 && r != len(s) { + if r*2 <= len(s) { + swap(s[:r], s[len(s)-r:]) + s = s[:len(s)-r] + } else { + swap(s[:len(s)-r], s[r:]) + s, r = s[len(s)-r:], r*2-len(s) + } + } +} +func rotateRight[E any](s []E, r int) { + rotateLeft(s, len(s)-r) +} + +// swap swaps the contents of x and y. x and y must be equal length and disjoint. +func swap[E any](x, y []E) { + for i := 0; i < len(x); i++ { + x[i], y[i] = y[i], x[i] + } +} + +// overlaps reports whether the memory ranges a[0:len(a)] and b[0:len(b)] overlap. +func overlaps[E any](a, b []E) bool { + if len(a) == 0 || len(b) == 0 { + return false + } + elemSize := unsafe.Sizeof(a[0]) + if elemSize == 0 { + return false + } + // TODO: use a runtime/unsafe facility once one becomes available. See issue 12445. + // Also see crypto/internal/alias/alias.go:AnyOverlap + return uintptr(unsafe.Pointer(&a[0])) <= uintptr(unsafe.Pointer(&b[len(b)-1]))+(elemSize-1) && + uintptr(unsafe.Pointer(&b[0])) <= uintptr(unsafe.Pointer(&a[len(a)-1]))+(elemSize-1) +} + +// startIdx returns the index in haystack where the needle starts. +// prerequisite: the needle must be aliased entirely inside the haystack. +func startIdx[E any](haystack, needle []E) int { + p := &needle[0] + for i := range haystack { + if p == &haystack[i] { + return i + } + } + // TODO: what if the overlap is by a non-integral number of Es? + panic("needle not found") +} + +// Reverse reverses the elements of the slice in place. +func Reverse[S ~[]E, E any](s S) { + for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } +} diff --git a/vendor/golang.org/x/exp/slices/sort.go b/vendor/golang.org/x/exp/slices/sort.go index 231b6448a..b67897f76 100644 --- a/vendor/golang.org/x/exp/slices/sort.go +++ b/vendor/golang.org/x/exp/slices/sort.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:generate go run $GOROOT/src/sort/gen_sort_variants.go -exp + package slices import ( @@ -11,57 +13,116 @@ import ( ) // Sort sorts a slice of any ordered type in ascending order. -// Sort may fail to sort correctly when sorting slices of floating-point -// numbers containing Not-a-number (NaN) values. -// Use slices.SortFunc(x, func(a, b float64) bool {return a < b || (math.IsNaN(a) && !math.IsNaN(b))}) -// instead if the input may contain NaNs. -func Sort[E constraints.Ordered](x []E) { +// When sorting floating-point numbers, NaNs are ordered before other values. +func Sort[S ~[]E, E constraints.Ordered](x S) { n := len(x) pdqsortOrdered(x, 0, n, bits.Len(uint(n))) } -// SortFunc sorts the slice x in ascending order as determined by the less function. -// This sort is not guaranteed to be stable. +// SortFunc sorts the slice x in ascending order as determined by the cmp +// function. This sort is not guaranteed to be stable. +// cmp(a, b) should return a negative number when a < b, a positive number when +// a > b and zero when a == b. // -// SortFunc requires that less is a strict weak ordering. +// SortFunc requires that cmp is a strict weak ordering. // See https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings. -func SortFunc[E any](x []E, less func(a, b E) bool) { +func SortFunc[S ~[]E, E any](x S, cmp func(a, b E) int) { n := len(x) - pdqsortLessFunc(x, 0, n, bits.Len(uint(n)), less) + pdqsortCmpFunc(x, 0, n, bits.Len(uint(n)), cmp) } // SortStableFunc sorts the slice x while keeping the original order of equal -// elements, using less to compare elements. -func SortStableFunc[E any](x []E, less func(a, b E) bool) { - stableLessFunc(x, len(x), less) +// elements, using cmp to compare elements in the same way as [SortFunc]. +func SortStableFunc[S ~[]E, E any](x S, cmp func(a, b E) int) { + stableCmpFunc(x, len(x), cmp) } // IsSorted reports whether x is sorted in ascending order. -func IsSorted[E constraints.Ordered](x []E) bool { +func IsSorted[S ~[]E, E constraints.Ordered](x S) bool { for i := len(x) - 1; i > 0; i-- { - if x[i] < x[i-1] { + if cmpLess(x[i], x[i-1]) { return false } } return true } -// IsSortedFunc reports whether x is sorted in ascending order, with less as the -// comparison function. -func IsSortedFunc[E any](x []E, less func(a, b E) bool) bool { +// IsSortedFunc reports whether x is sorted in ascending order, with cmp as the +// comparison function as defined by [SortFunc]. +func IsSortedFunc[S ~[]E, E any](x S, cmp func(a, b E) int) bool { for i := len(x) - 1; i > 0; i-- { - if less(x[i], x[i-1]) { + if cmp(x[i], x[i-1]) < 0 { return false } } return true } +// Min returns the minimal value in x. It panics if x is empty. +// For floating-point numbers, Min propagates NaNs (any NaN value in x +// forces the output to be NaN). +func Min[S ~[]E, E constraints.Ordered](x S) E { + if len(x) < 1 { + panic("slices.Min: empty list") + } + m := x[0] + for i := 1; i < len(x); i++ { + m = min(m, x[i]) + } + return m +} + +// MinFunc returns the minimal value in x, using cmp to compare elements. +// It panics if x is empty. If there is more than one minimal element +// according to the cmp function, MinFunc returns the first one. +func MinFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E { + if len(x) < 1 { + panic("slices.MinFunc: empty list") + } + m := x[0] + for i := 1; i < len(x); i++ { + if cmp(x[i], m) < 0 { + m = x[i] + } + } + return m +} + +// Max returns the maximal value in x. It panics if x is empty. +// For floating-point E, Max propagates NaNs (any NaN value in x +// forces the output to be NaN). +func Max[S ~[]E, E constraints.Ordered](x S) E { + if len(x) < 1 { + panic("slices.Max: empty list") + } + m := x[0] + for i := 1; i < len(x); i++ { + m = max(m, x[i]) + } + return m +} + +// MaxFunc returns the maximal value in x, using cmp to compare elements. +// It panics if x is empty. If there is more than one maximal element +// according to the cmp function, MaxFunc returns the first one. +func MaxFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E { + if len(x) < 1 { + panic("slices.MaxFunc: empty list") + } + m := x[0] + for i := 1; i < len(x); i++ { + if cmp(x[i], m) > 0 { + m = x[i] + } + } + return m +} + // BinarySearch searches for target in a sorted slice and returns the position // where target is found, or the position where target would appear in the // sort order; it also returns a bool saying whether the target is really found // in the slice. The slice must be sorted in increasing order. -func BinarySearch[E constraints.Ordered](x []E, target E) (int, bool) { +func BinarySearch[S ~[]E, E constraints.Ordered](x S, target E) (int, bool) { // Inlining is faster than calling BinarySearchFunc with a lambda. n := len(x) // Define x[-1] < target and x[n] >= target. @@ -70,24 +131,24 @@ func BinarySearch[E constraints.Ordered](x []E, target E) (int, bool) { for i < j { h := int(uint(i+j) >> 1) // avoid overflow when computing h // i ≤ h < j - if x[h] < target { + if cmpLess(x[h], target) { i = h + 1 // preserves x[i-1] < target } else { j = h // preserves x[j] >= target } } // i == j, x[i-1] < target, and x[j] (= x[i]) >= target => answer is i. - return i, i < n && x[i] == target + return i, i < n && (x[i] == target || (isNaN(x[i]) && isNaN(target))) } -// BinarySearchFunc works like BinarySearch, but uses a custom comparison +// BinarySearchFunc works like [BinarySearch], but uses a custom comparison // function. The slice must be sorted in increasing order, where "increasing" // is defined by cmp. cmp should return 0 if the slice element matches // the target, a negative number if the slice element precedes the target, // or a positive number if the slice element follows the target. // cmp must implement the same ordering as the slice, such that if // cmp(a, t) < 0 and cmp(b, t) >= 0, then a must precede b in the slice. -func BinarySearchFunc[E, T any](x []E, target T, cmp func(E, T) int) (int, bool) { +func BinarySearchFunc[S ~[]E, E, T any](x S, target T, cmp func(E, T) int) (int, bool) { n := len(x) // Define cmp(x[-1], target) < 0 and cmp(x[n], target) >= 0 . // Invariant: cmp(x[i - 1], target) < 0, cmp(x[j], target) >= 0. @@ -126,3 +187,9 @@ func (r *xorshift) Next() uint64 { func nextPowerOfTwo(length int) uint { return 1 << bits.Len(uint(length)) } + +// isNaN reports whether x is a NaN without requiring the math package. +// This will always return false if T is not floating-point. +func isNaN[T constraints.Ordered](x T) bool { + return x != x +} diff --git a/vendor/golang.org/x/exp/slices/zsortfunc.go b/vendor/golang.org/x/exp/slices/zsortanyfunc.go similarity index 64% rename from vendor/golang.org/x/exp/slices/zsortfunc.go rename to vendor/golang.org/x/exp/slices/zsortanyfunc.go index 2a632476c..06f2c7a24 100644 --- a/vendor/golang.org/x/exp/slices/zsortfunc.go +++ b/vendor/golang.org/x/exp/slices/zsortanyfunc.go @@ -6,28 +6,28 @@ package slices -// insertionSortLessFunc sorts data[a:b] using insertion sort. -func insertionSortLessFunc[E any](data []E, a, b int, less func(a, b E) bool) { +// insertionSortCmpFunc sorts data[a:b] using insertion sort. +func insertionSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) { for i := a + 1; i < b; i++ { - for j := i; j > a && less(data[j], data[j-1]); j-- { + for j := i; j > a && (cmp(data[j], data[j-1]) < 0); j-- { data[j], data[j-1] = data[j-1], data[j] } } } -// siftDownLessFunc implements the heap property on data[lo:hi]. +// siftDownCmpFunc implements the heap property on data[lo:hi]. // first is an offset into the array where the root of the heap lies. -func siftDownLessFunc[E any](data []E, lo, hi, first int, less func(a, b E) bool) { +func siftDownCmpFunc[E any](data []E, lo, hi, first int, cmp func(a, b E) int) { root := lo for { child := 2*root + 1 if child >= hi { break } - if child+1 < hi && less(data[first+child], data[first+child+1]) { + if child+1 < hi && (cmp(data[first+child], data[first+child+1]) < 0) { child++ } - if !less(data[first+root], data[first+child]) { + if !(cmp(data[first+root], data[first+child]) < 0) { return } data[first+root], data[first+child] = data[first+child], data[first+root] @@ -35,30 +35,30 @@ func siftDownLessFunc[E any](data []E, lo, hi, first int, less func(a, b E) bool } } -func heapSortLessFunc[E any](data []E, a, b int, less func(a, b E) bool) { +func heapSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) { first := a lo := 0 hi := b - a // Build heap with greatest element at top. for i := (hi - 1) / 2; i >= 0; i-- { - siftDownLessFunc(data, i, hi, first, less) + siftDownCmpFunc(data, i, hi, first, cmp) } // Pop elements, largest first, into end of data. for i := hi - 1; i >= 0; i-- { data[first], data[first+i] = data[first+i], data[first] - siftDownLessFunc(data, lo, i, first, less) + siftDownCmpFunc(data, lo, i, first, cmp) } } -// pdqsortLessFunc sorts data[a:b]. +// pdqsortCmpFunc sorts data[a:b]. // The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort. // pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf // C++ implementation: https://github.com/orlp/pdqsort // Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/ // limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort. -func pdqsortLessFunc[E any](data []E, a, b, limit int, less func(a, b E) bool) { +func pdqsortCmpFunc[E any](data []E, a, b, limit int, cmp func(a, b E) int) { const maxInsertion = 12 var ( @@ -70,25 +70,25 @@ func pdqsortLessFunc[E any](data []E, a, b, limit int, less func(a, b E) bool) { length := b - a if length <= maxInsertion { - insertionSortLessFunc(data, a, b, less) + insertionSortCmpFunc(data, a, b, cmp) return } // Fall back to heapsort if too many bad choices were made. if limit == 0 { - heapSortLessFunc(data, a, b, less) + heapSortCmpFunc(data, a, b, cmp) return } // If the last partitioning was imbalanced, we need to breaking patterns. if !wasBalanced { - breakPatternsLessFunc(data, a, b, less) + breakPatternsCmpFunc(data, a, b, cmp) limit-- } - pivot, hint := choosePivotLessFunc(data, a, b, less) + pivot, hint := choosePivotCmpFunc(data, a, b, cmp) if hint == decreasingHint { - reverseRangeLessFunc(data, a, b, less) + reverseRangeCmpFunc(data, a, b, cmp) // The chosen pivot was pivot-a elements after the start of the array. // After reversing it is pivot-a elements before the end of the array. // The idea came from Rust's implementation. @@ -98,48 +98,48 @@ func pdqsortLessFunc[E any](data []E, a, b, limit int, less func(a, b E) bool) { // The slice is likely already sorted. if wasBalanced && wasPartitioned && hint == increasingHint { - if partialInsertionSortLessFunc(data, a, b, less) { + if partialInsertionSortCmpFunc(data, a, b, cmp) { return } } // Probably the slice contains many duplicate elements, partition the slice into // elements equal to and elements greater than the pivot. - if a > 0 && !less(data[a-1], data[pivot]) { - mid := partitionEqualLessFunc(data, a, b, pivot, less) + if a > 0 && !(cmp(data[a-1], data[pivot]) < 0) { + mid := partitionEqualCmpFunc(data, a, b, pivot, cmp) a = mid continue } - mid, alreadyPartitioned := partitionLessFunc(data, a, b, pivot, less) + mid, alreadyPartitioned := partitionCmpFunc(data, a, b, pivot, cmp) wasPartitioned = alreadyPartitioned leftLen, rightLen := mid-a, b-mid balanceThreshold := length / 8 if leftLen < rightLen { wasBalanced = leftLen >= balanceThreshold - pdqsortLessFunc(data, a, mid, limit, less) + pdqsortCmpFunc(data, a, mid, limit, cmp) a = mid + 1 } else { wasBalanced = rightLen >= balanceThreshold - pdqsortLessFunc(data, mid+1, b, limit, less) + pdqsortCmpFunc(data, mid+1, b, limit, cmp) b = mid } } } -// partitionLessFunc does one quicksort partition. +// partitionCmpFunc does one quicksort partition. // Let p = data[pivot] // Moves elements in data[a:b] around, so that data[i]

=p for inewpivot. // On return, data[newpivot] = p -func partitionLessFunc[E any](data []E, a, b, pivot int, less func(a, b E) bool) (newpivot int, alreadyPartitioned bool) { +func partitionCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int, alreadyPartitioned bool) { data[a], data[pivot] = data[pivot], data[a] i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned - for i <= j && less(data[i], data[a]) { + for i <= j && (cmp(data[i], data[a]) < 0) { i++ } - for i <= j && !less(data[j], data[a]) { + for i <= j && !(cmp(data[j], data[a]) < 0) { j-- } if i > j { @@ -151,10 +151,10 @@ func partitionLessFunc[E any](data []E, a, b, pivot int, less func(a, b E) bool) j-- for { - for i <= j && less(data[i], data[a]) { + for i <= j && (cmp(data[i], data[a]) < 0) { i++ } - for i <= j && !less(data[j], data[a]) { + for i <= j && !(cmp(data[j], data[a]) < 0) { j-- } if i > j { @@ -168,17 +168,17 @@ func partitionLessFunc[E any](data []E, a, b, pivot int, less func(a, b E) bool) return j, false } -// partitionEqualLessFunc partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot]. +// partitionEqualCmpFunc partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot]. // It assumed that data[a:b] does not contain elements smaller than the data[pivot]. -func partitionEqualLessFunc[E any](data []E, a, b, pivot int, less func(a, b E) bool) (newpivot int) { +func partitionEqualCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int) { data[a], data[pivot] = data[pivot], data[a] i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned for { - for i <= j && !less(data[a], data[i]) { + for i <= j && !(cmp(data[a], data[i]) < 0) { i++ } - for i <= j && less(data[a], data[j]) { + for i <= j && (cmp(data[a], data[j]) < 0) { j-- } if i > j { @@ -191,15 +191,15 @@ func partitionEqualLessFunc[E any](data []E, a, b, pivot int, less func(a, b E) return i } -// partialInsertionSortLessFunc partially sorts a slice, returns true if the slice is sorted at the end. -func partialInsertionSortLessFunc[E any](data []E, a, b int, less func(a, b E) bool) bool { +// partialInsertionSortCmpFunc partially sorts a slice, returns true if the slice is sorted at the end. +func partialInsertionSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) bool { const ( maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted shortestShifting = 50 // don't shift any elements on short arrays ) i := a + 1 for j := 0; j < maxSteps; j++ { - for i < b && !less(data[i], data[i-1]) { + for i < b && !(cmp(data[i], data[i-1]) < 0) { i++ } @@ -216,7 +216,7 @@ func partialInsertionSortLessFunc[E any](data []E, a, b int, less func(a, b E) b // Shift the smaller one to the left. if i-a >= 2 { for j := i - 1; j >= 1; j-- { - if !less(data[j], data[j-1]) { + if !(cmp(data[j], data[j-1]) < 0) { break } data[j], data[j-1] = data[j-1], data[j] @@ -225,7 +225,7 @@ func partialInsertionSortLessFunc[E any](data []E, a, b int, less func(a, b E) b // Shift the greater one to the right. if b-i >= 2 { for j := i + 1; j < b; j++ { - if !less(data[j], data[j-1]) { + if !(cmp(data[j], data[j-1]) < 0) { break } data[j], data[j-1] = data[j-1], data[j] @@ -235,9 +235,9 @@ func partialInsertionSortLessFunc[E any](data []E, a, b int, less func(a, b E) b return false } -// breakPatternsLessFunc scatters some elements around in an attempt to break some patterns +// breakPatternsCmpFunc scatters some elements around in an attempt to break some patterns // that might cause imbalanced partitions in quicksort. -func breakPatternsLessFunc[E any](data []E, a, b int, less func(a, b E) bool) { +func breakPatternsCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) { length := b - a if length >= 8 { random := xorshift(length) @@ -253,12 +253,12 @@ func breakPatternsLessFunc[E any](data []E, a, b int, less func(a, b E) bool) { } } -// choosePivotLessFunc chooses a pivot in data[a:b]. +// choosePivotCmpFunc chooses a pivot in data[a:b]. // // [0,8): chooses a static pivot. // [8,shortestNinther): uses the simple median-of-three method. // [shortestNinther,∞): uses the Tukey ninther method. -func choosePivotLessFunc[E any](data []E, a, b int, less func(a, b E) bool) (pivot int, hint sortedHint) { +func choosePivotCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) (pivot int, hint sortedHint) { const ( shortestNinther = 50 maxSwaps = 4 * 3 @@ -276,12 +276,12 @@ func choosePivotLessFunc[E any](data []E, a, b int, less func(a, b E) bool) (piv if l >= 8 { if l >= shortestNinther { // Tukey ninther method, the idea came from Rust's implementation. - i = medianAdjacentLessFunc(data, i, &swaps, less) - j = medianAdjacentLessFunc(data, j, &swaps, less) - k = medianAdjacentLessFunc(data, k, &swaps, less) + i = medianAdjacentCmpFunc(data, i, &swaps, cmp) + j = medianAdjacentCmpFunc(data, j, &swaps, cmp) + k = medianAdjacentCmpFunc(data, k, &swaps, cmp) } // Find the median among i, j, k and stores it into j. - j = medianLessFunc(data, i, j, k, &swaps, less) + j = medianCmpFunc(data, i, j, k, &swaps, cmp) } switch swaps { @@ -294,29 +294,29 @@ func choosePivotLessFunc[E any](data []E, a, b int, less func(a, b E) bool) (piv } } -// order2LessFunc returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a. -func order2LessFunc[E any](data []E, a, b int, swaps *int, less func(a, b E) bool) (int, int) { - if less(data[b], data[a]) { +// order2CmpFunc returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a. +func order2CmpFunc[E any](data []E, a, b int, swaps *int, cmp func(a, b E) int) (int, int) { + if cmp(data[b], data[a]) < 0 { *swaps++ return b, a } return a, b } -// medianLessFunc returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c. -func medianLessFunc[E any](data []E, a, b, c int, swaps *int, less func(a, b E) bool) int { - a, b = order2LessFunc(data, a, b, swaps, less) - b, c = order2LessFunc(data, b, c, swaps, less) - a, b = order2LessFunc(data, a, b, swaps, less) +// medianCmpFunc returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c. +func medianCmpFunc[E any](data []E, a, b, c int, swaps *int, cmp func(a, b E) int) int { + a, b = order2CmpFunc(data, a, b, swaps, cmp) + b, c = order2CmpFunc(data, b, c, swaps, cmp) + a, b = order2CmpFunc(data, a, b, swaps, cmp) return b } -// medianAdjacentLessFunc finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a. -func medianAdjacentLessFunc[E any](data []E, a int, swaps *int, less func(a, b E) bool) int { - return medianLessFunc(data, a-1, a, a+1, swaps, less) +// medianAdjacentCmpFunc finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a. +func medianAdjacentCmpFunc[E any](data []E, a int, swaps *int, cmp func(a, b E) int) int { + return medianCmpFunc(data, a-1, a, a+1, swaps, cmp) } -func reverseRangeLessFunc[E any](data []E, a, b int, less func(a, b E) bool) { +func reverseRangeCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) { i := a j := b - 1 for i < j { @@ -326,37 +326,37 @@ func reverseRangeLessFunc[E any](data []E, a, b int, less func(a, b E) bool) { } } -func swapRangeLessFunc[E any](data []E, a, b, n int, less func(a, b E) bool) { +func swapRangeCmpFunc[E any](data []E, a, b, n int, cmp func(a, b E) int) { for i := 0; i < n; i++ { data[a+i], data[b+i] = data[b+i], data[a+i] } } -func stableLessFunc[E any](data []E, n int, less func(a, b E) bool) { +func stableCmpFunc[E any](data []E, n int, cmp func(a, b E) int) { blockSize := 20 // must be > 0 a, b := 0, blockSize for b <= n { - insertionSortLessFunc(data, a, b, less) + insertionSortCmpFunc(data, a, b, cmp) a = b b += blockSize } - insertionSortLessFunc(data, a, n, less) + insertionSortCmpFunc(data, a, n, cmp) for blockSize < n { a, b = 0, 2*blockSize for b <= n { - symMergeLessFunc(data, a, a+blockSize, b, less) + symMergeCmpFunc(data, a, a+blockSize, b, cmp) a = b b += 2 * blockSize } if m := a + blockSize; m < n { - symMergeLessFunc(data, a, m, n, less) + symMergeCmpFunc(data, a, m, n, cmp) } blockSize *= 2 } } -// symMergeLessFunc merges the two sorted subsequences data[a:m] and data[m:b] using +// symMergeCmpFunc merges the two sorted subsequences data[a:m] and data[m:b] using // the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum // Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz // Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in @@ -375,7 +375,7 @@ func stableLessFunc[E any](data []E, n int, less func(a, b E) bool) { // symMerge assumes non-degenerate arguments: a < m && m < b. // Having the caller check this condition eliminates many leaf recursion calls, // which improves performance. -func symMergeLessFunc[E any](data []E, a, m, b int, less func(a, b E) bool) { +func symMergeCmpFunc[E any](data []E, a, m, b int, cmp func(a, b E) int) { // Avoid unnecessary recursions of symMerge // by direct insertion of data[a] into data[m:b] // if data[a:m] only contains one element. @@ -387,7 +387,7 @@ func symMergeLessFunc[E any](data []E, a, m, b int, less func(a, b E) bool) { j := b for i < j { h := int(uint(i+j) >> 1) - if less(data[h], data[a]) { + if cmp(data[h], data[a]) < 0 { i = h + 1 } else { j = h @@ -411,7 +411,7 @@ func symMergeLessFunc[E any](data []E, a, m, b int, less func(a, b E) bool) { j := m for i < j { h := int(uint(i+j) >> 1) - if !less(data[m], data[h]) { + if !(cmp(data[m], data[h]) < 0) { i = h + 1 } else { j = h @@ -438,7 +438,7 @@ func symMergeLessFunc[E any](data []E, a, m, b int, less func(a, b E) bool) { for start < r { c := int(uint(start+r) >> 1) - if !less(data[p-c], data[c]) { + if !(cmp(data[p-c], data[c]) < 0) { start = c + 1 } else { r = c @@ -447,33 +447,33 @@ func symMergeLessFunc[E any](data []E, a, m, b int, less func(a, b E) bool) { end := n - start if start < m && m < end { - rotateLessFunc(data, start, m, end, less) + rotateCmpFunc(data, start, m, end, cmp) } if a < start && start < mid { - symMergeLessFunc(data, a, start, mid, less) + symMergeCmpFunc(data, a, start, mid, cmp) } if mid < end && end < b { - symMergeLessFunc(data, mid, end, b, less) + symMergeCmpFunc(data, mid, end, b, cmp) } } -// rotateLessFunc rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data: +// rotateCmpFunc rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data: // Data of the form 'x u v y' is changed to 'x v u y'. // rotate performs at most b-a many calls to data.Swap, // and it assumes non-degenerate arguments: a < m && m < b. -func rotateLessFunc[E any](data []E, a, m, b int, less func(a, b E) bool) { +func rotateCmpFunc[E any](data []E, a, m, b int, cmp func(a, b E) int) { i := m - a j := b - m for i != j { if i > j { - swapRangeLessFunc(data, m-i, m, j, less) + swapRangeCmpFunc(data, m-i, m, j, cmp) i -= j } else { - swapRangeLessFunc(data, m-i, m+j-i, i, less) + swapRangeCmpFunc(data, m-i, m+j-i, i, cmp) j -= i } } // i == j - swapRangeLessFunc(data, m-i, m, i, less) + swapRangeCmpFunc(data, m-i, m, i, cmp) } diff --git a/vendor/golang.org/x/exp/slices/zsortordered.go b/vendor/golang.org/x/exp/slices/zsortordered.go index efaa1c8b7..99b47c398 100644 --- a/vendor/golang.org/x/exp/slices/zsortordered.go +++ b/vendor/golang.org/x/exp/slices/zsortordered.go @@ -11,7 +11,7 @@ import "golang.org/x/exp/constraints" // insertionSortOrdered sorts data[a:b] using insertion sort. func insertionSortOrdered[E constraints.Ordered](data []E, a, b int) { for i := a + 1; i < b; i++ { - for j := i; j > a && (data[j] < data[j-1]); j-- { + for j := i; j > a && cmpLess(data[j], data[j-1]); j-- { data[j], data[j-1] = data[j-1], data[j] } } @@ -26,10 +26,10 @@ func siftDownOrdered[E constraints.Ordered](data []E, lo, hi, first int) { if child >= hi { break } - if child+1 < hi && (data[first+child] < data[first+child+1]) { + if child+1 < hi && cmpLess(data[first+child], data[first+child+1]) { child++ } - if !(data[first+root] < data[first+child]) { + if !cmpLess(data[first+root], data[first+child]) { return } data[first+root], data[first+child] = data[first+child], data[first+root] @@ -107,7 +107,7 @@ func pdqsortOrdered[E constraints.Ordered](data []E, a, b, limit int) { // Probably the slice contains many duplicate elements, partition the slice into // elements equal to and elements greater than the pivot. - if a > 0 && !(data[a-1] < data[pivot]) { + if a > 0 && !cmpLess(data[a-1], data[pivot]) { mid := partitionEqualOrdered(data, a, b, pivot) a = mid continue @@ -138,10 +138,10 @@ func partitionOrdered[E constraints.Ordered](data []E, a, b, pivot int) (newpivo data[a], data[pivot] = data[pivot], data[a] i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned - for i <= j && (data[i] < data[a]) { + for i <= j && cmpLess(data[i], data[a]) { i++ } - for i <= j && !(data[j] < data[a]) { + for i <= j && !cmpLess(data[j], data[a]) { j-- } if i > j { @@ -153,10 +153,10 @@ func partitionOrdered[E constraints.Ordered](data []E, a, b, pivot int) (newpivo j-- for { - for i <= j && (data[i] < data[a]) { + for i <= j && cmpLess(data[i], data[a]) { i++ } - for i <= j && !(data[j] < data[a]) { + for i <= j && !cmpLess(data[j], data[a]) { j-- } if i > j { @@ -177,10 +177,10 @@ func partitionEqualOrdered[E constraints.Ordered](data []E, a, b, pivot int) (ne i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned for { - for i <= j && !(data[a] < data[i]) { + for i <= j && !cmpLess(data[a], data[i]) { i++ } - for i <= j && (data[a] < data[j]) { + for i <= j && cmpLess(data[a], data[j]) { j-- } if i > j { @@ -201,7 +201,7 @@ func partialInsertionSortOrdered[E constraints.Ordered](data []E, a, b int) bool ) i := a + 1 for j := 0; j < maxSteps; j++ { - for i < b && !(data[i] < data[i-1]) { + for i < b && !cmpLess(data[i], data[i-1]) { i++ } @@ -218,7 +218,7 @@ func partialInsertionSortOrdered[E constraints.Ordered](data []E, a, b int) bool // Shift the smaller one to the left. if i-a >= 2 { for j := i - 1; j >= 1; j-- { - if !(data[j] < data[j-1]) { + if !cmpLess(data[j], data[j-1]) { break } data[j], data[j-1] = data[j-1], data[j] @@ -227,7 +227,7 @@ func partialInsertionSortOrdered[E constraints.Ordered](data []E, a, b int) bool // Shift the greater one to the right. if b-i >= 2 { for j := i + 1; j < b; j++ { - if !(data[j] < data[j-1]) { + if !cmpLess(data[j], data[j-1]) { break } data[j], data[j-1] = data[j-1], data[j] @@ -298,7 +298,7 @@ func choosePivotOrdered[E constraints.Ordered](data []E, a, b int) (pivot int, h // order2Ordered returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a. func order2Ordered[E constraints.Ordered](data []E, a, b int, swaps *int) (int, int) { - if data[b] < data[a] { + if cmpLess(data[b], data[a]) { *swaps++ return b, a } @@ -389,7 +389,7 @@ func symMergeOrdered[E constraints.Ordered](data []E, a, m, b int) { j := b for i < j { h := int(uint(i+j) >> 1) - if data[h] < data[a] { + if cmpLess(data[h], data[a]) { i = h + 1 } else { j = h @@ -413,7 +413,7 @@ func symMergeOrdered[E constraints.Ordered](data []E, a, m, b int) { j := m for i < j { h := int(uint(i+j) >> 1) - if !(data[m] < data[h]) { + if !cmpLess(data[m], data[h]) { i = h + 1 } else { j = h @@ -440,7 +440,7 @@ func symMergeOrdered[E constraints.Ordered](data []E, a, m, b int) { for start < r { c := int(uint(start+r) >> 1) - if !(data[p-c] < data[c]) { + if !cmpLess(data[p-c], data[c]) { start = c + 1 } else { r = c diff --git a/vendor/golang.org/x/mod/modfile/rule.go b/vendor/golang.org/x/mod/modfile/rule.go index 930b6c59b..e0869fa38 100644 --- a/vendor/golang.org/x/mod/modfile/rule.go +++ b/vendor/golang.org/x/mod/modfile/rule.go @@ -367,7 +367,7 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a } } if !fixed { - errorf("invalid go version '%s': must match format 1.23", args[0]) + errorf("invalid go version '%s': must match format 1.23.0", args[0]) return } } @@ -384,7 +384,7 @@ func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, a errorf("toolchain directive expects exactly one argument") return } else if strict && !ToolchainRE.MatchString(args[0]) { - errorf("invalid toolchain version '%s': must match format go1.23 or local", args[0]) + errorf("invalid toolchain version '%s': must match format go1.23.0 or local", args[0]) return } f.Toolchain = &Toolchain{Syntax: line} diff --git a/vendor/golang.org/x/net/dns/dnsmessage/message.go b/vendor/golang.org/x/net/dns/dnsmessage/message.go index 37da3de4d..b6b4f9c19 100644 --- a/vendor/golang.org/x/net/dns/dnsmessage/message.go +++ b/vendor/golang.org/x/net/dns/dnsmessage/message.go @@ -361,6 +361,8 @@ func (m *Header) GoString() string { "Truncated: " + printBool(m.Truncated) + ", " + "RecursionDesired: " + printBool(m.RecursionDesired) + ", " + "RecursionAvailable: " + printBool(m.RecursionAvailable) + ", " + + "AuthenticData: " + printBool(m.AuthenticData) + ", " + + "CheckingDisabled: " + printBool(m.CheckingDisabled) + ", " + "RCode: " + m.RCode.GoString() + "}" } @@ -490,7 +492,7 @@ func (r *Resource) GoString() string { // A ResourceBody is a DNS resource record minus the header. type ResourceBody interface { // pack packs a Resource except for its header. - pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) + pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) // realType returns the actual type of the Resource. This is used to // fill in the header Type field. @@ -501,7 +503,7 @@ type ResourceBody interface { } // pack appends the wire format of the Resource to msg. -func (r *Resource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { +func (r *Resource) pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) { if r.Body == nil { return msg, errNilResouceBody } @@ -540,11 +542,13 @@ type Parser struct { msg []byte header header - section section - off int - index int - resHeaderValid bool - resHeader ResourceHeader + section section + off int + index int + resHeaderValid bool + resHeaderOffset int + resHeaderType Type + resHeaderLength uint16 } // Start parses the header and enables the parsing of Questions. @@ -595,8 +599,9 @@ func (p *Parser) resource(sec section) (Resource, error) { func (p *Parser) resourceHeader(sec section) (ResourceHeader, error) { if p.resHeaderValid { - return p.resHeader, nil + p.off = p.resHeaderOffset } + if err := p.checkAdvance(sec); err != nil { return ResourceHeader{}, err } @@ -606,14 +611,16 @@ func (p *Parser) resourceHeader(sec section) (ResourceHeader, error) { return ResourceHeader{}, err } p.resHeaderValid = true - p.resHeader = hdr + p.resHeaderOffset = p.off + p.resHeaderType = hdr.Type + p.resHeaderLength = hdr.Length p.off = off return hdr, nil } func (p *Parser) skipResource(sec section) error { - if p.resHeaderValid { - newOff := p.off + int(p.resHeader.Length) + if p.resHeaderValid && p.section == sec { + newOff := p.off + int(p.resHeaderLength) if newOff > len(p.msg) { return errResourceLen } @@ -864,14 +871,14 @@ func (p *Parser) SkipAllAdditionals() error { // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) CNAMEResource() (CNAMEResource, error) { - if !p.resHeaderValid || p.resHeader.Type != TypeCNAME { + if !p.resHeaderValid || p.resHeaderType != TypeCNAME { return CNAMEResource{}, ErrNotStarted } r, err := unpackCNAMEResource(p.msg, p.off) if err != nil { return CNAMEResource{}, err } - p.off += int(p.resHeader.Length) + p.off += int(p.resHeaderLength) p.resHeaderValid = false p.index++ return r, nil @@ -882,14 +889,14 @@ func (p *Parser) CNAMEResource() (CNAMEResource, error) { // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) MXResource() (MXResource, error) { - if !p.resHeaderValid || p.resHeader.Type != TypeMX { + if !p.resHeaderValid || p.resHeaderType != TypeMX { return MXResource{}, ErrNotStarted } r, err := unpackMXResource(p.msg, p.off) if err != nil { return MXResource{}, err } - p.off += int(p.resHeader.Length) + p.off += int(p.resHeaderLength) p.resHeaderValid = false p.index++ return r, nil @@ -900,14 +907,14 @@ func (p *Parser) MXResource() (MXResource, error) { // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) NSResource() (NSResource, error) { - if !p.resHeaderValid || p.resHeader.Type != TypeNS { + if !p.resHeaderValid || p.resHeaderType != TypeNS { return NSResource{}, ErrNotStarted } r, err := unpackNSResource(p.msg, p.off) if err != nil { return NSResource{}, err } - p.off += int(p.resHeader.Length) + p.off += int(p.resHeaderLength) p.resHeaderValid = false p.index++ return r, nil @@ -918,14 +925,14 @@ func (p *Parser) NSResource() (NSResource, error) { // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) PTRResource() (PTRResource, error) { - if !p.resHeaderValid || p.resHeader.Type != TypePTR { + if !p.resHeaderValid || p.resHeaderType != TypePTR { return PTRResource{}, ErrNotStarted } r, err := unpackPTRResource(p.msg, p.off) if err != nil { return PTRResource{}, err } - p.off += int(p.resHeader.Length) + p.off += int(p.resHeaderLength) p.resHeaderValid = false p.index++ return r, nil @@ -936,14 +943,14 @@ func (p *Parser) PTRResource() (PTRResource, error) { // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) SOAResource() (SOAResource, error) { - if !p.resHeaderValid || p.resHeader.Type != TypeSOA { + if !p.resHeaderValid || p.resHeaderType != TypeSOA { return SOAResource{}, ErrNotStarted } r, err := unpackSOAResource(p.msg, p.off) if err != nil { return SOAResource{}, err } - p.off += int(p.resHeader.Length) + p.off += int(p.resHeaderLength) p.resHeaderValid = false p.index++ return r, nil @@ -954,14 +961,14 @@ func (p *Parser) SOAResource() (SOAResource, error) { // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) TXTResource() (TXTResource, error) { - if !p.resHeaderValid || p.resHeader.Type != TypeTXT { + if !p.resHeaderValid || p.resHeaderType != TypeTXT { return TXTResource{}, ErrNotStarted } - r, err := unpackTXTResource(p.msg, p.off, p.resHeader.Length) + r, err := unpackTXTResource(p.msg, p.off, p.resHeaderLength) if err != nil { return TXTResource{}, err } - p.off += int(p.resHeader.Length) + p.off += int(p.resHeaderLength) p.resHeaderValid = false p.index++ return r, nil @@ -972,14 +979,14 @@ func (p *Parser) TXTResource() (TXTResource, error) { // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) SRVResource() (SRVResource, error) { - if !p.resHeaderValid || p.resHeader.Type != TypeSRV { + if !p.resHeaderValid || p.resHeaderType != TypeSRV { return SRVResource{}, ErrNotStarted } r, err := unpackSRVResource(p.msg, p.off) if err != nil { return SRVResource{}, err } - p.off += int(p.resHeader.Length) + p.off += int(p.resHeaderLength) p.resHeaderValid = false p.index++ return r, nil @@ -990,14 +997,14 @@ func (p *Parser) SRVResource() (SRVResource, error) { // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) AResource() (AResource, error) { - if !p.resHeaderValid || p.resHeader.Type != TypeA { + if !p.resHeaderValid || p.resHeaderType != TypeA { return AResource{}, ErrNotStarted } r, err := unpackAResource(p.msg, p.off) if err != nil { return AResource{}, err } - p.off += int(p.resHeader.Length) + p.off += int(p.resHeaderLength) p.resHeaderValid = false p.index++ return r, nil @@ -1008,14 +1015,14 @@ func (p *Parser) AResource() (AResource, error) { // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) AAAAResource() (AAAAResource, error) { - if !p.resHeaderValid || p.resHeader.Type != TypeAAAA { + if !p.resHeaderValid || p.resHeaderType != TypeAAAA { return AAAAResource{}, ErrNotStarted } r, err := unpackAAAAResource(p.msg, p.off) if err != nil { return AAAAResource{}, err } - p.off += int(p.resHeader.Length) + p.off += int(p.resHeaderLength) p.resHeaderValid = false p.index++ return r, nil @@ -1026,14 +1033,14 @@ func (p *Parser) AAAAResource() (AAAAResource, error) { // One of the XXXHeader methods must have been called before calling this // method. func (p *Parser) OPTResource() (OPTResource, error) { - if !p.resHeaderValid || p.resHeader.Type != TypeOPT { + if !p.resHeaderValid || p.resHeaderType != TypeOPT { return OPTResource{}, ErrNotStarted } - r, err := unpackOPTResource(p.msg, p.off, p.resHeader.Length) + r, err := unpackOPTResource(p.msg, p.off, p.resHeaderLength) if err != nil { return OPTResource{}, err } - p.off += int(p.resHeader.Length) + p.off += int(p.resHeaderLength) p.resHeaderValid = false p.index++ return r, nil @@ -1047,11 +1054,11 @@ func (p *Parser) UnknownResource() (UnknownResource, error) { if !p.resHeaderValid { return UnknownResource{}, ErrNotStarted } - r, err := unpackUnknownResource(p.resHeader.Type, p.msg, p.off, p.resHeader.Length) + r, err := unpackUnknownResource(p.resHeaderType, p.msg, p.off, p.resHeaderLength) if err != nil { return UnknownResource{}, err } - p.off += int(p.resHeader.Length) + p.off += int(p.resHeaderLength) p.resHeaderValid = false p.index++ return r, nil @@ -1122,7 +1129,7 @@ func (m *Message) AppendPack(b []byte) ([]byte, error) { // DNS messages can be a maximum of 512 bytes long. Without compression, // many DNS response messages are over this limit, so enabling // compression will help ensure compliance. - compression := map[string]int{} + compression := map[string]uint16{} for i := range m.Questions { var err error @@ -1213,7 +1220,7 @@ type Builder struct { // compression is a mapping from name suffixes to their starting index // in msg. - compression map[string]int + compression map[string]uint16 } // NewBuilder creates a new builder with compression disabled. @@ -1250,7 +1257,7 @@ func NewBuilder(buf []byte, h Header) Builder { // // Compression should be enabled before any sections are added for best results. func (b *Builder) EnableCompression() { - b.compression = map[string]int{} + b.compression = map[string]uint16{} } func (b *Builder) startCheck(s section) error { @@ -1666,7 +1673,7 @@ func (h *ResourceHeader) GoString() string { // pack appends the wire format of the ResourceHeader to oldMsg. // // lenOff is the offset in msg where the Length field was packed. -func (h *ResourceHeader) pack(oldMsg []byte, compression map[string]int, compressionOff int) (msg []byte, lenOff int, err error) { +func (h *ResourceHeader) pack(oldMsg []byte, compression map[string]uint16, compressionOff int) (msg []byte, lenOff int, err error) { msg = oldMsg if msg, err = h.Name.pack(msg, compression, compressionOff); err != nil { return oldMsg, 0, &nestedError{"Name", err} @@ -1894,7 +1901,7 @@ func unpackBytes(msg []byte, off int, field []byte) (int, error) { const nonEncodedNameMax = 254 -// A Name is a non-encoded domain name. It is used instead of strings to avoid +// A Name is a non-encoded and non-escaped domain name. It is used instead of strings to avoid // allocations. type Name struct { Data [255]byte @@ -1921,6 +1928,8 @@ func MustNewName(name string) Name { } // String implements fmt.Stringer.String. +// +// Note: characters inside the labels are not escaped in any way. func (n Name) String() string { return string(n.Data[:n.Length]) } @@ -1937,7 +1946,7 @@ func (n *Name) GoString() string { // // The compression map will be updated with new domain suffixes. If compression // is nil, compression will not be used. -func (n *Name) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { +func (n *Name) pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) { oldMsg := msg if n.Length > nonEncodedNameMax { @@ -1954,6 +1963,8 @@ func (n *Name) pack(msg []byte, compression map[string]int, compressionOff int) return append(msg, 0), nil } + var nameAsStr string + // Emit sequence of counted strings, chopping at dots. for i, begin := 0, 0; i < int(n.Length); i++ { // Check for the end of the segment. @@ -1984,16 +1995,22 @@ func (n *Name) pack(msg []byte, compression map[string]int, compressionOff int) // segment. A pointer is two bytes with the two most significant // bits set to 1 to indicate that it is a pointer. if (i == 0 || n.Data[i-1] == '.') && compression != nil { - if ptr, ok := compression[string(n.Data[i:])]; ok { + if ptr, ok := compression[string(n.Data[i:n.Length])]; ok { // Hit. Emit a pointer instead of the rest of // the domain. return append(msg, byte(ptr>>8|0xC0), byte(ptr)), nil } // Miss. Add the suffix to the compression table if the - // offset can be stored in the available 14 bytes. - if len(msg) <= int(^uint16(0)>>2) { - compression[string(n.Data[i:])] = len(msg) - compressionOff + // offset can be stored in the available 14 bits. + newPtr := len(msg) - compressionOff + if newPtr <= int(^uint16(0)>>2) { + if nameAsStr == "" { + // allocate n.Data on the heap once, to avoid allocating it + // multiple times (for next labels). + nameAsStr = string(n.Data[:n.Length]) + } + compression[nameAsStr[i:]] = uint16(newPtr) } } } @@ -2133,7 +2150,7 @@ type Question struct { } // pack appends the wire format of the Question to msg. -func (q *Question) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { +func (q *Question) pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) { msg, err := q.Name.pack(msg, compression, compressionOff) if err != nil { return msg, &nestedError{"Name", err} @@ -2229,7 +2246,7 @@ func (r *CNAMEResource) realType() Type { } // pack appends the wire format of the CNAMEResource to msg. -func (r *CNAMEResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { +func (r *CNAMEResource) pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) { return r.CNAME.pack(msg, compression, compressionOff) } @@ -2257,7 +2274,7 @@ func (r *MXResource) realType() Type { } // pack appends the wire format of the MXResource to msg. -func (r *MXResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { +func (r *MXResource) pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) { oldMsg := msg msg = packUint16(msg, r.Pref) msg, err := r.MX.pack(msg, compression, compressionOff) @@ -2296,7 +2313,7 @@ func (r *NSResource) realType() Type { } // pack appends the wire format of the NSResource to msg. -func (r *NSResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { +func (r *NSResource) pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) { return r.NS.pack(msg, compression, compressionOff) } @@ -2323,7 +2340,7 @@ func (r *PTRResource) realType() Type { } // pack appends the wire format of the PTRResource to msg. -func (r *PTRResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { +func (r *PTRResource) pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) { return r.PTR.pack(msg, compression, compressionOff) } @@ -2360,7 +2377,7 @@ func (r *SOAResource) realType() Type { } // pack appends the wire format of the SOAResource to msg. -func (r *SOAResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { +func (r *SOAResource) pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) { oldMsg := msg msg, err := r.NS.pack(msg, compression, compressionOff) if err != nil { @@ -2432,7 +2449,7 @@ func (r *TXTResource) realType() Type { } // pack appends the wire format of the TXTResource to msg. -func (r *TXTResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { +func (r *TXTResource) pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) { oldMsg := msg for _, s := range r.TXT { var err error @@ -2488,7 +2505,7 @@ func (r *SRVResource) realType() Type { } // pack appends the wire format of the SRVResource to msg. -func (r *SRVResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { +func (r *SRVResource) pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) { oldMsg := msg msg = packUint16(msg, r.Priority) msg = packUint16(msg, r.Weight) @@ -2539,7 +2556,7 @@ func (r *AResource) realType() Type { } // pack appends the wire format of the AResource to msg. -func (r *AResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { +func (r *AResource) pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) { return packBytes(msg, r.A[:]), nil } @@ -2573,7 +2590,7 @@ func (r *AAAAResource) GoString() string { } // pack appends the wire format of the AAAAResource to msg. -func (r *AAAAResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { +func (r *AAAAResource) pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) { return packBytes(msg, r.AAAA[:]), nil } @@ -2613,7 +2630,7 @@ func (r *OPTResource) realType() Type { return TypeOPT } -func (r *OPTResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { +func (r *OPTResource) pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) { for _, opt := range r.Options { msg = packUint16(msg, opt.Code) l := uint16(len(opt.Data)) @@ -2671,7 +2688,7 @@ func (r *UnknownResource) realType() Type { } // pack appends the wire format of the UnknownResource to msg. -func (r *UnknownResource) pack(msg []byte, compression map[string]int, compressionOff int) ([]byte, error) { +func (r *UnknownResource) pack(msg []byte, compression map[string]uint16, compressionOff int) ([]byte, error) { return packBytes(msg, r.Data[:]), nil } diff --git a/vendor/golang.org/x/net/publicsuffix/data/children b/vendor/golang.org/x/net/publicsuffix/data/children index 1038c561a..08261bffd 100644 Binary files a/vendor/golang.org/x/net/publicsuffix/data/children and b/vendor/golang.org/x/net/publicsuffix/data/children differ diff --git a/vendor/golang.org/x/net/publicsuffix/data/nodes b/vendor/golang.org/x/net/publicsuffix/data/nodes index 34751cd5b..1dae6ede8 100644 Binary files a/vendor/golang.org/x/net/publicsuffix/data/nodes and b/vendor/golang.org/x/net/publicsuffix/data/nodes differ diff --git a/vendor/golang.org/x/net/publicsuffix/data/text b/vendor/golang.org/x/net/publicsuffix/data/text index 124dcd61f..7e516413f 100644 --- a/vendor/golang.org/x/net/publicsuffix/data/text +++ b/vendor/golang.org/x/net/publicsuffix/data/text @@ -1 +1 @@ -billustrationionjukudoyamakeupowiathletajimageandsoundandvision-riopretobishimagentositecnologiabiocelotenkawabipanasonicatfoodnetworkinggroupperbirdartcenterprisecloudaccesscamdvrcampaniabirkenesoddtangenovarahkkeravjuegoshikikiraraholtalenishikatakazakindependent-revieweirbirthplaceu-1bitbucketrzynishikatsuragirlyuzawabitternidiscoverybjarkoybjerkreimdbaltimore-og-romsdalp1bjugnishikawazukamishihoronobeautydalwaysdatabaseballangenkainanaejrietisalatinabenogatabitorderblackfridaybloombergbauernishimerabloxcms3-website-us-west-2blushakotanishinomiyashironocparachutingjovikarateu-2bmoattachmentsalangenishinoomotegovtattoolforgerockartuzybmsalon-1bmwellbeingzoneu-3bnrwesteuropenairbusantiquesaltdalomzaporizhzhedmarkaratsuginamikatagamilanotairesistanceu-4bondigitaloceanspacesaludishangrilanciabonnishinoshimatsusakahoginankokubunjindianapolis-a-bloggerbookonlinewjerseyboomlahppiacenzachpomorskienishiokoppegardiskussionsbereichattanooganordkapparaglidinglassassinationalheritageu-north-1boschaefflerdalondonetskarelianceu-south-1bostik-serveronagasukevje-og-hornnesalvadordalibabalatinord-aurdalipaywhirlondrinaplesknsalzburgleezextraspace-to-rentalstomakomaibarabostonakijinsekikogentappssejnyaarparalleluxembourglitcheltenham-radio-opensocialorenskogliwicebotanicalgardeno-staginglobodoes-itcouldbeworldisrechtranakamurataiwanairforcechireadthedocsxeroxfinitybotanicgardenishitosashimizunaminamiawajikindianmarketinglogowestfalenishiwakindielddanuorrindigenamsskoganeindustriabotanyanagawallonieruchomoscienceandindustrynissandiegoddabouncemerckmsdnipropetrovskjervoyageorgeorgiabounty-fullensakerrypropertiesamegawaboutiquebecommerce-shopselectaxihuanissayokkaichintaifun-dnsaliasamnangerboutireservditchyouriparasiteboyfriendoftheinternetflixjavaldaostathellevangerbozen-sudtirolottokorozawabozen-suedtirolouvreisenissedalovepoparisor-fronisshingucciprianiigataipeidsvollovesickariyakumodumeloyalistoragebplaceducatorprojectcmembersampalermomahaccapooguybrandywinevalleybrasiliadboxosascoli-picenorddalpusercontentcp4bresciaokinawashirosatobamagazineuesamsclubartowestus2brindisibenikitagataikikuchikumagayagawalmartgorybristoloseyouriparliamentjeldsundivtasvuodnakaniikawatanagurabritishcolumbialowiezaganiyodogawabroadcastlebtimnetzlgloomy-routerbroadwaybroke-itvedestrandivttasvuotnakanojohanamakindlefrakkestadiybrokerbrothermesaverdeatnulmemergencyachtsamsungloppennebrowsersafetymarketsandnessjoenl-ams-1brumunddalublindesnesandoybrunelastxn--0trq7p7nnbrusselsandvikcoromantovalle-daostavangerbruxellesanfranciscofreakunekobayashikaoirmemorialucaniabryanskodjedugit-pagespeedmobilizeroticagliaricoharuovatlassian-dev-builderscbglugsjcbnpparibashkiriabrynewmexicoacharterbuzzwfarmerseinebwhalingmbhartiffany-2bzhitomirbzzcodyn-vpndnsantacruzsantafedjeffersoncoffeedbackdropocznordlandrudupontariobranconavstackasaokamikoaniikappudownloadurbanamexhibitioncogretakamatsukawacollectioncolognewyorkshirebungoonordre-landurhamburgrimstadynamisches-dnsantamariakecolonialwilliamsburgripeeweeklylotterycoloradoplateaudnedalncolumbusheycommunexus-3community-prochowicecomobaravendbambleborkapsicilyonagoyauthgear-stagingivestbyglandroverhallair-traffic-controlleyombomloabaths-heilbronnoysunddnslivegarsheiheijibigawaustraliaustinnfshostrolekamisatokaizukameyamatotakadaustevollivornowtv-infolldalolipopmcdircompanychipstmncomparemarkerryhotelsantoandrepbodynaliasnesoddenmarkhangelskjakdnepropetrovskiervaapsteigenflfannefrankfurtjxn--12cfi8ixb8lutskashibatakashimarshallstatebankashiharacomsecaaskimitsubatamibuildingriwatarailwaycondoshichinohealth-carereformemsettlersanukindustriesteamfamberlevagangaviikanonjinfinitigotembaixadaconferenceconstructionconsuladogadollsaobernardomniweatherchanneluxuryconsultanthropologyconsultingroks-thisayamanobeokakegawacontactkmaxxn--12co0c3b4evalled-aostamayukinsuregruhostingrondarcontagematsubaravennaharimalborkashiwaracontemporaryarteducationalchikugodonnakaiwamizawashtenawsmppl-wawdev-myqnapcloudcontrolledogawarabikomaezakirunoopschlesischesaogoncartoonartdecologiacontractorskenconventureshinodearthickashiwazakiyosatokamachilloutsystemscloudsitecookingchannelsdvrdnsdojogaszkolancashirecifedexetercoolblogdnsfor-better-thanawassamukawatarikuzentakatairavpagecooperativano-frankivskygearapparochernigovernmentksatxn--1ck2e1bananarepublic-inquiryggeebinatsukigatajimidsundevelopmentatarantours3-external-1copenhagencyclopedichiropracticatholicaxiashorokanaiecoproductionsaotomeinforumzcorporationcorsicahcesuoloanswatch-and-clockercorvettenrissagaeroclubmedecincinnativeamericanantiquest-le-patron-k3sapporomuracosenzamamidorittoeigersundynathomebuiltwithdarkasserverrankoshigayaltakasugaintelligencecosidnshome-webservercellikescandypoppdaluzerncostumedicallynxn--1ctwolominamatargets-itlon-2couchpotatofriesardegnarutomobegetmyiparsardiniacouncilvivanovoldacouponsarlcozoracq-acranbrookuwanalyticsarpsborgrongausdalcrankyowariasahikawatchandclockasukabeauxartsandcraftsarufutsunomiyawakasaikaitabashijonawatecrdyndns-at-homedepotaruinterhostsolutionsasayamatta-varjjatmpartinternationalfirearmsaseboknowsitallcreditcardyndns-at-workshoppingrossetouchigasakitahiroshimansionsaskatchewancreditunioncremonashgabadaddjaguarqcxn--1lqs03ncrewhmessinarashinomutashinaintuitoyosatoyokawacricketnedalcrimeast-kazakhstanangercrotonecrownipartsassarinuyamashinazawacrsaudacruisesauheradyndns-blogsitextilegnicapetownnews-stagingroundhandlingroznycuisinellancasterculturalcentertainmentoyotapartysvardocuneocupcakecuritibabymilk3curvallee-d-aosteinkjerusalempresashibetsurugashimaringatlantajirinvestmentsavannahgacutegirlfriendyndns-freeboxoslocalzonecymrulvikasumigaurawa-mazowszexnetlifyinzairtrafficplexus-1cyonabarumesswithdnsaveincloudyndns-homednsaves-the-whalessandria-trani-barletta-andriatranibarlettaandriacyouthruherecipescaracaltanissettaishinomakilovecollegefantasyleaguernseyfembetsukumiyamazonawsglobalacceleratorahimeshimabaridagawatchesciencecentersciencehistoryfermockasuyamegurownproviderferraraferraris-a-catererferrerotikagoshimalopolskanlandyndns-picsaxofetsundyndns-remotewdyndns-ipasadenaroyfgujoinvilleitungsenfhvalerfidontexistmein-iservschulegallocalhostrodawarafieldyndns-serverdalfigueresindevicenzaolkuszczytnoipirangalsaceofilateliafilegear-augustowhoswholdingsmall-webthingscientistordalfilegear-debianfilegear-gbizfilegear-iefilegear-jpmorganfilegear-sg-1filminamiechizenfinalfinancefineartscrapper-sitefinlandyndns-weblikes-piedmonticellocus-4finnoyfirebaseappaviancarrdyndns-wikinkobearalvahkijoetsuldalvdalaskanittedallasalleasecuritytacticschoenbrunnfirenetoystre-slidrettozawafirenzefirestonefirewebpaascrappingulenfirmdaleikangerfishingoldpoint2thisamitsukefitjarvodkafjordyndns-workangerfitnessettlementozsdellogliastradingunmanxn--1qqw23afjalerfldrvalleeaosteflekkefjordyndns1flesberguovdageaidnunjargaflickragerogerscrysecretrosnubar0flierneflirfloginlinefloppythonanywhereggio-calabriafloraflorencefloridatsunangojomedicinakamagayahabackplaneapplinzis-a-celticsfanfloripadoval-daostavalleyfloristanohatakahamalselvendrellflorokunohealthcareerscwienflowerservehalflifeinsurancefltrani-andria-barletta-trani-andriaflynnhosting-clusterfnchiryukyuragifuchungbukharanzanfndynnschokokekschokoladenfnwkaszubytemarkatowicefoolfor-ourfor-somedio-campidano-mediocampidanomediofor-theaterforexrothachijolsterforgotdnservehttpbin-butterforli-cesena-forlicesenaforlillesandefjordynservebbscholarshipschoolbusinessebyforsaleirfjordynuniversityforsandasuolodingenfortalfortefortmissoulangevagrigentomologyeonggiehtavuoatnagahamaroygardencowayfortworthachinoheavyfosneservehumourfotraniandriabarlettatraniandriafoxfordecampobassociatest-iserveblogsytemp-dnserveirchitachinakagawashingtondchernivtsiciliafozfr-par-1fr-par-2franamizuhobby-sitefrancaiseharafranziskanerimalvikatsushikabedzin-addrammenuorochesterfredrikstadtvserveminecraftranoyfreeddnsfreebox-oservemp3freedesktopfizerfreemasonryfreemyiphosteurovisionfreesitefreetlservep2pgfoggiafreiburgushikamifuranorfolkebibleksvikatsuyamarugame-hostyhostingxn--2m4a15efrenchkisshikirkeneservepicservequakefreseniuscultureggio-emilia-romagnakasatsunairguardiannakadomarinebraskaunicommbankaufentigerfribourgfriuli-v-giuliafriuli-ve-giuliafriuli-vegiuliafriuli-venezia-giuliafriuli-veneziagiuliafriuli-vgiuliafriuliv-giuliafriulive-giuliafriulivegiuliafriulivenezia-giuliafriuliveneziagiuliafriulivgiuliafrlfroganservesarcasmatartanddesignfrognfrolandynv6from-akrehamnfrom-alfrom-arfrom-azurewebsiteshikagamiishibukawakepnoorfrom-capitalonewportransipharmacienservicesevastopolefrom-coalfrom-ctranslatedynvpnpluscountryestateofdelawareclaimschoolsztynsettsupportoyotomiyazakis-a-candidatefrom-dchitosetodayfrom-dediboxafrom-flandersevenassisienarvikautokeinoticeablewismillerfrom-gaulardalfrom-hichisochikuzenfrom-iafrom-idyroyrvikingruenoharafrom-ilfrom-in-berlindasewiiheyaizuwakamatsubushikusakadogawafrom-ksharpharmacyshawaiijimarcheapartmentshellaspeziafrom-kyfrom-lanshimokawafrom-mamurogawatsonfrom-mdfrom-medizinhistorischeshimokitayamattelekommunikationfrom-mifunefrom-mnfrom-modalenfrom-mshimonitayanagit-reposts-and-telecommunicationshimonosekikawafrom-mtnfrom-nchofunatoriginstantcloudfrontdoorfrom-ndfrom-nefrom-nhktistoryfrom-njshimosuwalkis-a-chefarsundyndns-mailfrom-nminamifuranofrom-nvalleedaostefrom-nynysagamiharafrom-ohdattorelayfrom-oketogolffanshimotsukefrom-orfrom-padualstackazoologicalfrom-pratogurafrom-ris-a-conservativegashimotsumayfirstockholmestrandfrom-schmidtre-gauldalfrom-sdscloudfrom-tnfrom-txn--2scrj9chonanbunkyonanaoshimakanegasakikugawaltervistailscaleforcefrom-utsiracusaikirovogradoyfrom-vald-aostarostwodzislawildlifestylefrom-vtransportefrom-wafrom-wiardwebview-assetshinichinanfrom-wvanylvenneslaskerrylogisticshinjournalismartlabelingfrom-wyfrosinonefrostalowa-wolawafroyal-commissionfruskydivingfujiiderafujikawaguchikonefujiminokamoenairkitapps-auction-rancherkasydneyfujinomiyadattowebhoptogakushimotoganefujiokayamandalfujisatoshonairlinedre-eikerfujisawafujishiroishidakabiratoridedyn-berlincolnfujitsuruokazakiryuohkurafujiyoshidavvenjargap-east-1fukayabeardubaiduckdnsncfdfukuchiyamadavvesiidappnodebalancertmgrazimutheworkpccwilliamhillfukudomigawafukuis-a-cpalacefukumitsubishigakisarazure-mobileirvikazteleportlligatransurlfukuokakamigaharafukuroishikarikaturindalfukusakishiwadazaifudaigokaseljordfukuyamagatakaharunusualpersonfunabashiriuchinadafunagatakahashimamakisofukushimangonnakatombetsumy-gatewayfunahashikamiamakusatsumasendaisenergyfundaciofunkfeuerfuoiskujukuriyamangyshlakasamatsudoomdnstracefuosskoczowinbar1furubirafurudonostiaafurukawajimaniwakuratefusodegaurafussaintlouis-a-anarchistoireggiocalabriafutabayamaguchinomihachimanagementrapaniizafutboldlygoingnowhere-for-morenakatsugawafuttsurutaharafuturecmshinjukumamotoyamashikefuturehostingfuturemailingfvghamurakamigoris-a-designerhandcraftedhandsonyhangglidinghangoutwentehannanmokuizumodenaklodzkochikuseihidorahannorthwesternmutualhanyuzenhapmircloudletshintokushimahappounzenharvestcelebrationhasamap-northeast-3hasaminami-alpshintomikasaharahashbangryhasudahasura-apphiladelphiaareadmyblogspotrdhasvikfh-muensterhatogayahoooshikamaishimofusartshinyoshitomiokamisunagawahatoyamazakitakatakanabeatshiojirishirifujiedahatsukaichikaiseiyoichimkentrendhostinghattfjelldalhayashimamotobusellfylkesbiblackbaudcdn-edgestackhero-networkisboringhazuminobushistoryhelplfinancialhelsinkitakyushuaiahembygdsforbundhemneshioyanaizuerichardlimanowarudahemsedalhepforgeblockshirahamatonbetsurgeonshalloffameiwamasoyheroyhetemlbfanhgtvaohigashiagatsumagoianiahigashichichibuskerudhigashihiroshimanehigashiizumozakitamigrationhigashikagawahigashikagurasoedahigashikawakitaaikitamotosunndalhigashikurumeeresinstaginghigashimatsushimarburghigashimatsuyamakitaakitadaitoigawahigashimurayamamotorcycleshirakokonoehigashinarusells-for-lesshiranukamitondabayashiogamagoriziahigashinehigashiomitamanortonsberghigashiosakasayamanakakogawahigashishirakawamatakanezawahigashisumiyoshikawaminamiaikitanakagusukumodernhigashitsunosegawahigashiurausukitashiobarahigashiyamatokoriyamanashifteditorxn--30rr7yhigashiyodogawahigashiyoshinogaris-a-doctorhippyhiraizumisatohnoshoohirakatashinagawahiranairportland-4-salernogiessennanjobojis-a-financialadvisor-aurdalhirarahiratsukaerusrcfastlylbanzaicloudappspotagerhirayaitakaokalmykiahistorichouseshiraois-a-geekhakassiahitachiomiyagildeskaliszhitachiotagonohejis-a-greenhitraeumtgeradegreehjartdalhjelmelandholeckodairaholidayholyhomegoodshiraokamitsuehomeiphilatelyhomelinkyard-cloudjiffyresdalhomelinuxn--32vp30hachiojiyahikobierzycehomeofficehomesecuritymacaparecidahomesecuritypchoseikarugamvikarlsoyhomesenseeringhomesklepphilipsynology-diskstationhomeunixn--3bst00minamiiserniahondahongooglecodebergentinghonjyoitakarazukaluganskharkivaporcloudhornindalhorsells-for-ustkanmakiwielunnerhortendofinternet-dnshiratakahagitapphoenixn--3ds443ghospitalhoteleshishikuis-a-guruhotelwithflightshisognehotmailhoyangerhoylandetakasagophonefosshisuifuettertdasnetzhumanitieshitaramahungryhurdalhurumajis-a-hard-workershizukuishimogosenhyllestadhyogoris-a-hunterhyugawarahyundaiwafuneis-into-carsiiitesilkharkovaresearchaeologicalvinklein-the-bandairtelebitbridgestoneenebakkeshibechambagricultureadymadealstahaugesunderseaportsinfolionetworkdalaheadjudygarlandis-into-cartoonsimple-urlis-into-gamesserlillyis-leetrentin-suedtirolis-lostre-toteneis-a-lawyeris-not-certifiedis-savedis-slickhersonis-uberleetrentino-a-adigeis-very-badajozis-a-liberalis-very-evillageis-very-goodyearis-very-niceis-very-sweetpepperugiais-with-thebandovre-eikerisleofmanaustdaljellybeanjenv-arubahccavuotnagaragusabaerobaticketsirdaljeonnamerikawauejetztrentino-aadigejevnakershusdecorativeartslupskhmelnytskyivarggatrentino-alto-adigejewelryjewishartgalleryjfkhplaystation-cloudyclusterjgorajlljls-sto1jls-sto2jls-sto3jmphotographysiojnjaworznospamproxyjoyentrentino-altoadigejoyokaichibajddarchitecturealtorlandjpnjprslzjurkotohiradomainstitutekotourakouhokutamamurakounosupabasembokukizunokunimilitarykouyamarylhurstjordalshalsenkouzushimasfjordenkozagawakozakis-a-llamarnardalkozowindowskrakowinnersnoasakatakkokamiminersokndalkpnkppspbarcelonagawakkanaibetsubamericanfamilyds3-fips-us-gov-west-1krasnikahokutokashikis-a-musiciankrasnodarkredstonekrelliankristiansandcatsolarssonkristiansundkrodsheradkrokstadelvalle-aostatic-accessolognekryminamiizukaminokawanishiaizubangekumanotteroykumatorinovecoregontrailroadkumejimashikis-a-nascarfankumenantokonamegatakatoris-a-nursells-itrentin-sud-tirolkunisakis-a-painteractivelvetrentin-sudtirolkunitachiaraindropilotsolundbecknx-serversellsyourhomeftphxn--3e0b707ekunitomigusukuleuvenetokigawakunneppuboliviajessheimpertrixcdn77-secureggioemiliaromagnamsosnowiechristiansburgminakamichiharakunstsammlungkunstunddesignkuokgroupimientaketomisatoolsomakurehabmerkurgankurobeeldengeluidkurogimimatakatsukis-a-patsfankuroisoftwarezzoologykuromatsunais-a-personaltrainerkuronkurotakikawasakis-a-photographerokussldkushirogawakustanais-a-playershiftcryptonomichigangwonkusupersalezajskomakiyosemitekutchanelkutnowruzhgorodeokuzumakis-a-republicanonoichinomiyakekvafjordkvalsundkvamscompute-1kvanangenkvinesdalkvinnheradkviteseidatingkvitsoykwpspdnsomnatalkzmisakis-a-soxfanmisasaguris-a-studentalmisawamisconfusedmishimasudamissilemisugitokuyamatsumaebashikshacknetrentino-sued-tirolmitakeharamitourismilemitoyoakemiuramiyazurecontainerdpolicemiyotamatsukuris-a-teacherkassyno-dshowamjondalenmonstermontrealestatefarmequipmentrentino-suedtirolmonza-brianzapposor-odalmonza-e-della-brianzaptokyotangotpantheonsitemonzabrianzaramonzaebrianzamonzaedellabrianzamoonscalebookinghostedpictetrentinoa-adigemordoviamoriyamatsumotofukemoriyoshiminamiashigaramormonmouthachirogatakamoriokakudamatsuemoroyamatsunomortgagemoscowiosor-varangermoseushimodatemosjoenmoskenesorfoldmossorocabalena-devicesorreisahayakawakamiichikawamisatottoris-a-techietis-a-landscaperspectakasakitchenmosvikomatsushimarylandmoteginowaniihamatamakinoharamoviemovimientolgamozilla-iotrentinoaadigemtranbytomaritimekeepingmuginozawaonsensiositemuikaminoyamaxunispacemukoebenhavnmulhouseoullensvanguardmunakatanemuncienciamuosattemupinbarclaycards3-sa-east-1murmanskomforbar2murotorcraftrentinoalto-adigemusashinoharamuseetrentinoaltoadigemuseumverenigingmusicargodaddyn-o-saurlandesortlandmutsuzawamy-wanggoupilemyactivedirectorymyamazeplaymyasustor-elvdalmycdmycloudnsoruminamimakis-a-rockstarachowicemydattolocalcertificationmyddnsgeekgalaxymydissentrentinos-tirolmydobissmarterthanyoumydrobofageologymydsoundcastronomy-vigorlicemyeffectrentinostirolmyfastly-terrariuminamiminowamyfirewalledreplittlestargardmyforuminamioguni5myfritzmyftpaccessouthcarolinaturalhistorymuseumcentermyhome-servermyjinomykolaivencloud66mymailermymediapchristmasakillucernemyokohamamatsudamypepinkommunalforbundmypetsouthwest1-uslivinghistorymyphotoshibalashovhadanorth-kazakhstanmypicturestaurantrentinosud-tirolmypsxn--3pxu8kommunemysecuritycamerakermyshopblocksowamyshopifymyspreadshopwarendalenugmythic-beastspectruminamisanrikubetsuppliesoomytis-a-bookkeepermaritimodspeedpartnermytuleap-partnersphinxn--41amyvnchromediatechnologymywirepaircraftingvollohmusashimurayamashikokuchuoplantationplantspjelkavikomorotsukagawaplatformsharis-a-therapistoiaplatter-appinokofuefukihaboromskogplatterpioneerplazaplcube-serversicherungplumbingoplurinacionalpodhalepodlasiellaktyubinskiptveterinairealmpmnpodzonepohlpoivronpokerpokrovskomvuxn--3hcrj9choyodobashichikashukujitawaraumalatvuopmicrosoftbankarmoypoliticarrierpolitiendapolkowicepoltavalle-d-aostaticspydebergpomorzeszowitdkongsbergponpesaro-urbino-pesarourbinopesaromasvuotnarusawapordenonepornporsangerporsangugeporsgrunnanyokoshibahikariwanumatakinouepoznanpraxis-a-bruinsfanprdpreservationpresidioprgmrprimetelemarkongsvingerprincipeprivatizehealthinsuranceprofesionalprogressivestfoldpromombetsupplypropertyprotectionprotonetrentinosued-tirolprudentialpruszkowithgoogleapiszprvcyberprzeworskogpulawypunyufuelveruminamiuonumassa-carrara-massacarraramassabuyshousesopotrentino-sud-tirolpupugliapussycateringebuzentsujiiepvhadselfiphdfcbankazunoticiashinkamigototalpvtrentinosuedtirolpwchungnamdalseidsbergmodellingmxn--11b4c3dray-dnsupdaterpzqhaebaruericssongdalenviknakayamaoris-a-cubicle-slavellinodeobjectshinshinotsurfashionstorebaselburguidefinimamateramochizukimobetsumidatlantichirurgiens-dentistes-en-franceqldqotoyohashimotoshimatsuzakis-an-accountantshowtimelbourneqponiatowadaqslgbtrentinsud-tirolqualifioappippueblockbusternopilawaquickconnectrentinsudtirolquicksytesrhtrentinsued-tirolquipelementsrltunestuff-4-saletunkonsulatrobeebyteappigboatsmolaquilanxessmushcdn77-sslingturystykaniepcetuscanytushuissier-justicetuvalleaostaverntuxfamilytwmailvestvagoyvevelstadvibo-valentiavibovalentiavideovillastufftoread-booksnestorfjordvinnicasadelamonedagestangevinnytsiavipsinaappiwatevirginiavirtual-uservecounterstrikevirtualcloudvirtualservervirtualuserveexchangevirtuelvisakuhokksundviterbolognagasakikonaikawagoevivianvivolkenkundenvixn--42c2d9avlaanderennesoyvladikavkazimierz-dolnyvladimirvlogintoyonezawavminanovologdanskonyveloftrentino-stirolvolvolkswagentstuttgartrentinsuedtirolvolyngdalvoorlopervossevangenvotevotingvotoyonovps-hostrowiecircustomer-ocimmobilienwixsitewloclawekoobindalwmcloudwmflabsurnadalwoodsidelmenhorstabackyardsurreyworse-thandawowithyoutuberspacekitagawawpdevcloudwpenginepoweredwphostedmailwpmucdnpixolinodeusercontentrentinosudtirolwpmudevcdnaccessokanagawawritesthisblogoipizzawroclawiwatsukiyonoshiroomgwtcirclerkstagewtfastvps-serverisignwuozuwzmiuwajimaxn--4gbriminingxn--4it168dxn--4it797kooris-a-libertarianxn--4pvxs4allxn--54b7fta0ccivilaviationredumbrellajollamericanexpressexyxn--55qw42gxn--55qx5dxn--5dbhl8dxn--5js045dxn--5rtp49civilisationrenderxn--5rtq34koperviklabudhabikinokawachinaganoharamcocottempurlxn--5su34j936bgsgxn--5tzm5gxn--6btw5axn--6frz82gxn--6orx2rxn--6qq986b3xlxn--7t0a264civilizationthewifiatmallorcafederation-webspacexn--80aaa0cvacationsusonoxn--80adxhksuzakananiimiharuxn--80ao21axn--80aqecdr1axn--80asehdbarclays3-us-east-2xn--80aswgxn--80aukraanghkembuchikujobservableusercontentrevisohughestripperxn--8dbq2axn--8ltr62koryokamikawanehonbetsuwanouchijiwadeliveryxn--8pvr4uxn--8y0a063axn--90a1affinitylotterybnikeisenbahnxn--90a3academiamicable-modemoneyxn--90aeroportalabamagasakishimabaraffleentry-snowplowiczeladzxn--90aishobarakawaharaoxn--90amckinseyxn--90azhytomyrxn--9dbhblg6dietritonxn--9dbq2axn--9et52uxn--9krt00axn--andy-iraxn--aroport-byandexcloudxn--asky-iraxn--aurskog-hland-jnbarefootballooningjerstadgcapebretonamicrolightingjesdalombardiadembroideryonagunicloudiherokuappanamasteiermarkaracoldwarszawauthgearappspacehosted-by-previderxn--avery-yuasakuragawaxn--b-5gaxn--b4w605ferdxn--balsan-sdtirol-nsbsuzukanazawaxn--bck1b9a5dre4civilwarmiasadoesntexisteingeekarpaczest-a-la-maisondre-landrayddns5yxn--bdddj-mrabdxn--bearalvhki-y4axn--berlevg-jxaxn--bhcavuotna-s4axn--bhccavuotna-k7axn--bidr-5nachikatsuuraxn--bievt-0qa2xn--bjarky-fyaotsurgeryxn--bjddar-ptargithubpreviewsaitohmannore-og-uvdalxn--blt-elabourxn--bmlo-graingerxn--bod-2naturalsciencesnaturellesuzukis-an-actorxn--bozen-sdtirol-2obanazawaxn--brnny-wuacademy-firewall-gatewayxn--brnnysund-m8accident-investigation-acornxn--brum-voagatroandinosaureportrentoyonakagyokutoyakomaganexn--btsfjord-9zaxn--bulsan-sdtirol-nsbaremetalpha-myqnapcloud9guacuiababia-goracleaningitpagexlimoldell-ogliastraderxn--c1avgxn--c2br7gxn--c3s14mincomcastreserve-onlinexn--cck2b3bargainstances3-us-gov-west-1xn--cckwcxetdxn--cesena-forl-mcbremangerxn--cesenaforl-i8axn--cg4bkis-an-actresshwindmillxn--ciqpnxn--clchc0ea0b2g2a9gcdxn--comunicaes-v6a2oxn--correios-e-telecomunicaes-ghc29axn--czr694barreaudiblebesbydgoszczecinemagnethnologyoriikaragandauthordalandroiddnss3-ap-southeast-2ix4432-balsan-suedtirolimiteddnskinggfakefurniturecreationavuotnaritakoelnayorovigotsukisosakitahatakahatakaishimoichinosekigaharaurskog-holandingitlaborxn--czrs0trogstadxn--czru2dxn--czrw28barrel-of-knowledgeappgafanquanpachicappacificurussiautomotivelandds3-ca-central-16-balsan-sudtirollagdenesnaaseinet-freaks3-ap-southeast-123websiteleaf-south-123webseiteckidsmynasushiobarackmazerbaijan-mayen-rootaribeiraogakibichuobiramusementdllpages3-ap-south-123sitewebhareidfjordvagsoyerhcloudd-dnsiskinkyolasiteastcoastaldefenceastus2038xn--d1acj3barrell-of-knowledgecomputerhistoryofscience-fictionfabricafjs3-us-west-1xn--d1alfaromeoxn--d1atromsakegawaxn--d5qv7z876clanbibaidarmeniaxn--davvenjrga-y4axn--djrs72d6uyxn--djty4kosaigawaxn--dnna-grajewolterskluwerxn--drbak-wuaxn--dyry-iraxn--e1a4cldmailukowhitesnow-dnsangohtawaramotoineppubtlsanjotelulubin-brbambinagisobetsuitagajoburgjerdrumcprequalifymein-vigorgebetsukuibmdeveloperauniteroizumizakinderoyomitanobninskanzakiyokawaraustrheimatunduhrennebulsan-suedtirololitapunk123kotisivultrobjectselinogradimo-siemenscaledekaascolipiceno-ipifony-1337xn--eckvdtc9dxn--efvn9svalbardunloppaderbornxn--efvy88hagakhanamigawaxn--ehqz56nxn--elqq16hagebostadxn--eveni-0qa01gaxn--f6qx53axn--fct429kosakaerodromegallupaasdaburxn--fhbeiarnxn--finny-yuaxn--fiq228c5hsvchurchaseljeepsondriodejaneirockyotobetsuliguriaxn--fiq64barsycenterprisesakievennodesadistcgrouplidlugolekagaminord-frontierxn--fiqs8sveioxn--fiqz9svelvikoninjambylxn--fjord-lraxn--fjq720axn--fl-ziaxn--flor-jraxn--flw351exn--forl-cesena-fcbssvizzeraxn--forlcesena-c8axn--fpcrj9c3dxn--frde-grandrapidsvn-repostorjcloud-ver-jpchowderxn--frna-woaraisaijosoyroroswedenxn--frya-hraxn--fzc2c9e2cleverappsannanxn--fzys8d69uvgmailxn--g2xx48clicketcloudcontrolapparmatsuuraxn--gckr3f0fauskedsmokorsetagayaseralingenoamishirasatogliattipschulserverxn--gecrj9clickrisinglesannohekinannestadraydnsanokaruizawaxn--ggaviika-8ya47haibarakitakamiizumisanofidelitysfjordxn--gildeskl-g0axn--givuotna-8yasakaiminatoyookaneyamazoexn--gjvik-wuaxn--gk3at1exn--gls-elacaixaxn--gmq050is-an-anarchistoricalsocietysnesigdalxn--gmqw5axn--gnstigbestellen-zvbrplsbxn--45br5cylxn--gnstigliefern-wobihirosakikamijimatsushigexn--h-2failxn--h1aeghair-surveillancexn--h1ahnxn--h1alizxn--h2breg3eveneswidnicasacampinagrandebungotakadaemongolianxn--h2brj9c8clinichippubetsuikilatironporterxn--h3cuzk1digickoseis-a-linux-usershoujis-a-knightpointtohoboleslawieconomiastalbanshizuokamogawaxn--hbmer-xqaxn--hcesuolo-7ya35barsyonlinewhampshirealtychyattorneyagawakuyabukihokumakogeniwaizumiotsurugimbalsfjordeportexaskoyabeagleboardetroitskypecorivneatonoshoes3-eu-west-3utilitiesquare7xn--hebda8basicserversaillesjabbottateshinanomachildrensgardenhlfanhsbc66xn--hery-iraxn--hgebostad-g3axn--hkkinen-5waxn--hmmrfeasta-s4accident-prevention-aptibleangaviikadenaamesjevuemielnoboribetsuckswidnikkolobrzegersundxn--hnefoss-q1axn--hobl-iraxn--holtlen-hxaxn--hpmir-xqaxn--hxt814exn--hyanger-q1axn--hylandet-54axn--i1b6b1a6a2exn--imr513nxn--indery-fyasugithubusercontentromsojamisonxn--io0a7is-an-artistgstagexn--j1adpkomonotogawaxn--j1aefbsbxn--1lqs71dyndns-office-on-the-webhostingrpassagensavonarviikamiokameokamakurazakiwakunigamihamadaxn--j1ael8basilicataniautoscanadaeguambulancentralus-2xn--j1amhakatanorthflankddiamondshinshiroxn--j6w193gxn--jlq480n2rgxn--jlq61u9w7basketballfinanzgorzeleccodespotenzakopanewspaperxn--jlster-byasuokannamihokkaidopaaskvollxn--jrpeland-54axn--jvr189miniserversusakis-a-socialistg-builderxn--k7yn95exn--karmy-yuaxn--kbrq7oxn--kcrx77d1x4axn--kfjord-iuaxn--klbu-woaxn--klt787dxn--kltp7dxn--kltx9axn--klty5xn--45brj9cistrondheimperiaxn--koluokta-7ya57hakodatexn--kprw13dxn--kpry57dxn--kput3is-an-engineeringxn--krager-gyatominamibosogndalxn--kranghke-b0axn--krdsherad-m8axn--krehamn-dxaxn--krjohka-hwab49jdevcloudfunctionsimplesitexn--ksnes-uuaxn--kvfjord-nxaxn--kvitsy-fyatsukanoyakagexn--kvnangen-k0axn--l-1fairwindswiebodzin-dslattuminamiyamashirokawanabeepilepsykkylvenicexn--l1accentureklamborghinikolaeventswinoujscienceandhistoryxn--laheadju-7yatsushiroxn--langevg-jxaxn--lcvr32dxn--ldingen-q1axn--leagaviika-52batochigifts3-us-west-2xn--lesund-huaxn--lgbbat1ad8jdfaststackschulplattformetacentrumeteorappassenger-associationxn--lgrd-poacctrusteexn--lhppi-xqaxn--linds-pramericanartrvestnestudioxn--lns-qlavagiskexn--loabt-0qaxn--lrdal-sraxn--lrenskog-54axn--lt-liacliniquedapliexn--lten-granexn--lury-iraxn--m3ch0j3axn--mely-iraxn--merker-kuaxn--mgb2ddeswisstpetersburgxn--mgb9awbfbx-ostrowwlkpmguitarschwarzgwangjuifminamidaitomanchesterxn--mgba3a3ejtrycloudflarevistaplestudynamic-dnsrvaroyxn--mgba3a4f16axn--mgba3a4fra1-deloittevaksdalxn--mgba7c0bbn0axn--mgbaakc7dvfstdlibestadxn--mgbaam7a8hakonexn--mgbab2bdxn--mgbah1a3hjkrdxn--mgbai9a5eva00batsfjordiscordsays3-website-ap-northeast-1xn--mgbai9azgqp6jejuniperxn--mgbayh7gpalmaseratis-an-entertainerxn--mgbbh1a71exn--mgbc0a9azcgxn--mgbca7dzdoxn--mgbcpq6gpa1axn--mgberp4a5d4a87gxn--mgberp4a5d4arxn--mgbgu82axn--mgbi4ecexposedxn--mgbpl2fhskosherbrookegawaxn--mgbqly7c0a67fbclintonkotsukubankarumaifarmsteadrobaknoluoktachikawakayamadridvallee-aosteroyxn--mgbqly7cvafr-1xn--mgbt3dhdxn--mgbtf8flapymntrysiljanxn--mgbtx2bauhauspostman-echocolatemasekd1xn--mgbx4cd0abbvieeexn--mix082fbxoschweizxn--mix891fedorainfraclouderaxn--mjndalen-64axn--mk0axin-vpnclothingdustdatadetectjmaxxxn--12c1fe0bradescotlandrrxn--mk1bu44cn-northwest-1xn--mkru45is-bykleclerchoshibuyachiyodancexn--mlatvuopmi-s4axn--mli-tlavangenxn--mlselv-iuaxn--moreke-juaxn--mori-qsakurais-certifiedxn--mosjen-eyawaraxn--mot-tlazioxn--mre-og-romsdal-qqbuseranishiaritakurashikis-foundationxn--msy-ula0hakubaghdadultravelchannelxn--mtta-vrjjat-k7aflakstadaokagakicks-assnasaarlandxn--muost-0qaxn--mxtq1minisitexn--ngbc5azdxn--ngbe9e0axn--ngbrxn--45q11citadelhicampinashikiminohostfoldnavyxn--nit225koshimizumakiyosunnydayxn--nmesjevuemie-tcbalestrandabergamoarekeymachineustarnbergxn--nnx388axn--nodessakyotanabelaudiopsysynology-dstreamlitappittsburghofficialxn--nqv7fs00emaxn--nry-yla5gxn--ntso0iqx3axn--ntsq17gxn--nttery-byaeserveftplanetariuminamitanexn--nvuotna-hwaxn--nyqy26axn--o1achernihivgubsxn--o3cw4hakuis-a-democratravelersinsurancexn--o3cyx2axn--od0algxn--od0aq3belementorayoshiokanumazuryukuhashimojibxos3-website-ap-southeast-1xn--ogbpf8flatangerxn--oppegrd-ixaxn--ostery-fyawatahamaxn--osyro-wuaxn--otu796dxn--p1acfedorapeoplegoismailillehammerfeste-ipatriaxn--p1ais-gonexn--pgbs0dhlx3xn--porsgu-sta26fedoraprojectoyotsukaidoxn--pssu33lxn--pssy2uxn--q7ce6axn--q9jyb4cngreaterxn--qcka1pmcpenzaporizhzhiaxn--qqqt11minnesotaketakayamassivegridxn--qxa6axn--qxamsterdamnserverbaniaxn--rady-iraxn--rdal-poaxn--rde-ulaxn--rdy-0nabaris-into-animeetrentin-sued-tirolxn--rennesy-v1axn--rhkkervju-01afeiraquarelleasingujaratoyouraxn--rholt-mragowoltlab-democraciaxn--rhqv96gxn--rht27zxn--rht3dxn--rht61exn--risa-5naturbruksgymnxn--risr-iraxn--rland-uuaxn--rlingen-mxaxn--rmskog-byaxn--rny31hakusanagochihayaakasakawaiishopitsitexn--rovu88bellevuelosangeles3-website-ap-southeast-2xn--rros-granvindafjordxn--rskog-uuaxn--rst-0naturhistorischesxn--rsta-framercanvasxn--rvc1e0am3exn--ryken-vuaxn--ryrvik-byaxn--s-1faithaldenxn--s9brj9cnpyatigorskolecznagatorodoyxn--sandnessjen-ogbellunord-odalombardyn53xn--sandy-yuaxn--sdtirol-n2axn--seral-lraxn--ses554gxn--sgne-graphoxn--4dbgdty6citichernovtsyncloudrangedaluccarbonia-iglesias-carboniaiglesiascarboniaxn--skierv-utazasxn--skjervy-v1axn--skjk-soaxn--sknit-yqaxn--sknland-fxaxn--slat-5natuurwetenschappenginexn--slt-elabcieszynh-servebeero-stageiseiroumuenchencoreapigeelvinckoshunantankmpspawnextdirectrentino-s-tirolxn--smla-hraxn--smna-gratangentlentapisa-geekosugexn--snase-nraxn--sndre-land-0cbeneventochiokinoshimaintenancebinordreisa-hockeynutazurestaticappspaceusercontentateyamaveroykenglandeltaitogitsumitakagiizeasypanelblagrarchaeologyeongbuk0emmafann-arboretumbriamallamaceiobbcg123homepagefrontappchizip61123minsidaarborteaches-yogasawaracingroks-theatree123hjemmesidealerimo-i-rana4u2-localhistorybolzano-altoadigeometre-experts-comptables3-ap-northeast-123miwebcambridgehirn4t3l3p0rtarumizusawabogadobeaemcloud-fr123paginaweberkeleyokosukanrabruzzombieidskoguchikushinonsenasakuchinotsuchiurakawafaicloudineat-url-o-g-i-naval-d-aosta-valleyokote164-b-datacentermezproxyzgoraetnabudejjudaicadaquest-mon-blogueurodirumaceratabuseating-organicbcn-north-123saitamakawabartheshopencraftrainingdyniajuedischesapeakebayernavigationavoi234lima-cityeats3-ap-northeast-20001wwwedeployokozeastasiamunemurorangecloudplatform0xn--snes-poaxn--snsa-roaxn--sr-aurdal-l8axn--sr-fron-q1axn--sr-odal-q1axn--sr-varanger-ggbentleyurihonjournalistjohnikonanporovnobserverxn--srfold-byaxn--srreisa-q1axn--srum-gratis-a-bulls-fanxn--stfold-9xaxn--stjrdal-s1axn--stjrdalshalsen-sqbeppublishproxyusuharavocatanzarowegroweiboltashkentatamotorsitestingivingjemnes3-eu-central-1kappleadpages-12hpalmspringsakerxn--stre-toten-zcbeskidyn-ip24xn--t60b56axn--tckweddingxn--tiq49xqyjelasticbeanstalkhmelnitskiyamarumorimachidaxn--tjme-hraxn--tn0agrocerydxn--tnsberg-q1axn--tor131oxn--trany-yuaxn--trentin-sd-tirol-rzbestbuyshoparenagareyamaizurugbyenvironmentalconservationflashdrivefsnillfjordiscordsezjampaleoceanographics3-website-eu-west-1xn--trentin-sdtirol-7vbetainaboxfuseekloges3-website-sa-east-1xn--trentino-sd-tirol-c3bhzcasertainaioirasebastopologyeongnamegawafflecellclstagemologicaliforniavoues3-eu-west-1xn--trentino-sdtirol-szbielawalbrzycharitypedreamhostersvp4xn--trentinosd-tirol-rzbiellaakesvuemieleccebizenakanotoddeninoheguriitatebayashiibahcavuotnagaivuotnagaokakyotambabybluebitelevisioncilla-speziaxarnetbank8s3-eu-west-2xn--trentinosdtirol-7vbieszczadygeyachimataijiiyamanouchikuhokuryugasakitaurayasudaxn--trentinsd-tirol-6vbievat-band-campaignieznombrendlyngengerdalces3-website-us-east-1xn--trentinsdtirol-nsbifukagawalesundiscountypeformelhusgardeninomiyakonojorpelandiscourses3-website-us-west-1xn--trgstad-r1axn--trna-woaxn--troms-zuaxn--tysvr-vraxn--uc0atvestre-slidrexn--uc0ay4axn--uist22halsakakinokiaxn--uisz3gxn--unjrga-rtarnobrzegyptianxn--unup4yxn--uuwu58axn--vads-jraxn--valle-aoste-ebbtularvikonskowolayangroupiemontexn--valle-d-aoste-ehboehringerikexn--valleaoste-e7axn--valledaoste-ebbvadsoccerxn--vard-jraxn--vegrshei-c0axn--vermgensberater-ctb-hostingxn--vermgensberatung-pwbigvalledaostaobaomoriguchiharag-cloud-championshiphoplixboxenirasakincheonishiazaindependent-commissionishigouvicasinordeste-idclkarasjohkamikitayamatsurindependent-inquest-a-la-masionishiharaxn--vestvgy-ixa6oxn--vg-yiabkhaziaxn--vgan-qoaxn--vgsy-qoa0jelenia-goraxn--vgu402cnsantabarbaraxn--vhquvestre-totennishiawakuraxn--vler-qoaxn--vre-eiker-k8axn--vrggt-xqadxn--vry-yla5gxn--vuq861biharstadotsubetsugaruhrxn--w4r85el8fhu5dnraxn--w4rs40lxn--wcvs22dxn--wgbh1cntjomeldaluroyxn--wgbl6axn--xhq521bihorologyusuisservegame-serverxn--xkc2al3hye2axn--xkc2dl3a5ee0hammarfeastafricaravantaaxn--y9a3aquariumintereitrentino-sudtirolxn--yer-znaumburgxn--yfro4i67oxn--ygarden-p1axn--ygbi2ammxn--4dbrk0cexn--ystre-slidre-ujbikedaejeonbukarasjokarasuyamarriottatsunoceanographiquehimejindependent-inquiryuufcfanishiizunazukindependent-panelomoliseminemrxn--zbx025dxn--zf0ao64axn--zf0avxlxn--zfr164bilbaogashimadachicagoboavistanbulsan-sudtirolbia-tempio-olbiatempioolbialystokkeliwebredirectme-south-1xnbayxz \ No newline at end of file +birkenesoddtangentinglogoweirbitbucketrzynishikatakayamatta-varjjatjomembersaltdalovepopartysfjordiskussionsbereichatinhlfanishikatsuragitappassenger-associationishikawazukamiokameokamakurazakitaurayasudabitternidisrechtrainingloomy-routerbjarkoybjerkreimdbalsan-suedtirololitapunkapsienamsskoganeibmdeveloperauniteroirmemorialombardiadempresashibetsukumiyamagasakinderoyonagunicloudevelopmentaxiijimarriottayninhaccanthobby-siteval-d-aosta-valleyoriikaracolognebinatsukigataiwanumatajimidsundgcahcesuolocustomer-ocimperiautoscanalytics-gatewayonagoyaveroykenflfanpachihayaakasakawaiishopitsitemasekd1kappenginedre-eikerimo-siemenscaledekaascolipicenoboribetsucks3-eu-west-3utilities-16-balestrandabergentappsseekloges3-eu-west-123paginawebcamauction-acornfshostrodawaraktyubinskaunicommbank123kotisivultrobjectselinogradimo-i-rana4u2-localhostrolekanieruchomoscientistordal-o-g-i-nikolaevents3-ap-northeast-2-ddnsking123homepagefrontappchizip61123saitamakawababia-goracleaningheannakadomarineat-urlimanowarudakuneustarostwodzislawdev-myqnapcloudcontrolledgesuite-stagingdyniamusementdllclstagehirnikonantomobelementorayokosukanoyakumoliserniaurland-4-salernord-aurdalipaywhirlimiteddnslivelanddnss3-ap-south-123siteweberlevagangaviikanonji234lima-cityeats3-ap-southeast-123webseiteambulancechireadmyblogspotaribeiraogakicks-assurfakefurniturealmpmninoheguribigawaurskog-holandinggfarsundds3-ap-southeast-20001wwwedeployokote123hjemmesidealerdalaheadjuegoshikibichuobiraustevollimombetsupplyokoze164-balena-devices3-ca-central-123websiteleaf-south-12hparliamentatsunobninsk8s3-eu-central-1337bjugnishimerablackfridaynightjxn--11b4c3ditchyouripatriabloombergretaijindustriesteinkjerbloxcmsaludivtasvuodnakaiwanairlinekobayashimodatecnologiablushakotanishinomiyashironomniwebview-assetsalvadorbmoattachmentsamegawabmsamnangerbmwellbeingzonebnrweatherchannelsdvrdnsamparalleluxenishinoomotegotsukishiwadavvenjargamvikarpaczest-a-la-maisondre-landivttasvuotnakamai-stagingloppennebomlocalzonebonavstackartuzybondigitaloceanspacesamsclubartowest1-usamsunglugsmall-webspacebookonlineboomlaakesvuemielecceboschristmasakilatiron-riopretoeidsvollovesickaruizawabostik-serverrankoshigayachtsandvikcoromantovalle-d-aostakinouebostonakijinsekikogentlentapisa-geekarumaifmemsetkmaxxn--12c1fe0bradescotksatmpaviancapitalonebouncemerckmsdscloudiybounty-fullensakerrypropertiesangovtoyosatoyokawaboutiquebecologialaichaugiangmbhartiengiangminakamichiharaboutireservdrangedalpusercontentoyotapfizerboyfriendoftheinternetflixn--12cfi8ixb8lublindesnesanjosoyrovnoticiasannanishinoshimattelemarkasaokamikitayamatsurinfinitigopocznore-og-uvdalucaniabozen-sudtiroluccanva-appstmnishiokoppegardray-dnsupdaterbozen-suedtirolukowesteuropencraftoyotomiyazakinsurealtypeformesswithdnsannohekinanporovigonohejinternationaluroybplacedogawarabikomaezakirunordkappgfoggiabrandrayddns5ybrasiliadboxoslockerbresciaogashimadachicappadovaapstemp-dnswatchest-mon-blogueurodirumagazinebrindisiciliabroadwaybroke-itvedestrandraydnsanokashibatakashimashikiyosatokigawabrokerbrothermesserlifestylebtimnetzpisdnpharmaciensantamariakebrowsersafetymarketingmodumetacentrumeteorappharmacymruovatlassian-dev-builderschaefflerbrumunddalutskashiharabrusselsantoandreclaimsanukintlon-2bryanskiptveterinaireadthedocsaobernardovre-eikerbrynebwestus2bzhitomirbzzwhitesnowflakecommunity-prochowicecomodalenissandoycompanyaarphdfcbankasumigaurawa-mazowszexn--1ck2e1bambinagisobetsuldalpha-myqnapcloudaccess3-us-east-2ixboxeroxfinityolasiteastus2comparemarkerryhotelsaves-the-whalessandria-trani-barletta-andriatranibarlettaandriacomsecaasnesoddeno-stagingrondarcondoshifteditorxn--1ctwolominamatarnobrzegrongrossetouchijiwadedyn-berlincolnissayokoshibahikariyaltakazakinzais-a-bookkeepermarshallstatebankasuyalibabahccavuotnagaraholtaleniwaizumiotsurugashimaintenanceomutazasavonarviikaminoyamaxunispaceconferenceconstructionflashdrivefsncf-ipfsaxoconsuladobeio-static-accesscamdvrcampaniaconsultantranoyconsultingroundhandlingroznysaitohnoshookuwanakayamangyshlakdnepropetrovskanlandyndns-freeboxostrowwlkpmgrphilipsyno-dschokokekscholarshipschoolbusinessebycontactivetrailcontagematsubaravendbambleborkdalvdalcest-le-patron-rancherkasydneyukuhashimokawavoues3-sa-east-1contractorskenissedalcookingruecoolblogdnsfor-better-thanhhoarairforcentralus-1cooperativano-frankivskodjeephonefosschoolsztynsetransiphotographysiocoproductionschulplattforminamiechizenisshingucciprianiigatairaumalatvuopmicrolightinguidefinimaringatlancastercorsicafjschulservercosenzakopanecosidnshome-webservercellikescandypopensocialcouchpotatofrieschwarzgwangjuh-ohtawaramotoineppueblockbusternopilawacouncilcouponscrapper-sitecozoravennaharimalborkaszubytemarketscrappinguitarscrysecretrosnubananarepublic-inquiryurihonjoyenthickaragandaxarnetbankanzakiwielunnerepairbusanagochigasakishimabarakawaharaolbia-tempio-olbiatempioolbialowiezachpomorskiengiangjesdalolipopmcdirepbodyn53cqcxn--1lqs03niyodogawacrankyotobetsumidaknongujaratmallcrdyndns-homednscwhminamifuranocreditcardyndns-iphutholdingservehttpbincheonl-ams-1creditunionionjukujitawaravpagecremonashorokanaiecrewhoswholidaycricketnedalcrimeast-kazakhstanangercrotonecrowniphuyencrsvp4cruiseservehumourcuisinellair-traffic-controllagdenesnaaseinet-freakserveircasertainaircraftingvolloansnasaarlanduponthewifidelitypedreamhostersaotomeldaluxurycuneocupcakecuritibacgiangiangryggeecurvalled-aostargets-itranslatedyndns-mailcutegirlfriendyndns-office-on-the-webhoptogurafedoraprojectransurlfeirafembetsukuis-a-bruinsfanfermodenakasatsunairportrapaniizaferraraferraris-a-bulls-fanferrerotikagoshimalopolskanittedalfetsundyndns-wikimobetsumitakagildeskaliszkolamericanfamilydservemp3fgunmaniwamannorth-kazakhstanfhvalerfilegear-augustowiiheyakagefilegear-deatnuniversitysvardofilegear-gbizfilegear-iefilegear-jpmorgangwonporterfilegear-sg-1filminamiizukamiminefinalchikugokasellfyis-a-candidatefinancefinnoyfirebaseappiemontefirenetlifylkesbiblackbaudcdn-edgestackhero-networkinggroupowiathletajimabaria-vungtaudiopsysharpigboatshawilliamhillfirenzefirestonefireweblikes-piedmontravelersinsurancefirmdalegalleryfishingoldpoint2thisamitsukefitjarfitnessettsurugiminamimakis-a-catererfjalerfkatsushikabeebyteappilottonsberguovdageaidnunjargausdalflekkefjordyndns-workservep2phxn--1lqs71dyndns-remotewdyndns-picserveminecraftransporteflesbergushikamifuranorthflankatsuyamashikokuchuoflickragerokunohealthcareershellflierneflirfloginlinefloppythonanywherealtorfloraflorencefloripalmasfjordenfloristanohatajiris-a-celticsfanfloromskogxn--2m4a15eflowershimokitayamafltravinhlonganflynnhosting-clusterfncashgabadaddjabbottoyourafndyndns1fnwkzfolldalfoolfor-ourfor-somegurownproviderfor-theaterfordebianforexrotheworkpccwinbar0emmafann-arborlandd-dnsiskinkyowariasahikawarszawashtenawsmppl-wawsglobalacceleratorahimeshimakanegasakievennodebalancern4t3l3p0rtatarantours3-ap-northeast-123minsidaarborteaches-yogano-ipifony-123miwebaccelastx4432-b-datacenterprisesakijobservableusercontentateshinanomachintaifun-dnsdojournalistoloseyouriparisor-fronavuotnarashinoharaetnabudejjunipereggio-emilia-romagnaroyboltateyamajureggiocalabriakrehamnayoro0o0forgotdnshimonitayanagithubpreviewsaikisarazure-mobileirfjordynnservepicservequakeforli-cesena-forlicesenaforlillehammerfeste-ipimientaketomisatoolshimonosekikawaforsalegoismailillesandefjordynservebbservesarcasmileforsandasuolodingenfortalfortefosneshimosuwalkis-a-chefashionstorebaseljordyndns-serverisignfotrdynulvikatowicefoxn--2scrj9casinordlandurbanamexnetgamersapporomurafozfr-1fr-par-1fr-par-2franamizuhoboleslawiecommerce-shoppingyeongnamdinhachijohanamakisofukushimaoris-a-conservativegarsheiheijis-a-cparachutingfredrikstadynv6freedesktopazimuthaibinhphuocelotenkawakayamagnetcieszynh-servebeero-stageiseiroumugifuchungbukharag-cloud-championshiphoplixn--30rr7yfreemyiphosteurovisionredumbrellangevagrigentobishimadridvagsoygardenebakkeshibechambagricoharugbydgoszczecin-berlindasdaburfreesitefreetlshimotsukefreisennankokubunjis-a-cubicle-slavellinodeobjectshimotsumafrenchkisshikindleikangerfreseniushinichinanfriuli-v-giuliafriuli-ve-giuliafriuli-vegiuliafriuli-venezia-giuliafriuli-veneziagiuliafriuli-vgiuliafriuliv-giuliafriulive-giuliafriulivegiuliafriulivenezia-giuliafriuliveneziagiuliafriulivgiuliafrlfroganshinjotelulubin-vpncateringebunkyonanaoshimamateramockashiwarafrognfrolandynvpnpluservicesevastopolitiendafrom-akamaized-stagingfrom-alfrom-arfrom-azurewebsiteshikagamiishibuyabukihokuizumobaragusabaerobaticketshinjukuleuvenicefrom-campobassociatest-iserveblogsytenrissadistdlibestadultrentin-sudtirolfrom-coachaseljeducationcillahppiacenzaganfrom-ctrentin-sued-tirolfrom-dcatfooddagestangefrom-decagliarikuzentakataikillfrom-flapymntrentin-suedtirolfrom-gap-east-1from-higashiagatsumagoianiafrom-iafrom-idyroyrvikingulenfrom-ilfrom-in-the-bandairtelebitbridgestonemurorangecloudplatform0from-kshinkamigototalfrom-kyfrom-langsonyantakahamalselveruminamiminowafrom-malvikaufentigerfrom-mdfrom-mein-vigorlicefrom-mifunefrom-mnfrom-modshinshinotsurgeryfrom-mshinshirofrom-mtnfrom-ncatholicurus-4from-ndfrom-nefrom-nhs-heilbronnoysundfrom-njshintokushimafrom-nminamioguni5from-nvalledaostargithubusercontentrentino-a-adigefrom-nycaxiaskvollpagesardegnarutolgaulardalvivanovoldafrom-ohdancefrom-okegawassamukawataris-a-democratrentino-aadigefrom-orfrom-panasonichernovtsykkylvenneslaskerrylogisticsardiniafrom-pratohmamurogawatsonrenderfrom-ris-a-designerimarugame-hostyhostingfrom-schmidtre-gauldalfrom-sdfrom-tnfrom-txn--32vp30hachinoheavyfrom-utsiracusagaeroclubmedecin-addrammenuorodoyerfrom-val-daostavalleyfrom-vtrentino-alto-adigefrom-wafrom-wiardwebthingsjcbnpparibashkiriafrom-wvallee-aosteroyfrom-wyfrosinonefrostabackplaneapplebesbyengerdalp1froyal-commissionfruskydivingfujiiderafujikawaguchikonefujiminokamoenairtrafficplexus-2fujinomiyadapliefujiokazakinkobearalvahkikonaibetsubame-south-1fujisatoshoeshintomikasaharafujisawafujishiroishidakabiratoridediboxn--3bst00minamisanrikubetsupportrentino-altoadigefujitsuruokakamigaharafujiyoshidappnodearthainguyenfukayabeardubaikawagoefukuchiyamadatsunanjoburgfukudomigawafukuis-a-doctorfukumitsubishigakirkeneshinyoshitomiokamisatokamachippubetsuikitchenfukuokakegawafukuroishikariwakunigamigrationfukusakirovogradoyfukuyamagatakaharunusualpersonfunabashiriuchinadattorelayfunagatakahashimamakiryuohkurafunahashikamiamakusatsumasendaisenergyeongginowaniihamatamakinoharafundfunkfeuerfuoiskujukuriyamandalfuosskoczowindowskrakowinefurubirafurudonordreisa-hockeynutwentertainmentrentino-s-tirolfurukawajimangolffanshiojirishirifujiedafusoctrangfussagamiharafutabayamaguchinomihachimanagementrentino-stirolfutboldlygoingnowhere-for-more-og-romsdalfuttsurutashinais-a-financialadvisor-aurdalfuturecmshioyamelhushirahamatonbetsurnadalfuturehostingfuturemailingfvghakuis-a-gurunzenhakusandnessjoenhaldenhalfmoonscalebookinghostedpictetrentino-sud-tirolhalsakakinokiaham-radio-opinbar1hamburghammarfeastasiahamurakamigoris-a-hard-workershiraokamisunagawahanamigawahanawahandavvesiidanangodaddyn-o-saurealestatefarmerseinehandcrafteducatorprojectrentino-sudtirolhangglidinghangoutrentino-sued-tirolhannannestadhannosegawahanoipinkazohanyuzenhappouzshiratakahagianghasamap-northeast-3hasaminami-alpshishikuis-a-hunterhashbanghasudazaifudaigodogadobeioruntimedio-campidano-mediocampidanomediohasura-appinokokamikoaniikappudopaashisogndalhasvikazteleportrentino-suedtirolhatogayahoooshikamagayaitakamoriokakudamatsuehatoyamazakitahiroshimarcheapartmentshisuifuettertdasnetzhatsukaichikaiseiyoichipshitaramahattfjelldalhayashimamotobusells-for-lesshizukuishimoichilloutsystemscloudsitehazuminobushibukawahelplfinancialhelsinkitakamiizumisanofidonnakamurataitogliattinnhemneshizuokamitondabayashiogamagoriziahemsedalhepforgeblockshoujis-a-knightpointtokaizukamaishikshacknetrentinoa-adigehetemlbfanhigashichichibuzentsujiiehigashihiroshimanehigashiizumozakitakatakanabeautychyattorneyagawakkanaioirasebastopoleangaviikadenagahamaroyhigashikagawahigashikagurasoedahigashikawakitaaikitakyushunantankazunovecorebungoonow-dnshowahigashikurumeinforumzhigashimatsushimarnardalhigashimatsuyamakitaakitadaitoigawahigashimurayamamotorcycleshowtimeloyhigashinarusells-for-uhigashinehigashiomitamanoshiroomghigashiosakasayamanakakogawahigashishirakawamatakanezawahigashisumiyoshikawaminamiaikitamihamadahigashitsunospamproxyhigashiurausukitamotosunnydayhigashiyamatokoriyamanashiibaclieu-1higashiyodogawahigashiyoshinogaris-a-landscaperspectakasakitanakagusukumoldeliveryhippyhiraizumisatohokkaidontexistmein-iservschulecznakaniikawatanagurahirakatashinagawahiranais-a-lawyerhirarahiratsukaeruhirayaizuwakamatsubushikusakadogawahitachiomiyaginozawaonsensiositehitachiotaketakaokalmykiahitraeumtgeradegreehjartdalhjelmelandholyhomegoodshwinnersiiitesilkddiamondsimple-urlhomeipioneerhomelinkyard-cloudjiffyresdalhomelinuxn--3ds443ghomeofficehomesecuritymacaparecidahomesecuritypchiryukyuragiizehomesenseeringhomeskleppippugliahomeunixn--3e0b707ehondahonjyoitakarazukaluganskfh-muensterhornindalhorsells-itrentinoaadigehortendofinternet-dnsimplesitehospitalhotelwithflightsirdalhotmailhoyangerhoylandetakasagooglecodespotrentinoalto-adigehungyenhurdalhurumajis-a-liberalhyllestadhyogoris-a-libertarianhyugawarahyundaiwafuneis-very-evillasalleitungsenis-very-goodyearis-very-niceis-very-sweetpepperugiais-with-thebandoomdnstraceisk01isk02jenv-arubacninhbinhdinhktistoryjeonnamegawajetztrentinostiroljevnakerjewelryjgorajlljls-sto1jls-sto2jls-sto3jmpixolinodeusercontentrentinosud-tiroljnjcloud-ver-jpchitosetogitsuliguriajoyokaichibahcavuotnagaivuotnagaokakyotambabymilk3jozis-a-musicianjpnjprsolarvikhersonlanxessolundbeckhmelnitskiyamasoykosaigawakosakaerodromegalloabatobamaceratachikawafaicloudineencoreapigeekoseis-a-painterhostsolutionslupskhakassiakosheroykoshimizumakis-a-patsfankoshughesomakosugekotohiradomainstitutekotourakouhokumakogenkounosupersalevangerkouyamasudakouzushimatrixn--3pxu8khplaystation-cloudyclusterkozagawakozakis-a-personaltrainerkozowiosomnarviklabudhabikinokawachinaganoharamcocottekpnkppspbarcelonagawakepnord-odalwaysdatabaseballangenkainanaejrietisalatinabenogiehtavuoatnaamesjevuemielnombrendlyngen-rootaruibxos3-us-gov-west-1krasnikahokutokonamegatakatoris-a-photographerokussldkrasnodarkredstonekrelliankristiansandcatsoowitdkmpspawnextdirectrentinosudtirolkristiansundkrodsheradkrokstadelvaldaostavangerkropyvnytskyis-a-playershiftcryptonomichinomiyakekryminamiyamashirokawanabelaudnedalnkumamotoyamatsumaebashimofusakatakatsukis-a-republicanonoichinosekigaharakumanowtvaokumatorinokumejimatsumotofukekumenanyokkaichirurgiens-dentistes-en-francekundenkunisakis-a-rockstarachowicekunitachiaraisaijolsterkunitomigusukukis-a-socialistgstagekunneppubtlsopotrentinosued-tirolkuokgroupizzakurgankurobegetmyipirangalluplidlugolekagaminorddalkurogimimozaokinawashirosatochiokinoshimagentositempurlkuroisodegaurakuromatsunais-a-soxfankuronkurotakikawasakis-a-studentalkushirogawakustanais-a-teacherkassyncloudkusuppliesor-odalkutchanelkutnokuzumakis-a-techietipslzkvafjordkvalsundkvamsterdamnserverbaniakvanangenkvinesdalkvinnheradkviteseidatingkvitsoykwpspdnsor-varangermishimatsusakahogirlymisugitokorozawamitakeharamitourismartlabelingmitoyoakemiuramiyazurecontainerdpoliticaobangmiyotamatsukuris-an-actormjondalenmonzabrianzaramonzaebrianzamonzaedellabrianzamordoviamorenapolicemoriyamatsuuramoriyoshiminamiashigaramormonstermoroyamatsuzakis-an-actressmushcdn77-sslingmortgagemoscowithgoogleapiszmoseushimogosenmosjoenmoskenesorreisahayakawakamiichikawamisatottoris-an-anarchistjordalshalsenmossortlandmosviknx-serversusakiyosupabaseminemotegit-reposoruminanomoviemovimientokyotangotembaixadattowebhareidsbergmozilla-iotrentinosuedtirolmtranbytomaridagawalmartrentinsud-tirolmuikaminokawanishiaizubangemukoelnmunakatanemuosattemupkomatsushimassa-carrara-massacarraramassabuzzmurmanskomforbar2murotorcraftranakatombetsumy-gatewaymusashinodesakegawamuseumincomcastoripressorfoldmusicapetownnews-stagingmutsuzawamy-vigormy-wanggoupilemyactivedirectorymyamazeplaymyasustor-elvdalmycdmycloudnsoundcastorjdevcloudfunctionsokndalmydattolocalcertificationmyddnsgeekgalaxymydissentrentinsudtirolmydobissmarterthanyoumydrobofageometre-experts-comptablesowamydspectruminisitemyeffectrentinsued-tirolmyfastly-edgekey-stagingmyfirewalledreplittlestargardmyforuminterecifedextraspace-to-rentalstomakomaibaramyfritzmyftpaccesspeedpartnermyhome-servermyjinomykolaivencloud66mymailermymediapchoseikarugalsacemyokohamamatsudamypeplatformsharis-an-artistockholmestrandmypetsphinxn--41amyphotoshibajddarvodkafjordvaporcloudmypictureshinomypsxn--42c2d9amysecuritycamerakermyshopblockspjelkavikommunalforbundmyshopifymyspreadshopselectrentinsuedtirolmytabitordermythic-beastspydebergmytis-a-anarchistg-buildermytuleap-partnersquaresindevicenzamyvnchoshichikashukudoyamakeuppermywirecipescaracallypoivronpokerpokrovskommunepolkowicepoltavalle-aostavernpomorzeszowithyoutuberspacekitagawaponpesaro-urbino-pesarourbinopesaromasvuotnaritakurashikis-bykleclerchitachinakagawaltervistaipeigersundynamic-dnsarlpordenonepornporsangerporsangugeporsgrunnanpoznanpraxihuanprdprgmrprimetelprincipeprivatelinkomonowruzhgorodeoprivatizehealthinsuranceprofesionalprogressivegasrlpromonza-e-della-brianzaptokuyamatsushigepropertysnesrvarggatrevisogneprotectionprotonetroandindependent-inquest-a-la-masionprudentialpruszkowiwatsukiyonotaireserve-onlineprvcyonabarumbriaprzeworskogpunyufuelpupulawypussycatanzarowixsitepvhachirogatakahatakaishimojis-a-geekautokeinotteroypvtrogstadpwchowderpzqhadanorthwesternmutualqldqotoyohashimotoshimaqponiatowadaqslgbtroitskomorotsukagawaqualifioapplatter-applatterplcube-serverquangngais-certifiedugit-pagespeedmobilizeroticaltanissettailscaleforcequangninhthuanquangtritonoshonais-foundationquickconnectromsakuragawaquicksytestreamlitapplumbingouvaresearchitectesrhtrentoyonakagyokutoyakomakizunokunimimatakasugais-an-engineeringquipelementstrippertuscanytushungrytuvalle-daostamayukis-into-animeiwamizawatuxfamilytuyenquangbinhthuantwmailvestnesuzukis-gonevestre-slidreggio-calabriavestre-totennishiawakuravestvagoyvevelstadvibo-valentiaavibovalentiavideovinhphuchromedicinagatorogerssarufutsunomiyawakasaikaitakokonoevinnicarbonia-iglesias-carboniaiglesiascarboniavinnytsiavipsinaapplurinacionalvirginanmokurennebuvirtual-userveexchangevirtualservervirtualuserveftpodhalevisakurais-into-carsnoasakuholeckodairaviterboliviajessheimmobilienvivianvivoryvixn--45br5cylvlaanderennesoyvladikavkazimierz-dolnyvladimirvlogintoyonezawavmintsorocabalashovhachiojiyahikobierzycevologdanskoninjambylvolvolkswagencyouvolyngdalvoorlopervossevangenvotevotingvotoyonovps-hostrowiechungnamdalseidfjordynathomebuiltwithdarkhangelskypecorittogojomeetoystre-slidrettozawawmemergencyahabackdropalermochizukikirarahkkeravjuwmflabsvalbardunloppadualstackomvuxn--3hcrj9chonanbuskerudynamisches-dnsarpsborgripeeweeklylotterywoodsidellogliastradingworse-thanhphohochiminhadselbuyshouseshirakolobrzegersundongthapmircloudletshiranukamishihorowowloclawekonskowolawawpdevcloudwpenginepoweredwphostedmailwpmucdnipropetrovskygearappodlasiellaknoluoktagajobojis-an-entertainerwpmudevcdnaccessojamparaglidingwritesthisblogoipodzonewroclawmcloudwsseoullensvanguardianwtcp4wtfastlylbanzaicloudappspotagereporthruherecreationinomiyakonojorpelandigickarasjohkameyamatotakadawuozuerichardlillywzmiuwajimaxn--4it797konsulatrobeepsondriobranconagareyamaizuruhrxn--4pvxs4allxn--54b7fta0ccistrondheimpertrixcdn77-secureadymadealstahaugesunderxn--55qw42gxn--55qx5dxn--5dbhl8dxn--5js045dxn--5rtp49citadelhichisochimkentozsdell-ogliastraderxn--5rtq34kontuminamiuonumatsunoxn--5su34j936bgsgxn--5tzm5gxn--6btw5axn--6frz82gxn--6orx2rxn--6qq986b3xlxn--7t0a264citicarrdrobakamaiorigin-stagingmxn--12co0c3b4evalleaostaobaomoriguchiharaffleentrycloudflare-ipfstcgroupaaskimitsubatamibulsan-suedtirolkuszczytnoopscbgrimstadrrxn--80aaa0cvacationsvchoyodobashichinohealth-carereforminamidaitomanaustdalxn--80adxhksveioxn--80ao21axn--80aqecdr1axn--80asehdbarclaycards3-us-west-1xn--80aswgxn--80aukraanghkeliwebpaaskoyabeagleboardxn--8dbq2axn--8ltr62konyvelohmusashimurayamassivegridxn--8pvr4uxn--8y0a063axn--90a1affinitylotterybnikeisencowayxn--90a3academiamicable-modemoneyxn--90aeroportsinfolionetworkangerxn--90aishobaraxn--90amckinseyxn--90azhytomyrxn--9dbq2axn--9et52uxn--9krt00axn--andy-iraxn--aroport-byanagawaxn--asky-iraxn--aurskog-hland-jnbarclays3-us-west-2xn--avery-yuasakurastoragexn--b-5gaxn--b4w605ferdxn--balsan-sdtirol-nsbsvelvikongsbergxn--bck1b9a5dre4civilaviationfabricafederation-webredirectmediatechnologyeongbukashiwazakiyosembokutamamuraxn--bdddj-mrabdxn--bearalvhki-y4axn--berlevg-jxaxn--bhcavuotna-s4axn--bhccavuotna-k7axn--bidr-5nachikatsuuraxn--bievt-0qa2xn--bjarky-fyanaizuxn--bjddar-ptarumizusawaxn--blt-elabcienciamallamaceiobbcn-north-1xn--bmlo-graingerxn--bod-2natalxn--bozen-sdtirol-2obanazawaxn--brnny-wuacademy-firewall-gatewayxn--brnnysund-m8accident-investigation-aptibleadpagesquare7xn--brum-voagatrustkanazawaxn--btsfjord-9zaxn--bulsan-sdtirol-nsbarefootballooningjovikarasjoketokashikiyokawaraxn--c1avgxn--c2br7gxn--c3s14misakis-a-therapistoiaxn--cck2b3baremetalombardyn-vpndns3-website-ap-northeast-1xn--cckwcxetdxn--cesena-forl-mcbremangerxn--cesenaforl-i8axn--cg4bkis-into-cartoonsokamitsuexn--ciqpnxn--clchc0ea0b2g2a9gcdxn--czr694bargainstantcloudfrontdoorestauranthuathienhuebinordre-landiherokuapparochernigovernmentjeldsundiscordsays3-website-ap-southeast-1xn--czrs0trvaroyxn--czru2dxn--czrw28barrel-of-knowledgeapplinziitatebayashijonawatebizenakanojoetsumomodellinglassnillfjordiscordsezgoraxn--d1acj3barrell-of-knowledgecomputermezproxyzgorzeleccoffeedbackanagawarmiastalowa-wolayangroupars3-website-ap-southeast-2xn--d1alfaststacksevenassigdalxn--d1atrysiljanxn--d5qv7z876clanbibaiduckdnsaseboknowsitallxn--davvenjrga-y4axn--djrs72d6uyxn--djty4koobindalxn--dnna-grajewolterskluwerxn--drbak-wuaxn--dyry-iraxn--e1a4cldmail-boxaxn--eckvdtc9dxn--efvn9svn-repostuff-4-salexn--efvy88haebaruericssongdalenviknaklodzkochikushinonsenasakuchinotsuchiurakawaxn--ehqz56nxn--elqq16hagakhanhhoabinhduongxn--eveni-0qa01gaxn--f6qx53axn--fct429kooris-a-nascarfanxn--fhbeiarnxn--finny-yuaxn--fiq228c5hsbcleverappsassarinuyamashinazawaxn--fiq64barsycenterprisecloudcontrolappgafanquangnamasteigenoamishirasatochigifts3-website-eu-west-1xn--fiqs8swidnicaravanylvenetogakushimotoganexn--fiqz9swidnikitagatakkomaganexn--fjord-lraxn--fjq720axn--fl-ziaxn--flor-jraxn--flw351exn--forl-cesena-fcbsswiebodzindependent-commissionxn--forlcesena-c8axn--fpcrj9c3dxn--frde-granexn--frna-woaxn--frya-hraxn--fzc2c9e2clickrisinglesjaguarxn--fzys8d69uvgmailxn--g2xx48clinicasacampinagrandebungotakadaemongolianishitosashimizunaminamiawajikintuitoyotsukaidownloadrudtvsaogoncapooguyxn--gckr3f0fastvps-serveronakanotoddenxn--gecrj9cliniquedaklakasamatsudoesntexisteingeekasserversicherungroks-theatrentin-sud-tirolxn--ggaviika-8ya47hagebostadxn--gildeskl-g0axn--givuotna-8yandexcloudxn--gjvik-wuaxn--gk3at1exn--gls-elacaixaxn--gmq050is-into-gamessinamsosnowieconomiasadojin-dslattuminamitanexn--gmqw5axn--gnstigbestellen-zvbrplsbxn--45brj9churcharterxn--gnstigliefern-wobihirosakikamijimayfirstorfjordxn--h-2failxn--h1ahnxn--h1alizxn--h2breg3eveneswinoujsciencexn--h2brj9c8clothingdustdatadetectrani-andria-barletta-trani-andriaxn--h3cuzk1dienbienxn--hbmer-xqaxn--hcesuolo-7ya35barsyonlinehimejiiyamanouchikujoinvilleirvikarasuyamashikemrevistathellequipmentjmaxxxjavald-aostatics3-website-sa-east-1xn--hebda8basicserversejny-2xn--hery-iraxn--hgebostad-g3axn--hkkinen-5waxn--hmmrfeasta-s4accident-prevention-k3swisstufftoread-booksnestudioxn--hnefoss-q1axn--hobl-iraxn--holtlen-hxaxn--hpmir-xqaxn--hxt814exn--hyanger-q1axn--hylandet-54axn--i1b6b1a6a2exn--imr513nxn--indery-fyaotsusonoxn--io0a7is-leetrentinoaltoadigexn--j1adpohlxn--j1aefauskedsmokorsetagayaseralingenovaraxn--j1ael8basilicataniaxn--j1amhaibarakisosakitahatakamatsukawaxn--j6w193gxn--jlq480n2rgxn--jlster-byasakaiminatoyookananiimiharuxn--jrpeland-54axn--jvr189misasaguris-an-accountantsmolaquilaocais-a-linux-useranishiaritabashikaoizumizakitashiobaraxn--k7yn95exn--karmy-yuaxn--kbrq7oxn--kcrx77d1x4axn--kfjord-iuaxn--klbu-woaxn--klt787dxn--kltp7dxn--kltx9axn--klty5xn--45q11circlerkstagentsasayamaxn--koluokta-7ya57haiduongxn--kprw13dxn--kpry57dxn--kput3is-lostre-toteneis-a-llamarumorimachidaxn--krager-gyasugitlabbvieeexn--kranghke-b0axn--krdsherad-m8axn--krehamn-dxaxn--krjohka-hwab49jdfastly-terrariuminamiiseharaxn--ksnes-uuaxn--kvfjord-nxaxn--kvitsy-fyasuokanmakiwakuratexn--kvnangen-k0axn--l-1fairwindsynology-diskstationxn--l1accentureklamborghinikkofuefukihabororosynology-dsuzakadnsaliastudynaliastrynxn--laheadju-7yatominamibosoftwarendalenugxn--langevg-jxaxn--lcvr32dxn--ldingen-q1axn--leagaviika-52basketballfinanzjaworznoticeableksvikaratsuginamikatagamilanotogawaxn--lesund-huaxn--lgbbat1ad8jejuxn--lgrd-poacctulaspeziaxn--lhppi-xqaxn--linds-pramericanexpresservegame-serverxn--loabt-0qaxn--lrdal-sraxn--lrenskog-54axn--lt-liacn-northwest-1xn--lten-granvindafjordxn--lury-iraxn--m3ch0j3axn--mely-iraxn--merker-kuaxn--mgb2ddesxn--mgb9awbfbsbxn--1qqw23axn--mgba3a3ejtunesuzukamogawaxn--mgba3a4f16axn--mgba3a4fra1-deloittexn--mgba7c0bbn0axn--mgbaakc7dvfsxn--mgbaam7a8haiphongonnakatsugawaxn--mgbab2bdxn--mgbah1a3hjkrdxn--mgbai9a5eva00batsfjordiscountry-snowplowiczeladzlgleezeu-2xn--mgbai9azgqp6jelasticbeanstalkharkovalleeaostexn--mgbayh7gparasitexn--mgbbh1a71exn--mgbc0a9azcgxn--mgbca7dzdoxn--mgbcpq6gpa1axn--mgberp4a5d4a87gxn--mgberp4a5d4arxn--mgbgu82axn--mgbi4ecexposedxn--mgbpl2fhskopervikhmelnytskyivalleedaostexn--mgbqly7c0a67fbcngroks-thisayamanobeatsaudaxn--mgbqly7cvafricargoboavistanbulsan-sudtirolxn--mgbt3dhdxn--mgbtf8flatangerxn--mgbtx2bauhauspostman-echofunatoriginstances3-website-us-east-1xn--mgbx4cd0abkhaziaxn--mix082fbx-osewienxn--mix891fbxosexyxn--mjndalen-64axn--mk0axindependent-inquiryxn--mk1bu44cnpyatigorskjervoyagexn--mkru45is-not-certifiedxn--mlatvuopmi-s4axn--mli-tlavagiskexn--mlselv-iuaxn--moreke-juaxn--mori-qsakuratanxn--mosjen-eyatsukannamihokksundxn--mot-tlavangenxn--mre-og-romsdal-qqbuservecounterstrikexn--msy-ula0hair-surveillancexn--mtta-vrjjat-k7aflakstadaokayamazonaws-cloud9guacuiababybluebiteckidsmynasushiobaracingrok-freeddnsfreebox-osascoli-picenogatabuseating-organicbcgjerdrumcprequalifymelbourneasypanelblagrarq-authgear-stagingjerstadeltaishinomakilovecollegefantasyleaguenoharauthgearappspacehosted-by-previderehabmereitattoolforgerockyombolzano-altoadigeorgeorgiauthordalandroideporteatonamidorivnebetsukubankanumazuryomitanocparmautocodebergamoarekembuchikumagayagawafflecelloisirs3-external-180reggioemiliaromagnarusawaustrheimbalsan-sudtirolivingitpagexlivornobserveregruhostingivestbyglandroverhalladeskjakamaiedge-stagingivingjemnes3-eu-west-2038xn--muost-0qaxn--mxtq1misawaxn--ngbc5azdxn--ngbe9e0axn--ngbrxn--4dbgdty6ciscofreakamaihd-stagingriwataraindroppdalxn--nit225koryokamikawanehonbetsuwanouchikuhokuryugasakis-a-nursellsyourhomeftpiwatexn--nmesjevuemie-tcbalatinord-frontierxn--nnx388axn--nodessakurawebsozais-savedxn--nqv7fs00emaxn--nry-yla5gxn--ntso0iqx3axn--ntsq17gxn--nttery-byaeservehalflifeinsurancexn--nvuotna-hwaxn--nyqy26axn--o1achernivtsicilynxn--4dbrk0cexn--o3cw4hakatanortonkotsunndalxn--o3cyx2axn--od0algardxn--od0aq3beneventodayusuharaxn--ogbpf8fldrvelvetromsohuissier-justicexn--oppegrd-ixaxn--ostery-fyatsushiroxn--osyro-wuaxn--otu796dxn--p1acfedjeezxn--p1ais-slickharkivallee-d-aostexn--pgbs0dhlx3xn--porsgu-sta26fedorainfraclouderaxn--pssu33lxn--pssy2uxn--q7ce6axn--q9jyb4cnsauheradyndns-at-homedepotenzamamicrosoftbankasukabedzin-brbalsfjordietgoryoshiokanravocats3-fips-us-gov-west-1xn--qcka1pmcpenzapposxn--qqqt11misconfusedxn--qxa6axn--qxamunexus-3xn--rady-iraxn--rdal-poaxn--rde-ulazioxn--rdy-0nabaris-uberleetrentinos-tirolxn--rennesy-v1axn--rhkkervju-01afedorapeoplefrakkestadyndns-webhostingujogaszxn--rholt-mragowoltlab-democraciaxn--rhqv96gxn--rht27zxn--rht3dxn--rht61exn--risa-5naturalxn--risr-iraxn--rland-uuaxn--rlingen-mxaxn--rmskog-byawaraxn--rny31hakodatexn--rovu88bentleyusuitatamotorsitestinglitchernihivgubs3-website-us-west-1xn--rros-graphicsxn--rskog-uuaxn--rst-0naturbruksgymnxn--rsta-framercanvasxn--rvc1e0am3exn--ryken-vuaxn--ryrvik-byawatahamaxn--s-1faitheshopwarezzoxn--s9brj9cntraniandriabarlettatraniandriaxn--sandnessjen-ogbentrendhostingliwiceu-3xn--sandy-yuaxn--sdtirol-n2axn--seral-lraxn--ses554gxn--sgne-graphoxn--4gbriminiserverxn--skierv-utazurestaticappspaceusercontentunkongsvingerxn--skjervy-v1axn--skjk-soaxn--sknit-yqaxn--sknland-fxaxn--slat-5navigationxn--slt-elabogadobeaemcloud-fr1xn--smla-hraxn--smna-gratangenxn--snase-nraxn--sndre-land-0cbeppublishproxyuufcfanirasakindependent-panelomonza-brianzaporizhzhedmarkarelianceu-4xn--snes-poaxn--snsa-roaxn--sr-aurdal-l8axn--sr-fron-q1axn--sr-odal-q1axn--sr-varanger-ggbeskidyn-ip24xn--srfold-byaxn--srreisa-q1axn--srum-gratis-a-bloggerxn--stfold-9xaxn--stjrdal-s1axn--stjrdalshalsen-sqbestbuyshoparenagasakikuchikuseihicampinashikiminohostfoldnavyuzawaxn--stre-toten-zcbetainaboxfuselfipartindependent-reviewegroweibolognagasukeu-north-1xn--t60b56axn--tckweddingxn--tiq49xqyjelenia-goraxn--tjme-hraxn--tn0agrocerydxn--tnsberg-q1axn--tor131oxn--trany-yuaxn--trentin-sd-tirol-rzbhzc66xn--trentin-sdtirol-7vbialystokkeymachineu-south-1xn--trentino-sd-tirol-c3bielawakuyachimataharanzanishiazaindielddanuorrindigenamerikawauevje-og-hornnes3-website-us-west-2xn--trentino-sdtirol-szbiella-speziaxn--trentinosd-tirol-rzbieszczadygeyachiyodaeguamfamscompute-1xn--trentinosdtirol-7vbievat-band-campaignieznoorstaplesakyotanabellunordeste-idclkarlsoyxn--trentinsd-tirol-6vbifukagawalbrzycharitydalomzaporizhzhiaxn--trentinsdtirol-nsbigv-infolkebiblegnicalvinklein-butterhcloudiscoursesalangenishigotpantheonsitexn--trgstad-r1axn--trna-woaxn--troms-zuaxn--tysvr-vraxn--uc0atventuresinstagingxn--uc0ay4axn--uist22hakonexn--uisz3gxn--unjrga-rtashkenturindalxn--unup4yxn--uuwu58axn--vads-jraxn--valle-aoste-ebbturystykaneyamazoexn--valle-d-aoste-ehboehringerikexn--valleaoste-e7axn--valledaoste-ebbvadsoccertmgreaterxn--vard-jraxn--vegrshei-c0axn--vermgensberater-ctb-hostingxn--vermgensberatung-pwbiharstadotsubetsugarulezajskiervaksdalondonetskarmoyxn--vestvgy-ixa6oxn--vg-yiabruzzombieidskogasawarackmazerbaijan-mayenbaidarmeniaxn--vgan-qoaxn--vgsy-qoa0jellybeanxn--vgu402coguchikuzenishiwakinvestmentsaveincloudyndns-at-workisboringsakershusrcfdyndns-blogsitexn--vhquvestfoldxn--vler-qoaxn--vre-eiker-k8axn--vrggt-xqadxn--vry-yla5gxn--vuq861bihoronobeokagakikugawalesundiscoverdalondrinaplesknsalon-1xn--w4r85el8fhu5dnraxn--w4rs40lxn--wcvs22dxn--wgbh1communexn--wgbl6axn--xhq521bikedaejeonbuk0xn--xkc2al3hye2axn--xkc2dl3a5ee0hakubackyardshiraois-a-greenxn--y9a3aquarelleasingxn--yer-znavois-very-badxn--yfro4i67oxn--ygarden-p1axn--ygbi2ammxn--4it168dxn--ystre-slidre-ujbiofficialorenskoglobodoes-itcouldbeworldishangrilamdongnairkitapps-audibleasecuritytacticsxn--0trq7p7nnishiharaxn--zbx025dxn--zf0ao64axn--zf0avxlxn--zfr164bipartsaloonishiizunazukindustriaxnbayernxz \ No newline at end of file diff --git a/vendor/golang.org/x/net/publicsuffix/table.go b/vendor/golang.org/x/net/publicsuffix/table.go index 6bdadcc44..78d400fa6 100644 --- a/vendor/golang.org/x/net/publicsuffix/table.go +++ b/vendor/golang.org/x/net/publicsuffix/table.go @@ -4,7 +4,7 @@ package publicsuffix import _ "embed" -const version = "publicsuffix.org's public_suffix_list.dat, git revision e248cbc92a527a166454afe9914c4c1b4253893f (2022-11-15T18:02:38Z)" +const version = "publicsuffix.org's public_suffix_list.dat, git revision 63cbc63d470d7b52c35266aa96c4c98c96ec499c (2023-08-03T10:01:25Z)" const ( nodesBits = 40 @@ -26,7 +26,7 @@ const ( ) // numTLD is the number of top level domains. -const numTLD = 1494 +const numTLD = 1474 // text is the combined text of all labels. // @@ -63,8 +63,8 @@ var nodes uint40String //go:embed data/children var children uint32String -// max children 718 (capacity 1023) -// max text offset 32976 (capacity 65535) -// max text length 36 (capacity 63) -// max hi 9656 (capacity 16383) -// max lo 9651 (capacity 16383) +// max children 743 (capacity 1023) +// max text offset 30876 (capacity 65535) +// max text length 31 (capacity 63) +// max hi 9322 (capacity 16383) +// max lo 9317 (capacity 16383) diff --git a/vendor/golang.org/x/sync/singleflight/singleflight.go b/vendor/golang.org/x/sync/singleflight/singleflight.go index 8473fb792..405183098 100644 --- a/vendor/golang.org/x/sync/singleflight/singleflight.go +++ b/vendor/golang.org/x/sync/singleflight/singleflight.go @@ -31,6 +31,15 @@ func (p *panicError) Error() string { return fmt.Sprintf("%v\n\n%s", p.value, p.stack) } +func (p *panicError) Unwrap() error { + err, ok := p.value.(error) + if !ok { + return nil + } + + return err +} + func newPanicError(v interface{}) error { stack := debug.Stack() diff --git a/vendor/golang.org/x/term/term_unix.go b/vendor/golang.org/x/term/term_unix.go index 62c2b3f41..1ad0ddfe3 100644 --- a/vendor/golang.org/x/term/term_unix.go +++ b/vendor/golang.org/x/term/term_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package term diff --git a/vendor/golang.org/x/term/term_unix_bsd.go b/vendor/golang.org/x/term/term_unix_bsd.go index 853b3d698..9dbf54629 100644 --- a/vendor/golang.org/x/term/term_unix_bsd.go +++ b/vendor/golang.org/x/term/term_unix_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package term diff --git a/vendor/golang.org/x/term/term_unix_other.go b/vendor/golang.org/x/term/term_unix_other.go index 1e8955c93..1b36de799 100644 --- a/vendor/golang.org/x/term/term_unix_other.go +++ b/vendor/golang.org/x/term/term_unix_other.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || linux || solaris || zos -// +build aix linux solaris zos package term diff --git a/vendor/golang.org/x/term/term_unsupported.go b/vendor/golang.org/x/term/term_unsupported.go index f1df85065..3c409e588 100644 --- a/vendor/golang.org/x/term/term_unsupported.go +++ b/vendor/golang.org/x/term/term_unsupported.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !zos && !windows && !solaris && !plan9 -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!zos,!windows,!solaris,!plan9 package term diff --git a/vendor/golang.org/x/text/cases/icu.go b/vendor/golang.org/x/text/cases/icu.go index 2dc84b39e..db7c237cc 100644 --- a/vendor/golang.org/x/text/cases/icu.go +++ b/vendor/golang.org/x/text/cases/icu.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build icu -// +build icu package cases diff --git a/vendor/golang.org/x/text/cases/tables10.0.0.go b/vendor/golang.org/x/text/cases/tables10.0.0.go index ca9923105..bd28ae145 100644 --- a/vendor/golang.org/x/text/cases/tables10.0.0.go +++ b/vendor/golang.org/x/text/cases/tables10.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.10 && !go1.13 -// +build go1.10,!go1.13 package cases diff --git a/vendor/golang.org/x/text/cases/tables11.0.0.go b/vendor/golang.org/x/text/cases/tables11.0.0.go index b1106b417..ce00ce372 100644 --- a/vendor/golang.org/x/text/cases/tables11.0.0.go +++ b/vendor/golang.org/x/text/cases/tables11.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.13 && !go1.14 -// +build go1.13,!go1.14 package cases diff --git a/vendor/golang.org/x/text/cases/tables12.0.0.go b/vendor/golang.org/x/text/cases/tables12.0.0.go index ae7dc2407..84d841b14 100644 --- a/vendor/golang.org/x/text/cases/tables12.0.0.go +++ b/vendor/golang.org/x/text/cases/tables12.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.14 && !go1.16 -// +build go1.14,!go1.16 package cases diff --git a/vendor/golang.org/x/text/cases/tables13.0.0.go b/vendor/golang.org/x/text/cases/tables13.0.0.go index 68d2981d1..6187e6b46 100644 --- a/vendor/golang.org/x/text/cases/tables13.0.0.go +++ b/vendor/golang.org/x/text/cases/tables13.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.16 && !go1.21 -// +build go1.16,!go1.21 package cases diff --git a/vendor/golang.org/x/text/cases/tables15.0.0.go b/vendor/golang.org/x/text/cases/tables15.0.0.go index e431b9953..aee0f3108 100644 --- a/vendor/golang.org/x/text/cases/tables15.0.0.go +++ b/vendor/golang.org/x/text/cases/tables15.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.21 -// +build go1.21 package cases diff --git a/vendor/golang.org/x/text/cases/tables9.0.0.go b/vendor/golang.org/x/text/cases/tables9.0.0.go index 636d5d14d..3aeb7be6d 100644 --- a/vendor/golang.org/x/text/cases/tables9.0.0.go +++ b/vendor/golang.org/x/text/cases/tables9.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build !go1.10 -// +build !go1.10 package cases diff --git a/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go b/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go index 8a7392c4a..784bb8808 100644 --- a/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go +++ b/vendor/golang.org/x/text/secure/bidirule/bidirule10.0.0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.10 -// +build go1.10 package bidirule diff --git a/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go b/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go index bb0a92001..8e1e94395 100644 --- a/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go +++ b/vendor/golang.org/x/text/secure/bidirule/bidirule9.0.0.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.10 -// +build !go1.10 package bidirule diff --git a/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go index 42fa8d72c..d2bd71181 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables10.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.10 && !go1.13 -// +build go1.10,!go1.13 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go index 56a0e1ea2..f76bdca27 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables11.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.13 && !go1.14 -// +build go1.13,!go1.14 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go index baacf32b4..3aa2c3bdf 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables12.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.14 && !go1.16 -// +build go1.14,!go1.16 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go index ffadb7beb..a71375790 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables13.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.16 && !go1.21 -// +build go1.16,!go1.21 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables15.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables15.0.0.go index 92cce5802..f15746f7d 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables15.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables15.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.21 -// +build go1.21 package bidi diff --git a/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go b/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go index f517fdb20..c164d3791 100644 --- a/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go +++ b/vendor/golang.org/x/text/unicode/bidi/tables9.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build !go1.10 -// +build !go1.10 package bidi diff --git a/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go index f5a078827..1af161c75 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.10 && !go1.13 -// +build go1.10,!go1.13 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go index cb7239c43..eb73ecc37 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables11.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.13 && !go1.14 -// +build go1.13,!go1.14 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go index 11b273300..276cb8d8c 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables12.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.14 && !go1.16 -// +build go1.14,!go1.16 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go index f65785e8a..0cceffd73 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables13.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.16 && !go1.21 -// +build go1.16,!go1.21 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables15.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables15.0.0.go index e1858b879..b0819e42d 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables15.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables15.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.21 -// +build go1.21 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go b/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go index 0175eae50..bf65457d9 100644 --- a/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go +++ b/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build !go1.10 -// +build !go1.10 package norm diff --git a/vendor/golang.org/x/text/unicode/norm/trie.go b/vendor/golang.org/x/text/unicode/norm/trie.go index 423386bf4..e4250ae22 100644 --- a/vendor/golang.org/x/text/unicode/norm/trie.go +++ b/vendor/golang.org/x/text/unicode/norm/trie.go @@ -29,7 +29,7 @@ var ( nfkcData = newNfkcTrie(0) ) -// lookupValue determines the type of block n and looks up the value for b. +// lookup determines the type of block n and looks up the value for b. // For n < t.cutoff, the block is a simple lookup table. Otherwise, the block // is a list of ranges with an accompanying value. Given a matching range r, // the value for b is by r.value + (b - r.lo) * stride. diff --git a/vendor/golang.org/x/tools/cmd/goimports/goimports.go b/vendor/golang.org/x/tools/cmd/goimports/goimports.go index b354c9e82..3b6bd7250 100644 --- a/vendor/golang.org/x/tools/cmd/goimports/goimports.go +++ b/vendor/golang.org/x/tools/cmd/goimports/goimports.go @@ -13,7 +13,6 @@ import ( "go/scanner" exec "golang.org/x/sys/execabs" "io" - "io/ioutil" "log" "os" "path/filepath" @@ -106,7 +105,7 @@ func processFile(filename string, in io.Reader, out io.Writer, argType argumentT in = f } - src, err := ioutil.ReadAll(in) + src, err := io.ReadAll(in) if err != nil { return err } @@ -159,7 +158,7 @@ func processFile(filename string, in io.Reader, out io.Writer, argType argumentT if fi, err := os.Stat(filename); err == nil { perms = fi.Mode() & os.ModePerm } - err = ioutil.WriteFile(filename, res, perms) + err = os.WriteFile(filename, res, perms) if err != nil { return err } @@ -296,7 +295,7 @@ func gofmtMain() { } func writeTempFile(dir, prefix string, data []byte) (string, error) { - file, err := ioutil.TempFile(dir, prefix) + file, err := os.CreateTemp(dir, prefix) if err != nil { return "", err } diff --git a/vendor/golang.org/x/tools/go/packages/doc.go b/vendor/golang.org/x/tools/go/packages/doc.go index da4ab89fe..a7a8f73e3 100644 --- a/vendor/golang.org/x/tools/go/packages/doc.go +++ b/vendor/golang.org/x/tools/go/packages/doc.go @@ -35,7 +35,7 @@ The Package struct provides basic information about the package, including - Imports, a map from source import strings to the Packages they name; - Types, the type information for the package's exported symbols; - Syntax, the parsed syntax trees for the package's source code; and - - TypeInfo, the result of a complete type-check of the package syntax trees. + - TypesInfo, the result of a complete type-check of the package syntax trees. (See the documentation for type Package for the complete list of fields and more detailed descriptions.) diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go index b5de9cf9f..1f1eade0a 100644 --- a/vendor/golang.org/x/tools/go/packages/golist.go +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -9,7 +9,6 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "log" "os" "path" @@ -1109,7 +1108,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err if len(state.cfg.Overlay) == 0 { return "", func() {}, nil } - dir, err := ioutil.TempDir("", "gopackages-*") + dir, err := os.MkdirTemp("", "gopackages-*") if err != nil { return "", nil, err } @@ -1128,7 +1127,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err // Create a unique filename for the overlaid files, to avoid // creating nested directories. noSeparator := strings.Join(strings.Split(filepath.ToSlash(k), "/"), "") - f, err := ioutil.TempFile(dir, fmt.Sprintf("*-%s", noSeparator)) + f, err := os.CreateTemp(dir, fmt.Sprintf("*-%s", noSeparator)) if err != nil { return "", func() {}, err } @@ -1146,7 +1145,7 @@ func (state *golistState) writeOverlays() (filename string, cleanup func(), err } // Write out the overlay file that contains the filepath mappings. filename = filepath.Join(dir, "overlay.json") - if err := ioutil.WriteFile(filename, b, 0665); err != nil { + if err := os.WriteFile(filename, b, 0665); err != nil { return "", func() {}, err } return filename, cleanup, nil diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go index 124a6fe14..ece0e7c60 100644 --- a/vendor/golang.org/x/tools/go/packages/packages.go +++ b/vendor/golang.org/x/tools/go/packages/packages.go @@ -16,7 +16,6 @@ import ( "go/token" "go/types" "io" - "io/ioutil" "log" "os" "path/filepath" @@ -1127,7 +1126,7 @@ func (ld *loader) parseFile(filename string) (*ast.File, error) { var err error if src == nil { ioLimit <- true // wait - src, err = ioutil.ReadFile(filename) + src, err = os.ReadFile(filename) <-ioLimit // signal } if err != nil { diff --git a/vendor/golang.org/x/tools/imports/forward.go b/vendor/golang.org/x/tools/imports/forward.go index d2547c743..cb6db8893 100644 --- a/vendor/golang.org/x/tools/imports/forward.go +++ b/vendor/golang.org/x/tools/imports/forward.go @@ -7,8 +7,8 @@ package imports // import "golang.org/x/tools/imports" import ( - "io/ioutil" "log" + "os" "golang.org/x/tools/internal/gocommand" intimp "golang.org/x/tools/internal/imports" @@ -44,7 +44,7 @@ var LocalPrefix string func Process(filename string, src []byte, opt *Options) ([]byte, error) { var err error if src == nil { - src, err = ioutil.ReadFile(filename) + src, err = os.ReadFile(filename) if err != nil { return nil, err } diff --git a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_portable.go b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_portable.go index 085d31160..27e860243 100644 --- a/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_portable.go +++ b/vendor/golang.org/x/tools/internal/fastwalk/fastwalk_portable.go @@ -8,7 +8,6 @@ package fastwalk import ( - "io/ioutil" "os" ) @@ -17,16 +16,20 @@ import ( // If fn returns a non-nil error, readDir returns with that error // immediately. func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error { - fis, err := ioutil.ReadDir(dirName) + fis, err := os.ReadDir(dirName) if err != nil { return err } skipFiles := false for _, fi := range fis { - if fi.Mode().IsRegular() && skipFiles { + info, err := fi.Info() + if err != nil { + return err + } + if info.Mode().IsRegular() && skipFiles { continue } - if err := fn(dirName, fi.Name(), fi.Mode()&os.ModeType); err != nil { + if err := fn(dirName, fi.Name(), info.Mode()&os.ModeType); err != nil { if err == ErrSkipFiles { skipFiles = true continue diff --git a/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go b/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go index b1223713b..2d078ccb1 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go @@ -29,7 +29,6 @@ import ( "go/token" "go/types" "io" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -221,7 +220,7 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func switch hdr { case "$$B\n": var data []byte - data, err = ioutil.ReadAll(buf) + data, err = io.ReadAll(buf) if err != nil { break } diff --git a/vendor/golang.org/x/tools/internal/imports/fix.go b/vendor/golang.org/x/tools/internal/imports/fix.go index d4f1b4e8a..01e8ba5fa 100644 --- a/vendor/golang.org/x/tools/internal/imports/fix.go +++ b/vendor/golang.org/x/tools/internal/imports/fix.go @@ -13,6 +13,7 @@ import ( "go/build" "go/parser" "go/token" + "io/fs" "io/ioutil" "os" "path" @@ -107,7 +108,7 @@ func parseOtherFiles(fset *token.FileSet, srcDir, filename string) []*ast.File { considerTests := strings.HasSuffix(filename, "_test.go") fileBase := filepath.Base(filename) - packageFileInfos, err := ioutil.ReadDir(srcDir) + packageFileInfos, err := os.ReadDir(srcDir) if err != nil { return nil } @@ -1469,11 +1470,11 @@ func VendorlessPath(ipath string) string { func loadExportsFromFiles(ctx context.Context, env *ProcessEnv, dir string, includeTest bool) (string, []string, error) { // Look for non-test, buildable .go files which could provide exports. - all, err := ioutil.ReadDir(dir) + all, err := os.ReadDir(dir) if err != nil { return "", nil, err } - var files []os.FileInfo + var files []fs.DirEntry for _, fi := range all { name := fi.Name() if !strings.HasSuffix(name, ".go") || (!includeTest && strings.HasSuffix(name, "_test.go")) { diff --git a/vendor/golang.org/x/tools/internal/imports/mod.go b/vendor/golang.org/x/tools/internal/imports/mod.go index 977d2389d..5f4d435d3 100644 --- a/vendor/golang.org/x/tools/internal/imports/mod.go +++ b/vendor/golang.org/x/tools/internal/imports/mod.go @@ -9,7 +9,6 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "os" "path" "path/filepath" @@ -265,7 +264,7 @@ func (r *ModuleResolver) findPackage(importPath string) (*gocommand.ModuleJSON, } // Not cached. Read the filesystem. - pkgFiles, err := ioutil.ReadDir(pkgDir) + pkgFiles, err := os.ReadDir(pkgDir) if err != nil { continue } @@ -370,7 +369,7 @@ func (r *ModuleResolver) dirIsNestedModule(dir string, mod *gocommand.ModuleJSON func (r *ModuleResolver) modInfo(dir string) (modDir string, modName string) { readModName := func(modFile string) string { - modBytes, err := ioutil.ReadFile(modFile) + modBytes, err := os.ReadFile(modFile) if err != nil { return "" } diff --git a/vendor/golang.org/x/xerrors/LICENSE b/vendor/golang.org/x/xerrors/LICENSE deleted file mode 100644 index e4a47e17f..000000000 --- a/vendor/golang.org/x/xerrors/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2019 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/xerrors/PATENTS b/vendor/golang.org/x/xerrors/PATENTS deleted file mode 100644 index 733099041..000000000 --- a/vendor/golang.org/x/xerrors/PATENTS +++ /dev/null @@ -1,22 +0,0 @@ -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the Go project. - -Google hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) -patent license to make, have made, use, offer to sell, sell, import, -transfer and otherwise run, modify and propagate the contents of this -implementation of Go, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of Go. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of Go or any code incorporated within this -implementation of Go constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of Go -shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/xerrors/README b/vendor/golang.org/x/xerrors/README deleted file mode 100644 index aac7867a5..000000000 --- a/vendor/golang.org/x/xerrors/README +++ /dev/null @@ -1,2 +0,0 @@ -This repository holds the transition packages for the new Go 1.13 error values. -See golang.org/design/29934-error-values. diff --git a/vendor/golang.org/x/xerrors/adaptor.go b/vendor/golang.org/x/xerrors/adaptor.go deleted file mode 100644 index 4317f2483..000000000 --- a/vendor/golang.org/x/xerrors/adaptor.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xerrors - -import ( - "bytes" - "fmt" - "io" - "reflect" - "strconv" -) - -// FormatError calls the FormatError method of f with an errors.Printer -// configured according to s and verb, and writes the result to s. -func FormatError(f Formatter, s fmt.State, verb rune) { - // Assuming this function is only called from the Format method, and given - // that FormatError takes precedence over Format, it cannot be called from - // any package that supports errors.Formatter. It is therefore safe to - // disregard that State may be a specific printer implementation and use one - // of our choice instead. - - // limitations: does not support printing error as Go struct. - - var ( - sep = " " // separator before next error - p = &state{State: s} - direct = true - ) - - var err error = f - - switch verb { - // Note that this switch must match the preference order - // for ordinary string printing (%#v before %+v, and so on). - - case 'v': - if s.Flag('#') { - if stringer, ok := err.(fmt.GoStringer); ok { - io.WriteString(&p.buf, stringer.GoString()) - goto exit - } - // proceed as if it were %v - } else if s.Flag('+') { - p.printDetail = true - sep = "\n - " - } - case 's': - case 'q', 'x', 'X': - // Use an intermediate buffer in the rare cases that precision, - // truncation, or one of the alternative verbs (q, x, and X) are - // specified. - direct = false - - default: - p.buf.WriteString("%!") - p.buf.WriteRune(verb) - p.buf.WriteByte('(') - switch { - case err != nil: - p.buf.WriteString(reflect.TypeOf(f).String()) - default: - p.buf.WriteString("") - } - p.buf.WriteByte(')') - io.Copy(s, &p.buf) - return - } - -loop: - for { - switch v := err.(type) { - case Formatter: - err = v.FormatError((*printer)(p)) - case fmt.Formatter: - v.Format(p, 'v') - break loop - default: - io.WriteString(&p.buf, v.Error()) - break loop - } - if err == nil { - break - } - if p.needColon || !p.printDetail { - p.buf.WriteByte(':') - p.needColon = false - } - p.buf.WriteString(sep) - p.inDetail = false - p.needNewline = false - } - -exit: - width, okW := s.Width() - prec, okP := s.Precision() - - if !direct || (okW && width > 0) || okP { - // Construct format string from State s. - format := []byte{'%'} - if s.Flag('-') { - format = append(format, '-') - } - if s.Flag('+') { - format = append(format, '+') - } - if s.Flag(' ') { - format = append(format, ' ') - } - if okW { - format = strconv.AppendInt(format, int64(width), 10) - } - if okP { - format = append(format, '.') - format = strconv.AppendInt(format, int64(prec), 10) - } - format = append(format, string(verb)...) - fmt.Fprintf(s, string(format), p.buf.String()) - } else { - io.Copy(s, &p.buf) - } -} - -var detailSep = []byte("\n ") - -// state tracks error printing state. It implements fmt.State. -type state struct { - fmt.State - buf bytes.Buffer - - printDetail bool - inDetail bool - needColon bool - needNewline bool -} - -func (s *state) Write(b []byte) (n int, err error) { - if s.printDetail { - if len(b) == 0 { - return 0, nil - } - if s.inDetail && s.needColon { - s.needNewline = true - if b[0] == '\n' { - b = b[1:] - } - } - k := 0 - for i, c := range b { - if s.needNewline { - if s.inDetail && s.needColon { - s.buf.WriteByte(':') - s.needColon = false - } - s.buf.Write(detailSep) - s.needNewline = false - } - if c == '\n' { - s.buf.Write(b[k:i]) - k = i + 1 - s.needNewline = true - } - } - s.buf.Write(b[k:]) - if !s.inDetail { - s.needColon = true - } - } else if !s.inDetail { - s.buf.Write(b) - } - return len(b), nil -} - -// printer wraps a state to implement an xerrors.Printer. -type printer state - -func (s *printer) Print(args ...interface{}) { - if !s.inDetail || s.printDetail { - fmt.Fprint((*state)(s), args...) - } -} - -func (s *printer) Printf(format string, args ...interface{}) { - if !s.inDetail || s.printDetail { - fmt.Fprintf((*state)(s), format, args...) - } -} - -func (s *printer) Detail() bool { - s.inDetail = true - return s.printDetail -} diff --git a/vendor/golang.org/x/xerrors/codereview.cfg b/vendor/golang.org/x/xerrors/codereview.cfg deleted file mode 100644 index 3f8b14b64..000000000 --- a/vendor/golang.org/x/xerrors/codereview.cfg +++ /dev/null @@ -1 +0,0 @@ -issuerepo: golang/go diff --git a/vendor/golang.org/x/xerrors/doc.go b/vendor/golang.org/x/xerrors/doc.go deleted file mode 100644 index 2ef99f5a8..000000000 --- a/vendor/golang.org/x/xerrors/doc.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package xerrors implements functions to manipulate errors. -// -// This package is based on the Go 2 proposal for error values: -// -// https://golang.org/design/29934-error-values -// -// These functions were incorporated into the standard library's errors package -// in Go 1.13: -// - Is -// - As -// - Unwrap -// -// Also, Errorf's %w verb was incorporated into fmt.Errorf. -// -// Use this package to get equivalent behavior in all supported Go versions. -// -// No other features of this package were included in Go 1.13, and at present -// there are no plans to include any of them. -package xerrors // import "golang.org/x/xerrors" diff --git a/vendor/golang.org/x/xerrors/errors.go b/vendor/golang.org/x/xerrors/errors.go deleted file mode 100644 index e88d3772d..000000000 --- a/vendor/golang.org/x/xerrors/errors.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xerrors - -import "fmt" - -// errorString is a trivial implementation of error. -type errorString struct { - s string - frame Frame -} - -// New returns an error that formats as the given text. -// -// The returned error contains a Frame set to the caller's location and -// implements Formatter to show this information when printed with details. -func New(text string) error { - return &errorString{text, Caller(1)} -} - -func (e *errorString) Error() string { - return e.s -} - -func (e *errorString) Format(s fmt.State, v rune) { FormatError(e, s, v) } - -func (e *errorString) FormatError(p Printer) (next error) { - p.Print(e.s) - e.frame.Format(p) - return nil -} diff --git a/vendor/golang.org/x/xerrors/fmt.go b/vendor/golang.org/x/xerrors/fmt.go deleted file mode 100644 index 27a5d70bd..000000000 --- a/vendor/golang.org/x/xerrors/fmt.go +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xerrors - -import ( - "fmt" - "strings" - "unicode" - "unicode/utf8" - - "golang.org/x/xerrors/internal" -) - -const percentBangString = "%!" - -// Errorf formats according to a format specifier and returns the string as a -// value that satisfies error. -// -// The returned error includes the file and line number of the caller when -// formatted with additional detail enabled. If the last argument is an error -// the returned error's Format method will return it if the format string ends -// with ": %s", ": %v", or ": %w". If the last argument is an error and the -// format string ends with ": %w", the returned error implements an Unwrap -// method returning it. -// -// If the format specifier includes a %w verb with an error operand in a -// position other than at the end, the returned error will still implement an -// Unwrap method returning the operand, but the error's Format method will not -// return the wrapped error. -// -// It is invalid to include more than one %w verb or to supply it with an -// operand that does not implement the error interface. The %w verb is otherwise -// a synonym for %v. -// -// Note that as of Go 1.13, the fmt.Errorf function will do error formatting, -// but it will not capture a stack backtrace. -func Errorf(format string, a ...interface{}) error { - format = formatPlusW(format) - // Support a ": %[wsv]" suffix, which works well with xerrors.Formatter. - wrap := strings.HasSuffix(format, ": %w") - idx, format2, ok := parsePercentW(format) - percentWElsewhere := !wrap && idx >= 0 - if !percentWElsewhere && (wrap || strings.HasSuffix(format, ": %s") || strings.HasSuffix(format, ": %v")) { - err := errorAt(a, len(a)-1) - if err == nil { - return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)} - } - // TODO: this is not entirely correct. The error value could be - // printed elsewhere in format if it mixes numbered with unnumbered - // substitutions. With relatively small changes to doPrintf we can - // have it optionally ignore extra arguments and pass the argument - // list in its entirety. - msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...) - frame := Frame{} - if internal.EnableTrace { - frame = Caller(1) - } - if wrap { - return &wrapError{msg, err, frame} - } - return &noWrapError{msg, err, frame} - } - // Support %w anywhere. - // TODO: don't repeat the wrapped error's message when %w occurs in the middle. - msg := fmt.Sprintf(format2, a...) - if idx < 0 { - return &noWrapError{msg, nil, Caller(1)} - } - err := errorAt(a, idx) - if !ok || err == nil { - // Too many %ws or argument of %w is not an error. Approximate the Go - // 1.13 fmt.Errorf message. - return &noWrapError{fmt.Sprintf("%sw(%s)", percentBangString, msg), nil, Caller(1)} - } - frame := Frame{} - if internal.EnableTrace { - frame = Caller(1) - } - return &wrapError{msg, err, frame} -} - -func errorAt(args []interface{}, i int) error { - if i < 0 || i >= len(args) { - return nil - } - err, ok := args[i].(error) - if !ok { - return nil - } - return err -} - -// formatPlusW is used to avoid the vet check that will barf at %w. -func formatPlusW(s string) string { - return s -} - -// Return the index of the only %w in format, or -1 if none. -// Also return a rewritten format string with %w replaced by %v, and -// false if there is more than one %w. -// TODO: handle "%[N]w". -func parsePercentW(format string) (idx int, newFormat string, ok bool) { - // Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go. - idx = -1 - ok = true - n := 0 - sz := 0 - var isW bool - for i := 0; i < len(format); i += sz { - if format[i] != '%' { - sz = 1 - continue - } - // "%%" is not a format directive. - if i+1 < len(format) && format[i+1] == '%' { - sz = 2 - continue - } - sz, isW = parsePrintfVerb(format[i:]) - if isW { - if idx >= 0 { - ok = false - } else { - idx = n - } - // "Replace" the last character, the 'w', with a 'v'. - p := i + sz - 1 - format = format[:p] + "v" + format[p+1:] - } - n++ - } - return idx, format, ok -} - -// Parse the printf verb starting with a % at s[0]. -// Return how many bytes it occupies and whether the verb is 'w'. -func parsePrintfVerb(s string) (int, bool) { - // Assume only that the directive is a sequence of non-letters followed by a single letter. - sz := 0 - var r rune - for i := 1; i < len(s); i += sz { - r, sz = utf8.DecodeRuneInString(s[i:]) - if unicode.IsLetter(r) { - return i + sz, r == 'w' - } - } - return len(s), false -} - -type noWrapError struct { - msg string - err error - frame Frame -} - -func (e *noWrapError) Error() string { - return fmt.Sprint(e) -} - -func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } - -func (e *noWrapError) FormatError(p Printer) (next error) { - p.Print(e.msg) - e.frame.Format(p) - return e.err -} - -type wrapError struct { - msg string - err error - frame Frame -} - -func (e *wrapError) Error() string { - return fmt.Sprint(e) -} - -func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } - -func (e *wrapError) FormatError(p Printer) (next error) { - p.Print(e.msg) - e.frame.Format(p) - return e.err -} - -func (e *wrapError) Unwrap() error { - return e.err -} diff --git a/vendor/golang.org/x/xerrors/format.go b/vendor/golang.org/x/xerrors/format.go deleted file mode 100644 index 1bc9c26b9..000000000 --- a/vendor/golang.org/x/xerrors/format.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xerrors - -// A Formatter formats error messages. -type Formatter interface { - error - - // FormatError prints the receiver's first error and returns the next error in - // the error chain, if any. - FormatError(p Printer) (next error) -} - -// A Printer formats error messages. -// -// The most common implementation of Printer is the one provided by package fmt -// during Printf (as of Go 1.13). Localization packages such as golang.org/x/text/message -// typically provide their own implementations. -type Printer interface { - // Print appends args to the message output. - Print(args ...interface{}) - - // Printf writes a formatted string. - Printf(format string, args ...interface{}) - - // Detail reports whether error detail is requested. - // After the first call to Detail, all text written to the Printer - // is formatted as additional detail, or ignored when - // detail has not been requested. - // If Detail returns false, the caller can avoid printing the detail at all. - Detail() bool -} diff --git a/vendor/golang.org/x/xerrors/frame.go b/vendor/golang.org/x/xerrors/frame.go deleted file mode 100644 index 0de628ec5..000000000 --- a/vendor/golang.org/x/xerrors/frame.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xerrors - -import ( - "runtime" -) - -// A Frame contains part of a call stack. -type Frame struct { - // Make room for three PCs: the one we were asked for, what it called, - // and possibly a PC for skipPleaseUseCallersFrames. See: - // https://go.googlesource.com/go/+/032678e0fb/src/runtime/extern.go#169 - frames [3]uintptr -} - -// Caller returns a Frame that describes a frame on the caller's stack. -// The argument skip is the number of frames to skip over. -// Caller(0) returns the frame for the caller of Caller. -func Caller(skip int) Frame { - var s Frame - runtime.Callers(skip+1, s.frames[:]) - return s -} - -// location reports the file, line, and function of a frame. -// -// The returned function may be "" even if file and line are not. -func (f Frame) location() (function, file string, line int) { - frames := runtime.CallersFrames(f.frames[:]) - if _, ok := frames.Next(); !ok { - return "", "", 0 - } - fr, ok := frames.Next() - if !ok { - return "", "", 0 - } - return fr.Function, fr.File, fr.Line -} - -// Format prints the stack as error detail. -// It should be called from an error's Format implementation -// after printing any other error detail. -func (f Frame) Format(p Printer) { - if p.Detail() { - function, file, line := f.location() - if function != "" { - p.Printf("%s\n ", function) - } - if file != "" { - p.Printf("%s:%d\n", file, line) - } - } -} diff --git a/vendor/golang.org/x/xerrors/internal/internal.go b/vendor/golang.org/x/xerrors/internal/internal.go deleted file mode 100644 index 89f4eca5d..000000000 --- a/vendor/golang.org/x/xerrors/internal/internal.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package internal - -// EnableTrace indicates whether stack information should be recorded in errors. -var EnableTrace = true diff --git a/vendor/golang.org/x/xerrors/wrap.go b/vendor/golang.org/x/xerrors/wrap.go deleted file mode 100644 index 9842758ca..000000000 --- a/vendor/golang.org/x/xerrors/wrap.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xerrors - -import ( - "reflect" -) - -// A Wrapper provides context around another error. -type Wrapper interface { - // Unwrap returns the next error in the error chain. - // If there is no next error, Unwrap returns nil. - Unwrap() error -} - -// Opaque returns an error with the same error formatting as err -// but that does not match err and cannot be unwrapped. -func Opaque(err error) error { - return noWrapper{err} -} - -type noWrapper struct { - error -} - -func (e noWrapper) FormatError(p Printer) (next error) { - if f, ok := e.error.(Formatter); ok { - return f.FormatError(p) - } - p.Print(e.error) - return nil -} - -// Unwrap returns the result of calling the Unwrap method on err, if err implements -// Unwrap. Otherwise, Unwrap returns nil. -// -// Deprecated: As of Go 1.13, use errors.Unwrap instead. -func Unwrap(err error) error { - u, ok := err.(Wrapper) - if !ok { - return nil - } - return u.Unwrap() -} - -// Is reports whether any error in err's chain matches target. -// -// An error is considered to match a target if it is equal to that target or if -// it implements a method Is(error) bool such that Is(target) returns true. -// -// Deprecated: As of Go 1.13, use errors.Is instead. -func Is(err, target error) bool { - if target == nil { - return err == target - } - - isComparable := reflect.TypeOf(target).Comparable() - for { - if isComparable && err == target { - return true - } - if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) { - return true - } - // TODO: consider supporing target.Is(err). This would allow - // user-definable predicates, but also may allow for coping with sloppy - // APIs, thereby making it easier to get away with them. - if err = Unwrap(err); err == nil { - return false - } - } -} - -// As finds the first error in err's chain that matches the type to which target -// points, and if so, sets the target to its value and returns true. An error -// matches a type if it is assignable to the target type, or if it has a method -// As(interface{}) bool such that As(target) returns true. As will panic if target -// is not a non-nil pointer to a type which implements error or is of interface type. -// -// The As method should set the target to its value and return true if err -// matches the type to which target points. -// -// Deprecated: As of Go 1.13, use errors.As instead. -func As(err error, target interface{}) bool { - if target == nil { - panic("errors: target cannot be nil") - } - val := reflect.ValueOf(target) - typ := val.Type() - if typ.Kind() != reflect.Ptr || val.IsNil() { - panic("errors: target must be a non-nil pointer") - } - if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) { - panic("errors: *target must be interface or implement error") - } - targetType := typ.Elem() - for err != nil { - if reflect.TypeOf(err).AssignableTo(targetType) { - val.Elem().Set(reflect.ValueOf(err)) - return true - } - if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) { - return true - } - err = Unwrap(err) - } - return false -} - -var errorType = reflect.TypeOf((*error)(nil)).Elem() diff --git a/vendor/modules.txt b/vendor/modules.txt index a1959a9a0..93fc18f25 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# github.com/BurntSushi/toml v1.2.1 +# github.com/BurntSushi/toml v1.3.2 ## explicit; go 1.16 github.com/BurntSushi/toml github.com/BurntSushi/toml/internal @@ -179,7 +179,7 @@ github.com/containerd/cgroups/stats/v1 # github.com/coreos/go-systemd/v22 v22.5.0 ## explicit; go 1.12 github.com/coreos/go-systemd/v22/dbus -# github.com/cpuguy83/go-md2man/v2 v2.0.2 +# github.com/cpuguy83/go-md2man/v2 v2.0.4 ## explicit; go 1.11 github.com/cpuguy83/go-md2man/v2/md2man # github.com/cruxic/go-hmac-drbg v0.0.0-20170206035330-84c46983886d @@ -349,8 +349,6 @@ github.com/golang/groupcache/singleflight # github.com/golang/mock v1.6.0 ## explicit; go 1.11 github.com/golang/mock/gomock -github.com/golang/mock/mockgen -github.com/golang/mock/mockgen/model # github.com/golang/protobuf v1.5.3 ## explicit; go 1.9 github.com/golang/protobuf/proto @@ -367,7 +365,7 @@ github.com/google/btree # github.com/google/gopacket v1.1.19 ## explicit; go 1.12 github.com/google/gopacket/routing -# github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 +# github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b ## explicit; go 1.19 github.com/google/pprof/profile # github.com/google/uuid v1.3.0 @@ -399,9 +397,10 @@ github.com/hashicorp/go-version ## explicit; go 1.12 github.com/hashicorp/golang-lru github.com/hashicorp/golang-lru/simplelru -# github.com/hashicorp/golang-lru/v2 v2.0.2 +# github.com/hashicorp/golang-lru/v2 v2.0.5 ## explicit; go 1.18 github.com/hashicorp/golang-lru/v2 +github.com/hashicorp/golang-lru/v2/internal github.com/hashicorp/golang-lru/v2/simplelru # github.com/holiman/bloomfilter/v2 v2.0.3 ## explicit; go 1.15 @@ -412,7 +411,7 @@ github.com/holiman/uint256 # github.com/huandu/xstrings v1.3.2 ## explicit; go 1.12 github.com/huandu/xstrings -# github.com/huin/goupnp v1.2.0 +# github.com/huin/goupnp v1.3.0 ## explicit; go 1.14 github.com/huin/goupnp github.com/huin/goupnp/dcps/internetgateway1 @@ -452,7 +451,7 @@ github.com/keighl/metabolize # github.com/kilic/bls12-381 v0.0.0-20200607163746-32e1441c8a9f ## explicit; go 1.12 github.com/kilic/bls12-381 -# github.com/klauspost/compress v1.16.7 +# github.com/klauspost/compress v1.17.2 ## explicit; go 1.18 github.com/klauspost/compress github.com/klauspost/compress/fse @@ -499,8 +498,8 @@ github.com/libp2p/go-cidranger/net # github.com/libp2p/go-flow-metrics v0.1.0 ## explicit; go 1.17 github.com/libp2p/go-flow-metrics -# github.com/libp2p/go-libp2p v0.29.2 -## explicit; go 1.19 +# github.com/libp2p/go-libp2p v0.32.2 +## explicit; go 1.20 github.com/libp2p/go-libp2p github.com/libp2p/go-libp2p/config github.com/libp2p/go-libp2p/core/canonicallog @@ -526,6 +525,7 @@ github.com/libp2p/go-libp2p/core/sec github.com/libp2p/go-libp2p/core/sec/insecure github.com/libp2p/go-libp2p/core/sec/insecure/pb github.com/libp2p/go-libp2p/core/transport +github.com/libp2p/go-libp2p/internal/sha256 github.com/libp2p/go-libp2p/p2p/discovery/backoff github.com/libp2p/go-libp2p/p2p/host/autonat github.com/libp2p/go-libp2p/p2p/host/autonat/pb @@ -540,7 +540,6 @@ github.com/libp2p/go-libp2p/p2p/host/relaysvc github.com/libp2p/go-libp2p/p2p/host/resource-manager github.com/libp2p/go-libp2p/p2p/host/routed github.com/libp2p/go-libp2p/p2p/metricshelper -github.com/libp2p/go-libp2p/p2p/muxer/mplex github.com/libp2p/go-libp2p/p2p/muxer/yamux github.com/libp2p/go-libp2p/p2p/net/connmgr github.com/libp2p/go-libp2p/p2p/net/nat @@ -569,8 +568,11 @@ github.com/libp2p/go-libp2p/p2p/transport/webtransport # github.com/libp2p/go-libp2p-asn-util v0.3.0 ## explicit; go 1.19 github.com/libp2p/go-libp2p-asn-util -# github.com/libp2p/go-libp2p-pubsub v0.9.3 -## explicit; go 1.19 +# github.com/libp2p/go-libp2p-mplex v0.9.0 +## explicit; go 1.20 +github.com/libp2p/go-libp2p-mplex +# github.com/libp2p/go-libp2p-pubsub v0.10.1 +## explicit; go 1.20 github.com/libp2p/go-libp2p-pubsub github.com/libp2p/go-libp2p-pubsub/pb github.com/libp2p/go-libp2p-pubsub/timecache @@ -588,8 +590,8 @@ github.com/libp2p/go-nat # github.com/libp2p/go-netroute v0.2.1 ## explicit; go 1.18 github.com/libp2p/go-netroute -# github.com/libp2p/go-reuseport v0.3.0 -## explicit; go 1.19 +# github.com/libp2p/go-reuseport v0.4.0 +## explicit; go 1.20 github.com/libp2p/go-reuseport # github.com/libp2p/go-yamux/v4 v4.0.1 ## explicit; go 1.18 @@ -612,7 +614,7 @@ github.com/mat/besticon/ico # github.com/mattn/go-colorable v0.1.8 ## explicit; go 1.13 github.com/mattn/go-colorable -# github.com/mattn/go-isatty v0.0.19 +# github.com/mattn/go-isatty v0.0.20 ## explicit; go 1.15 github.com/mattn/go-isatty # github.com/mattn/go-runewidth v0.0.13 @@ -624,7 +626,7 @@ github.com/matttproud/golang_protobuf_extensions/pbutil # github.com/meirf/gopart v0.0.0-20180520194036-37e9492a85a8 ## explicit github.com/meirf/gopart -# github.com/miekg/dns v1.1.55 +# github.com/miekg/dns v1.1.56 ## explicit; go 1.19 github.com/miekg/dns # github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b @@ -654,8 +656,8 @@ github.com/multiformats/go-base32 # github.com/multiformats/go-base36 v0.2.0 ## explicit; go 1.18 github.com/multiformats/go-base36 -# github.com/multiformats/go-multiaddr v0.10.1 -## explicit; go 1.19 +# github.com/multiformats/go-multiaddr v0.12.3 +## explicit; go 1.21 github.com/multiformats/go-multiaddr github.com/multiformats/go-multiaddr/net # github.com/multiformats/go-multiaddr-dns v0.3.1 @@ -681,8 +683,8 @@ github.com/multiformats/go-multihash/register/miniosha256 github.com/multiformats/go-multihash/register/murmur3 github.com/multiformats/go-multihash/register/sha256 github.com/multiformats/go-multihash/register/sha3 -# github.com/multiformats/go-multistream v0.4.1 -## explicit; go 1.19 +# github.com/multiformats/go-multistream v0.5.0 +## explicit; go 1.20 github.com/multiformats/go-multistream # github.com/multiformats/go-varint v0.0.7 ## explicit; go 1.18 @@ -702,7 +704,7 @@ github.com/olekukonko/tablewriter # github.com/oliamb/cutter v0.2.2 ## explicit github.com/oliamb/cutter -# github.com/onsi/ginkgo/v2 v2.11.0 +# github.com/onsi/ginkgo/v2 v2.13.0 ## explicit; go 1.18 github.com/onsi/ginkgo/v2/config github.com/onsi/ginkgo/v2/formatter @@ -720,7 +722,7 @@ github.com/onsi/ginkgo/v2/internal/interrupt_handler github.com/onsi/ginkgo/v2/internal/parallel_support github.com/onsi/ginkgo/v2/reporters github.com/onsi/ginkgo/v2/types -# github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 +# github.com/opencontainers/runtime-spec v1.1.0 ## explicit github.com/opencontainers/runtime-spec/specs-go # github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 @@ -729,13 +731,14 @@ github.com/pbnjay/memory # github.com/pborman/uuid v1.2.0 ## explicit github.com/pborman/uuid -# github.com/pion/datachannel v1.5.2 +# github.com/pion/datachannel v1.5.5 ## explicit; go 1.13 github.com/pion/datachannel -# github.com/pion/dtls/v2 v2.1.2 +# github.com/pion/dtls/v2 v2.2.7 ## explicit; go 1.13 github.com/pion/dtls/v2 github.com/pion/dtls/v2/internal/ciphersuite +github.com/pion/dtls/v2/internal/ciphersuite/types github.com/pion/dtls/v2/internal/closer github.com/pion/dtls/v2/internal/util github.com/pion/dtls/v2/pkg/crypto/ccm @@ -752,52 +755,61 @@ github.com/pion/dtls/v2/pkg/protocol/alert github.com/pion/dtls/v2/pkg/protocol/extension github.com/pion/dtls/v2/pkg/protocol/handshake github.com/pion/dtls/v2/pkg/protocol/recordlayer -# github.com/pion/ice/v2 v2.1.20 +# github.com/pion/ice/v2 v2.3.6 ## explicit; go 1.13 github.com/pion/ice/v2 -# github.com/pion/interceptor v0.1.7 +github.com/pion/ice/v2/internal/atomic +github.com/pion/ice/v2/internal/fakenet +github.com/pion/ice/v2/internal/stun +# github.com/pion/interceptor v0.1.17 ## explicit; go 1.15 github.com/pion/interceptor +github.com/pion/interceptor/internal/ntp github.com/pion/interceptor/pkg/nack github.com/pion/interceptor/pkg/report github.com/pion/interceptor/pkg/twcc # github.com/pion/logging v0.2.2 ## explicit; go 1.12 github.com/pion/logging -# github.com/pion/mdns v0.0.5 +# github.com/pion/mdns v0.0.7 ## explicit; go 1.12 github.com/pion/mdns # github.com/pion/randutil v0.1.0 ## explicit; go 1.14 github.com/pion/randutil -# github.com/pion/rtcp v1.2.9 +# github.com/pion/rtcp v1.2.10 ## explicit; go 1.13 github.com/pion/rtcp -# github.com/pion/rtp v1.7.4 +# github.com/pion/rtp v1.7.13 ## explicit; go 1.13 github.com/pion/rtp github.com/pion/rtp/codecs -# github.com/pion/sctp v1.8.2 +github.com/pion/rtp/pkg/obu +# github.com/pion/sctp v1.8.7 ## explicit; go 1.13 github.com/pion/sctp -# github.com/pion/sdp/v3 v3.0.4 +# github.com/pion/sdp/v3 v3.0.6 ## explicit; go 1.13 github.com/pion/sdp/v3 -# github.com/pion/srtp/v2 v2.0.5 +# github.com/pion/srtp/v2 v2.0.15 ## explicit; go 1.14 github.com/pion/srtp/v2 -# github.com/pion/stun v0.3.5 +# github.com/pion/stun v0.6.0 ## explicit; go 1.12 github.com/pion/stun github.com/pion/stun/internal/hmac -# github.com/pion/transport v0.13.0 +# github.com/pion/transport/v2 v2.2.1 ## explicit; go 1.12 -github.com/pion/transport/connctx -github.com/pion/transport/deadline -github.com/pion/transport/packetio -github.com/pion/transport/replaydetector -github.com/pion/transport/vnet -# github.com/pion/turn/v2 v2.0.6 +github.com/pion/transport/v2 +github.com/pion/transport/v2/connctx +github.com/pion/transport/v2/deadline +github.com/pion/transport/v2/packetio +github.com/pion/transport/v2/replaydetector +github.com/pion/transport/v2/stdnet +github.com/pion/transport/v2/udp +github.com/pion/transport/v2/utils/xor +github.com/pion/transport/v2/vnet +# github.com/pion/turn/v2 v2.1.0 ## explicit; go 1.13 github.com/pion/turn/v2 github.com/pion/turn/v2/internal/allocation @@ -805,10 +817,7 @@ github.com/pion/turn/v2/internal/client github.com/pion/turn/v2/internal/ipnet github.com/pion/turn/v2/internal/proto github.com/pion/turn/v2/internal/server -# github.com/pion/udp v0.1.1 -## explicit; go 1.14 -github.com/pion/udp -# github.com/pion/webrtc/v3 v3.1.24-0.20220208053747-94262c1b2b38 +# github.com/pion/webrtc/v3 v3.2.9 ## explicit; go 1.13 github.com/pion/webrtc/v3 github.com/pion/webrtc/v3/internal/fmtp @@ -846,14 +855,11 @@ github.com/prometheus/tsdb/fileutil # github.com/quic-go/qpack v0.4.0 ## explicit; go 1.18 github.com/quic-go/qpack -# github.com/quic-go/qtls-go1-19 v0.3.3 -## explicit; go 1.19 -github.com/quic-go/qtls-go1-19 -# github.com/quic-go/qtls-go1-20 v0.2.3 +# github.com/quic-go/qtls-go1-20 v0.3.4 ## explicit; go 1.20 github.com/quic-go/qtls-go1-20 -# github.com/quic-go/quic-go v0.36.4 -## explicit; go 1.19 +# github.com/quic-go/quic-go v0.39.4 +## explicit; go 1.20 github.com/quic-go/quic-go github.com/quic-go/quic-go/http3 github.com/quic-go/quic-go/internal/ackhandler @@ -871,8 +877,8 @@ github.com/quic-go/quic-go/internal/wire github.com/quic-go/quic-go/logging github.com/quic-go/quic-go/qlog github.com/quic-go/quic-go/quicvarint -# github.com/quic-go/webtransport-go v0.5.3 -## explicit; go 1.18 +# github.com/quic-go/webtransport-go v0.6.0 +## explicit; go 1.20 github.com/quic-go/webtransport-go # github.com/raulk/go-watchdog v1.3.0 ## explicit; go 1.15 @@ -952,7 +958,7 @@ github.com/status-im/mvds/state/migrations github.com/status-im/mvds/store github.com/status-im/mvds/store/migrations github.com/status-im/mvds/transport -# github.com/status-im/rendezvous v1.3.7 +# github.com/status-im/rendezvous v1.3.8-0.20240110194857-cc5be22bf83e ## explicit; go 1.18 github.com/status-im/rendezvous github.com/status-im/rendezvous/protocol @@ -974,11 +980,11 @@ github.com/status-im/zxcvbn-go/match github.com/status-im/zxcvbn-go/matching github.com/status-im/zxcvbn-go/scoring github.com/status-im/zxcvbn-go/utils/math -# github.com/stretchr/objx v0.5.0 -## explicit; go 1.12 -github.com/stretchr/objx -# github.com/stretchr/testify v1.8.4 +# github.com/stretchr/objx v0.5.2 ## explicit; go 1.20 +github.com/stretchr/objx +# github.com/stretchr/testify v1.9.0 +## explicit; go 1.17 github.com/stretchr/testify/assert github.com/stretchr/testify/mock github.com/stretchr/testify/require @@ -1010,21 +1016,21 @@ github.com/tsenart/tb ## explicit; go 1.14 github.com/tyler-smith/go-bip39 github.com/tyler-smith/go-bip39/wordlists -# github.com/urfave/cli/v2 v2.24.4 +# github.com/urfave/cli/v2 v2.27.2 ## explicit; go 1.18 github.com/urfave/cli/v2 -# github.com/waku-org/go-discover v0.0.0-20240129014929-85f2c00b96a3 +# github.com/waku-org/go-discover v0.0.0-20240506173252-4912704efdc5 ## explicit; go 1.15 github.com/waku-org/go-discover/discover github.com/waku-org/go-discover/discover/v4wire github.com/waku-org/go-discover/discover/v5wire -# github.com/waku-org/go-libp2p-rendezvous v0.0.0-20230628220917-7b4e5ae4c0e7 +# github.com/waku-org/go-libp2p-rendezvous v0.0.0-20240110193335-a67d1cc760a0 ## explicit; go 1.19 github.com/waku-org/go-libp2p-rendezvous github.com/waku-org/go-libp2p-rendezvous/db github.com/waku-org/go-libp2p-rendezvous/pb -# github.com/waku-org/go-waku v0.8.1-0.20240415131212-6d889ca3e2fe -## explicit; go 1.19 +# github.com/waku-org/go-waku v0.8.1-0.20240507175626-19d27befd98b +## explicit; go 1.20 github.com/waku-org/go-waku/logging github.com/waku-org/go-waku/waku/persistence github.com/waku-org/go-waku/waku/v2/discv5 @@ -1038,8 +1044,8 @@ github.com/waku-org/go-waku/waku/v2/protocol github.com/waku-org/go-waku/waku/v2/protocol/enr github.com/waku-org/go-waku/waku/v2/protocol/filter github.com/waku-org/go-waku/waku/v2/protocol/filter/pb -github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter -github.com/waku-org/go-waku/waku/v2/protocol/legacy_filter/pb +github.com/waku-org/go-waku/waku/v2/protocol/legacy_store +github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb github.com/waku-org/go-waku/waku/v2/protocol/lightpush github.com/waku-org/go-waku/waku/v2/protocol/lightpush/pb github.com/waku-org/go-waku/waku/v2/protocol/metadata @@ -1121,8 +1127,8 @@ github.com/xeipuuv/gojsonreference # github.com/xeipuuv/gojsonschema v1.2.0 ## explicit github.com/xeipuuv/gojsonschema -# github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 -## explicit +# github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 +## explicit; go 1.15 github.com/xrash/smetrics # github.com/yeqown/go-qrcode/v2 v2.2.1 ## explicit; go 1.18 @@ -1147,25 +1153,29 @@ go.etcd.io/bbolt # go.uber.org/atomic v1.11.0 ## explicit; go 1.18 go.uber.org/atomic -# go.uber.org/dig v1.17.0 -## explicit; go 1.18 +# go.uber.org/dig v1.17.1 +## explicit; go 1.20 go.uber.org/dig go.uber.org/dig/internal/digerror go.uber.org/dig/internal/digreflect go.uber.org/dig/internal/dot go.uber.org/dig/internal/graph -# go.uber.org/fx v1.20.0 -## explicit; go 1.19 +# go.uber.org/fx v1.20.1 +## explicit; go 1.20 go.uber.org/fx go.uber.org/fx/fxevent go.uber.org/fx/internal/fxclock go.uber.org/fx/internal/fxlog go.uber.org/fx/internal/fxreflect go.uber.org/fx/internal/lifecycle +# go.uber.org/mock v0.3.0 +## explicit; go 1.20 +go.uber.org/mock/mockgen +go.uber.org/mock/mockgen/model # go.uber.org/multierr v1.11.0 ## explicit; go 1.19 go.uber.org/multierr -# go.uber.org/zap v1.24.0 +# go.uber.org/zap v1.27.0 ## explicit; go 1.19 go.uber.org/zap go.uber.org/zap/buffer @@ -1173,9 +1183,11 @@ go.uber.org/zap/internal go.uber.org/zap/internal/bufferpool go.uber.org/zap/internal/color go.uber.org/zap/internal/exit +go.uber.org/zap/internal/pool +go.uber.org/zap/internal/stacktrace go.uber.org/zap/zapcore -# golang.org/x/crypto v0.12.0 -## explicit; go 1.17 +# golang.org/x/crypto v0.18.0 +## explicit; go 1.18 golang.org/x/crypto/blake2b golang.org/x/crypto/blake2s golang.org/x/crypto/chacha20 @@ -1193,10 +1205,11 @@ golang.org/x/crypto/salsa20/salsa golang.org/x/crypto/scrypt golang.org/x/crypto/sha3 golang.org/x/crypto/ssh/terminal -# golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 +# golang.org/x/exp v0.0.0-20231006140011-7918f672742d ## explicit; go 1.20 golang.org/x/exp/constraints golang.org/x/exp/maps +golang.org/x/exp/rand golang.org/x/exp/slices # golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb ## explicit; go 1.12 @@ -1213,13 +1226,13 @@ golang.org/x/image/vector golang.org/x/image/vp8 golang.org/x/image/vp8l golang.org/x/image/webp -# golang.org/x/mod v0.12.0 -## explicit; go 1.17 +# golang.org/x/mod v0.13.0 +## explicit; go 1.18 golang.org/x/mod/internal/lazyregexp golang.org/x/mod/modfile golang.org/x/mod/module golang.org/x/mod/semver -# golang.org/x/net v0.14.0 +# golang.org/x/net v0.17.0 ## explicit; go 1.17 golang.org/x/net/bpf golang.org/x/net/dns/dnsmessage @@ -1237,7 +1250,7 @@ golang.org/x/net/ipv6 golang.org/x/net/proxy golang.org/x/net/publicsuffix golang.org/x/net/route -# golang.org/x/sync v0.3.0 +# golang.org/x/sync v0.4.0 ## explicit; go 1.17 golang.org/x/sync/errgroup golang.org/x/sync/singleflight @@ -1248,11 +1261,11 @@ golang.org/x/sys/execabs golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.11.0 -## explicit; go 1.17 +# golang.org/x/term v0.16.0 +## explicit; go 1.18 golang.org/x/term -# golang.org/x/text v0.12.0 -## explicit; go 1.17 +# golang.org/x/text v0.14.0 +## explicit; go 1.18 golang.org/x/text/cases golang.org/x/text/encoding golang.org/x/text/encoding/charmap @@ -1278,7 +1291,7 @@ golang.org/x/text/unicode/norm # golang.org/x/time v0.0.0-20220922220347-f3bd1da661af ## explicit golang.org/x/time/rate -# golang.org/x/tools v0.12.1-0.20230818130535-1517d1a3ba60 +# golang.org/x/tools v0.14.0 ## explicit; go 1.18 golang.org/x/tools/cmd/goimports golang.org/x/tools/go/ast/astutil @@ -1303,10 +1316,6 @@ golang.org/x/tools/internal/pkgbits golang.org/x/tools/internal/tokeninternal golang.org/x/tools/internal/typeparams golang.org/x/tools/internal/typesinternal -# golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f -## explicit; go 1.17 -golang.org/x/xerrors -golang.org/x/xerrors/internal # google.golang.org/protobuf v1.31.0 ## explicit; go 1.11 google.golang.org/protobuf/cmd/protoc-gen-go diff --git a/wakuv2/common/message.go b/wakuv2/common/message.go index f413cdbf2..d89cfc5a0 100644 --- a/wakuv2/common/message.go +++ b/wakuv2/common/message.go @@ -175,7 +175,7 @@ func NewReceivedMessage(env *protocol.Envelope, msgType MessageType) *ReceivedMe // Hash returns the SHA3 hash of the envelope, calculating it if not yet done. func (msg *ReceivedMessage) Hash() common.Hash { if (msg.hash == common.Hash{}) { - msg.hash = common.BytesToHash(msg.Envelope.Hash()) + msg.hash = common.BytesToHash(msg.Envelope.Hash().Bytes()) } return msg.hash } diff --git a/wakuv2/persistence/dbstore.go b/wakuv2/persistence/dbstore.go index ec6ff0021..d4e985efd 100644 --- a/wakuv2/persistence/dbstore.go +++ b/wakuv2/persistence/dbstore.go @@ -11,8 +11,8 @@ import ( gowakuPersistence "github.com/waku-org/go-waku/waku/persistence" "github.com/waku-org/go-waku/waku/v2/protocol" + storepb "github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb" "github.com/waku-org/go-waku/waku/v2/protocol/pb" - storepb "github.com/waku-org/go-waku/waku/v2/protocol/store/pb" "github.com/waku-org/go-waku/waku/v2/timesource" "github.com/waku-org/go-waku/waku/v2/utils" diff --git a/wakuv2/waku.go b/wakuv2/waku.go index 6346343c0..26e24e3f3 100644 --- a/wakuv2/waku.go +++ b/wakuv2/waku.go @@ -57,9 +57,12 @@ import ( "github.com/libp2p/go-libp2p/core/metrics" "github.com/waku-org/go-waku/waku/v2/dnsdisc" + "github.com/waku-org/go-waku/waku/v2/peermanager" wps "github.com/waku-org/go-waku/waku/v2/peerstore" "github.com/waku-org/go-waku/waku/v2/protocol" "github.com/waku-org/go-waku/waku/v2/protocol/filter" + "github.com/waku-org/go-waku/waku/v2/protocol/legacy_store" + storepb "github.com/waku-org/go-waku/waku/v2/protocol/legacy_store/pb" "github.com/waku-org/go-waku/waku/v2/protocol/lightpush" "github.com/waku-org/go-waku/waku/v2/protocol/peer_exchange" "github.com/waku-org/go-waku/waku/v2/protocol/relay" @@ -72,8 +75,6 @@ import ( node "github.com/waku-org/go-waku/waku/v2/node" "github.com/waku-org/go-waku/waku/v2/protocol/pb" - "github.com/waku-org/go-waku/waku/v2/protocol/store" - storepb "github.com/waku-org/go-waku/waku/v2/protocol/store/pb" ) const messageQueueLimit = 1024 @@ -126,7 +127,7 @@ type Waku struct { storeMsgIDs map[gethcommon.Hash]bool // Map of the currently processing ids storeMsgIDsMu sync.RWMutex - connStatusChan chan node.ConnStatus + topicHealthStatusChan chan peermanager.TopicHealthStatus connStatusSubscriptions map[string]*types.ConnStatusSubscription connStatusMu sync.Mutex @@ -196,7 +197,7 @@ func New(nodeKey string, fleet string, cfg *Config, logger *zap.Logger, appDB *s expirations: make(map[uint32]mapset.Set), msgQueue: make(chan *common.ReceivedMessage, messageQueueLimit), sendQueue: make(chan *protocol.Envelope, 1000), - connStatusChan: make(chan node.ConnStatus, 100), + topicHealthStatusChan: make(chan peermanager.TopicHealthStatus, 100), connStatusSubscriptions: make(map[string]*types.ConnStatusSubscription), ctx: ctx, cancel: cancel, @@ -244,7 +245,7 @@ func New(nodeKey string, fleet string, cfg *Config, logger *zap.Logger, appDB *s node.WithLibP2POptions(libp2pOpts...), node.WithPrivateKey(privateKey), node.WithHostAddress(hostAddr), - node.WithConnectionStatusChannel(waku.connStatusChan), + node.WithTopicHealthStatusChannel(waku.topicHealthStatusChan), node.WithKeepAlive(time.Duration(cfg.KeepAliveInterval) * time.Second), node.WithMaxPeerConnections(cfg.DiscoveryLimit), node.WithLogger(logger), @@ -505,7 +506,7 @@ func (w *Waku) telemetryBandwidthStats(telemetryServerURL string) { w.bandwidthCounter.Reset() } - storeStats := w.bandwidthCounter.GetBandwidthForProtocol(store.StoreID_v20beta4) + storeStats := w.bandwidthCounter.GetBandwidthForProtocol(legacy_store.StoreID_v20beta4) relayStats := w.bandwidthCounter.GetBandwidthForProtocol(relay.WakuRelayID_v200) go telemetry.PushProtocolStats(relayStats, storeStats) } @@ -979,7 +980,7 @@ func (w *Waku) broadcast() { for { select { case envelope := <-w.sendQueue: - logger := w.logger.With(zap.String("envelopeHash", hexutil.Encode(envelope.Hash())), zap.String("pubsubTopic", envelope.PubsubTopic()), zap.String("contentTopic", envelope.Message().ContentTopic), zap.Int64("timestamp", envelope.Message().GetTimestamp())) + logger := w.logger.With(zap.Stringer("envelopeHash", envelope.Hash()), zap.String("pubsubTopic", envelope.PubsubTopic()), zap.String("contentTopic", envelope.Message().ContentTopic), zap.Int64("timestamp", envelope.Message().GetTimestamp())) var fn publishFn if w.cfg.SkipPublishToTopic { // For now only used in testing to simulate going offline @@ -1023,7 +1024,7 @@ func (w *Waku) publishEnvelope(envelope *protocol.Envelope, publishFn publishFn, } w.SendEnvelopeEvent(common.EnvelopeEvent{ - Hash: gethcommon.BytesToHash(envelope.Hash()), + Hash: gethcommon.BytesToHash(envelope.Hash().Bytes()), Event: event, }) } @@ -1051,7 +1052,7 @@ func (w *Waku) Send(pubsubTopic string, msg *pb.WakuMessage) ([]byte, error) { w.sendQueue <- envelope w.poolMu.Lock() - alreadyCached := w.envelopeCache.Has(gethcommon.BytesToHash(envelope.Hash())) + alreadyCached := w.envelopeCache.Has(gethcommon.BytesToHash(envelope.Hash().Bytes())) w.poolMu.Unlock() if !alreadyCached { recvMessage := common.NewReceivedMessage(envelope, common.RelayedMessageType) @@ -1059,21 +1060,21 @@ func (w *Waku) Send(pubsubTopic string, msg *pb.WakuMessage) ([]byte, error) { w.addEnvelope(recvMessage) } - return envelope.Hash(), nil + return envelope.Hash().Bytes(), nil } -func (w *Waku) query(ctx context.Context, peerID peer.ID, pubsubTopic string, topics []common.TopicType, from uint64, to uint64, requestID []byte, opts []store.HistoryRequestOption) (*store.Result, error) { +func (w *Waku) query(ctx context.Context, peerID peer.ID, pubsubTopic string, topics []common.TopicType, from uint64, to uint64, requestID []byte, opts []legacy_store.HistoryRequestOption) (*legacy_store.Result, error) { - opts = append(opts, store.WithRequestID(requestID)) + opts = append(opts, legacy_store.WithRequestID(requestID)) strTopics := make([]string, len(topics)) for i, t := range topics { strTopics[i] = t.ContentTopic() } - opts = append(opts, store.WithPeer(peerID)) + opts = append(opts, legacy_store.WithPeer(peerID)) - query := store.Query{ + query := legacy_store.Query{ StartTime: proto.Int64(int64(from) * int64(time.Second)), EndTime: proto.Int64(int64(to) * int64(time.Second)), ContentTopics: strTopics, @@ -1088,10 +1089,10 @@ func (w *Waku) query(ctx context.Context, peerID peer.ID, pubsubTopic string, to zap.String("pubsubTopic", query.PubsubTopic), zap.Stringer("peerID", peerID)) - return w.node.Store().Query(ctx, query, opts...) + return w.node.LegacyStore().Query(ctx, query, opts...) } -func (w *Waku) Query(ctx context.Context, peerID peer.ID, pubsubTopic string, topics []common.TopicType, from uint64, to uint64, opts []store.HistoryRequestOption, processEnvelopes bool) (cursor *storepb.Index, envelopesCount int, err error) { +func (w *Waku) Query(ctx context.Context, peerID peer.ID, pubsubTopic string, topics []common.TopicType, from uint64, to uint64, opts []legacy_store.HistoryRequestOption, processEnvelopes bool) (cursor *storepb.Index, envelopesCount int, err error) { requestID := protocol.GenerateRequestID() pubsubTopic = w.getPubsubTopic(pubsubTopic) @@ -1117,7 +1118,7 @@ func (w *Waku) Query(ctx context.Context, peerID peer.ID, pubsubTopic string, to envelope := protocol.NewEnvelope(msg, msg.GetTimestamp(), pubsubTopic) w.logger.Info("received waku2 store message", - zap.Any("envelopeHash", hexutil.Encode(envelope.Hash())), + zap.Stringer("envelopeHash", envelope.Hash()), zap.String("pubsubTopic", pubsubTopic), zap.Int64p("timestamp", envelope.Message().Timestamp), ) @@ -1191,8 +1192,13 @@ func (w *Waku) Start() error { select { case <-w.ctx.Done(): return - case c := <-w.connStatusChan: + case c := <-w.topicHealthStatusChan: w.connStatusMu.Lock() + + // TODO: https://github.com/status-im/status-go/issues/4628 + // This code is not using the topic health status correctly. + // It assumes we are using a single pubsub topic for now + latestConnStatus := formatConnStatus(w.node, c) w.logger.Debug("peer stats", zap.Int("peersCount", len(latestConnStatus.Peers)), @@ -1337,7 +1343,7 @@ func (w *Waku) OnNewEnvelopes(envelope *protocol.Envelope, msgType common.Messag logger := w.logger.With( zap.Any("messageType", msgType), - zap.String("envelopeHash", hexutil.Encode(envelope.Hash())), + zap.Stringer("envelopeHash", envelope.Hash()), zap.String("contentTopic", envelope.Message().ContentTopic), zap.Int64("timestamp", envelope.Message().GetTimestamp()), ) @@ -1426,7 +1432,7 @@ func (w *Waku) processQueueLoop() { func (w *Waku) processReceivedMessage(e *common.ReceivedMessage) { logger := w.logger.With( - zap.String("envelopeHash", hexutil.Encode(e.Envelope.Hash())), + zap.Stringer("envelopeHash", e.Envelope.Hash()), zap.String("pubsubTopic", e.PubsubTopic), zap.String("contentTopic", e.ContentTopic.ContentTopic()), zap.Int64("timestamp", e.Envelope.Message().GetTimestamp()), @@ -1495,7 +1501,7 @@ func (w *Waku) PeerCount() int { } func (w *Waku) Peers() map[string]types.WakuV2Peer { - return FormatPeerStats(w.node, w.node.PeerStats()) + return FormatPeerStats(w.node) } func (w *Waku) ListenAddresses() []string { @@ -1703,7 +1709,7 @@ func (w *Waku) AddStorePeer(address string) (peer.ID, error) { return "", err } - peerID, err := w.node.AddPeer(addr, wps.Static, []string{}, store.StoreID_v20beta4) + peerID, err := w.node.AddPeer(addr, wps.Static, []string{}, legacy_store.StoreID_v20beta4) if err != nil { return "", err } @@ -1816,13 +1822,13 @@ func toDeterministicID(id string, expectedLen int) (string, error) { return id, nil } -func FormatPeerStats(wakuNode *node.WakuNode, peers node.PeerStats) map[string]types.WakuV2Peer { +func FormatPeerStats(wakuNode *node.WakuNode) map[string]types.WakuV2Peer { p := make(map[string]types.WakuV2Peer) - for k, v := range peers { + for k, v := range wakuNode.PeerStats() { peerInfo := wakuNode.Host().Peerstore().PeerInfo(k) wakuV2Peer := types.WakuV2Peer{} wakuV2Peer.Protocols = v - hostInfo, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/p2p/%s", k.Pretty())) + hostInfo, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/p2p/%s", k.String())) for _, addr := range peerInfo.Addrs { wakuV2Peer.Addresses = append(wakuV2Peer.Addresses, addr.Encapsulate(hostInfo).String()) } @@ -1831,14 +1837,18 @@ func FormatPeerStats(wakuNode *node.WakuNode, peers node.PeerStats) map[string]t return p } -func formatConnStatus(wakuNode *node.WakuNode, c node.ConnStatus) types.ConnStatus { +func formatConnStatus(wakuNode *node.WakuNode, c peermanager.TopicHealthStatus) types.ConnStatus { + isOnline := true + if c.Health == peermanager.UnHealthy { + isOnline = false + } + return types.ConnStatus{ - IsOnline: c.IsOnline, - HasHistory: c.HasHistory, - Peers: FormatPeerStats(wakuNode, c.Peers), + IsOnline: isOnline, + Peers: FormatPeerStats(wakuNode), } } -func (w *Waku) StoreNode() store.Store { - return w.node.Store() +func (w *Waku) StoreNode() legacy_store.Store { + return w.node.LegacyStore() } diff --git a/wakuv2/waku_test.go b/wakuv2/waku_test.go index b3344969c..152d2c217 100644 --- a/wakuv2/waku_test.go +++ b/wakuv2/waku_test.go @@ -23,9 +23,9 @@ import ( "google.golang.org/protobuf/proto" "github.com/waku-org/go-waku/waku/v2/dnsdisc" + "github.com/waku-org/go-waku/waku/v2/protocol/legacy_store" "github.com/waku-org/go-waku/waku/v2/protocol/pb" "github.com/waku-org/go-waku/waku/v2/protocol/relay" - "github.com/waku-org/go-waku/waku/v2/protocol/store" "github.com/waku-org/go-waku/waku/v2/protocol/subscription" "github.com/status-im/status-go/appdatabase" @@ -183,7 +183,7 @@ func TestBasicWakuV2(t *testing.T) { b.InitialInterval = 500 * time.Millisecond } err = tt.RetryWithBackOff(func() error { - storeResult, err := w.query(context.Background(), storeNode.PeerID, relay.DefaultWakuTopic, []common.TopicType{contentTopic}, uint64(timestampInSeconds-int64(marginInSeconds)), uint64(timestampInSeconds+int64(marginInSeconds)), []byte{}, []store.HistoryRequestOption{}) + storeResult, err := w.query(context.Background(), storeNode.PeerID, relay.DefaultWakuTopic, []common.TopicType{contentTopic}, uint64(timestampInSeconds-int64(marginInSeconds)), uint64(timestampInSeconds+int64(marginInSeconds)), []byte{}, []legacy_store.HistoryRequestOption{}) if err != nil || len(storeResult.Messages) == 0 { // in case of failure extend timestamp margin up to 40secs if marginInSeconds < 40 { @@ -461,7 +461,7 @@ func TestWakuV2Store(t *testing.T) { marginInSeconds := 5 // Query the second node's store for the message - storeResult, err := w1.query(context.Background(), w2.node.Host().ID(), relay.DefaultWakuTopic, []common.TopicType{contentTopic}, uint64(timestampInSeconds-int64(marginInSeconds)), uint64(timestampInSeconds+int64(marginInSeconds)), []byte{}, []store.HistoryRequestOption{}) + storeResult, err := w1.query(context.Background(), w2.node.Host().ID(), relay.DefaultWakuTopic, []common.TopicType{contentTopic}, uint64(timestampInSeconds-int64(marginInSeconds)), uint64(timestampInSeconds+int64(marginInSeconds)), []byte{}, []legacy_store.HistoryRequestOption{}) require.NoError(t, err) require.True(t, len(storeResult.Messages) > 0, "no messages received from store node") }