From fdf465438a2b936c9683248105b68df2851cc1b0 Mon Sep 17 00:00:00 2001 From: "felix.gateru" Date: Mon, 16 Oct 2023 08:21:26 +0300 Subject: [PATCH] Add internal server dtls config Signed-off-by: felix.gateru --- coap/api/transport.go | 6 +- coap/client.go | 6 +- go.mod | 62 +- go.sum | 158 +--- internal/server/coap/coap.go | 62 +- vendor/github.com/pion/dtls/v2/AUTHORS.txt | 3 + .../github.com/pion/dtls/v2/cipher_suite.go | 2 +- vendor/github.com/pion/dtls/v2/config.go | 20 + vendor/github.com/pion/dtls/v2/conn.go | 296 ++++-- .../github.com/pion/dtls/v2/connection_id.go | 101 ++ .../github.com/pion/dtls/v2/flight0handler.go | 18 + .../github.com/pion/dtls/v2/flight1handler.go | 15 + .../github.com/pion/dtls/v2/flight3handler.go | 18 + .../github.com/pion/dtls/v2/flight4handler.go | 11 +- .../github.com/pion/dtls/v2/flight5handler.go | 5 +- .../github.com/pion/dtls/v2/flight6handler.go | 1 + .../pion/dtls/v2/fragment_buffer.go | 2 +- vendor/github.com/pion/dtls/v2/handshaker.go | 9 +- .../dtls/v2/internal/ciphersuite/aes_ccm.go | 4 +- ...tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go | 4 +- .../tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go | 4 +- .../tls_ecdhe_psk_with_aes_128_cbc_sha256.go | 4 +- .../tls_psk_with_aes_128_cbc_sha256.go | 4 +- .../pion/dtls/v2/internal/net/buffer.go | 235 +++++ .../dtls/v2/internal/net/udp/packet_conn.go | 407 ++++++++ .../pion/dtls/v2/internal/util/util.go | 9 + vendor/github.com/pion/dtls/v2/listener.go | 17 +- vendor/github.com/pion/dtls/v2/packet.go | 5 +- .../dtls/v2/pkg/crypto/ciphersuite/cbc.go | 82 +- .../dtls/v2/pkg/crypto/ciphersuite/ccm.go | 38 +- .../v2/pkg/crypto/ciphersuite/ciphersuite.go | 28 + .../dtls/v2/pkg/crypto/ciphersuite/gcm.go | 31 +- vendor/github.com/pion/dtls/v2/pkg/net/net.go | 108 +++ .../pion/dtls/v2/pkg/protocol/content.go | 1 + .../pkg/protocol/extension/connection_id.go | 59 ++ .../dtls/v2/pkg/protocol/extension/errors.go | 1 + .../v2/pkg/protocol/extension/extension.go | 7 + .../extension/supported_point_formats.go | 8 +- .../v2/pkg/protocol/recordlayer/header.go | 29 +- .../protocol/recordlayer/inner_plaintext.go | 47 + .../pkg/protocol/recordlayer/recordlayer.go | 57 +- vendor/github.com/pion/dtls/v2/resume.go | 4 +- vendor/github.com/pion/dtls/v2/state.go | 26 +- .../pion/transport/{v2 => v3}/AUTHORS.txt | 5 + .../pion/transport/{v2 => v3}/LICENSE | 0 .../transport/{v2 => v3}/deadline/deadline.go | 0 .../connctx/connctx.go => v3/netctx/conn.go} | 43 +- .../pion/transport/v3/netctx/packetconn.go | 175 ++++ .../{v2/connctx => v3/netctx}/pipe.go | 8 +- .../transport/{v2 => v3}/packetio/buffer.go | 2 +- .../transport/{v2 => v3}/packetio/errors.go | 0 .../{v2 => v3}/packetio/hardlimit.go | 0 .../{v2 => v3}/packetio/no_hardlimit.go | 0 .../{v2 => v3}/replaydetector/fixedbig.go | 0 .../replaydetector/replaydetector.go | 35 +- .../transport/{v2 => v3}/udp/batchconn.go | 0 .../pion/transport/{v2 => v3}/udp/conn.go | 5 + .../github.com/plgd-dev/go-coap/v2/Dockerfile | 11 - .../plgd-dev/go-coap/v2/dtls/client.go | 222 ----- .../plgd-dev/go-coap/v2/dtls/optionmux.go | 11 - .../plgd-dev/go-coap/v2/dtls/options.go | 325 ------- .../plgd-dev/go-coap/v2/dtls/server.go | 347 ------- .../plgd-dev/go-coap/v2/mux/client.go | 34 - .../plgd-dev/go-coap/v2/mux/message.go | 23 - .../go-coap/v2/net/blockwise/blockwise.go | 878 ----------------- .../v2/net/blockwise/senderRequestMap.go | 116 --- .../plgd-dev/go-coap/v2/net/dtlslistener.go | 86 -- .../monitor/inactivity/inactivitymonitor.go | 72 -- .../v2/net/monitor/inactivity/keepalive.go | 54 -- .../plgd-dev/go-coap/v2/pkg/cache/cache.go | 99 -- .../github.com/plgd-dev/go-coap/v2/server.go | 79 -- .../plgd-dev/go-coap/v2/tcp/client.go | 129 --- .../plgd-dev/go-coap/v2/tcp/clientconn.go | 559 ----------- .../plgd-dev/go-coap/v2/tcp/clientobserve.go | 216 ----- .../plgd-dev/go-coap/v2/tcp/container.go | 70 -- .../go-coap/v2/tcp/message/message.go | 346 ------- .../go-coap/v2/tcp/message/pool/message.go | 186 ---- .../plgd-dev/go-coap/v2/tcp/nocopy.go | 12 - .../plgd-dev/go-coap/v2/tcp/optionmux.go | 41 - .../plgd-dev/go-coap/v2/tcp/options.go | 365 ------- .../plgd-dev/go-coap/v2/tcp/responsewriter.go | 59 -- .../plgd-dev/go-coap/v2/tcp/server.go | 334 ------- .../plgd-dev/go-coap/v2/tcp/session.go | 427 --------- .../plgd-dev/go-coap/v2/udp/client.go | 223 ----- .../plgd-dev/go-coap/v2/udp/client/client.go | 129 --- .../go-coap/v2/udp/client/clientconn.go | 805 ---------------- .../go-coap/v2/udp/client/clientobserve.go | 215 ----- .../go-coap/v2/udp/client/container.go | 70 -- .../plgd-dev/go-coap/v2/udp/client/mux.go | 42 - .../go-coap/v2/udp/client/responsewriter.go | 66 -- .../plgd-dev/go-coap/v2/udp/message/getmid.go | 30 - .../go-coap/v2/udp/message/pool/message.go | 243 ----- .../plgd-dev/go-coap/v2/udp/optionmux.go | 11 - .../plgd-dev/go-coap/v2/udp/options.go | 322 ------- .../plgd-dev/go-coap/v2/udp/server.go | 439 --------- .../plgd-dev/go-coap/{v2 => v3}/.codecov.yml | 1 + .../plgd-dev/go-coap/{v2 => v3}/.gitignore | 1 + .../plgd-dev/go-coap/{v2 => v3}/.golangci.yml | 77 +- .../plgd-dev/go-coap/{v2 => v3}/LICENSE | 0 .../plgd-dev/go-coap/{v2 => v3}/README.md | 25 +- .../plgd-dev/go-coap/v3/dtls/client.go | 127 +++ .../plgd-dev/go-coap/v3/dtls/server.go | 7 + .../plgd-dev/go-coap/v3/dtls/server/config.go | 64 ++ .../plgd-dev/go-coap/v3/dtls/server/server.go | 231 +++++ .../{v2/dtls => v3/dtls/server}/session.go | 32 +- .../{v2 => v3}/message/codes/code_string.go | 1 + .../go-coap/{v2 => v3}/message/codes/codes.go | 4 +- .../{v2 => v3}/message/encodeDecodeUint32.go | 2 +- .../go-coap/{v2 => v3}/message/error.go | 0 .../go-coap/{v2 => v3}/message/getETag.go | 2 +- .../go-coap/{v2 => v3}/message/getToken.go | 0 .../plgd-dev/go-coap/v3/message/getmid.go | 35 + .../go-coap/{v2 => v3}/message/message.go | 25 +- .../{v2 => v3}/message/noresponse/error.go | 0 .../message/noresponse/noresponse.go | 2 +- .../go-coap/{v2 => v3}/message/option.go | 48 +- .../go-coap/{v2 => v3}/message/options.go | 46 +- .../{v2 => v3}/message/pool/message.go | 239 ++++- .../plgd-dev/go-coap/v3/message/pool/pool.go | 64 ++ .../plgd-dev/go-coap/v3/message/tcpOptions.go | 78 ++ .../go-coap/{v2/udp => v3}/message/type.go | 13 +- .../plgd-dev/go-coap/v3/mux/client.go | 51 + .../plgd-dev/go-coap/v3/mux/message.go | 16 + .../go-coap/{v2 => v3}/mux/middleware.go | 0 .../go-coap/v3/mux/muxResponseWriter.go | 47 + .../plgd-dev/go-coap/{v2 => v3}/mux/regexp.go | 0 .../plgd-dev/go-coap/{v2 => v3}/mux/router.go | 13 +- .../go-coap/v3/net/blockwise/blockwise.go | 844 +++++++++++++++++ .../go-coap/{v2 => v3}/net/blockwise/error.go | 0 .../plgd-dev/go-coap/v3/net/client/client.go | 241 +++++ .../limitParallelRequests.go | 135 +++ .../v3/net/client/receivedMessageReader.go | 96 ++ .../plgd-dev/go-coap/{v2 => v3}/net/conn.go | 6 +- .../go-coap/{v2 => v3}/net/connUDP.go | 48 +- .../plgd-dev/go-coap/v3/net/dtlslistener.go | 197 ++++ .../plgd-dev/go-coap/{v2 => v3}/net/error.go | 8 +- .../go-coap/{v2 => v3}/net/error_unix.go | 0 .../go-coap/{v2 => v3}/net/error_windows.go | 0 .../v3/net/monitor/inactivity/keepalive.go | 63 ++ .../v3/net/monitor/inactivity/monitor.go | 68 ++ .../go-coap/v3/net/observation/handler.go | 267 ++++++ .../{v2 => v3}/net/observation/observation.go | 0 .../go-coap/{v2 => v3}/net/options.go | 29 +- .../v3/net/responsewriter/responseWriter.go | 79 ++ .../go-coap/{v2 => v3}/net/tcplistener.go | 2 +- .../go-coap/{v2 => v3}/net/tlslistener.go | 2 +- .../plgd-dev/go-coap/{v2 => v3}/net/udp.go | 0 .../go-coap/v3/options/commonOptions.go | 786 ++++++++++++++++ .../go-coap/v3/options/config/common.go | 61 ++ .../plgd-dev/go-coap/v3/options/tcpOptions.go | 76 ++ .../plgd-dev/go-coap/v3/options/udpOptions.go | 70 ++ .../plgd-dev/go-coap/v3/pkg/cache/cache.go | 85 ++ .../{v2 => v3}/pkg/connections/connections.go | 23 +- .../plgd-dev/go-coap/v3/pkg/errors/error.go | 5 + .../plgd-dev/go-coap/v3/pkg/fn/funcList.go | 19 + .../plgd-dev/go-coap/v3/pkg/rand/rand.go | 31 + .../pkg/runner/periodic/periodic.go | 2 +- .../plgd-dev/go-coap/v3/pkg/sync/map.go | 215 +++++ .../plgd-dev/go-coap/{v2 => v3}/renovate.json | 0 .../github.com/plgd-dev/go-coap/v3/server.go | 161 ++++ .../{v2 => v3}/sonar-project.properties | 2 +- .../plgd-dev/go-coap/v3/tcp/client.go | 110 +++ .../plgd-dev/go-coap/v3/tcp/client/config.go | 49 + .../plgd-dev/go-coap/v3/tcp/client/conn.go | 370 ++++++++ .../plgd-dev/go-coap/v3/tcp/client/session.go | 272 ++++++ .../plgd-dev/go-coap/v3/tcp/coder/coder.go | 254 +++++ .../{v2/udp/message => v3/tcp/coder}/error.go | 2 +- .../plgd-dev/go-coap/v3/tcp/server.go | 9 + .../plgd-dev/go-coap/v3/tcp/server/config.go | 62 ++ .../plgd-dev/go-coap/v3/tcp/server/server.go | 223 +++++ .../plgd-dev/go-coap/v3/udp/client.go | 116 +++ .../plgd-dev/go-coap/v3/udp/client/config.go | 55 ++ .../plgd-dev/go-coap/v3/udp/client/conn.go | 888 ++++++++++++++++++ .../go-coap/{v2 => v3}/udp/client/mutexmap.go | 0 .../message.go => v3/udp/coder/coder.go} | 52 +- .../plgd-dev/go-coap/v3/udp/coder/error.go | 8 + .../plgd-dev/go-coap/v3/udp/server.go | 7 + .../plgd-dev/go-coap/v3/udp/server/config.go | 64 ++ .../{v2/udp => v3/udp/server}/discover.go | 41 +- .../plgd-dev/go-coap/v3/udp/server/server.go | 383 ++++++++ .../{v2/udp => v3/udp/server}/session.go | 33 +- vendor/github.com/plgd-dev/kit/v2/LICENSE | 201 ---- vendor/github.com/plgd-dev/kit/v2/sync/map.go | 141 --- .../github.com/plgd-dev/kit/v2/sync/once.go | 35 - .../github.com/plgd-dev/kit/v2/sync/pool.go | 81 -- .../plgd-dev/kit/v2/sync/refcounter.go | 59 -- vendor/golang.org/x/sys/unix/syscall_linux.go | 99 ++ .../golang.org/x/sys/unix/syscall_netbsd.go | 262 ++++++ .../golang.org/x/sys/unix/syscall_solaris.go | 21 + .../x/sys/unix/ztypes_linux_riscv64.go | 3 + .../golang.org/x/tools/go/packages/golist.go | 4 + vendor/modules.txt | 77 +- 192 files changed, 10024 insertions(+), 8812 deletions(-) create mode 100644 vendor/github.com/pion/dtls/v2/connection_id.go create mode 100644 vendor/github.com/pion/dtls/v2/internal/net/buffer.go create mode 100644 vendor/github.com/pion/dtls/v2/internal/net/udp/packet_conn.go create mode 100644 vendor/github.com/pion/dtls/v2/pkg/net/net.go create mode 100644 vendor/github.com/pion/dtls/v2/pkg/protocol/extension/connection_id.go create mode 100644 vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/inner_plaintext.go rename vendor/github.com/pion/transport/{v2 => v3}/AUTHORS.txt (83%) rename vendor/github.com/pion/transport/{v2 => v3}/LICENSE (100%) rename vendor/github.com/pion/transport/{v2 => v3}/deadline/deadline.go (100%) rename vendor/github.com/pion/transport/{v2/connctx/connctx.go => v3/netctx/conn.go} (68%) create mode 100644 vendor/github.com/pion/transport/v3/netctx/packetconn.go rename vendor/github.com/pion/transport/{v2/connctx => v3/netctx}/pipe.go (56%) rename vendor/github.com/pion/transport/{v2 => v3}/packetio/buffer.go (99%) rename vendor/github.com/pion/transport/{v2 => v3}/packetio/errors.go (100%) rename vendor/github.com/pion/transport/{v2 => v3}/packetio/hardlimit.go (100%) rename vendor/github.com/pion/transport/{v2 => v3}/packetio/no_hardlimit.go (100%) rename vendor/github.com/pion/transport/{v2 => v3}/replaydetector/fixedbig.go (100%) rename vendor/github.com/pion/transport/{v2 => v3}/replaydetector/replaydetector.go (78%) rename vendor/github.com/pion/transport/{v2 => v3}/udp/batchconn.go (100%) rename vendor/github.com/pion/transport/{v2 => v3}/udp/conn.go (97%) delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/Dockerfile delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/dtls/client.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/dtls/optionmux.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/dtls/options.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/dtls/server.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/mux/client.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/mux/message.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/net/blockwise/blockwise.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/net/blockwise/senderRequestMap.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/net/dtlslistener.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/net/monitor/inactivity/inactivitymonitor.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/net/monitor/inactivity/keepalive.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/pkg/cache/cache.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/server.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/tcp/client.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/tcp/clientconn.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/tcp/clientobserve.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/tcp/container.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/tcp/message/message.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/tcp/message/pool/message.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/tcp/nocopy.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/tcp/optionmux.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/tcp/options.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/tcp/responsewriter.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/tcp/server.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/tcp/session.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/udp/client.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/udp/client/client.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/udp/client/clientconn.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/udp/client/clientobserve.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/udp/client/container.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/udp/client/mux.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/udp/client/responsewriter.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/udp/message/getmid.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/udp/message/pool/message.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/udp/optionmux.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/udp/options.go delete mode 100644 vendor/github.com/plgd-dev/go-coap/v2/udp/server.go rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/.codecov.yml (82%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/.gitignore (98%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/.golangci.yml (67%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/LICENSE (100%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/README.md (85%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/dtls/client.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/dtls/server.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/dtls/server/config.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/dtls/server/server.go rename vendor/github.com/plgd-dev/go-coap/{v2/dtls => v3/dtls/server}/session.go (76%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/message/codes/code_string.go (97%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/message/codes/codes.go (96%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/message/encodeDecodeUint32.go (95%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/message/error.go (100%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/message/getETag.go (93%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/message/getToken.go (100%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/message/getmid.go rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/message/message.go (52%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/message/noresponse/error.go (100%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/message/noresponse/noresponse.go (96%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/message/option.go (87%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/message/options.go (94%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/message/pool/message.go (62%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/message/pool/pool.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/message/tcpOptions.go rename vendor/github.com/plgd-dev/go-coap/{v2/udp => v3}/message/type.go (75%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/mux/client.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/mux/message.go rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/mux/middleware.go (100%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/mux/muxResponseWriter.go rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/mux/regexp.go (100%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/mux/router.go (95%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/net/blockwise/blockwise.go rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/net/blockwise/error.go (100%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/net/client/client.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/net/client/limitParallelRequests/limitParallelRequests.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/net/client/receivedMessageReader.go rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/net/conn.go (92%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/net/connUDP.go (90%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/net/dtlslistener.go rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/net/error.go (70%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/net/error_unix.go (100%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/net/error_windows.go (100%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/net/monitor/inactivity/keepalive.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/net/monitor/inactivity/monitor.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/net/observation/handler.go rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/net/observation/observation.go (100%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/net/options.go (81%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/net/responsewriter/responseWriter.go rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/net/tcplistener.go (97%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/net/tlslistener.go (96%) rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/net/udp.go (100%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/options/commonOptions.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/options/config/common.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/options/tcpOptions.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/options/udpOptions.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/pkg/cache/cache.go rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/pkg/connections/connections.go (73%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/pkg/errors/error.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/pkg/fn/funcList.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/pkg/rand/rand.go rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/pkg/runner/periodic/periodic.go (89%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/pkg/sync/map.go rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/renovate.json (100%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/server.go rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/sonar-project.properties (97%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/tcp/client.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/tcp/client/config.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/tcp/client/conn.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/tcp/client/session.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/tcp/coder/coder.go rename vendor/github.com/plgd-dev/go-coap/{v2/udp/message => v3/tcp/coder}/error.go (90%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/tcp/server.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/tcp/server/config.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/tcp/server/server.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/udp/client.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/udp/client/config.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/udp/client/conn.go rename vendor/github.com/plgd-dev/go-coap/{v2 => v3}/udp/client/mutexmap.go (100%) rename vendor/github.com/plgd-dev/go-coap/{v2/udp/message/message.go => v3/udp/coder/coder.go} (73%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/udp/coder/error.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/udp/server.go create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/udp/server/config.go rename vendor/github.com/plgd-dev/go-coap/{v2/udp => v3/udp/server}/discover.go (69%) create mode 100644 vendor/github.com/plgd-dev/go-coap/v3/udp/server/server.go rename vendor/github.com/plgd-dev/go-coap/{v2/udp => v3/udp/server}/session.go (78%) delete mode 100644 vendor/github.com/plgd-dev/kit/v2/LICENSE delete mode 100644 vendor/github.com/plgd-dev/kit/v2/sync/map.go delete mode 100644 vendor/github.com/plgd-dev/kit/v2/sync/once.go delete mode 100644 vendor/github.com/plgd-dev/kit/v2/sync/pool.go delete mode 100644 vendor/github.com/plgd-dev/kit/v2/sync/refcounter.go diff --git a/coap/api/transport.go b/coap/api/transport.go index 323cd58087e..3dc2517cf28 100644 --- a/coap/api/transport.go +++ b/coap/api/transport.go @@ -19,9 +19,9 @@ import ( mflog "github.com/mainflux/mainflux/logger" "github.com/mainflux/mainflux/pkg/errors" "github.com/mainflux/mainflux/pkg/messaging" - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - "github.com/plgd-dev/go-coap/v2/mux" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/mux" "github.com/prometheus/client_golang/prometheus/promhttp" ) diff --git a/coap/client.go b/coap/client.go index 91d7bad63e0..387f0448875 100644 --- a/coap/client.go +++ b/coap/client.go @@ -12,9 +12,9 @@ import ( "github.com/mainflux/mainflux/logger" "github.com/mainflux/mainflux/pkg/errors" "github.com/mainflux/mainflux/pkg/messaging" - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - mux "github.com/plgd-dev/go-coap/v2/mux" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + mux "github.com/plgd-dev/go-coap/v3/mux" ) // Client wraps CoAP client. diff --git a/go.mod b/go.mod index 00cf5c8e7c4..be1f654b2d2 100644 --- a/go.mod +++ b/go.mod @@ -39,26 +39,29 @@ require ( github.com/oklog/ulid/v2 v2.1.0 github.com/ory/dockertest/v3 v3.10.0 github.com/pelletier/go-toml v1.9.5 - github.com/plgd-dev/go-coap/v2 v2.6.0 - github.com/prometheus/client_golang v1.17.0 - github.com/rabbitmq/amqp091-go v1.9.0 - github.com/rubenv/sql-migrate v1.5.2 + github.com/pion/dtls/v2 v2.2.8-0.20230905141523-2b584af66577 + github.com/plgd-dev/go-coap/v3 v3.1.5 + github.com/prometheus/client_golang v1.16.0 + github.com/rabbitmq/amqp091-go v1.8.1 + github.com/rubenv/sql-migrate v1.5.1 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.17.0 github.com/stretchr/testify v1.8.4 - go.mongodb.org/mongo-driver v1.12.1 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 - go.opentelemetry.io/otel v1.19.0 - go.opentelemetry.io/otel/exporters/jaeger v1.17.0 - go.opentelemetry.io/otel/sdk v1.19.0 - go.opentelemetry.io/otel/trace v1.19.0 - golang.org/x/crypto v0.14.0 - golang.org/x/exp v0.0.0-20231006140011-7918f672742d - golang.org/x/net v0.17.0 - golang.org/x/sync v0.4.0 - gonum.org/v1/gonum v0.14.0 - google.golang.org/grpc v1.58.3 + github.com/subosito/gotenv v1.4.2 + go.mongodb.org/mongo-driver v1.12.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 + go.opentelemetry.io/contrib/propagators/jaeger v1.17.0 + go.opentelemetry.io/otel v1.16.0 + go.opentelemetry.io/otel/exporters/jaeger v1.16.0 + go.opentelemetry.io/otel/sdk v1.16.0 + go.opentelemetry.io/otel/trace v1.16.0 + golang.org/x/crypto v0.13.0 + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 + golang.org/x/net v0.15.0 + golang.org/x/sync v0.3.0 + gonum.org/v1/gonum v0.13.0 + google.golang.org/grpc v1.56.1 google.golang.org/protobuf v1.31.0 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df ) @@ -170,15 +173,13 @@ require ( github.com/nats-io/nuid v1.0.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect - github.com/opencontainers/runc v1.1.9 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect - github.com/pion/dtls/v2 v2.2.7 // indirect + github.com/opencontainers/runc v1.1.7 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pion/logging v0.2.2 // indirect - github.com/pion/transport/v2 v2.2.4 // indirect + github.com/pion/transport/v3 v3.0.1 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/plgd-dev/kit/v2 v2.0.0-20211006190727-057b33161b90 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_model v0.5.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect @@ -214,16 +215,13 @@ require ( github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect go.opentelemetry.io/otel/metric v1.19.0 // indirect go.uber.org/atomic v1.11.0 // indirect - go.uber.org/multierr v1.11.0 // indirect - golang.org/x/arch v0.5.0 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/sys v0.13.0 // indirect + golang.org/x/arch v0.4.0 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/sys v0.12.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.14.0 // indirect - google.golang.org/genproto v0.0.0-20231012201019-e917dd12ba7a // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a // indirect + golang.org/x/tools v0.13.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 1ffc01b7ce8..40e100407fa 100644 --- a/go.sum +++ b/go.sum @@ -93,7 +93,6 @@ github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZF github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/caarlos0/env/v7 v7.1.0 h1:9lzTF5amyQeWHZzuZeKlCb5FWSUxpG1js43mhbY8ozg= github.com/caarlos0/env/v7 v7.1.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= @@ -136,9 +135,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/deepmap/oapi-codegen v1.15.0 h1:SQqViaeb4k2vMul8gx12oDOIadEtoRqTdLkxjzqtQ90= -github.com/deepmap/oapi-codegen v1.15.0/go.mod h1:a6KoHV7lMRwsPoEg2C6NDHiXYV3EQfiFocOlJ8dgJQE= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/deepmap/oapi-codegen v1.13.0 h1:cnFHelhsRQbYvanCUAbRSn/ZpkUb1HPRlQcu8YqSORQ= +github.com/deepmap/oapi-codegen v1.13.0/go.mod h1:Amy7tbubKY9qkZOXqymI3Z6xSbndmu+atMJheLdyg44= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= @@ -149,8 +147,6 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dsnet/golib/memfile v0.0.0-20190531212259-571cdbcff553/go.mod h1:tXGNW9q3RwvWt1VV2qrRKlSSz0npnh12yftCSCy2T64= -github.com/dsnet/golib/memfile v0.0.0-20200723050859-c110804dfa93/go.mod h1:tXGNW9q3RwvWt1VV2qrRKlSSz0npnh12yftCSCy2T64= github.com/dsnet/golib/memfile v1.0.0 h1:J9pUspY2bDCbF9o+YGwcf3uG6MdyITfh/Fk3/CaEiFs= github.com/dsnet/golib/memfile v1.0.0/go.mod h1:tXGNW9q3RwvWt1VV2qrRKlSSz0npnh12yftCSCy2T64= github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik= @@ -176,8 +172,6 @@ github.com/fiorix/go-smpp v0.0.0-20210403173735-2894b96e70ba/go.mod h1:VfKFK7fGe github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw= github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= @@ -189,9 +183,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-acme/lego v2.7.2+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= -github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= -github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -199,13 +190,11 @@ github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU= -github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg= +github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= +github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= @@ -214,8 +203,6 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ocf/go-coap/v2 v2.0.4-0.20200728125043-f38b86f047a7/go.mod h1:X9wVKcaOSx7wBxKcvrWgMQq1R2DNeA7NBLW2osIb8TM= -github.com/go-ocf/kit v0.0.0-20200728130040-4aebdb6982bc/go.mod h1:TIsoMT/iB7t9P6ahkcOnsmvS83SIJsv9qXRfz/yLf6M= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -243,14 +230,11 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/gocql/gocql v1.6.0 h1:IdFdOTbnpbd0pDhl4REKQDM+Q0SzKXQ1Yh+YZZ8T/qU= -github.com/gocql/gocql v1.6.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= -github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gocql/gocql v1.5.2 h1:WnKf8xRQImcT/KLaEWG2pjEeryDB7K0qQN9mPs1C58Q= +github.com/gocql/gocql v1.5.2/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -334,16 +318,9 @@ github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IP github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e/go.mod h1:AFIo+02s+12CEg8Gzz9kzhCbmbq6JcKNrhHffCGA9z4= github.com/gopcua/opcua v0.1.6 h1:B9SVRKQGzcWcwP2QPYN93Uku32+3wL+v5cgzBxE6V5I= github.com/gopcua/opcua v0.1.6/go.mod h1:INwnDoRxmNWAt7+tzqxuGqQkSF2c1C69VAL0c2q6AcY= -github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= -github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= -github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= -github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -447,7 +424,6 @@ github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSlj github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -460,25 +436,8 @@ github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPci github.com/jzelinskie/stringz v0.0.2 h1:OSjMEYvz8tjhovgZ/6cGcPID736ubeukr35mu6RYAmg= github.com/jzelinskie/stringz v0.0.2/go.mod h1:hHYbgxJuNLRw91CmpuFsYEOyQqpDVFg8pvEh23vy4P0= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= -github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= -github.com/kataras/blocks v0.0.8 h1:MrpVhoFTCR2v1iOOfGng5VJSILKeZZI+7NGfxEh3SUM= -github.com/kataras/blocks v0.0.8/go.mod h1:9Jm5zx6BB+06NwA+OhTbHW1xkMOYxahnqTN5DveZ2Yg= -github.com/kataras/golog v0.1.9 h1:vLvSDpP7kihFGKFAvBSofYo7qZNULYSHOH2D7rPTKJk= -github.com/kataras/golog v0.1.9/go.mod h1:jlpk/bOaYCyqDqH18pgDHdaJab72yBE6i0O3s30hpWY= -github.com/kataras/iris/v12 v12.2.7 h1:C9KWZmZT5pB5f2ot1XYWDBdi5XeTz0CGweHRXCDARZg= -github.com/kataras/iris/v12 v12.2.7/go.mod h1:mD76k/tIBFy8pHTFIgUPrVrkI4lTKvFbIcfbStJSBnA= -github.com/kataras/pio v0.0.12 h1:o52SfVYauS3J5X08fNjlGS5arXHjW/ItLkyLcKjoH6w= -github.com/kataras/pio v0.0.12/go.mod h1:ODK/8XBhhQ5WqrAhKy+9lTPS7sBf6O3KcLhc9klfRcY= -github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY= -github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4= -github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA= -github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw= -github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= @@ -489,7 +448,6 @@ github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgSh github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -510,16 +468,13 @@ github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZ github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= -github.com/lestrrat-go/iter v0.0.0-20200422075355-fc1769541911/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= -github.com/lestrrat-go/jwx v1.0.2/go.mod h1:TPF17WiSFegZo+c20fdpw49QD+/7n4/IsGvEmCSWwT0= -github.com/lestrrat-go/jwx/v2 v2.0.13 h1:XdxzJbudGaHEoNmyJACAT8aFCB+DmviiaiMoZwuJoUo= -github.com/lestrrat-go/jwx/v2 v2.0.13/go.mod h1:UzXMzcV99p9/xe1JsIb336NJDGXLsleR+Qj3ucEDtfI= +github.com/lestrrat-go/jwx/v2 v2.0.11 h1:ViHMnaMeaO0qV16RZWBHM7GTrAnX2aFLVKofc7FuKLQ= +github.com/lestrrat-go/jwx/v2 v2.0.11/go.mod h1:ZtPtMFlrfDrH2Y0iwfa3dRFn8VzwBrB+cyrm3IBWdDg= github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= -github.com/lestrrat-go/pdebug v0.0.0-20200204225717-4d6bd78da58d/go.mod h1:B06CSso/AWxiPejj+fheUINGeBKeeEZNt8w+EoU7+L8= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -564,9 +519,6 @@ github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOj github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= -github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= -github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -611,44 +563,30 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalNwQLM= -github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opencontainers/runc v1.1.7 h1:y2EZDS8sNng4Ksf0GUYNhKbTShZJPJg1FiXJNH/uoCk= +github.com/opencontainers/runc v1.1.7/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= -github.com/panjf2000/ants/v2 v2.4.3/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/pion/dtls/v2 v2.0.1-0.20200503085337-8e86b3a7d585/go.mod h1:/GahSOC8ZY/+17zkaGJIG4OUkSGAcZu/N/g3roBOCkM= -github.com/pion/dtls/v2 v2.0.10-0.20210502094952-3dc563b9aede/go.mod h1:86wv5dgx2J/z871nUR+5fTTY9tISLUlo+C5Gm86r1Hs= -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/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pion/dtls/v2 v2.2.8-0.20230905141523-2b584af66577 h1:JWOGC998HSupoCjz7RKLpJcQjwnUhgIfHn8pRz9HvCk= +github.com/pion/dtls/v2 v2.2.8-0.20230905141523-2b584af66577/go.mod h1:gKEfO5iCAoS9mBySDZwcIRU2ksZ2a5HqzU+jTUNTzdM= 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/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE= -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/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= -github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= -github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= -github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= +github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= +github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/plgd-dev/go-coap/v2 v2.0.4-0.20200819112225-8eb712b901bc/go.mod h1:+tCi9Q78H/orWRtpVWyBgrr4vKFo2zYtbbxUllerBp4= -github.com/plgd-dev/go-coap/v2 v2.4.1-0.20210517130748-95c37ac8e1fa/go.mod h1:rA7fc7ar+B/qa+Q0hRqv7yj/EMtIlmo1l7vkQGSrHPU= -github.com/plgd-dev/go-coap/v2 v2.6.0 h1:T8tefZK4jag1ssr6gAGU+922QhVcrjk207aPhdg7i3o= -github.com/plgd-dev/go-coap/v2 v2.6.0/go.mod h1:wm9fcL58Ky442Krix74S9Y54rCo36u59xFcYKRQaSBg= -github.com/plgd-dev/kit v0.0.0-20200819113605-d5fcf3e94f63/go.mod h1:Yl9zisyXfPdtP9hTWlJqjJYXmgU/jtSDKttz9/CeD90= -github.com/plgd-dev/kit/v2 v2.0.0-20211006190727-057b33161b90 h1:TC1HJ/UbyflJFPvaOdGmNZ5TeFGex1/dyr9urNGLy7M= -github.com/plgd-dev/kit/v2 v2.0.0-20211006190727-057b33161b90/go.mod h1:Z7oKFLSGQjdi8eInxwFCs0tSApuEM1o0qNck+sJYp4M= +github.com/plgd-dev/go-coap/v3 v3.1.5 h1:Bn3l1fEFvC2XsRibJXIgXonY8GLwYSQP3gYYop5D3l0= +github.com/plgd-dev/go-coap/v3 v3.1.5/go.mod h1:BbPQ1x6ojsvA1Ccp9kVnIuw3Z3J4b/fhNnG/OwCsksQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -745,22 +683,14 @@ github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0= github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.12.0/go.mod h1:229t1eWu9UXTPmoUkbpN/fctKPBY4IJoFXQnxHGXy6E= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/vmihailenco/msgpack/v5 v5.4.0 h1:hRM0digJwyR6vll33NNAwCFguy5JuBD6jxDmQP3l608= -github.com/vmihailenco/msgpack/v5 v5.4.0/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= -github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= -github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -838,8 +768,6 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 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.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y= golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -851,8 +779,6 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= @@ -861,11 +787,10 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -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.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -876,8 +801,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-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= 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= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -920,7 +845,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -937,21 +861,16 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210502030024-e5908800b52b/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -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.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -994,9 +913,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1038,22 +955,17 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/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.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -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.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= 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= @@ -1068,15 +980,16 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1101,7 +1014,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1116,7 +1028,6 @@ golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200417140056-c07e33ef3290/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -1136,8 +1047,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 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/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 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= @@ -1263,12 +1174,9 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/server/coap/coap.go b/internal/server/coap/coap.go index 05ab0125c4a..4749ce60b8c 100644 --- a/internal/server/coap/coap.go +++ b/internal/server/coap/coap.go @@ -3,14 +3,16 @@ package coap import ( "context" "crypto/tls" + "crypto/tls" "fmt" "time" + "os" "github.com/mainflux/mainflux/internal/server" "github.com/mainflux/mainflux/logger" piondtls "github.com/pion/dtls/v2" - gocoap "github.com/plgd-dev/go-coap/v2" - "github.com/plgd-dev/go-coap/v2/mux" + gocoap "github.com/plgd-dev/go-coap/v3" + "github.com/plgd-dev/go-coap/v3/mux" ) const ( @@ -22,7 +24,7 @@ type Server struct { handler mux.HandlerFunc } -var enableDTLS = true + var _ server.Server = (*Server)(nil) @@ -48,30 +50,48 @@ func (s *Server) Start() error { case s.Config.CertFile != "" || s.Config.KeyFile != "": s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s with TLS cert %s and key %s", s.Name, s.Protocol, s.Address, s.Config.CertFile, s.Config.KeyFile)) certificate, err := tls.LoadX509KeyPair(s.Config.CertFile, s.Config.KeyFile) + dtlsConfig := &piondtls.Config{ + Certificates: []tls.Certificate{certificate}, + ExtendedMasterSecret: piondtls.RequireExtendedMasterSecret, + ClientCAs: tlsConfi, + ClientAuth: piondtls.RequireAndVerifyClientCert, + ConnectContextMaker: func() (context.Context, func()) { + return context.WithTimeout(ctx, 30*time.Second) + }, + } if err != nil { return fmt.Errorf("failed to load auth certificates: %w", err) } - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{certificate}, + clientCA, err := loadCertFile(s.Config.ClientCAFile) + if err != nil { + return fmt.Errorf("failed to load client ca file: %w", err) + } + + if len(clientCA) > 0 { + if dtlsConfig.ClientCAs == nil { + dtlsConfig.ClientCAs = x509.NewCertPool() + } + if !dtlsConfig.ClientCAs.AppendCertsFromPEM(clientCA) { + return fmt.Errorf("failed to append client ca to tls.Config") + } } go func() { - errCh <- gocoap.ListenAndServeTCPTLS("udp", s.Address, tlsConfig, s.handler) + errCh <- gocoap.ListenAndServeDTLS("udp", s.Address, dtlsConfig, s.handler) }() - case enableDTLS: - s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s with TLS cert %s and key %s", s.Name, s.Protocol, s.Address, s.Config.Certfile, s.config.KeyFile)) + case s.Config.CertFile != "" || s.Config.KeyFile != "": + s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s with TLS cert %s and key %s", s.Name, s.Protocol, s.Address, s.Config.CertFile, s.Config.KeyFile)) + certificate, err := tls.LoadX509KeyPair(s.Config.CertFile, s.Config.KeyFile) + if err != nil { + return fmt.Errorf("failed to load auth certificates: %w", err) + } + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{certificate}, + } go func() { - errCh <- gocoap.ListenAndServeDTLS("udp", ":5688", &piondtls.Config{ - PSK: func(hint []byte) ([]byte, error) { - fmt.Printf("Client's hint: %s \n", hint) - return []byte{0xAB, 0xC1, 0x23}, nil - }, - PSKIdentityHint: []byte("Pion DTLS Client"), - CipherSuites: []piondtls.CipherSuiteID{piondtls.TLS_PSK_WITH_AES_128_CCM_8}, - }, s.handler) - + errCh <- gocoap.ListenAndServeTCPTLS("udp", s.Address, tlsConfig, s.handler) }() default: @@ -100,3 +120,11 @@ func (s *Server) Stop() error { s.Logger.Info(fmt.Sprintf("%s service shutdown of http at %s", s.Name, s.Address)) return nil } + +func loadCertFile(certFile string) ([]byte, error) { + if certFile != "" { + return os.ReadFile(certFile) + } + return []byte{}, nil +} + diff --git a/vendor/github.com/pion/dtls/v2/AUTHORS.txt b/vendor/github.com/pion/dtls/v2/AUTHORS.txt index e14fae4c08e..fbaf97711de 100644 --- a/vendor/github.com/pion/dtls/v2/AUTHORS.txt +++ b/vendor/github.com/pion/dtls/v2/AUTHORS.txt @@ -15,6 +15,7 @@ Carson Hoffman Cecylia Bocovich Chris Hiszpanski cnderrauber +Daniel Mangum Daniele Sluijters folbrich Hayden James @@ -41,8 +42,10 @@ Rachel Chen Robert Eperjesi Ryan Gordon Sam Lancia +Sean Sean DuBois Sean DuBois +Sean DuBois Sean DuBois Shelikhoo Stefan Tatschner diff --git a/vendor/github.com/pion/dtls/v2/cipher_suite.go b/vendor/github.com/pion/dtls/v2/cipher_suite.go index 7a5bb4a585b..6f7015c0269 100644 --- a/vendor/github.com/pion/dtls/v2/cipher_suite.go +++ b/vendor/github.com/pion/dtls/v2/cipher_suite.go @@ -95,7 +95,7 @@ type CipherSuite interface { Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error IsInitialized() bool Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) - Decrypt(in []byte) ([]byte, error) + Decrypt(h recordlayer.Header, in []byte) ([]byte, error) } // CipherSuiteName provides the same functionality as tls.CipherSuiteName diff --git a/vendor/github.com/pion/dtls/v2/config.go b/vendor/github.com/pion/dtls/v2/config.go index fbc3ee2476a..604a4d5757c 100644 --- a/vendor/github.com/pion/dtls/v2/config.go +++ b/vendor/github.com/pion/dtls/v2/config.go @@ -176,6 +176,26 @@ type Config struct { // skip hello verify phase and receive ServerHello after initial ClientHello. // This have implication on DoS attack resistance. InsecureSkipVerifyHello bool + + // ConnectionIDGenerator generates connection identifiers that should be + // sent by the remote party if it supports the DTLS Connection Identifier + // extension, as determined during the handshake. Generated connection + // identifiers must always have the same length. Returning a zero-length + // connection identifier indicates that the local party supports sending + // connection identifiers but does not require the remote party to send + // them. A nil ConnectionIDGenerator indicates that connection identifiers + // are not supported. + // https://datatracker.ietf.org/doc/html/rfc9146 + ConnectionIDGenerator func() []byte + + // PaddingLengthGenerator generates the number of padding bytes used to + // inflate ciphertext size in order to obscure content size from observers. + // The length of the content is passed to the generator such that both + // deterministic and random padding schemes can be applied while not + // exceeding maximum record size. + // If no PaddingLengthGenerator is specified, padding will not be applied. + // https://datatracker.ietf.org/doc/html/rfc9146#section-4 + PaddingLengthGenerator func(uint) uint } func defaultConnectContextMaker() (context.Context, func()) { diff --git a/vendor/github.com/pion/dtls/v2/conn.go b/vendor/github.com/pion/dtls/v2/conn.go index 2b758510867..9d1da84cb1a 100644 --- a/vendor/github.com/pion/dtls/v2/conn.go +++ b/vendor/github.com/pion/dtls/v2/conn.go @@ -4,6 +4,7 @@ package dtls import ( + "bytes" "context" "errors" "fmt" @@ -21,9 +22,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/v2/connctx" - "github.com/pion/transport/v2/deadline" - "github.com/pion/transport/v2/replaydetector" + "github.com/pion/transport/v3/deadline" + "github.com/pion/transport/v3/netctx" + "github.com/pion/transport/v3/replaydetector" ) const ( @@ -45,21 +46,27 @@ func invalidKeyingLabels() map[string]bool { } } +type addrPkt struct { + rAddr net.Addr + data []byte +} + // Conn represents a DTLS connection type Conn struct { - lock sync.RWMutex // Internal lock (must not be public) - nextConn connctx.ConnCtx // Embedded Conn, typically a udpconn we read/write from - fragmentBuffer *fragmentBuffer // out-of-order and missing fragment handling - handshakeCache *handshakeCache // caching of handshake messages for verifyData generation - decrypted chan interface{} // Decrypted Application Data or error, pull by calling `Read` - - state State // Internal state + lock sync.RWMutex // Internal lock (must not be public) + nextConn netctx.PacketConn // Embedded Conn, typically a udpconn we read/write from + fragmentBuffer *fragmentBuffer // out-of-order and missing fragment handling + handshakeCache *handshakeCache // caching of handshake messages for verifyData generation + decrypted chan interface{} // Decrypted Application Data or error, pull by calling `Read` + rAddr net.Addr + state State // Internal state maximumTransmissionUnit int + paddingLengthGenerator func(uint) uint handshakeCompletedSuccessfully atomic.Value - encryptedPackets [][]byte + encryptedPackets []addrPkt connectionClosedByUser bool closeLock sync.Mutex @@ -81,9 +88,8 @@ type Conn struct { replayProtectionWindow uint } -func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient bool, initialState *State) (*Conn, error) { - err := validateConfig(config) - if err != nil { +func createConn(ctx context.Context, nextConn net.PacketConn, rAddr net.Addr, config *Config, isClient bool, initialState *State) (*Conn, error) { + if err := validateConfig(config); err != nil { return nil, err } @@ -123,11 +129,18 @@ func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient replayProtectionWindow = defaultReplayProtectionWindow } + paddingLengthGenerator := config.PaddingLengthGenerator + if paddingLengthGenerator == nil { + paddingLengthGenerator = func(uint) uint { return 0 } + } + c := &Conn{ - nextConn: connctx.New(nextConn), + rAddr: rAddr, + nextConn: netctx.NewPacketConn(nextConn), fragmentBuffer: newFragmentBuffer(), handshakeCache: newHandshakeCache(), maximumTransmissionUnit: mtu, + paddingLengthGenerator: paddingLengthGenerator, decrypted: make(chan interface{}, 1), log: logger, @@ -188,6 +201,7 @@ func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient localGetCertificate: config.GetCertificate, localGetClientCertificate: config.GetClientCertificate, insecureSkipHelloVerify: config.InsecureSkipVerifyHello, + connectionIDGenerator: config.ConnectionIDGenerator, } // rfc5246#section-7.4.3 @@ -234,44 +248,49 @@ func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient // Dial connects to the given network address and establishes a DTLS connection on top. // Connection handshake will timeout using ConnectContextMaker in the Config. // If you want to specify the timeout duration, use DialWithContext() instead. -func Dial(network string, raddr *net.UDPAddr, config *Config) (*Conn, error) { +func Dial(network string, rAddr *net.UDPAddr, config *Config) (*Conn, error) { ctx, cancel := config.connectContextMaker() defer cancel() - return DialWithContext(ctx, network, raddr, config) + return DialWithContext(ctx, network, rAddr, config) } // Client establishes a DTLS connection over an existing connection. // Connection handshake will timeout using ConnectContextMaker in the Config. // If you want to specify the timeout duration, use ClientWithContext() instead. -func Client(conn net.Conn, config *Config) (*Conn, error) { +func Client(conn net.PacketConn, rAddr net.Addr, config *Config) (*Conn, error) { ctx, cancel := config.connectContextMaker() defer cancel() - return ClientWithContext(ctx, conn, config) + return ClientWithContext(ctx, conn, rAddr, config) } // Server listens for incoming DTLS connections. // Connection handshake will timeout using ConnectContextMaker in the Config. // If you want to specify the timeout duration, use ServerWithContext() instead. -func Server(conn net.Conn, config *Config) (*Conn, error) { +func Server(conn net.PacketConn, rAddr net.Addr, config *Config) (*Conn, error) { ctx, cancel := config.connectContextMaker() defer cancel() - return ServerWithContext(ctx, conn, config) + return ServerWithContext(ctx, conn, rAddr, config) } -// DialWithContext connects to the given network address and establishes a DTLS connection on top. -func DialWithContext(ctx context.Context, network string, raddr *net.UDPAddr, config *Config) (*Conn, error) { - pConn, err := net.DialUDP(network, nil, raddr) +// DialWithContext connects to the given network address and establishes a DTLS +// connection on top. +func DialWithContext(ctx context.Context, network string, rAddr *net.UDPAddr, config *Config) (*Conn, error) { + // net.ListenUDP is used rather than net.DialUDP as the latter prevents the + // use of net.PacketConn.WriteTo. + // https://github.com/golang/go/blob/ce5e37ec21442c6eb13a43e68ca20129102ebac0/src/net/udpsock_posix.go#L115 + pConn, err := net.ListenUDP(network, nil) if err != nil { return nil, err } - return ClientWithContext(ctx, pConn, config) + + return ClientWithContext(ctx, pConn, rAddr, config) } // ClientWithContext establishes a DTLS connection over an existing connection. -func ClientWithContext(ctx context.Context, conn net.Conn, config *Config) (*Conn, error) { +func ClientWithContext(ctx context.Context, conn net.PacketConn, rAddr net.Addr, config *Config) (*Conn, error) { switch { case config == nil: return nil, errNoConfigProvided @@ -279,16 +298,16 @@ func ClientWithContext(ctx context.Context, conn net.Conn, config *Config) (*Con return nil, errPSKAndIdentityMustBeSetForClient } - return createConn(ctx, conn, config, true, nil) + return createConn(ctx, conn, rAddr, config, true, nil) } // ServerWithContext listens for incoming DTLS connections. -func ServerWithContext(ctx context.Context, conn net.Conn, config *Config) (*Conn, error) { +func ServerWithContext(ctx context.Context, conn net.PacketConn, rAddr net.Addr, config *Config) (*Conn, error) { if config == nil { return nil, errNoConfigProvided } - return createConn(ctx, conn, config, false, nil) + return createConn(ctx, conn, rAddr, config, false, nil) } // Read reads data from the connection. @@ -352,6 +371,7 @@ func (c *Conn) Write(p []byte) (int, error) { Data: p, }, }, + shouldWrapCID: len(c.state.remoteConnectionID) > 0, shouldEncrypt: true, }, }) @@ -400,7 +420,8 @@ func (c *Conn) writePackets(ctx context.Context, pkts []*packet) error { c.log.Tracef("[handshake:%v] -> %s (epoch: %d, seq: %d)", srvCliStr(c.state.isClient), h.Header.Type.String(), p.record.Header.Epoch, h.Header.MessageSequence) - c.handshakeCache.push(handshakeRaw[recordlayer.HeaderSize:], p.record.Header.Epoch, h.Header.MessageSequence, h.Header.Type, c.state.isClient) + + c.handshakeCache.push(handshakeRaw[recordlayer.FixedHeaderSize:], p.record.Header.Epoch, h.Header.MessageSequence, h.Header.Type, c.state.isClient) rawHandshakePackets, err := c.processHandshakePacket(p, h) if err != nil { @@ -421,7 +442,7 @@ func (c *Conn) writePackets(ctx context.Context, pkts []*packet) error { compactedRawPackets := c.compactRawPackets(rawPackets) for _, compactedRawPackets := range compactedRawPackets { - if _, err := c.nextConn.WriteContext(ctx, compactedRawPackets); err != nil { + if _, err := c.nextConn.WriteToContext(ctx, compactedRawPackets, c.rAddr); err != nil { return netError(err) } } @@ -465,9 +486,44 @@ func (c *Conn) processPacket(p *packet) ([]byte, error) { } p.record.Header.SequenceNumber = seq - rawPacket, err := p.record.Marshal() - if err != nil { - return nil, err + var rawPacket []byte + if p.shouldWrapCID { + // Record must be marshaled to populate fields used in inner plaintext. + if _, err := p.record.Marshal(); err != nil { + return nil, err + } + content, err := p.record.Content.Marshal() + if err != nil { + return nil, err + } + inner := &recordlayer.InnerPlaintext{ + Content: content, + RealType: p.record.Header.ContentType, + } + rawInner, err := inner.Marshal() //nolint:govet + if err != nil { + return nil, err + } + cidHeader := &recordlayer.Header{ + Version: p.record.Header.Version, + ContentType: protocol.ContentTypeConnectionID, + Epoch: p.record.Header.Epoch, + ContentLen: uint16(len(rawInner)), + ConnectionID: c.state.remoteConnectionID, + SequenceNumber: p.record.Header.SequenceNumber, + } + rawPacket, err = cidHeader.Marshal() + if err != nil { + return nil, err + } + p.record.Header = *cidHeader + rawPacket = append(rawPacket, rawInner...) + } else { + var err error + rawPacket, err = p.record.Marshal() + if err != nil { + return nil, err + } } if p.shouldEncrypt { @@ -499,22 +555,49 @@ func (c *Conn) processHandshakePacket(p *packet, h *handshake.Handshake) ([][]by return nil, errSequenceNumberOverflow } - recordlayerHeader := &recordlayer.Header{ - Version: p.record.Header.Version, - ContentType: p.record.Header.ContentType, - ContentLen: uint16(len(handshakeFragment)), - Epoch: p.record.Header.Epoch, - SequenceNumber: seq, - } + var rawPacket []byte + if p.shouldWrapCID { + inner := &recordlayer.InnerPlaintext{ + Content: handshakeFragment, + RealType: protocol.ContentTypeHandshake, + Zeros: c.paddingLengthGenerator(uint(len(handshakeFragment))), + } + rawInner, err := inner.Marshal() //nolint:govet + if err != nil { + return nil, err + } + cidHeader := &recordlayer.Header{ + Version: p.record.Header.Version, + ContentType: protocol.ContentTypeConnectionID, + Epoch: p.record.Header.Epoch, + ContentLen: uint16(len(rawInner)), + ConnectionID: c.state.remoteConnectionID, + SequenceNumber: p.record.Header.SequenceNumber, + } + rawPacket, err = cidHeader.Marshal() + if err != nil { + return nil, err + } + p.record.Header = *cidHeader + rawPacket = append(rawPacket, rawInner...) + } else { + recordlayerHeader := &recordlayer.Header{ + Version: p.record.Header.Version, + ContentType: p.record.Header.ContentType, + ContentLen: uint16(len(handshakeFragment)), + Epoch: p.record.Header.Epoch, + SequenceNumber: seq, + } - rawPacket, err := recordlayerHeader.Marshal() - if err != nil { - return nil, err - } + rawPacket, err = recordlayerHeader.Marshal() + if err != nil { + return nil, err + } - p.record.Header = *recordlayerHeader + p.record.Header = *recordlayerHeader + rawPacket = append(rawPacket, handshakeFragment...) + } - rawPacket = append(rawPacket, handshakeFragment...) if p.shouldEncrypt { var err error rawPacket, err = c.state.cipherSuite.Encrypt(p.record, rawPacket) @@ -585,19 +668,19 @@ func (c *Conn) readAndBuffer(ctx context.Context) error { defer poolReadBuffer.Put(bufptr) b := *bufptr - i, err := c.nextConn.ReadContext(ctx, b) + i, rAddr, err := c.nextConn.ReadFromContext(ctx, b) if err != nil { return netError(err) } - pkts, err := recordlayer.UnpackDatagram(b[:i]) + pkts, err := recordlayer.ContentAwareUnpackDatagram(b[:i], len(c.state.localConnectionID)) if err != nil { return err } var hasHandshake bool for _, p := range pkts { - hs, alert, err := c.handleIncomingPacket(ctx, p, true) + hs, alert, err := c.handleIncomingPacket(ctx, p, rAddr, true) if alert != nil { if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil { if err == nil { @@ -605,18 +688,17 @@ func (c *Conn) readAndBuffer(ctx context.Context) error { } } } - if hs { - hasHandshake = true - } var e *alertError - if errors.As(err, &e) { - if e.IsFatalOrCloseNotify() { - return e - } - } else if err != nil { + if errors.As(err, &e) && e.IsFatalOrCloseNotify() { return e } + if err != nil { + return err + } + if hs { + hasHandshake = true + } } if hasHandshake { done := make(chan struct{}) @@ -636,7 +718,7 @@ func (c *Conn) handleQueuedPackets(ctx context.Context) error { c.encryptedPackets = nil for _, p := range pkts { - _, alert, err := c.handleIncomingPacket(ctx, p, false) // don't re-enqueue + _, alert, err := c.handleIncomingPacket(ctx, p.data, p.rAddr, false) // don't re-enqueue if alert != nil { if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil { if err == nil { @@ -645,19 +727,23 @@ func (c *Conn) handleQueuedPackets(ctx context.Context) error { } } var e *alertError - if errors.As(err, &e) { - if e.IsFatalOrCloseNotify() { - return e - } - } else if err != nil { + if errors.As(err, &e) && e.IsFatalOrCloseNotify() { return e } + if err != nil { + return err + } } return nil } -func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, enqueue bool) (bool, *alert.Alert, error) { //nolint:gocognit +func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, rAddr net.Addr, enqueue bool) (bool, *alert.Alert, error) { //nolint:gocognit h := &recordlayer.Header{} + // Set connection ID size so that records of content type tls12_cid will + // be parsed correctly. + if len(c.state.localConnectionID) > 0 { + h.ConnectionID = make([]byte, len(c.state.localConnectionID)) + } if err := h.Unmarshal(buf); err != nil { // Decode error must be silently discarded // [RFC6347 Section-4.1.2.7] @@ -676,7 +762,7 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, enqueue boo } if enqueue { c.log.Debug("received packet of next epoch, queuing packet") - c.encryptedPackets = append(c.encryptedPackets, buf) + c.encryptedPackets = append(c.encryptedPackets, addrPkt{rAddr, buf}) } return false, nil, nil } @@ -695,22 +781,66 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, enqueue boo return false, nil, nil } + // originalCID indicates whether the original record had content type + // Connection ID. + originalCID := false + // Decrypt if h.Epoch != 0 { if c.state.cipherSuite == nil || !c.state.cipherSuite.IsInitialized() { if enqueue { - c.encryptedPackets = append(c.encryptedPackets, buf) + c.encryptedPackets = append(c.encryptedPackets, addrPkt{rAddr, buf}) c.log.Debug("handshake not finished, queuing packet") } return false, nil, nil } + // If a connection identifier had been negotiated and encryption is + // enabled, the connection identifier MUST be sent. + if len(c.state.localConnectionID) > 0 && h.ContentType != protocol.ContentTypeConnectionID { + c.log.Debug("discarded packet missing connection ID after value negotiated") + return false, nil, nil + } + var err error - buf, err = c.state.cipherSuite.Decrypt(buf) + var hdr recordlayer.Header + if h.ContentType == protocol.ContentTypeConnectionID { + hdr.ConnectionID = make([]byte, len(c.state.localConnectionID)) + } + buf, err = c.state.cipherSuite.Decrypt(hdr, buf) if err != nil { c.log.Debugf("%s: decrypt failed: %s", srvCliStr(c.state.isClient), err) return false, nil, nil } + // If this is a connection ID record, make it look like a normal record for + // further processing. + if h.ContentType == protocol.ContentTypeConnectionID { + originalCID = true + ip := &recordlayer.InnerPlaintext{} + if err := ip.Unmarshal(buf[h.Size():]); err != nil { //nolint:govet + c.log.Debugf("unpacking inner plaintext failed: %s", err) + return false, nil, nil + } + unpacked := &recordlayer.Header{ + ContentType: ip.RealType, + ContentLen: uint16(len(ip.Content)), + Version: h.Version, + Epoch: h.Epoch, + SequenceNumber: h.SequenceNumber, + } + buf, err = unpacked.Marshal() + if err != nil { + c.log.Debugf("converting CID record to inner plaintext failed: %s", err) + return false, nil, nil + } + buf = append(buf, ip.Content...) + } + + // If connection ID does not match discard the packet. + if !bytes.Equal(c.state.localConnectionID, h.ConnectionID) { + c.log.Debug("unexpected connection ID") + return false, nil, nil + } } isHandshake, err := c.fragmentBuffer.push(append([]byte{}, buf...)) @@ -738,6 +868,7 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, enqueue boo return false, &alert.Alert{Level: alert.Fatal, Description: alert.DecodeError}, err } + isLatestSeqNum := false switch content := r.Content.(type) { case *alert.Alert: c.log.Tracef("%s: <- %s", srvCliStr(c.state.isClient), content.String()) @@ -746,12 +877,12 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, enqueue boo // Respond with a close_notify [RFC5246 Section 7.2.1] a = &alert.Alert{Level: alert.Warning, Description: alert.CloseNotify} } - markPacketAsValid() + _ = markPacketAsValid() return false, a, &alertError{content} case *protocol.ChangeCipherSpec: if c.state.cipherSuite == nil || !c.state.cipherSuite.IsInitialized() { if enqueue { - c.encryptedPackets = append(c.encryptedPackets, buf) + c.encryptedPackets = append(c.encryptedPackets, addrPkt{rAddr, buf}) c.log.Debugf("CipherSuite not initialized, queuing packet") } return false, nil, nil @@ -762,14 +893,14 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, enqueue boo if c.state.getRemoteEpoch()+1 == newRemoteEpoch { c.setRemoteEpoch(newRemoteEpoch) - markPacketAsValid() + isLatestSeqNum = markPacketAsValid() } case *protocol.ApplicationData: if h.Epoch == 0 { return false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, errApplicationDataEpochZero } - markPacketAsValid() + isLatestSeqNum = markPacketAsValid() select { case c.decrypted <- content.Data: @@ -780,6 +911,18 @@ func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, enqueue boo default: return false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, fmt.Errorf("%w: %d", errUnhandledContextType, content.ContentType()) } + + // Any valid connection ID record is a candidate for updating the remote + // address if it is the latest record received. + // https://datatracker.ietf.org/doc/html/rfc9146#peer-address-update + if originalCID && isLatestSeqNum { + if rAddr != c.RemoteAddr() { + c.lock.Lock() + c.rAddr = rAddr + c.lock.Unlock() + } + } + return false, nil, nil } @@ -810,6 +953,7 @@ func (c *Conn) notify(ctx context.Context, level alert.Level, desc alert.Descrip Description: desc, }, }, + shouldWrapCID: len(c.state.remoteConnectionID) > 0, shouldEncrypt: c.isHandshakeCompletedSuccessfully(), }, }) @@ -883,7 +1027,7 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh } } else { switch { - case errors.Is(err, context.DeadlineExceeded), errors.Is(err, context.Canceled), errors.Is(err, io.EOF): + case errors.Is(err, context.DeadlineExceeded), errors.Is(err, context.Canceled), errors.Is(err, io.EOF), errors.Is(err, net.ErrClosed): default: if c.isHandshakeCompletedSuccessfully() { // Keep read loop and pass the read error to Read() @@ -997,7 +1141,9 @@ func (c *Conn) LocalAddr() net.Addr { // RemoteAddr implements net.Conn.RemoteAddr func (c *Conn) RemoteAddr() net.Addr { - return c.nextConn.RemoteAddr() + c.lock.RLock() + defer c.lock.RUnlock() + return c.rAddr } func (c *Conn) sessionKey() []byte { @@ -1005,7 +1151,7 @@ func (c *Conn) sessionKey() []byte { // As ServerName can be like 0.example.com, it's better to add // delimiter character which is not allowed to be in // neither address or domain name. - return []byte(c.nextConn.RemoteAddr().String() + "_" + c.fsm.cfg.serverName) + return []byte(c.rAddr.String() + "_" + c.fsm.cfg.serverName) } return c.state.SessionID } diff --git a/vendor/github.com/pion/dtls/v2/connection_id.go b/vendor/github.com/pion/dtls/v2/connection_id.go new file mode 100644 index 00000000000..b2fbbd7a87e --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/connection_id.go @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package dtls + +import ( + "crypto/rand" + + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/extension" + "github.com/pion/dtls/v2/pkg/protocol/handshake" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) + +// RandomCIDGenerator is a random Connection ID generator where CID is the +// specified size. Specifying a size of 0 will indicate to peers that sending a +// Connection ID is not necessary. +func RandomCIDGenerator(size int) func() []byte { + return func() []byte { + cid := make([]byte, size) + if _, err := rand.Read(cid); err != nil { + panic(err) //nolint -- nonrecoverable + } + return cid + } +} + +// OnlySendCIDGenerator enables sending Connection IDs negotiated with a peer, +// but indicates to the peer that sending Connection IDs in return is not +// necessary. +func OnlySendCIDGenerator() func() []byte { + return func() []byte { + return nil + } +} + +// cidDatagramRouter extracts connection IDs from incoming datagram payloads and +// uses them to route to the proper connection. +// NOTE: properly routing datagrams based on connection IDs requires using +// constant size connection IDs. +func cidDatagramRouter(size int) func([]byte) (string, bool) { + return func(packet []byte) (string, bool) { + pkts, err := recordlayer.ContentAwareUnpackDatagram(packet, size) + if err != nil || len(pkts) < 1 { + return "", false + } + for _, pkt := range pkts { + h := &recordlayer.Header{ + ConnectionID: make([]byte, size), + } + if err := h.Unmarshal(pkt); err != nil { + continue + } + if h.ContentType != protocol.ContentTypeConnectionID { + continue + } + return string(h.ConnectionID), true + } + return "", false + } +} + +// cidConnIdentifier extracts connection IDs from outgoing ServerHello records +// and associates them with the associated connection. +// NOTE: a ServerHello should always be the first record in a datagram if +// multiple are present, so we avoid iterating through all packets if the first +// is not a ServerHello. +func cidConnIdentifier() func([]byte) (string, bool) { + return func(packet []byte) (string, bool) { + pkts, err := recordlayer.UnpackDatagram(packet) + if err != nil || len(pkts) < 1 { + return "", false + } + var h recordlayer.Header + if hErr := h.Unmarshal(pkts[0]); hErr != nil { + return "", false + } + if h.ContentType != protocol.ContentTypeHandshake { + return "", false + } + var hh handshake.Header + var sh handshake.MessageServerHello + for _, pkt := range pkts { + if hhErr := hh.Unmarshal(pkt[recordlayer.FixedHeaderSize:]); hhErr != nil { + continue + } + if err = sh.Unmarshal(pkt[recordlayer.FixedHeaderSize+handshake.HeaderLength:]); err == nil { + break + } + } + if err != nil { + return "", false + } + for _, ext := range sh.Extensions { + if e, ok := ext.(*extension.ConnectionID); ok { + return string(e.CID), true + } + } + return "", false + } +} diff --git a/vendor/github.com/pion/dtls/v2/flight0handler.go b/vendor/github.com/pion/dtls/v2/flight0handler.go index ec766ddff2f..648c52883a5 100644 --- a/vendor/github.com/pion/dtls/v2/flight0handler.go +++ b/vendor/github.com/pion/dtls/v2/flight0handler.go @@ -22,6 +22,12 @@ func flight0Parse(_ context.Context, _ flightConn, state *State, cache *handshak // No valid message received. Keep reading return 0, nil, nil } + + // Connection Identifiers must be negotiated afresh on session resumption. + // https://datatracker.ietf.org/doc/html/rfc9146#name-the-connection_id-extension + state.localConnectionID = nil + state.remoteConnectionID = nil + state.handshakeRecvSequence = seq var clientHello *handshake.MessageClientHello @@ -69,9 +75,21 @@ func flight0Parse(_ context.Context, _ flightConn, state *State, cache *handshak state.serverName = e.ServerName // remote server name case *extension.ALPN: state.peerSupportedProtocols = e.ProtocolNameList + case *extension.ConnectionID: + // Only set connection ID to be sent if server supports connection + // IDs. + if cfg.connectionIDGenerator != nil { + state.remoteConnectionID = e.CID + } } } + // If the client doesn't support connection IDs, the server should not + // expect one to be sent. + if state.remoteConnectionID == nil { + state.localConnectionID = nil + } + if cfg.extendedMasterSecret == RequireExtendedMasterSecret && !state.extendedMasterSecret { return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errServerRequiredButNoClientEMS } diff --git a/vendor/github.com/pion/dtls/v2/flight1handler.go b/vendor/github.com/pion/dtls/v2/flight1handler.go index 94fdc222d07..48bc88213e3 100644 --- a/vendor/github.com/pion/dtls/v2/flight1handler.go +++ b/vendor/github.com/pion/dtls/v2/flight1handler.go @@ -118,6 +118,21 @@ func flight1Generate(c flightConn, state *State, _ *handshakeCache, cfg *handsha } } + // If we have a connection ID generator, use it. The CID may be zero length, + // in which case we are just requesting that the server send us a CID to + // use. + if cfg.connectionIDGenerator != nil { + state.localConnectionID = cfg.connectionIDGenerator() + // The presence of a generator indicates support for connection IDs. We + // use the presence of a non-nil local CID in flight 3 to determine + // whether we send a CID in the second ClientHello, so we convert any + // nil CID returned by a generator to []byte{}. + if state.localConnectionID == nil { + state.localConnectionID = []byte{} + } + extensions = append(extensions, &extension.ConnectionID{CID: state.localConnectionID}) + } + return []*packet{ { record: &recordlayer.RecordLayer{ diff --git a/vendor/github.com/pion/dtls/v2/flight3handler.go b/vendor/github.com/pion/dtls/v2/flight3handler.go index 5a763dc08d5..b17a7986c3e 100644 --- a/vendor/github.com/pion/dtls/v2/flight3handler.go +++ b/vendor/github.com/pion/dtls/v2/flight3handler.go @@ -66,8 +66,20 @@ func flight3Parse(ctx context.Context, c flightConn, state *State, cache *handsh return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, extension.ErrALPNInvalidFormat // Meh, internal error? } state.NegotiatedProtocol = e.ProtocolNameList[0] + case *extension.ConnectionID: + // Only set connection ID to be sent if client supports connection + // IDs. + if cfg.connectionIDGenerator != nil { + state.remoteConnectionID = e.CID + } } } + // If the server doesn't support connection IDs, the client should not + // expect one to be sent. + if state.remoteConnectionID == nil { + state.localConnectionID = nil + } + if cfg.extendedMasterSecret == RequireExtendedMasterSecret && !state.extendedMasterSecret { return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errClientRequiredButNoServerEMS } @@ -268,6 +280,12 @@ func flight3Generate(_ flightConn, state *State, _ *handshakeCache, cfg *handsha extensions = append(extensions, &extension.ALPN{ProtocolNameList: cfg.supportedProtocols}) } + // If we sent a connection ID on the first ClientHello, send it on the + // second. + if state.localConnectionID != nil { + extensions = append(extensions, &extension.ConnectionID{CID: state.localConnectionID}) + } + return []*packet{ { record: &recordlayer.RecordLayer{ diff --git a/vendor/github.com/pion/dtls/v2/flight4handler.go b/vendor/github.com/pion/dtls/v2/flight4handler.go index 67a48646162..52568139f7a 100644 --- a/vendor/github.com/pion/dtls/v2/flight4handler.go +++ b/vendor/github.com/pion/dtls/v2/flight4handler.go @@ -218,7 +218,7 @@ func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handsh return flight6, nil, nil } -func flight4Generate(_ flightConn, state *State, _ *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { +func flight4Generate(_ flightConn, state *State, _ *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { //nolint:gocognit extensions := []extension.Extension{&extension.RenegotiationInfo{ RenegotiatedConnection: 0, }} @@ -250,6 +250,15 @@ func flight4Generate(_ flightConn, state *State, _ *handshakeCache, cfg *handsha state.NegotiatedProtocol = selectedProto } + // If we have a connection ID generator, we are willing to use connection + // IDs. We already know whether the client supports connection IDs from + // parsing the ClientHello, so avoid setting local connection ID if the + // client won't send it. + if cfg.connectionIDGenerator != nil && state.remoteConnectionID != nil { + state.localConnectionID = cfg.connectionIDGenerator() + extensions = append(extensions, &extension.ConnectionID{CID: state.localConnectionID}) + } + var pkts []*packet cipherSuiteID := uint16(state.cipherSuite.ID()) diff --git a/vendor/github.com/pion/dtls/v2/flight5handler.go b/vendor/github.com/pion/dtls/v2/flight5handler.go index e8adf4f3620..e1cca6238ac 100644 --- a/vendor/github.com/pion/dtls/v2/flight5handler.go +++ b/vendor/github.com/pion/dtls/v2/flight5handler.go @@ -173,7 +173,7 @@ func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *han merged = append(merged, raw...) } - if alertPtr, err := initalizeCipherSuite(state, cache, cfg, serverKeyExchange, merged); err != nil { + if alertPtr, err := initializeCipherSuite(state, cache, cfg, serverKeyExchange, merged); err != nil { return nil, alertPtr, err } @@ -277,6 +277,7 @@ func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *han }, }, }, + shouldWrapCID: len(state.remoteConnectionID) > 0, shouldEncrypt: true, resetLocalSequenceNumber: true, }) @@ -284,7 +285,7 @@ func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *han return pkts, nil, nil } -func initalizeCipherSuite(state *State, cache *handshakeCache, cfg *handshakeConfig, h *handshake.MessageServerKeyExchange, sendingPlainText []byte) (*alert.Alert, error) { //nolint:gocognit +func initializeCipherSuite(state *State, cache *handshakeCache, cfg *handshakeConfig, h *handshake.MessageServerKeyExchange, sendingPlainText []byte) (*alert.Alert, error) { //nolint:gocognit if state.cipherSuite.IsInitialized() { 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 57ac1436008..c038904256d 100644 --- a/vendor/github.com/pion/dtls/v2/flight6handler.go +++ b/vendor/github.com/pion/dtls/v2/flight6handler.go @@ -77,6 +77,7 @@ func flight6Generate(_ flightConn, state *State, cache *handshakeCache, cfg *han }, }, }, + shouldWrapCID: len(state.remoteConnectionID) > 0, shouldEncrypt: true, resetLocalSequenceNumber: true, }, diff --git a/vendor/github.com/pion/dtls/v2/fragment_buffer.go b/vendor/github.com/pion/dtls/v2/fragment_buffer.go index f200337589a..fb5af6c3dbb 100644 --- a/vendor/github.com/pion/dtls/v2/fragment_buffer.go +++ b/vendor/github.com/pion/dtls/v2/fragment_buffer.go @@ -58,7 +58,7 @@ func (f *fragmentBuffer) push(buf []byte) (bool, error) { return false, nil } - for buf = buf[recordlayer.HeaderSize:]; len(buf) != 0; frag = new(fragment) { + for buf = buf[recordlayer.FixedHeaderSize:]; len(buf) != 0; frag = new(fragment) { if err := frag.handshakeHeader.Unmarshal(buf); err != nil { return false, err } diff --git a/vendor/github.com/pion/dtls/v2/handshaker.go b/vendor/github.com/pion/dtls/v2/handshaker.go index 1c6d58fe99e..46fbd38bda2 100644 --- a/vendor/github.com/pion/dtls/v2/handshaker.go +++ b/vendor/github.com/pion/dtls/v2/handshaker.go @@ -113,6 +113,7 @@ type handshakeConfig struct { customCipherSuites func() []CipherSuite ellipticCurves []elliptic.Curve insecureSkipHelloVerify bool + connectionIDGenerator func() []byte onFlightState func(flightVal, handshakeState) log logging.LeveledLogger @@ -263,9 +264,7 @@ func (s *handshakeFSM) wait(ctx context.Context, c flightConn) (handshakeState, parse, errFlight := s.currentFlight.getFlightParser() if errFlight != nil { if alertErr := c.notify(ctx, alert.Fatal, alert.InternalError); alertErr != nil { - if errFlight != nil { - return handshakeErrored, alertErr - } + return handshakeErrored, alertErr } return handshakeErrored, errFlight } @@ -311,9 +310,7 @@ func (s *handshakeFSM) finish(ctx context.Context, c flightConn) (handshakeState parse, errFlight := s.currentFlight.getFlightParser() if errFlight != nil { if alertErr := c.notify(ctx, alert.Fatal, alert.InternalError); alertErr != nil { - if errFlight != nil { - return handshakeErrored, alertErr - } + return handshakeErrored, alertErr } return handshakeErrored, errFlight } 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 index dc51198237a..ee3cca90310 100644 --- a/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_ccm.go +++ b/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_ccm.go @@ -103,11 +103,11 @@ func (c *AesCcm) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, erro } // Decrypt decrypts a single TLS RecordLayer -func (c *AesCcm) Decrypt(raw []byte) ([]byte, error) { +func (c *AesCcm) Decrypt(h recordlayer.Header, 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) + return cipherSuite.Decrypt(h, raw) } 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 0c919fe47a9..362370b9874 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 @@ -98,11 +98,11 @@ func (c *TLSEcdheEcdsaWithAes128GcmSha256) Encrypt(pkt *recordlayer.RecordLayer, } // Decrypt decrypts a single TLS RecordLayer -func (c *TLSEcdheEcdsaWithAes128GcmSha256) Decrypt(raw []byte) ([]byte, error) { +func (c *TLSEcdheEcdsaWithAes128GcmSha256) Decrypt(h recordlayer.Header, raw []byte) ([]byte, error) { cipherSuite, ok := c.gcm.Load().(*ciphersuite.GCM) if !ok { return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) } - return cipherSuite.Decrypt(raw) + return cipherSuite.Decrypt(h, 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 577192c89b1..07ad66fd1a4 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 @@ -104,11 +104,11 @@ func (c *TLSEcdheEcdsaWithAes256CbcSha) Encrypt(pkt *recordlayer.RecordLayer, ra } // Decrypt decrypts a single TLS RecordLayer -func (c *TLSEcdheEcdsaWithAes256CbcSha) Decrypt(raw []byte) ([]byte, error) { +func (c *TLSEcdheEcdsaWithAes256CbcSha) Decrypt(h recordlayer.Header, raw []byte) ([]byte, error) { cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC) if !ok { return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) } - return cipherSuite.Decrypt(raw) + return cipherSuite.Decrypt(h, raw) } 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 index 75a25633a4f..10cc58c0ba2 100644 --- 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 @@ -108,11 +108,11 @@ func (c *TLSEcdhePskWithAes128CbcSha256) Encrypt(pkt *recordlayer.RecordLayer, r } // Decrypt decrypts a single TLS RecordLayer -func (c *TLSEcdhePskWithAes128CbcSha256) Decrypt(raw []byte) ([]byte, error) { +func (c *TLSEcdhePskWithAes128CbcSha256) Decrypt(h recordlayer.Header, 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) + return cipherSuite.Decrypt(h, raw) } 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 7336ad946a7..dea0dfc75ee 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 @@ -103,11 +103,11 @@ func (c *TLSPskWithAes128CbcSha256) Encrypt(pkt *recordlayer.RecordLayer, raw [] } // Decrypt decrypts a single TLS RecordLayer -func (c *TLSPskWithAes128CbcSha256) Decrypt(raw []byte) ([]byte, error) { +func (c *TLSPskWithAes128CbcSha256) Decrypt(h recordlayer.Header, raw []byte) ([]byte, error) { cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC) if !ok { return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit) } - return cipherSuite.Decrypt(raw) + return cipherSuite.Decrypt(h, raw) } diff --git a/vendor/github.com/pion/dtls/v2/internal/net/buffer.go b/vendor/github.com/pion/dtls/v2/internal/net/buffer.go new file mode 100644 index 00000000000..9ab290e4cd5 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/net/buffer.go @@ -0,0 +1,235 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package net implements DTLS specific networking primitives. +// NOTE: this package is an adaption of pion/transport/packetio that allows for +// storing a remote address alongside each packet in the buffer and implements +// relevant methods of net.PacketConn. If possible, the updates made in this +// repository will be reflected back upstream. If not, it is likely that this +// will be moved to a public package in this repository. +// +// This package was migrated from pion/transport/packetio at +// https://github.com/pion/transport/commit/6890c795c807a617c054149eee40a69d7fdfbfdb +package net + +import ( + "bytes" + "errors" + "io" + "net" + "sync" + "time" + + "github.com/pion/transport/v3/deadline" +) + +// ErrTimeout indicates that deadline was reached before operation could be +// completed. +var ErrTimeout = errors.New("buffer: i/o timeout") + +// AddrPacket is a packet payload and the associated remote address from which +// it was received. +type AddrPacket struct { + addr net.Addr + data bytes.Buffer +} + +// PacketBuffer is a circular buffer for network packets. Each slot in the +// buffer contains the remote address from which the packet was received, as +// well as the packet data. +type PacketBuffer struct { + mutex sync.Mutex + + packets []AddrPacket + write, read int + + // full indicates whether the buffer is full, which is needed to distinguish + // when the write pointer and read pointer are at the same index. + full bool + + notify chan struct{} + closed bool + + readDeadline *deadline.Deadline +} + +// NewPacketBuffer creates a new PacketBuffer. +func NewPacketBuffer() *PacketBuffer { + return &PacketBuffer{ + readDeadline: deadline.New(), + // In the narrow context in which this package is currently used, there + // will always be at least one packet written to the buffer. Therefore, + // we opt to allocate with size of 1 during construction, rather than + // waiting until that first packet is written. + packets: make([]AddrPacket, 1), + full: false, + } +} + +// WriteTo writes a single packet to the buffer. The supplied address will +// remain associated with the packet. +func (b *PacketBuffer) WriteTo(p []byte, addr net.Addr) (int, error) { + b.mutex.Lock() + + if b.closed { + b.mutex.Unlock() + return 0, io.ErrClosedPipe + } + + var notify chan struct{} + if b.notify != nil { + notify = b.notify + b.notify = nil + } + + // Check to see if we are full. + if b.full { + // If so, grow AddrPacket buffer. + var newSize int + if len(b.packets) < 128 { + // Double the number of packets. + newSize = len(b.packets) * 2 + } else { + // Increase the number of packets by 25%. + newSize = 5 * len(b.packets) / 4 + } + newBuf := make([]AddrPacket, newSize) + var n int + if b.read < b.write { + n = copy(newBuf, b.packets[b.read:b.write]) + } else { + n = copy(newBuf, b.packets[b.read:]) + n += copy(newBuf[n:], b.packets[:b.write]) + } + + b.packets = newBuf + + // Update write pointer to point to new location and mark buffer as not + // full. + b.write = n + b.full = false + } + + // Store the packet at the write pointer. + packet := &b.packets[b.write] + packet.data.Reset() + n, err := packet.data.Write(p) + if err != nil { + b.mutex.Unlock() + return n, err + } + packet.addr = addr + + // Increment write pointer. + b.write++ + + // If the write pointer is equal to the length of the buffer, wrap around. + if len(b.packets) == b.write { + b.write = 0 + } + + // If a write resulted in making write and read pointers equivalent, then we + // are full. + if b.write == b.read { + b.full = true + } + + b.mutex.Unlock() + + if notify != nil { + close(notify) + } + + return n, nil +} + +// ReadFrom reads a single packet from the buffer, or blocks until one is +// available. +func (b *PacketBuffer) ReadFrom(packet []byte) (n int, addr net.Addr, err error) { + select { + case <-b.readDeadline.Done(): + return 0, nil, ErrTimeout + default: + } + + for { + b.mutex.Lock() + + if b.read != b.write || b.full { + ap := b.packets[b.read] + if len(packet) < ap.data.Len() { + b.mutex.Unlock() + return 0, nil, io.ErrShortBuffer + } + + // Copy packet data from buffer. + n, err := ap.data.Read(packet) + if err != nil { + b.mutex.Unlock() + return n, nil, err + } + + // Advance read pointer. + b.read++ + if len(b.packets) == b.read { + b.read = 0 + } + + // If we were full before reading and have successfully read, we are + // no longer full. + if b.full { + b.full = false + } + + b.mutex.Unlock() + + return n, ap.addr, nil + } + + if b.closed { + b.mutex.Unlock() + return 0, nil, io.EOF + } + + if b.notify == nil { + b.notify = make(chan struct{}) + } + notify := b.notify + b.mutex.Unlock() + + select { + case <-b.readDeadline.Done(): + return 0, nil, ErrTimeout + case <-notify: + } + } +} + +// Close closes the buffer, allowing unread packets to be read, but erroring on +// any new writes. +func (b *PacketBuffer) Close() (err error) { + b.mutex.Lock() + + if b.closed { + b.mutex.Unlock() + return nil + } + + notify := b.notify + b.notify = nil + b.closed = true + + b.mutex.Unlock() + + if notify != nil { + close(notify) + } + + return nil +} + +// SetReadDeadline sets the read deadline for the buffer. +func (b *PacketBuffer) SetReadDeadline(t time.Time) error { + b.readDeadline.Set(t) + return nil +} diff --git a/vendor/github.com/pion/dtls/v2/internal/net/udp/packet_conn.go b/vendor/github.com/pion/dtls/v2/internal/net/udp/packet_conn.go new file mode 100644 index 00000000000..7dafbe23e72 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/internal/net/udp/packet_conn.go @@ -0,0 +1,407 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package udp implements DTLS specific UDP networking primitives. +// NOTE: this package is an adaption of pion/transport/udp that allows for +// routing datagrams based on identifiers other than the remote address. The +// primary use case for this functionality is routing based on DTLS connection +// IDs. In order to allow for consumers of this package to treat connections as +// generic net.PackageConn, routing and identitier establishment is based on +// custom introspecion of datagrams, rather than direct intervention by +// consumers. If possible, the updates made in this repository will be reflected +// back upstream. If not, it is likely that this will be moved to a public +// package in this repository. +// +// This package was migrated from pion/transport/udp at +// https://github.com/pion/transport/commit/6890c795c807a617c054149eee40a69d7fdfbfdb +package udp + +import ( + "context" + "errors" + "net" + "sync" + "sync/atomic" + "time" + + idtlsnet "github.com/pion/dtls/v2/internal/net" + dtlsnet "github.com/pion/dtls/v2/pkg/net" + "github.com/pion/transport/v3/deadline" +) + +const ( + receiveMTU = 8192 + defaultListenBacklog = 128 // same as Linux default +) + +// Typed errors +var ( + ErrClosedListener = errors.New("udp: listener closed") + ErrListenQueueExceeded = errors.New("udp: listen queue exceeded") +) + +// listener augments a connection-oriented Listener over a UDP PacketConn +type listener struct { + pConn *net.UDPConn + + accepting atomic.Value // bool + acceptCh chan *PacketConn + doneCh chan struct{} + doneOnce sync.Once + acceptFilter func([]byte) bool + datagramRouter func([]byte) (string, bool) + connIdentifier func([]byte) (string, bool) + + connLock sync.Mutex + conns map[string]*PacketConn + 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. +func (l *listener) Accept() (net.PacketConn, net.Addr, error) { + select { + case c := <-l.acceptCh: + l.connWG.Add(1) + return c, c.raddr, nil + + case <-l.readDoneCh: + err, _ := l.errRead.Load().(error) + return nil, nil, err + + case <-l.doneCh: + return nil, nil, ErrClosedListener + } +} + +// Close closes the listener. +// Any blocked Accept operations will be unblocked and return errors. +func (l *listener) Close() error { + var err error + l.doneOnce.Do(func() { + l.accepting.Store(false) + close(l.doneCh) + + l.connLock.Lock() + // Close unaccepted connections + lclose: + for { + select { + case c := <-l.acceptCh: + close(c.doneCh) + // If we have an alternate identifier, remove it from the connection + // map. + if id := c.id.Load(); id != nil { + delete(l.conns, id.(string)) //nolint:forcetypeassert + } + // If we haven't already removed the remote address, remove it + // from the connection map. + if c.rmraddr.Load() == nil { + delete(l.conns, c.raddr.String()) + c.rmraddr.Store(true) + } + default: + break lclose + } + } + nConns := len(l.conns) + l.connLock.Unlock() + + l.connWG.Done() + + if nConns == 0 { + // Wait if this is the final connection. + l.readWG.Wait() + if errClose, ok := l.errClose.Load().(error); ok { + err = errClose + } + } else { + err = nil + } + }) + + return err +} + +// Addr returns the listener's network address. +func (l *listener) Addr() net.Addr { + return l.pConn.LocalAddr() +} + +// ListenConfig stores options for listening to an address. +type ListenConfig struct { + // Backlog defines the maximum length of the queue of pending + // connections. It is equivalent of the backlog argument of + // POSIX listen function. + // If a connection request arrives when the queue is full, + // the request will be silently discarded, unlike TCP. + // Set zero to use default value 128 which is same as Linux default. + Backlog int + + // AcceptFilter determines whether the new conn should be made for + // the incoming packet. If not set, any packet creates new conn. + AcceptFilter func([]byte) bool + + // DatagramRouter routes an incoming datagram to a connection by extracting + // an identifier from the its paylod + DatagramRouter func([]byte) (string, bool) + + // ConnectionIdentifier extracts an identifier from an outgoing packet. If + // the identifier is not already associated with the connection, it will be + // added. + ConnectionIdentifier func([]byte) (string, bool) +} + +// Listen creates a new listener based on the ListenConfig. +func (lc *ListenConfig) Listen(network string, laddr *net.UDPAddr) (dtlsnet.PacketListener, error) { + if lc.Backlog == 0 { + lc.Backlog = defaultListenBacklog + } + + conn, err := net.ListenUDP(network, laddr) + if err != nil { + return nil, err + } + + l := &listener{ + pConn: conn, + acceptCh: make(chan *PacketConn, lc.Backlog), + conns: make(map[string]*PacketConn), + doneCh: make(chan struct{}), + acceptFilter: lc.AcceptFilter, + datagramRouter: lc.DatagramRouter, + connIdentifier: lc.ConnectionIdentifier, + readDoneCh: make(chan struct{}), + } + + l.accepting.Store(true) + l.connWG.Add(1) + l.readWG.Add(2) // wait readLoop and Close execution routine + + go l.readLoop() + go func() { + l.connWG.Wait() + if err := l.pConn.Close(); err != nil { + l.errClose.Store(err) + } + l.readWG.Done() + }() + + return l, nil +} + +// Listen creates a new listener using default ListenConfig. +func Listen(network string, laddr *net.UDPAddr) (dtlsnet.PacketListener, error) { + return (&ListenConfig{}).Listen(network, laddr) +} + +// readLoop dispatches packets to the proper connection, creating a new one if +// necessary, until all connections are closed. +func (l *listener) readLoop() { + defer l.readWG.Done() + defer close(l.readDoneCh) + + buf := make([]byte, receiveMTU) + + for { + n, raddr, err := l.pConn.ReadFrom(buf) + if err != nil { + l.errRead.Store(err) + return + } + conn, ok, err := l.getConn(raddr, buf[:n]) + if err != nil { + continue + } + if ok { + _, _ = conn.buffer.WriteTo(buf[:n], raddr) + } + } +} + +// getConn gets an existing connection or creates a new one. +func (l *listener) getConn(raddr net.Addr, buf []byte) (*PacketConn, bool, error) { + l.connLock.Lock() + defer l.connLock.Unlock() + // If we have a custom resolver, use it. + if l.datagramRouter != nil { + if id, ok := l.datagramRouter(buf); ok { + if conn, ok := l.conns[id]; ok { + return conn, true, nil + } + } + } + + // If we don't have a custom resolver, or we were unable to find an + // associated connection, fall back to remote address. + conn, ok := l.conns[raddr.String()] + if !ok { + if isAccepting, ok := l.accepting.Load().(bool); !isAccepting || !ok { + return nil, false, ErrClosedListener + } + if l.acceptFilter != nil { + if !l.acceptFilter(buf) { + return nil, false, nil + } + } + conn = l.newPacketConn(raddr) + select { + case l.acceptCh <- conn: + l.conns[raddr.String()] = conn + default: + return nil, false, ErrListenQueueExceeded + } + } + return conn, true, nil +} + +// PacketConn is a net.PacketConn implementation that is able to dictate its +// routing ID via an alternate identifier from its remote address. Internal +// buffering is performed for reads, and writes are passed through to the +// underlying net.PacketConn. +type PacketConn struct { + listener *listener + + raddr net.Addr + rmraddr atomic.Value // bool + id atomic.Value // string + + buffer *idtlsnet.PacketBuffer + + doneCh chan struct{} + doneOnce sync.Once + + writeDeadline *deadline.Deadline +} + +// newPacketConn constructs a new PacketConn. +func (l *listener) newPacketConn(raddr net.Addr) *PacketConn { + return &PacketConn{ + listener: l, + raddr: raddr, + buffer: idtlsnet.NewPacketBuffer(), + doneCh: make(chan struct{}), + writeDeadline: deadline.New(), + } +} + +// ReadFrom reads a single packet payload and its associated remote address from +// the underlying buffer. +func (c *PacketConn) ReadFrom(p []byte) (int, net.Addr, error) { + return c.buffer.ReadFrom(p) +} + +// WriteTo writes len(p) bytes from p to the specified address. +func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + // If we have a connection identifier, check to see if the outgoing packet + // sets it. + if c.listener.connIdentifier != nil { + id := c.id.Load() + // Only update establish identifier if we haven't already done so. + if id == nil { + candidate, ok := c.listener.connIdentifier(p) + // If we have an identifier, add entry to connection map. + if ok { + c.listener.connLock.Lock() + c.listener.conns[candidate] = c + c.listener.connLock.Unlock() + c.id.Store(candidate) + } + } + // If we are writing to a remote address that differs from the initial, + // we have an alternate identifier established, and we haven't already + // freed the remote address, free the remote address to be used by + // another connection. + // Note: this strategy results in holding onto a remote address after it + // is potentially no longer in use by the client. However, releasing + // earlier means that we could miss some packets that should have been + // routed to this connection. Ideally, we would drop the connection + // entry for the remote address as soon as the client starts sending + // using an alternate identifier, but in practice this proves + // challenging because any client could spoof a connection identifier, + // resulting in the remote address entry being dropped prior to the + // "real" client transitioning to sending using the alternate + // identifier. + if id != nil && c.rmraddr.Load() == nil && addr.String() != c.raddr.String() { + c.listener.connLock.Lock() + delete(c.listener.conns, c.raddr.String()) + c.rmraddr.Store(true) + c.listener.connLock.Unlock() + } + } + + select { + case <-c.writeDeadline.Done(): + return 0, context.DeadlineExceeded + default: + } + return c.listener.pConn.WriteTo(p, addr) +} + +// Close closes the conn and releases any Read calls +func (c *PacketConn) Close() error { + var err error + c.doneOnce.Do(func() { + c.listener.connWG.Done() + close(c.doneCh) + c.listener.connLock.Lock() + // If we have an alternate identifier, remove it from the connection + // map. + if id := c.id.Load(); id != nil { + delete(c.listener.conns, id.(string)) //nolint:forcetypeassert + } + // If we haven't already removed the remote address, remove it from the + // connection map. + if c.rmraddr.Load() == nil { + delete(c.listener.conns, c.raddr.String()) + c.rmraddr.Store(true) + } + nConns := len(c.listener.conns) + c.listener.connLock.Unlock() + + 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 { + err = errClose + } + } else { + err = nil + } + + if errBuf := c.buffer.Close(); errBuf != nil && err == nil { + err = errBuf + } + }) + + return err +} + +// LocalAddr implements net.PacketConn.LocalAddr. +func (c *PacketConn) LocalAddr() net.Addr { + return c.listener.pConn.LocalAddr() +} + +// SetDeadline implements net.PacketConn.SetDeadline. +func (c *PacketConn) SetDeadline(t time.Time) error { + c.writeDeadline.Set(t) + return c.SetReadDeadline(t) +} + +// SetReadDeadline implements net.PacketConn.SetReadDeadline. +func (c *PacketConn) SetReadDeadline(t time.Time) error { + return c.buffer.SetReadDeadline(t) +} + +// SetWriteDeadline implements net.PacketConn.SetWriteDeadline. +func (c *PacketConn) SetWriteDeadline(t time.Time) error { + c.writeDeadline.Set(t) + // Write deadline of underlying connection should not be changed + // since the connection can be shared. + return nil +} 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 685910fc213..382a0e1cdd8 100644 --- a/vendor/github.com/pion/dtls/v2/internal/util/util.go +++ b/vendor/github.com/pion/dtls/v2/internal/util/util.go @@ -6,6 +6,8 @@ package util import ( "encoding/binary" + + "golang.org/x/crypto/cryptobyte" ) // BigEndianUint24 returns the value of a big endian uint24 @@ -40,3 +42,10 @@ func Max(a, b int) int { } return b } + +// AddUint48 appends a big-endian, 48-bit value to the byte string. +// Remove if / when https://github.com/golang/crypto/pull/265 is merged +// upstream. +func AddUint48(b *cryptobyte.Builder, v uint64) { + b.AddBytes([]byte{byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}) +} diff --git a/vendor/github.com/pion/dtls/v2/listener.go b/vendor/github.com/pion/dtls/v2/listener.go index 190d236c723..90dbbb427cd 100644 --- a/vendor/github.com/pion/dtls/v2/listener.go +++ b/vendor/github.com/pion/dtls/v2/listener.go @@ -6,9 +6,10 @@ package dtls import ( "net" + "github.com/pion/dtls/v2/internal/net/udp" + dtlsnet "github.com/pion/dtls/v2/pkg/net" "github.com/pion/dtls/v2/pkg/protocol" "github.com/pion/dtls/v2/pkg/protocol/recordlayer" - "github.com/pion/transport/v2/udp" ) // Listen creates a DTLS listener @@ -30,6 +31,12 @@ func Listen(network string, laddr *net.UDPAddr, config *Config) (net.Listener, e return h.ContentType == protocol.ContentTypeHandshake }, } + // If connection ID support is enabled, then they must be supported in + // routing. + if config.ConnectionIDGenerator != nil { + lc.DatagramRouter = cidDatagramRouter(len(config.ConnectionIDGenerator())) + lc.ConnectionIdentifier = cidConnIdentifier() + } parent, err := lc.Listen(network, laddr) if err != nil { return nil, err @@ -41,7 +48,7 @@ func Listen(network string, laddr *net.UDPAddr, config *Config) (net.Listener, e } // NewListener creates a DTLS listener which accepts connections from an inner Listener. -func NewListener(inner net.Listener, config *Config) (net.Listener, error) { +func NewListener(inner dtlsnet.PacketListener, config *Config) (net.Listener, error) { if err := validateConfig(config); err != nil { return nil, err } @@ -55,7 +62,7 @@ func NewListener(inner net.Listener, config *Config) (net.Listener, error) { // listener represents a DTLS listener type listener struct { config *Config - parent net.Listener + parent dtlsnet.PacketListener } // Accept waits for and returns the next connection to the listener. @@ -63,11 +70,11 @@ type listener struct { // Connection handshake will timeout using ConnectContextMaker in the Config. // If you want to specify the timeout duration, set ConnectContextMaker. func (l *listener) Accept() (net.Conn, error) { - c, err := l.parent.Accept() + c, raddr, err := l.parent.Accept() if err != nil { return nil, err } - return Server(c, l.config) + return Server(c, raddr, l.config) } // Close closes the listener. diff --git a/vendor/github.com/pion/dtls/v2/packet.go b/vendor/github.com/pion/dtls/v2/packet.go index 55d6272eec8..052c33a19fe 100644 --- a/vendor/github.com/pion/dtls/v2/packet.go +++ b/vendor/github.com/pion/dtls/v2/packet.go @@ -3,10 +3,13 @@ package dtls -import "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +import ( + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" +) type packet struct { record *recordlayer.RecordLayer shouldEncrypt bool + shouldWrapCID bool resetLocalSequenceNumber bool } 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 460fb14379b..008a8365b8c 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 @@ -15,6 +15,7 @@ import ( //nolint:gci "github.com/pion/dtls/v2/pkg/crypto/prf" "github.com/pion/dtls/v2/pkg/protocol" "github.com/pion/dtls/v2/pkg/protocol/recordlayer" + "golang.org/x/crypto/cryptobyte" ) // block ciphers using cipher block chaining. @@ -64,18 +65,24 @@ func NewCBC(localKey, localWriteIV, localMac, remoteKey, remoteWriteIV, remoteMa // Encrypt encrypt a DTLS RecordLayer message func (c *CBC) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) { - payload := raw[recordlayer.HeaderSize:] - raw = raw[:recordlayer.HeaderSize] + payload := raw[pkt.Header.Size():] + raw = raw[:pkt.Header.Size()] blockSize := c.writeCBC.BlockSize() // Generate + Append MAC h := pkt.Header - MAC, err := c.hmac(h.Epoch, h.SequenceNumber, h.ContentType, h.Version, payload, c.writeMac, c.h) + var err error + var mac []byte + if h.ContentType == protocol.ContentTypeConnectionID { + mac, err = c.hmacCID(h.Epoch, h.SequenceNumber, h.Version, payload, c.writeMac, c.h, h.ConnectionID) + } else { + mac, err = c.hmac(h.Epoch, h.SequenceNumber, h.ContentType, h.Version, payload, c.writeMac, c.h) + } if err != nil { return nil, err } - payload = append(payload, MAC...) + payload = append(payload, mac...) // Generate + Append padding padding := make([]byte, blockSize-len(payload)%blockSize) @@ -96,26 +103,26 @@ func (c *CBC) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) c.writeCBC.CryptBlocks(payload, payload) payload = append(iv, payload...) - // Prepend unencrypte header with encrypted payload + // Prepend unencrypted header with encrypted payload raw = append(raw, payload...) // Update recordLayer size to include IV+MAC+Padding - binary.BigEndian.PutUint16(raw[recordlayer.HeaderSize-2:], uint16(len(raw)-recordlayer.HeaderSize)) + binary.BigEndian.PutUint16(raw[pkt.Header.Size()-2:], uint16(len(raw)-pkt.Header.Size())) return raw, nil } // Decrypt decrypts a DTLS RecordLayer message -func (c *CBC) Decrypt(in []byte) ([]byte, error) { - body := in[recordlayer.HeaderSize:] +func (c *CBC) Decrypt(h recordlayer.Header, in []byte) ([]byte, error) { blockSize := c.readCBC.BlockSize() mac := c.h() - var h recordlayer.Header - err := h.Unmarshal(in) - switch { - case err != nil: + if err := h.Unmarshal(in); err != nil { return nil, err + } + body := in[h.Size():] + + switch { case h.ContentType == protocol.ContentTypeChangeCipherSpec: // Nothing to encrypt with ChangeCipherSpec return in, nil @@ -145,14 +152,19 @@ func (c *CBC) Decrypt(in []byte) ([]byte, error) { dataEnd := len(body) - macSize - paddingLen expectedMAC := body[dataEnd : dataEnd+macSize] - actualMAC, err := c.hmac(h.Epoch, h.SequenceNumber, h.ContentType, h.Version, body[:dataEnd], c.readMac, c.h) - + var err error + var actualMAC []byte + if h.ContentType == protocol.ContentTypeConnectionID { + actualMAC, err = c.hmacCID(h.Epoch, h.SequenceNumber, h.Version, body[:dataEnd], c.readMac, c.h, h.ConnectionID) + } else { + actualMAC, err = c.hmac(h.Epoch, h.SequenceNumber, h.ContentType, h.Version, body[:dataEnd], c.readMac, c.h) + } // Compute Local MAC and compare if err != nil || !hmac.Equal(actualMAC, expectedMAC) { return nil, errInvalidMAC } - return append(in[:recordlayer.HeaderSize], body[:dataEnd]...), nil + return append(in[:h.Size()], body[:dataEnd]...), nil } func (c *CBC) hmac(epoch uint16, sequenceNumber uint64, contentType protocol.ContentType, protocolVersion protocol.Version, payload []byte, key []byte, hf func() hash.Hash) ([]byte, error) { @@ -169,7 +181,45 @@ func (c *CBC) hmac(epoch uint16, sequenceNumber uint64, contentType protocol.Con if _, err := h.Write(msg); err != nil { return nil, err - } else if _, err := h.Write(payload); err != nil { + } + if _, err := h.Write(payload); err != nil { + return nil, err + } + + return h.Sum(nil), nil +} + +// hmacCID calculates a MAC according to +// https://datatracker.ietf.org/doc/html/rfc9146#section-5.1 +func (c *CBC) hmacCID(epoch uint16, sequenceNumber uint64, protocolVersion protocol.Version, payload []byte, key []byte, hf func() hash.Hash, cid []byte) ([]byte, error) { + // Must unmarshal inner plaintext in orde to perform MAC. + ip := &recordlayer.InnerPlaintext{} + if err := ip.Unmarshal(payload); err != nil { + return nil, err + } + + h := hmac.New(hf, key) + + var msg cryptobyte.Builder + + msg.AddUint64(seqNumPlaceholder) + msg.AddUint8(uint8(protocol.ContentTypeConnectionID)) + msg.AddUint8(uint8(len(cid))) + msg.AddUint8(uint8(protocol.ContentTypeConnectionID)) + msg.AddUint8(protocolVersion.Major) + msg.AddUint8(protocolVersion.Minor) + msg.AddUint16(epoch) + util.AddUint48(&msg, sequenceNumber) + msg.AddBytes(cid) + msg.AddUint16(uint16(len(payload))) + msg.AddBytes(ip.Content) + msg.AddUint8(uint8(ip.RealType)) + msg.AddBytes(make([]byte, ip.Zeros)) + + if _, err := h.Write(msg.BytesOrPanic()); err != nil { + return nil, err + } + if _, err := h.Write(payload); err != nil { return nil, err } 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 24050dc9265..6fb185d85f5 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 @@ -62,46 +62,56 @@ func NewCCM(tagLen CCMTagLen, localKey, localWriteIV, remoteKey, remoteWriteIV [ // Encrypt encrypt a DTLS RecordLayer message func (c *CCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) { - payload := raw[recordlayer.HeaderSize:] - raw = raw[:recordlayer.HeaderSize] + payload := raw[pkt.Header.Size():] + raw = raw[:pkt.Header.Size()] nonce := append(append([]byte{}, c.localWriteIV[:4]...), make([]byte, 8)...) if _, err := rand.Read(nonce[4:]); err != nil { return nil, err } - additionalData := generateAEADAdditionalData(&pkt.Header, len(payload)) + var additionalData []byte + if pkt.Header.ContentType == protocol.ContentTypeConnectionID { + additionalData = generateAEADAdditionalDataCID(&pkt.Header, len(payload)) + } else { + additionalData = generateAEADAdditionalData(&pkt.Header, len(payload)) + } encryptedPayload := c.localCCM.Seal(nil, nonce, payload, additionalData) encryptedPayload = append(nonce[4:], encryptedPayload...) raw = append(raw, encryptedPayload...) // Update recordLayer size to include explicit nonce - binary.BigEndian.PutUint16(raw[recordlayer.HeaderSize-2:], uint16(len(raw)-recordlayer.HeaderSize)) + binary.BigEndian.PutUint16(raw[pkt.Header.Size()-2:], uint16(len(raw)-pkt.Header.Size())) return raw, nil } // Decrypt decrypts a DTLS RecordLayer message -func (c *CCM) Decrypt(in []byte) ([]byte, error) { - var h recordlayer.Header - err := h.Unmarshal(in) - switch { - case err != nil: +func (c *CCM) Decrypt(h recordlayer.Header, in []byte) ([]byte, error) { + if err := h.Unmarshal(in); err != nil { return nil, err + } + switch { case h.ContentType == protocol.ContentTypeChangeCipherSpec: // Nothing to encrypt with ChangeCipherSpec return in, nil - case len(in) <= (8 + recordlayer.HeaderSize): + case len(in) <= (8 + h.Size()): return nil, errNotEnoughRoomForNonce } - nonce := append(append([]byte{}, c.remoteWriteIV[:4]...), in[recordlayer.HeaderSize:recordlayer.HeaderSize+8]...) - out := in[recordlayer.HeaderSize+8:] + nonce := append(append([]byte{}, c.remoteWriteIV[:4]...), in[h.Size():h.Size()+8]...) + out := in[h.Size()+8:] - additionalData := generateAEADAdditionalData(&h, len(out)-int(c.tagLen)) + var additionalData []byte + if h.ContentType == protocol.ContentTypeConnectionID { + additionalData = generateAEADAdditionalDataCID(&h, len(out)-int(c.tagLen)) + } else { + additionalData = generateAEADAdditionalData(&h, len(out)-int(c.tagLen)) + } + var err error out, err = c.remoteCCM.Open(out[:0], nonce, out, additionalData) if err != nil { return nil, fmt.Errorf("%w: %v", errDecryptPacket, err) //nolint:errorlint } - return append(in[:recordlayer.HeaderSize], out...), nil + return append(in[:h.Size()], 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 9d9fb7418c1..a3130be1238 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 @@ -8,8 +8,16 @@ import ( "encoding/binary" "errors" + "github.com/pion/dtls/v2/internal/util" "github.com/pion/dtls/v2/pkg/protocol" "github.com/pion/dtls/v2/pkg/protocol/recordlayer" + "golang.org/x/crypto/cryptobyte" +) + +const ( + // 8 bytes of 0xff. + // https://datatracker.ietf.org/doc/html/rfc9146#name-record-payload-protection + seqNumPlaceholder = 0xffffffffffffffff ) var ( @@ -21,6 +29,7 @@ var ( func generateAEADAdditionalData(h *recordlayer.Header, payloadLen int) []byte { var additionalData [13]byte + // SequenceNumber MUST be set first // we only want uint48, clobbering an extra 2 (using uint64, Golang doesn't have uint48) binary.BigEndian.PutUint64(additionalData[:], h.SequenceNumber) @@ -33,6 +42,25 @@ func generateAEADAdditionalData(h *recordlayer.Header, payloadLen int) []byte { return additionalData[:] } +// generateAEADAdditionalDataCID generates additional data for AEAD ciphers +// according to https://datatracker.ietf.org/doc/html/rfc9146#name-aead-ciphers +func generateAEADAdditionalDataCID(h *recordlayer.Header, payloadLen int) []byte { + var b cryptobyte.Builder + + b.AddUint64(seqNumPlaceholder) + b.AddUint8(uint8(protocol.ContentTypeConnectionID)) + b.AddUint8(uint8(len(h.ConnectionID))) + b.AddUint8(uint8(protocol.ContentTypeConnectionID)) + b.AddUint8(h.Version.Major) + b.AddUint8(h.Version.Minor) + b.AddUint16(h.Epoch) + util.AddUint48(&b, h.SequenceNumber) + b.AddBytes(h.ConnectionID) + b.AddUint16(uint16(payloadLen)) + + return b.BytesOrPanic() +} + // examinePadding 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. 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 c0fd1f76f98..1d09c8eb954 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 @@ -55,8 +55,8 @@ func NewGCM(localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*GCM, erro // Encrypt encrypt a DTLS RecordLayer message func (g *GCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) { - payload := raw[recordlayer.HeaderSize:] - raw = raw[:recordlayer.HeaderSize] + payload := raw[pkt.Header.Size():] + raw = raw[:pkt.Header.Size()] nonce := make([]byte, gcmNonceLength) copy(nonce, g.localWriteIV[:4]) @@ -64,7 +64,12 @@ func (g *GCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) return nil, err } - additionalData := generateAEADAdditionalData(&pkt.Header, len(payload)) + var additionalData []byte + if pkt.Header.ContentType == protocol.ContentTypeConnectionID { + additionalData = generateAEADAdditionalDataCID(&pkt.Header, len(payload)) + } else { + additionalData = generateAEADAdditionalData(&pkt.Header, len(payload)) + } encryptedPayload := g.localGCM.Seal(nil, nonce, payload, additionalData) r := make([]byte, len(raw)+len(nonce[4:])+len(encryptedPayload)) copy(r, raw) @@ -72,13 +77,12 @@ func (g *GCM) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) copy(r[len(raw)+len(nonce[4:]):], encryptedPayload) // Update recordLayer size to include explicit nonce - binary.BigEndian.PutUint16(r[recordlayer.HeaderSize-2:], uint16(len(r)-recordlayer.HeaderSize)) + binary.BigEndian.PutUint16(r[pkt.Header.Size()-2:], uint16(len(r)-pkt.Header.Size())) return r, nil } // Decrypt decrypts a DTLS RecordLayer message -func (g *GCM) Decrypt(in []byte) ([]byte, error) { - var h recordlayer.Header +func (g *GCM) Decrypt(h recordlayer.Header, in []byte) ([]byte, error) { err := h.Unmarshal(in) switch { case err != nil: @@ -86,18 +90,23 @@ func (g *GCM) Decrypt(in []byte) ([]byte, error) { case h.ContentType == protocol.ContentTypeChangeCipherSpec: // Nothing to encrypt with ChangeCipherSpec return in, nil - case len(in) <= (8 + recordlayer.HeaderSize): + case len(in) <= (8 + h.Size()): return nil, errNotEnoughRoomForNonce } nonce := make([]byte, 0, gcmNonceLength) - nonce = append(append(nonce, g.remoteWriteIV[:4]...), in[recordlayer.HeaderSize:recordlayer.HeaderSize+8]...) - out := in[recordlayer.HeaderSize+8:] + nonce = append(append(nonce, g.remoteWriteIV[:4]...), in[h.Size():h.Size()+8]...) + out := in[h.Size()+8:] - additionalData := generateAEADAdditionalData(&h, len(out)-gcmTagLength) + var additionalData []byte + if h.ContentType == protocol.ContentTypeConnectionID { + additionalData = generateAEADAdditionalDataCID(&h, len(out)-gcmTagLength) + } else { + 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) //nolint:errorlint } - return append(in[:recordlayer.HeaderSize], out...), nil + return append(in[:h.Size()], out...), nil } diff --git a/vendor/github.com/pion/dtls/v2/pkg/net/net.go b/vendor/github.com/pion/dtls/v2/pkg/net/net.go new file mode 100644 index 00000000000..e76daf56aae --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/net/net.go @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +// Package net defines packet-oriented primitives that are compatible with net +// in the standard library. +package net + +import ( + "net" + "time" +) + +// A PacketListener is the same as net.Listener but returns a net.PacketConn on +// Accept() rather than a net.Conn. +// +// Multiple goroutines may invoke methods on a PacketListener simultaneously. +type PacketListener interface { + // Accept waits for and returns the next connection to the listener. + Accept() (net.PacketConn, net.Addr, error) + + // Close closes the listener. + // Any blocked Accept operations will be unblocked and return errors. + Close() error + + // Addr returns the listener's network address. + Addr() net.Addr +} + +// PacketListenerFromListener converts a net.Listener into a +// dtlsnet.PacketListener. +func PacketListenerFromListener(l net.Listener) PacketListener { + return &packetListenerWrapper{ + l: l, + } +} + +// packetListenerWrapper wraps a net.Listener and implements +// dtlsnet.PacketListener. +type packetListenerWrapper struct { + l net.Listener +} + +// Accept calls Accept on the underlying net.Listener and converts the returned +// net.Conn into a net.PacketConn. +func (p *packetListenerWrapper) Accept() (net.PacketConn, net.Addr, error) { + c, err := p.l.Accept() + if err != nil { + return PacketConnFromConn(c), nil, err + } + return PacketConnFromConn(c), c.RemoteAddr(), nil +} + +// Close closes the underlying net.Listener. +func (p *packetListenerWrapper) Close() error { + return p.l.Close() +} + +// Addr returns the address of the underlying net.Listener. +func (p *packetListenerWrapper) Addr() net.Addr { + return p.l.Addr() +} + +// PacketConnFromConn converts a net.Conn into a net.PacketConn. +func PacketConnFromConn(conn net.Conn) net.PacketConn { + return &packetConnWrapper{conn} +} + +// packetConnWrapper wraps a net.Conn and implements net.PacketConn. +type packetConnWrapper struct { + conn net.Conn +} + +// ReadFrom reads from the underlying net.Conn and returns its remote address. +func (p *packetConnWrapper) ReadFrom(b []byte) (int, net.Addr, error) { + n, err := p.conn.Read(b) + return n, p.conn.RemoteAddr(), err +} + +// WriteTo writes to the underlying net.Conn. +func (p *packetConnWrapper) WriteTo(b []byte, _ net.Addr) (int, error) { + n, err := p.conn.Write(b) + return n, err +} + +// Close closes the underlying net.Conn. +func (p *packetConnWrapper) Close() error { + return p.conn.Close() +} + +// LocalAddr returns the local address of the underlying net.Conn. +func (p *packetConnWrapper) LocalAddr() net.Addr { + return p.conn.LocalAddr() +} + +// SetDeadline sets the deadline on the underlying net.Conn. +func (p *packetConnWrapper) SetDeadline(t time.Time) error { + return p.conn.SetDeadline(t) +} + +// SetReadDeadline sets the read deadline on the underlying net.Conn. +func (p *packetConnWrapper) SetReadDeadline(t time.Time) error { + return p.conn.SetReadDeadline(t) +} + +// SetWriteDeadline sets the write deadline on the underlying net.Conn. +func (p *packetConnWrapper) SetWriteDeadline(t time.Time) error { + return p.conn.SetWriteDeadline(t) +} 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 92c9db2bf4f..154005e2c3e 100644 --- a/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go @@ -14,6 +14,7 @@ const ( ContentTypeAlert ContentType = 21 ContentTypeHandshake ContentType = 22 ContentTypeApplicationData ContentType = 23 + ContentTypeConnectionID ContentType = 25 ) // Content is the top level distinguisher for a DTLS Datagram diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/connection_id.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/connection_id.go new file mode 100644 index 00000000000..b3fe1640f22 --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/connection_id.go @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package extension + +import ( + "golang.org/x/crypto/cryptobyte" +) + +// ConnectionID is a DTLS extension that provides an alternative to IP address +// and port for session association. +// +// https://tools.ietf.org/html/rfc9146 +type ConnectionID struct { + // A zero-length connection ID indicates for a client or server that + // negotiated connection IDs from the peer will be sent but there is no need + // to respond with one + CID []byte // variable length +} + +// TypeValue returns the extension TypeValue +func (c ConnectionID) TypeValue() TypeValue { + return ConnectionIDTypeValue +} + +// Marshal encodes the extension +func (c *ConnectionID) Marshal() ([]byte, error) { + var b cryptobyte.Builder + b.AddUint16(uint16(c.TypeValue())) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(c.CID) + }) + }) + return b.Bytes() +} + +// Unmarshal populates the extension from encoded data +func (c *ConnectionID) Unmarshal(data []byte) error { + val := cryptobyte.String(data) + var extension uint16 + val.ReadUint16(&extension) + if TypeValue(extension) != c.TypeValue() { + return errInvalidExtensionType + } + + var extData cryptobyte.String + val.ReadUint16LengthPrefixed(&extData) + + var cid cryptobyte.String + if !extData.ReadUint8LengthPrefixed(&cid) { + return errInvalidCIDFormat + } + c.CID = make([]byte, len(cid)) + if !cid.CopyBytes(c.CID) { + return errInvalidCIDFormat + } + return nil +} 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 c5e954ce5ab..39431206f59 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 @@ -16,5 +16,6 @@ var ( errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113 errInvalidExtensionType = &protocol.FatalError{Err: errors.New("invalid extension type")} //nolint:goerr113 errInvalidSNIFormat = &protocol.FatalError{Err: errors.New("invalid server name format")} //nolint:goerr113 + errInvalidCIDFormat = &protocol.FatalError{Err: errors.New("invalid connection ID format")} //nolint:goerr113 errLengthMismatch = &protocol.InternalError{Err: errors.New("data length and declared length do not match")} //nolint:goerr113 ) 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 5173a5863ef..e4df859f890 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 @@ -20,6 +20,7 @@ const ( UseSRTPTypeValue TypeValue = 14 ALPNTypeValue TypeValue = 16 UseExtendedMasterSecretTypeValue TypeValue = 23 + ConnectionIDTypeValue TypeValue = 54 RenegotiationInfoTypeValue TypeValue = 65281 ) @@ -64,6 +65,10 @@ func Unmarshal(buf []byte) ([]Extension, error) { err = unmarshalAndAppend(buf[offset:], &ServerName{}) case SupportedEllipticCurvesTypeValue: err = unmarshalAndAppend(buf[offset:], &SupportedEllipticCurves{}) + case SupportedPointFormatsTypeValue: + err = unmarshalAndAppend(buf[offset:], &SupportedPointFormats{}) + case SupportedSignatureAlgorithmsTypeValue: + err = unmarshalAndAppend(buf[offset:], &SupportedSignatureAlgorithms{}) case UseSRTPTypeValue: err = unmarshalAndAppend(buf[offset:], &UseSRTP{}) case ALPNTypeValue: @@ -72,6 +77,8 @@ func Unmarshal(buf []byte) ([]Extension, error) { err = unmarshalAndAppend(buf[offset:], &UseExtendedMasterSecret{}) case RenegotiationInfoTypeValue: err = unmarshalAndAppend(buf[offset:], &RenegotiationInfo{}) + case ConnectionIDTypeValue: + err = unmarshalAndAppend(buf[offset:], &ConnectionID{}) default: } if err != nil { 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 9c2543e6e21..5ed0f347f22 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 @@ -44,12 +44,14 @@ func (s *SupportedPointFormats) Marshal() ([]byte, error) { func (s *SupportedPointFormats) Unmarshal(data []byte) error { if len(data) <= supportedPointFormatsSize { return errBufferTooSmall - } else if TypeValue(binary.BigEndian.Uint16(data)) != s.TypeValue() { + } + + if TypeValue(binary.BigEndian.Uint16(data)) != s.TypeValue() { return errInvalidExtensionType } - pointFormatCount := int(binary.BigEndian.Uint16(data[4:])) - if supportedGroupsHeaderSize+(pointFormatCount) > len(data) { + pointFormatCount := int(data[4]) + if supportedPointFormatsSize+pointFormatCount > len(data) { return errLengthMismatch } 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 92252502b13..66a1be810f3 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 @@ -17,11 +17,16 @@ type Header struct { Version protocol.Version Epoch uint16 SequenceNumber uint64 // uint48 in spec + + // Optional Fields + ConnectionID []byte } // RecordLayer enums const ( - HeaderSize = 13 + // FixedHeaderSize is the size of a DTLS record header when connection IDs + // are not in use. + FixedHeaderSize = 13 MaxSequenceNumber = 0x0000FFFFFFFFFFFF ) @@ -31,22 +36,33 @@ func (h *Header) Marshal() ([]byte, error) { return nil, errSequenceNumberOverflow } - out := make([]byte, HeaderSize) + hs := FixedHeaderSize + len(h.ConnectionID) + + out := make([]byte, hs) out[0] = byte(h.ContentType) out[1] = h.Version.Major out[2] = h.Version.Minor binary.BigEndian.PutUint16(out[3:], h.Epoch) util.PutBigEndianUint48(out[5:], h.SequenceNumber) - binary.BigEndian.PutUint16(out[HeaderSize-2:], h.ContentLen) + copy(out[11:11+len(h.ConnectionID)], h.ConnectionID) + binary.BigEndian.PutUint16(out[hs-2:], h.ContentLen) return out, nil } // Unmarshal populates a TLS RecordLayer Header from binary func (h *Header) Unmarshal(data []byte) error { - if len(data) < HeaderSize { + if len(data) < FixedHeaderSize { return errBufferTooSmall } h.ContentType = protocol.ContentType(data[0]) + if h.ContentType == protocol.ContentTypeConnectionID { + // If a CID was expected the ConnectionID should have been initialized. + if len(data) < FixedHeaderSize+len(h.ConnectionID) { + return errBufferTooSmall + } + h.ConnectionID = data[11 : 11+len(h.ConnectionID)] + } + h.Version.Major = data[1] h.Version.Minor = data[2] h.Epoch = binary.BigEndian.Uint16(data[3:]) @@ -62,3 +78,8 @@ func (h *Header) Unmarshal(data []byte) error { return nil } + +// Size returns the total size of the header. +func (h *Header) Size() int { + return FixedHeaderSize + len(h.ConnectionID) +} diff --git a/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/inner_plaintext.go b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/inner_plaintext.go new file mode 100644 index 00000000000..bbc94dd80dd --- /dev/null +++ b/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/inner_plaintext.go @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package recordlayer + +import ( + "github.com/pion/dtls/v2/pkg/protocol" + "golang.org/x/crypto/cryptobyte" +) + +// InnerPlaintext implements DTLSInnerPlaintext +// +// https://datatracker.ietf.org/doc/html/rfc9146#name-record-layer-extensions +type InnerPlaintext struct { + Content []byte + RealType protocol.ContentType + Zeros uint +} + +// Marshal encodes a DTLS InnerPlaintext to binary +func (p *InnerPlaintext) Marshal() ([]byte, error) { + var out cryptobyte.Builder + out.AddBytes(p.Content) + out.AddUint8(uint8(p.RealType)) + out.AddBytes(make([]byte, p.Zeros)) + return out.Bytes() +} + +// Unmarshal populates a DTLS InnerPlaintext from binary +func (p *InnerPlaintext) Unmarshal(data []byte) error { + // Process in reverse + i := len(data) - 1 + for i >= 0 { + if data[i] != 0 { + p.Zeros = uint(len(data) - 1 - i) + break + } + i-- + } + if i == 0 { + return errBufferTooSmall + } + p.RealType = protocol.ContentType(data[i]) + p.Content = append([]byte{}, data[:i]...) + + return nil +} 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 02325fd2d5d..213a7976adf 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 @@ -11,6 +11,23 @@ import ( "github.com/pion/dtls/v2/pkg/protocol/handshake" ) +// DTLS fixed size record layer header when Connection IDs are not in-use. + +// --------------------------------- +// | Type | Version | Epoch | +// --------------------------------- +// | Epoch | Sequence Number | +// --------------------------------- +// | Sequence Number | Length | +// --------------------------------- +// | Length | Fragment... | +// --------------------------------- + +// fixedHeaderLenIdx is the index at which the record layer content length is +// specified in a fixed length header (i.e. one that does not include a +// Connection ID). +const fixedHeaderLenIdx = 11 + // RecordLayer which handles all data transport. // The record layer is assumed to sit directly on top of some // reliable transport such as TCP. The record layer can carry four types of content: @@ -51,14 +68,11 @@ func (r *RecordLayer) Marshal() ([]byte, error) { // Unmarshal populates the RecordLayer from binary func (r *RecordLayer) Unmarshal(data []byte) error { - if len(data) < HeaderSize { - return errBufferTooSmall - } if err := r.Header.Unmarshal(data); err != nil { return err } - switch protocol.ContentType(data[0]) { + switch r.Header.ContentType { case protocol.ContentTypeChangeCipherSpec: r.Content = &protocol.ChangeCipherSpec{} case protocol.ContentTypeAlert: @@ -71,7 +85,7 @@ func (r *RecordLayer) Unmarshal(data []byte) error { return errInvalidContentType } - return r.Content.Unmarshal(data[HeaderSize:]) + return r.Content.Unmarshal(data[r.Header.Size()+len(r.Header.ConnectionID):]) } // UnpackDatagram extracts all RecordLayer messages from a single datagram. @@ -85,11 +99,40 @@ func UnpackDatagram(buf []byte) ([][]byte, error) { out := [][]byte{} for offset := 0; len(buf) != offset; { - if len(buf)-offset <= HeaderSize { + if len(buf)-offset <= FixedHeaderSize { + return nil, errInvalidPacketLength + } + + pktLen := (FixedHeaderSize + int(binary.BigEndian.Uint16(buf[offset+11:]))) + if offset+pktLen > len(buf) { + return nil, errInvalidPacketLength + } + + out = append(out, buf[offset:offset+pktLen]) + offset += pktLen + } + + return out, nil +} + +// ContentAwareUnpackDatagram is the same as UnpackDatagram but considers the +// presence of a connection identifier if the record is of content type +// tls12_cid. +func ContentAwareUnpackDatagram(buf []byte, cidLength int) ([][]byte, error) { + out := [][]byte{} + + for offset := 0; len(buf) != offset; { + headerSize := FixedHeaderSize + lenIdx := fixedHeaderLenIdx + if protocol.ContentType(buf[offset]) == protocol.ContentTypeConnectionID { + headerSize += cidLength + lenIdx += cidLength + } + if len(buf)-offset <= headerSize { return nil, errInvalidPacketLength } - pktLen := (HeaderSize + int(binary.BigEndian.Uint16(buf[offset+11:]))) + pktLen := (headerSize + int(binary.BigEndian.Uint16(buf[offset+lenIdx:]))) if offset+pktLen > len(buf) { return nil, errInvalidPacketLength } diff --git a/vendor/github.com/pion/dtls/v2/resume.go b/vendor/github.com/pion/dtls/v2/resume.go index c470d856b28..9e8a2ae42aa 100644 --- a/vendor/github.com/pion/dtls/v2/resume.go +++ b/vendor/github.com/pion/dtls/v2/resume.go @@ -9,11 +9,11 @@ import ( ) // Resume imports an already established dtls connection using a specific dtls state -func Resume(state *State, conn net.Conn, config *Config) (*Conn, error) { +func Resume(state *State, conn net.PacketConn, rAddr net.Addr, config *Config) (*Conn, error) { if err := state.initCipherSuite(); err != nil { return nil, err } - c, err := createConn(context.Background(), conn, config, state.isClient, state) + c, err := createConn(context.Background(), conn, rAddr, config, state.isClient, state) if err != nil { return nil, err } diff --git a/vendor/github.com/pion/dtls/v2/state.go b/vendor/github.com/pion/dtls/v2/state.go index e9f86a80b2d..cd694557350 100644 --- a/vendor/github.com/pion/dtls/v2/state.go +++ b/vendor/github.com/pion/dtls/v2/state.go @@ -11,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/v2/replaydetector" + "github.com/pion/transport/v3/replaydetector" ) // State holds the dtls connection state and implements both encoding.BinaryMarshaler and encoding.BinaryUnmarshaler @@ -27,6 +27,20 @@ type State struct { IdentityHint []byte SessionID []byte + // Connection Identifiers must be negotiated afresh on session resumption. + // https://datatracker.ietf.org/doc/html/rfc9146#name-the-connection_id-extension + + // localConnectionID is the locally generated connection ID that is expected + // to be received from the remote endpoint. + // For a server, this is the connection ID sent in ServerHello. + // For a client, this is the connection ID sent in the ClientHello. + localConnectionID []byte + // remoteConnectionID is the connection ID that the remote endpoint + // specifies should be sent. + // For a server, this is the connection ID received in the ClientHello. + // For a client, this is the connection ID received in the ServerHello. + remoteConnectionID []byte + isClient bool preMasterSecret []byte @@ -62,6 +76,8 @@ type serializedState struct { PeerCertificates [][]byte IdentityHint []byte SessionID []byte + LocalConnectionID []byte + RemoteConnectionID []byte IsClient bool } @@ -91,6 +107,8 @@ func (s *State) serialize() *serializedState { PeerCertificates: s.PeerCertificates, IdentityHint: s.IdentityHint, SessionID: s.SessionID, + LocalConnectionID: s.localConnectionID, + RemoteConnectionID: s.remoteConnectionID, IsClient: s.isClient, } } @@ -127,7 +145,13 @@ func (s *State) deserialize(serialized serializedState) { // Set remote certificate s.PeerCertificates = serialized.PeerCertificates + s.IdentityHint = serialized.IdentityHint + + // Set local and remote connection IDs + s.localConnectionID = serialized.LocalConnectionID + s.remoteConnectionID = serialized.RemoteConnectionID + s.SessionID = serialized.SessionID } diff --git a/vendor/github.com/pion/transport/v2/AUTHORS.txt b/vendor/github.com/pion/transport/v3/AUTHORS.txt similarity index 83% rename from vendor/github.com/pion/transport/v2/AUTHORS.txt rename to vendor/github.com/pion/transport/v3/AUTHORS.txt index b595c41c5af..7c0251dd313 100644 --- a/vendor/github.com/pion/transport/v2/AUTHORS.txt +++ b/vendor/github.com/pion/transport/v3/AUTHORS.txt @@ -7,6 +7,10 @@ Adrian Cable Atsushi Watanabe backkem cnderrauber +<<<<<<< HEAD:vendor/github.com/pion/transport/v2/AUTHORS.txt +======= +Daniel +>>>>>>> 03615721 (Add internal server dtls config):vendor/github.com/pion/transport/v3/AUTHORS.txt Daniel Mangum Hugo Arregui Jeremiah Millay @@ -18,6 +22,7 @@ OrlandoCo Sean DuBois Sean DuBois Sean DuBois +Sean DuBois Sean DuBois Steffen Vogel Winlin diff --git a/vendor/github.com/pion/transport/v2/LICENSE b/vendor/github.com/pion/transport/v3/LICENSE similarity index 100% rename from vendor/github.com/pion/transport/v2/LICENSE rename to vendor/github.com/pion/transport/v3/LICENSE diff --git a/vendor/github.com/pion/transport/v2/deadline/deadline.go b/vendor/github.com/pion/transport/v3/deadline/deadline.go similarity index 100% rename from vendor/github.com/pion/transport/v2/deadline/deadline.go rename to vendor/github.com/pion/transport/v3/deadline/deadline.go diff --git a/vendor/github.com/pion/transport/v2/connctx/connctx.go b/vendor/github.com/pion/transport/v3/netctx/conn.go similarity index 68% rename from vendor/github.com/pion/transport/v2/connctx/connctx.go rename to vendor/github.com/pion/transport/v3/netctx/conn.go index 4869b9c2e57..8f0076d6f02 100644 --- a/vendor/github.com/pion/transport/v2/connctx/connctx.go +++ b/vendor/github.com/pion/transport/v3/netctx/conn.go @@ -1,10 +1,15 @@ // SPDX-FileCopyrightText: 2023 The Pion community // SPDX-License-Identifier: MIT +<<<<<<< HEAD:vendor/github.com/pion/transport/v2/connctx/connctx.go // Package connctx wraps net.Conn using context.Context. // // Deprecated: use netctx instead. package connctx +======= +// Package netctx wraps common net interfaces using context.Context. +package netctx +>>>>>>> 03615721 (Add internal server dtls config):vendor/github.com/pion/transport/v3/netctx/conn.go import ( "context" @@ -35,8 +40,8 @@ type ReadWriter interface { Writer } -// ConnCtx is a wrapper of net.Conn using context.Context. -type ConnCtx interface { +// Conn is a wrapper of net.Conn using context.Context. +type Conn interface { Reader Writer io.Closer @@ -45,7 +50,7 @@ type ConnCtx interface { Conn() net.Conn } -type connCtx struct { +type conn struct { nextConn net.Conn closed chan struct{} closeOnce sync.Once @@ -55,22 +60,24 @@ type connCtx struct { var veryOld = time.Unix(0, 1) //nolint:gochecknoglobals -// New creates a new ConnCtx wrapping given net.Conn. -func New(conn net.Conn) ConnCtx { - c := &connCtx{ - nextConn: conn, +// NewConn creates a new Conn wrapping given net.Conn. +func NewConn(netConn net.Conn) Conn { + c := &conn{ + nextConn: netConn, closed: make(chan struct{}), } return c } -func (c *connCtx) ReadContext(ctx context.Context, b []byte) (int, error) { +// ReadContext reads data from the connection. +// Unlike net.Conn.Read(), the provided context is used to control timeout. +func (c *conn) ReadContext(ctx context.Context, b []byte) (int, error) { c.readMu.Lock() defer c.readMu.Unlock() select { case <-c.closed: - return 0, io.EOF + return 0, net.ErrClosed default: } @@ -108,7 +115,9 @@ func (c *connCtx) ReadContext(ctx context.Context, b []byte) (int, error) { return n, err } -func (c *connCtx) WriteContext(ctx context.Context, b []byte) (int, error) { +// WriteContext writes data to the connection. +// Unlike net.Conn.Write(), the provided context is used to control timeout. +func (c *conn) WriteContext(ctx context.Context, b []byte) (int, error) { c.writeMu.Lock() defer c.writeMu.Unlock() @@ -152,7 +161,10 @@ func (c *connCtx) WriteContext(ctx context.Context, b []byte) (int, error) { return n, err } -func (c *connCtx) Close() error { +// Close closes the connection. +// Any blocked ReadContext or WriteContext operations will be unblocked and +// return errors. +func (c *conn) Close() error { err := c.nextConn.Close() c.closeOnce.Do(func() { c.writeMu.Lock() @@ -164,14 +176,17 @@ func (c *connCtx) Close() error { return err } -func (c *connCtx) LocalAddr() net.Addr { +// LocalAddr returns the local network address, if known. +func (c *conn) LocalAddr() net.Addr { return c.nextConn.LocalAddr() } -func (c *connCtx) RemoteAddr() net.Addr { +// LocalAddr returns the local network address, if known. +func (c *conn) RemoteAddr() net.Addr { return c.nextConn.RemoteAddr() } -func (c *connCtx) Conn() net.Conn { +// Conn returns the underlying net.Conn. +func (c *conn) Conn() net.Conn { return c.nextConn } diff --git a/vendor/github.com/pion/transport/v3/netctx/packetconn.go b/vendor/github.com/pion/transport/v3/netctx/packetconn.go new file mode 100644 index 00000000000..a4ce22d956a --- /dev/null +++ b/vendor/github.com/pion/transport/v3/netctx/packetconn.go @@ -0,0 +1,175 @@ +// SPDX-FileCopyrightText: 2023 The Pion community +// SPDX-License-Identifier: MIT + +package netctx + +import ( + "context" + "io" + "net" + "sync" + "sync/atomic" + "time" +) + +// ReaderFrom is an interface for context controlled packet reader. +type ReaderFrom interface { + ReadFromContext(context.Context, []byte) (int, net.Addr, error) +} + +// WriterTo is an interface for context controlled packet writer. +type WriterTo interface { + WriteToContext(context.Context, []byte, net.Addr) (int, error) +} + +// PacketConn is a wrapper of net.PacketConn using context.Context. +type PacketConn interface { + ReaderFrom + WriterTo + io.Closer + LocalAddr() net.Addr + Conn() net.PacketConn +} + +type packetConn struct { + nextConn net.PacketConn + closed chan struct{} + closeOnce sync.Once + readMu sync.Mutex + writeMu sync.Mutex +} + +// NewPacketConn creates a new PacketConn wrapping the given net.PacketConn. +func NewPacketConn(pconn net.PacketConn) PacketConn { + p := &packetConn{ + nextConn: pconn, + closed: make(chan struct{}), + } + return p +} + +// ReadFromContext 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. +// Unlike net.PacketConn.ReadFrom(), the provided context is +// used to control timeout. +func (p *packetConn) ReadFromContext(ctx context.Context, b []byte) (int, net.Addr, error) { + p.readMu.Lock() + defer p.readMu.Unlock() + + select { + case <-p.closed: + return 0, nil, net.ErrClosed + default: + } + + done := make(chan struct{}) + var wg sync.WaitGroup + var errSetDeadline atomic.Value + wg.Add(1) + go func() { + defer wg.Done() + select { + case <-ctx.Done(): + // context canceled + if err := p.nextConn.SetReadDeadline(veryOld); err != nil { + errSetDeadline.Store(err) + return + } + <-done + if err := p.nextConn.SetReadDeadline(time.Time{}); err != nil { + errSetDeadline.Store(err) + } + case <-done: + } + }() + + n, raddr, err := p.nextConn.ReadFrom(b) + + close(done) + wg.Wait() + if e := ctx.Err(); e != nil && n == 0 { + err = e + } + if err2, ok := errSetDeadline.Load().(error); ok && err == nil && err2 != nil { + err = err2 + } + return n, raddr, err +} + +// WriteToContext writes a packet with payload p to addr. +// Unlike net.PacketConn.WriteTo(), the provided context +// is used to control timeout. +// On packet-oriented connections, write timeouts are rare. +func (p *packetConn) WriteToContext(ctx context.Context, b []byte, raddr net.Addr) (int, error) { + p.writeMu.Lock() + defer p.writeMu.Unlock() + + select { + case <-p.closed: + return 0, ErrClosing + default: + } + + done := make(chan struct{}) + var wg sync.WaitGroup + var errSetDeadline atomic.Value + wg.Add(1) + go func() { + defer wg.Done() + select { + case <-ctx.Done(): + // context canceled + if err := p.nextConn.SetWriteDeadline(veryOld); err != nil { + errSetDeadline.Store(err) + return + } + <-done + if err := p.nextConn.SetWriteDeadline(time.Time{}); err != nil { + errSetDeadline.Store(err) + } + case <-done: + } + }() + + n, err := p.nextConn.WriteTo(b, raddr) + + close(done) + wg.Wait() + if e := ctx.Err(); e != nil && n == 0 { + err = e + } + if err2, ok := errSetDeadline.Load().(error); ok && err == nil && err2 != nil { + err = err2 + } + return n, err +} + +// Close closes the connection. +// Any blocked ReadFromContext or WriteToContext operations will be unblocked +// and return errors. +func (p *packetConn) Close() error { + err := p.nextConn.Close() + p.closeOnce.Do(func() { + p.writeMu.Lock() + p.readMu.Lock() + close(p.closed) + p.readMu.Unlock() + p.writeMu.Unlock() + }) + return err +} + +// LocalAddr returns the local network address, if known. +func (p *packetConn) LocalAddr() net.Addr { + return p.nextConn.LocalAddr() +} + +// Conn returns the underlying net.PacketConn. +func (p *packetConn) Conn() net.PacketConn { + return p.nextConn +} diff --git a/vendor/github.com/pion/transport/v2/connctx/pipe.go b/vendor/github.com/pion/transport/v3/netctx/pipe.go similarity index 56% rename from vendor/github.com/pion/transport/v2/connctx/pipe.go rename to vendor/github.com/pion/transport/v3/netctx/pipe.go index 96b802e4343..7deae668aa2 100644 --- a/vendor/github.com/pion/transport/v2/connctx/pipe.go +++ b/vendor/github.com/pion/transport/v3/netctx/pipe.go @@ -1,14 +1,14 @@ // SPDX-FileCopyrightText: 2023 The Pion community // SPDX-License-Identifier: MIT -package connctx +package netctx import ( "net" ) -// Pipe creates piped pair of ConnCtx. -func Pipe() (ConnCtx, ConnCtx) { +// Pipe creates piped pair of Conn. +func Pipe() (Conn, Conn) { ca, cb := net.Pipe() - return New(ca), New(cb) + return NewConn(ca), NewConn(cb) } diff --git a/vendor/github.com/pion/transport/v2/packetio/buffer.go b/vendor/github.com/pion/transport/v3/packetio/buffer.go similarity index 99% rename from vendor/github.com/pion/transport/v2/packetio/buffer.go rename to vendor/github.com/pion/transport/v3/packetio/buffer.go index 2d46d796a14..76c72734d63 100644 --- a/vendor/github.com/pion/transport/v2/packetio/buffer.go +++ b/vendor/github.com/pion/transport/v3/packetio/buffer.go @@ -10,7 +10,7 @@ import ( "sync" "time" - "github.com/pion/transport/v2/deadline" + "github.com/pion/transport/v3/deadline" ) var errPacketTooBig = errors.New("packet too big") diff --git a/vendor/github.com/pion/transport/v2/packetio/errors.go b/vendor/github.com/pion/transport/v3/packetio/errors.go similarity index 100% rename from vendor/github.com/pion/transport/v2/packetio/errors.go rename to vendor/github.com/pion/transport/v3/packetio/errors.go diff --git a/vendor/github.com/pion/transport/v2/packetio/hardlimit.go b/vendor/github.com/pion/transport/v3/packetio/hardlimit.go similarity index 100% rename from vendor/github.com/pion/transport/v2/packetio/hardlimit.go rename to vendor/github.com/pion/transport/v3/packetio/hardlimit.go diff --git a/vendor/github.com/pion/transport/v2/packetio/no_hardlimit.go b/vendor/github.com/pion/transport/v3/packetio/no_hardlimit.go similarity index 100% rename from vendor/github.com/pion/transport/v2/packetio/no_hardlimit.go rename to vendor/github.com/pion/transport/v3/packetio/no_hardlimit.go diff --git a/vendor/github.com/pion/transport/v2/replaydetector/fixedbig.go b/vendor/github.com/pion/transport/v3/replaydetector/fixedbig.go similarity index 100% rename from vendor/github.com/pion/transport/v2/replaydetector/fixedbig.go rename to vendor/github.com/pion/transport/v3/replaydetector/fixedbig.go diff --git a/vendor/github.com/pion/transport/v2/replaydetector/replaydetector.go b/vendor/github.com/pion/transport/v3/replaydetector/replaydetector.go similarity index 78% rename from vendor/github.com/pion/transport/v2/replaydetector/replaydetector.go rename to vendor/github.com/pion/transport/v3/replaydetector/replaydetector.go index 4358d8f3bf8..d40799568cc 100644 --- a/vendor/github.com/pion/transport/v2/replaydetector/replaydetector.go +++ b/vendor/github.com/pion/transport/v3/replaydetector/replaydetector.go @@ -8,7 +8,14 @@ package replaydetector type ReplayDetector interface { // Check returns true if given sequence number is not replayed. // Call accept() to mark the packet is received properly. - Check(seq uint64) (accept func(), ok bool) + // The return value of accept() indicates whether the accepted packet is + // has the latest observed sequence number. + Check(seq uint64) (accept func() bool, ok bool) +} + +// nop is a no-op func that is returned in the case that Check() fails. +func nop() bool { + return false } type slidingWindowDetector struct { @@ -30,30 +37,33 @@ func New(windowSize uint, maxSeq uint64) ReplayDetector { } } -func (d *slidingWindowDetector) Check(seq uint64) (accept func(), ok bool) { +func (d *slidingWindowDetector) Check(seq uint64) (func() bool, bool) { if seq > d.maxSeq { // Exceeded upper limit. - return func() {}, false + return nop, false } if seq <= d.latestSeq { if d.latestSeq >= uint64(d.windowSize)+seq { - return func() {}, false + return nop, false } if d.mask.Bit(uint(d.latestSeq-seq)) != 0 { // The sequence number is duplicated. - return func() {}, false + return nop, false } } - return func() { + return func() bool { + latest := seq == 0 if seq > d.latestSeq { // Update the head of the window. d.mask.Lsh(uint(seq - d.latestSeq)) d.latestSeq = seq + latest = true } diff := (d.latestSeq - seq) % d.maxSeq d.mask.SetBit(uint(diff)) + return latest }, true } @@ -75,10 +85,10 @@ type wrappedSlidingWindowDetector struct { init bool } -func (d *wrappedSlidingWindowDetector) Check(seq uint64) (accept func(), ok bool) { +func (d *wrappedSlidingWindowDetector) Check(seq uint64) (func() bool, bool) { if seq > d.maxSeq { // Exceeded upper limit. - return func() {}, false + return nop, false } if !d.init { if seq != 0 { @@ -99,21 +109,24 @@ func (d *wrappedSlidingWindowDetector) Check(seq uint64) (accept func(), ok bool if diff >= int64(d.windowSize) { // Too old. - return func() {}, false + return nop, false } if diff >= 0 { if d.mask.Bit(uint(diff)) != 0 { // The sequence number is duplicated. - return func() {}, false + return nop, false } } - return func() { + return func() bool { + latest := false if diff < 0 { // Update the head of the window. d.mask.Lsh(uint(-diff)) d.latestSeq = seq + latest = true } d.mask.SetBit(uint(d.latestSeq - seq)) + return latest }, true } diff --git a/vendor/github.com/pion/transport/v2/udp/batchconn.go b/vendor/github.com/pion/transport/v3/udp/batchconn.go similarity index 100% rename from vendor/github.com/pion/transport/v2/udp/batchconn.go rename to vendor/github.com/pion/transport/v3/udp/batchconn.go diff --git a/vendor/github.com/pion/transport/v2/udp/conn.go b/vendor/github.com/pion/transport/v3/udp/conn.go similarity index 97% rename from vendor/github.com/pion/transport/v2/udp/conn.go rename to vendor/github.com/pion/transport/v3/udp/conn.go index e2378f88276..5eba5745b0d 100644 --- a/vendor/github.com/pion/transport/v2/udp/conn.go +++ b/vendor/github.com/pion/transport/v3/udp/conn.go @@ -12,8 +12,13 @@ import ( "sync/atomic" "time" +<<<<<<< HEAD:vendor/github.com/pion/transport/v2/udp/conn.go "github.com/pion/transport/v2/deadline" "github.com/pion/transport/v2/packetio" +======= + "github.com/pion/transport/v3/deadline" + "github.com/pion/transport/v3/packetio" +>>>>>>> 03615721 (Add internal server dtls config):vendor/github.com/pion/transport/v3/udp/conn.go "golang.org/x/net/ipv4" ) diff --git a/vendor/github.com/plgd-dev/go-coap/v2/Dockerfile b/vendor/github.com/plgd-dev/go-coap/v2/Dockerfile deleted file mode 100644 index 887a0a9ede8..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM ubuntu:22.04 AS build -RUN apt-get update \ - && apt-get install -y gcc make git curl file -RUN git clone https://github.com/udhos/update-golang.git \ - && cd update-golang \ - && ./update-golang.sh \ - && ln -s /usr/local/go/bin/go /usr/bin/go -WORKDIR $GOPATH/src/github.com/plgd-dev/go-coap -COPY go.mod go.sum ./ -RUN go mod download -COPY . . \ No newline at end of file diff --git a/vendor/github.com/plgd-dev/go-coap/v2/dtls/client.go b/vendor/github.com/plgd-dev/go-coap/v2/dtls/client.go deleted file mode 100644 index aaa800e7e8f..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/dtls/client.go +++ /dev/null @@ -1,222 +0,0 @@ -package dtls - -import ( - "context" - "fmt" - "net" - "time" - - "github.com/pion/dtls/v2" - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - coapNet "github.com/plgd-dev/go-coap/v2/net" - "github.com/plgd-dev/go-coap/v2/net/blockwise" - "github.com/plgd-dev/go-coap/v2/net/monitor/inactivity" - "github.com/plgd-dev/go-coap/v2/pkg/cache" - "github.com/plgd-dev/go-coap/v2/pkg/runner/periodic" - "github.com/plgd-dev/go-coap/v2/udp/client" - udpMessage "github.com/plgd-dev/go-coap/v2/udp/message" - "github.com/plgd-dev/go-coap/v2/udp/message/pool" - kitSync "github.com/plgd-dev/kit/v2/sync" -) - -var defaultDialOptions = func() dialOptions { - opts := dialOptions{ - ctx: context.Background(), - maxMessageSize: 64 * 1024, - errors: func(err error) { - fmt.Println(err) - }, - goPool: func(f func()) error { - go func() { - f() - }() - return nil - }, - dialer: &net.Dialer{Timeout: time.Second * 3}, - net: "udp", - blockwiseSZX: blockwise.SZX1024, - blockwiseEnable: true, - blockwiseTransferTimeout: time.Second * 5, - transmissionNStart: time.Second, - transmissionAcknowledgeTimeout: time.Second * 2, - transmissionMaxRetransmit: 4, - getMID: udpMessage.GetMID, - createInactivityMonitor: func() inactivity.Monitor { - return inactivity.NewNilMonitor() - }, - periodicRunner: func(f func(now time.Time) bool) { - go func() { - for f(time.Now()) { - time.Sleep(4 * time.Second) - } - }() - }, - messagePool: pool.New(1024, 1600), - } - opts.handler = func(w *client.ResponseWriter, m *pool.Message) { - switch m.Code() { - case codes.POST, codes.PUT, codes.GET, codes.DELETE: - if err := w.SetResponse(codes.NotFound, message.TextPlain, nil); err != nil { - opts.errors(fmt.Errorf("client handler: cannot set response: %w", err)) - } - } - } - return opts -}() - -type dialOptions struct { - net string - ctx context.Context - getMID GetMIDFunc - handler HandlerFunc - errors ErrorFunc - goPool GoPoolFunc - dialer *net.Dialer - periodicRunner periodic.Func - messagePool *pool.Pool - blockwiseTransferTimeout time.Duration - transmissionNStart time.Duration - transmissionAcknowledgeTimeout time.Duration - createInactivityMonitor func() inactivity.Monitor - maxMessageSize uint32 - transmissionMaxRetransmit uint32 - closeSocket bool - blockwiseSZX blockwise.SZX - blockwiseEnable bool -} - -// A DialOption sets options such as credentials, keepalive parameters, etc. -type DialOption interface { - applyDial(*dialOptions) -} - -// Dial creates a client connection to the given target. -func Dial(target string, dtlsCfg *dtls.Config, opts ...DialOption) (*client.ClientConn, error) { - cfg := defaultDialOptions - for _, o := range opts { - o.applyDial(&cfg) - } - - c, err := cfg.dialer.DialContext(cfg.ctx, cfg.net, target) - if err != nil { - return nil, err - } - - conn, err := dtls.Client(c, dtlsCfg) - if err != nil { - return nil, err - } - opts = append(opts, WithCloseSocket()) - return Client(conn, opts...), nil -} - -func bwCreateAcquireMessage(messagePool *pool.Pool) func(ctx context.Context) blockwise.Message { - return func(ctx context.Context) blockwise.Message { - return messagePool.AcquireMessage(ctx) - } -} - -func bwCreateReleaseMessage(messagePool *pool.Pool) func(m blockwise.Message) { - return func(m blockwise.Message) { - messagePool.ReleaseMessage(m.(*pool.Message)) - } -} - -func bwCreateHandlerFunc(messagePool *pool.Pool, observatioRequests *kitSync.Map) func(token message.Token) (blockwise.Message, bool) { - return func(token message.Token) (blockwise.Message, bool) { - msg, ok := observatioRequests.LoadWithFunc(token.Hash(), func(v interface{}) interface{} { - r := v.(*pool.Message) - d := messagePool.AcquireMessage(r.Context()) - d.ResetOptionsTo(r.Options()) - d.SetCode(r.Code()) - d.SetToken(r.Token()) - d.SetMessageID(r.MessageID()) - return d - }) - if !ok { - return nil, ok - } - bwMessage := msg.(blockwise.Message) - return bwMessage, ok - } -} - -// Client creates client over dtls connection. -func Client(conn *dtls.Conn, opts ...DialOption) *client.ClientConn { - cfg := defaultDialOptions - for _, o := range opts { - o.applyDial(&cfg) - } - if cfg.errors == nil { - cfg.errors = func(error) { - // default no-op - } - } - if cfg.createInactivityMonitor == nil { - cfg.createInactivityMonitor = func() inactivity.Monitor { - return inactivity.NewNilMonitor() - } - } - if cfg.messagePool == nil { - cfg.messagePool = pool.New(0, 0) - } - errorsFunc := cfg.errors - cfg.errors = func(err error) { - if coapNet.IsCancelOrCloseError(err) { - // this error was produced by cancellation context or closing connection. - return - } - errorsFunc(fmt.Errorf("dtls: %v: %w", conn.RemoteAddr(), err)) - } - - observatioRequests := kitSync.NewMap() - var blockWise *blockwise.BlockWise - if cfg.blockwiseEnable { - blockWise = blockwise.NewBlockWise( - bwCreateAcquireMessage(cfg.messagePool), - bwCreateReleaseMessage(cfg.messagePool), - cfg.blockwiseTransferTimeout, - cfg.errors, - false, - bwCreateHandlerFunc(cfg.messagePool, observatioRequests), - ) - } - - observationTokenHandler := client.NewHandlerContainer() - monitor := cfg.createInactivityMonitor() - var cc *client.ClientConn - l := coapNet.NewConn(conn) - session := NewSession(cfg.ctx, - l, - cfg.maxMessageSize, - cfg.closeSocket, - ) - cc = client.NewClientConn(session, - observationTokenHandler, observatioRequests, cfg.transmissionNStart, cfg.transmissionAcknowledgeTimeout, cfg.transmissionMaxRetransmit, - client.NewObservationHandler(observationTokenHandler, cfg.handler), - cfg.blockwiseSZX, - blockWise, - cfg.goPool, - cfg.errors, - cfg.getMID, - // The client does not support activity monitoring yet - monitor, - cache.NewCache(), - cfg.messagePool, - ) - - cfg.periodicRunner(func(now time.Time) bool { - cc.CheckExpirations(now) - return cc.Context().Err() == nil - }) - - go func() { - err := cc.Run() - if err != nil { - cfg.errors(err) - } - }() - - return cc -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/dtls/optionmux.go b/vendor/github.com/plgd-dev/go-coap/v2/dtls/optionmux.go deleted file mode 100644 index b1402033453..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/dtls/optionmux.go +++ /dev/null @@ -1,11 +0,0 @@ -package dtls - -import ( - "github.com/plgd-dev/go-coap/v2/mux" - "github.com/plgd-dev/go-coap/v2/udp/client" -) - -// WithMux set's multiplexer for handle requests. -func WithMux(m mux.Handler) HandlerFuncOpt { - return WithHandlerFunc(client.HandlerFuncToMux(m)) -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/dtls/options.go b/vendor/github.com/plgd-dev/go-coap/v2/dtls/options.go deleted file mode 100644 index f9729c764a3..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/dtls/options.go +++ /dev/null @@ -1,325 +0,0 @@ -package dtls - -import ( - "context" - "net" - "time" - - "github.com/plgd-dev/go-coap/v2/net/blockwise" - "github.com/plgd-dev/go-coap/v2/net/monitor/inactivity" - "github.com/plgd-dev/go-coap/v2/pkg/runner/periodic" - "github.com/plgd-dev/go-coap/v2/udp/client" - "github.com/plgd-dev/go-coap/v2/udp/message/pool" -) - -// HandlerFuncOpt handler function option. -type HandlerFuncOpt struct { - h HandlerFunc -} - -func (o HandlerFuncOpt) apply(opts *serverOptions) { - opts.handler = o.h -} - -func (o HandlerFuncOpt) applyDial(opts *dialOptions) { - opts.handler = o.h -} - -// WithHandlerFunc set handle for handling request's. -func WithHandlerFunc(h HandlerFunc) HandlerFuncOpt { - return HandlerFuncOpt{h: h} -} - -// ContextOpt handler function option. -type ContextOpt struct { - ctx context.Context -} - -func (o ContextOpt) apply(opts *serverOptions) { - opts.ctx = o.ctx -} - -func (o ContextOpt) applyDial(opts *dialOptions) { - opts.ctx = o.ctx -} - -// WithContext set's parent context of server. -func WithContext(ctx context.Context) ContextOpt { - return ContextOpt{ctx: ctx} -} - -// MaxMessageSizeOpt handler function option. -type MaxMessageSizeOpt struct { - maxMessageSize uint32 -} - -func (o MaxMessageSizeOpt) apply(opts *serverOptions) { - opts.maxMessageSize = o.maxMessageSize -} - -func (o MaxMessageSizeOpt) applyDial(opts *dialOptions) { - opts.maxMessageSize = o.maxMessageSize -} - -// WithMaxMessageSize limit size of processed message. -func WithMaxMessageSize(maxMessageSize uint32) MaxMessageSizeOpt { - return MaxMessageSizeOpt{maxMessageSize: maxMessageSize} -} - -// ErrorsOpt errors option. -type ErrorsOpt struct { - errors ErrorFunc -} - -func (o ErrorsOpt) apply(opts *serverOptions) { - opts.errors = o.errors -} - -func (o ErrorsOpt) applyDial(opts *dialOptions) { - opts.errors = o.errors -} - -// WithErrors set function for logging error. -func WithErrors(errors ErrorFunc) ErrorsOpt { - return ErrorsOpt{errors: errors} -} - -// GoPoolOpt gopool option. -type GoPoolOpt struct { - goPool GoPoolFunc -} - -func (o GoPoolOpt) apply(opts *serverOptions) { - opts.goPool = o.goPool -} - -func (o GoPoolOpt) applyDial(opts *dialOptions) { - opts.goPool = o.goPool -} - -// WithGoPool sets function for managing spawning go routines -// for handling incoming request's. -// Eg: https://github.com/panjf2000/ants. -func WithGoPool(goPool GoPoolFunc) GoPoolOpt { - return GoPoolOpt{goPool: goPool} -} - -// KeepAliveOpt keepalive option. -type KeepAliveOpt struct { - timeout time.Duration - onInactive inactivity.OnInactiveFunc - maxRetries uint32 -} - -func (o KeepAliveOpt) apply(opts *serverOptions) { - opts.createInactivityMonitor = func() inactivity.Monitor { - keepalive := inactivity.NewKeepAlive(o.maxRetries, o.onInactive, func(cc inactivity.ClientConn, receivePong func()) (func(), error) { - return cc.(*client.ClientConn).AsyncPing(receivePong) - }) - return inactivity.NewInactivityMonitor(o.timeout/time.Duration(o.maxRetries+1), keepalive.OnInactive) - } -} - -func (o KeepAliveOpt) applyDial(opts *dialOptions) { - opts.createInactivityMonitor = func() inactivity.Monitor { - keepalive := inactivity.NewKeepAlive(o.maxRetries, o.onInactive, func(cc inactivity.ClientConn, receivePong func()) (func(), error) { - return cc.(*client.ClientConn).AsyncPing(receivePong) - }) - return inactivity.NewInactivityMonitor(o.timeout/time.Duration(o.maxRetries+1), keepalive.OnInactive) - } -} - -// WithKeepAlive monitoring's client connection's. -func WithKeepAlive(maxRetries uint32, timeout time.Duration, onInactive inactivity.OnInactiveFunc) KeepAliveOpt { - return KeepAliveOpt{ - maxRetries: maxRetries, - timeout: timeout, - onInactive: onInactive, - } -} - -// InactivityMonitorOpt notifies when a connection was inactive for a given duration. -type InactivityMonitorOpt struct { - duration time.Duration - onInactive inactivity.OnInactiveFunc -} - -func (o InactivityMonitorOpt) apply(opts *serverOptions) { - opts.createInactivityMonitor = func() inactivity.Monitor { - return inactivity.NewInactivityMonitor(o.duration, o.onInactive) - } -} - -func (o InactivityMonitorOpt) applyDial(opts *dialOptions) { - opts.createInactivityMonitor = func() inactivity.Monitor { - return inactivity.NewInactivityMonitor(o.duration, o.onInactive) - } -} - -// WithInactivityMonitor set deadline's for read operations over client connection. -func WithInactivityMonitor(duration time.Duration, onInactive inactivity.OnInactiveFunc) InactivityMonitorOpt { - return InactivityMonitorOpt{ - duration: duration, - onInactive: onInactive, - } -} - -// NetOpt network option. -type NetOpt struct { - net string -} - -func (o NetOpt) applyDial(opts *dialOptions) { - opts.net = o.net -} - -// WithNetwork define's udp version (udp4, udp6, udp) for client. -func WithNetwork(net string) NetOpt { - return NetOpt{net: net} -} - -// PeriodicRunnerOpt function which is executed in every ticks -type PeriodicRunnerOpt struct { - periodicRunner periodic.Func -} - -func (o PeriodicRunnerOpt) applyDial(opts *dialOptions) { - opts.periodicRunner = o.periodicRunner -} - -func (o PeriodicRunnerOpt) apply(opts *serverOptions) { - opts.periodicRunner = o.periodicRunner -} - -// WithPeriodicRunner set function which is executed in every ticks. -func WithPeriodicRunner(periodicRunner periodic.Func) PeriodicRunnerOpt { - return PeriodicRunnerOpt{periodicRunner: periodicRunner} -} - -// BlockwiseOpt network option. -type BlockwiseOpt struct { - transferTimeout time.Duration - enable bool - szx blockwise.SZX -} - -func (o BlockwiseOpt) apply(opts *serverOptions) { - opts.blockwiseEnable = o.enable - opts.blockwiseSZX = o.szx - opts.blockwiseTransferTimeout = o.transferTimeout -} - -func (o BlockwiseOpt) applyDial(opts *dialOptions) { - opts.blockwiseEnable = o.enable - opts.blockwiseSZX = o.szx - opts.blockwiseTransferTimeout = o.transferTimeout -} - -// WithBlockwise configure's blockwise transfer. -func WithBlockwise(enable bool, szx blockwise.SZX, transferTimeout time.Duration) BlockwiseOpt { - return BlockwiseOpt{ - enable: enable, - szx: szx, - transferTimeout: transferTimeout, - } -} - -// OnNewClientConnOpt network option. -type OnNewClientConnOpt struct { - onNewClientConn OnNewClientConnFunc -} - -func (o OnNewClientConnOpt) apply(opts *serverOptions) { - opts.onNewClientConn = o.onNewClientConn -} - -// WithOnNewClientConn server's notify about new client connection. -// -// Note: Calling `dtlsConn.Close()` is forbidden, and `dtlsConn` should be treated as a -// "read-only" parameter, mainly used to get the peer certificate from the underlining connection -func WithOnNewClientConn(onNewClientConn OnNewClientConnFunc) OnNewClientConnOpt { - return OnNewClientConnOpt{ - onNewClientConn: onNewClientConn, - } -} - -// TransmissionOpt transmission options. -type TransmissionOpt struct { - transmissionNStart time.Duration - transmissionAcknowledgeTimeout time.Duration - transmissionMaxRetransmit uint32 -} - -func (o TransmissionOpt) apply(opts *serverOptions) { - opts.transmissionNStart = o.transmissionNStart - opts.transmissionAcknowledgeTimeout = o.transmissionAcknowledgeTimeout - opts.transmissionMaxRetransmit = o.transmissionMaxRetransmit -} - -func (o TransmissionOpt) applyDial(opts *dialOptions) { - opts.transmissionNStart = o.transmissionNStart - opts.transmissionAcknowledgeTimeout = o.transmissionAcknowledgeTimeout - opts.transmissionMaxRetransmit = o.transmissionMaxRetransmit -} - -// WithTransmission set options for (re)transmission for Confirmable message-s. -func WithTransmission(transmissionNStart time.Duration, - transmissionAcknowledgeTimeout time.Duration, - transmissionMaxRetransmit uint32) TransmissionOpt { - return TransmissionOpt{ - transmissionNStart: transmissionNStart, - transmissionAcknowledgeTimeout: transmissionAcknowledgeTimeout, - transmissionMaxRetransmit: transmissionMaxRetransmit, - } -} - -// CloseSocketOpt close socket option. -type CloseSocketOpt struct { -} - -func (o CloseSocketOpt) applyDial(opts *dialOptions) { - opts.closeSocket = true -} - -// WithCloseSocket closes socket at the close connection. -func WithCloseSocket() CloseSocketOpt { - return CloseSocketOpt{} -} - -// DialerOpt dialer option. -type DialerOpt struct { - dialer *net.Dialer -} - -func (o DialerOpt) applyDial(opts *dialOptions) { - if o.dialer != nil { - opts.dialer = o.dialer - } -} - -// WithDialer set dialer for dial. -func WithDialer(dialer *net.Dialer) DialerOpt { - return DialerOpt{ - dialer: dialer, - } -} - -// MessagePoolOpt network option. -type MessagePoolOpt struct { - messagePool *pool.Pool -} - -func (o MessagePoolOpt) apply(opts *serverOptions) { - opts.messagePool = o.messagePool -} - -func (o MessagePoolOpt) applyDial(opts *dialOptions) { - opts.messagePool = o.messagePool -} - -// WithMessagePool configure's message pool for acquire/releasing coap messages -func WithMessagePool(messagePool *pool.Pool) MessagePoolOpt { - return MessagePoolOpt{ - messagePool: messagePool, - } -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/dtls/server.go b/vendor/github.com/plgd-dev/go-coap/v2/dtls/server.go deleted file mode 100644 index 00f3408ec7d..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/dtls/server.go +++ /dev/null @@ -1,347 +0,0 @@ -package dtls - -import ( - "context" - "errors" - "fmt" - "io" - "net" - "strings" - "sync" - "time" - - "github.com/pion/dtls/v2" - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - coapNet "github.com/plgd-dev/go-coap/v2/net" - "github.com/plgd-dev/go-coap/v2/net/blockwise" - "github.com/plgd-dev/go-coap/v2/net/monitor/inactivity" - "github.com/plgd-dev/go-coap/v2/pkg/cache" - "github.com/plgd-dev/go-coap/v2/pkg/connections" - "github.com/plgd-dev/go-coap/v2/pkg/runner/periodic" - "github.com/plgd-dev/go-coap/v2/udp/client" - udpMessage "github.com/plgd-dev/go-coap/v2/udp/message" - "github.com/plgd-dev/go-coap/v2/udp/message/pool" - kitSync "github.com/plgd-dev/kit/v2/sync" -) - -// A ServerOption sets options such as credentials, codec and keepalive parameters, etc. -type ServerOption interface { - apply(*serverOptions) -} - -// The HandlerFunc type is an adapter to allow the use of -// ordinary functions as COAP handlers. -type HandlerFunc = func(*client.ResponseWriter, *pool.Message) - -type ErrorFunc = func(error) - -type GoPoolFunc = func(func()) error - -type BlockwiseFactoryFunc = func(getSentRequest func(token message.Token) (blockwise.Message, bool)) *blockwise.BlockWise - -// OnNewClientConnFunc is the callback for new connections. -// -// Note: Calling `dtlsConn.Close()` is forbidden, and `dtlsConn` should be treated as a -// "read-only" parameter, mainly used to get the peer certificate from the underlining connection -type OnNewClientConnFunc = func(cc *client.ClientConn, dtlsConn *dtls.Conn) - -type GetMIDFunc = func() uint16 - -var defaultServerOptions = func() serverOptions { - opts := serverOptions{ - ctx: context.Background(), - maxMessageSize: 64 * 1024, - errors: func(err error) { - fmt.Println(err) - }, - goPool: func(f func()) error { - go func() { - f() - }() - return nil - }, - createInactivityMonitor: func() inactivity.Monitor { - return inactivity.NewNilMonitor() - }, - blockwiseEnable: true, - blockwiseSZX: blockwise.SZX1024, - blockwiseTransferTimeout: time.Second * 5, - onNewClientConn: func(cc *client.ClientConn, dtlsConn *dtls.Conn) {}, - transmissionNStart: time.Second, - transmissionAcknowledgeTimeout: time.Second * 2, - transmissionMaxRetransmit: 4, - getMID: udpMessage.GetMID, - periodicRunner: func(f func(now time.Time) bool) { - go func() { - for f(time.Now()) { - time.Sleep(4 * time.Second) - } - }() - }, - messagePool: pool.New(1024, 1600), - } - opts.handler = func(w *client.ResponseWriter, m *pool.Message) { - if err := w.SetResponse(codes.NotFound, message.TextPlain, nil); err != nil { - opts.errors(fmt.Errorf("server handler: cannot set response: %w", err)) - } - } - return opts -}() - -type serverOptions struct { - ctx context.Context - messagePool *pool.Pool - handler HandlerFunc - errors ErrorFunc - goPool GoPoolFunc - createInactivityMonitor func() inactivity.Monitor - periodicRunner periodic.Func - getMID GetMIDFunc - blockwiseTransferTimeout time.Duration - onNewClientConn OnNewClientConnFunc - transmissionNStart time.Duration - transmissionAcknowledgeTimeout time.Duration - maxMessageSize uint32 - transmissionMaxRetransmit uint32 - blockwiseEnable bool - blockwiseSZX blockwise.SZX -} - -// Listener defined used by coap -type Listener interface { - Close() error - AcceptWithContext(ctx context.Context) (net.Conn, error) -} - -type Server struct { - listen Listener - - ctx context.Context - cache *cache.Cache - goPool GoPoolFunc - createInactivityMonitor func() inactivity.Monitor - errors ErrorFunc - cancel context.CancelFunc - - blockwiseTransferTimeout time.Duration - onNewClientConn OnNewClientConnFunc - - handler HandlerFunc - transmissionAcknowledgeTimeout time.Duration - messagePool *pool.Pool - - getMID GetMIDFunc - periodicRunner periodic.Func - transmissionNStart time.Duration - listenMutex sync.Mutex - transmissionMaxRetransmit uint32 - maxMessageSize uint32 - blockwiseEnable bool - blockwiseSZX blockwise.SZX -} - -func NewServer(opt ...ServerOption) *Server { - opts := defaultServerOptions - for _, o := range opt { - o.apply(&opts) - } - - ctx, cancel := context.WithCancel(opts.ctx) - if opts.errors == nil { - opts.errors = func(error) { - // default no-op - } - } - - if opts.getMID == nil { - opts.getMID = udpMessage.GetMID - } - - if opts.createInactivityMonitor == nil { - opts.createInactivityMonitor = func() inactivity.Monitor { - return inactivity.NewNilMonitor() - } - } - if opts.messagePool == nil { - opts.messagePool = pool.New(0, 0) - } - - errorsFunc := opts.errors - // assign updated func to opts.errors so opts.handler also uses the updated error handler - opts.errors = func(err error) { - if errors.Is(err, context.Canceled) || errors.Is(err, io.EOF) || strings.Contains(err.Error(), "use of closed network connection") { - // this error was produced by cancellation context or closing connection. - return - } - errorsFunc(fmt.Errorf("dtls: %w", err)) - } - - return &Server{ - ctx: ctx, - cancel: cancel, - handler: opts.handler, - maxMessageSize: opts.maxMessageSize, - errors: opts.errors, - goPool: opts.goPool, - createInactivityMonitor: opts.createInactivityMonitor, - blockwiseSZX: opts.blockwiseSZX, - blockwiseEnable: opts.blockwiseEnable, - blockwiseTransferTimeout: opts.blockwiseTransferTimeout, - onNewClientConn: opts.onNewClientConn, - transmissionNStart: opts.transmissionNStart, - transmissionAcknowledgeTimeout: opts.transmissionAcknowledgeTimeout, - transmissionMaxRetransmit: opts.transmissionMaxRetransmit, - getMID: opts.getMID, - periodicRunner: opts.periodicRunner, - cache: cache.NewCache(), - messagePool: opts.messagePool, - } -} - -func (s *Server) checkAndSetListener(l Listener) error { - s.listenMutex.Lock() - defer s.listenMutex.Unlock() - if s.listen != nil { - return fmt.Errorf("server already serve listener") - } - s.listen = l - return nil -} - -func (s *Server) checkAcceptError(err error) (bool, error) { - if err == nil { - return true, nil - } - switch { - case errors.Is(err, coapNet.ErrListenerIsClosed): - s.Stop() - return false, nil - case errors.Is(err, context.DeadlineExceeded), errors.Is(err, context.Canceled): - select { - case <-s.ctx.Done(): - default: - s.errors(fmt.Errorf("cannot accept connection: %w", err)) - return true, nil - } - return false, nil - default: - return true, nil - } -} - -func (s *Server) serveConnection(connections *connections.Connections, cc *client.ClientConn) { - connections.Store(cc) - defer connections.Delete(cc) - - if err := cc.Run(); err != nil { - s.errors(fmt.Errorf("%v: %w", cc.RemoteAddr(), err)) - } -} - -func (s *Server) Serve(l Listener) error { - if s.blockwiseSZX > blockwise.SZX1024 { - return fmt.Errorf("invalid blockwiseSZX") - } - err := s.checkAndSetListener(l) - if err != nil { - return err - } - defer func() { - s.listenMutex.Lock() - defer s.listenMutex.Unlock() - s.listen = nil - }() - - var wg sync.WaitGroup - defer wg.Wait() - - connections := connections.New() - s.periodicRunner(func(now time.Time) bool { - connections.CheckExpirations(now) - return s.ctx.Err() == nil - }) - defer connections.Close() - - for { - rw, err := l.AcceptWithContext(s.ctx) - ok, err := s.checkAcceptError(err) - if err != nil { - return err - } - if !ok { - return nil - } - if rw == nil { - continue - } - wg.Add(1) - var cc *client.ClientConn - monitor := s.createInactivityMonitor() - cc = s.createClientConn(coapNet.NewConn(rw), monitor) - if s.onNewClientConn != nil { - dtlsConn := rw.(*dtls.Conn) - s.onNewClientConn(cc, dtlsConn) - } - go func() { - defer wg.Done() - s.serveConnection(connections, cc) - }() - } -} - -// Stop stops server without wait of ends Serve function. -func (s *Server) Stop() { - s.cancel() - s.listenMutex.Lock() - l := s.listen - s.listen = nil - s.listenMutex.Unlock() - if l != nil { - if err := l.Close(); err != nil { - s.errors(fmt.Errorf("cannot close listener: %w", err)) - } - } -} - -func (s *Server) createClientConn(connection *coapNet.Conn, monitor inactivity.Monitor) *client.ClientConn { - var blockWise *blockwise.BlockWise - if s.blockwiseEnable { - blockWise = blockwise.NewBlockWise( - bwCreateAcquireMessage(s.messagePool), - bwCreateReleaseMessage(s.messagePool), - s.blockwiseTransferTimeout, - s.errors, - false, - func(token message.Token) (blockwise.Message, bool) { - return nil, false - }, - ) - } - obsHandler := client.NewHandlerContainer() - session := NewSession( - s.ctx, - connection, - s.maxMessageSize, - true, - ) - cc := client.NewClientConn( - session, - obsHandler, - kitSync.NewMap(), - s.transmissionNStart, - s.transmissionAcknowledgeTimeout, - s.transmissionMaxRetransmit, - client.NewObservationHandler(obsHandler, s.handler), - s.blockwiseSZX, - blockWise, - s.goPool, - s.errors, - s.getMID, - monitor, - s.cache, - s.messagePool, - ) - - return cc -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/mux/client.go b/vendor/github.com/plgd-dev/go-coap/v2/mux/client.go deleted file mode 100644 index fc4259270e3..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/mux/client.go +++ /dev/null @@ -1,34 +0,0 @@ -package mux - -import ( - "context" - "io" - "net" - - "github.com/plgd-dev/go-coap/v2/message" -) - -type Observation = interface { - Cancel(ctx context.Context) error - Canceled() bool -} - -type Client interface { - Ping(ctx context.Context) error - Get(ctx context.Context, path string, opts ...message.Option) (*message.Message, error) - Delete(ctx context.Context, path string, opts ...message.Option) (*message.Message, error) - Post(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*message.Message, error) - Put(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*message.Message, error) - Observe(ctx context.Context, path string, observeFunc func(notification *message.Message), opts ...message.Option) (Observation, error) - ClientConn() interface{} - - RemoteAddr() net.Addr - Context() context.Context - SetContextValue(key interface{}, val interface{}) - WriteMessage(req *message.Message) error - Do(req *message.Message) (*message.Message, error) - Close() error - Sequence() uint64 - // Done signalizes that connection is not more processed. - Done() <-chan struct{} -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/mux/message.go b/vendor/github.com/plgd-dev/go-coap/v2/mux/message.go deleted file mode 100644 index 40f6b9a281d..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/mux/message.go +++ /dev/null @@ -1,23 +0,0 @@ -package mux - -import "github.com/plgd-dev/go-coap/v2/message" - -// RouteParams contains all the information related to a route -type RouteParams struct { - Path string - Vars map[string]string - PathTemplate string -} - -// Message contains message with sequence number. -type Message struct { - *message.Message - // SequenceNumber identifies the order of the message from a TCP connection. For UDP it is just for debugging. - SequenceNumber uint64 - // IsConfirmable indicates that a UDP message is confirmable. For TCP the value has no semantic. - // When a handler blocks a confirmable message, the client might decide to issue a re-transmission. - // Long running handlers can be handled in a go routine and send the response via w.Client(). - // The ACK is sent as soon as the handler returns. - IsConfirmable bool - RouteParams *RouteParams -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/blockwise/blockwise.go b/vendor/github.com/plgd-dev/go-coap/v2/net/blockwise/blockwise.go deleted file mode 100644 index 2dbdd25a52d..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/net/blockwise/blockwise.go +++ /dev/null @@ -1,878 +0,0 @@ -package blockwise - -import ( - "bytes" - "context" - "errors" - "fmt" - "io" - "net" - "time" - - "github.com/dsnet/golib/memfile" - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - "github.com/plgd-dev/go-coap/v2/pkg/cache" - udpMessage "github.com/plgd-dev/go-coap/v2/udp/message" - "golang.org/x/sync/semaphore" -) - -// Block Opion value is represented: https://tools.ietf.org/html/rfc7959#section-2.2 -// 0 -// 0 1 2 3 4 5 6 7 -// +-+-+-+-+-+-+-+-+ -// | NUM |M| SZX | -// +-+-+-+-+-+-+-+-+ -// 0 1 -// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | NUM |M| SZX | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// 0 1 2 -// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | NUM |M| SZX | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - -const ( - // max block size is 3bytes: https://tools.ietf.org/html/rfc7959#section-2.1 - maxBlockValue = 0xffffff - // maxBlockNumber is 20bits (NUM) - maxBlockNumber = 0xffff7 - // moreBlocksFollowingMask is represented by one bit (M) - moreBlocksFollowingMask = 0x8 - // szxMask last 3bits represents SZX (SZX) - szxMask = 0x7 -) - -// SZX enum representation for the size of the block: https://tools.ietf.org/html/rfc7959#section-2.2 -type SZX uint8 - -const ( - //SZX16 block of size 16bytes - SZX16 SZX = 0 - //SZX32 block of size 32bytes - SZX32 SZX = 1 - //SZX64 block of size 64bytes - SZX64 SZX = 2 - //SZX128 block of size 128bytes - SZX128 SZX = 3 - //SZX256 block of size 256bytes - SZX256 SZX = 4 - //SZX512 block of size 512bytes - SZX512 SZX = 5 - //SZX1024 block of size 1024bytes - SZX1024 SZX = 6 - //SZXBERT block of size n*1024bytes - SZXBERT SZX = 7 -) - -var szxToSize = map[SZX]int64{ - SZX16: 16, - SZX32: 32, - SZX64: 64, - SZX128: 128, - SZX256: 256, - SZX512: 512, - SZX1024: 1024, - SZXBERT: 1024, -} - -// Size number of bytes. -func (s SZX) Size() int64 { - val, ok := szxToSize[s] - if ok { - return val - } - return -1 -} - -// ResponseWriter defines response interface for blockwise transfer. -type ResponseWriter interface { - Message() Message - SetMessage(Message) - RemoteAddr() net.Addr -} - -// Message defines message interface for blockwise transfer. -type Message interface { - // getters - Context() context.Context - Code() codes.Code - Token() message.Token - Queries() ([]string, error) - Path() (string, error) - GetOptionUint32(id message.OptionID) (uint32, error) - GetOptionBytes(id message.OptionID) ([]byte, error) - Options() message.Options - Body() io.ReadSeeker - BodySize() (int64, error) - Sequence() uint64 - // setters - SetCode(codes.Code) - SetToken(message.Token) - SetOptionUint32(id message.OptionID, value uint32) - SetOptionBytes(id message.OptionID, value []byte) - - Remove(id message.OptionID) - ResetOptionsTo(message.Options) - SetBody(r io.ReadSeeker) - SetSequence(uint64) - String() string -} - -// hasType enables access to message.Type for supported messages -// Since only UDP messages have a type -type hasType interface { - Type() udpMessage.Type - SetType(t udpMessage.Type) -} - -// EncodeBlockOption encodes block values to coap option. -func EncodeBlockOption(szx SZX, blockNumber int64, moreBlocksFollowing bool) (uint32, error) { - if szx > SZXBERT { - return 0, ErrInvalidSZX - } - if blockNumber < 0 { - return 0, ErrBlockNumberExceedLimit - } - if blockNumber > maxBlockNumber { - return 0, ErrBlockNumberExceedLimit - } - blockVal := uint32(blockNumber << 4) - m := uint32(0) - if moreBlocksFollowing { - m = 1 - } - blockVal += m << 3 - blockVal += uint32(szx) - return blockVal, nil -} - -// DecodeBlockOption decodes coap block option to block values. -func DecodeBlockOption(blockVal uint32) (szx SZX, blockNumber int64, moreBlocksFollowing bool, err error) { - if blockVal > maxBlockValue { - err = ErrBlockInvalidSize - return - } - - szx = SZX(blockVal & szxMask) //masking for the SZX - if (blockVal & moreBlocksFollowingMask) != 0 { //masking for the "M" - moreBlocksFollowing = true - } - blockNumber = int64(blockVal) >> 4 //shifting out the SZX and M vals. leaving the block number behind - if blockNumber > maxBlockNumber { - err = ErrBlockNumberExceedLimit - } - return -} - -type BlockWise struct { - acquireMessage func(ctx context.Context) Message - releaseMessage func(Message) - receivingMessagesCache *cache.Cache - sendingMessagesCache *cache.Cache - errors func(error) - getSentRequestFromOutside func(token message.Token) (Message, bool) - expiration time.Duration - - bwSentRequest *senderRequestMap - autoCleanUpResponseCache bool -} - -type messageGuard struct { - Message - *semaphore.Weighted -} - -func newRequestGuard(request Message) *messageGuard { - return &messageGuard{ - Message: request, - Weighted: semaphore.NewWeighted(1), - } -} - -// NewBlockWise provides blockwise. -// getSentRequestFromOutside must returns a copy of request which will be released by function releaseMessage after use. -func NewBlockWise( - acquireMessage func(ctx context.Context) Message, - releaseMessage func(Message), - expiration time.Duration, - errors func(error), - autoCleanUpResponseCache bool, - getSentRequestFromOutside func(token message.Token) (Message, bool), -) *BlockWise { - if getSentRequestFromOutside == nil { - getSentRequestFromOutside = func(token message.Token) (Message, bool) { return nil, false } - } - return &BlockWise{ - acquireMessage: acquireMessage, - releaseMessage: releaseMessage, - receivingMessagesCache: cache.NewCache(), - sendingMessagesCache: cache.NewCache(), - errors: errors, - autoCleanUpResponseCache: autoCleanUpResponseCache, - getSentRequestFromOutside: getSentRequestFromOutside, - bwSentRequest: newSenderRequestMap(), - expiration: expiration, - } -} - -func (b *BlockWise) storeSentRequest(req *senderRequest) error { - err := b.bwSentRequest.store(req) - if err != nil { - return fmt.Errorf("cannot store sent request %v: %w", req.String(), err) - } - return nil -} - -func bufferSize(szx SZX, maxMessageSize uint32) int64 { - if szx < SZXBERT { - return szx.Size() - } - return (int64(maxMessageSize) / szx.Size()) * szx.Size() -} - -// CheckExpirations iterates over caches and remove expired items. -func (b *BlockWise) CheckExpirations(now time.Time) { - b.receivingMessagesCache.CheckExpirations(now) - b.sendingMessagesCache.CheckExpirations(now) -} - -// Do sends an coap message and returns an coap response via blockwise transfer. -func (b *BlockWise) Do(r Message, maxSzx SZX, maxMessageSize uint32, do func(req Message) (Message, error)) (Message, error) { - if maxSzx > SZXBERT { - return nil, fmt.Errorf("invalid szx") - } - if len(r.Token()) == 0 { - return nil, fmt.Errorf("invalid token") - } - - req := b.newSentRequestMessage(r, true) - defer req.release() - err := b.storeSentRequest(req) - if err != nil { - return nil, err - } - defer b.bwSentRequest.deleteByToken(req.Token().Hash()) - if r.Body() == nil { - return do(r) - } - payloadSize, err := r.BodySize() - if err != nil { - return nil, fmt.Errorf("cannot get size of payload: %w", err) - } - if payloadSize <= maxSzx.Size() { - return do(r) - } - - switch r.Code() { - case codes.POST, codes.PUT: - break - default: - return nil, fmt.Errorf("unsupported command(%v)", r.Code()) - } - req.SetOptionUint32(message.Size1, uint32(payloadSize)) - - num := int64(0) - buf := make([]byte, 1024) - szx := maxSzx - for { - newBufLen := bufferSize(szx, maxMessageSize) - if int64(cap(buf)) < newBufLen { - buf = make([]byte, newBufLen) - } - buf = buf[:newBufLen] - - off := num * szx.Size() - newOff, err := r.Body().Seek(off, io.SeekStart) - if err != nil { - return nil, fmt.Errorf("cannot seek in payload: %w", err) - } - readed, err := io.ReadFull(r.Body(), buf) - if errors.Is(err, io.ErrUnexpectedEOF) { - if newOff+int64(readed) == payloadSize { - err = nil - } - } - if err != nil { - return nil, fmt.Errorf("cannot read payload: %w", err) - } - buf = buf[:readed] - req.SetBody(bytes.NewReader(buf)) - more := true - if newOff+int64(readed) == payloadSize { - more = false - } - block, err := EncodeBlockOption(szx, num, more) - if err != nil { - return nil, fmt.Errorf("cannot encode block option(%v, %v, %v) to bw request: %w", szx, num, more, err) - } - - req.SetOptionUint32(message.Block1, block) - resp, err := do(req.Message) - if err != nil { - return nil, fmt.Errorf("cannot do bw request: %w", err) - } - block, err = resp.GetOptionUint32(message.Block1) - if err != nil { - return resp, nil - } - switch resp.Code() { - case codes.Continue: - case codes.Created, codes.Changed: - if !more { - return resp, nil - } - default: - return resp, nil - } - - var newSzx SZX - var newNum int64 - newSzx, newNum, _, err = DecodeBlockOption(block) - if err != nil { - return resp, fmt.Errorf("cannot decode block option of bw response: %w", err) - } - if num != newNum { - return resp, fmt.Errorf("unexpected value of acknowledged sequence number(%v != %v)", num, newNum) - } - - num = num + newSzx.Size()/szx.Size() - szx = newSzx - } -} - -type writeMessageResponse struct { - request Message - remoteAddr net.Addr - releaseMessage func(Message) -} - -func newWriteRequestResponse(remoteAddr net.Addr, request Message, acquireMessage func(context.Context) Message, releaseMessage func(Message)) *writeMessageResponse { - req := acquireMessage(request.Context()) - req.SetCode(request.Code()) - req.SetToken(request.Token()) - req.ResetOptionsTo(request.Options()) - req.SetBody(request.Body()) - return &writeMessageResponse{ - request: req, - releaseMessage: releaseMessage, - remoteAddr: remoteAddr, - } -} - -func (w *writeMessageResponse) SetMessage(r Message) { - w.releaseMessage(w.request) - w.request = r -} - -func (w *writeMessageResponse) Message() Message { - return w.request -} - -func (w *writeMessageResponse) RemoteAddr() net.Addr { - return w.remoteAddr -} - -// WriteMessage sends an coap message via blockwise transfer. -func (b *BlockWise) WriteMessage(remoteAddr net.Addr, request Message, maxSZX SZX, maxMessageSize uint32, writeMessage func(r Message) error) error { - req := b.newSentRequestMessage(request, false) - err := b.storeSentRequest(req) - if err != nil { - return fmt.Errorf("cannot write message: %w", err) - } - startSendingMessageBlock, err := EncodeBlockOption(maxSZX, 0, true) - if err != nil { - return fmt.Errorf("cannot encode start sending message block option(%v,%v,%v): %w", maxSZX, 0, true, err) - } - - w := newWriteRequestResponse(remoteAddr, request, b.acquireMessage, b.releaseMessage) - err = b.startSendingMessage(w, maxSZX, maxMessageSize, startSendingMessageBlock) - if err != nil { - return fmt.Errorf("cannot start writing request: %w", err) - } - return writeMessage(w.Message()) -} - -func fitSZX(r Message, blockType message.OptionID, maxSZX SZX) SZX { - block, err := r.GetOptionUint32(blockType) - if err == nil { - szx, _, _, err := DecodeBlockOption(block) - if err != nil { - if maxSZX > szx { - return szx - } - } - } - return maxSZX -} - -func (b *BlockWise) handleSendingMessage(w ResponseWriter, sendingMessage Message, maxSZX SZX, maxMessageSize uint32, token []byte, block uint32) (bool, error) { - blockType := message.Block2 - sizeType := message.Size2 - switch sendingMessage.Code() { - case codes.POST, codes.PUT: - blockType = message.Block1 - sizeType = message.Size1 - } - - szx, num, _, err := DecodeBlockOption(block) - if err != nil { - return false, fmt.Errorf("cannot decode %v option: %w", blockType, err) - } - off := num * szx.Size() - if szx > maxSZX { - szx = maxSZX - } - sendMessage := b.acquireMessage(sendingMessage.Context()) - sendMessage.SetCode(sendingMessage.Code()) - sendMessage.ResetOptionsTo(sendingMessage.Options()) - sendMessage.SetToken(token) - payloadSize, err := sendingMessage.BodySize() - if err != nil { - return false, fmt.Errorf("cannot get size of payload: %w", err) - } - offSeek, err := sendingMessage.Body().Seek(off, io.SeekStart) - if err != nil { - return false, fmt.Errorf("cannot seek in response: %w", err) - } - if off != offSeek { - return false, fmt.Errorf("cannot seek to requested offset(%v != %v)", off, offSeek) - } - buf := make([]byte, 1024) - newBufLen := bufferSize(szx, maxMessageSize) - if int64(len(buf)) < newBufLen { - buf = make([]byte, newBufLen) - } - buf = buf[:newBufLen] - - readed, err := io.ReadFull(sendingMessage.Body(), buf) - if errors.Is(err, io.ErrUnexpectedEOF) { - if offSeek+int64(readed) == payloadSize { - err = nil - } - } - if err != nil { - return false, fmt.Errorf("cannot read response: %w", err) - } - - buf = buf[:readed] - sendMessage.SetBody(bytes.NewReader(buf)) - more := true - if offSeek+int64(readed) == payloadSize { - more = false - } - sendMessage.SetOptionUint32(sizeType, uint32(payloadSize)) - num = (offSeek+int64(readed))/szx.Size() - (int64(readed) / szx.Size()) - block, err = EncodeBlockOption(szx, num, more) - if err != nil { - return false, fmt.Errorf("cannot encode block option(%v,%v,%v): %w", szx, num, more, err) - } - sendMessage.SetOptionUint32(blockType, block) - w.SetMessage(sendMessage) - return more, nil -} - -// RemoveFromResponseCache removes response from cache. It need's tu be used for udp coap. -func (b *BlockWise) RemoveFromResponseCache(token message.Token) { - if len(token) == 0 { - return - } - b.sendingMessagesCache.Delete(token.Hash()) -} - -func (b *BlockWise) sendEntityIncomplete(w ResponseWriter, token message.Token) { - sendMessage := b.acquireMessage(w.Message().Context()) - sendMessage.SetCode(codes.RequestEntityIncomplete) - sendMessage.SetToken(token) - w.SetMessage(sendMessage) -} - -// Handle middleware which constructs COAP request from blockwise transfer and send COAP response via blockwise. -func (b *BlockWise) Handle(w ResponseWriter, r Message, maxSZX SZX, maxMessageSize uint32, next func(w ResponseWriter, r Message)) { - if maxSZX > SZXBERT { - panic("invalid maxSZX") - } - token := r.Token() - - if len(token) == 0 { - err := b.handleReceivedMessage(w, r, maxSZX, maxMessageSize, next) - if err != nil { - b.sendEntityIncomplete(w, token) - b.errors(fmt.Errorf("handleReceivedMessage(%v): %w", r, err)) - } - return - } - tokenStr := token.Hash() - - sendingMessageCached := b.sendingMessagesCache.Load(tokenStr) - - if sendingMessageCached == nil { - err := b.handleReceivedMessage(w, r, maxSZX, maxMessageSize, next) - if err != nil { - b.sendEntityIncomplete(w, token) - b.errors(fmt.Errorf("handleReceivedMessage(%v): %w", r, err)) - } - return - } - more, err := b.continueSendingMessage(w, r, maxSZX, maxMessageSize, sendingMessageCached.Data().(*messageGuard)) - if err != nil { - b.sendingMessagesCache.Delete(tokenStr) - b.errors(fmt.Errorf("continueSendingMessage(%v): %w", r, err)) - return - } - if b.autoCleanUpResponseCache && !more { - b.RemoveFromResponseCache(token) - } -} - -func (b *BlockWise) handleReceivedMessage(w ResponseWriter, r Message, maxSZX SZX, maxMessageSize uint32, next func(w ResponseWriter, r Message)) error { - startSendingMessageBlock, err := EncodeBlockOption(maxSZX, 0, true) - if err != nil { - return fmt.Errorf("cannot encode start sending message block option(%v,%v,%v): %w", maxSZX, 0, true, err) - } - switch r.Code() { - case codes.Empty: - next(w, r) - return nil - case codes.CSM, codes.Ping, codes.Pong, codes.Release, codes.Abort, codes.Continue: - next(w, r) - return nil - case codes.GET, codes.DELETE: - maxSZX = fitSZX(r, message.Block2, maxSZX) - block, errG := r.GetOptionUint32(message.Block2) - if errG == nil { - r.Remove(message.Block2) - } - next(w, r) - if w.Message().Code() == codes.Content && errG == nil { - startSendingMessageBlock = block - } - case codes.POST, codes.PUT: - maxSZX = fitSZX(r, message.Block1, maxSZX) - errP := b.processReceivedMessage(w, r, maxSZX, next, message.Block1, message.Size1) - if errP != nil { - return errP - } - default: - maxSZX = fitSZX(r, message.Block2, maxSZX) - errP := b.processReceivedMessage(w, r, maxSZX, next, message.Block2, message.Size2) - if errP != nil { - return errP - } - } - return b.startSendingMessage(w, maxSZX, maxMessageSize, startSendingMessageBlock) -} - -func (b *BlockWise) continueSendingMessage(w ResponseWriter, r Message, maxSZX SZX, maxMessageSize uint32, messageGuard *messageGuard) (bool, error) { - err := messageGuard.Acquire(r.Context(), 1) - if err != nil { - return false, fmt.Errorf("cannot lock message: %w", err) - } - defer messageGuard.Release(1) - resp := messageGuard.Message - blockType := message.Block2 - switch resp.Code() { - case codes.POST, codes.PUT: - blockType = message.Block1 - } - - block, err := r.GetOptionUint32(blockType) - if err != nil { - return false, fmt.Errorf("cannot get %v option: %w", blockType, err) - } - if blockType == message.Block1 { - // returned blockNumber just acknowledges position we need to set block to the next block. - szx, _, more, errB := DecodeBlockOption(block) - if errB != nil { - return false, fmt.Errorf("cannot decode %v(%v) option: %w", blockType, block, errB) - } - off, errB := resp.Body().Seek(0, io.SeekCurrent) - if errB != nil { - return false, fmt.Errorf("cannot get current position of seek: %w", errB) - } - num := off / szx.Size() - block, errB = EncodeBlockOption(szx, num, more) - if errB != nil { - return false, fmt.Errorf("cannot encode %v(%v, %v, %v) option: %w", blockType, szx, num, more, errB) - } - } - more, err := b.handleSendingMessage(w, resp, maxSZX, maxMessageSize, r.Token(), block) - - if err != nil { - return false, fmt.Errorf("handleSendingMessage: %w", err) - } - return more, err -} - -func isObserveResponse(msg Message) bool { - _, err := msg.GetOptionUint32(message.Observe) - if err != nil { - return false - } - if msg.Code() == codes.Content { - return true - } - return false -} - -func (b *BlockWise) startSendingMessage(w ResponseWriter, maxSZX SZX, maxMessageSize uint32, block uint32) error { - payloadSize, err := w.Message().BodySize() - if err != nil { - return fmt.Errorf("cannot get size of payload: %w", err) - } - - if payloadSize < maxSZX.Size() { - return nil - } - sendingMessage := b.acquireMessage(w.Message().Context()) - sendingMessage.ResetOptionsTo(w.Message().Options()) - sendingMessage.SetBody(w.Message().Body()) - sendingMessage.SetCode(w.Message().Code()) - sendingMessage.SetToken(w.Message().Token()) - - _, err = b.handleSendingMessage(w, sendingMessage, maxSZX, maxMessageSize, sendingMessage.Token(), block) - if err != nil { - return fmt.Errorf("handleSendingMessage: %w", err) - } - if isObserveResponse(w.Message()) { - // https://tools.ietf.org/html/rfc7959#section-2.6 - we don't need store it because client will be get values via GET. - return nil - } - expire := time.Now().Add(b.expiration) - deadline, ok := sendingMessage.Context().Deadline() - if ok { - expire = deadline - } - - _, loaded := b.sendingMessagesCache.LoadOrStore(sendingMessage.Token().Hash(), cache.NewElement(newRequestGuard(sendingMessage), expire, nil)) - if loaded { - return fmt.Errorf("cannot add to sending message cache: message with token %v already exist", sendingMessage.Token().Hash()) - } - return nil -} - -func (b *BlockWise) getSentRequest(token message.Token) Message { - req := b.bwSentRequest.loadByTokenWithFunc(token.Hash(), func(v *senderRequest) interface{} { - req := b.acquireMessage(v.Context()) - req.SetCode(v.Code()) - req.SetToken(v.Token()) - req.ResetOptionsTo(v.Options()) - return req - }) - if req != nil { - return req.(Message) - } - globalRequest, ok := b.getSentRequestFromOutside(token) - if ok { - return globalRequest - } - return nil -} - -func (b *BlockWise) processReceivedMessage(w ResponseWriter, r Message, maxSzx SZX, next func(w ResponseWriter, r Message), blockType message.OptionID, sizeType message.OptionID) error { - token := r.Token() - if len(token) == 0 { - next(w, r) - return nil - } - if r.Code() == codes.GET || r.Code() == codes.DELETE { - next(w, r) - return nil - } - block, err := r.GetOptionUint32(blockType) - if err != nil { - next(w, r) - return nil - } - szx, num, more, err := DecodeBlockOption(block) - if err != nil { - return fmt.Errorf("cannot decode block option: %w", err) - } - sentRequest := b.getSentRequest(token) - expire := time.Now().Add(b.expiration) - if sentRequest != nil { - defer b.releaseMessage(sentRequest) - deadline, ok := sentRequest.Context().Deadline() - if ok { - expire = deadline - } - } - if blockType == message.Block2 && sentRequest == nil { - return fmt.Errorf("cannot request body without paired request") - } - if isObserveResponse(r) { - // https://tools.ietf.org/html/rfc7959#section-2.6 - performs GET with new token. - if sentRequest == nil { - return fmt.Errorf("observation is not registered") - } - token, err = message.GetToken() - if err != nil { - return fmt.Errorf("cannot get token for create GET request: %w", err) - } - expire = time.Now().Add(b.expiration) // context of observation can be expired. - bwSentRequest := b.newSentRequestMessage(sentRequest, true) - bwSentRequest.SetToken(token) - if errS := b.storeSentRequest(bwSentRequest); errS != nil { - return fmt.Errorf("cannot process message: %w", errS) - } - } - - tokenStr := token.Hash() - var cachedReceivedMessageGuard interface{} - e := b.receivingMessagesCache.Load(tokenStr) - if e != nil { - cachedReceivedMessageGuard = e.Data() - } - cannotLockError := func(err error) error { - return fmt.Errorf("processReceivedMessage: cannot lock message: %w", err) - } - var msgGuard *messageGuard - if cachedReceivedMessageGuard == nil { - if szx > maxSzx { - szx = maxSzx - } - // if there is no more then just forward req to next handler - if !more { - next(w, r) - return nil - } - cachedReceivedMessage := b.acquireMessage(r.Context()) - cachedReceivedMessage.ResetOptionsTo(r.Options()) - cachedReceivedMessage.SetToken(r.Token()) - cachedReceivedMessage.SetSequence(r.Sequence()) - cachedReceivedMessage.SetBody(memfile.New(make([]byte, 0, 1024))) - cachedReceivedMessage.SetCode(r.Code()) - msgGuard = newRequestGuard(cachedReceivedMessage) - errA := msgGuard.Acquire(cachedReceivedMessage.Context(), 1) - if errA != nil { - return cannotLockError(errA) - } - defer msgGuard.Release(1) - element, loaded := b.receivingMessagesCache.LoadOrStore(tokenStr, cache.NewElement(msgGuard, expire, func(d interface{}) { - if d == nil { - return - } - b.bwSentRequest.deleteByToken(tokenStr) - })) - // request was already stored in cache, silently - if loaded { - cachedReceivedMessageGuard = element.Data() - if cachedReceivedMessageGuard != nil { - msgGuard = cachedReceivedMessageGuard.(*messageGuard) - errA := msgGuard.Acquire(cachedReceivedMessage.Context(), 1) - if errA != nil { - return cannotLockError(errA) - } - defer msgGuard.Release(1) - } else { - return fmt.Errorf("request was already stored in cache") - } - } - } else { - msgGuard = cachedReceivedMessageGuard.(*messageGuard) - errA := msgGuard.Acquire(msgGuard.Context(), 1) - if errA != nil { - return cannotLockError(errA) - } - defer msgGuard.Release(1) - } - defer func(err *error) { - if *err != nil { - b.receivingMessagesCache.Delete(tokenStr) - } - }(&err) - cachedReceivedMessage := msgGuard.Message - payloadFile, ok := cachedReceivedMessage.Body().(*memfile.File) - if !ok { - return fmt.Errorf("invalid body type(%T) stored in receivingMessagesCache", cachedReceivedMessage.Body()) - } - rETAG, errETAG := r.GetOptionBytes(message.ETag) - cachedReceivedMessageETAG, errCachedReceivedMessageETAG := cachedReceivedMessage.GetOptionBytes(message.ETag) - switch { - case errETAG == nil && errCachedReceivedMessageETAG != nil: - if len(cachedReceivedMessageETAG) > 0 { // make sure there is an etag there - return fmt.Errorf("received message doesn't contains ETAG but cached received message contains it(%v)", cachedReceivedMessageETAG) - } - case errETAG != nil && errCachedReceivedMessageETAG == nil: - if len(rETAG) > 0 { // make sure there is an etag there - return fmt.Errorf("received message contains ETAG(%v) but cached received message doesn't", rETAG) - } - case !bytes.Equal(rETAG, cachedReceivedMessageETAG): - // ETAG was changed - drop data and set new ETAG - cachedReceivedMessage.SetOptionBytes(message.ETag, rETAG) - if errT := payloadFile.Truncate(0); errT != nil { - return fmt.Errorf("cannot truncate cached request: %w", errT) - } - } - - off := num * szx.Size() - payloadSize, err := cachedReceivedMessage.BodySize() - if err != nil { - return fmt.Errorf("cannot get size of payload: %w", err) - } - - if off == payloadSize { - copyn, errS := payloadFile.Seek(off, io.SeekStart) - if errS != nil { - return fmt.Errorf("cannot seek to off(%v) of cached request: %w", off, errS) - } - if r.Body() != nil { - _, errS = r.Body().Seek(0, io.SeekStart) - if errS != nil { - return fmt.Errorf("cannot seek to start of request: %w", errS) - } - written, errC := io.Copy(payloadFile, r.Body()) - if errC != nil { - return fmt.Errorf("cannot copy to cached request: %w", errC) - } - payloadSize = copyn + written - } else { - payloadSize = copyn - } - err = payloadFile.Truncate(payloadSize) - if err != nil { - return fmt.Errorf("cannot truncate cached request: %w", err) - } - if !more { - b.receivingMessagesCache.Delete(tokenStr) - cachedReceivedMessage.Remove(blockType) - cachedReceivedMessage.Remove(sizeType) - setTypeFrom(cachedReceivedMessage, r) - if !bytes.Equal(cachedReceivedMessage.Token(), token) { - b.bwSentRequest.deleteByToken(tokenStr) - } - _, errS := cachedReceivedMessage.Body().Seek(0, io.SeekStart) - if errS != nil { - return fmt.Errorf("cannot seek to start of cachedReceivedMessage request: %w", errS) - } - next(w, cachedReceivedMessage) - return nil - } - } - if szx > maxSzx { - szx = maxSzx - } - - sendMessage := b.acquireMessage(r.Context()) - sendMessage.SetToken(token) - if blockType == message.Block2 { - num = payloadSize / szx.Size() - sendMessage.ResetOptionsTo(sentRequest.Options()) - sendMessage.SetCode(sentRequest.Code()) - sendMessage.Remove(message.Observe) - sendMessage.Remove(message.Block1) - sendMessage.Remove(message.Size1) - } else { - sendMessage.SetCode(codes.Continue) - } - respBlock, err := EncodeBlockOption(szx, num, more) - if err != nil { - b.releaseMessage(sendMessage) - return fmt.Errorf("cannot encode block option(%v,%v,%v): %w", szx, num, more, err) - } - sendMessage.SetOptionUint32(blockType, respBlock) - w.SetMessage(sendMessage) - return nil -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/blockwise/senderRequestMap.go b/vendor/github.com/plgd-dev/go-coap/v2/net/blockwise/senderRequestMap.go deleted file mode 100644 index 2751da364be..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/net/blockwise/senderRequestMap.go +++ /dev/null @@ -1,116 +0,0 @@ -package blockwise - -import ( - "fmt" - - kitSync "github.com/plgd-dev/kit/v2/sync" -) - -func messageToGuardTransferKey(msg Message) string { - code := msg.Code() - path, _ := msg.Path() - queries, _ := msg.Queries() - - return fmt.Sprintf("%v:%v:%v", code, path, queries) -} - -type senderRequest struct { - transferKey string - *messageGuard - release func() - lock bool -} - -func setTypeFrom(to Message, from Message) { - if udpTo, ok := to.(hasType); ok { - if udpFrom, ok := from.(hasType); ok { - udpTo.SetType(udpFrom.Type()) - } - } -} - -func (b *BlockWise) newSentRequestMessage(r Message, lock bool) *senderRequest { - req := b.acquireMessage(r.Context()) - req.SetCode(r.Code()) - req.SetToken(r.Token()) - req.ResetOptionsTo(r.Options()) - setTypeFrom(req, r) - data := &senderRequest{ - transferKey: messageToGuardTransferKey(req), - messageGuard: newRequestGuard(req), - release: func() { - b.releaseMessage(req) - }, - lock: lock, - } - return data -} - -type senderRequestMap struct { - byToken *kitSync.Map - byTransferKey *kitSync.Map -} - -func newSenderRequestMap() *senderRequestMap { - return &senderRequestMap{ - byToken: kitSync.NewMap(), - byTransferKey: kitSync.NewMap(), - } -} - -func (m *senderRequestMap) store(req *senderRequest) error { - if !req.lock { - m.byToken.Store(req.Token().Hash(), req) - return nil - } - for { - var err error - v, loaded := m.byTransferKey.LoadOrStoreWithFunc(req.transferKey, func(value interface{}) interface{} { - return value - }, func() interface{} { - err = req.Acquire(req.Context(), 1) - return req - }) - if err != nil { - return fmt.Errorf("cannot lock message: %w", err) - } - if !loaded { - m.byToken.Store(req.Token().Hash(), req) - return nil - } - p := v.(*senderRequest) - err = p.Acquire(req.Context(), 1) - if err != nil { - return fmt.Errorf("cannot lock message: %w", err) - } - p.Release(1) - } -} - -func (m *senderRequestMap) loadByTokenWithFunc(token uint64, onload func(value *senderRequest) interface{}) interface{} { - v, ok := m.byToken.LoadWithFunc(token, func(value interface{}) interface{} { - v := value.(*senderRequest) - return onload(v) - }) - if ok { - return v - } - return nil -} - -func (m *senderRequestMap) deleteByToken(token uint64) { - v, ok := m.byToken.PullOut(token) - if !ok { - return - } - req := v.(*senderRequest) - v1, ok := m.byTransferKey.Load(req.transferKey) - if !ok { - return - } - req1 := v1.(*senderRequest) - if req == req1 { - m.byTransferKey.Delete(req.transferKey) - req.Release(1) - } -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/dtlslistener.go b/vendor/github.com/plgd-dev/go-coap/v2/net/dtlslistener.go deleted file mode 100644 index e7c6c204c45..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/net/dtlslistener.go +++ /dev/null @@ -1,86 +0,0 @@ -package net - -import ( - "context" - "fmt" - "net" - "time" - - dtls "github.com/pion/dtls/v2" - "go.uber.org/atomic" -) - -// DTLSListener is a DTLS listener that provides accept with context. -type DTLSListener struct { - listener net.Listener - closed atomic.Bool -} - -// NewDTLSListener creates dtls listener. -// Known networks are "udp", "udp4" (IPv4-only), "udp6" (IPv6-only). -func NewDTLSListener(network string, addr string, dtlsCfg *dtls.Config) (*DTLSListener, error) { - a, err := net.ResolveUDPAddr(network, addr) - if err != nil { - return nil, fmt.Errorf("cannot resolve address: %w", err) - } - - var l DTLSListener - connectContextMaker := dtlsCfg.ConnectContextMaker - if connectContextMaker == nil { - connectContextMaker = func() (context.Context, func()) { - return context.WithTimeout(context.Background(), 30*time.Second) - } - } - dtlsCfg.ConnectContextMaker = func() (context.Context, func()) { - ctx, cancel := connectContextMaker() - if l.closed.Load() { - cancel() - } - return ctx, cancel - } - - listener, err := dtls.Listen(network, a, dtlsCfg) - if err != nil { - return nil, fmt.Errorf("cannot create new dtls listener: %w", err) - } - l.listener = listener - return &l, nil -} - -// AcceptWithContext waits with context for a generic Conn. -func (l *DTLSListener) AcceptWithContext(ctx context.Context) (net.Conn, error) { - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - } - if l.closed.Load() { - return nil, ErrListenerIsClosed - } - c, err := l.listener.Accept() - if err != nil { - return nil, err - } - if c == nil { - return nil, nil - } - return c, nil -} - -// Accept waits for a generic Conn. -func (l *DTLSListener) Accept() (net.Conn, error) { - return l.AcceptWithContext(context.Background()) -} - -// Close closes the connection. -func (l *DTLSListener) Close() error { - if !l.closed.CAS(false, true) { - return nil - } - return l.listener.Close() -} - -// Addr represents a network end point address. -func (l *DTLSListener) Addr() net.Addr { - return l.listener.Addr() -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/monitor/inactivity/inactivitymonitor.go b/vendor/github.com/plgd-dev/go-coap/v2/net/monitor/inactivity/inactivitymonitor.go deleted file mode 100644 index b34d3a20f09..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/net/monitor/inactivity/inactivitymonitor.go +++ /dev/null @@ -1,72 +0,0 @@ -package inactivity - -import ( - "context" - "sync/atomic" - "time" -) - -type Monitor = interface { - CheckInactivity(now time.Time, cc ClientConn) - Notify() -} - -type OnInactiveFunc = func(cc ClientConn) - -type ClientConn = interface { - Context() context.Context - Close() error -} - -type inactivityMonitor struct { - lastActivity atomic.Value - duration time.Duration - onInactive OnInactiveFunc -} - -func (m *inactivityMonitor) Notify() { - m.lastActivity.Store(time.Now()) -} - -func (m *inactivityMonitor) LastActivity() time.Time { - if t, ok := m.lastActivity.Load().(time.Time); ok { - return t - } - return time.Time{} -} - -func CloseClientConn(cc ClientConn) { - // call cc.Close() directly to check and handle error if necessary - _ = cc.Close() -} - -func NewInactivityMonitor(duration time.Duration, onInactive OnInactiveFunc) Monitor { - m := &inactivityMonitor{ - duration: duration, - onInactive: onInactive, - } - m.Notify() - return m -} - -func (m *inactivityMonitor) CheckInactivity(now time.Time, cc ClientConn) { - if m.onInactive == nil || m.duration == time.Duration(0) { - return - } - if now.After(m.LastActivity().Add(m.duration)) { - m.onInactive(cc) - } -} - -type nilMonitor struct { -} - -func (m *nilMonitor) CheckInactivity(now time.Time, cc ClientConn) { -} - -func (m *nilMonitor) Notify() { -} - -func NewNilMonitor() Monitor { - return &nilMonitor{} -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/monitor/inactivity/keepalive.go b/vendor/github.com/plgd-dev/go-coap/v2/net/monitor/inactivity/keepalive.go deleted file mode 100644 index 138bf7b01c5..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/net/monitor/inactivity/keepalive.go +++ /dev/null @@ -1,54 +0,0 @@ -package inactivity - -import ( - "sync/atomic" -) - -type KeepAlive struct { - pongToken uint64 - onInactive OnInactiveFunc - - sendPing func(cc ClientConn, receivePong func()) (func(), error) - cancelPing func() - numFails uint32 - - maxRetries uint32 -} - -func NewKeepAlive(maxRetries uint32, onInactive OnInactiveFunc, sendPing func(cc ClientConn, receivePong func()) (func(), error)) *KeepAlive { - return &KeepAlive{ - maxRetries: maxRetries, - sendPing: sendPing, - onInactive: onInactive, - } -} - -func (m *KeepAlive) OnInactive(cc ClientConn) { - v := m.incrementFails() - if m.cancelPing != nil { - m.cancelPing() - m.cancelPing = nil - } - if v > m.maxRetries { - m.onInactive(cc) - return - } - pongToken := atomic.AddUint64(&m.pongToken, 1) - cancel, err := m.sendPing(cc, func() { - if atomic.LoadUint64(&m.pongToken) == pongToken { - m.resetFails() - } - }) - if err != nil { - return - } - m.cancelPing = cancel -} - -func (m *KeepAlive) incrementFails() uint32 { - return atomic.AddUint32(&m.numFails, 1) -} - -func (m *KeepAlive) resetFails() { - atomic.StoreUint32(&m.numFails, 0) -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/pkg/cache/cache.go b/vendor/github.com/plgd-dev/go-coap/v2/pkg/cache/cache.go deleted file mode 100644 index 4af917197ed..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/pkg/cache/cache.go +++ /dev/null @@ -1,99 +0,0 @@ -package cache - -import ( - "time" - - kitSync "github.com/plgd-dev/kit/v2/sync" -) - -func DefaultOnExpire(d interface{}) { - // for nothing on expire -} - -type Element struct { - validUntil time.Time - data interface{} - onExpire func(d interface{}) -} - -func (e *Element) IsExpired(now time.Time) bool { - if e.validUntil.IsZero() { - return false - } - return now.After(e.validUntil) -} - -func (e *Element) Data() interface{} { - return e.data -} - -func NewElement(data interface{}, validUntil time.Time, onExpire func(d interface{})) *Element { - if onExpire == nil { - onExpire = DefaultOnExpire - } - return &Element{data: data, validUntil: validUntil, onExpire: onExpire} -} - -type Cache struct { - data kitSync.Map -} - -func NewCache() *Cache { - return &Cache{ - data: *kitSync.NewMap(), - } -} - -func (c *Cache) LoadOrStore(key interface{}, e *Element) (actual *Element, loaded bool) { - now := time.Now() - c.data.ReplaceWithFunc(key, func(oldValue interface{}, oldLoaded bool) (newValue interface{}, deleteValue bool) { - if oldLoaded { - o := oldValue.(*Element) - if !o.IsExpired(now) { - actual = o - return o, false - } - } - actual = e - return e, false - }) - return actual, actual != e -} - -func (c *Cache) Load(key interface{}) (actual *Element) { - a, loaded := c.data.Load(key) - if !loaded { - return nil - } - actual = a.(*Element) - if actual.IsExpired(time.Now()) { - return nil - } - return actual -} - -func (c *Cache) Delete(key interface{}) { - c.data.Delete(key) -} - -func (c *Cache) CheckExpirations(now time.Time) { - m := make(map[interface{}]*Element) - c.data.Range(func(key, value interface{}) bool { - m[key] = value.(*Element) - return true - }) - for k, e := range m { - if e.IsExpired(now) { - c.data.Delete(k) - e.onExpire(e.data) - } - } -} - -func (c *Cache) PullOutAll() map[interface{}]interface{} { - res := make(map[interface{}]interface{}) - for key, value := range c.data.PullOutAll() { - res[key] = value.(*Element).Data() - } - return res -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/server.go b/vendor/github.com/plgd-dev/go-coap/v2/server.go deleted file mode 100644 index 25b627af92f..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/server.go +++ /dev/null @@ -1,79 +0,0 @@ -// Package coap provides a CoAP client and server. -package coap - -import ( - "crypto/tls" - "fmt" - - piondtls "github.com/pion/dtls/v2" - "github.com/plgd-dev/go-coap/v2/dtls" - "github.com/plgd-dev/go-coap/v2/mux" - "github.com/plgd-dev/go-coap/v2/net" - "github.com/plgd-dev/go-coap/v2/tcp" - "github.com/plgd-dev/go-coap/v2/udp" -) - -// ListenAndServe Starts a server on address and network specified Invoke handler -// for incoming queries. -func ListenAndServe(network string, addr string, handler mux.Handler) (err error) { - switch network { - case "udp", "udp4", "udp6", "": - l, err := net.NewListenUDP(network, addr) - if err != nil { - return err - } - defer func() { - if errC := l.Close(); errC != nil && err == nil { - err = errC - } - }() - s := udp.NewServer(udp.WithMux(handler)) - return s.Serve(l) - case "tcp", "tcp4", "tcp6": - l, err := net.NewTCPListener(network, addr) - if err != nil { - return err - } - defer func() { - if errC := l.Close(); errC != nil && err == nil { - err = errC - } - }() - s := tcp.NewServer(tcp.WithMux(handler)) - return s.Serve(l) - default: - return fmt.Errorf("invalid network (%v)", network) - } -} - -// ListenAndServeTCPTLS Starts a server on address and network over TLS specified Invoke handler -// for incoming queries. -func ListenAndServeTCPTLS(network, addr string, config *tls.Config, handler mux.Handler) (err error) { - l, err := net.NewTLSListener(network, addr, config) - if err != nil { - return err - } - defer func() { - if errC := l.Close(); errC != nil && err == nil { - err = errC - } - }() - s := tcp.NewServer(tcp.WithMux(handler)) - return s.Serve(l) -} - -// ListenAndServeDTLS Starts a server on address and network over DTLS specified Invoke handler -// for incoming queries. -func ListenAndServeDTLS(network string, addr string, config *piondtls.Config, handler mux.Handler) (err error) { - l, err := net.NewDTLSListener(network, addr, config) - if err != nil { - return err - } - defer func() { - if errC := l.Close(); errC != nil && err == nil { - err = errC - } - }() - s := dtls.NewServer(dtls.WithMux(handler)) - return s.Serve(l) -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/tcp/client.go b/vendor/github.com/plgd-dev/go-coap/v2/tcp/client.go deleted file mode 100644 index 86360183905..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/tcp/client.go +++ /dev/null @@ -1,129 +0,0 @@ -package tcp - -import ( - "context" - "io" - "net" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/mux" - "github.com/plgd-dev/go-coap/v2/tcp/message/pool" -) - -type ClientTCP struct { - cc *ClientConn -} - -func NewClientTCP(cc *ClientConn) *ClientTCP { - return &ClientTCP{ - cc: cc, - } -} - -func (c *ClientTCP) Ping(ctx context.Context) error { - return c.cc.Ping(ctx) -} - -func (c *ClientTCP) Delete(ctx context.Context, path string, opts ...message.Option) (*message.Message, error) { - resp, err := c.cc.Delete(ctx, path, opts...) - if err != nil { - return nil, err - } - defer c.cc.session.messagePool.ReleaseMessage(resp) - return pool.ConvertTo(resp) -} - -func (c *ClientTCP) Put(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*message.Message, error) { - resp, err := c.cc.Put(ctx, path, contentFormat, payload, opts...) - if err != nil { - return nil, err - } - defer c.cc.session.messagePool.ReleaseMessage(resp) - return pool.ConvertTo(resp) -} - -func (c *ClientTCP) Post(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*message.Message, error) { - resp, err := c.cc.Post(ctx, path, contentFormat, payload, opts...) - if err != nil { - return nil, err - } - defer c.cc.session.messagePool.ReleaseMessage(resp) - return pool.ConvertTo(resp) -} - -func (c *ClientTCP) Get(ctx context.Context, path string, opts ...message.Option) (*message.Message, error) { - resp, err := c.cc.Get(ctx, path, opts...) - if err != nil { - return nil, err - } - defer c.cc.session.messagePool.ReleaseMessage(resp) - return pool.ConvertTo(resp) -} - -func (c *ClientTCP) Close() error { - return c.cc.Close() -} - -func (c *ClientTCP) RemoteAddr() net.Addr { - return c.cc.RemoteAddr() -} - -func (c *ClientTCP) Context() context.Context { - return c.cc.Context() -} - -func (c *ClientTCP) SetContextValue(key interface{}, val interface{}) { - c.cc.Session().SetContextValue(key, val) -} - -func (c *ClientTCP) WriteMessage(req *message.Message) error { - r, err := c.cc.session.messagePool.ConvertFrom(req) - if err != nil { - return err - } - defer c.cc.session.messagePool.ReleaseMessage(r) - return c.cc.WriteMessage(r) -} - -func (c *ClientTCP) Do(req *message.Message) (*message.Message, error) { - r, err := c.cc.session.messagePool.ConvertFrom(req) - if err != nil { - return nil, err - } - defer c.cc.session.messagePool.ReleaseMessage(r) - resp, err := c.cc.Do(r) - if err != nil { - return nil, err - } - defer c.cc.session.messagePool.ReleaseMessage(resp) - return pool.ConvertTo(resp) -} - -func createClientConnObserveHandler(observeFunc func(notification *message.Message)) func(n *pool.Message) { - return func(n *pool.Message) { - muxn, err := pool.ConvertTo(n) - if err != nil { - return - } - observeFunc(muxn) - } -} - -func (c *ClientTCP) Observe(ctx context.Context, path string, observeFunc func(notification *message.Message), opts ...message.Option) (mux.Observation, error) { - return c.cc.Observe(ctx, path, createClientConnObserveHandler(observeFunc), opts...) -} - -// Sequence acquires sequence number. -func (c *ClientTCP) Sequence() uint64 { - return c.cc.Sequence() -} - -// ClientConn get's underlaying client connection. -func (c *ClientTCP) ClientConn() interface{} { - return c.cc -} - -// Done signalizes that connection is not more processed. -func (c *ClientTCP) Done() <-chan struct{} { - return c.cc.Done() -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/tcp/clientconn.go b/vendor/github.com/plgd-dev/go-coap/v2/tcp/clientconn.go deleted file mode 100644 index 361deeadadf..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/tcp/clientconn.go +++ /dev/null @@ -1,559 +0,0 @@ -package tcp - -import ( - "context" - "crypto/tls" - "errors" - "fmt" - "io" - "net" - "time" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - coapNet "github.com/plgd-dev/go-coap/v2/net" - "github.com/plgd-dev/go-coap/v2/net/blockwise" - "github.com/plgd-dev/go-coap/v2/net/monitor/inactivity" - "github.com/plgd-dev/go-coap/v2/pkg/runner/periodic" - "github.com/plgd-dev/go-coap/v2/tcp/message/pool" - kitSync "github.com/plgd-dev/kit/v2/sync" -) - -var defaultDialOptions = func() dialOptions { - opts := dialOptions{ - ctx: context.Background(), - maxMessageSize: 64 * 1024, - errors: func(err error) { - fmt.Println(err) - }, - goPool: func(f func()) error { - go func() { - f() - }() - return nil - }, - dialer: &net.Dialer{Timeout: time.Second * 3}, - net: "tcp", - blockwiseSZX: blockwise.SZX1024, - blockwiseEnable: true, - blockwiseTransferTimeout: time.Second * 3, - createInactivityMonitor: func() inactivity.Monitor { - return inactivity.NewNilMonitor() - }, - periodicRunner: func(f func(now time.Time) bool) { - go func() { - for f(time.Now()) { - time.Sleep(4 * time.Second) - } - }() - }, - connectionCacheSize: 2048, - messagePool: pool.New(1024, 2048), - } - opts.handler = func(w *ResponseWriter, r *pool.Message) { - switch r.Code() { - case codes.POST, codes.PUT, codes.GET, codes.DELETE: - if err := w.SetResponse(codes.NotFound, message.TextPlain, nil); err != nil { - opts.errors(fmt.Errorf("client handler: cannot set response: %w", err)) - } - } - } - return opts -}() - -type dialOptions struct { - ctx context.Context - net string - blockwiseTransferTimeout time.Duration - messagePool *pool.Pool - goPool GoPoolFunc - dialer *net.Dialer - tlsCfg *tls.Config - periodicRunner periodic.Func - createInactivityMonitor func() inactivity.Monitor - handler HandlerFunc - errors ErrorFunc - maxMessageSize uint32 - connectionCacheSize uint16 - disablePeerTCPSignalMessageCSMs bool - closeSocket bool - blockwiseEnable bool - blockwiseSZX blockwise.SZX - disableTCPSignalMessageCSM bool -} - -// A DialOption sets options such as credentials, keepalive parameters, etc. -type DialOption interface { - applyDial(*dialOptions) -} - -type Notifier interface { - Notify() -} - -// ClientConn represents a virtual connection to a conceptual endpoint, to perform COAPs commands. -type ClientConn struct { - noCopy - session *Session - observationTokenHandler *HandlerContainer - observationRequests *kitSync.Map -} - -// Dial creates a client connection to the given target. -func Dial(target string, opts ...DialOption) (*ClientConn, error) { - cfg := defaultDialOptions - for _, o := range opts { - o.applyDial(&cfg) - } - - var conn net.Conn - var err error - if cfg.tlsCfg != nil { - conn, err = tls.DialWithDialer(cfg.dialer, cfg.net, target, cfg.tlsCfg) - } else { - conn, err = cfg.dialer.DialContext(cfg.ctx, cfg.net, target) - } - if err != nil { - return nil, err - } - opts = append(opts, WithCloseSocket()) - return Client(conn, opts...), nil -} - -func bwCreateAcquireMessage(messagePool *pool.Pool) func(ctx context.Context) blockwise.Message { - return func(ctx context.Context) blockwise.Message { - return messagePool.AcquireMessage(ctx) - } -} - -func bwCreateReleaseMessage(messagePool *pool.Pool) func(m blockwise.Message) { - return func(m blockwise.Message) { - messagePool.ReleaseMessage(m.(*pool.Message)) - } -} - -func bwCreateHandlerFunc(messagePool *pool.Pool, observationRequests *kitSync.Map) func(token message.Token) (blockwise.Message, bool) { - return func(token message.Token) (blockwise.Message, bool) { - msg, ok := observationRequests.LoadWithFunc(token.Hash(), func(v interface{}) interface{} { - r := v.(message.Message) - d := messagePool.AcquireMessage(r.Context) - d.ResetOptionsTo(r.Options) - d.SetCode(r.Code) - d.SetToken(r.Token) - return d - }) - if !ok { - return nil, ok - } - bwMessage := msg.(blockwise.Message) - return bwMessage, ok - } -} - -// Client creates client over tcp/tcp-tls connection. -func Client(conn net.Conn, opts ...DialOption) *ClientConn { - cfg := defaultDialOptions - for _, o := range opts { - o.applyDial(&cfg) - } - if cfg.errors == nil { - cfg.errors = func(error) { - // default no-op - } - } - if cfg.createInactivityMonitor == nil { - cfg.createInactivityMonitor = func() inactivity.Monitor { - return inactivity.NewNilMonitor() - } - } - if cfg.messagePool == nil { - cfg.messagePool = pool.New(0, 0) - } - errorsFunc := cfg.errors - cfg.errors = func(err error) { - if coapNet.IsCancelOrCloseError(err) { - // this error was produced by cancellation context or closing connection. - return - } - errorsFunc(fmt.Errorf("tcp: %w", err)) - } - - observationRequests := kitSync.NewMap() - var blockWise *blockwise.BlockWise - if cfg.blockwiseEnable { - blockWise = blockwise.NewBlockWise( - bwCreateAcquireMessage(cfg.messagePool), - bwCreateReleaseMessage(cfg.messagePool), - cfg.blockwiseTransferTimeout, - cfg.errors, - false, - bwCreateHandlerFunc(cfg.messagePool, observationRequests), - ) - } - - l := coapNet.NewConn(conn) - monitor := cfg.createInactivityMonitor() - observationTokenHandler := NewHandlerContainer() - session := NewSession(cfg.ctx, - l, - NewObservationHandler(observationTokenHandler, cfg.handler), - cfg.maxMessageSize, - cfg.goPool, - cfg.errors, - cfg.blockwiseSZX, - blockWise, - cfg.disablePeerTCPSignalMessageCSMs, - cfg.disableTCPSignalMessageCSM, - cfg.closeSocket, - monitor, - cfg.connectionCacheSize, - cfg.messagePool, - ) - cc := NewClientConn(session, observationTokenHandler, observationRequests) - - cfg.periodicRunner(func(now time.Time) bool { - cc.CheckExpirations(now) - return cc.Context().Err() == nil - }) - - go func() { - err := cc.Run() - if err != nil { - cfg.errors(fmt.Errorf("%v: %w", cc.RemoteAddr(), err)) - } - }() - - return cc -} - -// NewClientConn creates connection over session and observation. -func NewClientConn(session *Session, observationTokenHandler *HandlerContainer, observationRequests *kitSync.Map) *ClientConn { - return &ClientConn{ - session: session, - observationTokenHandler: observationTokenHandler, - observationRequests: observationRequests, - } -} - -func (cc *ClientConn) Session() *Session { - return cc.session -} - -// Close closes connection without wait of ends Run function. -func (cc *ClientConn) Close() error { - err := cc.session.Close() - if errors.Is(err, net.ErrClosed) { - return nil - } - return err -} - -func (cc *ClientConn) do(req *pool.Message) (*pool.Message, error) { - token := req.Token() - if token == nil { - return nil, fmt.Errorf("invalid token") - } - respChan := make(chan *pool.Message, 1) - if err := cc.session.TokenHandler().Insert(token, func(w *ResponseWriter, r *pool.Message) { - r.Hijack() - select { - case respChan <- r: - default: - } - }); err != nil { - return nil, fmt.Errorf("cannot add token handler: %w", err) - } - defer func() { - if _, err := cc.session.TokenHandler().Pop(token); err != nil && !errors.Is(err, ErrKeyNotExists) { - cc.session.errors(fmt.Errorf("cannot remove token handler: %w", err)) - } - }() - if err := cc.session.WriteMessage(req); err != nil { - return nil, fmt.Errorf("cannot write request: %w", err) - } - - select { - case <-req.Context().Done(): - return nil, req.Context().Err() - case <-cc.session.Context().Done(): - return nil, fmt.Errorf("connection was closed: %w", cc.Context().Err()) - case resp := <-respChan: - return resp, nil - } -} - -// Do sends an coap message and returns an coap response. -// -// An error is returned if by failure to speak COAP (such as a network connectivity problem). -// Any status code doesn't cause an error. -// -// Caller is responsible to release request and response. -func (cc *ClientConn) Do(req *pool.Message) (*pool.Message, error) { - if !cc.session.PeerBlockWiseTransferEnabled() || cc.session.blockWise == nil { - return cc.do(req) - } - bwresp, err := cc.session.blockWise.Do(req, cc.session.blockwiseSZX, cc.session.maxMessageSize, func(bwreq blockwise.Message) (blockwise.Message, error) { - return cc.do(bwreq.(*pool.Message)) - }) - if err != nil { - return nil, err - } - return bwresp.(*pool.Message), nil -} - -func (cc *ClientConn) writeMessage(req *pool.Message) error { - return cc.session.WriteMessage(req) -} - -// WriteMessage sends an coap message. -func (cc *ClientConn) WriteMessage(req *pool.Message) error { - if !cc.session.PeerBlockWiseTransferEnabled() || cc.session.blockWise == nil { - return cc.writeMessage(req) - } - return cc.session.blockWise.WriteMessage(cc.RemoteAddr(), req, cc.session.blockwiseSZX, cc.session.maxMessageSize, func(bwreq blockwise.Message) error { - return cc.writeMessage(bwreq.(*pool.Message)) - }) -} - -func newCommonRequest(ctx context.Context, messagePool *pool.Pool, code codes.Code, path string, opts ...message.Option) (*pool.Message, error) { - token, err := message.GetToken() - if err != nil { - return nil, fmt.Errorf("cannot get token: %w", err) - } - req := messagePool.AcquireMessage(ctx) - req.SetCode(code) - req.SetToken(token) - req.ResetOptionsTo(opts) - if err := req.SetPath(path); err != nil { - messagePool.ReleaseMessage(req) - return nil, err - } - return req, nil -} - -// NewGetRequest creates get request. -// -// Use ctx to set timeout. -func NewGetRequest(ctx context.Context, messagePool *pool.Pool, path string, opts ...message.Option) (*pool.Message, error) { - return newCommonRequest(ctx, messagePool, codes.GET, path, opts...) -} - -// Get issues a GET to the specified path. -// -// Use ctx to set timeout. -// -// An error is returned if by failure to speak COAP (such as a network connectivity problem). -// Any status code doesn't cause an error. -func (cc *ClientConn) Get(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) { - req, err := NewGetRequest(ctx, cc.session.messagePool, path, opts...) - if err != nil { - return nil, fmt.Errorf("cannot create get request: %w", err) - } - defer cc.session.messagePool.ReleaseMessage(req) - return cc.Do(req) -} - -// NewPostRequest creates post request. -// -// Use ctx to set timeout. -// -// An error is returned if by failure to speak COAP (such as a network connectivity problem). -// Any status code doesn't cause an error. -// -// If payload is nil then content format is not used. -func NewPostRequest(ctx context.Context, messagePool *pool.Pool, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) { - req, err := newCommonRequest(ctx, messagePool, codes.POST, path, opts...) - if err != nil { - return nil, err - } - if payload != nil { - req.SetContentFormat(contentFormat) - req.SetBody(payload) - } - return req, nil -} - -// Post issues a POST to the specified path. -// -// Use ctx to set timeout. -// -// An error is returned if by failure to speak COAP (such as a network connectivity problem). -// Any status code doesn't cause an error. -// -// If payload is nil then content format is not used. -func (cc *ClientConn) Post(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) { - req, err := NewPostRequest(ctx, cc.session.messagePool, path, contentFormat, payload, opts...) - if err != nil { - return nil, fmt.Errorf("cannot create post request: %w", err) - } - defer cc.session.messagePool.ReleaseMessage(req) - return cc.Do(req) -} - -// NewPutRequest creates put request. -// -// Use ctx to set timeout. -// -// If payload is nil then content format is not used. -func NewPutRequest(ctx context.Context, messagePool *pool.Pool, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) { - req, err := newCommonRequest(ctx, messagePool, codes.PUT, path, opts...) - if err != nil { - return nil, err - } - if payload != nil { - req.SetContentFormat(contentFormat) - req.SetBody(payload) - } - return req, nil -} - -// Put issues a PUT to the specified path. -// -// Use ctx to set timeout. -// -// An error is returned if by failure to speak COAP (such as a network connectivity problem). -// Any status code doesn't cause an error. -// -// If payload is nil then content format is not used. -func (cc *ClientConn) Put(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) { - req, err := NewPutRequest(ctx, cc.session.messagePool, path, contentFormat, payload, opts...) - if err != nil { - return nil, fmt.Errorf("cannot create put request: %w", err) - } - defer cc.session.messagePool.ReleaseMessage(req) - return cc.Do(req) -} - -// NewDeleteRequest creates delete request. -// -// Use ctx to set timeout. -func NewDeleteRequest(ctx context.Context, messagePool *pool.Pool, path string, opts ...message.Option) (*pool.Message, error) { - return newCommonRequest(ctx, messagePool, codes.DELETE, path, opts...) -} - -// Delete deletes the resource identified by the request path. -// -// Use ctx to set timeout. -func (cc *ClientConn) Delete(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) { - req, err := NewDeleteRequest(ctx, cc.session.messagePool, path, opts...) - if err != nil { - return nil, fmt.Errorf("cannot create delete request: %w", err) - } - defer cc.session.messagePool.ReleaseMessage(req) - return cc.Do(req) -} - -// Context returns the client's context. -// -// If connections was closed context is cancelled. -func (cc *ClientConn) Context() context.Context { - return cc.session.Context() -} - -// Ping issues a PING to the client and waits for PONG response. -// -// Use ctx to set timeout. -func (cc *ClientConn) Ping(ctx context.Context) error { - resp := make(chan bool, 1) - receivedPong := func() { - select { - case resp <- true: - default: - } - } - cancel, err := cc.AsyncPing(receivedPong) - if err != nil { - return err - } - defer cancel() - select { - case <-resp: - return nil - case <-ctx.Done(): - return ctx.Err() - } -} - -// AsyncPing sends ping and receivedPong will be called when pong arrives. It returns cancellation of ping operation. -func (cc *ClientConn) AsyncPing(receivedPong func()) (func(), error) { - token, err := message.GetToken() - if err != nil { - return nil, fmt.Errorf("cannot get token: %w", err) - } - req := cc.session.messagePool.AcquireMessage(cc.Context()) - req.SetToken(token) - req.SetCode(codes.Ping) - defer cc.session.messagePool.ReleaseMessage(req) - - err = cc.session.TokenHandler().Insert(token, func(w *ResponseWriter, r *pool.Message) { - if r.Code() == codes.Pong { - receivedPong() - } - }) - if err != nil { - return nil, fmt.Errorf("cannot add token handler: %w", err) - } - removeTokenHandler := func() { - if _, errT := cc.session.TokenHandler().Pop(token); errT != nil && !errors.Is(errT, ErrKeyNotExists) { - cc.session.errors(fmt.Errorf("cannot remove token handler: %w", errT)) - } - } - err = cc.session.WriteMessage(req) - if err != nil { - removeTokenHandler() - return nil, fmt.Errorf("cannot write request: %w", err) - } - return removeTokenHandler, nil -} - -// Run reads and process requests from a connection, until the connection is not closed. -func (cc *ClientConn) Run() (err error) { - return cc.session.Run(cc) -} - -// AddOnClose calls function on close connection event. -func (cc *ClientConn) AddOnClose(f EventFunc) { - cc.session.AddOnClose(f) -} - -// RemoteAddr gets remote address. -func (cc *ClientConn) RemoteAddr() net.Addr { - return cc.session.connection.RemoteAddr() -} - -func (cc *ClientConn) LocalAddr() net.Addr { - return cc.session.connection.LocalAddr() -} - -// Client get instance which implements mux.Client. -func (cc *ClientConn) Client() *ClientTCP { - return NewClientTCP(cc) -} - -// Sequence acquires sequence number. -func (cc *ClientConn) Sequence() uint64 { - return cc.session.Sequence() -} - -// SetContextValue stores the value associated with key to context of connection. -func (cc *ClientConn) SetContextValue(key interface{}, val interface{}) { - cc.session.SetContextValue(key, val) -} - -// Done signalizes that connection is not more processed. -func (cc *ClientConn) Done() <-chan struct{} { - return cc.session.Done() -} - -// CheckExpirations checks and remove expired items from caches. -func (cc *ClientConn) CheckExpirations(now time.Time) { - cc.session.CheckExpirations(now, cc) -} - -func (cc *ClientConn) AcquireMessage(ctx context.Context) *pool.Message { - return cc.session.messagePool.AcquireMessage(ctx) -} - -func (cc *ClientConn) ReleaseMessage(m *pool.Message) { - cc.session.messagePool.ReleaseMessage(m) -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/tcp/clientobserve.go b/vendor/github.com/plgd-dev/go-coap/v2/tcp/clientobserve.go deleted file mode 100644 index bd4e9c59bc1..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/tcp/clientobserve.go +++ /dev/null @@ -1,216 +0,0 @@ -package tcp - -import ( - "context" - "fmt" - "sync" - "time" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - "github.com/plgd-dev/go-coap/v2/net/observation" - "github.com/plgd-dev/go-coap/v2/tcp/message/pool" - "go.uber.org/atomic" -) - -func NewObservationHandler(obsertionTokenHandler *HandlerContainer, next HandlerFunc) HandlerFunc { - return func(w *ResponseWriter, r *pool.Message) { - v, err := obsertionTokenHandler.Get(r.Token()) - if err != nil { - next(w, r) - return - } - v(w, r) - } -} - -type respObservationMessage struct { - code codes.Code - notSupported bool -} - -//Observation represents subscription to resource on the server -type Observation struct { - token message.Token - path string - cc *ClientConn - observeFunc func(req *pool.Message) - respObservationChan chan respObservationMessage - waitForResponse atomic.Bool - - mutex sync.Mutex - obsSequence uint32 // guarded by mutex - lastEvent time.Time // guarded by mutex -} - -func (o *Observation) Canceled() bool { - _, ok := o.cc.observationRequests.Load(o.token.Hash()) - return !ok -} - -func newObservation(token message.Token, path string, cc *ClientConn, observeFunc func(req *pool.Message), respObservationChan chan respObservationMessage) *Observation { - return &Observation{ - token: token, - path: path, - obsSequence: 0, - cc: cc, - waitForResponse: *atomic.NewBool(true), - respObservationChan: respObservationChan, - observeFunc: observeFunc, - } -} - -func (o *Observation) handler(w *ResponseWriter, r *pool.Message) { - code := r.Code() - notSupported := !r.HasOption(message.Observe) - if o.waitForResponse.CAS(true, false) { - select { - case o.respObservationChan <- respObservationMessage{ - code: code, - notSupported: notSupported, - }: - default: - } - o.respObservationChan = nil - } - if o.wantBeNotified(r) { - o.observeFunc(r) - } -} - -func (o *Observation) cleanUp() bool { - // we can ignore err during cleanUp, if err != nil then some other - // part of code already removed the handler for the token - _, _ = o.cc.observationTokenHandler.Pop(o.token) - _, ok := o.cc.observationRequests.PullOut(o.token.Hash()) - return ok -} - -// Cancel remove observation from server. For recreate observation use Observe. -func (o *Observation) Cancel(ctx context.Context) error { - if !o.cleanUp() { - // observation was already cleanup - return nil - } - req, err := NewGetRequest(ctx, o.cc.session.messagePool, o.path) - if err != nil { - return fmt.Errorf("cannot cancel observation request: %w", err) - } - defer o.cc.session.messagePool.ReleaseMessage(req) - req.SetObserve(1) - req.SetToken(o.token) - resp, err := o.cc.Do(req) - if err != nil { - return err - } - defer o.cc.session.messagePool.ReleaseMessage(resp) - if resp.Code() != codes.Content { - return fmt.Errorf("unexpected return code(%v)", resp.Code()) - } - return nil -} - -func (o *Observation) wantBeNotified(r *pool.Message) bool { - obsSequence, err := r.Observe() - if err != nil { - return true - } - now := time.Now() - - o.mutex.Lock() - defer o.mutex.Unlock() - if observation.ValidSequenceNumber(o.obsSequence, obsSequence, o.lastEvent, now) { - o.obsSequence = obsSequence - o.lastEvent = now - return true - } - - return false -} - -// NewObserveRequest creates observe request. -// -// Use ctx to set timeout. -func NewObserveRequest(ctx context.Context, messagePool *pool.Pool, path string, opts ...message.Option) (*pool.Message, error) { - req, err := NewGetRequest(ctx, messagePool, path, opts...) - if err != nil { - return nil, fmt.Errorf("cannot create observe request: %w", err) - } - req.SetObserve(0) - return req, nil -} - -// ObserveRequest subscribes for every change of resource on path. -func (cc *ClientConn) ObserveRequest(req *pool.Message, observeFunc func(req *pool.Message)) (*Observation, error) { - path, err := req.Path() - if err != nil { - return nil, fmt.Errorf("cannot get path: %w", err) - } - observe, err := req.Observe() - if err != nil { - return nil, fmt.Errorf("cannot get observe option: %w", err) - } - if observe != 0 { - return nil, fmt.Errorf("invalid value of observe(%v): expected 0", observe) - } - token := req.Token() - if len(token) == 0 { - return nil, fmt.Errorf("empty token") - } - respObservationChan := make(chan respObservationMessage, 1) - o := newObservation(token, path, cc, observeFunc, respObservationChan) - - options, err := req.Options().Clone() - if err != nil { - return nil, fmt.Errorf("cannot clone options: %w", err) - } - - obs := message.Message{ - Context: req.Context(), - Token: req.Token(), - Code: req.Code(), - Options: options, - } - cc.observationRequests.Store(token.Hash(), obs) - err = o.cc.observationTokenHandler.Insert(token.Hash(), o.handler) - defer func(err *error) { - if *err != nil { - o.cleanUp() - } - }(&err) - if err != nil { - return nil, err - } - - err = cc.WriteMessage(req) - if err != nil { - return nil, err - } - select { - case <-req.Context().Done(): - err = req.Context().Err() - return nil, err - case <-cc.Context().Done(): - err = fmt.Errorf("connection was closed: %w", cc.Context().Err()) - return nil, err - case respObservationMessage := <-respObservationChan: - if respObservationMessage.code != codes.Content { - err = fmt.Errorf("unexpected return code(%v)", respObservationMessage.code) - return nil, err - } - if respObservationMessage.notSupported { - o.cleanUp() - } - return o, nil - } -} - -// Observe subscribes for every change of resource on path. -func (cc *ClientConn) Observe(ctx context.Context, path string, observeFunc func(req *pool.Message), opts ...message.Option) (*Observation, error) { - req, err := NewObserveRequest(ctx, cc.session.messagePool, path, opts...) - if err != nil { - return nil, err - } - defer cc.session.messagePool.ReleaseMessage(req) - return cc.ObserveRequest(req, observeFunc) -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/tcp/container.go b/vendor/github.com/plgd-dev/go-coap/v2/tcp/container.go deleted file mode 100644 index 9a8679b3e32..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/tcp/container.go +++ /dev/null @@ -1,70 +0,0 @@ -package tcp - -import ( - "errors" - "sync" - - "github.com/plgd-dev/go-coap/v2/message" -) - -var ( - ErrKeyAlreadyExists = errors.New("key already exists") - - ErrKeyNotExists = errors.New("key does not exist") -) - -// HandlerContainer for regirstration handlers by key -type HandlerContainer struct { - datas map[interface{}]HandlerFunc - mutex sync.Mutex -} - -// NewHandlerContainer factory -func NewHandlerContainer() *HandlerContainer { - return &HandlerContainer{ - datas: make(map[interface{}]HandlerFunc), - } -} - -// Insert handler for key. -func (s *HandlerContainer) Insert(key interface{}, handler HandlerFunc) error { - if v, ok := key.(message.Token); ok { - key = v.Hash() - } - s.mutex.Lock() - defer s.mutex.Unlock() - if _, keyExists := s.datas[key]; keyExists { - return ErrKeyAlreadyExists - } - s.datas[key] = handler - return nil -} - -// Get returns handler for key -func (s *HandlerContainer) Get(key interface{}) (HandlerFunc, error) { - if v, ok := key.(message.Token); ok { - key = v.Hash() - } - s.mutex.Lock() - defer s.mutex.Unlock() - v, keyExists := s.datas[key] - if !keyExists { - return nil, ErrKeyNotExists - } - return v, nil -} - -// Pop pops handler for key -func (s *HandlerContainer) Pop(key interface{}) (HandlerFunc, error) { - if v, ok := key.(message.Token); ok { - key = v.Hash() - } - s.mutex.Lock() - defer s.mutex.Unlock() - v, keyExists := s.datas[key] - if !keyExists { - return nil, ErrKeyNotExists - } - delete(s.datas, key) - return v, nil -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/tcp/message/message.go b/vendor/github.com/plgd-dev/go-coap/v2/tcp/message/message.go deleted file mode 100644 index 2fa5cd3ce8b..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/tcp/message/message.go +++ /dev/null @@ -1,346 +0,0 @@ -package message - -import ( - "encoding/binary" - "errors" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" -) - -const ( - messageLen13Base = 13 - messageLen14Base = 269 - messageLen15Base = 65805 - messageMaxLen = 0x7fff0000 // Large number that works in 32-bit builds -) - -// Signal CSM Option IDs -/* - +-----+---+---+-------------------+--------+--------+---------+ - | No. | C | R | Name | Format | Length | Default | - +-----+---+---+-------------------+--------+--------+---------+ - | 2 | | | MaxMessageSize | uint | 0-4 | 1152 | - | 4 | | | BlockWiseTransfer | empty | 0 | (none) | - +-----+---+---+-------------------+--------+--------+---------+ - C=Critical, R=Repeatable -*/ - -const ( - MaxMessageSize message.OptionID = 2 - BlockWiseTransfer message.OptionID = 4 -) - -// Signal Ping/Pong Option IDs -/* - +-----+---+---+-------------------+--------+--------+---------+ - | No. | C | R | Name | Format | Length | Default | - +-----+---+---+-------------------+--------+--------+---------+ - | 2 | | | Custody | empty | 0 | (none) | - +-----+---+---+-------------------+--------+--------+---------+ - C=Critical, R=Repeatable -*/ - -const ( - Custody message.OptionID = 2 -) - -// Signal Release Option IDs -/* - +-----+---+---+---------------------+--------+--------+---------+ - | No. | C | R | Name | Format | Length | Default | - +-----+---+---+---------------------+--------+--------+---------+ - | 2 | | x | Alternative-Address | string | 1-255 | (none) | - | 4 | | | Hold-Off | uint3 | 0-3 | (none) | - +-----+---+---+---------------------+--------+--------+---------+ - C=Critical, R=Repeatable -*/ - -const ( - AlternativeAddress message.OptionID = 2 - HoldOff message.OptionID = 4 -) - -// Signal Abort Option IDs -/* - +-----+---+---+---------------------+--------+--------+---------+ - | No. | C | R | Name | Format | Length | Default | - +-----+---+---+---------------------+--------+--------+---------+ - | 2 | | | Bad-CSM-Option | uint | 0-2 | (none) | - +-----+---+---+---------------------+--------+--------+---------+ - C=Critical, R=Repeatable -*/ -const ( - BadCSMOption message.OptionID = 2 -) - -var signalCSMOptionDefs = map[message.OptionID]message.OptionDef{ - MaxMessageSize: {ValueFormat: message.ValueUint, MinLen: 0, MaxLen: 4}, - BlockWiseTransfer: {ValueFormat: message.ValueEmpty, MinLen: 0, MaxLen: 0}, -} - -var signalPingPongOptionDefs = map[message.OptionID]message.OptionDef{ - Custody: {ValueFormat: message.ValueEmpty, MinLen: 0, MaxLen: 0}, -} - -var signalReleaseOptionDefs = map[message.OptionID]message.OptionDef{ - AlternativeAddress: {ValueFormat: message.ValueString, MinLen: 1, MaxLen: 255}, - HoldOff: {ValueFormat: message.ValueUint, MinLen: 0, MaxLen: 3}, -} - -var signalAbortOptionDefs = map[message.OptionID]message.OptionDef{ - BadCSMOption: {ValueFormat: message.ValueUint, MinLen: 0, MaxLen: 2}, -} - -// TcpMessage is a CoAP MessageBase that can encode itself for Message -// transport. -type Message struct { - Token []byte - Payload []byte - - Options message.Options //Options must be sorted by ID - Code codes.Code -} - -func (m Message) Size() (int, error) { - size, err := m.MarshalTo(nil) - if errors.Is(err, message.ErrTooSmall) { - err = nil - } - return size, err -} - -func (m Message) Marshal() ([]byte, error) { - b := make([]byte, 1024) - l, err := m.MarshalTo(b) - if errors.Is(err, message.ErrTooSmall) { - b = append(b[:0], make([]byte, l)...) - l, err = m.MarshalTo(b) - } - return b[:l], err -} - -func (m Message) MarshalTo(buf []byte) (int, error) { - /* - A CoAP Message message lomessage.OKs like: - - 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 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Len | TKL | Extended Length ... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Code | TKL bytes ... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Options (if any) ... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |1 1 1 1 1 1 1 1| Payload (if any) ... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - The size of the Extended Length field is inferred from the value of the - Len field as follows: - - | Len value | Extended Length size | Total length | - +------------+-----------------------+---------------------------+ - | 0-12 | 0 | Len | - | 13 | 1 | Extended Length + 13 | - | 14 | 2 | Extended Length + 269 | - | 15 | 4 | Extended Length + 65805 | - */ - - if len(m.Token) > message.MaxTokenSize { - return -1, message.ErrInvalidTokenLen - } - - payloadLen := len(m.Payload) - if payloadLen > 0 { - //for separator 0xff - payloadLen++ - } - optionsLen, err := m.Options.Marshal(nil) - if !errors.Is(err, message.ErrTooSmall) { - return -1, err - } - bufLen := payloadLen + optionsLen - var lenNib uint8 - var extLenBytes []byte - - if bufLen < messageLen13Base { - lenNib = uint8(bufLen) - } else if bufLen < messageLen14Base { - lenNib = 13 - extLen := bufLen - messageLen13Base - extLenBytes = []byte{uint8(extLen)} - } else if bufLen < messageLen15Base { - lenNib = 14 - extLen := bufLen - messageLen14Base - extLenBytes = make([]byte, 2) - binary.BigEndian.PutUint16(extLenBytes, uint16(extLen)) - } else if bufLen < messageMaxLen { - lenNib = 15 - extLen := bufLen - messageLen15Base - extLenBytes = make([]byte, 4) - binary.BigEndian.PutUint32(extLenBytes, uint32(extLen)) - } - - var hdr [1 + 4 + message.MaxTokenSize + 1]byte - hdrLen := 1 + len(extLenBytes) + len(m.Token) + 1 - hdrOff := 0 - - copyToHdr := func(offset int, data []byte) int { - if len(data) > 0 { - copy(hdr[hdrOff:hdrOff+len(data)], data) - offset += len(data) - } - return offset - } - - // Length and TKL nibbles. - hdr[hdrOff] = uint8(0xf&len(m.Token)) | (lenNib << 4) - hdrOff++ - - // Extended length, if present. - hdrOff = copyToHdr(hdrOff, extLenBytes) - - // Code. - hdr[hdrOff] = byte(m.Code) - hdrOff++ - - // Token. - copyToHdr(hdrOff, m.Token) - - bufLen = bufLen + hdrLen - if len(buf) < bufLen { - return bufLen, message.ErrTooSmall - } - - copy(buf, hdr[:hdrLen]) - optionsLen, err = m.Options.Marshal(buf[hdrLen:]) - switch { - case err == nil: - case errors.Is(err, message.ErrTooSmall): - return bufLen, err - default: - return -1, err - } - if len(m.Payload) > 0 { - copy(buf[hdrLen+optionsLen:], []byte{0xff}) - copy(buf[hdrLen+optionsLen+1:], m.Payload) - } - - return bufLen, nil -} - -type MessageHeader struct { - Token []byte - HeaderLen uint32 - TotalLen uint32 - Code codes.Code -} - -// Unmarshal infers information about a Message CoAP message from the first -// fragment. -func (i *MessageHeader) Unmarshal(data []byte) error { - hdrOff := uint32(0) - if len(data) == 0 { - return message.ErrShortRead - } - - firstByte := data[0] - data = data[1:] - hdrOff++ - - lenNib := (firstByte & 0xf0) >> 4 - tkl := firstByte & 0x0f - - var opLen int - switch { - case lenNib < messageLen13Base: - opLen = int(lenNib) - case lenNib == 13: - if len(data) < 1 { - return message.ErrShortRead - } - extLen := data[0] - data = data[1:] - hdrOff++ - opLen = messageLen13Base + int(extLen) - case lenNib == 14: - if len(data) < 2 { - return message.ErrShortRead - } - extLen := binary.BigEndian.Uint16(data) - data = data[2:] - hdrOff += 2 - opLen = messageLen14Base + int(extLen) - case lenNib == 15: - if len(data) < 4 { - return message.ErrShortRead - } - extLen := binary.BigEndian.Uint32(data) - data = data[4:] - hdrOff += 4 - opLen = messageLen15Base + int(extLen) - } - - i.TotalLen = hdrOff + 1 + uint32(tkl) + uint32(opLen) - if len(data) < 1 { - return message.ErrShortRead - } - i.Code = codes.Code(data[0]) - data = data[1:] - hdrOff++ - if len(data) < int(tkl) { - return message.ErrShortRead - } - if tkl > 0 { - i.Token = data[:tkl] - } - hdrOff += uint32(tkl) - - i.HeaderLen = hdrOff - - return nil -} - -func (m *Message) UnmarshalWithHeader(header MessageHeader, data []byte) (int, error) { - optionDefs := message.CoapOptionDefs - processed := header.HeaderLen - switch header.Code { - case codes.CSM: - optionDefs = signalCSMOptionDefs - case codes.Ping, codes.Pong: - optionDefs = signalPingPongOptionDefs - case codes.Release: - optionDefs = signalReleaseOptionDefs - case codes.Abort: - optionDefs = signalAbortOptionDefs - } - - proc, err := m.Options.Unmarshal(data, optionDefs) - if err != nil { - return -1, err - } - data = data[proc:] - processed += uint32(proc) - - if len(data) > 0 { - m.Payload = data - } - processed = processed + uint32(len(data)) - m.Code = header.Code - m.Token = header.Token - - return int(processed), nil -} - -func (m *Message) Unmarshal(data []byte) (int, error) { - header := MessageHeader{Token: m.Token} - err := header.Unmarshal(data) - if err != nil { - return -1, err - } - if uint32(len(data)) < header.TotalLen { - return -1, message.ErrShortRead - } - return m.UnmarshalWithHeader(header, data[header.HeaderLen:]) -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/tcp/message/pool/message.go b/vendor/github.com/plgd-dev/go-coap/v2/tcp/message/pool/message.go deleted file mode 100644 index 8b0ff50849c..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/tcp/message/pool/message.go +++ /dev/null @@ -1,186 +0,0 @@ -package pool - -import ( - "bytes" - "context" - "fmt" - "io" - "sync" - "sync/atomic" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/pool" - tcp "github.com/plgd-dev/go-coap/v2/tcp/message" -) - -type Message struct { - - //local vars - rawData []byte - rawMarshalData []byte - - ctx context.Context - *pool.Message - - rawMaxSize uint16 - - isModified bool -} - -// Reset clear message for next reuse -func (r *Message) Reset() { - r.Message.Reset() - if cap(r.rawData) > int(r.rawMaxSize) { - r.rawData = make([]byte, 256) - } - if cap(r.rawMarshalData) > int(r.rawMaxSize) { - r.rawMarshalData = make([]byte, 256) - } - r.isModified = false -} - -func (r *Message) Context() context.Context { - return r.ctx -} - -func (r *Message) IsModified() bool { - return r.isModified || r.Message.IsModified() -} - -func (r *Message) Unmarshal(data []byte) (int, error) { - if len(r.rawData) < len(data) { - r.rawData = append(r.rawData, make([]byte, len(data)-len(r.rawData))...) - } - copy(r.rawData, data) - r.rawData = r.rawData[:len(data)] - m := &tcp.Message{ - Options: make(message.Options, 0, 16), - } - - n, err := m.Unmarshal(r.rawData) - if err != nil { - return n, err - } - r.Message.SetCode(m.Code) - r.Message.SetToken(m.Token) - r.Message.ResetOptionsTo(m.Options) - if len(m.Payload) > 0 { - r.Message.SetBody(bytes.NewReader(m.Payload)) - } - return n, err -} - -func (r *Message) Marshal() ([]byte, error) { - m := tcp.Message{ - Code: r.Code(), - Token: r.Message.Token(), - Options: r.Message.Options(), - } - payload, err := r.ReadBody() - if err != nil { - return nil, err - } - m.Payload = payload - size, err := m.Size() - if err != nil { - return nil, err - } - if len(r.rawMarshalData) < size { - r.rawMarshalData = append(r.rawMarshalData, make([]byte, size-len(r.rawMarshalData))...) - } - n, err := m.MarshalTo(r.rawMarshalData) - if err != nil { - return nil, err - } - r.rawMarshalData = r.rawMarshalData[:n] - return r.rawMarshalData, nil -} - -type Pool struct { - // This field needs to be the first in the struct to ensure proper word alignment on 32-bit platforms. - // See: https://golang.org/pkg/sync/atomic/#pkg-note-BUG - currentMessagesInPool int64 - messagePool sync.Pool - maxNumMessages uint32 - maxMessageBufferSize uint16 -} - -func New(maxNumMessages uint32, maxMessageBufferSize uint16) *Pool { - return &Pool{ - maxNumMessages: maxNumMessages, - maxMessageBufferSize: maxMessageBufferSize, - } -} - -// AcquireMessage returns an empty Message instance from Message pool. -// -// The returned Message instance may be passed to ReleaseMessage when it is -// no longer needed. This allows Message recycling, reduces GC pressure -// and usually improves performance. -func (p *Pool) AcquireMessage(ctx context.Context) *Message { - v := p.messagePool.Get() - if v == nil { - return &Message{ - Message: pool.NewMessage(), - rawData: make([]byte, 256), - rawMarshalData: make([]byte, 256), - ctx: ctx, - rawMaxSize: p.maxMessageBufferSize, - } - } - atomic.AddInt64(&p.currentMessagesInPool, -1) - r := v.(*Message) - r.ctx = ctx - return r -} - -// ReleaseMessage returns req acquired via AcquireMessage to Message pool. -// -// It is forbidden accessing req and/or its' members after returning -// it to Message pool. -func (p *Pool) ReleaseMessage(req *Message) { - v := atomic.LoadInt64(&p.currentMessagesInPool) - if v >= int64(p.maxNumMessages) { - return - } - atomic.AddInt64(&p.currentMessagesInPool, 1) - req.Reset() - req.ctx = nil - p.messagePool.Put(req) -} - -// ConvertFrom converts common message to pool message. -func (p *Pool) ConvertFrom(m *message.Message) (*Message, error) { - if m.Context == nil { - return nil, fmt.Errorf("invalid context") - } - r := p.AcquireMessage(m.Context) - r.SetCode(m.Code) - r.ResetOptionsTo(m.Options) - r.SetBody(m.Body) - r.SetToken(m.Token) - return r, nil -} - -// ConvertTo converts pool message to common message. -func ConvertTo(m *Message) (*message.Message, error) { - opts, err := m.Options().Clone() - if err != nil { - return nil, err - } - var body io.ReadSeeker - if m.Body() != nil { - payload, err := m.ReadBody() - if err != nil { - return nil, err - } - body = bytes.NewReader(payload) - } - return &message.Message{ - Context: m.Context(), - Code: m.Code(), - Token: m.Token(), - Body: body, - Options: opts, - }, nil -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/tcp/nocopy.go b/vendor/github.com/plgd-dev/go-coap/v2/tcp/nocopy.go deleted file mode 100644 index c95f8ac233d..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/tcp/nocopy.go +++ /dev/null @@ -1,12 +0,0 @@ -package tcp - -// Embed this type into a struct, which mustn't be copied, -// so `go vet` gives a warning if this struct is copied. -// -// See https://github.com/golang/go/issues/8005#issuecomment-190753527 for details. -// and also: https://stackoverflow.com/questions/52494458/nocopy-minimal-example - -type noCopy struct{} - -func (*noCopy) Lock() {} -func (*noCopy) Unlock() {} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/tcp/optionmux.go b/vendor/github.com/plgd-dev/go-coap/v2/tcp/optionmux.go deleted file mode 100644 index 460850437ca..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/tcp/optionmux.go +++ /dev/null @@ -1,41 +0,0 @@ -package tcp - -import ( - "io" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - "github.com/plgd-dev/go-coap/v2/mux" - "github.com/plgd-dev/go-coap/v2/tcp/message/pool" -) - -// WithMux set's multiplexer for handle requests. -func WithMux(m mux.Handler) HandlerFuncOpt { - h := func(w *ResponseWriter, r *pool.Message) { - muxw := &muxResponseWriter{ - w: w, - } - muxr, err := pool.ConvertTo(r) - if err != nil { - return - } - m.ServeCOAP(muxw, &mux.Message{ - Message: muxr, - SequenceNumber: r.Sequence(), - RouteParams: new(mux.RouteParams), - }) - } - return WithHandlerFunc(h) -} - -type muxResponseWriter struct { - w *ResponseWriter -} - -func (w *muxResponseWriter) SetResponse(code codes.Code, contentFormat message.MediaType, d io.ReadSeeker, opts ...message.Option) error { - return w.w.SetResponse(code, contentFormat, d, opts...) -} - -func (w *muxResponseWriter) Client() mux.Client { - return w.w.ClientConn().Client() -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/tcp/options.go b/vendor/github.com/plgd-dev/go-coap/v2/tcp/options.go deleted file mode 100644 index 1d56551c059..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/tcp/options.go +++ /dev/null @@ -1,365 +0,0 @@ -package tcp - -import ( - "context" - "crypto/tls" - "net" - "time" - - "github.com/plgd-dev/go-coap/v2/net/blockwise" - "github.com/plgd-dev/go-coap/v2/net/monitor/inactivity" - "github.com/plgd-dev/go-coap/v2/pkg/runner/periodic" - "github.com/plgd-dev/go-coap/v2/tcp/message/pool" -) - -// HandlerFuncOpt handler function option. -type HandlerFuncOpt struct { - h HandlerFunc -} - -func (o HandlerFuncOpt) apply(opts *serverOptions) { - opts.handler = o.h -} - -func (o HandlerFuncOpt) applyDial(opts *dialOptions) { - opts.handler = o.h -} - -// WithHandlerFunc set handle for handling request's. -func WithHandlerFunc(h HandlerFunc) HandlerFuncOpt { - return HandlerFuncOpt{h: h} -} - -// ContextOpt handler function option. -type ContextOpt struct { - ctx context.Context -} - -func (o ContextOpt) apply(opts *serverOptions) { - opts.ctx = o.ctx -} - -func (o ContextOpt) applyDial(opts *dialOptions) { - opts.ctx = o.ctx -} - -// WithContext set's parent context of server. -func WithContext(ctx context.Context) ContextOpt { - return ContextOpt{ctx: ctx} -} - -// MaxMessageSizeOpt handler function option. -type MaxMessageSizeOpt struct { - maxMessageSize uint32 -} - -func (o MaxMessageSizeOpt) apply(opts *serverOptions) { - opts.maxMessageSize = o.maxMessageSize -} - -func (o MaxMessageSizeOpt) applyDial(opts *dialOptions) { - opts.maxMessageSize = o.maxMessageSize -} - -// WithMaxMessageSize limit size of processed message. -func WithMaxMessageSize(maxMessageSize uint32) MaxMessageSizeOpt { - return MaxMessageSizeOpt{maxMessageSize: maxMessageSize} -} - -// ErrorsOpt errors option. -type ErrorsOpt struct { - errors ErrorFunc -} - -func (o ErrorsOpt) apply(opts *serverOptions) { - opts.errors = o.errors -} - -func (o ErrorsOpt) applyDial(opts *dialOptions) { - opts.errors = o.errors -} - -// WithErrors set function for logging error. -func WithErrors(errors ErrorFunc) ErrorsOpt { - return ErrorsOpt{errors: errors} -} - -// GoPoolOpt gopool option. -type GoPoolOpt struct { - goPool GoPoolFunc -} - -func (o GoPoolOpt) apply(opts *serverOptions) { - opts.goPool = o.goPool -} - -func (o GoPoolOpt) applyDial(opts *dialOptions) { - opts.goPool = o.goPool -} - -// WithGoPool sets function for managing spawning go routines -// for handling incoming request's. -// Eg: https://github.com/panjf2000/ants. -func WithGoPool(goPool GoPoolFunc) GoPoolOpt { - return GoPoolOpt{goPool: goPool} -} - -// KeepAliveOpt keepalive option. -type KeepAliveOpt struct { - timeout time.Duration - onInactive inactivity.OnInactiveFunc - maxRetries uint32 -} - -func (o KeepAliveOpt) apply(opts *serverOptions) { - opts.createInactivityMonitor = func() inactivity.Monitor { - keepalive := inactivity.NewKeepAlive(o.maxRetries, o.onInactive, func(cc inactivity.ClientConn, receivePong func()) (func(), error) { - return cc.(*ClientConn).AsyncPing(receivePong) - }) - return inactivity.NewInactivityMonitor(o.timeout/time.Duration(o.maxRetries+1), keepalive.OnInactive) - } -} - -func (o KeepAliveOpt) applyDial(opts *dialOptions) { - opts.createInactivityMonitor = func() inactivity.Monitor { - keepalive := inactivity.NewKeepAlive(o.maxRetries, o.onInactive, func(cc inactivity.ClientConn, receivePong func()) (func(), error) { - return cc.(*ClientConn).AsyncPing(receivePong) - }) - return inactivity.NewInactivityMonitor(o.timeout/time.Duration(o.maxRetries+1), keepalive.OnInactive) - } -} - -// WithKeepAlive monitoring's client connection's. -func WithKeepAlive(maxRetries uint32, timeout time.Duration, onInactive inactivity.OnInactiveFunc) KeepAliveOpt { - return KeepAliveOpt{ - maxRetries: maxRetries, - timeout: timeout, - onInactive: onInactive, - } -} - -// InactivityMonitorOpt notifies when a connection was inactive for a given duration. -type InactivityMonitorOpt struct { - duration time.Duration - onInactive inactivity.OnInactiveFunc -} - -func (o InactivityMonitorOpt) apply(opts *serverOptions) { - opts.createInactivityMonitor = func() inactivity.Monitor { - return inactivity.NewInactivityMonitor(o.duration, o.onInactive) - } -} - -func (o InactivityMonitorOpt) applyDial(opts *dialOptions) { - opts.createInactivityMonitor = func() inactivity.Monitor { - return inactivity.NewInactivityMonitor(o.duration, o.onInactive) - } -} - -// WithInactivityMonitor set deadline's for read operations over client connection. -func WithInactivityMonitor(duration time.Duration, onInactive inactivity.OnInactiveFunc) InactivityMonitorOpt { - return InactivityMonitorOpt{ - duration: duration, - onInactive: onInactive, - } -} - -// NetOpt network option. -type NetOpt struct { - net string -} - -func (o NetOpt) applyDial(opts *dialOptions) { - opts.net = o.net -} - -// WithNetwork define's tcp version (udp4, udp6, tcp) for client. -func WithNetwork(net string) NetOpt { - return NetOpt{net: net} -} - -// PeriodicRunnerOpt function which is executed in every ticks -type PeriodicRunnerOpt struct { - periodicRunner periodic.Func -} - -func (o PeriodicRunnerOpt) applyDial(opts *dialOptions) { - opts.periodicRunner = o.periodicRunner -} - -func (o PeriodicRunnerOpt) apply(opts *serverOptions) { - opts.periodicRunner = o.periodicRunner -} - -// WithPeriodicRunner set function which is executed in every ticks. -func WithPeriodicRunner(periodicRunner periodic.Func) PeriodicRunnerOpt { - return PeriodicRunnerOpt{periodicRunner: periodicRunner} -} - -// BlockwiseOpt network option. -type BlockwiseOpt struct { - transferTimeout time.Duration - enable bool - szx blockwise.SZX -} - -func (o BlockwiseOpt) apply(opts *serverOptions) { - opts.blockwiseEnable = o.enable - opts.blockwiseSZX = o.szx - opts.blockwiseTransferTimeout = o.transferTimeout -} - -func (o BlockwiseOpt) applyDial(opts *dialOptions) { - opts.blockwiseEnable = o.enable - opts.blockwiseSZX = o.szx - opts.blockwiseTransferTimeout = o.transferTimeout -} - -// WithBlockwise configure's blockwise transfer. -func WithBlockwise(enable bool, szx blockwise.SZX, transferTimeout time.Duration) BlockwiseOpt { - return BlockwiseOpt{ - enable: enable, - szx: szx, - transferTimeout: transferTimeout, - } -} - -// OnNewClientConnOpt network option. -type OnNewClientConnOpt struct { - onNewClientConn OnNewClientConnFunc -} - -func (o OnNewClientConnOpt) apply(opts *serverOptions) { - opts.onNewClientConn = o.onNewClientConn -} - -// WithOnNewClientConn server's notify about new client connection. -// -// Note: Calling `tlscon.Close()` is forbidden, and `tlscon` should be treated as a -// "read-only" parameter, mainly used to get the peer certificate from the underlining connection -func WithOnNewClientConn(onNewClientConn OnNewClientConnFunc) OnNewClientConnOpt { - return OnNewClientConnOpt{ - onNewClientConn: onNewClientConn, - } -} - -// DisablePeerTCPSignalMessageCSMsOpt coap-tcp csm option. -type DisablePeerTCPSignalMessageCSMsOpt struct { -} - -func (o DisablePeerTCPSignalMessageCSMsOpt) apply(opts *serverOptions) { - opts.disablePeerTCPSignalMessageCSMs = true -} - -func (o DisablePeerTCPSignalMessageCSMsOpt) applyDial(opts *dialOptions) { - opts.disablePeerTCPSignalMessageCSMs = true -} - -// WithDisablePeerTCPSignalMessageCSMs ignor peer's CSM message. -func WithDisablePeerTCPSignalMessageCSMs() DisablePeerTCPSignalMessageCSMsOpt { - return DisablePeerTCPSignalMessageCSMsOpt{} -} - -// DisableTCPSignalMessageCSMOpt coap-tcp csm option. -type DisableTCPSignalMessageCSMOpt struct { -} - -func (o DisableTCPSignalMessageCSMOpt) apply(opts *serverOptions) { - opts.disableTCPSignalMessageCSM = true -} - -func (o DisableTCPSignalMessageCSMOpt) applyDial(opts *dialOptions) { - opts.disableTCPSignalMessageCSM = true -} - -// WithDisableTCPSignalMessageCSM don't send CSM when client conn is created. -func WithDisableTCPSignalMessageCSM() DisableTCPSignalMessageCSMOpt { - return DisableTCPSignalMessageCSMOpt{} -} - -// TLSOpt tls configuration option. -type TLSOpt struct { - tlsCfg *tls.Config -} - -func (o TLSOpt) applyDial(opts *dialOptions) { - opts.tlsCfg = o.tlsCfg -} - -// WithTLS creates tls connection. -func WithTLS(cfg *tls.Config) TLSOpt { - return TLSOpt{ - tlsCfg: cfg, - } -} - -// CloseSocketOpt close socket option. -type CloseSocketOpt struct { -} - -func (o CloseSocketOpt) applyDial(opts *dialOptions) { - opts.closeSocket = true -} - -// WithCloseSocket closes socket at the close connection. -func WithCloseSocket() CloseSocketOpt { - return CloseSocketOpt{} -} - -// DialerOpt dialer option. -type DialerOpt struct { - dialer *net.Dialer -} - -func (o DialerOpt) applyDial(opts *dialOptions) { - if o.dialer != nil { - opts.dialer = o.dialer - } -} - -// WithDialer set dialer for dial. -func WithDialer(dialer *net.Dialer) DialerOpt { - return DialerOpt{ - dialer: dialer, - } -} - -// ConnectionCacheOpt network option. -type ConnectionCacheSizeOpt struct { - connectionCacheSize uint16 -} - -func (o ConnectionCacheSizeOpt) apply(opts *serverOptions) { - opts.connectionCacheSize = o.connectionCacheSize -} - -func (o ConnectionCacheSizeOpt) applyDial(opts *dialOptions) { - opts.connectionCacheSize = o.connectionCacheSize -} - -// WithConnectionCacheSize configure's maximum size of cache of read buffer. -func WithConnectionCacheSize(connectionCacheSize uint16) ConnectionCacheSizeOpt { - return ConnectionCacheSizeOpt{ - connectionCacheSize: connectionCacheSize, - } -} - -// ConnectionCacheOpt network option. -type MessagePoolOpt struct { - messagePool *pool.Pool -} - -func (o MessagePoolOpt) apply(opts *serverOptions) { - opts.messagePool = o.messagePool -} - -func (o MessagePoolOpt) applyDial(opts *dialOptions) { - opts.messagePool = o.messagePool -} - -// WithMessagePool configure's message pool for acquire/releasing coap messages -func WithMessagePool(messagePool *pool.Pool) MessagePoolOpt { - return MessagePoolOpt{ - messagePool: messagePool, - } -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/tcp/responsewriter.go b/vendor/github.com/plgd-dev/go-coap/v2/tcp/responsewriter.go deleted file mode 100644 index 98810263be2..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/tcp/responsewriter.go +++ /dev/null @@ -1,59 +0,0 @@ -package tcp - -import ( - "io" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - "github.com/plgd-dev/go-coap/v2/message/noresponse" - "github.com/plgd-dev/go-coap/v2/tcp/message/pool" -) - -// A ResponseWriter interface is used by an CAOP handler to construct an COAP response. -type ResponseWriter struct { - noResponseValue *uint32 - response *pool.Message - cc *ClientConn -} - -func NewResponseWriter(response *pool.Message, cc *ClientConn, requestOptions message.Options) *ResponseWriter { - var noResponseValue *uint32 - v, err := requestOptions.GetUint32(message.NoResponse) - if err == nil { - noResponseValue = &v - } - - return &ResponseWriter{ - response: response, - cc: cc, - noResponseValue: noResponseValue, - } -} - -func (r *ResponseWriter) SetResponse(code codes.Code, contentFormat message.MediaType, d io.ReadSeeker, opts ...message.Option) error { - if r.noResponseValue != nil { - err := noresponse.IsNoResponseCode(code, *r.noResponseValue) - if err != nil { - return err - } - } - - r.response.SetCode(code) - r.response.ResetOptionsTo(opts) - if d != nil { - r.response.SetContentFormat(contentFormat) - r.response.SetBody(d) - if !r.response.HasOption(message.ETag) { - etag, err := message.GetETag(d) - if err != nil { - return err - } - r.response.SetOptionBytes(message.ETag, etag) - } - } - return nil -} - -func (r *ResponseWriter) ClientConn() *ClientConn { - return r.cc -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/tcp/server.go b/vendor/github.com/plgd-dev/go-coap/v2/tcp/server.go deleted file mode 100644 index 6e459ebb978..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/tcp/server.go +++ /dev/null @@ -1,334 +0,0 @@ -package tcp - -import ( - "context" - "crypto/tls" - "errors" - "fmt" - "io" - "net" - "strings" - "sync" - "time" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - coapNet "github.com/plgd-dev/go-coap/v2/net" - "github.com/plgd-dev/go-coap/v2/net/blockwise" - "github.com/plgd-dev/go-coap/v2/net/monitor/inactivity" - "github.com/plgd-dev/go-coap/v2/pkg/connections" - "github.com/plgd-dev/go-coap/v2/pkg/runner/periodic" - "github.com/plgd-dev/go-coap/v2/tcp/message/pool" - kitSync "github.com/plgd-dev/kit/v2/sync" -) - -// A ServerOption sets options such as credentials, codec and keepalive parameters, etc. -type ServerOption interface { - apply(*serverOptions) -} - -// The HandlerFunc type is an adapter to allow the use of -// ordinary functions as COAP handlers. -type HandlerFunc = func(*ResponseWriter, *pool.Message) - -type ErrorFunc = func(error) - -type GoPoolFunc = func(func()) error - -type BlockwiseFactoryFunc = func(getSentRequest func(token message.Token) (blockwise.Message, bool)) *blockwise.BlockWise - -// OnNewClientConnFunc is the callback for new connections. -// -// Note: Calling `tlscon.Close()` is forbidden, and `tlscon` should be treated as a -// "read-only" parameter, mainly used to get the peer certificate from the underlining connection -type OnNewClientConnFunc = func(cc *ClientConn, tlscon *tls.Conn) - -var defaultServerOptions = func() serverOptions { - opts := serverOptions{ - ctx: context.Background(), - maxMessageSize: 64 * 1024, - errors: func(err error) { - fmt.Println(err) - }, - goPool: func(f func()) error { - go func() { - f() - }() - return nil - }, - blockwiseEnable: true, - blockwiseSZX: blockwise.SZX1024, - blockwiseTransferTimeout: time.Second * 3, - onNewClientConn: func(cc *ClientConn, tlscon *tls.Conn) {}, - createInactivityMonitor: func() inactivity.Monitor { - return inactivity.NewNilMonitor() - }, - periodicRunner: func(f func(now time.Time) bool) { - go func() { - for f(time.Now()) { - time.Sleep(4 * time.Second) - } - }() - }, - connectionCacheSize: 2 * 1024, - messagePool: pool.New(1024, 2048), - } - opts.handler = func(w *ResponseWriter, r *pool.Message) { - if err := w.SetResponse(codes.NotFound, message.TextPlain, nil); err != nil { - opts.errors(fmt.Errorf("server handler: cannot set response: %w", err)) - } - } - return opts -}() - -type serverOptions struct { - ctx context.Context - messagePool *pool.Pool - handler HandlerFunc - errors ErrorFunc - goPool GoPoolFunc - createInactivityMonitor func() inactivity.Monitor - periodicRunner periodic.Func - onNewClientConn OnNewClientConnFunc - blockwiseTransferTimeout time.Duration - maxMessageSize uint32 - connectionCacheSize uint16 - disablePeerTCPSignalMessageCSMs bool - disableTCPSignalMessageCSM bool - blockwiseSZX blockwise.SZX - blockwiseEnable bool -} - -// Listener defined used by coap -type Listener interface { - Close() error - AcceptWithContext(ctx context.Context) (net.Conn, error) -} - -type Server struct { - listen Listener - ctx context.Context - - cancel context.CancelFunc - - messagePool *pool.Pool - - errors ErrorFunc - goPool GoPoolFunc - - blockwiseTransferTimeout time.Duration - onNewClientConn OnNewClientConnFunc - - handler HandlerFunc - createInactivityMonitor func() inactivity.Monitor - periodicRunner periodic.Func - listenMutex sync.Mutex - maxMessageSize uint32 - connectionCacheSize uint16 - disableTCPSignalMessageCSM bool - blockwiseEnable bool - blockwiseSZX blockwise.SZX - disablePeerTCPSignalMessageCSMs bool -} - -func NewServer(opt ...ServerOption) *Server { - opts := defaultServerOptions - for _, o := range opt { - o.apply(&opts) - } - - ctx, cancel := context.WithCancel(opts.ctx) - - if opts.createInactivityMonitor == nil { - opts.createInactivityMonitor = func() inactivity.Monitor { - return inactivity.NewNilMonitor() - } - } - if opts.messagePool == nil { - opts.messagePool = pool.New(0, 0) - } - - if opts.errors == nil { - opts.errors = func(error) { - // default no-op - } - } - errorsFunc := opts.errors - // assign updated func to opts.errors so opts.handler also uses the updated error handler - opts.errors = func(err error) { - if errors.Is(err, context.Canceled) || errors.Is(err, io.EOF) || strings.Contains(err.Error(), "use of closed network connection") { - // this error was produced by cancellation context or closing connection. - return - } - errorsFunc(fmt.Errorf("tcp: %w", err)) - } - - return &Server{ - ctx: ctx, - cancel: cancel, - handler: opts.handler, - maxMessageSize: opts.maxMessageSize, - errors: opts.errors, - goPool: opts.goPool, - blockwiseSZX: opts.blockwiseSZX, - blockwiseEnable: opts.blockwiseEnable, - blockwiseTransferTimeout: opts.blockwiseTransferTimeout, - disablePeerTCPSignalMessageCSMs: opts.disablePeerTCPSignalMessageCSMs, - disableTCPSignalMessageCSM: opts.disableTCPSignalMessageCSM, - onNewClientConn: opts.onNewClientConn, - createInactivityMonitor: opts.createInactivityMonitor, - periodicRunner: opts.periodicRunner, - connectionCacheSize: opts.connectionCacheSize, - messagePool: opts.messagePool, - } -} - -func (s *Server) checkAndSetListener(l Listener) error { - s.listenMutex.Lock() - defer s.listenMutex.Unlock() - if s.listen != nil { - return fmt.Errorf("server already serves listener") - } - s.listen = l - return nil -} - -func (s *Server) popListener() Listener { - s.listenMutex.Lock() - defer s.listenMutex.Unlock() - l := s.listen - s.listen = nil - return l -} - -func (s *Server) checkAcceptError(err error) (bool, error) { - if err == nil { - return true, nil - } - switch { - case errors.Is(err, coapNet.ErrListenerIsClosed): - s.Stop() - return false, nil - case errors.Is(err, context.DeadlineExceeded), errors.Is(err, context.Canceled): - select { - case <-s.ctx.Done(): - default: - s.errors(fmt.Errorf("cannot accept connection: %w", err)) - return true, nil - } - return false, nil - default: - return true, nil - } -} - -func (s *Server) serveConnection(connections *connections.Connections, rw net.Conn) { - var cc *ClientConn - monitor := s.createInactivityMonitor() - cc = s.createClientConn(coapNet.NewConn(rw), monitor) - if s.onNewClientConn != nil { - if tlscon, ok := rw.(*tls.Conn); ok { - s.onNewClientConn(cc, tlscon) - } else { - s.onNewClientConn(cc, nil) - } - } - connections.Store(cc) - defer connections.Delete(cc) - - if err := cc.Run(); err != nil { - s.errors(fmt.Errorf("%v: %w", cc.RemoteAddr(), err)) - } -} - -func (s *Server) Serve(l Listener) error { - if s.blockwiseSZX > blockwise.SZXBERT { - return fmt.Errorf("invalid blockwiseSZX") - } - - err := s.checkAndSetListener(l) - if err != nil { - return err - } - defer func() { - s.Stop() - }() - var wg sync.WaitGroup - defer wg.Wait() - - connections := connections.New() - s.periodicRunner(func(now time.Time) bool { - connections.CheckExpirations(now) - return s.ctx.Err() == nil - }) - defer connections.Close() - - for { - rw, err := l.AcceptWithContext(s.ctx) - ok, err := s.checkAcceptError(err) - if err != nil { - return err - } - if !ok { - return nil - } - if rw == nil { - continue - } - wg.Add(1) - go func() { - defer wg.Done() - s.serveConnection(connections, rw) - }() - } -} - -// Stop stops server without wait of ends Serve function. -func (s *Server) Stop() { - s.cancel() - l := s.popListener() - if l == nil { - return - } - if err := l.Close(); err != nil { - s.errors(fmt.Errorf("cannot close listener: %w", err)) - } -} - -func (s *Server) createClientConn(connection *coapNet.Conn, monitor inactivity.Monitor) *ClientConn { - var blockWise *blockwise.BlockWise - if s.blockwiseEnable { - blockWise = blockwise.NewBlockWise( - bwCreateAcquireMessage(s.messagePool), - bwCreateReleaseMessage(s.messagePool), - s.blockwiseTransferTimeout, - s.errors, - false, - func(token message.Token) (blockwise.Message, bool) { - return nil, false - }, - ) - } - obsHandler := NewHandlerContainer() - cc := NewClientConn( - NewSession( - s.ctx, - connection, - NewObservationHandler(obsHandler, s.handler), - s.maxMessageSize, - s.goPool, - s.errors, - s.blockwiseSZX, - blockWise, - s.disablePeerTCPSignalMessageCSMs, - s.disableTCPSignalMessageCSM, - true, - monitor, - s.connectionCacheSize, - s.messagePool, - ), - obsHandler, kitSync.NewMap(), - ) - - return cc -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/tcp/session.go b/vendor/github.com/plgd-dev/go-coap/v2/tcp/session.go deleted file mode 100644 index d93d89ff83c..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/tcp/session.go +++ /dev/null @@ -1,427 +0,0 @@ -package tcp - -import ( - "bytes" - "context" - "errors" - "fmt" - "net" - "sync" - "sync/atomic" - "time" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - coapNet "github.com/plgd-dev/go-coap/v2/net" - "github.com/plgd-dev/go-coap/v2/net/blockwise" - "github.com/plgd-dev/go-coap/v2/net/monitor/inactivity" - coapTCP "github.com/plgd-dev/go-coap/v2/tcp/message" - "github.com/plgd-dev/go-coap/v2/tcp/message/pool" -) - -type EventFunc func() - -type Session struct { - // This field needs to be the first in the struct to ensure proper word alignment on 32-bit platforms. - // See: https://golang.org/pkg/sync/atomic/#pkg-note-BUG - sequence uint64 - - onClose []EventFunc - - ctx atomic.Value - - inactivityMonitor inactivity.Monitor - - errSendCSM error - - cancel context.CancelFunc - - done chan struct{} - - goPool GoPoolFunc - errors ErrorFunc - blockWise *blockwise.BlockWise - - connection *coapNet.Conn - - handler HandlerFunc - - midHandlerContainer *HandlerContainer - - tokenHandlerContainer *HandlerContainer - - messagePool *pool.Pool - - mutex sync.Mutex - - maxMessageSize uint32 - peerBlockWiseTranferEnabled uint32 - peerMaxMessageSize uint32 - connectionCacheSize uint16 - disableTCPSignalMessageCSM bool - disablePeerTCPSignalMessageCSMs bool - - blockwiseSZX blockwise.SZX - closeSocket bool -} - -func NewSession( - ctx context.Context, - connection *coapNet.Conn, - handler HandlerFunc, - maxMessageSize uint32, - goPool GoPoolFunc, - errors ErrorFunc, - blockwiseSZX blockwise.SZX, - blockWise *blockwise.BlockWise, - disablePeerTCPSignalMessageCSMs bool, - disableTCPSignalMessageCSM bool, - closeSocket bool, - inactivityMonitor inactivity.Monitor, - connectionCacheSize uint16, - messagePool *pool.Pool, -) *Session { - ctx, cancel := context.WithCancel(ctx) - if errors == nil { - errors = func(error) { - // default no-op - } - } - if inactivityMonitor == nil { - inactivityMonitor = inactivity.NewNilMonitor() - } - - s := &Session{ - cancel: cancel, - connection: connection, - handler: handler, - maxMessageSize: maxMessageSize, - tokenHandlerContainer: NewHandlerContainer(), - midHandlerContainer: NewHandlerContainer(), - goPool: goPool, - errors: errors, - blockWise: blockWise, - blockwiseSZX: blockwiseSZX, - disablePeerTCPSignalMessageCSMs: disablePeerTCPSignalMessageCSMs, - disableTCPSignalMessageCSM: disableTCPSignalMessageCSM, - closeSocket: closeSocket, - inactivityMonitor: inactivityMonitor, - done: make(chan struct{}), - connectionCacheSize: connectionCacheSize, - messagePool: messagePool, - } - s.ctx.Store(&ctx) - - if !disableTCPSignalMessageCSM { - err := s.sendCSM() - if err != nil { - s.errSendCSM = fmt.Errorf("cannot send CSM: %w", err) - } - } - - return s -} - -// SetContextValue stores the value associated with key to context of connection. -func (s *Session) SetContextValue(key interface{}, val interface{}) { - s.mutex.Lock() - defer s.mutex.Unlock() - ctx := context.WithValue(s.Context(), key, val) - s.ctx.Store(&ctx) -} - -// Done signalizes that connection is not more processed. -func (s *Session) Done() <-chan struct{} { - return s.done -} - -func (s *Session) AddOnClose(f EventFunc) { - s.mutex.Lock() - defer s.mutex.Unlock() - s.onClose = append(s.onClose, f) -} - -func (s *Session) popOnClose() []EventFunc { - s.mutex.Lock() - defer s.mutex.Unlock() - tmp := s.onClose - s.onClose = nil - return tmp -} - -func (s *Session) shutdown() { - defer close(s.done) - for _, f := range s.popOnClose() { - f() - } -} - -func (s *Session) Close() error { - s.cancel() - if s.closeSocket { - return s.connection.Close() - } - return nil -} - -func (s *Session) Sequence() uint64 { - return atomic.AddUint64(&s.sequence, 1) -} - -func (s *Session) Context() context.Context { - return *s.ctx.Load().(*context.Context) -} - -func (s *Session) PeerMaxMessageSize() uint32 { - return atomic.LoadUint32(&s.peerMaxMessageSize) -} - -func (s *Session) PeerBlockWiseTransferEnabled() bool { - return atomic.LoadUint32(&s.peerBlockWiseTranferEnabled) == 1 -} - -func (s *Session) handleBlockwise(w *ResponseWriter, r *pool.Message) { - if s.blockWise != nil && s.PeerBlockWiseTransferEnabled() { - bwr := bwResponseWriter{ - w: w, - } - s.blockWise.Handle(&bwr, r, s.blockwiseSZX, s.maxMessageSize, func(bw blockwise.ResponseWriter, br blockwise.Message) { - h, err := s.tokenHandlerContainer.Pop(r.Token()) - rw := bw.(*bwResponseWriter).w - m := br.(*pool.Message) - if err == nil { - h(rw, m) - return - } - s.handler(rw, m) - }) - return - } - h, err := s.tokenHandlerContainer.Pop(r.Token()) - if err == nil { - h(w, r) - return - } - s.handler(w, r) -} - -func (s *Session) handleSignals(r *pool.Message, cc *ClientConn) bool { - switch r.Code() { - case codes.CSM: - if s.disablePeerTCPSignalMessageCSMs { - return true - } - if size, err := r.GetOptionUint32(coapTCP.MaxMessageSize); err == nil { - atomic.StoreUint32(&s.peerMaxMessageSize, size) - } - if r.HasOption(coapTCP.BlockWiseTransfer) { - atomic.StoreUint32(&s.peerBlockWiseTranferEnabled, 1) - } - return true - case codes.Ping: - // if r.HasOption(coapTCP.Custody) { - //TODO - // } - if err := s.sendPong(r.Token()); err != nil && !coapNet.IsConnectionBrokenError(err) { - s.errors(fmt.Errorf("cannot handle ping signal: %w", err)) - } - return true - case codes.Release: - // if r.HasOption(coapTCP.AlternativeAddress) { - //TODO - // } - return true - case codes.Abort: - // if r.HasOption(coapTCP.BadCSMOption) { - //TODO - // } - return true - case codes.Pong: - h, err := s.tokenHandlerContainer.Pop(r.Token()) - if err == nil { - s.processReq(r, cc, h) - } - return true - } - return false -} - -type bwResponseWriter struct { - w *ResponseWriter -} - -func (b *bwResponseWriter) Message() blockwise.Message { - return b.w.response -} - -func (b *bwResponseWriter) SetMessage(m blockwise.Message) { - b.w.cc.session.messagePool.ReleaseMessage(b.w.response) - b.w.response = m.(*pool.Message) -} - -func (b *bwResponseWriter) RemoteAddr() net.Addr { - return b.w.cc.RemoteAddr() -} - -func (s *Session) Handle(w *ResponseWriter, r *pool.Message) { - s.handleBlockwise(w, r) -} - -func (s *Session) TokenHandler() *HandlerContainer { - return s.tokenHandlerContainer -} - -func (s *Session) processReq(req *pool.Message, cc *ClientConn, handler func(w *ResponseWriter, r *pool.Message)) { - origResp := s.messagePool.AcquireMessage(s.Context()) - origResp.SetToken(req.Token()) - w := NewResponseWriter(origResp, cc, req.Options()) - handler(w, req) - defer s.messagePool.ReleaseMessage(w.response) - if !req.IsHijacked() { - s.messagePool.ReleaseMessage(req) - } - if w.response.IsModified() { - err := s.WriteMessage(w.response) - if err != nil { - if errC := s.Close(); errC != nil { - s.errors(fmt.Errorf("cannot close connection: %w", errC)) - } - s.errors(fmt.Errorf("cannot write response to %v: %w", s.connection.RemoteAddr(), err)) - } - } -} - -func seekBufferToNextMessage(buffer *bytes.Buffer, msgSize int) *bytes.Buffer { - if msgSize == buffer.Len() { - // buffer is empty so reset it - buffer.Reset() - return buffer - } - // rewind to next message - trimmed := 0 - for trimmed != msgSize { - b := make([]byte, 4096) - max := 4096 - if msgSize-trimmed < max { - max = msgSize - trimmed - } - v, _ := buffer.Read(b[:max]) - trimmed += v - } - return buffer -} - -func (s *Session) processBuffer(buffer *bytes.Buffer, cc *ClientConn) error { - for buffer.Len() > 0 { - var hdr coapTCP.MessageHeader - err := hdr.Unmarshal(buffer.Bytes()) - if errors.Is(err, message.ErrShortRead) { - return nil - } - if hdr.TotalLen > s.maxMessageSize { - return fmt.Errorf("max message size(%v) was exceeded %v", s.maxMessageSize, hdr.TotalLen) - } - if uint32(buffer.Len()) < hdr.TotalLen { - return nil - } - req := s.messagePool.AcquireMessage(s.Context()) - read, err := req.Unmarshal(buffer.Bytes()[:hdr.TotalLen]) - if err != nil { - s.messagePool.ReleaseMessage(req) - return fmt.Errorf("cannot unmarshal with header: %w", err) - } - buffer = seekBufferToNextMessage(buffer, read) - req.SetSequence(s.Sequence()) - s.inactivityMonitor.Notify() - if s.handleSignals(req, cc) { - continue - } - err = s.goPool(func() { - s.processReq(req, cc, s.Handle) - }) - if err != nil { - return fmt.Errorf("cannot spawn go routine: %w", err) - } - } - return nil -} - -func (s *Session) WriteMessage(req *pool.Message) error { - data, err := req.Marshal() - if err != nil { - return fmt.Errorf("cannot marshal: %w", err) - } - err = s.connection.WriteWithContext(req.Context(), data) - if err != nil { - return fmt.Errorf("cannot write to connection: %w", err) - } - return err -} - -func (s *Session) sendCSM() error { - token, err := message.GetToken() - if err != nil { - return fmt.Errorf("cannot get token: %w", err) - } - req := s.messagePool.AcquireMessage(s.Context()) - defer s.messagePool.ReleaseMessage(req) - req.SetCode(codes.CSM) - req.SetToken(token) - return s.WriteMessage(req) -} - -func (s *Session) sendPong(token message.Token) error { - req := s.messagePool.AcquireMessage(s.Context()) - defer s.messagePool.ReleaseMessage(req) - req.SetCode(codes.Pong) - req.SetToken(token) - return s.WriteMessage(req) -} - -func shrinkBufferIfNecessary(buffer *bytes.Buffer, maxCap uint16) *bytes.Buffer { - if buffer.Len() == 0 && buffer.Cap() > int(maxCap) { - buffer = bytes.NewBuffer(make([]byte, 0, maxCap)) - } - return buffer -} - -// Run reads and process requests from a connection, until the connection is not closed. -func (s *Session) Run(cc *ClientConn) (err error) { - defer func() { - err1 := s.Close() - if err == nil { - err = err1 - } - s.shutdown() - }() - if s.errSendCSM != nil { - return s.errSendCSM - } - buffer := bytes.NewBuffer(make([]byte, 0, s.connectionCacheSize)) - readBuf := make([]byte, s.connectionCacheSize) - for { - err = s.processBuffer(buffer, cc) - if err != nil { - return err - } - buffer = shrinkBufferIfNecessary(buffer, s.connectionCacheSize) - readLen, err := s.connection.ReadWithContext(s.Context(), readBuf) - if err != nil { - if coapNet.IsConnectionBrokenError(err) { // other side closed the connection, ignore the error and return - return nil - } - return fmt.Errorf("cannot read from connection: %w", err) - } - if readLen > 0 { - buffer.Write(readBuf[:readLen]) - } - } -} - -// CheckExpirations checks and remove expired items from caches. -func (s *Session) CheckExpirations(now time.Time, cc *ClientConn) { - s.inactivityMonitor.CheckInactivity(now, cc) - if s.blockWise != nil { - s.blockWise.CheckExpirations(now) - } -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/client.go b/vendor/github.com/plgd-dev/go-coap/v2/udp/client.go deleted file mode 100644 index 560ab8b352f..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/client.go +++ /dev/null @@ -1,223 +0,0 @@ -package udp - -import ( - "context" - "fmt" - "net" - "time" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - coapNet "github.com/plgd-dev/go-coap/v2/net" - "github.com/plgd-dev/go-coap/v2/net/blockwise" - "github.com/plgd-dev/go-coap/v2/net/monitor/inactivity" - "github.com/plgd-dev/go-coap/v2/pkg/cache" - "github.com/plgd-dev/go-coap/v2/pkg/runner/periodic" - "github.com/plgd-dev/go-coap/v2/udp/client" - udpMessage "github.com/plgd-dev/go-coap/v2/udp/message" - "github.com/plgd-dev/go-coap/v2/udp/message/pool" - kitSync "github.com/plgd-dev/kit/v2/sync" -) - -var defaultDialOptions = func() dialOptions { - opts := dialOptions{ - ctx: context.Background(), - maxMessageSize: 64 * 1024, - - errors: func(err error) { - fmt.Println(err) - }, - goPool: func(f func()) error { - go func() { - f() - }() - return nil - }, - periodicRunner: func(f func(now time.Time) bool) { - go func() { - for f(time.Now()) { - time.Sleep(4 * time.Second) - } - }() - }, - dialer: &net.Dialer{Timeout: time.Second * 3}, - net: "udp", - blockwiseSZX: blockwise.SZX1024, - blockwiseEnable: true, - blockwiseTransferTimeout: time.Second * 3, - transmissionNStart: time.Second, - transmissionAcknowledgeTimeout: time.Second * 2, - transmissionMaxRetransmit: 4, - getMID: udpMessage.GetMID, - createInactivityMonitor: func() inactivity.Monitor { - return inactivity.NewNilMonitor() - }, - messagePool: pool.New(1024, 1600), - } - opts.handler = func(w *client.ResponseWriter, r *pool.Message) { - switch r.Code() { - case codes.POST, codes.PUT, codes.GET, codes.DELETE: - if err := w.SetResponse(codes.NotFound, message.TextPlain, nil); err != nil { - opts.errors(fmt.Errorf("udp client: cannot set response: %w", err)) - } - } - } - return opts -}() - -type dialOptions struct { - net string - ctx context.Context - getMID GetMIDFunc - handler HandlerFunc - errors ErrorFunc - goPool GoPoolFunc - dialer *net.Dialer - periodicRunner periodic.Func - messagePool *pool.Pool - blockwiseTransferTimeout time.Duration - transmissionNStart time.Duration - transmissionAcknowledgeTimeout time.Duration - createInactivityMonitor func() inactivity.Monitor - maxMessageSize uint32 - transmissionMaxRetransmit uint32 - closeSocket bool - blockwiseSZX blockwise.SZX - blockwiseEnable bool -} - -// A DialOption sets options such as credentials, keepalive parameters, etc. -type DialOption interface { - applyDial(*dialOptions) -} - -// Dial creates a client connection to the given target. -func Dial(target string, opts ...DialOption) (*client.ClientConn, error) { - cfg := defaultDialOptions - for _, o := range opts { - o.applyDial(&cfg) - } - - c, err := cfg.dialer.DialContext(cfg.ctx, cfg.net, target) - if err != nil { - return nil, err - } - conn, ok := c.(*net.UDPConn) - if !ok { - return nil, fmt.Errorf("unsupported connection type: %T", c) - } - opts = append(opts, WithCloseSocket()) - return Client(conn, opts...), nil -} - -func bwCreateAcquireMessage(messagePool *pool.Pool) func(ctx context.Context) blockwise.Message { - return func(ctx context.Context) blockwise.Message { - return messagePool.AcquireMessage(ctx) - } -} - -func bwCreateReleaseMessage(messagePool *pool.Pool) func(m blockwise.Message) { - return func(m blockwise.Message) { - messagePool.ReleaseMessage(m.(*pool.Message)) - } -} - -func bwCreateHandlerFunc(messagePool *pool.Pool, observatioRequests *kitSync.Map) func(token message.Token) (blockwise.Message, bool) { - return func(token message.Token) (blockwise.Message, bool) { - msg, ok := observatioRequests.LoadWithFunc(token.Hash(), func(v interface{}) interface{} { - r := v.(*pool.Message) - d := messagePool.AcquireMessage(r.Context()) - d.ResetOptionsTo(r.Options()) - d.SetCode(r.Code()) - d.SetToken(r.Token()) - d.SetMessageID(r.MessageID()) - return d - }) - if !ok { - return nil, ok - } - bwMessage := msg.(blockwise.Message) - return bwMessage, ok - } -} - -// Client creates client over udp connection. -func Client(conn *net.UDPConn, opts ...DialOption) *client.ClientConn { - cfg := defaultDialOptions - for _, o := range opts { - o.applyDial(&cfg) - } - if cfg.errors == nil { - cfg.errors = func(error) { - // default no-op - } - } - if cfg.createInactivityMonitor == nil { - cfg.createInactivityMonitor = func() inactivity.Monitor { - return inactivity.NewNilMonitor() - } - } - if cfg.messagePool == nil { - cfg.messagePool = pool.New(0, 0) - } - - errorsFunc := cfg.errors - cfg.errors = func(err error) { - if coapNet.IsCancelOrCloseError(err) { - // this error was produced by cancellation context or closing connection. - return - } - errorsFunc(fmt.Errorf("udp: %v: %w", conn.RemoteAddr(), err)) - } - - addr, _ := conn.RemoteAddr().(*net.UDPAddr) - observatioRequests := kitSync.NewMap() - var blockWise *blockwise.BlockWise - if cfg.blockwiseEnable { - blockWise = blockwise.NewBlockWise( - bwCreateAcquireMessage(cfg.messagePool), - bwCreateReleaseMessage(cfg.messagePool), - cfg.blockwiseTransferTimeout, - cfg.errors, - false, - bwCreateHandlerFunc(cfg.messagePool, observatioRequests), - ) - } - - observationTokenHandler := client.NewHandlerContainer() - monitor := cfg.createInactivityMonitor() - cache := cache.NewCache() - l := coapNet.NewUDPConn(cfg.net, conn, coapNet.WithErrors(cfg.errors)) - session := NewSession(cfg.ctx, - l, - addr, - cfg.maxMessageSize, - cfg.closeSocket, - context.Background(), - ) - cc := client.NewClientConn(session, - observationTokenHandler, observatioRequests, cfg.transmissionNStart, cfg.transmissionAcknowledgeTimeout, cfg.transmissionMaxRetransmit, - client.NewObservationHandler(observationTokenHandler, cfg.handler), - cfg.blockwiseSZX, - blockWise, - cfg.goPool, - cfg.errors, - cfg.getMID, - monitor, - cache, - cfg.messagePool, - ) - cfg.periodicRunner(func(now time.Time) bool { - cc.CheckExpirations(now) - return cc.Context().Err() == nil - }) - - go func() { - err := cc.Run() - if err != nil { - cfg.errors(err) - } - }() - - return cc -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/client/client.go b/vendor/github.com/plgd-dev/go-coap/v2/udp/client/client.go deleted file mode 100644 index dfd25dd0625..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/client/client.go +++ /dev/null @@ -1,129 +0,0 @@ -package client - -import ( - "context" - "io" - "net" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/mux" - "github.com/plgd-dev/go-coap/v2/udp/message/pool" -) - -type Client struct { - cc *ClientConn -} - -func NewClient(cc *ClientConn) *Client { - return &Client{ - cc: cc, - } -} - -func (c *Client) Ping(ctx context.Context) error { - return c.cc.Ping(ctx) -} - -func (c *Client) Delete(ctx context.Context, path string, opts ...message.Option) (*message.Message, error) { - resp, err := c.cc.Delete(ctx, path, opts...) - if err != nil { - return nil, err - } - defer c.cc.ReleaseMessage(resp) - return pool.ConvertTo(resp) -} - -func (c *Client) Put(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*message.Message, error) { - resp, err := c.cc.Put(ctx, path, contentFormat, payload, opts...) - if err != nil { - return nil, err - } - defer c.cc.ReleaseMessage(resp) - return pool.ConvertTo(resp) -} - -func (c *Client) Post(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*message.Message, error) { - resp, err := c.cc.Post(ctx, path, contentFormat, payload, opts...) - if err != nil { - return nil, err - } - defer c.cc.ReleaseMessage(resp) - return pool.ConvertTo(resp) -} - -func (c *Client) Get(ctx context.Context, path string, opts ...message.Option) (*message.Message, error) { - resp, err := c.cc.Get(ctx, path, opts...) - if err != nil { - return nil, err - } - defer c.cc.ReleaseMessage(resp) - return pool.ConvertTo(resp) -} - -func (c *Client) Close() error { - return c.cc.Close() -} - -func (c *Client) RemoteAddr() net.Addr { - return c.cc.RemoteAddr() -} - -func (c *Client) Context() context.Context { - return c.cc.Context() -} - -func (c *Client) SetContextValue(key interface{}, val interface{}) { - c.cc.Session().SetContextValue(key, val) -} - -func (c *Client) WriteMessage(req *message.Message) error { - r, err := c.cc.messagePool.ConvertFrom(req) - if err != nil { - return err - } - defer c.cc.ReleaseMessage(r) - return c.cc.WriteMessage(r) -} - -func (c *Client) Do(req *message.Message) (*message.Message, error) { - r, err := c.cc.messagePool.ConvertFrom(req) - if err != nil { - return nil, err - } - defer c.cc.ReleaseMessage(r) - resp, err := c.cc.Do(r) - if err != nil { - return nil, err - } - defer c.cc.ReleaseMessage(resp) - return pool.ConvertTo(resp) -} - -func createClientConnObserveHandler(observeFunc func(notification *message.Message)) func(n *pool.Message) { - return func(n *pool.Message) { - muxn, err := pool.ConvertTo(n) - if err != nil { - return - } - observeFunc(muxn) - } -} - -func (c *Client) Observe(ctx context.Context, path string, observeFunc func(notification *message.Message), opts ...message.Option) (mux.Observation, error) { - return c.cc.Observe(ctx, path, createClientConnObserveHandler(observeFunc), opts...) -} - -// Sequence acquires sequence number. -func (c *Client) Sequence() uint64 { - return c.cc.Sequence() -} - -// ClientConn get's underlaying client connection. -func (c *Client) ClientConn() interface{} { - return c.cc -} - -// Done signalizes that connection is not more processed. -func (c *Client) Done() <-chan struct{} { - return c.cc.Done() -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/client/clientconn.go b/vendor/github.com/plgd-dev/go-coap/v2/udp/client/clientconn.go deleted file mode 100644 index cea3db0d91c..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/client/clientconn.go +++ /dev/null @@ -1,805 +0,0 @@ -package client - -import ( - "context" - "errors" - "fmt" - "io" - "net" - "sync/atomic" - "time" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - coapNet "github.com/plgd-dev/go-coap/v2/net" - "github.com/plgd-dev/go-coap/v2/net/blockwise" - "github.com/plgd-dev/go-coap/v2/net/monitor/inactivity" - "github.com/plgd-dev/go-coap/v2/pkg/cache" - udpMessage "github.com/plgd-dev/go-coap/v2/udp/message" - "github.com/plgd-dev/go-coap/v2/udp/message/pool" - kitSync "github.com/plgd-dev/kit/v2/sync" - atomicTypes "go.uber.org/atomic" -) - -// https://datatracker.ietf.org/doc/html/rfc7252#section-4.8.2 -const ExchangeLifetime = 247 * time.Second - -type HandlerFunc = func(*ResponseWriter, *pool.Message) -type ErrorFunc = func(error) -type GoPoolFunc = func(func()) error -type EventFunc = func() -type GetMIDFunc = func() uint16 - -type Session interface { - Context() context.Context - Close() error - MaxMessageSize() uint32 - RemoteAddr() net.Addr - LocalAddr() net.Addr - WriteMessage(req *pool.Message) error - // WriteMulticast sends multicast to the remote multicast address. - // By default it is sent over all network interfaces and all compatible source IP addresses with hop limit 1. - // Via opts you can specify the network interface, source IP address, and hop limit. - WriteMulticastMessage(req *pool.Message, address *net.UDPAddr, opts ...coapNet.MulticastOption) error - Run(cc *ClientConn) error - AddOnClose(f EventFunc) - SetContextValue(key interface{}, val interface{}) - Done() <-chan struct{} -} - -// ClientConn represents a virtual connection to a conceptual endpoint, to perform COAPs commands. -type ClientConn struct { - session Session - inactivityMonitor inactivity.Monitor - - blockWise *blockwise.BlockWise - handler HandlerFunc - observationTokenHandler *HandlerContainer - observationRequests *kitSync.Map - transmission *Transmission - messagePool *pool.Pool - - // This field needs to be the first in the struct to ensure proper word alignment on 32-bit platforms. - // See: https://golang.org/pkg/sync/atomic/#pkg-note-BUG - sequence uint64 - goPool GoPoolFunc - errors ErrorFunc - responseMsgCache *cache.Cache - msgIDMutex *MutexMap - - tokenHandlerContainer *HandlerContainer - midHandlerContainer *HandlerContainer - msgID uint32 - blockwiseSZX blockwise.SZX -} - -// Transmission is a threadsafe container for transmission related parameters -type Transmission struct { - nStart *atomicTypes.Duration - acknowledgeTimeout *atomicTypes.Duration - maxRetransmit *atomicTypes.Int32 -} - -func (t *Transmission) SetTransmissionNStart(d time.Duration) { - t.nStart.Store(d) -} - -func (t *Transmission) SetTransmissionAcknowledgeTimeout(d time.Duration) { - t.acknowledgeTimeout.Store(d) -} - -func (t *Transmission) SetTransmissionMaxRetransmit(d int32) { - t.maxRetransmit.Store(d) -} - -func (cc *ClientConn) Transmission() *Transmission { - return cc.transmission -} - -// NewClientConn creates connection over session and observation. -func NewClientConn( - session Session, - observationTokenHandler *HandlerContainer, - observationRequests *kitSync.Map, - transmissionNStart time.Duration, - transmissionAcknowledgeTimeout time.Duration, - transmissionMaxRetransmit uint32, - handler HandlerFunc, - blockwiseSZX blockwise.SZX, - blockWise *blockwise.BlockWise, - goPool GoPoolFunc, - errors ErrorFunc, - getMID GetMIDFunc, - inactivityMonitor inactivity.Monitor, - responseMsgCache *cache.Cache, - messagePool *pool.Pool, -) *ClientConn { - if errors == nil { - errors = func(error) { - // default no-op - } - } - if getMID == nil { - getMID = udpMessage.GetMID - } - - return &ClientConn{ - msgID: uint32(getMID() - 0xffff/2), - session: session, - observationTokenHandler: observationTokenHandler, - observationRequests: observationRequests, - transmission: &Transmission{ - atomicTypes.NewDuration(transmissionNStart), - atomicTypes.NewDuration(transmissionAcknowledgeTimeout), - atomicTypes.NewInt32(int32(transmissionMaxRetransmit)), - }, - handler: handler, - blockwiseSZX: blockwiseSZX, - blockWise: blockWise, - - tokenHandlerContainer: NewHandlerContainer(), - midHandlerContainer: NewHandlerContainer(), - goPool: goPool, - errors: errors, - msgIDMutex: NewMutexMap(), - responseMsgCache: responseMsgCache, - inactivityMonitor: inactivityMonitor, - messagePool: messagePool, - } -} - -func (cc *ClientConn) Session() Session { - return cc.session -} - -func (cc *ClientConn) getMID() uint16 { - return uint16(atomic.AddUint32(&cc.msgID, 1)) -} - -// Close closes connection without waiting for the end of the Run function. -func (cc *ClientConn) Close() error { - err := cc.session.Close() - if errors.Is(err, net.ErrClosed) { - return nil - } - return err -} - -func (cc *ClientConn) do(req *pool.Message) (*pool.Message, error) { - token := req.Token() - if token == nil { - return nil, fmt.Errorf("invalid token") - } - - respChan := make(chan *pool.Message, 1) - err := cc.tokenHandlerContainer.Insert(token, func(w *ResponseWriter, r *pool.Message) { - r.Hijack() - select { - case respChan <- r: - default: - } - }) - if err != nil { - return nil, fmt.Errorf("cannot add token handler: %w", err) - } - defer func() { - _, _ = cc.tokenHandlerContainer.Pop(token) - }() - err = cc.writeMessage(req) - if err != nil { - return nil, fmt.Errorf("cannot write request: %w", err) - } - select { - case <-req.Context().Done(): - return nil, req.Context().Err() - case <-cc.session.Context().Done(): - return nil, fmt.Errorf("connection was closed: %w", cc.session.Context().Err()) - case resp := <-respChan: - return resp, nil - } -} - -// Do sends an coap message and returns an coap response. -// -// An error is returned if by failure to speak COAP (such as a network connectivity problem). -// Any status code doesn't cause an error. -// -// Caller is responsible to release request and response. -func (cc *ClientConn) Do(req *pool.Message) (*pool.Message, error) { - if cc.blockWise == nil { - req.UpsertMessageID(cc.getMID()) - return cc.do(req) - } - bwresp, err := cc.blockWise.Do(req, cc.blockwiseSZX, cc.session.MaxMessageSize(), func(bwreq blockwise.Message) (blockwise.Message, error) { - req := bwreq.(*pool.Message) - if req.Options().HasOption(message.Block1) || req.Options().HasOption(message.Block2) { - req.SetMessageID(cc.getMID()) - } else { - req.UpsertMessageID(cc.getMID()) - } - return cc.do(req) - }) - if err != nil { - return nil, err - } - return bwresp.(*pool.Message), nil -} - -func (cc *ClientConn) writeMessage(req *pool.Message) error { - respChan := make(chan struct{}) - - // Only confirmable messages ever match an message ID - if req.Type() == udpMessage.Confirmable { - err := cc.midHandlerContainer.Insert(req.MessageID(), func(w *ResponseWriter, r *pool.Message) { - close(respChan) - if r.IsSeparate() { - // separate message - just accept - return - } - cc.handleBW(w, r) - }) - if err != nil { - return fmt.Errorf("cannot insert mid handler: %w", err) - } - defer func() { - _, _ = cc.midHandlerContainer.Pop(req.MessageID()) - }() - } - - err := cc.session.WriteMessage(req) - if err != nil { - return fmt.Errorf("cannot write request: %w", err) - } - if req.Type() != udpMessage.Confirmable { - // If the request is not confirmable, we do not need to wait for a response - // and skip retransmissions - close(respChan) - } - - maxRetransmit := cc.transmission.maxRetransmit.Load() - for i := int32(0); i < maxRetransmit; i++ { - select { - case <-respChan: - return nil - case <-req.Context().Done(): - return req.Context().Err() - case <-cc.Context().Done(): - return fmt.Errorf("connection was closed: %w", cc.Context().Err()) - case <-time.After(cc.transmission.acknowledgeTimeout.Load()): - select { - case <-req.Context().Done(): - return req.Context().Err() - case <-cc.session.Context().Done(): - return fmt.Errorf("connection was closed: %w", cc.Context().Err()) - case <-time.After(cc.transmission.nStart.Load()): - err = cc.session.WriteMessage(req) - if err != nil { - return fmt.Errorf("cannot write request: %w", err) - } - } - } - } - return fmt.Errorf("timeout: retransmission(%v) was exhausted", cc.transmission.maxRetransmit.Load()) -} - -// WriteMessage sends an coap message. -func (cc *ClientConn) WriteMessage(req *pool.Message) error { - if cc.blockWise == nil { - req.UpsertMessageID(cc.getMID()) - return cc.writeMessage(req) - } - return cc.blockWise.WriteMessage(cc.RemoteAddr(), req, cc.blockwiseSZX, cc.session.MaxMessageSize(), func(bwreq blockwise.Message) error { - req := bwreq.(*pool.Message) - if req.Options().HasOption(message.Block1) || req.Options().HasOption(message.Block2) { - req.SetMessageID(cc.getMID()) - } else { - req.UpsertMessageID(cc.getMID()) - } - return cc.writeMessage(req) - }) -} - -func newCommonRequest(ctx context.Context, messagePool *pool.Pool, code codes.Code, path string, opts ...message.Option) (*pool.Message, error) { - token, err := message.GetToken() - if err != nil { - return nil, fmt.Errorf("cannot get token: %w", err) - } - req := messagePool.AcquireMessage(ctx) - req.SetCode(code) - req.SetToken(token) - req.ResetOptionsTo(opts) - if err := req.SetPath(path); err != nil { - messagePool.ReleaseMessage(req) - return nil, err - } - req.SetType(udpMessage.Confirmable) - return req, nil -} - -// NewGetRequest creates get request. -// -// Use ctx to set timeout. -func NewGetRequest(ctx context.Context, messagePool *pool.Pool, path string, opts ...message.Option) (*pool.Message, error) { - return newCommonRequest(ctx, messagePool, codes.GET, path, opts...) -} - -// Get issues a GET to the specified path. -// -// Use ctx to set timeout. -// -// An error is returned if by failure to speak COAP (such as a network connectivity problem). -// Any status code doesn't cause an error. -func (cc *ClientConn) Get(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) { - req, err := NewGetRequest(ctx, cc.messagePool, path, opts...) - if err != nil { - return nil, fmt.Errorf("cannot create get request: %w", err) - } - defer cc.ReleaseMessage(req) - return cc.Do(req) -} - -// NewPostRequest creates post request. -// -// Use ctx to set timeout. -// -// An error is returned if by failure to speak COAP (such as a network connectivity problem). -// Any status code doesn't cause an error. -// -// If payload is nil then content format is not used. -func NewPostRequest(ctx context.Context, messagePool *pool.Pool, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) { - req, err := newCommonRequest(ctx, messagePool, codes.POST, path, opts...) - if err != nil { - return nil, err - } - if payload != nil { - req.SetContentFormat(contentFormat) - req.SetBody(payload) - } - return req, nil -} - -// Post issues a POST to the specified path. -// -// Use ctx to set timeout. -// -// An error is returned if by failure to speak COAP (such as a network connectivity problem). -// Any status code doesn't cause an error. -// -// If payload is nil then content format is not used. -func (cc *ClientConn) Post(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) { - req, err := NewPostRequest(ctx, cc.messagePool, path, contentFormat, payload, opts...) - if err != nil { - return nil, fmt.Errorf("cannot create post request: %w", err) - } - defer cc.ReleaseMessage(req) - return cc.Do(req) -} - -// NewPutRequest creates put request. -// -// Use ctx to set timeout. -// -// If payload is nil then content format is not used. -func NewPutRequest(ctx context.Context, messagePool *pool.Pool, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) { - req, err := newCommonRequest(ctx, messagePool, codes.PUT, path, opts...) - if err != nil { - return nil, err - } - if payload != nil { - req.SetContentFormat(contentFormat) - req.SetBody(payload) - } - return req, nil -} - -// Put issues a PUT to the specified path. -// -// Use ctx to set timeout. -// -// An error is returned if by failure to speak COAP (such as a network connectivity problem). -// Any status code doesn't cause an error. -// -// If payload is nil then content format is not used. -func (cc *ClientConn) Put(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) { - req, err := NewPutRequest(ctx, cc.messagePool, path, contentFormat, payload, opts...) - if err != nil { - return nil, fmt.Errorf("cannot create put request: %w", err) - } - defer cc.ReleaseMessage(req) - return cc.Do(req) -} - -// NewDeleteRequest creates delete request. -// -// Use ctx to set timeout. -func NewDeleteRequest(ctx context.Context, messagePool *pool.Pool, path string, opts ...message.Option) (*pool.Message, error) { - return newCommonRequest(ctx, messagePool, codes.DELETE, path, opts...) -} - -// Delete deletes the resource identified by the request path. -// -// Use ctx to set timeout. -func (cc *ClientConn) Delete(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) { - req, err := NewDeleteRequest(ctx, cc.messagePool, path, opts...) - if err != nil { - return nil, fmt.Errorf("cannot create delete request: %w", err) - } - defer cc.ReleaseMessage(req) - return cc.Do(req) -} - -// Context returns the client's context. -// -// If connections was closed context is cancelled. -func (cc *ClientConn) Context() context.Context { - return cc.session.Context() -} - -// Ping issues a PING to the client and waits for PONG response. -// -// Use ctx to set timeout. -func (cc *ClientConn) Ping(ctx context.Context) error { - resp := make(chan bool, 1) - receivedPong := func() { - select { - case resp <- true: - default: - } - } - cancel, err := cc.AsyncPing(receivedPong) - if err != nil { - return err - } - defer cancel() - select { - case <-resp: - return nil - case <-ctx.Done(): - return ctx.Err() - } -} - -// AsyncPing sends ping and receivedPong will be called when pong arrives. It returns cancellation of ping operation. -func (cc *ClientConn) AsyncPing(receivedPong func()) (func(), error) { - req := cc.AcquireMessage(cc.Context()) - defer cc.ReleaseMessage(req) - req.SetType(udpMessage.Confirmable) - req.SetCode(codes.Empty) - mid := cc.getMID() - req.SetMessageID(mid) - err := cc.midHandlerContainer.Insert(mid, func(w *ResponseWriter, r *pool.Message) { - if r.Type() == udpMessage.Reset || r.Type() == udpMessage.Acknowledgement { - receivedPong() - } - }) - if err != nil { - return nil, fmt.Errorf("cannot insert mid handler: %w", err) - } - removeMidHandler := func() { - _, _ = cc.midHandlerContainer.Pop(mid) - } - err = cc.session.WriteMessage(req) - if err != nil { - removeMidHandler() - return nil, fmt.Errorf("cannot write request: %w", err) - } - return removeMidHandler, nil -} - -// Run reads and process requests from a connection, until the connection is closed. -func (cc *ClientConn) Run() error { - return cc.session.Run(cc) -} - -// AddOnClose calls function on close connection event. -func (cc *ClientConn) AddOnClose(f EventFunc) { - cc.session.AddOnClose(f) -} - -func (cc *ClientConn) RemoteAddr() net.Addr { - return cc.session.RemoteAddr() -} - -func (cc *ClientConn) LocalAddr() net.Addr { - return cc.session.LocalAddr() -} - -func (cc *ClientConn) sendPong(w *ResponseWriter, r *pool.Message) { - if err := w.SetResponse(codes.Empty, message.TextPlain, nil); err != nil { - cc.errors(fmt.Errorf("cannot send pong response: %w", err)) - } -} - -type bwResponseWriter struct { - w *ResponseWriter -} - -func (b *bwResponseWriter) Message() blockwise.Message { - return b.w.response -} - -func (b *bwResponseWriter) SetMessage(m blockwise.Message) { - b.w.cc.ReleaseMessage(b.w.response) - b.w.response = m.(*pool.Message) -} - -func (b *bwResponseWriter) RemoteAddr() net.Addr { - return b.w.cc.RemoteAddr() -} - -func (cc *ClientConn) handleBW(w *ResponseWriter, m *pool.Message) { - if cc.blockWise != nil { - bwr := bwResponseWriter{ - w: w, - } - cc.blockWise.Handle(&bwr, m, cc.blockwiseSZX, cc.session.MaxMessageSize(), func(bw blockwise.ResponseWriter, br blockwise.Message) { - h, err := cc.tokenHandlerContainer.Pop(m.Token()) - rw := bw.(*bwResponseWriter).w - rm := br.(*pool.Message) - if err == nil { - h(rw, rm) - return - } - cc.handler(rw, rm) - }) - return - } - h, err := cc.tokenHandlerContainer.Pop(m.Token()) - if err == nil { - h(w, m) - return - } - cc.handler(w, m) -} - -func (cc *ClientConn) handle(w *ResponseWriter, r *pool.Message) { - if r.Code() == codes.Empty && r.Type() == udpMessage.Confirmable && len(r.Token()) == 0 && len(r.Options()) == 0 && r.Body() == nil { - cc.sendPong(w, r) - return - } - h, err := cc.midHandlerContainer.Pop(r.MessageID()) - if err == nil { - h(w, r) - return - } - if r.IsSeparate() { - // msg was processed by token handler - just drop it. - return - } - cc.handleBW(w, r) -} - -// Sequence acquires sequence number. -func (cc *ClientConn) Sequence() uint64 { - return atomic.AddUint64(&cc.sequence, 1) -} - -func (cc *ClientConn) responseMsgCacheID(msgID uint16) string { - return fmt.Sprintf("resp-%v-%d", cc.RemoteAddr(), msgID) -} - -func (cc *ClientConn) addResponseToCache(resp *pool.Message) error { - marshaledResp, err := resp.Marshal() - if err != nil { - return err - } - cacheMsg := make([]byte, len(marshaledResp)) - copy(cacheMsg, marshaledResp) - cc.responseMsgCache.LoadOrStore(cc.responseMsgCacheID(resp.MessageID()), cache.NewElement(cacheMsg, time.Now().Add(ExchangeLifetime), nil)) - return nil -} - -func (cc *ClientConn) getResponseFromCache(mid uint16, resp *pool.Message) (bool, error) { - cachedResp := cc.responseMsgCache.Load(cc.responseMsgCacheID(mid)) - if cachedResp == nil { - return false, nil - } - if rawMsg, ok := cachedResp.Data().([]byte); ok { - _, err := resp.Unmarshal(rawMsg) - if err != nil { - return false, err - } - return true, nil - } - return false, nil -} - -// CheckMyMessageID compare client msgID against peer messageID and if it is near < 0xffff/4 then incrase msgID. -// When msgIDs met it can cause issue because cache can send message to which doesn't bellows to request. -func (cc *ClientConn) CheckMyMessageID(req *pool.Message) { - if req.Type() == udpMessage.Confirmable && req.MessageID()-uint16(atomic.LoadUint32(&cc.msgID)) < 0xffff/4 { - atomic.AddUint32(&cc.msgID, 0xffff/2) - } -} - -func (cc *ClientConn) checkResponseCache(req *pool.Message, w *ResponseWriter) (bool, error) { - if req.Type() == udpMessage.Confirmable || req.Type() == udpMessage.NonConfirmable { - if ok, err := cc.getResponseFromCache(req.MessageID(), w.response); ok { - w.response.SetMessageID(req.MessageID()) - w.response.SetType(udpMessage.NonConfirmable) - if req.Type() == udpMessage.Confirmable { - // req could be changed from NonConfirmation to confirmation message. - w.response.SetType(udpMessage.Acknowledgement) - } - return true, nil - } else if err != nil { - return false, fmt.Errorf("cannot unmarshal response from cache: %w", err) - } - } - return false, nil -} - -func isPongOrResetResponse(w *ResponseWriter) bool { - return w.response.IsModified() && (w.response.Type() == udpMessage.Reset || w.response.Code() == codes.Empty) -} - -func sendJustAcknowledgeMessage(reqType udpMessage.Type, w *ResponseWriter) bool { - return reqType == udpMessage.Confirmable && !w.response.IsModified() -} - -func (cc *ClientConn) processResponse(reqType udpMessage.Type, reqMessageID uint16, w *ResponseWriter) error { - switch { - case isPongOrResetResponse(w): - if reqType == udpMessage.Confirmable { - w.response.SetType(udpMessage.Acknowledgement) - w.response.SetMessageID(reqMessageID) - } else { - if w.response.Type() != udpMessage.Reset { - w.response.SetType(udpMessage.NonConfirmable) - } - w.response.SetMessageID(cc.getMID()) - } - return nil - case sendJustAcknowledgeMessage(reqType, w): - // send message to separate(confirm received) message, if response is not modified - w.response.SetCode(codes.Empty) - w.response.SetType(udpMessage.Acknowledgement) - w.response.SetMessageID(reqMessageID) - w.response.SetToken(nil) - err := cc.addResponseToCache(w.response) - if err != nil { - return fmt.Errorf("cannot cache response: %w", err) - } - return nil - case !w.response.IsModified(): - // don't send response - return nil - } - - // send piggybacked response - w.response.SetType(udpMessage.Confirmable) - w.response.SetMessageID(cc.getMID()) - if reqType == udpMessage.Confirmable { - w.response.SetType(udpMessage.Acknowledgement) - w.response.SetMessageID(reqMessageID) - } - if reqType == udpMessage.Confirmable || reqType == udpMessage.NonConfirmable { - err := cc.addResponseToCache(w.response) - if err != nil { - return fmt.Errorf("cannot cache response: %w", err) - } - } - return nil -} - -func (cc *ClientConn) processReq(req *pool.Message, w *ResponseWriter) error { - defer cc.inactivityMonitor.Notify() - reqMid := req.MessageID() - - // The same message ID can not be handled concurrently - // for deduplication to work - l := cc.msgIDMutex.Lock(reqMid) - defer l.Unlock() - - if ok, err := cc.checkResponseCache(req, w); err != nil { - return err - } else if ok { - return nil - } - - w.response.SetModified(false) - reqType := req.Type() - reqMessageID := req.MessageID() - cc.handle(w, req) - - return cc.processResponse(reqType, reqMessageID, w) -} - -func (cc *ClientConn) Process(datagram []byte) error { - if uint32(len(datagram)) > cc.session.MaxMessageSize() { - return fmt.Errorf("max message size(%v) was exceeded %v", cc.session.MaxMessageSize(), len(datagram)) - } - req := cc.AcquireMessage(cc.Context()) - _, err := req.Unmarshal(datagram) - if err != nil { - cc.ReleaseMessage(req) - return err - } - closeConnection := func() { - if errC := cc.Close(); errC != nil { - cc.errors(fmt.Errorf("cannot close connection: %w", errC)) - } - } - req.SetSequence(cc.Sequence()) - cc.CheckMyMessageID(req) - cc.inactivityMonitor.Notify() - err = cc.goPool(func() { - defer func() { - if !req.IsHijacked() { - cc.ReleaseMessage(req) - } - }() - resp := cc.AcquireMessage(cc.Context()) - resp.SetToken(req.Token()) - w := NewResponseWriter(resp, cc, req.Options()) - defer func() { - cc.ReleaseMessage(w.response) - }() - errP := cc.processReq(req, w) - if errP != nil { - closeConnection() - cc.errors(fmt.Errorf("cannot write response: %w", errP)) - return - } - if !w.response.IsModified() { - // nothing to send - return - } - errW := cc.writeMessage(w.response) - if errW != nil { - closeConnection() - cc.errors(fmt.Errorf("cannot write response: %w", errW)) - } - }) - if err != nil { - cc.ReleaseMessage(req) - return err - } - return nil -} - -func (cc *ClientConn) Client() *Client { - return NewClient(cc) -} - -// SetContextValue stores the value associated with key to context of connection. -func (cc *ClientConn) SetContextValue(key interface{}, val interface{}) { - cc.session.SetContextValue(key, val) -} - -// Done signalizes that connection is not more processed. -func (cc *ClientConn) Done() <-chan struct{} { - return cc.session.Done() -} - -// CheckExpirations checks and remove expired items from caches. -func (cc *ClientConn) CheckExpirations(now time.Time) { - cc.inactivityMonitor.CheckInactivity(now, cc) - if cc.blockWise != nil { - cc.blockWise.CheckExpirations(now) - } -} - -func (cc *ClientConn) AcquireMessage(ctx context.Context) *pool.Message { - return cc.messagePool.AcquireMessage(ctx) -} - -func (cc *ClientConn) ReleaseMessage(m *pool.Message) { - cc.messagePool.ReleaseMessage(m) -} - -// WriteMulticastMessage sends multicast to the remote multicast address. -// By default it is sent over all network interfaces and all compatible source IP addresses with hop limit 1. -// Via opts you can specify the network interface, source IP address, and hop limit. -func (cc *ClientConn) WriteMulticastMessage(req *pool.Message, address *net.UDPAddr, options ...coapNet.MulticastOption) error { - if req.Type() == udpMessage.Confirmable { - return fmt.Errorf("multicast messages cannot be confirmable") - } - req.SetMessageID(cc.getMID()) - - err := cc.session.WriteMulticastMessage(req, address, options...) - if err != nil { - return fmt.Errorf("cannot write request: %w", err) - } - return nil -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/client/clientobserve.go b/vendor/github.com/plgd-dev/go-coap/v2/udp/client/clientobserve.go deleted file mode 100644 index 8ff385526ac..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/client/clientobserve.go +++ /dev/null @@ -1,215 +0,0 @@ -package client - -import ( - "context" - "fmt" - "sync" - "time" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - "github.com/plgd-dev/go-coap/v2/net/observation" - "github.com/plgd-dev/go-coap/v2/udp/message/pool" - "go.uber.org/atomic" -) - -func NewObservationHandler(obsertionTokenHandler *HandlerContainer, next HandlerFunc) HandlerFunc { - return func(w *ResponseWriter, r *pool.Message) { - v, err := obsertionTokenHandler.Get(r.Token()) - if err == nil { - v(w, r) - return - } - obs, err := r.Observe() - if err == nil && obs > 1 { - w.SendReset() - return - } - next(w, r) - } -} - -type respObservationMessage struct { - code codes.Code - notSupported bool -} - -//Observation represents subscription to resource on the server -type Observation struct { - token message.Token - path string - cc *ClientConn - observeFunc func(req *pool.Message) - respObservationChan chan respObservationMessage - waitForResponse atomic.Bool - - mutex sync.Mutex - obsSequence uint32 // guarded by mutex - lastEvent time.Time // guarded by mutex -} - -func newObservation(token message.Token, path string, cc *ClientConn, observeFunc func(req *pool.Message), respObservationChan chan respObservationMessage) *Observation { - return &Observation{ - token: token, - path: path, - obsSequence: 0, - cc: cc, - waitForResponse: *atomic.NewBool(true), - respObservationChan: respObservationChan, - observeFunc: observeFunc, - } -} - -func (o *Observation) Canceled() bool { - _, ok := o.cc.observationRequests.Load(o.token.Hash()) - return !ok -} - -func (o *Observation) cleanUp() bool { - // we can ignore err during cleanUp, if err != nil then some other - // part of code already removed the handler for the token - _, _ = o.cc.observationTokenHandler.Pop(o.token) - registeredRequest, ok := o.cc.observationRequests.PullOut(o.token.Hash()) - if ok { - o.cc.ReleaseMessage(registeredRequest.(*pool.Message)) - } - return ok -} - -func (o *Observation) handler(w *ResponseWriter, r *pool.Message) { - code := r.Code() - notSupported := !r.HasOption(message.Observe) - if o.waitForResponse.CAS(true, false) { - select { - case o.respObservationChan <- respObservationMessage{ - code: code, - notSupported: notSupported, - }: - default: - } - o.respObservationChan = nil - } - if o.wantBeNotified(r) { - o.observeFunc(r) - } -} - -// Cancel remove observation from server. For recreate observation use Observe. -func (o *Observation) Cancel(ctx context.Context) error { - if !o.cleanUp() { - // observation was already cleanup - return nil - } - req, err := NewGetRequest(ctx, o.cc.messagePool, o.path) - if err != nil { - return fmt.Errorf("cannot cancel observation request: %w", err) - } - defer o.cc.ReleaseMessage(req) - req.SetObserve(1) - req.SetToken(o.token) - resp, err := o.cc.Do(req) - if err != nil { - return err - } - defer o.cc.ReleaseMessage(resp) - if resp.Code() != codes.Content { - return fmt.Errorf("unexpected return code(%v)", resp.Code()) - } - return err -} - -func (o *Observation) wantBeNotified(r *pool.Message) bool { - obsSequence, err := r.Observe() - if err != nil { - return true - } - now := time.Now() - - o.mutex.Lock() - defer o.mutex.Unlock() - - if observation.ValidSequenceNumber(o.obsSequence, obsSequence, o.lastEvent, now) { - o.obsSequence = obsSequence - o.lastEvent = now - return true - } - - return false -} - -// NewObserveRequest creates observe request. -// -// Use ctx to set timeout. -func NewObserveRequest(ctx context.Context, messagePool *pool.Pool, path string, opts ...message.Option) (*pool.Message, error) { - req, err := NewGetRequest(ctx, messagePool, path, opts...) - if err != nil { - return nil, fmt.Errorf("cannot create observe request: %w", err) - } - req.SetObserve(0) - return req, nil -} - -// ObserveRequest subscribes for every change of resource on path. It can return canceled observation and it happens when resource doesn't support observation. -// This is detected when the first notification doesn't contains observe option. -func (cc *ClientConn) ObserveRequest(req *pool.Message, observeFunc func(req *pool.Message)) (*Observation, error) { - path, err := req.Path() - if err != nil { - return nil, fmt.Errorf("cannot get path: %w", err) - } - observe, err := req.Observe() - if err != nil { - return nil, fmt.Errorf("cannot get observe option: %w", err) - } - if observe != 0 { - return nil, fmt.Errorf("invalid value of observe(%v): expected 0", observe) - } - token := req.Token() - if len(token) == 0 { - return nil, fmt.Errorf("empty token") - } - respObservationChan := make(chan respObservationMessage, 1) - o := newObservation(token, path, cc, observeFunc, respObservationChan) - - cc.observationRequests.Store(token.Hash(), req) - err = o.cc.observationTokenHandler.Insert(token.Hash(), o.handler) - defer func(err *error) { - if *err != nil { - o.cleanUp() - } - }(&err) - if err != nil { - return nil, err - } - - err = cc.WriteMessage(req) - if err != nil { - return nil, err - } - select { - case <-req.Context().Done(): - err = req.Context().Err() - return nil, err - case <-cc.Context().Done(): - err = fmt.Errorf("connection was closed: %w", cc.Context().Err()) - return nil, err - case respObservationMessage := <-respObservationChan: - if respObservationMessage.code != codes.Content { - err = fmt.Errorf("unexpected return code(%v)", respObservationMessage.code) - return nil, err - } - if respObservationMessage.notSupported { - o.cleanUp() - } - return o, nil - } -} - -// Observe subscribes for every change of resource on path. It can return canceled observation and it happens when resource doesn't support observation. -// This is detected when the first notification doesn't contains observe option. -func (cc *ClientConn) Observe(ctx context.Context, path string, observeFunc func(msg *pool.Message), opts ...message.Option) (*Observation, error) { - req, err := NewObserveRequest(ctx, cc.messagePool, path, opts...) - if err != nil { - return nil, err - } - return cc.ObserveRequest(req, observeFunc) -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/client/container.go b/vendor/github.com/plgd-dev/go-coap/v2/udp/client/container.go deleted file mode 100644 index 2ec27a23c72..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/client/container.go +++ /dev/null @@ -1,70 +0,0 @@ -package client - -import ( - "errors" - "sync" - - "github.com/plgd-dev/go-coap/v2/message" -) - -var ( - ErrKeyAlreadyExists = errors.New("key already exists") - - ErrKeyNotExists = errors.New("key does not exist") -) - -// HandlerContainer for regirstration handlers by key -type HandlerContainer struct { - datas map[interface{}]HandlerFunc - mutex sync.Mutex -} - -// NewHandlerContainer factory -func NewHandlerContainer() *HandlerContainer { - return &HandlerContainer{ - datas: make(map[interface{}]HandlerFunc), - } -} - -// Insert handler for key. -func (s *HandlerContainer) Insert(key interface{}, handler HandlerFunc) error { - if v, ok := key.(message.Token); ok { - key = v.Hash() - } - s.mutex.Lock() - defer s.mutex.Unlock() - if _, keyExist := s.datas[key]; keyExist { - return ErrKeyAlreadyExists - } - s.datas[key] = handler - return nil -} - -// Get returns handler for key -func (s *HandlerContainer) Get(key interface{}) (HandlerFunc, error) { - if v, ok := key.(message.Token); ok { - key = v.Hash() - } - s.mutex.Lock() - defer s.mutex.Unlock() - v, keyExists := s.datas[key] - if !keyExists { - return nil, ErrKeyNotExists - } - return v, nil -} - -// Pop pops handler for key -func (s *HandlerContainer) Pop(key interface{}) (HandlerFunc, error) { - if v, ok := key.(message.Token); ok { - key = v.Hash() - } - s.mutex.Lock() - defer s.mutex.Unlock() - v, keyExists := s.datas[key] - if !keyExists { - return nil, ErrKeyNotExists - } - delete(s.datas, key) - return v, nil -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/client/mux.go b/vendor/github.com/plgd-dev/go-coap/v2/udp/client/mux.go deleted file mode 100644 index 0194df4b356..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/client/mux.go +++ /dev/null @@ -1,42 +0,0 @@ -package client - -import ( - "io" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - "github.com/plgd-dev/go-coap/v2/mux" - udpMessage "github.com/plgd-dev/go-coap/v2/udp/message" - "github.com/plgd-dev/go-coap/v2/udp/message/pool" -) - -func HandlerFuncToMux(m mux.Handler) HandlerFunc { - h := func(w *ResponseWriter, r *pool.Message) { - muxw := &muxResponseWriter{ - w: w, - } - muxr, err := pool.ConvertTo(r) - if err != nil { - return - } - m.ServeCOAP(muxw, &mux.Message{ - Message: muxr, - SequenceNumber: r.Sequence(), - IsConfirmable: r.Type() == udpMessage.Confirmable, - RouteParams: new(mux.RouteParams), - }) - } - return h -} - -type muxResponseWriter struct { - w *ResponseWriter -} - -func (w *muxResponseWriter) SetResponse(code codes.Code, contentFormat message.MediaType, d io.ReadSeeker, opts ...message.Option) error { - return w.w.SetResponse(code, contentFormat, d, opts...) -} - -func (w *muxResponseWriter) Client() mux.Client { - return w.w.ClientConn().Client() -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/client/responsewriter.go b/vendor/github.com/plgd-dev/go-coap/v2/udp/client/responsewriter.go deleted file mode 100644 index cb90aadda7b..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/client/responsewriter.go +++ /dev/null @@ -1,66 +0,0 @@ -package client - -import ( - "io" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - "github.com/plgd-dev/go-coap/v2/message/noresponse" - udpMessage "github.com/plgd-dev/go-coap/v2/udp/message" - "github.com/plgd-dev/go-coap/v2/udp/message/pool" -) - -// A ResponseWriter interface is used by an COAP handler to construct an COAP response. -type ResponseWriter struct { - noResponseValue *uint32 - response *pool.Message - cc *ClientConn -} - -func NewResponseWriter(response *pool.Message, cc *ClientConn, requestOptions message.Options) *ResponseWriter { - var noResponseValue *uint32 - v, err := requestOptions.GetUint32(message.NoResponse) - if err == nil { - noResponseValue = &v - } - - return &ResponseWriter{ - response: response, - cc: cc, - noResponseValue: noResponseValue, - } -} - -func (r *ResponseWriter) SetResponse(code codes.Code, contentFormat message.MediaType, d io.ReadSeeker, opts ...message.Option) error { - if r.noResponseValue != nil { - err := noresponse.IsNoResponseCode(code, *r.noResponseValue) - if err != nil { - return err - } - } - - r.response.SetCode(code) - r.response.ResetOptionsTo(opts) - if d != nil { - r.response.SetContentFormat(contentFormat) - r.response.SetBody(d) - if !r.response.HasOption(message.ETag) { - etag, err := message.GetETag(d) - if err != nil { - return err - } - r.response.SetOptionBytes(message.ETag, etag) - } - } - return nil -} - -func (r *ResponseWriter) ClientConn() *ClientConn { - return r.cc -} - -func (r *ResponseWriter) SendReset() { - r.response.Reset() - r.response.SetCode(codes.Empty) - r.response.SetType(udpMessage.Reset) -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/message/getmid.go b/vendor/github.com/plgd-dev/go-coap/v2/udp/message/getmid.go deleted file mode 100644 index 60a6bd471ca..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/message/getmid.go +++ /dev/null @@ -1,30 +0,0 @@ -package message - -import ( - "crypto/rand" - "encoding/binary" - mathRand "math/rand" - "sync/atomic" - "time" -) - -func init() { - mathRand.Seed(time.Now().UnixNano()) -} - -var msgID = uint32(RandMID()) - -// GetMID generates a message id for UDP-coap -func GetMID() uint16 { - return uint16(atomic.AddUint32(&msgID, 1)) -} - -func RandMID() uint16 { - b := make([]byte, 4) - _, err := rand.Read(b) - if err != nil { - // fallback to cryptographically insecure pseudo-random generator - return uint16(mathRand.Uint32() >> 16) - } - return uint16(binary.BigEndian.Uint32(b)) -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/message/pool/message.go b/vendor/github.com/plgd-dev/go-coap/v2/udp/message/pool/message.go deleted file mode 100644 index cf2df9fd643..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/message/pool/message.go +++ /dev/null @@ -1,243 +0,0 @@ -package pool - -import ( - "bytes" - "context" - "fmt" - "io" - "sync" - "sync/atomic" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - "github.com/plgd-dev/go-coap/v2/message/pool" - udp "github.com/plgd-dev/go-coap/v2/udp/message" -) - -type Message struct { - - //local vars - rawData []byte - rawMarshalData []byte - - ctx context.Context - messageID *uint16 - *pool.Message - rawMaxSize uint16 - - typ udp.Type - - isModified bool -} - -// Reset clear message for next reuse -func (r *Message) Reset() { - r.Message.Reset() - r.messageID = nil - r.typ = udp.NonConfirmable - if cap(r.rawData) > int(r.rawMaxSize) { - r.rawData = make([]byte, 256) - } - if cap(r.rawMarshalData) > int(r.rawMaxSize) { - r.rawMarshalData = make([]byte, 256) - } - r.isModified = false -} - -func (r *Message) Context() context.Context { - return r.ctx -} - -func (r *Message) SetMessageID(mid uint16) { - r.messageID = &mid - r.isModified = true -} - -func (r *Message) UpsertMessageID(mid uint16) uint16 { - if r.messageID != nil { - return *r.messageID - } - r.messageID = &mid - return mid -} - -func (r *Message) MessageID() uint16 { - if r.messageID == nil { - panic("messageID is not set") - } - return *r.messageID -} - -func (r *Message) SetType(typ udp.Type) { - r.typ = typ - r.isModified = true -} - -func (r *Message) Type() udp.Type { - return r.typ -} - -func (r *Message) IsModified() bool { - return r.isModified || r.Message.IsModified() -} - -func (r *Message) SetModified(b bool) { - r.isModified = b - r.Message.SetModified(b) -} - -func (r *Message) Unmarshal(data []byte) (int, error) { - if len(r.rawData) < len(data) { - r.rawData = append(r.rawData, make([]byte, len(data)-len(r.rawData))...) - } - copy(r.rawData, data) - r.rawData = r.rawData[:len(data)] - m := &udp.Message{ - Options: make(message.Options, 0, 16), - } - - n, err := m.Unmarshal(r.rawData) - if err != nil { - return n, err - } - r.Message.SetCode(m.Code) - r.Message.SetToken(m.Token) - r.Message.ResetOptionsTo(m.Options) - r.typ = m.Type - r.messageID = &m.MessageID - if len(m.Payload) > 0 { - r.Message.SetBody(bytes.NewReader(m.Payload)) - } - return n, err -} - -func (r *Message) Marshal() ([]byte, error) { - m := udp.Message{ - Code: r.Code(), - Token: r.Message.Token(), - Options: r.Message.Options(), - MessageID: r.MessageID(), - Type: r.typ, - } - payload, err := r.ReadBody() - if err != nil { - return nil, err - } - m.Payload = payload - size, err := m.Size() - if err != nil { - return nil, err - } - if len(r.rawMarshalData) < size { - r.rawMarshalData = append(r.rawMarshalData, make([]byte, size-len(r.rawMarshalData))...) - } - n, err := m.MarshalTo(r.rawMarshalData) - if err != nil { - return nil, err - } - r.rawMarshalData = r.rawMarshalData[:n] - return r.rawMarshalData, nil -} - -func (r *Message) IsSeparate() bool { - return r.Code() == codes.Empty && r.Token() == nil && r.Type() == udp.Acknowledgement && len(r.Options()) == 0 && r.Body() == nil -} - -func (r *Message) String() string { - if r == nil { - return "nil" - } - mid := "nil" - if r.messageID != nil { - mid = fmt.Sprint(*r.messageID) - } - return fmt.Sprintf("Type: %v, MID: %v, %s", r.Type(), mid, r.Message.String()) -} - -type Pool struct { - // This field needs to be the first in the struct to ensure proper word alignment on 32-bit platforms. - // See: https://golang.org/pkg/sync/atomic/#pkg-note-BUG - currentMessagesInPool int64 - messagePool sync.Pool - maxNumMessages uint32 - maxMessageBufferSize uint16 -} - -func New(maxNumMessages uint32, maxMessageBufferSize uint16) *Pool { - return &Pool{ - maxNumMessages: maxNumMessages, - maxMessageBufferSize: maxMessageBufferSize, - } -} - -// AcquireMessage returns an empty Message instance from Message pool. -// -// The returned Message instance may be passed to ReleaseMessage when it is -// no longer needed. This allows Message recycling, reduces GC pressure -// and usually improves performance. -func (p *Pool) AcquireMessage(ctx context.Context) *Message { - v := p.messagePool.Get() - if v == nil { - return &Message{ - Message: pool.NewMessage(), - rawData: make([]byte, 256), - rawMarshalData: make([]byte, 256), - ctx: ctx, - } - } - r := v.(*Message) - atomic.AddInt64(&p.currentMessagesInPool, -1) - r.ctx = ctx - return r -} - -// ReleaseMessage returns req acquired via AcquireMessage to Message pool. -// -// It is forbidden accessing req and/or its' members after returning -// it to Message pool. -func (p *Pool) ReleaseMessage(req *Message) { - v := atomic.LoadInt64(&p.currentMessagesInPool) - if v >= int64(p.maxNumMessages) { - return - } - atomic.AddInt64(&p.currentMessagesInPool, 1) - req.Reset() - req.ctx = nil - p.messagePool.Put(req) -} - -// ConvertFrom converts common message to pool message. -func (p *Pool) ConvertFrom(m *message.Message) (*Message, error) { - if m.Context == nil { - return nil, fmt.Errorf("invalid context") - } - r := p.AcquireMessage(m.Context) - r.SetCode(m.Code) - r.ResetOptionsTo(m.Options) - r.SetBody(m.Body) - r.SetToken(m.Token) - return r, nil -} - -// ConvertTo converts pool message to common message. -func ConvertTo(m *Message) (*message.Message, error) { - opts, err := m.Options().Clone() - if err != nil { - return nil, err - } - var body io.ReadSeeker - if m.Body() != nil { - payload, err := m.ReadBody() - if err != nil { - return nil, err - } - body = bytes.NewReader(payload) - } - return &message.Message{ - Context: m.Context(), - Code: m.Code(), - Token: m.Token(), - Body: body, - Options: opts, - }, nil -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/optionmux.go b/vendor/github.com/plgd-dev/go-coap/v2/udp/optionmux.go deleted file mode 100644 index 04c1d3a2bd7..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/optionmux.go +++ /dev/null @@ -1,11 +0,0 @@ -package udp - -import ( - "github.com/plgd-dev/go-coap/v2/mux" - "github.com/plgd-dev/go-coap/v2/udp/client" -) - -// WithMux set's multiplexer for handle requests. -func WithMux(m mux.Handler) HandlerFuncOpt { - return WithHandlerFunc(client.HandlerFuncToMux(m)) -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/options.go b/vendor/github.com/plgd-dev/go-coap/v2/udp/options.go deleted file mode 100644 index 5bdc5c484d6..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/options.go +++ /dev/null @@ -1,322 +0,0 @@ -package udp - -import ( - "context" - "net" - "time" - - "github.com/plgd-dev/go-coap/v2/net/blockwise" - "github.com/plgd-dev/go-coap/v2/net/monitor/inactivity" - "github.com/plgd-dev/go-coap/v2/pkg/runner/periodic" - "github.com/plgd-dev/go-coap/v2/udp/client" - "github.com/plgd-dev/go-coap/v2/udp/message/pool" -) - -// HandlerFuncOpt handler function option. -type HandlerFuncOpt struct { - h HandlerFunc -} - -func (o HandlerFuncOpt) apply(opts *serverOptions) { - opts.handler = o.h -} - -func (o HandlerFuncOpt) applyDial(opts *dialOptions) { - opts.handler = o.h -} - -// WithHandlerFunc set handle for handling request's. -func WithHandlerFunc(h HandlerFunc) HandlerFuncOpt { - return HandlerFuncOpt{h: h} -} - -// ContextOpt handler function option. -type ContextOpt struct { - ctx context.Context -} - -func (o ContextOpt) apply(opts *serverOptions) { - opts.ctx = o.ctx -} - -func (o ContextOpt) applyDial(opts *dialOptions) { - opts.ctx = o.ctx -} - -// WithContext set's parent context of server. -func WithContext(ctx context.Context) ContextOpt { - return ContextOpt{ctx: ctx} -} - -// MaxMessageSizeOpt handler function option. -type MaxMessageSizeOpt struct { - maxMessageSize uint32 -} - -func (o MaxMessageSizeOpt) apply(opts *serverOptions) { - opts.maxMessageSize = o.maxMessageSize -} - -func (o MaxMessageSizeOpt) applyDial(opts *dialOptions) { - opts.maxMessageSize = o.maxMessageSize -} - -// WithMaxMessageSize limit size of processed message. -func WithMaxMessageSize(maxMessageSize uint32) MaxMessageSizeOpt { - return MaxMessageSizeOpt{maxMessageSize: maxMessageSize} -} - -// ErrorsOpt errors option. -type ErrorsOpt struct { - errors ErrorFunc -} - -func (o ErrorsOpt) apply(opts *serverOptions) { - opts.errors = o.errors -} - -func (o ErrorsOpt) applyDial(opts *dialOptions) { - opts.errors = o.errors -} - -// WithErrors set function for logging error. -func WithErrors(errors ErrorFunc) ErrorsOpt { - return ErrorsOpt{errors: errors} -} - -// GoPoolOpt gopool option. -type GoPoolOpt struct { - goPool GoPoolFunc -} - -func (o GoPoolOpt) apply(opts *serverOptions) { - opts.goPool = o.goPool -} - -func (o GoPoolOpt) applyDial(opts *dialOptions) { - opts.goPool = o.goPool -} - -// WithGoPool sets function for managing spawning go routines -// for handling incoming request's. -// Eg: https://github.com/panjf2000/ants. -func WithGoPool(goPool GoPoolFunc) GoPoolOpt { - return GoPoolOpt{goPool: goPool} -} - -// KeepAliveOpt keepalive option. -type KeepAliveOpt struct { - timeout time.Duration - onInactive inactivity.OnInactiveFunc - maxRetries uint32 -} - -func (o KeepAliveOpt) apply(opts *serverOptions) { - opts.createInactivityMonitor = func() inactivity.Monitor { - keepalive := inactivity.NewKeepAlive(o.maxRetries, o.onInactive, func(cc inactivity.ClientConn, receivePong func()) (func(), error) { - return cc.(*client.ClientConn).AsyncPing(receivePong) - }) - return inactivity.NewInactivityMonitor(o.timeout/time.Duration(o.maxRetries+1), keepalive.OnInactive) - } -} - -func (o KeepAliveOpt) applyDial(opts *dialOptions) { - opts.createInactivityMonitor = func() inactivity.Monitor { - keepalive := inactivity.NewKeepAlive(o.maxRetries, o.onInactive, func(cc inactivity.ClientConn, receivePong func()) (func(), error) { - return cc.(*client.ClientConn).AsyncPing(receivePong) - }) - return inactivity.NewInactivityMonitor(o.timeout/time.Duration(o.maxRetries+1), keepalive.OnInactive) - } -} - -// WithKeepAlive monitoring's client connection's. -func WithKeepAlive(maxRetries uint32, timeout time.Duration, onInactive inactivity.OnInactiveFunc) KeepAliveOpt { - return KeepAliveOpt{ - maxRetries: maxRetries, - timeout: timeout, - onInactive: onInactive, - } -} - -// InactivityMonitorOpt notifies when a connection was inactive for a given duration. -type InactivityMonitorOpt struct { - duration time.Duration - onInactive inactivity.OnInactiveFunc -} - -func (o InactivityMonitorOpt) apply(opts *serverOptions) { - opts.createInactivityMonitor = func() inactivity.Monitor { - return inactivity.NewInactivityMonitor(o.duration, o.onInactive) - } -} - -func (o InactivityMonitorOpt) applyDial(opts *dialOptions) { - opts.createInactivityMonitor = func() inactivity.Monitor { - return inactivity.NewInactivityMonitor(o.duration, o.onInactive) - } -} - -// WithInactivityMonitor set deadline's for read operations over client connection. -func WithInactivityMonitor(duration time.Duration, onInactive inactivity.OnInactiveFunc) InactivityMonitorOpt { - return InactivityMonitorOpt{ - duration: duration, - onInactive: onInactive, - } -} - -// NetOpt network option. -type NetOpt struct { - net string -} - -func (o NetOpt) applyDial(opts *dialOptions) { - opts.net = o.net -} - -// WithNetwork define's udp version (udp4, udp6, udp) for client. -func WithNetwork(net string) NetOpt { - return NetOpt{net: net} -} - -// PeriodicRunnerOpt function which is executed in every ticks -type PeriodicRunnerOpt struct { - periodicRunner periodic.Func -} - -func (o PeriodicRunnerOpt) applyDial(opts *dialOptions) { - opts.periodicRunner = o.periodicRunner -} - -func (o PeriodicRunnerOpt) apply(opts *serverOptions) { - opts.periodicRunner = o.periodicRunner -} - -// WithPeriodicRunner set function which is executed in every ticks. -func WithPeriodicRunner(periodicRunner periodic.Func) PeriodicRunnerOpt { - return PeriodicRunnerOpt{periodicRunner: periodicRunner} -} - -// BlockwiseOpt network option. -type BlockwiseOpt struct { - transferTimeout time.Duration - enable bool - szx blockwise.SZX -} - -func (o BlockwiseOpt) apply(opts *serverOptions) { - opts.blockwiseEnable = o.enable - opts.blockwiseSZX = o.szx - opts.blockwiseTransferTimeout = o.transferTimeout -} - -func (o BlockwiseOpt) applyDial(opts *dialOptions) { - opts.blockwiseEnable = o.enable - opts.blockwiseSZX = o.szx - opts.blockwiseTransferTimeout = o.transferTimeout -} - -// WithBlockwise configure's blockwise transfer. -func WithBlockwise(enable bool, szx blockwise.SZX, transferTimeout time.Duration) BlockwiseOpt { - return BlockwiseOpt{ - enable: enable, - szx: szx, - transferTimeout: transferTimeout, - } -} - -// OnNewClientConnOpt network option. -type OnNewClientConnOpt struct { - onNewClientConn OnNewClientConnFunc -} - -func (o OnNewClientConnOpt) apply(opts *serverOptions) { - opts.onNewClientConn = o.onNewClientConn -} - -// WithOnNewClientConn server's notify about new client connection. -func WithOnNewClientConn(onNewClientConn OnNewClientConnFunc) OnNewClientConnOpt { - return OnNewClientConnOpt{ - onNewClientConn: onNewClientConn, - } -} - -// TransmissionOpt transmission options. -type TransmissionOpt struct { - transmissionNStart time.Duration - transmissionAcknowledgeTimeout time.Duration - transmissionMaxRetransmit uint32 -} - -func (o TransmissionOpt) apply(opts *serverOptions) { - opts.transmissionNStart = o.transmissionNStart - opts.transmissionAcknowledgeTimeout = o.transmissionAcknowledgeTimeout - opts.transmissionMaxRetransmit = o.transmissionMaxRetransmit -} - -func (o TransmissionOpt) applyDial(opts *dialOptions) { - opts.transmissionNStart = o.transmissionNStart - opts.transmissionAcknowledgeTimeout = o.transmissionAcknowledgeTimeout - opts.transmissionMaxRetransmit = o.transmissionMaxRetransmit -} - -// WithTransmission set options for (re)transmission for Confirmable message-s. -func WithTransmission(transmissionNStart time.Duration, - transmissionAcknowledgeTimeout time.Duration, - transmissionMaxRetransmit uint32) TransmissionOpt { - return TransmissionOpt{ - transmissionNStart: transmissionNStart, - transmissionAcknowledgeTimeout: transmissionAcknowledgeTimeout, - transmissionMaxRetransmit: transmissionMaxRetransmit, - } -} - -// CloseSocketOpt close socket option. -type CloseSocketOpt struct { -} - -func (o CloseSocketOpt) applyDial(opts *dialOptions) { - opts.closeSocket = true -} - -// WithCloseSocket closes socket at the close connection. -func WithCloseSocket() CloseSocketOpt { - return CloseSocketOpt{} -} - -// DialerOpt dialer option. -type DialerOpt struct { - dialer *net.Dialer -} - -func (o DialerOpt) applyDial(opts *dialOptions) { - if o.dialer != nil { - opts.dialer = o.dialer - } -} - -// WithDialer set dialer for dial. -func WithDialer(dialer *net.Dialer) DialerOpt { - return DialerOpt{ - dialer: dialer, - } -} - -// ConnectionCacheOpt network option. -type MessagePoolOpt struct { - messagePool *pool.Pool -} - -func (o MessagePoolOpt) apply(opts *serverOptions) { - opts.messagePool = o.messagePool -} - -func (o MessagePoolOpt) applyDial(opts *dialOptions) { - opts.messagePool = o.messagePool -} - -// WithMessagePool configure's message pool for acquire/releasing coap messages -func WithMessagePool(messagePool *pool.Pool) MessagePoolOpt { - return MessagePoolOpt{ - messagePool: messagePool, - } -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/server.go b/vendor/github.com/plgd-dev/go-coap/v2/udp/server.go deleted file mode 100644 index ad189bbd9ff..00000000000 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/server.go +++ /dev/null @@ -1,439 +0,0 @@ -package udp - -import ( - "context" - "errors" - "fmt" - "io" - "net" - "strings" - "sync" - "time" - - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" - coapNet "github.com/plgd-dev/go-coap/v2/net" - "github.com/plgd-dev/go-coap/v2/net/blockwise" - "github.com/plgd-dev/go-coap/v2/net/monitor/inactivity" - "github.com/plgd-dev/go-coap/v2/pkg/cache" - "github.com/plgd-dev/go-coap/v2/pkg/runner/periodic" - "github.com/plgd-dev/go-coap/v2/udp/client" - udpMessage "github.com/plgd-dev/go-coap/v2/udp/message" - "github.com/plgd-dev/go-coap/v2/udp/message/pool" - kitSync "github.com/plgd-dev/kit/v2/sync" -) - -// A ServerOption sets options such as credentials, codec and keepalive parameters, etc. -type ServerOption interface { - apply(*serverOptions) -} - -// The HandlerFunc type is an adapter to allow the use of -// ordinary functions as COAP handlers. -type HandlerFunc = func(*client.ResponseWriter, *pool.Message) - -type ErrorFunc = func(error) - -type GoPoolFunc = func(func()) error - -type BlockwiseFactoryFunc = func(getSentRequest func(token message.Token) (blockwise.Message, bool)) *blockwise.BlockWise - -type OnNewClientConnFunc = func(cc *client.ClientConn) - -type GetMIDFunc = func() uint16 - -var defaultServerOptions = func() serverOptions { - opts := serverOptions{ - ctx: context.Background(), - maxMessageSize: 64 * 1024, - errors: func(err error) { - fmt.Println(err) - }, - goPool: func(f func()) error { - go func() { - f() - }() - return nil - }, - createInactivityMonitor: func() inactivity.Monitor { - return inactivity.NewNilMonitor() - }, - blockwiseEnable: true, - blockwiseSZX: blockwise.SZX1024, - blockwiseTransferTimeout: time.Second * 3, - onNewClientConn: func(cc *client.ClientConn) {}, - transmissionNStart: time.Second, - transmissionAcknowledgeTimeout: time.Second * 2, - transmissionMaxRetransmit: 4, - getMID: udpMessage.GetMID, - periodicRunner: func(f func(now time.Time) bool) { - go func() { - for f(time.Now()) { - time.Sleep(4 * time.Second) - } - }() - }, - messagePool: pool.New(1024, 1600), - } - opts.handler = func(w *client.ResponseWriter, r *pool.Message) { - if err := w.SetResponse(codes.NotFound, message.TextPlain, nil); err != nil { - opts.errors(fmt.Errorf("udp server: cannot set response: %w", err)) - } - } - return opts -}() - -type serverOptions struct { - ctx context.Context - messagePool *pool.Pool - blockwiseTransferTimeout time.Duration - errors ErrorFunc - goPool GoPoolFunc - createInactivityMonitor func() inactivity.Monitor - periodicRunner periodic.Func - getMID GetMIDFunc - handler HandlerFunc - onNewClientConn OnNewClientConnFunc - transmissionNStart time.Duration - transmissionAcknowledgeTimeout time.Duration - maxMessageSize uint32 - transmissionMaxRetransmit uint32 - blockwiseEnable bool - blockwiseSZX blockwise.SZX -} - -type Server struct { - doneCtx context.Context - ctx context.Context - - listen *coapNet.UDPConn - - goPool GoPoolFunc - createInactivityMonitor func() inactivity.Monitor - periodicRunner periodic.Func - cache *cache.Cache - blockwiseTransferTimeout time.Duration - onNewClientConn OnNewClientConnFunc - transmissionNStart time.Duration - transmissionAcknowledgeTimeout time.Duration - - conns map[string]*client.ClientConn - getMID GetMIDFunc - - messagePool *pool.Pool - doneCancel context.CancelFunc - handler HandlerFunc - cancel context.CancelFunc - serverStartedChan chan struct{} - - multicastRequests *kitSync.Map - multicastHandler *client.HandlerContainer - - errors ErrorFunc - connsMutex sync.Mutex - listenMutex sync.Mutex - transmissionMaxRetransmit uint32 - maxMessageSize uint32 - blockwiseEnable bool - blockwiseSZX blockwise.SZX -} - -func NewServer(opt ...ServerOption) *Server { - opts := defaultServerOptions - for _, o := range opt { - o.apply(&opts) - } - - if opts.errors == nil { - opts.errors = func(error) { - // default no-op - } - } - - if opts.getMID == nil { - opts.getMID = udpMessage.GetMID - } - - if opts.createInactivityMonitor == nil { - opts.createInactivityMonitor = func() inactivity.Monitor { - return inactivity.NewNilMonitor() - } - } - if opts.messagePool == nil { - opts.messagePool = pool.New(0, 0) - } - - ctx, cancel := context.WithCancel(opts.ctx) - serverStartedChan := make(chan struct{}) - - doneCtx, doneCancel := context.WithCancel(context.Background()) - - return &Server{ - ctx: ctx, - cancel: cancel, - handler: opts.handler, - maxMessageSize: opts.maxMessageSize, - errors: func(err error) { - if errors.Is(err, context.Canceled) || errors.Is(err, io.EOF) || strings.Contains(err.Error(), "use of closed network connection") { - // this error was produced by cancellation context or closing connection. - return - } - opts.errors(fmt.Errorf("udp: %w", err)) - }, - goPool: opts.goPool, - createInactivityMonitor: opts.createInactivityMonitor, - blockwiseSZX: opts.blockwiseSZX, - blockwiseEnable: opts.blockwiseEnable, - blockwiseTransferTimeout: opts.blockwiseTransferTimeout, - multicastHandler: client.NewHandlerContainer(), - multicastRequests: kitSync.NewMap(), - serverStartedChan: serverStartedChan, - onNewClientConn: opts.onNewClientConn, - transmissionNStart: opts.transmissionNStart, - transmissionAcknowledgeTimeout: opts.transmissionAcknowledgeTimeout, - transmissionMaxRetransmit: opts.transmissionMaxRetransmit, - getMID: opts.getMID, - periodicRunner: opts.periodicRunner, - doneCtx: doneCtx, - doneCancel: doneCancel, - cache: cache.NewCache(), - messagePool: opts.messagePool, - - conns: make(map[string]*client.ClientConn), - } -} - -func (s *Server) checkAndSetListener(l *coapNet.UDPConn) error { - s.listenMutex.Lock() - defer s.listenMutex.Unlock() - if s.listen != nil { - return fmt.Errorf("server already serve: %v", s.listen.LocalAddr().String()) - } - s.listen = l - close(s.serverStartedChan) - return nil -} - -func (s *Server) closeConnection(cc *client.ClientConn) { - if err := cc.Close(); err != nil { - s.errors(fmt.Errorf("cannot close connection: %w", err)) - } -} - -func (s *Server) Serve(l *coapNet.UDPConn) error { - if s.blockwiseSZX > blockwise.SZX1024 { - return fmt.Errorf("invalid blockwiseSZX") - } - - err := s.checkAndSetListener(l) - if err != nil { - return err - } - - defer func() { - s.closeSessions() - s.doneCancel() - s.listenMutex.Lock() - defer s.listenMutex.Unlock() - s.listen = nil - s.serverStartedChan = make(chan struct{}, 1) - }() - - m := make([]byte, s.maxMessageSize) - var wg sync.WaitGroup - - s.periodicRunner(func(now time.Time) bool { - s.handleInactivityMonitors(now) - s.cache.CheckExpirations(now) - return s.ctx.Err() == nil - }) - - for { - buf := m - n, raddr, err := l.ReadWithContext(s.ctx, buf) - if err != nil { - wg.Wait() - - select { - case <-s.ctx.Done(): - return nil - default: - if coapNet.IsCancelOrCloseError(err) { - return nil - } - return err - } - } - buf = buf[:n] - cc := s.getClientConn(l, raddr) - err = cc.Process(buf) - if err != nil { - s.closeConnection(cc) - s.errors(fmt.Errorf("%v: %w", cc.RemoteAddr(), err)) - } - } -} - -func (s *Server) getListener() *coapNet.UDPConn { - s.listenMutex.Lock() - defer s.listenMutex.Unlock() - return s.listen -} - -// Stop stops server without wait of ends Serve function. -func (s *Server) Stop() { - s.cancel() - l := s.getListener() - if l != nil { - if errC := l.Close(); errC != nil { - s.errors(fmt.Errorf("cannot close listener: %w", errC)) - } - } - s.closeSessions() -} - -func (s *Server) closeSessions() { - s.connsMutex.Lock() - conns := s.conns - s.conns = make(map[string]*client.ClientConn) - s.connsMutex.Unlock() - for _, cc := range conns { - s.closeConnection(cc) - if closeFn := getClose(cc); closeFn != nil { - closeFn() - } - } -} - -func (s *Server) conn() *coapNet.UDPConn { - s.listenMutex.Lock() - serverStartedChan := s.serverStartedChan - s.listenMutex.Unlock() - select { - case <-serverStartedChan: - case <-s.ctx.Done(): - } - s.listenMutex.Lock() - defer s.listenMutex.Unlock() - return s.listen -} - -const closeKey = "gocoapCloseConnection" - -func (s *Server) getClientConns() []*client.ClientConn { - s.connsMutex.Lock() - defer s.connsMutex.Unlock() - conns := make([]*client.ClientConn, 0, 32) - for _, c := range s.conns { - conns = append(conns, c) - } - return conns -} - -func (s *Server) handleInactivityMonitors(now time.Time) { - for _, cc := range s.getClientConns() { - select { - case <-cc.Context().Done(): - if closeFn := getClose(cc); closeFn != nil { - closeFn() - } - continue - default: - cc.CheckExpirations(now) - } - } -} - -func getClose(cc *client.ClientConn) func() { - v := cc.Context().Value(closeKey) - if v == nil { - return nil - } - return v.(func()) -} - -func (s *Server) getOrCreateClientConn(UDPConn *coapNet.UDPConn, raddr *net.UDPAddr) (cc *client.ClientConn, created bool) { - s.connsMutex.Lock() - defer s.connsMutex.Unlock() - key := raddr.String() - cc = s.conns[key] - if cc == nil { - created = true - var blockWise *blockwise.BlockWise - if s.blockwiseEnable { - blockWise = blockwise.NewBlockWise( - bwCreateAcquireMessage(s.messagePool), - bwCreateReleaseMessage(s.messagePool), - s.blockwiseTransferTimeout, - s.errors, - false, - bwCreateHandlerFunc(s.messagePool, s.multicastRequests), - ) - } - obsHandler := client.NewHandlerContainer() - session := NewSession( - s.ctx, - UDPConn, - raddr, - s.maxMessageSize, - false, - s.doneCtx, - ) - monitor := s.createInactivityMonitor() - cc = client.NewClientConn( - session, - obsHandler, - s.multicastRequests, - s.transmissionNStart, - s.transmissionAcknowledgeTimeout, - s.transmissionMaxRetransmit, - client.NewObservationHandler(obsHandler, func(w *client.ResponseWriter, r *pool.Message) { - h, err := s.multicastHandler.Get(r.Token()) - if err == nil { - h(w, r) - return - } - s.handler(w, r) - }), - s.blockwiseSZX, - blockWise, - s.goPool, - s.errors, - s.getMID, - monitor, - s.cache, - s.messagePool, - ) - cc.SetContextValue(closeKey, func() { - if err := session.Close(); err != nil { - s.errors(fmt.Errorf("cannot close session: %w", err)) - } - session.shutdown() - }) - cc.AddOnClose(func() { - s.connsMutex.Lock() - defer s.connsMutex.Unlock() - delete(s.conns, key) - }) - s.conns[key] = cc - } - return cc, created -} - -func (s *Server) getClientConn(l *coapNet.UDPConn, raddr *net.UDPAddr) *client.ClientConn { - cc, created := s.getOrCreateClientConn(l, raddr) - if created { - if s.onNewClientConn != nil { - s.onNewClientConn(cc) - } - } - return cc -} - -func (s *Server) NewClientConn(addr *net.UDPAddr) (*client.ClientConn, error) { - l := s.getListener() - if l == nil { - return nil, fmt.Errorf("server is not running") - } - return s.getClientConn(l, addr), nil -} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/.codecov.yml b/vendor/github.com/plgd-dev/go-coap/v3/.codecov.yml similarity index 82% rename from vendor/github.com/plgd-dev/go-coap/v2/.codecov.yml rename to vendor/github.com/plgd-dev/go-coap/v3/.codecov.yml index 5bb8d88fced..3f1b1a655ad 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/.codecov.yml +++ b/vendor/github.com/plgd-dev/go-coap/v3/.codecov.yml @@ -1,3 +1,4 @@ ignore: - "examples" - "**/*_test.go" + - "v3" diff --git a/vendor/github.com/plgd-dev/go-coap/v2/.gitignore b/vendor/github.com/plgd-dev/go-coap/v3/.gitignore similarity index 98% rename from vendor/github.com/plgd-dev/go-coap/v2/.gitignore rename to vendor/github.com/plgd-dev/go-coap/v3/.gitignore index 8826cb82d27..8d21c950b91 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/.gitignore +++ b/vendor/github.com/plgd-dev/go-coap/v3/.gitignore @@ -10,6 +10,7 @@ server client !client/ vendor/ +v3/ # Test binary, build with `go test -c` *.test diff --git a/vendor/github.com/plgd-dev/go-coap/v2/.golangci.yml b/vendor/github.com/plgd-dev/go-coap/v3/.golangci.yml similarity index 67% rename from vendor/github.com/plgd-dev/go-coap/v2/.golangci.yml rename to vendor/github.com/plgd-dev/go-coap/v3/.golangci.yml index 787aad2c41e..2d5d6ae8fad 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/.golangci.yml +++ b/vendor/github.com/plgd-dev/go-coap/v3/.golangci.yml @@ -6,64 +6,62 @@ 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 + - 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 + - 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`. + - 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 + - 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 - # - gocyclo # Computes and checks the cyclomatic complexity of functions + - 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 + - gocyclo # Computes and checks the cyclomatic complexity of functions # - 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. + - 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 + - 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. + - 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. + - 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 - nolintlint # Reports ill-formed or insufficient nolint directives # - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - predeclared # find code that shadows one of Go's predeclared identifiers - # - revive # golint replacement, finds style mistakes + - 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 # - 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 + - 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: @@ -93,6 +91,35 @@ linters: - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! -# issues: +issues: + # Excluding configuration per-path, per-linter, per-text and per-source + exclude-rules: + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - dupl + - forcetypeassert + - gosec + - gocyclo + - gocognit + + - path: ^test/.*\.go + linters: + - dupl + - forcetypeassert + - gosec + - gocyclo + - gocognit + + - path: example_test\.go + text: "exitAfterDefer" + linters: + - gocritic + + - path: pkg/rand/rand.go + text: "G404: Use of weak random number generator \\(math/rand instead of crypto/rand\\)" + linters: + - gosec + # # Fix found issues (if it's supported by the linter). # fix: true diff --git a/vendor/github.com/plgd-dev/go-coap/v2/LICENSE b/vendor/github.com/plgd-dev/go-coap/v3/LICENSE similarity index 100% rename from vendor/github.com/plgd-dev/go-coap/v2/LICENSE rename to vendor/github.com/plgd-dev/go-coap/v3/LICENSE diff --git a/vendor/github.com/plgd-dev/go-coap/v2/README.md b/vendor/github.com/plgd-dev/go-coap/v3/README.md similarity index 85% rename from vendor/github.com/plgd-dev/go-coap/v2/README.md rename to vendor/github.com/plgd-dev/go-coap/v3/README.md index 24bc02258b1..65e6d794e45 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/README.md +++ b/vendor/github.com/plgd-dev/go-coap/v3/README.md @@ -1,14 +1,14 @@ # Go-CoAP -[![Build Status](https://travis-ci.com/plgd-dev/go-coap.svg?branch=master)](https://travis-ci.com/plgd-dev/go-coap) -[![codecov](https://codecov.io/gh/plgd-dev/go-coap/branch/master/graph/badge.svg)](https://codecov.io/gh/plgd-dev/go-coap) +[![Build Status](https://github.com/plgd-dev/go-coap/workflows/Test/badge.svg)](https://github.com/plgd-dev/go-coap/actions?query=workflow%3ATest) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=plgd-dev_go-coap&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=plgd-dev_go-coap) +[![Coverage](https://img.shields.io/sonar/coverage/plgd-dev_go-coap?server=https%3A%2F%2Fsonarcloud.io)](https://sonarcloud.io/summary/overall?id=plgd-dev_go-coap) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fplgd-dev%2Fgo-coap.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fplgd-dev%2Fgo-coap?ref=badge_shield) [![sponsors](https://opencollective.com/go-coap/sponsors/badge.svg)](https://opencollective.com/go-coap#sponsors) [![contributors](https://img.shields.io/github/contributors/plgd-dev/go-coap)](https://github.com/plgd-dev/go-coap/graphs/contributors) [![GitHub stars](https://img.shields.io/github/stars/plgd-dev/go-coap)](https://github.com/plgd-dev/go-coap/stargazers) [![GitHub license](https://img.shields.io/github/license/plgd-dev/go-coap)](https://github.com/plgd-dev/go-coap/blob/master/LICENSE) -[![GoDoc](https://godoc.org/github.com/plgd-dev/go-coap?status.svg)](https://godoc.org/github.com/plgd-dev/go-coap) -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=plgd-dev_go-coap&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=plgd-dev_go-coap) +[![GoDoc](https://pkg.go.dev/badge/github.com/plgd-dev/go-coap/v3?utm_source=godoc)](https://pkg.go.dev/github.com/plgd-dev/go-coap/v3?utm_source=godoc) The Constrained Application Protocol (CoAP) is a specialized web transfer protocol for use with constrained nodes and constrained networks in the Internet of Things. @@ -26,6 +26,7 @@ The go-coap provides servers and clients for DTLS, TCP-TLS, UDP, TCP in golang l * multicast * CoAP NoResponse option in CoAP [RFC 7967][coap-noresponse] * CoAP over DTLS [pion/dtls][pion-dtls] +* Too many requests response code [RFC 8516][coap-429] [coap]: http://tools.ietf.org/html/rfc7252 [coap-tcp]: https://tools.ietf.org/html/rfc8323 @@ -33,6 +34,11 @@ The go-coap provides servers and clients for DTLS, TCP-TLS, UDP, TCP in golang l [coap-observe]: https://tools.ietf.org/html/rfc7641 [coap-noresponse]: https://tools.ietf.org/html/rfc7967 [pion-dtls]: https://github.com/pion/dtls +[coap-429]: https://datatracker.ietf.org/doc/html/rfc8516 + +## Requirements + +* Go 1.18 or higher ## Samples @@ -46,7 +52,7 @@ The go-coap provides servers and clients for DTLS, TCP-TLS, UDP, TCP in golang l // Middleware function, which will be called for each request. func loggingMiddleware(next mux.Handler) mux.Handler { return mux.HandlerFunc(func(w mux.ResponseWriter, r *mux.Message) { - log.Printf("ClientAddress %v, %v\n", w.Client().RemoteAddr(), r.String()) + log.Printf("ClientAddress %v, %v\n", w.Conn().RemoteAddr(), r.String()) next.ServeCOAP(w, r) }) } @@ -122,15 +128,6 @@ The go-coap provides servers and clients for DTLS, TCP-TLS, UDP, TCP in golang l [Client](examples/mcast/client/main.go) example. -## Contributing - -In order to run the tests that the CI will run locally, the following two commands can be used to build the Docker image and run the tests. When making changes, these are the tests that the CI will run, so please make sure that the tests work locally before committing. - -```shell -docker build . --network=host -t go-coap:build --target build -docker run --mount type=bind,source="$(pwd)",target=/shared,readonly --network=host go-coap:build go test './...' -``` - ## License Apache 2.0 diff --git a/vendor/github.com/plgd-dev/go-coap/v3/dtls/client.go b/vendor/github.com/plgd-dev/go-coap/v3/dtls/client.go new file mode 100644 index 00000000000..e2075f364aa --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/dtls/client.go @@ -0,0 +1,127 @@ +package dtls + +import ( + "fmt" + "time" + + "github.com/pion/dtls/v2" + dtlsnet "github.com/pion/dtls/v2/pkg/net" + "github.com/plgd-dev/go-coap/v3/dtls/server" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + coapNet "github.com/plgd-dev/go-coap/v3/net" + "github.com/plgd-dev/go-coap/v3/net/blockwise" + "github.com/plgd-dev/go-coap/v3/net/monitor/inactivity" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" + "github.com/plgd-dev/go-coap/v3/options" + "github.com/plgd-dev/go-coap/v3/udp" + udpClient "github.com/plgd-dev/go-coap/v3/udp/client" +) + +var DefaultConfig = func() udpClient.Config { + cfg := udpClient.DefaultConfig + cfg.Handler = func(w *responsewriter.ResponseWriter[*udpClient.Conn], r *pool.Message) { + switch r.Code() { + case codes.POST, codes.PUT, codes.GET, codes.DELETE: + if err := w.SetResponse(codes.NotFound, message.TextPlain, nil); err != nil { + cfg.Errors(fmt.Errorf("dtls client: cannot set response: %w", err)) + } + } + } + return cfg +}() + +// Dial creates a client connection to the given target. +func Dial(target string, dtlsCfg *dtls.Config, opts ...udp.Option) (*udpClient.Conn, error) { + cfg := DefaultConfig + for _, o := range opts { + o.UDPClientApply(&cfg) + } + + c, err := cfg.Dialer.DialContext(cfg.Ctx, cfg.Net, target) + if err != nil { + return nil, err + } + + conn, err := dtls.Client(dtlsnet.PacketConnFromConn(c), c.RemoteAddr(), dtlsCfg) + if err != nil { + return nil, err + } + opts = append(opts, options.WithCloseSocket()) + return Client(conn, opts...), nil +} + +// Client creates client over dtls connection. +func Client(conn *dtls.Conn, opts ...udp.Option) *udpClient.Conn { + cfg := DefaultConfig + for _, o := range opts { + o.UDPClientApply(&cfg) + } + if cfg.Errors == nil { + cfg.Errors = func(error) { + // default no-op + } + } + if cfg.CreateInactivityMonitor == nil { + cfg.CreateInactivityMonitor = func() udpClient.InactivityMonitor { + return inactivity.NewNilMonitor[*udpClient.Conn]() + } + } + if cfg.MessagePool == nil { + cfg.MessagePool = pool.New(0, 0) + } + errorsFunc := cfg.Errors + cfg.Errors = func(err error) { + if coapNet.IsCancelOrCloseError(err) { + // this error was produced by cancellation context or closing connection. + return + } + errorsFunc(fmt.Errorf("dtls: %v: %w", conn.RemoteAddr(), err)) + } + + createBlockWise := func(cc *udpClient.Conn) *blockwise.BlockWise[*udpClient.Conn] { + return nil + } + if cfg.BlockwiseEnable { + createBlockWise = func(cc *udpClient.Conn) *blockwise.BlockWise[*udpClient.Conn] { + v := cc + return blockwise.New( + v, + cfg.BlockwiseTransferTimeout, + cfg.Errors, + func(token message.Token) (*pool.Message, bool) { + return v.GetObservationRequest(token) + }, + ) + } + } + + monitor := cfg.CreateInactivityMonitor() + l := coapNet.NewConn(conn) + session := server.NewSession(cfg.Ctx, + l, + cfg.MaxMessageSize, + cfg.MTU, + cfg.CloseSocket, + ) + cc := udpClient.NewConn(session, + createBlockWise, + monitor, + &cfg, + ) + + cfg.PeriodicRunner(func(now time.Time) bool { + cc.CheckExpirations(now) + return cc.Context().Err() == nil + }) + + go func() { + err := cc.Run() + if err != nil { + cfg.Errors(err) + } + }() + + return cc +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/dtls/server.go b/vendor/github.com/plgd-dev/go-coap/v3/dtls/server.go new file mode 100644 index 00000000000..881c844dad8 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/dtls/server.go @@ -0,0 +1,7 @@ +package dtls + +import "github.com/plgd-dev/go-coap/v3/dtls/server" + +func NewServer(opt ...server.Option) *server.Server { + return server.New(opt...) +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/dtls/server/config.go b/vendor/github.com/plgd-dev/go-coap/v3/dtls/server/config.go new file mode 100644 index 00000000000..dba7297a7d5 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/dtls/server/config.go @@ -0,0 +1,64 @@ +package server + +import ( + "fmt" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + "github.com/plgd-dev/go-coap/v3/net/monitor/inactivity" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" + "github.com/plgd-dev/go-coap/v3/options/config" + udpClient "github.com/plgd-dev/go-coap/v3/udp/client" +) + +// The HandlerFunc type is an adapter to allow the use of +// ordinary functions as COAP handlers. +type HandlerFunc = func(*responsewriter.ResponseWriter[*udpClient.Conn], *pool.Message) + +type ErrorFunc = func(error) + +// OnNewConnFunc is the callback for new connections. +type OnNewConnFunc = func(cc *udpClient.Conn) + +type GetMIDFunc = func() int32 + +var DefaultConfig = func() Config { + opts := Config{ + Common: config.NewCommon[*udpClient.Conn](), + CreateInactivityMonitor: func() udpClient.InactivityMonitor { + timeout := time.Second * 16 + onInactive := func(cc *udpClient.Conn) { + _ = cc.Close() + } + return inactivity.New(timeout, onInactive) + }, + OnNewConn: func(cc *udpClient.Conn) { + // do nothing by default + }, + TransmissionNStart: 1, + TransmissionAcknowledgeTimeout: time.Second * 2, + TransmissionMaxRetransmit: 4, + GetMID: message.GetMID, + MTU: udpClient.DefaultMTU, + } + opts.Handler = func(w *responsewriter.ResponseWriter[*udpClient.Conn], r *pool.Message) { + if err := w.SetResponse(codes.NotFound, message.TextPlain, nil); err != nil { + opts.Errors(fmt.Errorf("dtls server: cannot set response: %w", err)) + } + } + return opts +}() + +type Config struct { + config.Common[*udpClient.Conn] + CreateInactivityMonitor func() udpClient.InactivityMonitor + GetMID GetMIDFunc + Handler HandlerFunc + OnNewConn OnNewConnFunc + TransmissionNStart uint32 + TransmissionAcknowledgeTimeout time.Duration + TransmissionMaxRetransmit uint32 + MTU uint16 +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/dtls/server/server.go b/vendor/github.com/plgd-dev/go-coap/v3/dtls/server/server.go new file mode 100644 index 00000000000..01587f854dc --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/dtls/server/server.go @@ -0,0 +1,231 @@ +package server + +import ( + "context" + "errors" + "fmt" + "net" + "sync" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/pool" + coapNet "github.com/plgd-dev/go-coap/v3/net" + "github.com/plgd-dev/go-coap/v3/net/blockwise" + "github.com/plgd-dev/go-coap/v3/net/monitor/inactivity" + "github.com/plgd-dev/go-coap/v3/pkg/connections" + udpClient "github.com/plgd-dev/go-coap/v3/udp/client" +) + +// Listener defined used by coap +type Listener interface { + Close() error + AcceptWithContext(ctx context.Context) (net.Conn, error) +} + +type Server struct { + ctx context.Context + cancel context.CancelFunc + cfg *Config + + listenMutex sync.Mutex + listen Listener +} + +// A Option sets options such as credentials, codec and keepalive parameters, etc. +type Option interface { + DTLSServerApply(cfg *Config) +} + +func New(opt ...Option) *Server { + cfg := DefaultConfig + for _, o := range opt { + o.DTLSServerApply(&cfg) + } + + ctx, cancel := context.WithCancel(cfg.Ctx) + if cfg.Errors == nil { + cfg.Errors = func(error) { + // default no-op + } + } + + if cfg.GetMID == nil { + cfg.GetMID = message.GetMID + } + + if cfg.GetToken == nil { + cfg.GetToken = message.GetToken + } + + if cfg.CreateInactivityMonitor == nil { + cfg.CreateInactivityMonitor = func() udpClient.InactivityMonitor { + return inactivity.NewNilMonitor[*udpClient.Conn]() + } + } + if cfg.MessagePool == nil { + cfg.MessagePool = pool.New(0, 0) + } + + errorsFunc := cfg.Errors + // assign updated func to cfg.errors so cfg.handler also uses the updated error handler + cfg.Errors = func(err error) { + if coapNet.IsCancelOrCloseError(err) { + // this error was produced by cancellation context or closing connection. + return + } + errorsFunc(fmt.Errorf("dtls: %w", err)) + } + + return &Server{ + ctx: ctx, + cancel: cancel, + cfg: &cfg, + } +} + +func (s *Server) checkAndSetListener(l Listener) error { + s.listenMutex.Lock() + defer s.listenMutex.Unlock() + if s.listen != nil { + return fmt.Errorf("server already serve listener") + } + s.listen = l + return nil +} + +func (s *Server) checkAcceptError(err error) bool { + if err == nil { + return true + } + switch { + case errors.Is(err, coapNet.ErrListenerIsClosed): + s.Stop() + return false + case errors.Is(err, context.DeadlineExceeded), errors.Is(err, context.Canceled): + select { + case <-s.ctx.Done(): + default: + s.cfg.Errors(fmt.Errorf("cannot accept connection: %w", err)) + return true + } + return false + default: + return true + } +} + +func (s *Server) serveConnection(connections *connections.Connections, cc *udpClient.Conn) { + connections.Store(cc) + defer connections.Delete(cc) + + if err := cc.Run(); err != nil { + s.cfg.Errors(fmt.Errorf("%v: %w", cc.RemoteAddr(), err)) + } +} + +func (s *Server) Serve(l Listener) error { + if s.cfg.BlockwiseSZX > blockwise.SZX1024 { + return fmt.Errorf("invalid blockwiseSZX") + } + err := s.checkAndSetListener(l) + if err != nil { + return err + } + defer func() { + s.listenMutex.Lock() + defer s.listenMutex.Unlock() + s.listen = nil + }() + + var wg sync.WaitGroup + defer wg.Wait() + + connections := connections.New() + s.cfg.PeriodicRunner(func(now time.Time) bool { + connections.CheckExpirations(now) + return s.ctx.Err() == nil + }) + defer connections.Close() + + for { + rw, err := l.AcceptWithContext(s.ctx) + if ok := s.checkAcceptError(err); !ok { + return nil + } + if rw == nil { + continue + } + wg.Add(1) + var cc *udpClient.Conn + monitor := s.cfg.CreateInactivityMonitor() + cc = s.createConn(coapNet.NewConn(rw), monitor) + if s.cfg.OnNewConn != nil { + s.cfg.OnNewConn(cc) + } + go func() { + defer wg.Done() + s.serveConnection(connections, cc) + }() + } +} + +// Stop stops server without wait of ends Serve function. +func (s *Server) Stop() { + s.cancel() + s.listenMutex.Lock() + l := s.listen + s.listen = nil + s.listenMutex.Unlock() + if l != nil { + if err := l.Close(); err != nil { + s.cfg.Errors(fmt.Errorf("cannot close listener: %w", err)) + } + } +} + +func (s *Server) createConn(connection *coapNet.Conn, monitor udpClient.InactivityMonitor) *udpClient.Conn { + createBlockWise := func(cc *udpClient.Conn) *blockwise.BlockWise[*udpClient.Conn] { + return nil + } + if s.cfg.BlockwiseEnable { + createBlockWise = func(cc *udpClient.Conn) *blockwise.BlockWise[*udpClient.Conn] { + v := cc + return blockwise.New( + v, + s.cfg.BlockwiseTransferTimeout, + s.cfg.Errors, + func(token message.Token) (*pool.Message, bool) { + return v.GetObservationRequest(token) + }, + ) + } + } + session := NewSession( + s.ctx, + connection, + s.cfg.MaxMessageSize, + s.cfg.MTU, + true, + ) + cfg := udpClient.DefaultConfig + cfg.TransmissionNStart = s.cfg.TransmissionNStart + cfg.TransmissionAcknowledgeTimeout = s.cfg.TransmissionAcknowledgeTimeout + cfg.TransmissionMaxRetransmit = s.cfg.TransmissionMaxRetransmit + cfg.Handler = s.cfg.Handler + cfg.BlockwiseSZX = s.cfg.BlockwiseSZX + cfg.Errors = s.cfg.Errors + cfg.GetMID = s.cfg.GetMID + cfg.GetToken = s.cfg.GetToken + cfg.MessagePool = s.cfg.MessagePool + cfg.ReceivedMessageQueueSize = s.cfg.ReceivedMessageQueueSize + cfg.ProcessReceivedMessage = s.cfg.ProcessReceivedMessage + cc := udpClient.NewConn( + session, + createBlockWise, + monitor, + &cfg, + ) + + return cc +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/dtls/session.go b/vendor/github.com/plgd-dev/go-coap/v3/dtls/server/session.go similarity index 76% rename from vendor/github.com/plgd-dev/go-coap/v2/dtls/session.go rename to vendor/github.com/plgd-dev/go-coap/v3/dtls/server/session.go index 653c7bda354..9eb2d001a56 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/dtls/session.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/dtls/server/session.go @@ -1,4 +1,4 @@ -package dtls +package server import ( "context" @@ -8,9 +8,10 @@ import ( "sync" "sync/atomic" - coapNet "github.com/plgd-dev/go-coap/v2/net" - "github.com/plgd-dev/go-coap/v2/udp/client" - "github.com/plgd-dev/go-coap/v2/udp/message/pool" + "github.com/plgd-dev/go-coap/v3/message/pool" + coapNet "github.com/plgd-dev/go-coap/v3/net" + "github.com/plgd-dev/go-coap/v3/udp/client" + "github.com/plgd-dev/go-coap/v3/udp/coder" ) type EventFunc = func() @@ -18,7 +19,7 @@ type EventFunc = func() type Session struct { onClose []EventFunc - ctx atomic.Value + ctx atomic.Value // TODO: change to atomic.Pointer[context.Context] for go1.19 cancel context.CancelFunc connection *coapNet.Conn @@ -29,6 +30,8 @@ type Session struct { maxMessageSize uint32 + mtu uint16 + closeSocket bool } @@ -36,6 +39,7 @@ func NewSession( ctx context.Context, connection *coapNet.Conn, maxMessageSize uint32, + mtu uint16, closeSocket bool, ) *Session { ctx, cancel := context.WithCancel(ctx) @@ -44,6 +48,7 @@ func NewSession( connection: connection, maxMessageSize: maxMessageSize, closeSocket: closeSocket, + mtu: mtu, done: make(chan struct{}), } s.ctx.Store(&ctx) @@ -85,19 +90,17 @@ func (s *Session) Close() error { } func (s *Session) Context() context.Context { - return *s.ctx.Load().(*context.Context) + return *s.ctx.Load().(*context.Context) //nolint:forcetypeassert } // SetContextValue stores the value associated with key to context of connection. func (s *Session) SetContextValue(key interface{}, val interface{}) { - s.mutex.Lock() - defer s.mutex.Unlock() ctx := context.WithValue(s.Context(), key, val) s.ctx.Store(&ctx) } func (s *Session) WriteMessage(req *pool.Message) error { - data, err := req.Marshal() + data, err := req.MarshalWithEncoder(coder.DefaultCoder) if err != nil { return fmt.Errorf("cannot marshal: %w", err) } @@ -110,7 +113,7 @@ func (s *Session) WriteMessage(req *pool.Message) error { // WriteMulticastMessage sends multicast to the remote multicast address. // Currently it is not implemented - is is just satisfy golang udp/client/Session interface. -func (s *Session) WriteMulticastMessage(req *pool.Message, address *net.UDPAddr, opts ...coapNet.MulticastOption) error { +func (s *Session) WriteMulticastMessage(*pool.Message, *net.UDPAddr, ...coapNet.MulticastOption) error { return errors.New("multicast messages not implemented for DTLS") } @@ -127,7 +130,7 @@ func (s *Session) LocalAddr() net.Addr { } // Run reads and process requests from a connection, until the connection is not closed. -func (s *Session) Run(cc *client.ClientConn) (err error) { +func (s *Session) Run(cc *client.Conn) (err error) { defer func() { err1 := s.Close() if err == nil { @@ -135,7 +138,7 @@ func (s *Session) Run(cc *client.ClientConn) (err error) { } s.shutdown() }() - m := make([]byte, s.maxMessageSize) + m := make([]byte, s.mtu) for { readBuf := m readLen, err := s.connection.ReadWithContext(s.Context(), readBuf) @@ -149,3 +152,8 @@ func (s *Session) Run(cc *client.ClientConn) (err error) { } } } + +// NetConn returns the underlying connection that is wrapped by s. The Conn returned is shared by all invocations of NetConn, so do not modify it. +func (s *Session) NetConn() net.Conn { + return s.connection.NetConn() +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/message/codes/code_string.go b/vendor/github.com/plgd-dev/go-coap/v3/message/codes/code_string.go similarity index 97% rename from vendor/github.com/plgd-dev/go-coap/v2/message/codes/code_string.go rename to vendor/github.com/plgd-dev/go-coap/v3/message/codes/code_string.go index 1ef9f30e950..79f248a756d 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/message/codes/code_string.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/message/codes/code_string.go @@ -26,6 +26,7 @@ var codeToString = map[Code]string{ PreconditionFailed: "PreconditionFailed", RequestEntityTooLarge: "RequestEntityTooLarge", UnsupportedMediaType: "UnsupportedMediaType", + TooManyRequests: "TooManyRequests", InternalServerError: "InternalServerError", NotImplemented: "NotImplemented", BadGateway: "BadGateway", diff --git a/vendor/github.com/plgd-dev/go-coap/v2/message/codes/codes.go b/vendor/github.com/plgd-dev/go-coap/v3/message/codes/codes.go similarity index 96% rename from vendor/github.com/plgd-dev/go-coap/v2/message/codes/codes.go rename to vendor/github.com/plgd-dev/go-coap/v3/message/codes/codes.go index a97842fa303..62e3d8c38c3 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/message/codes/codes.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/message/codes/codes.go @@ -36,6 +36,7 @@ const ( PreconditionFailed Code = 140 RequestEntityTooLarge Code = 141 UnsupportedMediaType Code = 143 + TooManyRequests Code = 157 InternalServerError Code = 160 NotImplemented Code = 161 BadGateway Code = 162 @@ -44,7 +45,7 @@ const ( ProxyingNotSupported Code = 165 ) -//Signaling Codes for TCP +// Signaling Codes for TCP const ( CSM Code = 225 Ping Code = 226 @@ -75,6 +76,7 @@ var strToCode = map[string]Code{ `"PreconditionFailed"`: PreconditionFailed, `"RequestEntityTooLarge"`: RequestEntityTooLarge, `"UnsupportedMediaType"`: UnsupportedMediaType, + `"TooManyRequests"`: TooManyRequests, `"InternalServerError"`: InternalServerError, `"NotImplemented"`: NotImplemented, `"BadGateway"`: BadGateway, diff --git a/vendor/github.com/plgd-dev/go-coap/v2/message/encodeDecodeUint32.go b/vendor/github.com/plgd-dev/go-coap/v3/message/encodeDecodeUint32.go similarity index 95% rename from vendor/github.com/plgd-dev/go-coap/v2/message/encodeDecodeUint32.go rename to vendor/github.com/plgd-dev/go-coap/v3/message/encodeDecodeUint32.go index 0d23de25462..51355c39b3e 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/message/encodeDecodeUint32.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/message/encodeDecodeUint32.go @@ -25,7 +25,7 @@ func EncodeUint32(buf []byte, value uint32) (int, error) { return 3, ErrTooSmall } rv := make([]byte, 4) - binary.BigEndian.PutUint32(rv[:], value) + binary.BigEndian.PutUint32(rv, value) copy(buf, rv[1:]) return 3, nil default: diff --git a/vendor/github.com/plgd-dev/go-coap/v2/message/error.go b/vendor/github.com/plgd-dev/go-coap/v3/message/error.go similarity index 100% rename from vendor/github.com/plgd-dev/go-coap/v2/message/error.go rename to vendor/github.com/plgd-dev/go-coap/v3/message/error.go diff --git a/vendor/github.com/plgd-dev/go-coap/v2/message/getETag.go b/vendor/github.com/plgd-dev/go-coap/v3/message/getETag.go similarity index 93% rename from vendor/github.com/plgd-dev/go-coap/v2/message/getETag.go rename to vendor/github.com/plgd-dev/go-coap/v3/message/getETag.go index b261103023d..0b096230342 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/message/getETag.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/message/getETag.go @@ -39,6 +39,6 @@ func GetETag(r io.ReadSeeker) ([]byte, error) { return nil, err } b := make([]byte, 8) - binary.LittleEndian.PutUint64(b, c64.Sum64()) + binary.BigEndian.PutUint64(b, c64.Sum64()) return b, nil } diff --git a/vendor/github.com/plgd-dev/go-coap/v2/message/getToken.go b/vendor/github.com/plgd-dev/go-coap/v3/message/getToken.go similarity index 100% rename from vendor/github.com/plgd-dev/go-coap/v2/message/getToken.go rename to vendor/github.com/plgd-dev/go-coap/v3/message/getToken.go diff --git a/vendor/github.com/plgd-dev/go-coap/v3/message/getmid.go b/vendor/github.com/plgd-dev/go-coap/v3/message/getmid.go new file mode 100644 index 00000000000..e049381e4af --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/message/getmid.go @@ -0,0 +1,35 @@ +package message + +import ( + "crypto/rand" + "encoding/binary" + "math" + "sync/atomic" + "time" + + pkgRand "github.com/plgd-dev/go-coap/v3/pkg/rand" +) + +var weakRng = pkgRand.NewRand(time.Now().UnixNano()) + +var msgID = uint32(RandMID()) + +// GetMID generates a message id for UDP. (0 <= mid <= 65535) +func GetMID() int32 { + return int32(uint16(atomic.AddUint32(&msgID, 1))) +} + +func RandMID() int32 { + b := make([]byte, 4) + _, err := rand.Read(b) + if err != nil { + // fallback to cryptographically insecure pseudo-random generator + return int32(uint16(weakRng.Uint32() >> 16)) + } + return int32(uint16(binary.BigEndian.Uint32(b))) +} + +// ValidateMID validates a message id for UDP. (0 <= mid <= 65535) +func ValidateMID(mid int32) bool { + return mid >= 0 && mid <= math.MaxUint16 +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/message/message.go b/vendor/github.com/plgd-dev/go-coap/v3/message/message.go similarity index 52% rename from vendor/github.com/plgd-dev/go-coap/v2/message/message.go rename to vendor/github.com/plgd-dev/go-coap/v3/message/message.go index f64e2b023b9..c68ce5f36d2 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/message/message.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/message/message.go @@ -1,11 +1,9 @@ package message import ( - "context" "fmt" - "io" - "github.com/plgd-dev/go-coap/v2/message/codes" + "github.com/plgd-dev/go-coap/v3/message/codes" ) // MaxTokenSize maximum of token size that can be used in message @@ -15,13 +13,17 @@ type Message struct { Token Token Options Options Code codes.Code - // Context context of request. - Context context.Context - // Body of message. It is nil for message without body. - Body io.ReadSeeker + Payload []byte + + // For DTLS and UDP messages + MessageID int32 // uint16 is valid, all other values are invalid, -1 is used for unset + Type Type // uint8 is valid, all other values are invalid, -1 is used for unset } func (r *Message) String() string { + if r == nil { + return "nil" + } buf := fmt.Sprintf("Code: %v, Token: %v", r.Code, r.Token) path, err := r.Options.Path() if err == nil { @@ -35,5 +37,14 @@ func (r *Message) String() string { if err == nil { buf = fmt.Sprintf("%s, Queries: %+v", buf, queries) } + if ValidateType(r.Type) { + buf = fmt.Sprintf("%s, Type: %v", buf, r.Type) + } + if ValidateMID(r.MessageID) { + buf = fmt.Sprintf("%s, MessageID: %v", buf, r.MessageID) + } + if len(r.Payload) > 0 { + buf = fmt.Sprintf("%s, PayloadLen: %v", buf, len(r.Payload)) + } return buf } diff --git a/vendor/github.com/plgd-dev/go-coap/v2/message/noresponse/error.go b/vendor/github.com/plgd-dev/go-coap/v3/message/noresponse/error.go similarity index 100% rename from vendor/github.com/plgd-dev/go-coap/v2/message/noresponse/error.go rename to vendor/github.com/plgd-dev/go-coap/v3/message/noresponse/error.go diff --git a/vendor/github.com/plgd-dev/go-coap/v2/message/noresponse/noresponse.go b/vendor/github.com/plgd-dev/go-coap/v3/message/noresponse/noresponse.go similarity index 96% rename from vendor/github.com/plgd-dev/go-coap/v2/message/noresponse/noresponse.go rename to vendor/github.com/plgd-dev/go-coap/v3/message/noresponse/noresponse.go index 5f4fe06299d..8bd0e52dec6 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/message/noresponse/noresponse.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/message/noresponse/noresponse.go @@ -1,7 +1,7 @@ package noresponse import ( - "github.com/plgd-dev/go-coap/v2/message/codes" + "github.com/plgd-dev/go-coap/v3/message/codes" ) var ( diff --git a/vendor/github.com/plgd-dev/go-coap/v2/message/option.go b/vendor/github.com/plgd-dev/go-coap/v3/message/option.go similarity index 87% rename from vendor/github.com/plgd-dev/go-coap/v2/message/option.go rename to vendor/github.com/plgd-dev/go-coap/v3/message/option.go index 3232e846b63..1a692f12c96 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/message/option.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/message/option.go @@ -161,7 +161,7 @@ type MediaType uint16 // Content formats. var ( - TextPlain MediaType = 0 // text/plain;charset=utf-8 + TextPlain MediaType // text/plain;charset=utf-8 AppCoseEncrypt0 MediaType = 16 // application/cose; cose-type="cose-encrypt0" (RFC 8152) AppCoseMac0 MediaType = 17 // application/cose; cose-type="cose-mac0" (RFC 8152) AppCoseSign1 MediaType = 18 // application/cose; cose-type="cose-sign1" (RFC 8152) @@ -170,19 +170,24 @@ var ( AppOctets MediaType = 42 // application/octet-stream AppExi MediaType = 47 // application/exi AppJSON MediaType = 50 // application/json - AppJSONPatch MediaType = 51 //application/json-patch+json (RFC6902) - AppJSONMergePatch MediaType = 52 //application/merge-patch+json (RFC7396) - AppCBOR MediaType = 60 //application/cbor (RFC 7049) - AppCWT MediaType = 61 //application/cwt - AppCoseEncrypt MediaType = 96 //application/cose; cose-type="cose-encrypt" (RFC 8152) - AppCoseMac MediaType = 97 //application/cose; cose-type="cose-mac" (RFC 8152) - AppCoseSign MediaType = 98 //application/cose; cose-type="cose-sign" (RFC 8152) - AppCoseKey MediaType = 101 //application/cose-key (RFC 8152) - AppCoseKeySet MediaType = 102 //application/cose-key-set (RFC 8152) - AppCoapGroup MediaType = 256 //coap-group+json (RFC 7390) - AppOcfCbor MediaType = 10000 //application/vnd.ocf+cbor - AppLwm2mTLV MediaType = 11542 //application/vnd.oma.lwm2m+tlv - AppLwm2mJSON MediaType = 11543 //application/vnd.oma.lwm2m+json + AppJSONPatch MediaType = 51 // application/json-patch+json (RFC6902) + AppJSONMergePatch MediaType = 52 // application/merge-patch+json (RFC7396) + AppCBOR MediaType = 60 // application/cbor (RFC 7049) + AppCWT MediaType = 61 // application/cwt + AppCoseEncrypt MediaType = 96 // application/cose; cose-type="cose-encrypt" (RFC 8152) + AppCoseMac MediaType = 97 // application/cose; cose-type="cose-mac" (RFC 8152) + AppCoseSign MediaType = 98 // application/cose; cose-type="cose-sign" (RFC 8152) + AppCoseKey MediaType = 101 // application/cose-key (RFC 8152) + AppCoseKeySet MediaType = 102 // application/cose-key-set (RFC 8152) + AppSenmlJSON MediaType = 110 // application/senml+json + AppSenmlCbor MediaType = 112 // application/senml+cbor + AppCoapGroup MediaType = 256 // coap-group+json (RFC 7390) + AppSenmlEtchJSON MediaType = 320 // application/senml-etch+json + AppSenmlEtchCbor MediaType = 322 // application/senml-etch+cbor + AppOcfCbor MediaType = 10000 // application/vnd.ocf+cbor + AppLwm2mTLV MediaType = 11542 // application/vnd.oma.lwm2m+tlv + AppLwm2mJSON MediaType = 11543 // application/vnd.oma.lwm2m+json + AppLwm2mCbor MediaType = 11544 // application/vnd.oma.lwm2m+cbor ) var mediaTypeToString = map[MediaType]string{ @@ -204,10 +209,15 @@ var mediaTypeToString = map[MediaType]string{ AppCoseSign: "application/cose; cose-type=\"cose-sign\" (RFC 8152)", AppCoseKey: "application/cose-key (RFC 8152)", AppCoseKeySet: "application/cose-key-set (RFC 8152)", + AppSenmlJSON: "application/senml+json", + AppSenmlCbor: "application/senml+cbor", AppCoapGroup: "coap-group+json (RFC 7390)", + AppSenmlEtchJSON: "application/senml-etch+json", + AppSenmlEtchCbor: "application/senml-etch+cbor", AppOcfCbor: "application/vnd.ocf+cbor", AppLwm2mTLV: "application/vnd.oma.lwm2m+tlv", AppLwm2mJSON: "application/vnd.oma.lwm2m+json", + AppLwm2mCbor: "application/vnd.oma.lwm2m+cbor", } func (c MediaType) String() string { @@ -369,7 +379,7 @@ func (o Option) Marshal(buf []byte, previousID OptionID) (int, error) { return -1, err } - //header marshal + // header marshal lenBuf, err = marshalOptionHeader(buf, delta, lenBuf) switch { case err == nil: @@ -393,7 +403,7 @@ func (o Option) Marshal(buf []byte, previousID OptionID) (int, error) { default: return -1, err } - length = length + lenBuf + length += lenBuf if buf == nil { return length, ErrTooSmall @@ -420,8 +430,8 @@ func parseExtOpt(data []byte, opt int) (int, int, error) { return processed, opt, nil } -func (o *Option) Unmarshal(data []byte, optionDefs map[OptionID]OptionDef, OptionID OptionID) (int, error) { - if def, ok := optionDefs[OptionID]; ok { +func (o *Option) Unmarshal(data []byte, optionDefs map[OptionID]OptionDef, optionID OptionID) (int, error) { + if def, ok := optionDefs[optionID]; ok { if def.ValueFormat == ValueUnknown { // Skip unrecognized options (RFC7252 section 5.4.1) return len(data), nil @@ -431,7 +441,7 @@ func (o *Option) Unmarshal(data []byte, optionDefs map[OptionID]OptionDef, Optio return len(data), nil } } - o.ID = OptionID + o.ID = optionID proc, err := o.UnmarshalValue(data) if err != nil { return -1, err diff --git a/vendor/github.com/plgd-dev/go-coap/v2/message/options.go b/vendor/github.com/plgd-dev/go-coap/v3/message/options.go similarity index 94% rename from vendor/github.com/plgd-dev/go-coap/v2/message/options.go rename to vendor/github.com/plgd-dev/go-coap/v3/message/options.go index 32940a001f6..b162f9a7c2e 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/message/options.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/message/options.go @@ -19,7 +19,7 @@ func GetPathBufferSize(path string) (int, error) { subPath := path[start:] segmentSize := strings.Index(subPath, "/") if segmentSize == 0 { - start = start + 1 + start++ continue } if segmentSize < 0 { @@ -28,8 +28,8 @@ func GetPathBufferSize(path string) (int, error) { if segmentSize > maxPathValue { return -1, ErrInvalidValueLength } - size = size + segmentSize - start = start + segmentSize + 1 + size += segmentSize + start += segmentSize + 1 } return size, nil } @@ -54,7 +54,7 @@ func setPath(options Options, optionID OptionID, buf []byte, path string) (Optio subPath := path[start:] end := strings.Index(subPath, "/") if end == 0 { - start = start + 1 + start++ continue } if end < 0 { @@ -68,7 +68,7 @@ func setPath(options Options, optionID OptionID, buf []byte, path string) (Optio return o, -1, err } encoded += enc - start = start + end + 1 + start += end + 1 } return o, encoded, nil } @@ -335,12 +335,12 @@ func (options Options) SetContentFormat(buf []byte, contentFormat MediaType) (Op return options.SetUint32(buf, ContentFormat, uint32(contentFormat)) } -// SetObserve sets ContentFormat option. +// SetObserve sets Observe option. func (options Options) SetObserve(buf []byte, observe uint32) (Options, int, error) { return options.SetUint32(buf, Observe, observe) } -// Observe gets observe option. +// Observe gets Observe option. func (options Options) Observe() (uint32, error) { return options.GetUint32(Observe) } @@ -357,8 +357,8 @@ func (options Options) Accept() (MediaType, error) { } // Find returns range of type options. First number is index and second number is index of next option type. -func (options Options) Find(ID OptionID) (int, int, error) { - idxPre, idxPost := options.findPosition(ID) +func (options Options) Find(id OptionID) (int, int, error) { + idxPre, idxPost := options.findPosition(id) if idxPre == -1 && idxPost == 0 { return -1, -1, ErrOptionNotFound } @@ -368,7 +368,7 @@ func (options Options) Find(ID OptionID) (int, int, error) { if idxPre < idxPost && idxPost-idxPre == 1 { return -1, -1, ErrOptionNotFound } - idxPre = idxPre + 1 + idxPre++ if idxPost < 0 { idxPost = len(options) } @@ -376,7 +376,7 @@ func (options Options) Find(ID OptionID) (int, int, error) { } // findPosition returns opened interval, -1 at means minIdx insert at 0, -1 maxIdx at maxIdx means append. -func (options Options) findPosition(ID OptionID) (minIdx int, maxIdx int) { +func (options Options) findPosition(id OptionID) (minIdx int, maxIdx int) { if len(options) == 0 { return -1, 0 } @@ -385,19 +385,21 @@ func (options Options) findPosition(ID OptionID) (minIdx int, maxIdx int) { minIdx = 0 for { switch { - case ID == options[pivot].ID || (maxIdx-minIdx)/2 == 0: - for maxIdx = pivot; maxIdx < len(options) && options[maxIdx].ID <= ID; maxIdx++ { + case id == options[pivot].ID || (maxIdx-minIdx)/2 == 0: + for maxIdx = pivot; maxIdx < len(options) && options[maxIdx].ID <= id; { + maxIdx++ } if maxIdx == len(options) { maxIdx = -1 } - for minIdx = pivot; minIdx >= 0 && options[minIdx].ID >= ID; minIdx-- { + for minIdx = pivot; minIdx >= 0 && options[minIdx].ID >= id; { + minIdx-- } return minIdx, maxIdx - case ID < options[pivot].ID: + case id < options[pivot].ID: maxIdx = pivot pivot = maxIdx - (maxIdx-minIdx)/2 - case ID > options[pivot].ID: + case id > options[pivot].ID: minIdx = pivot pivot = minIdx + (maxIdx-minIdx)/2 } @@ -410,7 +412,7 @@ func (options Options) findPosition(ID OptionID) (minIdx int, maxIdx int) { func (options Options) Set(opt Option) Options { idxPre, idxPost := options.findPosition(opt.ID) if idxPre == -1 && idxPost == -1 { - //append + // append options = append(options[:0], opt) return options } @@ -444,7 +446,7 @@ func (options Options) Set(opt Option) Options { } else { options = options[:len(options)+1] } - //replace + move + // replace + move updateIdx := updateTo if updateFrom < updateTo { for i := optsLength; i > updateFrom; i-- { @@ -482,8 +484,8 @@ func (options Options) Add(opt Option) Options { } // Remove removes all options with ID. -func (options Options) Remove(ID OptionID) Options { - idxPre, idxPost, err := options.Find(ID) +func (options Options) Remove(id OptionID) Options { + idxPre, idxPost, err := options.Find(id) if err != nil { return options } @@ -506,7 +508,7 @@ func (options Options) Marshal(buf []byte) (int, error) { length := 0 for _, o := range options { - //return coap.error but calculate length + // return coap.error but calculate length if length > len(buf) { buf = nil } @@ -528,7 +530,7 @@ func (options Options) Marshal(buf []byte) (int, error) { default: return -1, err } - length = length + optionLength + length += optionLength } if buf == nil { return length, ErrTooSmall diff --git a/vendor/github.com/plgd-dev/go-coap/v2/message/pool/message.go b/vendor/github.com/plgd-dev/go-coap/v3/message/pool/message.go similarity index 62% rename from vendor/github.com/plgd-dev/go-coap/v2/message/pool/message.go rename to vendor/github.com/plgd-dev/go-coap/v3/message/pool/message.go index 4d63ad912c0..22cf8865245 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/message/pool/message.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/message/pool/message.go @@ -1,36 +1,112 @@ package pool import ( + "bytes" + "context" "errors" "fmt" "io" - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" + multierror "github.com/hashicorp/go-multierror" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" "go.uber.org/atomic" ) +type Encoder interface { + Size(m message.Message) (int, error) + Encode(m message.Message, buf []byte) (int, error) +} + +type Decoder interface { + Decode(buf []byte, m *message.Message) (int, error) +} + type Message struct { + // Context context of request. + ctx context.Context msg message.Message hijacked atomic.Bool isModified bool valueBuffer []byte origValueBuffer []byte - payload io.ReadSeeker + body io.ReadSeeker sequence uint64 + + // local vars + bufferUnmarshal []byte + bufferMarshal []byte } const valueBufferSize = 256 -func NewMessage() *Message { +func NewMessage(ctx context.Context) *Message { valueBuffer := make([]byte, valueBufferSize) return &Message{ + ctx: ctx, msg: message.Message{ - Options: make(message.Options, 0, 16), + Options: make(message.Options, 0, 16), + MessageID: -1, + Type: message.Unset, }, valueBuffer: valueBuffer, origValueBuffer: valueBuffer, + bufferUnmarshal: make([]byte, 256), + bufferMarshal: make([]byte, 256), + } +} + +func (r *Message) Context() context.Context { + return r.ctx +} + +func (r *Message) SetContext(ctx context.Context) { + r.ctx = ctx +} + +func (r *Message) SetMessage(message message.Message) { + r.Reset() + r.msg = message + if len(message.Payload) > 0 { + r.body = bytes.NewReader(message.Payload) + } + r.isModified = true +} + +// SetMessageID only 0 to 2^16-1 are valid. +func (r *Message) SetMessageID(mid int32) { + r.msg.MessageID = mid + r.isModified = true +} + +// UpsertMessageID set value only when origin value is invalid. Only 0 to 2^16-1 values are valid. +func (r *Message) UpsertMessageID(mid int32) { + if message.ValidateMID(r.msg.MessageID) { + return + } + r.SetMessageID(mid) +} + +// MessageID returns 0 to 2^16-1 otherwise it contains invalid value. +func (r *Message) MessageID() int32 { + return r.msg.MessageID +} + +func (r *Message) SetType(typ message.Type) { + r.msg.Type = typ + r.isModified = true +} + +// UpsertType set value only when origin value is invalid. Only 0 to 2^8-1 values are valid. +func (r *Message) UpsertType(typ message.Type) { + if message.ValidateType(r.msg.Type) { + return } + r.SetType(typ) +} + +func (r *Message) Type() message.Type { + return r.msg.Type } // Reset clear message for next reuse @@ -38,8 +114,18 @@ func (r *Message) Reset() { r.msg.Token = nil r.msg.Code = codes.Empty r.msg.Options = r.msg.Options[:0] + r.msg.MessageID = -1 + r.msg.Type = message.Unset + r.msg.Payload = nil r.valueBuffer = r.origValueBuffer - r.payload = nil + r.body = nil + r.isModified = false + if cap(r.bufferMarshal) > 1024 { + r.bufferMarshal = make([]byte, 256) + } + if cap(r.bufferUnmarshal) > 1024 { + r.bufferUnmarshal = make([]byte, 256) + } r.isModified = false } @@ -139,7 +225,7 @@ func (r *Message) SetCode(code codes.Code) { // AddETag appends value to existing ETags. // // Option definition: -// - format: opaque, length: 1-8, repeatable +// - format: opaque, length: 1-8, repeatable func (r *Message) AddETag(value []byte) error { if !message.VerifyOptLen(message.ETag, len(value)) { return message.ErrInvalidValueLength @@ -300,22 +386,22 @@ func (r *Message) Accept() (message.MediaType, error) { } func (r *Message) BodySize() (int64, error) { - if r.payload == nil { + if r.body == nil { return 0, nil } - orig, err := r.payload.Seek(0, io.SeekCurrent) + orig, err := r.body.Seek(0, io.SeekCurrent) if err != nil { return 0, err } - _, err = r.payload.Seek(0, io.SeekStart) + _, err = r.body.Seek(0, io.SeekStart) if err != nil { return 0, err } - size, err := r.payload.Seek(0, io.SeekEnd) + size, err := r.body.Seek(0, io.SeekEnd) if err != nil { return 0, err } - _, err = r.payload.Seek(orig, io.SeekStart) + _, err = r.body.Seek(orig, io.SeekStart) if err != nil { return 0, err } @@ -323,12 +409,12 @@ func (r *Message) BodySize() (int64, error) { } func (r *Message) SetBody(s io.ReadSeeker) { - r.payload = s + r.body = s r.isModified = true } func (r *Message) Body() io.ReadSeeker { - return r.payload + return r.body } func (r *Message) SetSequence(seq uint64) { @@ -387,3 +473,128 @@ func (r *Message) ReadBody() ([]byte, error) { } return payload[:n], nil } + +func (r *Message) toMessage() (message.Message, error) { + payload, err := r.ReadBody() + if err != nil { + return message.Message{}, err + } + m := r.msg + m.Payload = payload + return m, nil +} + +func (r *Message) MarshalWithEncoder(encoder Encoder) ([]byte, error) { + msg, err := r.toMessage() + if err != nil { + return nil, err + } + size, err := encoder.Size(msg) + if err != nil { + return nil, err + } + if len(r.bufferMarshal) < size { + r.bufferMarshal = append(r.bufferMarshal, make([]byte, size-len(r.bufferMarshal))...) + } + n, err := encoder.Encode(msg, r.bufferMarshal) + if err != nil { + return nil, err + } + r.bufferMarshal = r.bufferMarshal[:n] + return r.bufferMarshal, nil +} + +func (r *Message) UnmarshalWithDecoder(decoder Decoder, data []byte) (int, error) { + if len(r.bufferUnmarshal) < len(data) { + r.bufferUnmarshal = append(r.bufferUnmarshal, make([]byte, len(data)-len(r.bufferUnmarshal))...) + } + copy(r.bufferUnmarshal, data) + r.body = nil + r.bufferUnmarshal = r.bufferUnmarshal[:len(data)] + n, err := decoder.Decode(r.bufferUnmarshal, &r.msg) + if err != nil { + return n, err + } + if len(r.msg.Payload) > 0 { + r.body = bytes.NewReader(r.msg.Payload) + } + return n, err +} + +func (r *Message) IsSeparateMessage() bool { + return r.Code() == codes.Empty && r.Token() == nil && r.Type() == message.Acknowledgement && len(r.Options()) == 0 && r.Body() == nil +} + +func (r *Message) setupCommon(code codes.Code, path string, token message.Token, opts ...message.Option) error { + r.SetCode(code) + r.SetToken(token) + r.ResetOptionsTo(opts) + return r.SetPath(path) +} + +func (r *Message) SetupGet(path string, token message.Token, opts ...message.Option) error { + return r.setupCommon(codes.GET, path, token, opts...) +} + +func (r *Message) SetupPost(path string, token message.Token, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) error { + if err := r.setupCommon(codes.POST, path, token, opts...); err != nil { + return err + } + if payload != nil { + r.SetContentFormat(contentFormat) + r.SetBody(payload) + } + return nil +} + +func (r *Message) SetupPut(path string, token message.Token, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) error { + if err := r.setupCommon(codes.PUT, path, token, opts...); err != nil { + return err + } + if payload != nil { + r.SetContentFormat(contentFormat) + r.SetBody(payload) + } + return nil +} + +func (r *Message) SetupDelete(path string, token message.Token, opts ...message.Option) error { + return r.setupCommon(codes.DELETE, path, token, opts...) +} + +func (r *Message) Clone(msg *Message) error { + msg.SetCode(r.Code()) + msg.SetToken(r.Token()) + msg.ResetOptionsTo(r.Options()) + msg.SetType(r.Type()) + msg.SetMessageID(r.MessageID()) + + if r.Body() != nil { + buf := bytes.NewBuffer(nil) + n, err := r.Body().Seek(0, io.SeekCurrent) + if err != nil { + return err + } + _, err = r.body.Seek(0, io.SeekStart) + if err != nil { + return err + } + _, err = io.Copy(buf, r.Body()) + if err != nil { + var errs *multierror.Error + errs = multierror.Append(errs, err) + _, errS := r.Body().Seek(n, io.SeekStart) + if errS != nil { + errs = multierror.Append(errs, errS) + } + return errs.ErrorOrNil() + } + _, err = r.Body().Seek(n, io.SeekStart) + if err != nil { + return err + } + r := bytes.NewReader(buf.Bytes()) + msg.SetBody(r) + } + return nil +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/message/pool/pool.go b/vendor/github.com/plgd-dev/go-coap/v3/message/pool/pool.go new file mode 100644 index 00000000000..e9c91a29217 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/message/pool/pool.go @@ -0,0 +1,64 @@ +package pool + +import ( + "context" + "fmt" + "sync" + + "go.uber.org/atomic" +) + +type Pool struct { + // This field needs to be the first in the struct to ensure proper word alignment on 32-bit platforms. + // See: https://golang.org/pkg/sync/atomic/#pkg-note-BUG + currentMessagesInPool atomic.Int64 + messagePool sync.Pool + maxNumMessages uint32 + maxMessageBufferSize uint16 +} + +func New(maxNumMessages uint32, maxMessageBufferSize uint16) *Pool { + return &Pool{ + maxNumMessages: maxNumMessages, + maxMessageBufferSize: maxMessageBufferSize, + } +} + +// AcquireMessage returns an empty Message instance from Message pool. +// +// The returned Message instance may be passed to ReleaseMessage when it is +// no longer needed. This allows Message recycling, reduces GC pressure +// and usually improves performance. +func (p *Pool) AcquireMessage(ctx context.Context) *Message { + v := p.messagePool.Get() + if v == nil { + return NewMessage(ctx) + } + r, ok := v.(*Message) + if !ok { + panic(fmt.Errorf("invalid message type(%T) for pool", v)) + } + p.currentMessagesInPool.Dec() + r.ctx = ctx + return r +} + +// ReleaseMessage returns req acquired via AcquireMessage to Message pool. +// +// It is forbidden accessing req and/or its' members after returning +// it to Message pool. +func (p *Pool) ReleaseMessage(req *Message) { + for { + v := p.currentMessagesInPool.Load() + if v >= int64(p.maxNumMessages) { + return + } + next := v + 1 + if p.currentMessagesInPool.CompareAndSwap(v, next) { + break + } + } + req.Reset() + req.ctx = nil + p.messagePool.Put(req) +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/message/tcpOptions.go b/vendor/github.com/plgd-dev/go-coap/v3/message/tcpOptions.go new file mode 100644 index 00000000000..b6d6a740aab --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/message/tcpOptions.go @@ -0,0 +1,78 @@ +package message + +// Signal CSM Option IDs +/* + +-----+---+---+-------------------+--------+--------+---------+ + | No. | C | R | Name | Format | Length | Default | + +-----+---+---+-------------------+--------+--------+---------+ + | 2 | | | MaxMessageSize | uint | 0-4 | 1152 | + | 4 | | | BlockWiseTransfer | empty | 0 | (none) | + +-----+---+---+-------------------+--------+--------+---------+ + C=Critical, R=Repeatable +*/ + +const ( + TCPMaxMessageSize OptionID = 2 + TCPBlockWiseTransfer OptionID = 4 +) + +// Signal Ping/Pong Option IDs +/* + +-----+---+---+-------------------+--------+--------+---------+ + | No. | C | R | Name | Format | Length | Default | + +-----+---+---+-------------------+--------+--------+---------+ + | 2 | | | Custody | empty | 0 | (none) | + +-----+---+---+-------------------+--------+--------+---------+ + C=Critical, R=Repeatable +*/ + +const ( + TCPCustody OptionID = 2 +) + +// Signal Release Option IDs +/* + +-----+---+---+---------------------+--------+--------+---------+ + | No. | C | R | Name | Format | Length | Default | + +-----+---+---+---------------------+--------+--------+---------+ + | 2 | | x | Alternative-Address | string | 1-255 | (none) | + | 4 | | | Hold-Off | uint3 | 0-3 | (none) | + +-----+---+---+---------------------+--------+--------+---------+ + C=Critical, R=Repeatable +*/ + +const ( + TCPAlternativeAddress OptionID = 2 + TCPHoldOff OptionID = 4 +) + +// Signal Abort Option IDs +/* + +-----+---+---+---------------------+--------+--------+---------+ + | No. | C | R | Name | Format | Length | Default | + +-----+---+---+---------------------+--------+--------+---------+ + | 2 | | | Bad-CSM-Option | uint | 0-2 | (none) | + +-----+---+---+---------------------+--------+--------+---------+ + C=Critical, R=Repeatable +*/ +const ( + TCPBadCSMOption OptionID = 2 +) + +var TCPSignalCSMOptionDefs = map[OptionID]OptionDef{ + TCPMaxMessageSize: {ValueFormat: ValueUint, MinLen: 0, MaxLen: 4}, + TCPBlockWiseTransfer: {ValueFormat: ValueEmpty, MinLen: 0, MaxLen: 0}, +} + +var TCPSignalPingPongOptionDefs = map[OptionID]OptionDef{ + TCPCustody: {ValueFormat: ValueEmpty, MinLen: 0, MaxLen: 0}, +} + +var TCPSignalReleaseOptionDefs = map[OptionID]OptionDef{ + TCPAlternativeAddress: {ValueFormat: ValueString, MinLen: 1, MaxLen: 255}, + TCPHoldOff: {ValueFormat: ValueUint, MinLen: 0, MaxLen: 3}, +} + +var TCPSignalAbortOptionDefs = map[OptionID]OptionDef{ + TCPBadCSMOption: {ValueFormat: ValueUint, MinLen: 0, MaxLen: 2}, +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/message/type.go b/vendor/github.com/plgd-dev/go-coap/v3/message/type.go similarity index 75% rename from vendor/github.com/plgd-dev/go-coap/v2/udp/message/type.go rename to vendor/github.com/plgd-dev/go-coap/v3/message/type.go index 842a6be7954..1c0e0ea5059 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/message/type.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/message/type.go @@ -1,15 +1,18 @@ package message import ( + "math" "strconv" ) // Type represents the message type. // It's only part of CoAP UDP messages. // Reliable transports like TCP do not have a type. -type Type uint8 +type Type int16 const ( + // Used for unset + Unset Type = -1 // Confirmable messages require acknowledgements. Confirmable Type = 0 // NonConfirmable messages do not require acknowledgements. @@ -21,10 +24,11 @@ const ( ) var typeToString = map[Type]string{ + Unset: "Unset", Confirmable: "Confirmable", NonConfirmable: "NonConfirmable", Acknowledgement: "Acknowledgement", - Reset: "reset", + Reset: "Reset", } func (t Type) String() string { @@ -34,3 +38,8 @@ func (t Type) String() string { } return "Type(" + strconv.FormatInt(int64(t), 10) + ")" } + +// ValidateType validates the type for UDP. (0 <= typ <= 255) +func ValidateType(typ Type) bool { + return typ >= 0 && typ <= math.MaxUint8 +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/mux/client.go b/vendor/github.com/plgd-dev/go-coap/v3/mux/client.go new file mode 100644 index 00000000000..2a82077e512 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/mux/client.go @@ -0,0 +1,51 @@ +package mux + +import ( + "context" + "io" + "net" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/pool" +) + +type Observation = interface { + Cancel(ctx context.Context, opts ...message.Option) error + Canceled() bool +} + +type Conn interface { + // create message from pool + AcquireMessage(ctx context.Context) *pool.Message + // return back the message to the pool for next use + ReleaseMessage(m *pool.Message) + + Ping(ctx context.Context) error + Get(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) + Delete(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) + Post(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) + Put(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) + Observe(ctx context.Context, path string, observeFunc func(notification *pool.Message), opts ...message.Option) (Observation, error) + + RemoteAddr() net.Addr + // NetConn returns the underlying connection that is wrapped by client. The Conn returned is shared by all invocations of NetConn, so do not modify it. + NetConn() net.Conn + Context() context.Context + SetContextValue(key interface{}, val interface{}) + WriteMessage(req *pool.Message) error + // used for GET,PUT,POST,DELETE + Do(req *pool.Message) (*pool.Message, error) + // used for observation (GET with observe 0) + DoObserve(req *pool.Message, observeFunc func(req *pool.Message)) (Observation, error) + Close() error + Sequence() uint64 + // Done signalizes that connection is not more processed. + Done() <-chan struct{} + AddOnClose(func()) + + NewGetRequest(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) + NewObserveRequest(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) + NewPutRequest(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) + NewPostRequest(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) + NewDeleteRequest(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/mux/message.go b/vendor/github.com/plgd-dev/go-coap/v3/mux/message.go new file mode 100644 index 00000000000..bba3c2cfd49 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/mux/message.go @@ -0,0 +1,16 @@ +package mux + +import "github.com/plgd-dev/go-coap/v3/message/pool" + +// RouteParams contains all the information related to a route +type RouteParams struct { + Path string + Vars map[string]string + PathTemplate string +} + +// Message contains message with sequence number. +type Message struct { + *pool.Message + RouteParams *RouteParams +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/mux/middleware.go b/vendor/github.com/plgd-dev/go-coap/v3/mux/middleware.go similarity index 100% rename from vendor/github.com/plgd-dev/go-coap/v2/mux/middleware.go rename to vendor/github.com/plgd-dev/go-coap/v3/mux/middleware.go diff --git a/vendor/github.com/plgd-dev/go-coap/v3/mux/muxResponseWriter.go b/vendor/github.com/plgd-dev/go-coap/v3/mux/muxResponseWriter.go new file mode 100644 index 00000000000..32ea9d4f7c0 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/mux/muxResponseWriter.go @@ -0,0 +1,47 @@ +package mux + +import ( + "io" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" +) + +// ToHandler converts mux handler to udp/dtls/tcp handler. +func ToHandler[C Conn](m Handler) func(w *responsewriter.ResponseWriter[C], r *pool.Message) { + return func(w *responsewriter.ResponseWriter[C], r *pool.Message) { + muxw := &muxResponseWriter[C]{ + w: w, + } + m.ServeCOAP(muxw, &Message{ + Message: r, + RouteParams: new(RouteParams), + }) + } +} + +type muxResponseWriter[C Conn] struct { + w *responsewriter.ResponseWriter[C] +} + +// SetResponse simplifies the setup of the response for the request. ETags must be set via options. For advanced setup, use Message(). +func (w *muxResponseWriter[C]) SetResponse(code codes.Code, contentFormat message.MediaType, d io.ReadSeeker, opts ...message.Option) error { + return w.w.SetResponse(code, contentFormat, d, opts...) +} + +// Conn peer connection. +func (w *muxResponseWriter[C]) Conn() Conn { + return w.w.Conn() +} + +// Message direct access to the response. +func (w *muxResponseWriter[C]) Message() *pool.Message { + return w.w.Message() +} + +// SetMessage replaces the response message. Ensure that Token, MessageID(udp), and Type(udp) messages are paired correctly. +func (w *muxResponseWriter[C]) SetMessage(msg *pool.Message) { + w.w.SetMessage(msg) +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/mux/regexp.go b/vendor/github.com/plgd-dev/go-coap/v3/mux/regexp.go similarity index 100% rename from vendor/github.com/plgd-dev/go-coap/v2/mux/regexp.go rename to vendor/github.com/plgd-dev/go-coap/v3/mux/regexp.go diff --git a/vendor/github.com/plgd-dev/go-coap/v2/mux/router.go b/vendor/github.com/plgd-dev/go-coap/v3/mux/router.go similarity index 95% rename from vendor/github.com/plgd-dev/go-coap/v2/mux/router.go rename to vendor/github.com/plgd-dev/go-coap/v3/mux/router.go index 5064a633c0b..5eea89461a7 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/mux/router.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/mux/router.go @@ -6,13 +6,16 @@ import ( "io" "sync" - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" ) type ResponseWriter = interface { SetResponse(code codes.Code, contentFormat message.MediaType, d io.ReadSeeker, opts ...message.Option) error - Client() Client + Conn() Conn + SetMessage(m *pool.Message) + Message() *pool.Message } type Handler interface { @@ -87,7 +90,7 @@ func pathMatch(pattern Route, path string) bool { // Most-specific (longest) pattern wins func (r *Router) Match(path string, routeParams *RouteParams) (matchedRoute *Route, matchedPattern string) { r.m.RLock() - var n = 0 + n := 0 for pattern, route := range r.z { if !pathMatch(route, path) { continue @@ -193,7 +196,7 @@ func (r *Router) GetRoutes() map[string]Route { // is sought. // If no handler is found a standard NotFound message is returned func (r *Router) ServeCOAP(w ResponseWriter, req *Message) { - path, err := req.Options.Path() + path, err := req.Options().Path() r.m.RLock() defaultHandler := r.defaultHandler r.m.RUnlock() diff --git a/vendor/github.com/plgd-dev/go-coap/v3/net/blockwise/blockwise.go b/vendor/github.com/plgd-dev/go-coap/v3/net/blockwise/blockwise.go new file mode 100644 index 00000000000..1828d75eae2 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/blockwise/blockwise.go @@ -0,0 +1,844 @@ +package blockwise + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "time" + + "github.com/dsnet/golib/memfile" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" + "github.com/plgd-dev/go-coap/v3/pkg/cache" + "golang.org/x/sync/semaphore" +) + +// Block Option value is represented: https://tools.ietf.org/html/rfc7959#section-2.2 +// 0 +// 0 1 2 3 4 5 6 7 +// +-+-+-+-+-+-+-+-+ +// | NUM |M| SZX | +// +-+-+-+-+-+-+-+-+ +// 0 1 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NUM |M| SZX | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 0 1 2 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NUM |M| SZX | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +const ( + // max block size is 3bytes: https://tools.ietf.org/html/rfc7959#section-2.1 + maxBlockValue = 0xffffff + // maxBlockNumber is 20bits (NUM) + maxBlockNumber = 0xffff7 + // moreBlocksFollowingMask is represented by one bit (M) + moreBlocksFollowingMask = 0x8 + // szxMask last 3bits represents SZX (SZX) + szxMask = 0x7 +) + +// SZX enum representation for the size of the block: https://tools.ietf.org/html/rfc7959#section-2.2 +type SZX uint8 + +const ( + // SZX16 block of size 16bytes + SZX16 SZX = 0 + // SZX32 block of size 32bytes + SZX32 SZX = 1 + // SZX64 block of size 64bytes + SZX64 SZX = 2 + // SZX128 block of size 128bytes + SZX128 SZX = 3 + // SZX256 block of size 256bytes + SZX256 SZX = 4 + // SZX512 block of size 512bytes + SZX512 SZX = 5 + // SZX1024 block of size 1024bytes + SZX1024 SZX = 6 + // SZXBERT block of size n*1024bytes + SZXBERT SZX = 7 +) + +var szxToSize = map[SZX]int64{ + SZX16: 16, + SZX32: 32, + SZX64: 64, + SZX128: 128, + SZX256: 256, + SZX512: 512, + SZX1024: 1024, + SZXBERT: 1024, +} + +// Size number of bytes. +func (s SZX) Size() int64 { + val, ok := szxToSize[s] + if ok { + return val + } + return -1 +} + +// EncodeBlockOption encodes block values to coap option. +func EncodeBlockOption(szx SZX, blockNumber int64, moreBlocksFollowing bool) (uint32, error) { + if szx > SZXBERT { + return 0, ErrInvalidSZX + } + if blockNumber < 0 { + return 0, ErrBlockNumberExceedLimit + } + if blockNumber > maxBlockNumber { + return 0, ErrBlockNumberExceedLimit + } + blockVal := uint32(blockNumber << 4) + m := uint32(0) + if moreBlocksFollowing { + m = 1 + } + blockVal += m << 3 + blockVal += uint32(szx) + return blockVal, nil +} + +// DecodeBlockOption decodes coap block option to block values. +func DecodeBlockOption(blockVal uint32) (szx SZX, blockNumber int64, moreBlocksFollowing bool, err error) { + if blockVal > maxBlockValue { + err = ErrBlockInvalidSize + return + } + + szx = SZX(blockVal & szxMask) // masking for the SZX + if (blockVal & moreBlocksFollowingMask) != 0 { // masking for the "M" + moreBlocksFollowing = true + } + blockNumber = int64(blockVal) >> 4 // shifting out the SZX and M vals. leaving the block number behind + if blockNumber > maxBlockNumber { + err = ErrBlockNumberExceedLimit + } + return +} + +type Client interface { + // create message from pool + AcquireMessage(ctx context.Context) *pool.Message + // return back the message to the pool for next use + ReleaseMessage(m *pool.Message) +} + +type BlockWise[C Client] struct { + cc C + receivingMessagesCache *cache.Cache[uint64, *messageGuard] + sendingMessagesCache *cache.Cache[uint64, *pool.Message] + errors func(error) + getSentRequestFromOutside func(token message.Token) (*pool.Message, bool) + expiration time.Duration +} + +type messageGuard struct { + *pool.Message + *semaphore.Weighted +} + +func newRequestGuard(request *pool.Message) *messageGuard { + return &messageGuard{ + Message: request, + Weighted: semaphore.NewWeighted(1), + } +} + +// New provides blockwise. +// getSentRequestFromOutside must returns a copy of request which will be released after use. +func New[C Client]( + cc C, + expiration time.Duration, + errors func(error), + getSentRequestFromOutside func(token message.Token) (*pool.Message, bool), +) *BlockWise[C] { + if getSentRequestFromOutside == nil { + getSentRequestFromOutside = func(token message.Token) (*pool.Message, bool) { return nil, false } + } + return &BlockWise[C]{ + cc: cc, + receivingMessagesCache: cache.NewCache[uint64, *messageGuard](), + sendingMessagesCache: cache.NewCache[uint64, *pool.Message](), + errors: errors, + getSentRequestFromOutside: getSentRequestFromOutside, + expiration: expiration, + } +} + +func bufferSize(szx SZX, maxMessageSize uint32) int64 { + if szx < SZXBERT { + return szx.Size() + } + return (int64(maxMessageSize) / szx.Size()) * szx.Size() +} + +// CheckExpirations iterates over caches and remove expired items. +func (b *BlockWise[C]) CheckExpirations(now time.Time) { + b.receivingMessagesCache.CheckExpirations(now) + b.sendingMessagesCache.CheckExpirations(now) +} + +func (b *BlockWise[C]) cloneMessage(r *pool.Message) *pool.Message { + req := b.cc.AcquireMessage(r.Context()) + req.SetCode(r.Code()) + req.SetToken(r.Token()) + req.ResetOptionsTo(r.Options()) + req.SetType(r.Type()) + return req +} + +func payloadSizeError(err error) error { + return fmt.Errorf("cannot get size of payload: %w", err) +} + +// Do sends an coap message and returns an coap response via blockwise transfer. +func (b *BlockWise[C]) Do(r *pool.Message, maxSzx SZX, maxMessageSize uint32, do func(req *pool.Message) (*pool.Message, error)) (*pool.Message, error) { + if maxSzx > SZXBERT { + return nil, fmt.Errorf("invalid szx") + } + if len(r.Token()) == 0 { + return nil, fmt.Errorf("invalid token") + } + + expire, ok := r.Context().Deadline() + if !ok { + expire = time.Now().Add(b.expiration) + } + _, loaded := b.sendingMessagesCache.LoadOrStore(r.Token().Hash(), cache.NewElement(r, expire, nil)) + if loaded { + return nil, fmt.Errorf("invalid token") + } + defer b.sendingMessagesCache.Delete(r.Token().Hash()) + if r.Body() == nil { + return do(r) + } + payloadSize, err := r.BodySize() + if err != nil { + return nil, payloadSizeError(err) + } + if payloadSize <= maxSzx.Size() { + return do(r) + } + + switch r.Code() { + case codes.POST, codes.PUT: + break + default: + return nil, fmt.Errorf("unsupported command(%v)", r.Code()) + } + req := b.cloneMessage(r) + defer b.cc.ReleaseMessage(req) + req.SetOptionUint32(message.Size1, uint32(payloadSize)) + block, err := EncodeBlockOption(maxSzx, 0, true) + if err != nil { + return nil, fmt.Errorf("cannot encode block option(%v, %v, %v) to bw request: %w", maxSzx, 0, true, err) + } + req.SetOptionUint32(message.Block1, block) + newBufLen := bufferSize(maxSzx, maxMessageSize) + buf := make([]byte, newBufLen) + newOff, err := r.Body().Seek(0, io.SeekStart) + if err != nil { + return nil, fmt.Errorf("cannot seek in payload: %w", err) + } + readed, err := io.ReadFull(r.Body(), buf) + if errors.Is(err, io.ErrUnexpectedEOF) { + if newOff+int64(readed) == payloadSize { + err = nil + } + } + if err != nil { + return nil, fmt.Errorf("cannot read payload: %w", err) + } + buf = buf[:readed] + req.SetBody(bytes.NewReader(buf)) + return do(req) +} + +func newWriteRequestResponse[C Client](cc C, request *pool.Message) *responsewriter.ResponseWriter[C] { + req := cc.AcquireMessage(request.Context()) + req.SetCode(request.Code()) + req.SetToken(request.Token()) + req.ResetOptionsTo(request.Options()) + req.SetBody(request.Body()) + return responsewriter.New(req, cc, request.Options()...) +} + +// WriteMessage sends an coap message via blockwise transfer. +func (b *BlockWise[C]) WriteMessage(request *pool.Message, maxSZX SZX, maxMessageSize uint32, writeMessage func(r *pool.Message) error) error { + startSendingMessageBlock, err := EncodeBlockOption(maxSZX, 0, true) + if err != nil { + return fmt.Errorf("cannot encode start sending message block option(%v,%v,%v): %w", maxSZX, 0, true, err) + } + + w := newWriteRequestResponse(b.cc, request) + err = b.startSendingMessage(w, maxSZX, maxMessageSize, startSendingMessageBlock) + if err != nil { + return fmt.Errorf("cannot start writing request: %w", err) + } + return writeMessage(w.Message()) +} + +func fitSZX(r *pool.Message, blockType message.OptionID, maxSZX SZX) SZX { + block, err := r.GetOptionUint32(blockType) + if err != nil { + return maxSZX + } + + szx, _, _, err := DecodeBlockOption(block) + if err != nil { + return maxSZX + } + + if maxSZX > szx { + return szx + } + return maxSZX +} + +func (b *BlockWise[C]) sendEntityIncomplete(w *responsewriter.ResponseWriter[C], token message.Token) { + sendMessage := b.cc.AcquireMessage(w.Message().Context()) + sendMessage.SetCode(codes.RequestEntityIncomplete) + sendMessage.SetToken(token) + sendMessage.SetType(message.NonConfirmable) + w.SetMessage(sendMessage) +} + +func wantsToBeReceived(r *pool.Message) bool { + hasBlock1 := r.HasOption(message.Block1) + hasBlock2 := r.HasOption(message.Block2) + if hasBlock1 && (r.Code() == codes.POST || r.Code() == codes.PUT) { + // r contains payload which we received + return true + } + if hasBlock2 && (r.Code() >= codes.GET && r.Code() <= codes.DELETE) { + // r is command to get next block + return false + } + if r.Code() == codes.Continue { + return false + } + return true +} + +func (b *BlockWise[C]) getSendingMessageCode(token uint64) (codes.Code, bool) { + v := b.sendingMessagesCache.Load(token) + if v == nil { + return codes.Empty, false + } + return v.Data().Code(), true +} + +// Handle middleware which constructs COAP request from blockwise transfer and send COAP response via blockwise. +func (b *BlockWise[C]) Handle(w *responsewriter.ResponseWriter[C], r *pool.Message, maxSZX SZX, maxMessageSize uint32, next func(w *responsewriter.ResponseWriter[C], r *pool.Message)) { + if maxSZX > SZXBERT { + panic("invalid maxSZX") + } + token := r.Token() + + if len(token) == 0 { + err := b.handleReceivedMessage(w, r, maxSZX, maxMessageSize, next) + if err != nil { + b.sendEntityIncomplete(w, token) + b.errors(fmt.Errorf("handleReceivedMessage(%v): %w", r, err)) + } + return + } + tokenStr := token.Hash() + + sendingMessageCode, sendingMessageExist := b.getSendingMessageCode(tokenStr) + if !sendingMessageExist || wantsToBeReceived(r) { + err := b.handleReceivedMessage(w, r, maxSZX, maxMessageSize, next) + if err != nil { + b.sendEntityIncomplete(w, token) + b.errors(fmt.Errorf("handleReceivedMessage(%v): %w", r, err)) + } + return + } + more, err := b.continueSendingMessage(w, r, maxSZX, maxMessageSize, sendingMessageCode) + if err != nil { + b.sendingMessagesCache.Delete(tokenStr) + b.errors(fmt.Errorf("continueSendingMessage(%v): %w", r, err)) + return + } + // For codes GET,POST,PUT,DELETE, we want them to wait for pairing response and then delete them when the full response comes in or when timeout occurs. + if !more && sendingMessageCode > codes.DELETE { + b.sendingMessagesCache.Delete(tokenStr) + } +} + +func (b *BlockWise[C]) handleReceivedMessage(w *responsewriter.ResponseWriter[C], r *pool.Message, maxSZX SZX, maxMessageSize uint32, next func(w *responsewriter.ResponseWriter[C], r *pool.Message)) error { + startSendingMessageBlock, err := EncodeBlockOption(maxSZX, 0, true) + if err != nil { + return fmt.Errorf("cannot encode start sending message block option(%v,%v,%v): %w", maxSZX, 0, true, err) + } + switch r.Code() { + case codes.Empty, codes.CSM, codes.Ping, codes.Pong, codes.Release, codes.Abort: + next(w, r) + return nil + case codes.GET, codes.DELETE: + maxSZX = fitSZX(r, message.Block2, maxSZX) + block, errG := r.GetOptionUint32(message.Block2) + if errG == nil { + r.Remove(message.Block2) + } + next(w, r) + if w.Message().Code() == codes.Content && errG == nil { + startSendingMessageBlock = block + } + case codes.POST, codes.PUT: + maxSZX = fitSZX(r, message.Block1, maxSZX) + errP := b.processReceivedMessage(w, r, maxSZX, next, message.Block1, message.Size1) + if errP != nil { + return errP + } + default: + maxSZX = fitSZX(r, message.Block2, maxSZX) + errP := b.processReceivedMessage(w, r, maxSZX, next, message.Block2, message.Size2) + if errP != nil { + return errP + } + } + return b.startSendingMessage(w, maxSZX, maxMessageSize, startSendingMessageBlock) +} + +func (b *BlockWise[C]) createSendingMessage(sendingMessage *pool.Message, maxSZX SZX, maxMessageSize uint32, block uint32) (sendMessage *pool.Message, more bool, err error) { + blockType := message.Block2 + sizeType := message.Size2 + token := sendingMessage.Token() + switch sendingMessage.Code() { + case codes.POST, codes.PUT: + blockType = message.Block1 + sizeType = message.Size1 + } + + szx, num, _, err := DecodeBlockOption(block) + if err != nil { + return nil, false, fmt.Errorf("cannot decode %v option: %w", blockType, err) + } + + sendMessage = b.cc.AcquireMessage(sendingMessage.Context()) + sendMessage.SetCode(sendingMessage.Code()) + sendMessage.ResetOptionsTo(sendingMessage.Options()) + sendMessage.SetToken(token) + sendMessage.SetType(sendingMessage.Type()) + payloadSize, err := sendingMessage.BodySize() + if err != nil { + b.cc.ReleaseMessage(sendMessage) + return nil, false, payloadSizeError(err) + } + if szx > maxSZX { + szx = maxSZX + } + newBufLen := bufferSize(szx, maxMessageSize) + off := num * szx.Size() + if blockType == message.Block1 { + // For block1, we need to skip the already sent bytes. + off += newBufLen + } + offSeek, err := sendingMessage.Body().Seek(off, io.SeekStart) + if err != nil { + b.cc.ReleaseMessage(sendMessage) + return nil, false, fmt.Errorf("cannot seek in response: %w", err) + } + if off != offSeek { + b.cc.ReleaseMessage(sendMessage) + return nil, false, fmt.Errorf("cannot seek to requested offset(%v != %v)", off, offSeek) + } + buf := make([]byte, 1024) + if int64(len(buf)) < newBufLen { + buf = make([]byte, newBufLen) + } + buf = buf[:newBufLen] + + readed, err := io.ReadFull(sendingMessage.Body(), buf) + if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) { + if offSeek+int64(readed) == payloadSize { + err = nil + } + } + if err != nil { + b.cc.ReleaseMessage(sendMessage) + return nil, false, fmt.Errorf("cannot read response: %w", err) + } + + buf = buf[:readed] + sendMessage.SetBody(bytes.NewReader(buf)) + more = true + if offSeek+int64(readed) == payloadSize { + more = false + } + sendMessage.SetOptionUint32(sizeType, uint32(payloadSize)) + num = (offSeek) / szx.Size() + block, err = EncodeBlockOption(szx, num, more) + if err != nil { + b.cc.ReleaseMessage(sendMessage) + return nil, false, fmt.Errorf("cannot encode block option(%v,%v,%v): %w", szx, num, more, err) + } + sendMessage.SetOptionUint32(blockType, block) + return sendMessage, more, nil +} + +func (b *BlockWise[C]) continueSendingMessage(w *responsewriter.ResponseWriter[C], r *pool.Message, maxSZX SZX, maxMessageSize uint32, sendingMessageCode codes.Code /* msg *pool.Message*/) (bool, error) { + blockType := message.Block2 + switch sendingMessageCode { + case codes.POST, codes.PUT: + blockType = message.Block1 + } + + block, err := r.GetOptionUint32(blockType) + if err != nil { + return false, fmt.Errorf("cannot get %v option: %w", blockType, err) + } + var sendMessage *pool.Message + var more bool + b.sendingMessagesCache.LoadWithFunc(r.Token().Hash(), func(value *cache.Element[*pool.Message]) *cache.Element[*pool.Message] { + sendMessage, more, err = b.createSendingMessage(value.Data(), maxSZX, maxMessageSize, block) + if err != nil { + err = fmt.Errorf("cannot create sending message: %w", err) + } + return nil + }) + if err == nil && sendMessage == nil { + err = fmt.Errorf("cannot find sending message for token(%v)", r.Token()) + } + if err != nil { + return false, fmt.Errorf("handleSendingMessage: %w", err) + } + w.SetMessage(sendMessage) + return more, err +} + +func isObserveResponse(msg *pool.Message) bool { + _, err := msg.GetOptionUint32(message.Observe) + if err != nil { + return false + } + return msg.Code() >= codes.Created +} + +func (b *BlockWise[C]) startSendingMessage(w *responsewriter.ResponseWriter[C], maxSZX SZX, maxMessageSize uint32, block uint32) error { + payloadSize, err := w.Message().BodySize() + if err != nil { + return payloadSizeError(err) + } + + if payloadSize < maxSZX.Size() { + return nil + } + sendingMessage, _, err := b.createSendingMessage(w.Message(), maxSZX, maxMessageSize, block) + if err != nil { + return fmt.Errorf("handleSendingMessage: cannot create sending message: %w", err) + } + originalSendingMessage := w.Swap(sendingMessage) + if isObserveResponse(w.Message()) { + b.cc.ReleaseMessage(originalSendingMessage) + // https://tools.ietf.org/html/rfc7959#section-2.6 - we don't need store it because client will be get values via GET. + return nil + } + expire, ok := sendingMessage.Context().Deadline() + if !ok { + expire = time.Now().Add(b.expiration) + } + el, loaded := b.sendingMessagesCache.LoadOrStore(sendingMessage.Token().Hash(), cache.NewElement(originalSendingMessage, expire, nil)) + if loaded { + defer b.cc.ReleaseMessage(originalSendingMessage) + return fmt.Errorf("cannot add message (%v) to sending message cache: message(%v) with token(%v) already exist", originalSendingMessage, el.Data(), sendingMessage.Token()) + } + return nil +} + +func (b *BlockWise[C]) getSentRequest(token message.Token) *pool.Message { + data, ok := b.sendingMessagesCache.LoadWithFunc(token.Hash(), func(value *cache.Element[*pool.Message]) *cache.Element[*pool.Message] { + if value == nil { + return nil + } + v := value.Data() + msg := b.cc.AcquireMessage(v.Context()) + msg.SetCode(v.Code()) + msg.SetToken(v.Token()) + msg.ResetOptionsTo(v.Options()) + msg.SetType(v.Type()) + return cache.NewElement(msg, value.ValidUntil.Load(), nil) + }) + if ok { + return data.Data() + } + globalRequest, ok := b.getSentRequestFromOutside(token) + if ok { + return globalRequest + } + return nil +} + +func (b *BlockWise[C]) handleObserveResponse(sentRequest *pool.Message) (message.Token, time.Time, error) { + // https://tools.ietf.org/html/rfc7959#section-2.6 - performs GET with new token. + if sentRequest == nil { + return nil, time.Time{}, fmt.Errorf("observation is not registered") + } + token, err := message.GetToken() + if err != nil { + return nil, time.Time{}, fmt.Errorf("cannot get token for create GET request: %w", err) + } + validUntil := time.Now().Add(b.expiration) // context of observation can be expired. + bwSentRequest := b.cloneMessage(sentRequest) + bwSentRequest.SetToken(token) + _, loaded := b.sendingMessagesCache.LoadOrStore(token.Hash(), cache.NewElement(bwSentRequest, validUntil, nil)) + if loaded { + return nil, time.Time{}, fmt.Errorf("cannot process message: message with token already exist") + } + return token, validUntil, nil +} + +func (b *BlockWise[C]) getValidUntil(sentRequest *pool.Message) time.Time { + validUntil := time.Now().Add(b.expiration) + if sentRequest != nil { + if deadline, ok := sentRequest.Context().Deadline(); ok { + return deadline + } + } + return validUntil +} + +func getSzx(szx, maxSzx SZX) SZX { + if szx > maxSzx { + return maxSzx + } + return szx +} + +func (b *BlockWise[C]) getPayloadFromCachedReceivedMessage(r, cachedReceivedMessage *pool.Message) (*memfile.File, int64, error) { + payloadFile, ok := cachedReceivedMessage.Body().(*memfile.File) + if !ok { + return nil, 0, fmt.Errorf("invalid body type(%T) stored in receivingMessagesCache", cachedReceivedMessage.Body()) + } + rETAG, errETAG := r.GetOptionBytes(message.ETag) + cachedReceivedMessageETAG, errCachedReceivedMessageETAG := cachedReceivedMessage.GetOptionBytes(message.ETag) + switch { + case errETAG == nil && errCachedReceivedMessageETAG != nil: + if len(cachedReceivedMessageETAG) > 0 { // make sure there is an etag there + return nil, 0, fmt.Errorf("received message doesn't contains ETAG but cached received message contains it(%v)", cachedReceivedMessageETAG) + } + case errETAG != nil && errCachedReceivedMessageETAG == nil: + if len(rETAG) > 0 { // make sure there is an etag there + return nil, 0, fmt.Errorf("received message contains ETAG(%v) but cached received message doesn't", rETAG) + } + case !bytes.Equal(rETAG, cachedReceivedMessageETAG): + // ETAG was changed - drop data and set new ETAG + cachedReceivedMessage.SetOptionBytes(message.ETag, rETAG) + if err := payloadFile.Truncate(0); err != nil { + return nil, 0, fmt.Errorf("cannot truncate cached request: %w", err) + } + } + + payloadSize, err := cachedReceivedMessage.BodySize() + if err != nil { + return nil, 0, payloadSizeError(err) + } + return payloadFile, payloadSize, nil +} + +func copyToPayloadFromOffset(r *pool.Message, payloadFile *memfile.File, offset int64) (int64, error) { + payloadSize := int64(0) + copyn, err := payloadFile.Seek(offset, io.SeekStart) + if err != nil { + return 0, fmt.Errorf("cannot seek to off(%v) of cached request: %w", offset, err) + } + written := int64(0) + if r.Body() != nil { + _, err = r.Body().Seek(0, io.SeekStart) + if err != nil { + return 0, fmt.Errorf("cannot seek to start of request: %w", err) + } + written, err = io.Copy(payloadFile, r.Body()) + if err != nil { + return 0, fmt.Errorf("cannot copy to cached request: %w", err) + } + } + payloadSize = copyn + written + err = payloadFile.Truncate(payloadSize) + if err != nil { + return 0, fmt.Errorf("cannot truncate cached request: %w", err) + } + return payloadSize, nil +} + +func (b *BlockWise[C]) getCachedReceivedMessage(mg *messageGuard, r *pool.Message, tokenStr uint64, validUntil time.Time) (*pool.Message, func(), error) { + cannotLockError := func(err error) error { + return fmt.Errorf("processReceivedMessage: cannot lock message: %w", err) + } + if mg != nil { + errA := mg.Acquire(mg.Context(), 1) + if errA != nil { + return nil, nil, cannotLockError(errA) + } + return mg.Message, func() { mg.Release(1) }, nil + } + closeFnList := []func(){} + appendToClose := func(m *messageGuard) { + closeFnList = append(closeFnList, func() { + m.Release(1) + }) + } + closeFn := func() { + for i := range closeFnList { + closeFnList[len(closeFnList)-1-i]() + } + } + msg := b.cc.AcquireMessage(r.Context()) + msg.ResetOptionsTo(r.Options()) + msg.SetToken(r.Token()) + msg.SetSequence(r.Sequence()) + msg.SetBody(memfile.New(make([]byte, 0, 1024))) + msg.SetCode(r.Code()) + mg = newRequestGuard(msg) + errA := mg.Acquire(mg.Context(), 1) + if errA != nil { + return nil, nil, cannotLockError(errA) + } + appendToClose(mg) + element, loaded := b.receivingMessagesCache.LoadOrStore(tokenStr, cache.NewElement(mg, validUntil, func(d *messageGuard) { + if d == nil { + return + } + b.sendingMessagesCache.Delete(tokenStr) + })) + // request was already stored in cache, silently + if loaded { + mg = element.Data() + if mg == nil { + closeFn() + return nil, nil, fmt.Errorf("request was already stored in cache") + } + errA := mg.Acquire(mg.Context(), 1) + if errA != nil { + closeFn() + return nil, nil, cannotLockError(errA) + } + appendToClose(mg) + } + + return mg.Message, closeFn, nil +} + +//nolint:gocyclo,gocognit +func (b *BlockWise[C]) processReceivedMessage(w *responsewriter.ResponseWriter[C], r *pool.Message, maxSzx SZX, next func(w *responsewriter.ResponseWriter[C], r *pool.Message), blockType message.OptionID, sizeType message.OptionID) error { + // TODO: lower cyclomatic complexity + token := r.Token() + if len(token) == 0 { + next(w, r) + return nil + } + if r.Code() == codes.GET || r.Code() == codes.DELETE { + next(w, r) + return nil + } + block, err := r.GetOptionUint32(blockType) + if err != nil { + if errors.Is(err, message.ErrOptionNotFound) { + next(w, r) + return nil + } + return fmt.Errorf("cannot get Block(optionID=%d) option: %w", blockType, err) + } + szx, num, more, err := DecodeBlockOption(block) + if err != nil { + return fmt.Errorf("cannot decode block option: %w", err) + } + sentRequest := b.getSentRequest(token) + if sentRequest != nil { + defer b.cc.ReleaseMessage(sentRequest) + } + validUntil := b.getValidUntil(sentRequest) + if blockType == message.Block2 && sentRequest == nil { + return fmt.Errorf("cannot request body without paired request") + } + if isObserveResponse(r) { + token, validUntil, err = b.handleObserveResponse(sentRequest) + if err != nil { + return fmt.Errorf("cannot process message: %w", err) + } + } + + tokenStr := token.Hash() + var cachedReceivedMessageGuard *messageGuard + if e := b.receivingMessagesCache.Load(tokenStr); e != nil { + cachedReceivedMessageGuard = e.Data() + } + if cachedReceivedMessageGuard == nil { + szx = getSzx(szx, maxSzx) + // if there is no more then just forward req to next handler + if !more { + next(w, r) + return nil + } + } + cachedReceivedMessage, closeCachedReceivedMessage, err := b.getCachedReceivedMessage(cachedReceivedMessageGuard, r, tokenStr, validUntil) + if err != nil { + return err + } + defer closeCachedReceivedMessage() + + defer func(err *error) { + if *err != nil { + b.receivingMessagesCache.Delete(tokenStr) + } + }(&err) + payloadFile, payloadSize, err := b.getPayloadFromCachedReceivedMessage(r, cachedReceivedMessage) + if err != nil { + return fmt.Errorf("cannot get payload: %w", err) + } + off := num * szx.Size() + if off == payloadSize { + payloadSize, err = copyToPayloadFromOffset(r, payloadFile, off) + if err != nil { + return fmt.Errorf("cannot copy data to payload: %w", err) + } + if !more { + b.receivingMessagesCache.Delete(tokenStr) + cachedReceivedMessage.Remove(blockType) + cachedReceivedMessage.Remove(sizeType) + cachedReceivedMessage.SetType(r.Type()) + if !bytes.Equal(cachedReceivedMessage.Token(), token) { + b.sendingMessagesCache.Delete(tokenStr) + } + _, errS := cachedReceivedMessage.Body().Seek(0, io.SeekStart) + if errS != nil { + return fmt.Errorf("cannot seek to start of cachedReceivedMessage request: %w", errS) + } + next(w, cachedReceivedMessage) + return nil + } + } + + szx = getSzx(szx, maxSzx) + sendMessage := b.cc.AcquireMessage(r.Context()) + sendMessage.SetToken(token) + if blockType == message.Block2 { + num = payloadSize / szx.Size() + sendMessage.ResetOptionsTo(sentRequest.Options()) + sendMessage.SetCode(sentRequest.Code()) + sendMessage.Remove(message.Observe) + sendMessage.Remove(message.Block1) + sendMessage.Remove(message.Size1) + } else { + sendMessage.SetCode(codes.Continue) + } + respBlock, err := EncodeBlockOption(szx, num, more) + if err != nil { + b.cc.ReleaseMessage(sendMessage) + return fmt.Errorf("cannot encode block option(%v,%v,%v): %w", szx, num, more, err) + } + sendMessage.SetOptionUint32(blockType, respBlock) + w.SetMessage(sendMessage) + return nil +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/blockwise/error.go b/vendor/github.com/plgd-dev/go-coap/v3/net/blockwise/error.go similarity index 100% rename from vendor/github.com/plgd-dev/go-coap/v2/net/blockwise/error.go rename to vendor/github.com/plgd-dev/go-coap/v3/net/blockwise/error.go diff --git a/vendor/github.com/plgd-dev/go-coap/v3/net/client/client.go b/vendor/github.com/plgd-dev/go-coap/v3/net/client/client.go new file mode 100644 index 00000000000..b2cdca9ec15 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/client/client.go @@ -0,0 +1,241 @@ +package client + +import ( + "context" + "fmt" + "io" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/pool" + limitparallelrequests "github.com/plgd-dev/go-coap/v3/net/client/limitParallelRequests" + "github.com/plgd-dev/go-coap/v3/net/observation" +) + +type ( + GetTokenFunc = func() (message.Token, error) +) + +type Conn interface { + // create message from pool + AcquireMessage(ctx context.Context) *pool.Message + // return back the message to the pool for next use + ReleaseMessage(m *pool.Message) + WriteMessage(req *pool.Message) error + AsyncPing(receivedPong func()) (func(), error) + Context() context.Context +} + +type Client[C Conn] struct { + cc Conn + observationHandler *observation.Handler[C] + getToken GetTokenFunc + *limitparallelrequests.LimitParallelRequests +} + +func New[C Conn](cc C, observationHandler *observation.Handler[C], getToken GetTokenFunc, limitParallelRequests *limitparallelrequests.LimitParallelRequests) *Client[C] { + return &Client[C]{ + cc: cc, + observationHandler: observationHandler, + getToken: getToken, + LimitParallelRequests: limitParallelRequests, + } +} + +func (c *Client[C]) GetToken() (message.Token, error) { + return c.getToken() +} + +// NewGetRequest creates get request. +// +// Use ctx to set timeout. +func (c *Client[C]) NewGetRequest(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) { + req := c.cc.AcquireMessage(ctx) + token, err := c.GetToken() + if err != nil { + c.cc.ReleaseMessage(req) + return nil, err + } + err = req.SetupGet(path, token, opts...) + if err != nil { + c.cc.ReleaseMessage(req) + return nil, err + } + return req, nil +} + +// Get issues a GET to the specified path. +// +// Use ctx to set timeout. +// +// An error is returned if by failure to speak COAP (such as a network connectivity problem). +// Any status code doesn't cause an error. +func (c *Client[C]) Get(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) { + req, err := c.NewGetRequest(ctx, path, opts...) + if err != nil { + return nil, fmt.Errorf("cannot create get request: %w", err) + } + defer c.cc.ReleaseMessage(req) + return c.Do(req) +} + +type Observation = interface { + Cancel(ctx context.Context, opts ...message.Option) error + Canceled() bool +} + +// NewObserveRequest creates observe request. +// +// Use ctx to set timeout. +func (c *Client[C]) NewObserveRequest(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) { + req, err := c.NewGetRequest(ctx, path, opts...) + if err != nil { + return nil, err + } + req.SetObserve(0) + return req, nil +} + +// Observe subscribes for every change of resource on path. +func (c *Client[C]) Observe(ctx context.Context, path string, observeFunc func(req *pool.Message), opts ...message.Option) (Observation, error) { + req, err := c.NewObserveRequest(ctx, path, opts...) + if err != nil { + return nil, err + } + defer c.cc.ReleaseMessage(req) + return c.DoObserve(req, observeFunc) +} + +func (c *Client[C]) GetObservationRequest(token message.Token) (*pool.Message, bool) { + return c.observationHandler.GetObservationRequest(token) +} + +// NewPostRequest creates post request. +// +// Use ctx to set timeout. +// +// An error is returned if by failure to speak COAP (such as a network connectivity problem). +// Any status code doesn't cause an error. +// +// If payload is nil then content format is not used. +func (c *Client[C]) NewPostRequest(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) { + req := c.cc.AcquireMessage(ctx) + token, err := c.GetToken() + if err != nil { + c.cc.ReleaseMessage(req) + return nil, err + } + err = req.SetupPost(path, token, contentFormat, payload, opts...) + if err != nil { + c.cc.ReleaseMessage(req) + return nil, err + } + return req, nil +} + +// Post issues a POST to the specified path. +// +// Use ctx to set timeout. +// +// An error is returned if by failure to speak COAP (such as a network connectivity problem). +// Any status code doesn't cause an error. +// +// If payload is nil then content format is not used. +func (c *Client[C]) Post(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) { + req, err := c.NewPostRequest(ctx, path, contentFormat, payload, opts...) + if err != nil { + return nil, fmt.Errorf("cannot create post request: %w", err) + } + defer c.cc.ReleaseMessage(req) + return c.Do(req) +} + +// NewPutRequest creates put request. +// +// Use ctx to set timeout. +// +// If payload is nil then content format is not used. +func (c *Client[C]) NewPutRequest(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) { + req := c.cc.AcquireMessage(ctx) + token, err := c.GetToken() + if err != nil { + c.cc.ReleaseMessage(req) + return nil, err + } + err = req.SetupPut(path, token, contentFormat, payload, opts...) + if err != nil { + c.cc.ReleaseMessage(req) + return nil, err + } + return req, nil +} + +// Put issues a PUT to the specified path. +// +// Use ctx to set timeout. +// +// An error is returned if by failure to speak COAP (such as a network connectivity problem). +// Any status code doesn't cause an error. +// +// If payload is nil then content format is not used. +func (c *Client[C]) Put(ctx context.Context, path string, contentFormat message.MediaType, payload io.ReadSeeker, opts ...message.Option) (*pool.Message, error) { + req, err := c.NewPutRequest(ctx, path, contentFormat, payload, opts...) + if err != nil { + return nil, fmt.Errorf("cannot create put request: %w", err) + } + defer c.cc.ReleaseMessage(req) + return c.Do(req) +} + +// NewDeleteRequest creates delete request. +// +// Use ctx to set timeout. +func (c *Client[C]) NewDeleteRequest(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) { + req := c.cc.AcquireMessage(ctx) + token, err := c.GetToken() + if err != nil { + c.cc.ReleaseMessage(req) + return nil, err + } + err = req.SetupDelete(path, token, opts...) + if err != nil { + c.cc.ReleaseMessage(req) + return nil, err + } + return req, nil +} + +// Delete deletes the resource identified by the request path. +// +// Use ctx to set timeout. +func (c *Client[C]) Delete(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) { + req, err := c.NewDeleteRequest(ctx, path, opts...) + if err != nil { + return nil, fmt.Errorf("cannot create delete request: %w", err) + } + defer c.cc.ReleaseMessage(req) + return c.Do(req) +} + +// Ping issues a PING to the client and waits for PONG response. +// +// Use ctx to set timeout. +func (c *Client[C]) Ping(ctx context.Context) error { + resp := make(chan bool, 1) + receivedPong := func() { + select { + case resp <- true: + default: + } + } + cancel, err := c.cc.AsyncPing(receivedPong) + if err != nil { + return err + } + defer cancel() + select { + case <-resp: + return nil + case <-ctx.Done(): + return ctx.Err() + } +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/net/client/limitParallelRequests/limitParallelRequests.go b/vendor/github.com/plgd-dev/go-coap/v3/net/client/limitParallelRequests/limitParallelRequests.go new file mode 100644 index 00000000000..c718fd21152 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/client/limitParallelRequests/limitParallelRequests.go @@ -0,0 +1,135 @@ +package limitparallelrequests + +import ( + "context" + "fmt" + "hash/crc64" + "math" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/pool" + coapSync "github.com/plgd-dev/go-coap/v3/pkg/sync" + "golang.org/x/sync/semaphore" +) + +type ( + DoFunc = func(req *pool.Message) (*pool.Message, error) + DoObserveFunc = func(req *pool.Message, observeFunc func(req *pool.Message)) (Observation, error) +) + +type Observation = interface { + Cancel(ctx context.Context, opts ...message.Option) error + Canceled() bool +} + +type endpointQueue struct { + processedCounter int64 + orderedRequest []chan struct{} +} + +type LimitParallelRequests struct { + endpointLimit int64 + limit *semaphore.Weighted + do DoFunc + doObserve DoObserveFunc + // only one request can be processed by one endpoint + endpointQueues *coapSync.Map[uint64, *endpointQueue] +} + +// New creates new LimitParallelRequests. When limit, endpointLimit == 0, then limit is not used. +func New(limit, endpointLimit int64, do DoFunc, doObserve DoObserveFunc) *LimitParallelRequests { + if limit <= 0 { + limit = math.MaxInt64 + } + if endpointLimit <= 0 { + endpointLimit = math.MaxInt64 + } + return &LimitParallelRequests{ + limit: semaphore.NewWeighted(limit), + endpointLimit: endpointLimit, + do: do, + doObserve: doObserve, + endpointQueues: coapSync.NewMap[uint64, *endpointQueue](), + } +} + +func hash(opts message.Options) uint64 { + h := crc64.New(crc64.MakeTable(crc64.ISO)) + for _, opt := range opts { + if opt.ID == message.URIPath { + _, _ = h.Write(opt.Value) // hash never returns an error + } + } + return h.Sum64() +} + +func (c *LimitParallelRequests) acquireEndpoint(ctx context.Context, endpointLimitKey uint64) error { + reqChan := make(chan struct{}) // channel is closed when request can be processed by releaseEndpoint + _, _ = c.endpointQueues.LoadOrStoreWithFunc(endpointLimitKey, func(value *endpointQueue) *endpointQueue { + if value.processedCounter < c.endpointLimit { + close(reqChan) + value.processedCounter++ + return value + } + value.orderedRequest = append(value.orderedRequest, reqChan) + return value + }, func() *endpointQueue { + close(reqChan) + return &endpointQueue{ + processedCounter: 1, + } + }) + select { + case <-ctx.Done(): + c.releaseEndpoint(endpointLimitKey) + return ctx.Err() + case <-reqChan: + return nil + } +} + +func (c *LimitParallelRequests) releaseEndpoint(endpointLimitKey uint64) { + _, _ = c.endpointQueues.ReplaceWithFunc(endpointLimitKey, func(oldValue *endpointQueue, oldLoaded bool) (newValue *endpointQueue, doDelete bool) { + if oldLoaded { + if len(oldValue.orderedRequest) > 0 { + reqChan := oldValue.orderedRequest[0] + oldValue.orderedRequest = oldValue.orderedRequest[1:] + close(reqChan) + } else { + oldValue.processedCounter-- + if oldValue.processedCounter == 0 { + return nil, true + } + } + return oldValue, false + } + return nil, true + }) +} + +func (c *LimitParallelRequests) Do(req *pool.Message) (*pool.Message, error) { + endpointLimitKey := hash(req.Options()) + if err := c.acquireEndpoint(req.Context(), endpointLimitKey); err != nil { + return nil, fmt.Errorf("cannot process request %v for client endpoint limit: %w", req, err) + } + defer c.releaseEndpoint(endpointLimitKey) + if err := c.limit.Acquire(req.Context(), 1); err != nil { + return nil, fmt.Errorf("cannot process request %v for client limit: %w", req, err) + } + defer c.limit.Release(1) + return c.do(req) +} + +func (c *LimitParallelRequests) DoObserve(req *pool.Message, observeFunc func(req *pool.Message)) (Observation, error) { + endpointLimitKey := hash(req.Options()) + if err := c.acquireEndpoint(req.Context(), endpointLimitKey); err != nil { + return nil, fmt.Errorf("cannot process observe request %v for client endpoint limit: %w", req, err) + } + defer c.releaseEndpoint(endpointLimitKey) + err := c.limit.Acquire(req.Context(), 1) + if err != nil { + return nil, fmt.Errorf("cannot process observe request %v for client limit: %w", req, err) + } + defer c.limit.Release(1) + return c.doObserve(req, observeFunc) +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/net/client/receivedMessageReader.go b/vendor/github.com/plgd-dev/go-coap/v3/net/client/receivedMessageReader.go new file mode 100644 index 00000000000..acbf4ccab0e --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/client/receivedMessageReader.go @@ -0,0 +1,96 @@ +package client + +import ( + "sync" + + "github.com/plgd-dev/go-coap/v3/message/pool" + "go.uber.org/atomic" +) + +type ReceivedMessageReaderClient interface { + Done() <-chan struct{} + ProcessReceivedMessage(req *pool.Message) +} + +type ReceivedMessageReader[C ReceivedMessageReaderClient] struct { + queue chan *pool.Message + cc C + + private struct { + mutex sync.Mutex + loopDone chan struct{} + readingMessages *atomic.Bool + } +} + +// NewReceivedMessageReader creates a new ReceivedMessageReader[C] instance. +func NewReceivedMessageReader[C ReceivedMessageReaderClient](cc C, queueSize int) *ReceivedMessageReader[C] { + r := ReceivedMessageReader[C]{ + queue: make(chan *pool.Message, queueSize), + cc: cc, + private: struct { + mutex sync.Mutex + loopDone chan struct{} + readingMessages *atomic.Bool + }{ + loopDone: make(chan struct{}), + readingMessages: atomic.NewBool(true), + }, + } + + go r.loop(r.private.loopDone, r.private.readingMessages) + return &r +} + +// C returns the channel to push received messages to. +func (r *ReceivedMessageReader[C]) C() chan<- *pool.Message { + return r.queue +} + +// The loop function continuously listens to messages. IT can be replaced with a new one by calling the TryToReplaceLoop function, +// ensuring that only one loop is reading from the queue at a time. +// The loopDone channel is used to signal when the loop should be closed. +// The readingMessages variable is used to indicate if the loop is currently reading from the queue. +// When the loop is not reading from the queue, it sets readingMessages to false, and when it starts reading again, it sets it to true. +// If the client is closed, the loop also closes. +func (r *ReceivedMessageReader[C]) loop(loopDone chan struct{}, readingMessages *atomic.Bool) { + for { + select { + // if the loop is replaced, the old loop will be closed + case <-loopDone: + return + // process received message until the queue is empty + case req := <-r.queue: + // This signalizes that the loop is not reading messages. + readingMessages.Store(false) + r.cc.ProcessReceivedMessage(req) + // This signalizes that the loop is reading messages. We call mutex because we want to ensure that TryToReplaceLoop has ended and + // loopDone is closed if it was replaced. + r.private.mutex.Lock() + readingMessages.Store(true) + r.private.mutex.Unlock() + // if the client is closed, the loop will be closed + case <-r.cc.Done(): + return + } + } +} + +// TryToReplaceLoop function attempts to replace the loop with a new one, +// but only if the loop is not currently reading messages. If the loop is reading messages, +// the function returns immediately. If the loop is not reading messages, the current loop is closed, +// and new loopDone and readingMessages channels and variables are created. +func (r *ReceivedMessageReader[C]) TryToReplaceLoop() { + r.private.mutex.Lock() + if r.private.readingMessages.Load() { + r.private.mutex.Unlock() + return + } + defer r.private.mutex.Unlock() + close(r.private.loopDone) + loopDone := make(chan struct{}) + readingMessages := atomic.NewBool(true) + r.private.loopDone = loopDone + r.private.readingMessages = readingMessages + go r.loop(loopDone, readingMessages) +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/conn.go b/vendor/github.com/plgd-dev/go-coap/v3/net/conn.go similarity index 92% rename from vendor/github.com/plgd-dev/go-coap/v2/net/conn.go rename to vendor/github.com/plgd-dev/go-coap/v3/net/conn.go index 0b42a04897e..8d6faabea99 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/net/conn.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/conn.go @@ -39,8 +39,8 @@ func (c *Conn) LocalAddr() net.Addr { return c.connection.LocalAddr() } -// Connection returns the network connection. The Conn returned is shared by all invocations of Connection, so do not modify it. -func (c *Conn) Connection() net.Conn { +// NetConn returns the underlying connection that is wrapped by c. The Conn returned is shared by all invocations of Connection, so do not modify it. +func (c *Conn) NetConn() net.Conn { return c.connection } @@ -51,7 +51,7 @@ func (c *Conn) RemoteAddr() net.Addr { // Close closes the connection. func (c *Conn) Close() error { - if !c.closed.CAS(false, true) { + if !c.closed.CompareAndSwap(false, true) { return nil } return c.connection.Close() diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/connUDP.go b/vendor/github.com/plgd-dev/go-coap/v3/net/connUDP.go similarity index 90% rename from vendor/github.com/plgd-dev/go-coap/v2/net/connUDP.go rename to vendor/github.com/plgd-dev/go-coap/v3/net/connUDP.go index 033471bae47..f749b22d265 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/net/connUDP.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/connUDP.go @@ -136,14 +136,14 @@ func IsIPv6(addr net.IP) bool { return false } -var defaultUDPConnOptions = udpConnOptions{ - errors: func(err error) { +var DefaultUDPConnConfig = UDPConnConfig{ + Errors: func(err error) { // don't log any error from fails for multicast requests }, } -type udpConnOptions struct { - errors func(err error) +type UDPConnConfig struct { + Errors func(err error) } func NewListenUDP(network, addr string, opts ...UDPOption) (*UDPConn, error) { @@ -160,13 +160,21 @@ func NewListenUDP(network, addr string, opts ...UDPOption) (*UDPConn, error) { // NewUDPConn creates connection over net.UDPConn. func NewUDPConn(network string, c *net.UDPConn, opts ...UDPOption) *UDPConn { - cfg := defaultUDPConnOptions + cfg := DefaultUDPConnConfig for _, o := range opts { - o.applyUDP(&cfg) + o.ApplyUDP(&cfg) } + laddr := c.LocalAddr() + if laddr == nil { + panic(fmt.Errorf("invalid UDP connection")) + } + addr, ok := laddr.(*net.UDPAddr) + if !ok { + panic(fmt.Errorf("invalid address type(%T), UDP address expected", laddr)) + } var pc packetConn - if IsIPv6(c.LocalAddr().(*net.UDPAddr).IP) { + if IsIPv6(addr.IP) { pc = newPacketConnIPv6(ipv6.NewPacketConn(c)) } else { pc = newPacketConnIPv4(ipv4.NewPacketConn(c)) @@ -176,7 +184,7 @@ func NewUDPConn(network string, c *net.UDPConn, opts ...UDPOption) *UDPConn { network: network, connection: c, packetConn: pc, - errors: cfg.errors, + errors: cfg.Errors, } } @@ -197,7 +205,7 @@ func (c *UDPConn) Network() string { // Close closes the connection. func (c *UDPConn) Close() error { - if !c.closed.CAS(false, true) { + if !c.closed.CompareAndSwap(false, true) { return nil } return c.connection.Close() @@ -283,7 +291,7 @@ func (c *UDPConn) WriteMulticast(ctx context.Context, raddr *net.UDPAddr, buffer return c.writeMulticast(ctx, raddr, buffer, opt) } -func (c *UDPConn) writeMulticastWithInterface(ctx context.Context, raddr *net.UDPAddr, buffer []byte, opt MulticastOptions) error { +func (c *UDPConn) writeMulticastWithInterface(raddr *net.UDPAddr, buffer []byte, opt MulticastOptions) error { if opt.Iface == nil && opt.IFaceMode == MulticastSpecificInterface { return fmt.Errorf("invalid interface") } @@ -316,14 +324,15 @@ func (c *UDPConn) writeMulticastWithInterface(ctx context.Context, raddr *net.UD return fmt.Errorf("%v", errors) } -func (c *UDPConn) writeMulticastToAllInterfaces(ctx context.Context, raddr *net.UDPAddr, buffer []byte, opt MulticastOptions) error { +func (c *UDPConn) writeMulticastToAllInterfaces(raddr *net.UDPAddr, buffer []byte, opt MulticastOptions) error { ifaces, err := net.Interfaces() if err != nil { return fmt.Errorf("cannot get interfaces for multicast connection: %w", err) } var errors []error - for _, iface := range ifaces { + for i := range ifaces { + iface := ifaces[i] if iface.Flags&net.FlagMulticast == 0 { continue } @@ -333,7 +342,7 @@ func (c *UDPConn) writeMulticastToAllInterfaces(ctx context.Context, raddr *net. specificOpt := opt specificOpt.Iface = &iface specificOpt.IFaceMode = MulticastSpecificInterface - err = c.writeMulticastWithInterface(ctx, raddr, buffer, specificOpt) + err = c.writeMulticastWithInterface(raddr, buffer, specificOpt) if err != nil { if opt.InterfaceError != nil { opt.InterfaceError(&iface, err) @@ -351,7 +360,7 @@ func (c *UDPConn) writeMulticastToAllInterfaces(ctx context.Context, raddr *net. return fmt.Errorf("%v", errors) } -func (c *UDPConn) validateMulticast(ctx context.Context, raddr *net.UDPAddr, buffer []byte, opt MulticastOptions) error { +func (c *UDPConn) validateMulticast(ctx context.Context, raddr *net.UDPAddr, opt MulticastOptions) error { select { case <-ctx.Done(): return ctx.Err() @@ -370,14 +379,14 @@ func (c *UDPConn) validateMulticast(ctx context.Context, raddr *net.UDPAddr, buf } func (c *UDPConn) writeMulticast(ctx context.Context, raddr *net.UDPAddr, buffer []byte, opt MulticastOptions) error { - err := c.validateMulticast(ctx, raddr, buffer, opt) + err := c.validateMulticast(ctx, raddr, opt) if err != nil { return err } switch opt.IFaceMode { case MulticastAllInterface: - err := c.writeMulticastToAllInterfaces(ctx, raddr, buffer, opt) + err := c.writeMulticastToAllInterfaces(raddr, buffer, opt) if err != nil { return fmt.Errorf("cannot write multicast to all interfaces: %w", err) } @@ -387,7 +396,7 @@ func (c *UDPConn) writeMulticast(ctx context.Context, raddr *net.UDPAddr, buffer return fmt.Errorf("cannot write multicast to any: %w", err) } case MulticastSpecificInterface: - err := c.writeMulticastWithInterface(ctx, raddr, buffer, opt) + err := c.writeMulticastWithInterface(raddr, buffer, opt) if err != nil { if opt.InterfaceError != nil { opt.InterfaceError(opt.Iface, err) @@ -465,3 +474,8 @@ func (c *UDPConn) JoinGroup(ifi *net.Interface, group net.Addr) error { func (c *UDPConn) LeaveGroup(ifi *net.Interface, group net.Addr) error { return c.packetConn.LeaveGroup(ifi, group) } + +// NetConn returns the underlying connection that is wrapped by c. The Conn returned is shared by all invocations of NetConn, so do not modify it. +func (c *UDPConn) NetConn() net.Conn { + return c.connection +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/net/dtlslistener.go b/vendor/github.com/plgd-dev/go-coap/v3/net/dtlslistener.go new file mode 100644 index 00000000000..b797507f6c2 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/dtlslistener.go @@ -0,0 +1,197 @@ +package net + +import ( + "context" + "errors" + "fmt" + "net" + "sync" + "time" + + dtls "github.com/pion/dtls/v2" + dtlsnet "github.com/pion/dtls/v2/pkg/net" + "github.com/pion/dtls/v2/pkg/protocol" + "github.com/pion/dtls/v2/pkg/protocol/recordlayer" + "github.com/pion/transport/v3/udp" + "go.uber.org/atomic" +) + +type GoPoolFunc = func(f func()) error + +var DefaultDTLSListenerConfig = DTLSListenerConfig{ + GoPool: func(f func()) error { + go f() + return nil + }, +} + +type DTLSListenerConfig struct { + GoPool GoPoolFunc +} + +type acceptedConn struct { + conn net.Conn + err error +} + +// DTLSListener is a DTLS listener that provides accept with context. +type DTLSListener struct { + listener net.Listener + config *dtls.Config + closed atomic.Bool + goPool GoPoolFunc + acceptedConnChan chan acceptedConn + wg sync.WaitGroup + done chan struct{} +} + +func tlsPacketFilter(packet []byte) bool { + pkts, err := recordlayer.UnpackDatagram(packet) + if err != nil || len(pkts) < 1 { + return false + } + h := &recordlayer.Header{} + if err := h.Unmarshal(pkts[0]); err != nil { + return false + } + return h.ContentType == protocol.ContentTypeHandshake +} + +// NewDTLSListener creates dtls listener. +// Known networks are "udp", "udp4" (IPv4-only), "udp6" (IPv6-only). +func NewDTLSListener(network string, addr string, dtlsCfg *dtls.Config, opts ...DTLSListenerOption) (*DTLSListener, error) { + a, err := net.ResolveUDPAddr(network, addr) + if err != nil { + return nil, fmt.Errorf("cannot resolve address: %w", err) + } + cfg := DefaultDTLSListenerConfig + for _, o := range opts { + o.ApplyDTLS(&cfg) + } + + if cfg.GoPool == nil { + return nil, fmt.Errorf("empty go pool") + } + + l := DTLSListener{ + goPool: cfg.GoPool, + config: dtlsCfg, + acceptedConnChan: make(chan acceptedConn, 256), + done: make(chan struct{}), + } + connectContextMaker := dtlsCfg.ConnectContextMaker + if connectContextMaker == nil { + connectContextMaker = func() (context.Context, func()) { + return context.WithTimeout(context.Background(), 30*time.Second) + } + } + dtlsCfg.ConnectContextMaker = func() (context.Context, func()) { + ctx, cancel := connectContextMaker() + if l.closed.Load() { + cancel() + } + return ctx, cancel + } + + lc := udp.ListenConfig{ + AcceptFilter: tlsPacketFilter, + } + l.listener, err = lc.Listen(network, a) + if err != nil { + return nil, err + } + l.wg.Add(1) + go l.run() + return &l, nil +} + +func (l *DTLSListener) send(conn net.Conn, err error) { + select { + case <-l.done: + case l.acceptedConnChan <- acceptedConn{ + conn: conn, + err: err, + }: + } +} + +func (l *DTLSListener) accept() error { + c, err := l.listener.Accept() + if err != nil { + l.send(nil, err) + return err + } + err = l.goPool(func() { + l.send(dtls.Server(dtlsnet.PacketConnFromConn(c), c.RemoteAddr(), l.config)) + }) + if err != nil { + _ = c.Close() + } + return err +} + +func (l *DTLSListener) run() { + defer l.wg.Done() + for { + if l.closed.Load() { + return + } + err := l.accept() + if errors.Is(err, udp.ErrClosedListener) { + return + } + } +} + +// AcceptWithContext waits with context for a generic Conn. +func (l *DTLSListener) AcceptWithContext(ctx context.Context) (net.Conn, error) { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + if l.closed.Load() { + return nil, ErrListenerIsClosed + } + for { + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-l.done: + return nil, ErrListenerIsClosed + case d := <-l.acceptedConnChan: + err := d.err + if errors.Is(err, context.DeadlineExceeded) { + // we don't want to report error handshake deadline exceeded + continue + } + if errors.Is(err, udp.ErrClosedListener) { + return nil, ErrListenerIsClosed + } + if err != nil { + return nil, err + } + return d.conn, nil + } + } +} + +// Accept waits for a generic Conn. +func (l *DTLSListener) Accept() (net.Conn, error) { + return l.AcceptWithContext(context.Background()) +} + +// Close closes the connection. +func (l *DTLSListener) Close() error { + if !l.closed.CompareAndSwap(false, true) { + return nil + } + close(l.done) + defer l.wg.Wait() + return l.listener.Close() +} + +// Addr represents a network end point address. +func (l *DTLSListener) Addr() net.Addr { + return l.listener.Addr() +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/error.go b/vendor/github.com/plgd-dev/go-coap/v3/net/error.go similarity index 70% rename from vendor/github.com/plgd-dev/go-coap/v2/net/error.go rename to vendor/github.com/plgd-dev/go-coap/v3/net/error.go index 8dc532ee62c..be564170a24 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/net/error.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/error.go @@ -7,9 +7,11 @@ import ( "net" ) -var ErrListenerIsClosed = io.EOF -var ErrConnectionIsClosed = io.EOF -var ErrWriteInterrupted = errors.New("only part data was written to socket") +var ( + ErrListenerIsClosed = io.EOF + ErrConnectionIsClosed = io.EOF + ErrWriteInterrupted = errors.New("only part data was written to socket") +) func IsCancelOrCloseError(err error) bool { if err == nil { diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/error_unix.go b/vendor/github.com/plgd-dev/go-coap/v3/net/error_unix.go similarity index 100% rename from vendor/github.com/plgd-dev/go-coap/v2/net/error_unix.go rename to vendor/github.com/plgd-dev/go-coap/v3/net/error_unix.go diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/error_windows.go b/vendor/github.com/plgd-dev/go-coap/v3/net/error_windows.go similarity index 100% rename from vendor/github.com/plgd-dev/go-coap/v2/net/error_windows.go rename to vendor/github.com/plgd-dev/go-coap/v3/net/error_windows.go diff --git a/vendor/github.com/plgd-dev/go-coap/v3/net/monitor/inactivity/keepalive.go b/vendor/github.com/plgd-dev/go-coap/v3/net/monitor/inactivity/keepalive.go new file mode 100644 index 00000000000..dcb34fea904 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/monitor/inactivity/keepalive.go @@ -0,0 +1,63 @@ +package inactivity + +import ( + "unsafe" + + "go.uber.org/atomic" +) + +type cancelPingFunc func() + +type KeepAlive[C Conn] struct { + pongToken atomic.Uint64 + onInactive OnInactiveFunc[C] + + sendPing func(cc C, receivePong func()) (func(), error) + cancelPing atomic.UnsafePointer + numFails atomic.Uint32 + + maxRetries uint32 +} + +func NewKeepAlive[C Conn](maxRetries uint32, onInactive OnInactiveFunc[C], sendPing func(cc C, receivePong func()) (func(), error)) *KeepAlive[C] { + return &KeepAlive[C]{ + maxRetries: maxRetries, + sendPing: sendPing, + onInactive: onInactive, + } +} + +func (m *KeepAlive[C]) checkCancelPing() { + cancelPingPtr := m.cancelPing.Swap(nil) + if cancelPingPtr != nil { + cancelPing := *(*cancelPingFunc)(cancelPingPtr) + cancelPing() + } +} + +func (m *KeepAlive[C]) OnInactive(cc C) { + v := m.incrementFails() + m.checkCancelPing() + if v > m.maxRetries { + m.onInactive(cc) + return + } + pongToken := m.pongToken.Add(1) + cancel, err := m.sendPing(cc, func() { + if m.pongToken.Load() == pongToken { + m.resetFails() + } + }) + if err != nil { + return + } + m.cancelPing.Store(unsafe.Pointer(&cancel)) +} + +func (m *KeepAlive[C]) incrementFails() uint32 { + return m.numFails.Add(1) +} + +func (m *KeepAlive[C]) resetFails() { + m.numFails.Store(0) +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/net/monitor/inactivity/monitor.go b/vendor/github.com/plgd-dev/go-coap/v3/net/monitor/inactivity/monitor.go new file mode 100644 index 00000000000..33c020217a8 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/monitor/inactivity/monitor.go @@ -0,0 +1,68 @@ +package inactivity + +import ( + "context" + "sync/atomic" + "time" +) + +type OnInactiveFunc[C Conn] func(cc C) + +type Conn = interface { + Context() context.Context + Close() error +} + +type Monitor[C Conn] struct { + lastActivity atomic.Value + duration time.Duration + onInactive OnInactiveFunc[C] +} + +func (m *Monitor[C]) Notify() { + m.lastActivity.Store(time.Now()) +} + +func (m *Monitor[C]) LastActivity() time.Time { + if t, ok := m.lastActivity.Load().(time.Time); ok { + return t + } + return time.Time{} +} + +func CloseConn(cc Conn) { + // call cc.Close() directly to check and handle error if necessary + _ = cc.Close() +} + +func New[C Conn](duration time.Duration, onInactive OnInactiveFunc[C]) *Monitor[C] { + m := &Monitor[C]{ + duration: duration, + onInactive: onInactive, + } + m.Notify() + return m +} + +func (m *Monitor[C]) CheckInactivity(now time.Time, cc C) { + if m.onInactive == nil || m.duration == time.Duration(0) { + return + } + if now.After(m.LastActivity().Add(m.duration)) { + m.onInactive(cc) + } +} + +type NilMonitor[C Conn] struct{} + +func (m *NilMonitor[C]) CheckInactivity(time.Time, C) { + // do nothing +} + +func (m *NilMonitor[C]) Notify() { + // do nothing +} + +func NewNilMonitor[C Conn]() *NilMonitor[C] { + return &NilMonitor[C]{} +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/net/observation/handler.go b/vendor/github.com/plgd-dev/go-coap/v3/net/observation/handler.go new file mode 100644 index 00000000000..f170269f547 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/observation/handler.go @@ -0,0 +1,267 @@ +package observation + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" + "github.com/plgd-dev/go-coap/v3/pkg/errors" + coapSync "github.com/plgd-dev/go-coap/v3/pkg/sync" + "go.uber.org/atomic" +) + +type DoFunc = func(req *pool.Message) (*pool.Message, error) + +type Client interface { + Context() context.Context + WriteMessage(req *pool.Message) error + ReleaseMessage(msg *pool.Message) + AcquireMessage(ctx context.Context) *pool.Message +} + +// The HandlerFunc type is an adapter to allow the use of +// ordinary functions as COAP handlers. +type HandlerFunc[C Client] func(*responsewriter.ResponseWriter[C], *pool.Message) + +type Handler[C Client] struct { + cc C + observations *coapSync.Map[uint64, *Observation[C]] + next HandlerFunc[C] + do DoFunc +} + +func (h *Handler[C]) Handle(w *responsewriter.ResponseWriter[C], r *pool.Message) { + if o, ok := h.observations.Load(r.Token().Hash()); ok { + o.handle(r) + return + } + h.next(w, r) +} + +func (h *Handler[C]) client() C { + return h.cc +} + +func (h *Handler[C]) NewObservation(req *pool.Message, observeFunc func(req *pool.Message)) (*Observation[C], error) { + observe, err := req.Observe() + if err != nil { + return nil, fmt.Errorf("cannot get observe option: %w", err) + } + if observe != 0 { + return nil, fmt.Errorf("invalid value of observe(%v): expected 0", observe) + } + token := req.Token() + if len(token) == 0 { + return nil, fmt.Errorf("empty token") + } + options, err := req.Options().Clone() + if err != nil { + return nil, fmt.Errorf("cannot clone options: %w", err) + } + respObservationChan := make(chan respObservationMessage, 1) + o := newObservation(message.Message{ + Token: req.Token(), + Code: req.Code(), + Options: options, + }, h, observeFunc, respObservationChan) + defer func(err *error) { + if *err != nil { + o.cleanUp() + } + }(&err) + if _, loaded := h.observations.LoadOrStore(token.Hash(), o); loaded { + err = errors.ErrKeyAlreadyExists + return nil, err + } + + err = h.cc.WriteMessage(req) + if err != nil { + return nil, err + } + select { + case <-req.Context().Done(): + err = req.Context().Err() + return nil, err + case <-h.cc.Context().Done(): + err = fmt.Errorf("connection was closed: %w", h.cc.Context().Err()) + return nil, err + case resp := <-respObservationChan: + if resp.code != codes.Content && resp.code != codes.Valid { + err = fmt.Errorf("unexpected return code(%v)", resp.code) + return nil, err + } + if resp.notSupported { + o.cleanUp() + } + return o, nil + } +} + +func (h *Handler[C]) GetObservation(key uint64) (*Observation[C], bool) { + return h.observations.Load(key) +} + +// GetObservationRequest returns observation request for token +func (h *Handler[C]) GetObservationRequest(token message.Token) (*pool.Message, bool) { + obs, ok := h.GetObservation(token.Hash()) + if !ok { + return nil, false + } + req := obs.Request() + msg := h.cc.AcquireMessage(h.cc.Context()) + msg.ResetOptionsTo(req.Options) + msg.SetCode(req.Code) + msg.SetToken(req.Token) + return msg, true +} + +func (h *Handler[C]) pullOutObservation(key uint64) (*Observation[C], bool) { + return h.observations.LoadAndDelete(key) +} + +func NewHandler[C Client](cc C, next HandlerFunc[C], do DoFunc) *Handler[C] { + return &Handler[C]{ + cc: cc, + observations: coapSync.NewMap[uint64, *Observation[C]](), + next: next, + do: do, + } +} + +type respObservationMessage struct { + code codes.Code + notSupported bool +} + +// Observation represents subscription to resource on the server +type Observation[C Client] struct { + req message.Message + observeFunc func(req *pool.Message) + respObservationChan chan respObservationMessage + waitForResponse atomic.Bool + observationHandler *Handler[C] + + private struct { // members guarded by mutex + mutex sync.Mutex + obsSequence uint32 + lastEvent time.Time + etag []byte + } +} + +func (o *Observation[C]) Canceled() bool { + _, ok := o.observationHandler.GetObservation(o.req.Token.Hash()) + return !ok +} + +func newObservation[C Client](req message.Message, observationHandler *Handler[C], observeFunc func(req *pool.Message), respObservationChan chan respObservationMessage) *Observation[C] { + return &Observation[C]{ + req: req, + waitForResponse: *atomic.NewBool(true), + respObservationChan: respObservationChan, + observeFunc: observeFunc, + observationHandler: observationHandler, + } +} + +func (o *Observation[C]) handle(r *pool.Message) { + if o.waitForResponse.CompareAndSwap(true, false) { + select { + case o.respObservationChan <- respObservationMessage{ + code: r.Code(), + notSupported: !r.HasOption(message.Observe), + }: + default: + } + o.respObservationChan = nil + } + if o.wantBeNotified(r) { + o.observeFunc(r) + } +} + +func (o *Observation[C]) cleanUp() bool { + // we can ignore err during cleanUp, if err != nil then some other + // part of code already removed the handler for the token + _, ok := o.observationHandler.pullOutObservation(o.req.Token.Hash()) + return ok +} + +func (o *Observation[C]) client() C { + return o.observationHandler.client() +} + +func (o *Observation[C]) Request() message.Message { + return o.req +} + +func (o *Observation[C]) etag() []byte { + o.private.mutex.Lock() + defer o.private.mutex.Unlock() + return o.private.etag +} + +// Cancel remove observation from server. For recreate observation use Observe. +func (o *Observation[C]) Cancel(ctx context.Context, opts ...message.Option) error { + if !o.cleanUp() { + // observation was already cleanup + return nil + } + + req := o.client().AcquireMessage(ctx) + defer o.client().ReleaseMessage(req) + req.ResetOptionsTo(opts) + req.SetCode(codes.GET) + req.SetObserve(1) + if path, err := o.req.Options.Path(); err == nil { + if err := req.SetPath(path); err != nil { + return fmt.Errorf("cannot set path(%v): %w", path, err) + } + } + req.SetToken(o.req.Token) + etag := o.etag() + if len(etag) > 0 { + _ = req.SetETag(etag) // ignore invalid etag + } + resp, err := o.observationHandler.do(req) + if err != nil { + return err + } + defer o.client().ReleaseMessage(resp) + if resp.Code() != codes.Content && resp.Code() != codes.Valid { + return fmt.Errorf("unexpected return code(%v)", resp.Code()) + } + return nil +} + +func (o *Observation[C]) wantBeNotified(r *pool.Message) bool { + obsSequence, err := r.Observe() + if err != nil { + return true + } + now := time.Now() + + o.private.mutex.Lock() + defer o.private.mutex.Unlock() + if !ValidSequenceNumber(o.private.obsSequence, obsSequence, o.private.lastEvent, now) { + return false + } + + o.private.obsSequence = obsSequence + o.private.lastEvent = now + if etag, err := r.ETag(); err == nil { + if cap(o.private.etag) < len(etag) { + o.private.etag = make([]byte, len(etag)) + } + if len(o.private.etag) != len(etag) { + o.private.etag = o.private.etag[:len(etag)] + } + copy(o.private.etag, etag) + } + return true +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/observation/observation.go b/vendor/github.com/plgd-dev/go-coap/v3/net/observation/observation.go similarity index 100% rename from vendor/github.com/plgd-dev/go-coap/v2/net/observation/observation.go rename to vendor/github.com/plgd-dev/go-coap/v3/net/observation/observation.go diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/options.go b/vendor/github.com/plgd-dev/go-coap/v3/net/options.go similarity index 81% rename from vendor/github.com/plgd-dev/go-coap/v2/net/options.go rename to vendor/github.com/plgd-dev/go-coap/v3/net/options.go index 2bcafc2be23..c037ef9c28d 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/net/options.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/options.go @@ -4,15 +4,15 @@ import "net" // A UDPOption sets options such as errors parameters, etc. type UDPOption interface { - applyUDP(*udpConnOptions) + ApplyUDP(*UDPConnConfig) } type ErrorsOpt struct { errors func(err error) } -func (h ErrorsOpt) applyUDP(o *udpConnOptions) { - o.errors = h.errors +func (h ErrorsOpt) ApplyUDP(o *UDPConnConfig) { + o.Errors = h.errors } func WithErrors(v func(err error)) ErrorsOpt { @@ -91,6 +91,7 @@ type MulticastHoplimitOpt struct { func (m MulticastHoplimitOpt) applyMC(o *MulticastOptions) { o.HopLimit = m.hoplimit } + func WithMulticastHoplimit(hoplimit int) MulticastOption { return &MulticastHoplimitOpt{hoplimit: hoplimit} } @@ -102,6 +103,7 @@ type MulticastSourceOpt struct { func (m MulticastSourceOpt) applyMC(o *MulticastOptions) { o.Source = &m.source } + func WithMulticastSource(source net.IP) MulticastOption { return &MulticastSourceOpt{source: source} } @@ -118,3 +120,24 @@ func (m MulticastInterfaceErrorOpt) applyMC(o *MulticastOptions) { func WithMulticastInterfaceError(interfaceError InterfaceError) MulticastOption { return &MulticastInterfaceErrorOpt{interfaceError: interfaceError} } + +// A DTLSListenerOption sets options such as gopool. +type DTLSListenerOption interface { + ApplyDTLS(*DTLSListenerConfig) +} + +// GoPoolOpt gopool option. +type GoPoolOpt struct { + goPool GoPoolFunc +} + +func (o GoPoolOpt) ApplyDTLS(cfg *DTLSListenerConfig) { + cfg.GoPool = o.goPool +} + +// WithGoPool sets function for managing spawning go routines +// for handling incoming request's. +// Eg: https://github.com/panjf2000/ants. +func WithGoPool(goPool GoPoolFunc) GoPoolOpt { + return GoPoolOpt{goPool: goPool} +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/net/responsewriter/responseWriter.go b/vendor/github.com/plgd-dev/go-coap/v3/net/responsewriter/responseWriter.go new file mode 100644 index 00000000000..44fe2d9f704 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/responsewriter/responseWriter.go @@ -0,0 +1,79 @@ +package responsewriter + +import ( + "io" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/noresponse" + "github.com/plgd-dev/go-coap/v3/message/pool" +) + +type Client interface { + ReleaseMessage(msg *pool.Message) +} + +// A ResponseWriter is used by an COAP handler to construct an COAP response. +type ResponseWriter[C Client] struct { + noResponseValue *uint32 + response *pool.Message + cc C +} + +func New[C Client](response *pool.Message, cc C, requestOptions ...message.Option) *ResponseWriter[C] { + var noResponseValue *uint32 + if len(requestOptions) > 0 { + reqOpts := message.Options(requestOptions) + v, err := reqOpts.GetUint32(message.NoResponse) + if err == nil { + noResponseValue = &v + } + } + + return &ResponseWriter[C]{ + response: response, + cc: cc, + noResponseValue: noResponseValue, + } +} + +// SetResponse simplifies the setup of the response for the request. ETags must be set via options. For advanced setup, use Message(). +func (r *ResponseWriter[C]) SetResponse(code codes.Code, contentFormat message.MediaType, d io.ReadSeeker, opts ...message.Option) error { + if r.noResponseValue != nil { + err := noresponse.IsNoResponseCode(code, *r.noResponseValue) + if err != nil { + return err + } + } + + r.response.SetCode(code) + r.response.ResetOptionsTo(opts) + if d != nil { + r.response.SetContentFormat(contentFormat) + r.response.SetBody(d) + } + return nil +} + +// SetMessage replaces the response message. The original message was released to the message pool, so don't use it any more. Ensure that Token, MessageID(udp), and Type(udp) messages are paired correctly. +func (r *ResponseWriter[C]) SetMessage(m *pool.Message) { + r.cc.ReleaseMessage(r.response) + r.response = m +} + +// Message direct access to the response. +func (r *ResponseWriter[C]) Message() *pool.Message { + return r.response +} + +// Swap message in response without releasing. +func (r *ResponseWriter[C]) Swap(m *pool.Message) *pool.Message { + tmp := r.response + r.response = m + return tmp +} + +// CConn peer connection. +func (r *ResponseWriter[C]) Conn() C { + return r.cc +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/tcplistener.go b/vendor/github.com/plgd-dev/go-coap/v3/net/tcplistener.go similarity index 97% rename from vendor/github.com/plgd-dev/go-coap/v2/net/tcplistener.go rename to vendor/github.com/plgd-dev/go-coap/v3/net/tcplistener.go index ba477698bb0..43cc955ef0a 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/net/tcplistener.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/tcplistener.go @@ -57,7 +57,7 @@ func (l *TCPListener) Accept() (net.Conn, error) { // Close closes the connection. func (l *TCPListener) Close() error { - if !l.closed.CAS(false, true) { + if !l.closed.CompareAndSwap(false, true) { return nil } return l.listener.Close() diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/tlslistener.go b/vendor/github.com/plgd-dev/go-coap/v3/net/tlslistener.go similarity index 96% rename from vendor/github.com/plgd-dev/go-coap/v2/net/tlslistener.go rename to vendor/github.com/plgd-dev/go-coap/v3/net/tlslistener.go index 874af36ab24..df12b1a66c3 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/net/tlslistener.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/net/tlslistener.go @@ -54,7 +54,7 @@ func (l *TLSListener) Accept() (net.Conn, error) { // Close closes the connection. func (l *TLSListener) Close() error { - if !l.closed.CAS(false, true) { + if !l.closed.CompareAndSwap(false, true) { return nil } return l.listener.Close() diff --git a/vendor/github.com/plgd-dev/go-coap/v2/net/udp.go b/vendor/github.com/plgd-dev/go-coap/v3/net/udp.go similarity index 100% rename from vendor/github.com/plgd-dev/go-coap/v2/net/udp.go rename to vendor/github.com/plgd-dev/go-coap/v3/net/udp.go diff --git a/vendor/github.com/plgd-dev/go-coap/v3/options/commonOptions.go b/vendor/github.com/plgd-dev/go-coap/v3/options/commonOptions.go new file mode 100644 index 00000000000..535bb8bcb86 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/options/commonOptions.go @@ -0,0 +1,786 @@ +package options + +import ( + "context" + "fmt" + "net" + "time" + + dtlsServer "github.com/plgd-dev/go-coap/v3/dtls/server" + "github.com/plgd-dev/go-coap/v3/message/pool" + "github.com/plgd-dev/go-coap/v3/mux" + "github.com/plgd-dev/go-coap/v3/net/blockwise" + "github.com/plgd-dev/go-coap/v3/net/client" + "github.com/plgd-dev/go-coap/v3/net/monitor/inactivity" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" + "github.com/plgd-dev/go-coap/v3/options/config" + "github.com/plgd-dev/go-coap/v3/pkg/runner/periodic" + tcpClient "github.com/plgd-dev/go-coap/v3/tcp/client" + tcpServer "github.com/plgd-dev/go-coap/v3/tcp/server" + udpClient "github.com/plgd-dev/go-coap/v3/udp/client" + udpServer "github.com/plgd-dev/go-coap/v3/udp/server" +) + +type ErrorFunc = config.ErrorFunc + +type Handler interface { + tcpClient.HandlerFunc | udpClient.HandlerFunc +} + +// HandlerFuncOpt handler function option. +type HandlerFuncOpt[H Handler] struct { + h H +} + +func panicForInvalidHandlerFunc(t, exp any) { + panic(fmt.Errorf("invalid HandlerFunc type %T, expected %T", t, exp)) +} + +func (o HandlerFuncOpt[H]) TCPServerApply(cfg *tcpServer.Config) { + switch v := any(o.h).(type) { + case tcpClient.HandlerFunc: + cfg.Handler = v + default: + var exp tcpClient.HandlerFunc + panicForInvalidHandlerFunc(v, exp) + } +} + +func (o HandlerFuncOpt[H]) TCPClientApply(cfg *tcpClient.Config) { + switch v := any(o.h).(type) { + case tcpClient.HandlerFunc: + cfg.Handler = v + default: + var exp tcpClient.HandlerFunc + panicForInvalidHandlerFunc(v, exp) + } +} + +func (o HandlerFuncOpt[H]) UDPServerApply(cfg *udpServer.Config) { + switch v := any(o.h).(type) { + case udpClient.HandlerFunc: + cfg.Handler = v + default: + var exp udpClient.HandlerFunc + panicForInvalidHandlerFunc(v, exp) + } +} + +func (o HandlerFuncOpt[H]) DTLSServerApply(cfg *dtlsServer.Config) { + switch v := any(o.h).(type) { + case udpClient.HandlerFunc: + cfg.Handler = v + default: + var exp udpClient.HandlerFunc + panicForInvalidHandlerFunc(v, exp) + } +} + +func (o HandlerFuncOpt[H]) UDPClientApply(cfg *udpClient.Config) { + switch v := any(o.h).(type) { + case udpClient.HandlerFunc: + cfg.Handler = v + default: + var t udpClient.HandlerFunc + panicForInvalidHandlerFunc(v, t) + } +} + +// WithHandlerFunc set handle for handling request's. +func WithHandlerFunc[H Handler](h H) HandlerFuncOpt[H] { + return HandlerFuncOpt[H]{h: h} +} + +// HandlerFuncOpt handler function option. +type MuxHandlerOpt struct { + m mux.Handler +} + +func (o MuxHandlerOpt) TCPServerApply(cfg *tcpServer.Config) { + cfg.Handler = mux.ToHandler[*tcpClient.Conn](o.m) +} + +func (o MuxHandlerOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.Handler = mux.ToHandler[*tcpClient.Conn](o.m) +} + +func (o MuxHandlerOpt) UDPServerApply(cfg *udpServer.Config) { + cfg.Handler = mux.ToHandler[*udpClient.Conn](o.m) +} + +func (o MuxHandlerOpt) DTLSServerApply(cfg *dtlsServer.Config) { + cfg.Handler = mux.ToHandler[*udpClient.Conn](o.m) +} + +func (o MuxHandlerOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.Handler = mux.ToHandler[*udpClient.Conn](o.m) +} + +// WithMux set's multiplexer for handle requests. +func WithMux(m mux.Handler) MuxHandlerOpt { + return MuxHandlerOpt{ + m: m, + } +} + +// ContextOpt handler function option. +type ContextOpt struct { + ctx context.Context +} + +func (o ContextOpt) TCPServerApply(cfg *tcpServer.Config) { + cfg.Ctx = o.ctx +} + +func (o ContextOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.Ctx = o.ctx +} + +func (o ContextOpt) UDPServerApply(cfg *udpServer.Config) { + cfg.Ctx = o.ctx +} + +func (o ContextOpt) DTLSServerApply(cfg *dtlsServer.Config) { + cfg.Ctx = o.ctx +} + +func (o ContextOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.Ctx = o.ctx +} + +// WithContext set's parent context of server. +func WithContext(ctx context.Context) ContextOpt { + return ContextOpt{ctx: ctx} +} + +// MaxMessageSizeOpt handler function option. +type MaxMessageSizeOpt struct { + maxMessageSize uint32 +} + +func (o MaxMessageSizeOpt) TCPServerApply(cfg *tcpServer.Config) { + cfg.MaxMessageSize = o.maxMessageSize +} + +func (o MaxMessageSizeOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.MaxMessageSize = o.maxMessageSize +} + +func (o MaxMessageSizeOpt) UDPServerApply(cfg *udpServer.Config) { + cfg.MaxMessageSize = o.maxMessageSize +} + +func (o MaxMessageSizeOpt) DTLSServerApply(cfg *dtlsServer.Config) { + cfg.MaxMessageSize = o.maxMessageSize +} + +func (o MaxMessageSizeOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.MaxMessageSize = o.maxMessageSize +} + +// WithMaxMessageSize limit size of processed message. +func WithMaxMessageSize(maxMessageSize uint32) MaxMessageSizeOpt { + return MaxMessageSizeOpt{maxMessageSize: maxMessageSize} +} + +// ErrorsOpt errors option. +type ErrorsOpt struct { + errors ErrorFunc +} + +func (o ErrorsOpt) TCPServerApply(cfg *tcpServer.Config) { + cfg.Errors = o.errors +} + +func (o ErrorsOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.Errors = o.errors +} + +func (o ErrorsOpt) UDPServerApply(cfg *udpServer.Config) { + cfg.Errors = o.errors +} + +func (o ErrorsOpt) DTLSServerApply(cfg *dtlsServer.Config) { + cfg.Errors = o.errors +} + +func (o ErrorsOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.Errors = o.errors +} + +// WithErrors set function for logging error. +func WithErrors(errors ErrorFunc) ErrorsOpt { + return ErrorsOpt{errors: errors} +} + +// ProcessReceivedMessageOpt gopool option. +type ProcessReceivedMessageOpt[C responsewriter.Client] struct { + ProcessReceivedMessageFunc config.ProcessReceivedMessageFunc[C] +} + +func panicForInvalidProcessReceivedMessageFunc(t, exp any) { + panic(fmt.Errorf("invalid ProcessReceivedMessageFunc type %T, expected %T", t, exp)) +} + +func (o ProcessReceivedMessageOpt[C]) TCPServerApply(cfg *tcpServer.Config) { + switch v := any(o.ProcessReceivedMessageFunc).(type) { + case config.ProcessReceivedMessageFunc[*tcpClient.Conn]: + cfg.ProcessReceivedMessage = v + default: + var t config.ProcessReceivedMessageFunc[*tcpClient.Conn] + panicForInvalidProcessReceivedMessageFunc(v, t) + } +} + +func (o ProcessReceivedMessageOpt[C]) TCPClientApply(cfg *tcpClient.Config) { + switch v := any(o.ProcessReceivedMessageFunc).(type) { + case config.ProcessReceivedMessageFunc[*tcpClient.Conn]: + cfg.ProcessReceivedMessage = v + default: + var t config.ProcessReceivedMessageFunc[*tcpClient.Conn] + panicForInvalidProcessReceivedMessageFunc(v, t) + } +} + +func (o ProcessReceivedMessageOpt[C]) UDPServerApply(cfg *udpServer.Config) { + switch v := any(o.ProcessReceivedMessageFunc).(type) { + case config.ProcessReceivedMessageFunc[*udpClient.Conn]: + cfg.ProcessReceivedMessage = v + default: + var t config.ProcessReceivedMessageFunc[*udpClient.Conn] + panicForInvalidProcessReceivedMessageFunc(v, t) + } +} + +func (o ProcessReceivedMessageOpt[C]) DTLSServerApply(cfg *dtlsServer.Config) { + switch v := any(o.ProcessReceivedMessageFunc).(type) { + case config.ProcessReceivedMessageFunc[*udpClient.Conn]: + cfg.ProcessReceivedMessage = v + default: + var t config.ProcessReceivedMessageFunc[*udpClient.Conn] + panicForInvalidProcessReceivedMessageFunc(v, t) + } +} + +func (o ProcessReceivedMessageOpt[C]) UDPClientApply(cfg *udpClient.Config) { + switch v := any(o.ProcessReceivedMessageFunc).(type) { + case config.ProcessReceivedMessageFunc[*udpClient.Conn]: + cfg.ProcessReceivedMessage = v + default: + var t config.ProcessReceivedMessageFunc[*udpClient.Conn] + panicForInvalidProcessReceivedMessageFunc(v, t) + } +} + +func WithProcessReceivedMessageFunc[C responsewriter.Client](processReceivedMessageFunc config.ProcessReceivedMessageFunc[C]) ProcessReceivedMessageOpt[C] { + return ProcessReceivedMessageOpt[C]{ProcessReceivedMessageFunc: processReceivedMessageFunc} +} + +type ( + UDPOnInactive = func(cc *udpClient.Conn) + TCPOnInactive = func(cc *tcpClient.Conn) +) + +type OnInactiveFunc interface { + UDPOnInactive | TCPOnInactive +} + +func panicForInvalidOnInactiveFunc(t, exp any) { + panic(fmt.Errorf("invalid OnInactiveFunc type %T, expected %T", t, exp)) +} + +// KeepAliveOpt keepalive option. +type KeepAliveOpt[C OnInactiveFunc] struct { + timeout time.Duration + onInactive C + maxRetries uint32 +} + +func (o KeepAliveOpt[C]) toTCPCreateInactivityMonitor(onInactive TCPOnInactive) func() tcpClient.InactivityMonitor { + return func() tcpClient.InactivityMonitor { + keepalive := inactivity.NewKeepAlive(o.maxRetries, onInactive, func(cc *tcpClient.Conn, receivePong func()) (func(), error) { + return cc.AsyncPing(receivePong) + }) + return inactivity.New(o.timeout/time.Duration(o.maxRetries+1), keepalive.OnInactive) + } +} + +func (o KeepAliveOpt[C]) toUDPCreateInactivityMonitor(onInactive UDPOnInactive) func() udpClient.InactivityMonitor { + return func() udpClient.InactivityMonitor { + keepalive := inactivity.NewKeepAlive(o.maxRetries, onInactive, func(cc *udpClient.Conn, receivePong func()) (func(), error) { + return cc.AsyncPing(receivePong) + }) + return inactivity.New(o.timeout/time.Duration(o.maxRetries+1), keepalive.OnInactive) + } +} + +func (o KeepAliveOpt[C]) TCPServerApply(cfg *tcpServer.Config) { + switch onInactive := any(o.onInactive).(type) { + case TCPOnInactive: + cfg.CreateInactivityMonitor = o.toTCPCreateInactivityMonitor(onInactive) + default: + var t TCPOnInactive + panicForInvalidOnInactiveFunc(onInactive, t) + } +} + +func (o KeepAliveOpt[C]) TCPClientApply(cfg *tcpClient.Config) { + switch onInactive := any(o.onInactive).(type) { + case TCPOnInactive: + cfg.CreateInactivityMonitor = o.toTCPCreateInactivityMonitor(onInactive) + default: + var t TCPOnInactive + panicForInvalidOnInactiveFunc(onInactive, t) + } +} + +func (o KeepAliveOpt[C]) UDPServerApply(cfg *udpServer.Config) { + switch onInactive := any(o.onInactive).(type) { + case UDPOnInactive: + cfg.CreateInactivityMonitor = o.toUDPCreateInactivityMonitor(onInactive) + default: + var t UDPOnInactive + panicForInvalidOnInactiveFunc(onInactive, t) + } +} + +func (o KeepAliveOpt[C]) DTLSServerApply(cfg *dtlsServer.Config) { + switch onInactive := any(o.onInactive).(type) { + case UDPOnInactive: + cfg.CreateInactivityMonitor = o.toUDPCreateInactivityMonitor(onInactive) + default: + var t UDPOnInactive + panicForInvalidOnInactiveFunc(onInactive, t) + } +} + +func (o KeepAliveOpt[C]) UDPClientApply(cfg *udpClient.Config) { + switch onInactive := any(o.onInactive).(type) { + case UDPOnInactive: + cfg.CreateInactivityMonitor = o.toUDPCreateInactivityMonitor(onInactive) + default: + var t UDPOnInactive + panicForInvalidOnInactiveFunc(onInactive, t) + } +} + +// WithKeepAlive monitoring's client connection's. +func WithKeepAlive[C OnInactiveFunc](maxRetries uint32, timeout time.Duration, onInactive C) KeepAliveOpt[C] { + return KeepAliveOpt[C]{ + maxRetries: maxRetries, + timeout: timeout, + onInactive: onInactive, + } +} + +// InactivityMonitorOpt notifies when a connection was inactive for a given duration. +type InactivityMonitorOpt[C OnInactiveFunc] struct { + duration time.Duration + onInactive C +} + +func (o InactivityMonitorOpt[C]) toTCPCreateInactivityMonitor(onInactive TCPOnInactive) func() tcpClient.InactivityMonitor { + return func() tcpClient.InactivityMonitor { + return inactivity.New(o.duration, onInactive) + } +} + +func (o InactivityMonitorOpt[C]) toUDPCreateInactivityMonitor(onInactive UDPOnInactive) func() udpClient.InactivityMonitor { + return func() udpClient.InactivityMonitor { + return inactivity.New(o.duration, onInactive) + } +} + +func (o InactivityMonitorOpt[C]) TCPServerApply(cfg *tcpServer.Config) { + switch onInactive := any(o.onInactive).(type) { + case TCPOnInactive: + cfg.CreateInactivityMonitor = o.toTCPCreateInactivityMonitor(onInactive) + default: + var t TCPOnInactive + panicForInvalidOnInactiveFunc(onInactive, t) + } +} + +func (o InactivityMonitorOpt[C]) TCPClientApply(cfg *tcpClient.Config) { + switch onInactive := any(o.onInactive).(type) { + case TCPOnInactive: + cfg.CreateInactivityMonitor = o.toTCPCreateInactivityMonitor(onInactive) + default: + var t TCPOnInactive + panicForInvalidOnInactiveFunc(onInactive, t) + } +} + +func (o InactivityMonitorOpt[C]) UDPServerApply(cfg *udpServer.Config) { + switch onInactive := any(o.onInactive).(type) { + case UDPOnInactive: + cfg.CreateInactivityMonitor = o.toUDPCreateInactivityMonitor(onInactive) + default: + var t UDPOnInactive + panicForInvalidOnInactiveFunc(onInactive, t) + } +} + +func (o InactivityMonitorOpt[C]) DTLSServerApply(cfg *dtlsServer.Config) { + switch onInactive := any(o.onInactive).(type) { + case UDPOnInactive: + cfg.CreateInactivityMonitor = o.toUDPCreateInactivityMonitor(onInactive) + default: + var t UDPOnInactive + panicForInvalidOnInactiveFunc(onInactive, t) + } +} + +func (o InactivityMonitorOpt[C]) UDPClientApply(cfg *udpClient.Config) { + switch onInactive := any(o.onInactive).(type) { + case UDPOnInactive: + cfg.CreateInactivityMonitor = o.toUDPCreateInactivityMonitor(onInactive) + default: + var t UDPOnInactive + panicForInvalidOnInactiveFunc(onInactive, t) + } +} + +// WithInactivityMonitor set deadline's for read operations over client connection. +func WithInactivityMonitor[C OnInactiveFunc](duration time.Duration, onInactive C) InactivityMonitorOpt[C] { + return InactivityMonitorOpt[C]{ + duration: duration, + onInactive: onInactive, + } +} + +// NetOpt network option. +type NetOpt struct { + net string +} + +func (o NetOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.Net = o.net +} + +func (o NetOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.Net = o.net +} + +// WithNetwork define's tcp version (udp4, udp6, tcp) for client. +func WithNetwork(net string) NetOpt { + return NetOpt{net: net} +} + +// PeriodicRunnerOpt function which is executed in every ticks +type PeriodicRunnerOpt struct { + periodicRunner periodic.Func +} + +func (o PeriodicRunnerOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.PeriodicRunner = o.periodicRunner +} + +func (o PeriodicRunnerOpt) TCPServerApply(cfg *tcpServer.Config) { + cfg.PeriodicRunner = o.periodicRunner +} + +func (o PeriodicRunnerOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.PeriodicRunner = o.periodicRunner +} + +func (o PeriodicRunnerOpt) UDPServerApply(cfg *udpServer.Config) { + cfg.PeriodicRunner = o.periodicRunner +} + +func (o PeriodicRunnerOpt) DTLSServerApply(cfg *dtlsServer.Config) { + cfg.PeriodicRunner = o.periodicRunner +} + +// WithPeriodicRunner set function which is executed in every ticks. +func WithPeriodicRunner(periodicRunner periodic.Func) PeriodicRunnerOpt { + return PeriodicRunnerOpt{periodicRunner: periodicRunner} +} + +// BlockwiseOpt network option. +type BlockwiseOpt struct { + transferTimeout time.Duration + enable bool + szx blockwise.SZX +} + +func (o BlockwiseOpt) UDPServerApply(cfg *udpServer.Config) { + cfg.BlockwiseEnable = o.enable + cfg.BlockwiseSZX = o.szx + cfg.BlockwiseTransferTimeout = o.transferTimeout +} + +func (o BlockwiseOpt) DTLSServerApply(cfg *dtlsServer.Config) { + cfg.BlockwiseEnable = o.enable + cfg.BlockwiseSZX = o.szx + cfg.BlockwiseTransferTimeout = o.transferTimeout +} + +func (o BlockwiseOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.BlockwiseEnable = o.enable + cfg.BlockwiseSZX = o.szx + cfg.BlockwiseTransferTimeout = o.transferTimeout +} + +func (o BlockwiseOpt) TCPServerApply(cfg *tcpServer.Config) { + cfg.BlockwiseEnable = o.enable + cfg.BlockwiseSZX = o.szx + cfg.BlockwiseTransferTimeout = o.transferTimeout +} + +func (o BlockwiseOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.BlockwiseEnable = o.enable + cfg.BlockwiseSZX = o.szx + cfg.BlockwiseTransferTimeout = o.transferTimeout +} + +// WithBlockwise configure's blockwise transfer. +func WithBlockwise(enable bool, szx blockwise.SZX, transferTimeout time.Duration) BlockwiseOpt { + return BlockwiseOpt{ + enable: enable, + szx: szx, + transferTimeout: transferTimeout, + } +} + +type OnNewConnFunc interface { + tcpServer.OnNewConnFunc | udpServer.OnNewConnFunc +} + +// OnNewConnOpt network option. +type OnNewConnOpt[F OnNewConnFunc] struct { + f F +} + +func panicForInvalidOnNewConnFunc(t, exp any) { + panic(fmt.Errorf("invalid OnNewConnFunc type %T, expected %T", t, exp)) +} + +func (o OnNewConnOpt[F]) UDPServerApply(cfg *udpServer.Config) { + switch v := any(o.f).(type) { + case udpServer.OnNewConnFunc: + cfg.OnNewConn = v + default: + var exp udpServer.OnNewConnFunc + panicForInvalidOnNewConnFunc(v, exp) + } +} + +func (o OnNewConnOpt[F]) DTLSServerApply(cfg *dtlsServer.Config) { + switch v := any(o.f).(type) { + case udpServer.OnNewConnFunc: + cfg.OnNewConn = v + default: + var exp udpServer.OnNewConnFunc + panicForInvalidOnNewConnFunc(v, exp) + } +} + +func (o OnNewConnOpt[F]) TCPServerApply(cfg *tcpServer.Config) { + switch v := any(o.f).(type) { + case tcpServer.OnNewConnFunc: + cfg.OnNewConn = v + default: + var exp tcpServer.OnNewConnFunc + panicForInvalidOnNewConnFunc(v, exp) + } +} + +// WithOnNewConn server's notify about new client connection. +func WithOnNewConn[F OnNewConnFunc](onNewConn F) OnNewConnOpt[F] { + return OnNewConnOpt[F]{ + f: onNewConn, + } +} + +// CloseSocketOpt close socket option. +type CloseSocketOpt struct{} + +func (o CloseSocketOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.CloseSocket = true +} + +func (o CloseSocketOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.CloseSocket = true +} + +// WithCloseSocket closes socket at the close connection. +func WithCloseSocket() CloseSocketOpt { + return CloseSocketOpt{} +} + +// DialerOpt dialer option. +type DialerOpt struct { + dialer *net.Dialer +} + +func (o DialerOpt) UDPClientApply(cfg *udpClient.Config) { + if o.dialer != nil { + cfg.Dialer = o.dialer + } +} + +func (o DialerOpt) TCPClientApply(cfg *tcpClient.Config) { + if o.dialer != nil { + cfg.Dialer = o.dialer + } +} + +// WithDialer set dialer for dial. +func WithDialer(dialer *net.Dialer) DialerOpt { + return DialerOpt{ + dialer: dialer, + } +} + +// ConnectionCacheOpt network option. +type MessagePoolOpt struct { + messagePool *pool.Pool +} + +func (o MessagePoolOpt) TCPServerApply(cfg *tcpServer.Config) { + cfg.MessagePool = o.messagePool +} + +func (o MessagePoolOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.MessagePool = o.messagePool +} + +func (o MessagePoolOpt) UDPServerApply(cfg *udpServer.Config) { + cfg.MessagePool = o.messagePool +} + +func (o MessagePoolOpt) DTLSServerApply(cfg *dtlsServer.Config) { + cfg.MessagePool = o.messagePool +} + +func (o MessagePoolOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.MessagePool = o.messagePool +} + +// WithMessagePool configure's message pool for acquire/releasing coap messages +func WithMessagePool(messagePool *pool.Pool) MessagePoolOpt { + return MessagePoolOpt{ + messagePool: messagePool, + } +} + +// GetTokenOpt token option. +type GetTokenOpt struct { + getToken client.GetTokenFunc +} + +func (o GetTokenOpt) TCPServerApply(cfg *tcpServer.Config) { + cfg.GetToken = o.getToken +} + +func (o GetTokenOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.GetToken = o.getToken +} + +func (o GetTokenOpt) UDPServerApply(cfg *udpServer.Config) { + cfg.GetToken = o.getToken +} + +func (o GetTokenOpt) DTLSServerApply(cfg *dtlsServer.Config) { + cfg.GetToken = o.getToken +} + +func (o GetTokenOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.GetToken = o.getToken +} + +// WithGetToken set function for generating tokens. +func WithGetToken(getToken client.GetTokenFunc) GetTokenOpt { + return GetTokenOpt{getToken: getToken} +} + +// LimitClientParallelRequestOpt limit's number of parallel requests from client. +type LimitClientParallelRequestOpt struct { + limitClientParallelRequests int64 +} + +func (o LimitClientParallelRequestOpt) TCPServerApply(cfg *tcpServer.Config) { + cfg.LimitClientParallelRequests = o.limitClientParallelRequests +} + +func (o LimitClientParallelRequestOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.LimitClientParallelRequests = o.limitClientParallelRequests +} + +func (o LimitClientParallelRequestOpt) UDPServerApply(cfg *udpServer.Config) { + cfg.LimitClientParallelRequests = o.limitClientParallelRequests +} + +func (o LimitClientParallelRequestOpt) DTLSServerApply(cfg *dtlsServer.Config) { + cfg.LimitClientParallelRequests = o.limitClientParallelRequests +} + +func (o LimitClientParallelRequestOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.LimitClientParallelRequests = o.limitClientParallelRequests +} + +// WithLimitClientParallelRequestOpt limits number of parallel requests from client. (default: 1) +func WithLimitClientParallelRequest(limitClientParallelRequests int64) LimitClientParallelRequestOpt { + return LimitClientParallelRequestOpt{limitClientParallelRequests: limitClientParallelRequests} +} + +// LimitClientEndpointParallelRequestOpt limit's number of parallel requests to endpoint by client. +type LimitClientEndpointParallelRequestOpt struct { + limitClientEndpointParallelRequests int64 +} + +func (o LimitClientEndpointParallelRequestOpt) TCPServerApply(cfg *tcpServer.Config) { + cfg.LimitClientEndpointParallelRequests = o.limitClientEndpointParallelRequests +} + +func (o LimitClientEndpointParallelRequestOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.LimitClientEndpointParallelRequests = o.limitClientEndpointParallelRequests +} + +func (o LimitClientEndpointParallelRequestOpt) UDPServerApply(cfg *udpServer.Config) { + cfg.LimitClientEndpointParallelRequests = o.limitClientEndpointParallelRequests +} + +func (o LimitClientEndpointParallelRequestOpt) DTLSServerApply(cfg *dtlsServer.Config) { + cfg.LimitClientEndpointParallelRequests = o.limitClientEndpointParallelRequests +} + +func (o LimitClientEndpointParallelRequestOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.LimitClientEndpointParallelRequests = o.limitClientEndpointParallelRequests +} + +// WithLimitClientEndpointParallelRequest limits number of parallel requests to endpoint from client. (default: 1) +func WithLimitClientEndpointParallelRequest(limitClientEndpointParallelRequests int64) LimitClientEndpointParallelRequestOpt { + return LimitClientEndpointParallelRequestOpt{limitClientEndpointParallelRequests: limitClientEndpointParallelRequests} +} + +// ReceivedMessageQueueSizeOpt limit's message queue size for received messages. +type ReceivedMessageQueueSizeOpt struct { + receivedMessageQueueSize int +} + +func (o ReceivedMessageQueueSizeOpt) TCPServerApply(cfg *tcpServer.Config) { + cfg.ReceivedMessageQueueSize = o.receivedMessageQueueSize +} + +func (o ReceivedMessageQueueSizeOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.ReceivedMessageQueueSize = o.receivedMessageQueueSize +} + +func (o ReceivedMessageQueueSizeOpt) UDPServerApply(cfg *udpServer.Config) { + cfg.ReceivedMessageQueueSize = o.receivedMessageQueueSize +} + +func (o ReceivedMessageQueueSizeOpt) DTLSServerApply(cfg *dtlsServer.Config) { + cfg.ReceivedMessageQueueSize = o.receivedMessageQueueSize +} + +func (o ReceivedMessageQueueSizeOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.ReceivedMessageQueueSize = o.receivedMessageQueueSize +} + +// WithReceivedMessageQueueSize limit's message queue size for received messages. (default: 16) +func WithReceivedMessageQueueSize(receivedMessageQueueSize int) ReceivedMessageQueueSizeOpt { + return ReceivedMessageQueueSizeOpt{receivedMessageQueueSize: receivedMessageQueueSize} +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/options/config/common.go b/vendor/github.com/plgd-dev/go-coap/v3/options/config/common.go new file mode 100644 index 00000000000..e6fd876a314 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/options/config/common.go @@ -0,0 +1,61 @@ +package config + +import ( + "context" + "fmt" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/pool" + "github.com/plgd-dev/go-coap/v3/net/blockwise" + "github.com/plgd-dev/go-coap/v3/net/client" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" + "github.com/plgd-dev/go-coap/v3/pkg/runner/periodic" +) + +type ( + ErrorFunc = func(error) + HandlerFunc[C responsewriter.Client] func(w *responsewriter.ResponseWriter[C], r *pool.Message) + ProcessReceivedMessageFunc[C responsewriter.Client] func(req *pool.Message, cc C, handler HandlerFunc[C]) +) + +type Common[C responsewriter.Client] struct { + LimitClientParallelRequests int64 + LimitClientEndpointParallelRequests int64 + Ctx context.Context + Errors ErrorFunc + PeriodicRunner periodic.Func + MessagePool *pool.Pool + GetToken client.GetTokenFunc + MaxMessageSize uint32 + BlockwiseTransferTimeout time.Duration + BlockwiseSZX blockwise.SZX + BlockwiseEnable bool + ProcessReceivedMessage ProcessReceivedMessageFunc[C] + ReceivedMessageQueueSize int +} + +func NewCommon[C responsewriter.Client]() Common[C] { + return Common[C]{ + Ctx: context.Background(), + MaxMessageSize: 64 * 1024, + Errors: func(err error) { + fmt.Println(err) + }, + BlockwiseSZX: blockwise.SZX1024, + BlockwiseEnable: true, + BlockwiseTransferTimeout: time.Second * 3, + PeriodicRunner: func(f func(now time.Time) bool) { + go func() { + for f(time.Now()) { + time.Sleep(4 * time.Second) + } + }() + }, + MessagePool: pool.New(1024, 2048), + GetToken: message.GetToken, + LimitClientParallelRequests: 1, + LimitClientEndpointParallelRequests: 1, + ReceivedMessageQueueSize: 16, + } +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/options/tcpOptions.go b/vendor/github.com/plgd-dev/go-coap/v3/options/tcpOptions.go new file mode 100644 index 00000000000..ec2f9fb69f9 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/options/tcpOptions.go @@ -0,0 +1,76 @@ +package options + +import ( + "crypto/tls" + + tcpClient "github.com/plgd-dev/go-coap/v3/tcp/client" + tcpServer "github.com/plgd-dev/go-coap/v3/tcp/server" +) + +// DisablePeerTCPSignalMessageCSMsOpt coap-tcp csm option. +type DisablePeerTCPSignalMessageCSMsOpt struct{} + +func (o DisablePeerTCPSignalMessageCSMsOpt) TCPServerApply(cfg *tcpServer.Config) { + cfg.DisablePeerTCPSignalMessageCSMs = true +} + +func (o DisablePeerTCPSignalMessageCSMsOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.DisablePeerTCPSignalMessageCSMs = true +} + +// WithDisablePeerTCPSignalMessageCSMs ignor peer's CSM message. +func WithDisablePeerTCPSignalMessageCSMs() DisablePeerTCPSignalMessageCSMsOpt { + return DisablePeerTCPSignalMessageCSMsOpt{} +} + +// DisableTCPSignalMessageCSMOpt coap-tcp csm option. +type DisableTCPSignalMessageCSMOpt struct{} + +func (o DisableTCPSignalMessageCSMOpt) TCPServerApply(cfg *tcpServer.Config) { + cfg.DisableTCPSignalMessageCSM = true +} + +func (o DisableTCPSignalMessageCSMOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.DisableTCPSignalMessageCSM = true +} + +// WithDisableTCPSignalMessageCSM don't send CSM when client conn is created. +func WithDisableTCPSignalMessageCSM() DisableTCPSignalMessageCSMOpt { + return DisableTCPSignalMessageCSMOpt{} +} + +// TLSOpt tls configuration option. +type TLSOpt struct { + tlsCfg *tls.Config +} + +func (o TLSOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.TLSCfg = o.tlsCfg +} + +// WithTLS creates tls connection. +func WithTLS(cfg *tls.Config) TLSOpt { + return TLSOpt{ + tlsCfg: cfg, + } +} + +// ConnectionCacheOpt network option. +type ConnectionCacheSizeOpt struct { + connectionCacheSize uint16 +} + +func (o ConnectionCacheSizeOpt) TCPServerApply(cfg *tcpServer.Config) { + cfg.ConnectionCacheSize = o.connectionCacheSize +} + +func (o ConnectionCacheSizeOpt) TCPClientApply(cfg *tcpClient.Config) { + cfg.ConnectionCacheSize = o.connectionCacheSize +} + +// WithConnectionCacheSize configure's maximum size of cache of read buffer. +func WithConnectionCacheSize(connectionCacheSize uint16) ConnectionCacheSizeOpt { + return ConnectionCacheSizeOpt{ + connectionCacheSize: connectionCacheSize, + } +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/options/udpOptions.go b/vendor/github.com/plgd-dev/go-coap/v3/options/udpOptions.go new file mode 100644 index 00000000000..77f4ff931c3 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/options/udpOptions.go @@ -0,0 +1,70 @@ +package options + +import ( + "time" + + dtlsServer "github.com/plgd-dev/go-coap/v3/dtls/server" + udpClient "github.com/plgd-dev/go-coap/v3/udp/client" + udpServer "github.com/plgd-dev/go-coap/v3/udp/server" +) + +// TransmissionOpt transmission options. +type TransmissionOpt struct { + transmissionNStart uint32 + transmissionAcknowledgeTimeout time.Duration + transmissionMaxRetransmit uint32 +} + +func (o TransmissionOpt) UDPServerApply(cfg *udpServer.Config) { + cfg.TransmissionNStart = o.transmissionNStart + cfg.TransmissionAcknowledgeTimeout = o.transmissionAcknowledgeTimeout + cfg.TransmissionMaxRetransmit = o.transmissionMaxRetransmit +} + +func (o TransmissionOpt) DTLSServerApply(cfg *dtlsServer.Config) { + cfg.TransmissionNStart = o.transmissionNStart + cfg.TransmissionAcknowledgeTimeout = o.transmissionAcknowledgeTimeout + cfg.TransmissionMaxRetransmit = o.transmissionMaxRetransmit +} + +func (o TransmissionOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.TransmissionNStart = o.transmissionNStart + cfg.TransmissionAcknowledgeTimeout = o.transmissionAcknowledgeTimeout + cfg.TransmissionMaxRetransmit = o.transmissionMaxRetransmit +} + +// WithTransmission set options for (re)transmission for Confirmable message-s. +func WithTransmission(transmissionNStart uint32, + transmissionAcknowledgeTimeout time.Duration, + transmissionMaxRetransmit uint32, +) TransmissionOpt { + return TransmissionOpt{ + transmissionNStart: transmissionNStart, + transmissionAcknowledgeTimeout: transmissionAcknowledgeTimeout, + transmissionMaxRetransmit: transmissionMaxRetransmit, + } +} + +// MTUOpt transmission options. +type MTUOpt struct { + mtu uint16 +} + +func (o MTUOpt) UDPServerApply(cfg *udpServer.Config) { + cfg.MTU = o.mtu +} + +func (o MTUOpt) DTLSServerApply(cfg *dtlsServer.Config) { + cfg.MTU = o.mtu +} + +func (o MTUOpt) UDPClientApply(cfg *udpClient.Config) { + cfg.MTU = o.mtu +} + +// Setup MTU unit +func WithMTU(mtu uint16) MTUOpt { + return MTUOpt{ + mtu: mtu, + } +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/pkg/cache/cache.go b/vendor/github.com/plgd-dev/go-coap/v3/pkg/cache/cache.go new file mode 100644 index 00000000000..21099fc3130 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/pkg/cache/cache.go @@ -0,0 +1,85 @@ +package cache + +import ( + "time" + + "github.com/plgd-dev/go-coap/v3/pkg/sync" + "go.uber.org/atomic" +) + +func DefaultOnExpire[D any](D) { + // for nothing on expire +} + +type Element[D any] struct { + ValidUntil atomic.Time + data D + onExpire func(d D) +} + +func (e *Element[D]) IsExpired(now time.Time) bool { + value := e.ValidUntil.Load() + if value.IsZero() { + return false + } + return now.After(value) +} + +func (e *Element[D]) Data() D { + return e.data +} + +func NewElement[D any](data D, validUntil time.Time, onExpire func(d D)) *Element[D] { + if onExpire == nil { + onExpire = DefaultOnExpire[D] + } + e := &Element[D]{data: data, onExpire: onExpire} + e.ValidUntil.Store(validUntil) + return e +} + +type Cache[K comparable, D any] struct { + *sync.Map[K, *Element[D]] +} + +func NewCache[K comparable, D any]() *Cache[K, D] { + return &Cache[K, D]{ + Map: sync.NewMap[K, *Element[D]](), + } +} + +func (c *Cache[K, D]) LoadOrStore(key K, e *Element[D]) (actual *Element[D], loaded bool) { + now := time.Now() + c.Map.ReplaceWithFunc(key, func(oldValue *Element[D], oldLoaded bool) (newValue *Element[D], deleteValue bool) { + if oldLoaded { + if !oldValue.IsExpired(now) { + actual = oldValue + return oldValue, false + } + } + actual = e + return e, false + }) + return actual, actual != e +} + +func (c *Cache[K, D]) Load(key K) (actual *Element[D]) { + actual, loaded := c.Map.Load(key) + if !loaded { + return nil + } + if actual.IsExpired(time.Now()) { + return nil + } + return actual +} + +func (c *Cache[K, D]) CheckExpirations(now time.Time) { + c.Range(func(key K, value *Element[D]) bool { + if value.IsExpired(now) { + c.Map.Delete(key) + value.onExpire(value.Data()) + } + return true + }) +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/pkg/connections/connections.go b/vendor/github.com/plgd-dev/go-coap/v3/pkg/connections/connections.go similarity index 73% rename from vendor/github.com/plgd-dev/go-coap/v2/pkg/connections/connections.go rename to vendor/github.com/plgd-dev/go-coap/v3/pkg/connections/connections.go index ba1e93c90c0..d2de86579c3 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/pkg/connections/connections.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/pkg/connections/connections.go @@ -2,10 +2,10 @@ package connections import ( "context" + "fmt" "net" + "sync" "time" - - sync "github.com/plgd-dev/kit/v2/sync" ) type Connections struct { @@ -14,7 +14,7 @@ type Connections struct { func New() *Connections { return &Connections{ - data: sync.NewMap(), + data: &sync.Map{}, } } @@ -29,10 +29,23 @@ func (c *Connections) Store(conn Connection) { c.data.Store(conn.RemoteAddr().String(), conn) } +func (c *Connections) length() int { + var l int + c.data.Range(func(k, v interface{}) bool { + l++ + return true + }) + return l +} + func (c *Connections) copyConnections() []Connection { - m := make([]Connection, 0, c.data.Length()) + m := make([]Connection, 0, c.length()) c.data.Range(func(key, value interface{}) bool { - m = append(m, value.(Connection)) + con, ok := value.(Connection) + if !ok { + panic(fmt.Errorf("invalid type %T in connections map", con)) + } + m = append(m, con) return true }) return m diff --git a/vendor/github.com/plgd-dev/go-coap/v3/pkg/errors/error.go b/vendor/github.com/plgd-dev/go-coap/v3/pkg/errors/error.go new file mode 100644 index 00000000000..e98098b58c3 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/pkg/errors/error.go @@ -0,0 +1,5 @@ +package errors + +import "errors" + +var ErrKeyAlreadyExists = errors.New("key already exists") diff --git a/vendor/github.com/plgd-dev/go-coap/v3/pkg/fn/funcList.go b/vendor/github.com/plgd-dev/go-coap/v3/pkg/fn/funcList.go new file mode 100644 index 00000000000..9ed31e801b9 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/pkg/fn/funcList.go @@ -0,0 +1,19 @@ +package fn + +type FuncList []func() + +// Return a function that executions all added functions +// +// Functions are executed in reverse order they were added. +func (c FuncList) ToFunction() func() { + return func() { + for i := range c { + c[len(c)-1-i]() + } + } +} + +// Execute all added functions +func (c FuncList) Execute() { + c.ToFunction()() +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/pkg/rand/rand.go b/vendor/github.com/plgd-dev/go-coap/v3/pkg/rand/rand.go new file mode 100644 index 00000000000..124d3132b54 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/pkg/rand/rand.go @@ -0,0 +1,31 @@ +package rand + +import ( + "math/rand" + "sync" +) + +type Rand struct { + src *rand.Rand + lock sync.Mutex +} + +func NewRand(seed int64) *Rand { + return &Rand{ + src: rand.New(rand.NewSource(seed)), + } +} + +func (l *Rand) Int63() int64 { + l.lock.Lock() + val := l.src.Int63() + l.lock.Unlock() + return val +} + +func (l *Rand) Uint32() uint32 { + l.lock.Lock() + val := l.src.Uint32() + l.lock.Unlock() + return val +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/pkg/runner/periodic/periodic.go b/vendor/github.com/plgd-dev/go-coap/v3/pkg/runner/periodic/periodic.go similarity index 89% rename from vendor/github.com/plgd-dev/go-coap/v2/pkg/runner/periodic/periodic.go rename to vendor/github.com/plgd-dev/go-coap/v3/pkg/runner/periodic/periodic.go index 0768c7b7a4c..7cfe6245777 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/pkg/runner/periodic/periodic.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/pkg/runner/periodic/periodic.go @@ -23,7 +23,7 @@ func New(stop <-chan struct{}, tick time.Duration) Func { } v := make(map[uint64]func(time.Time) bool) m.Range(func(key, value interface{}) bool { - v[key.(uint64)] = value.(func(time.Time) bool) + v[key.(uint64)] = value.(func(time.Time) bool) //nolint:forcetypeassert return true }) for k, f := range v { diff --git a/vendor/github.com/plgd-dev/go-coap/v3/pkg/sync/map.go b/vendor/github.com/plgd-dev/go-coap/v3/pkg/sync/map.go new file mode 100644 index 00000000000..321aefb373b --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/pkg/sync/map.go @@ -0,0 +1,215 @@ +package sync + +import ( + "sync" + + "golang.org/x/exp/maps" +) + +// Map is like a Go map[interface{}]interface{} but is safe for concurrent use by multiple goroutines. +type Map[K comparable, V any] struct { + mutex sync.RWMutex + data map[K]V +} + +// NewMap creates map. +func NewMap[K comparable, V any]() *Map[K, V] { + return &Map[K, V]{ + data: make(map[K]V), + } +} + +// Store sets the value for a key. +func (m *Map[K, V]) Store(key K, value V) { + m.mutex.Lock() + defer m.mutex.Unlock() + m.data[key] = value +} + +// Load returns the value stored in the map for a key, or nil if no value is present. The loaded value is read-only and should not be modified. +// The ok result indicates whether value was found in the map. +func (m *Map[K, V]) Load(key K) (V, bool) { + m.mutex.RLock() + defer m.mutex.RUnlock() + v, ok := m.data[key] + return v, ok +} + +// LoadOrStore returns the existing value for the key if present. The loaded value is read-only and should not be modified. +// Otherwise, it stores and returns the given value. The loaded result is true if the value was loaded, false if stored. +func (m *Map[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) { + m.mutex.RLock() + v, ok := m.data[key] + m.mutex.RUnlock() + if ok { + return v, true + } + m.mutex.Lock() + m.data[key] = value + m.mutex.Unlock() + return value, false +} + +// Replace replaces the existing value with a new value and returns old value for the key. +func (m *Map[K, V]) Replace(key K, value V) (oldValue V, oldLoaded bool) { + m.mutex.Lock() + defer m.mutex.Unlock() + v, ok := m.data[key] + m.data[key] = value + return v, ok +} + +// Delete deletes the value for the key. +func (m *Map[K, V]) Delete(key K) { + m.mutex.Lock() + defer m.mutex.Unlock() + delete(m.data, key) +} + +// LoadAndDelete loads and deletes the value for the key. +func (m *Map[K, V]) LoadAndDelete(key K) (V, bool) { + m.mutex.Lock() + defer m.mutex.Unlock() + value, ok := m.data[key] + delete(m.data, key) + return value, ok +} + +// LoadAndDelete loads and deletes the value for the key. +func (m *Map[K, V]) LoadAndDeleteAll() map[K]V { + m.mutex.Lock() + data := m.data + m.data = make(map[K]V) + m.mutex.Unlock() + return data +} + +// CopyData creates a deep copy of the internal map. +func (m *Map[K, V]) CopyData() map[K]V { + c := make(map[K]V) + m.mutex.RLock() + maps.Copy(c, m.data) + m.mutex.RUnlock() + return c +} + +// Length returns number of stored values. +func (m *Map[K, V]) Length() int { + m.mutex.RLock() + defer m.mutex.RUnlock() + return len(m.data) +} + +// Range calls f sequentially for each key and value present in the map. If f returns false, range stops the iteration. +// +// Range does not copy the whole map, instead the read lock is locked on iteration of the map, and unlocked before f is called. +func (m *Map[K, V]) Range(f func(key K, value V) bool) { + m.mutex.RLock() + defer m.mutex.RUnlock() + for key, value := range m.data { + m.mutex.RUnlock() + ok := f(key, value) + m.mutex.RLock() + if !ok { + return + } + } +} + +// Range2 calls f sequentially for each key and value present in the map. If f returns false, range stops the iteration. +// +// Range2 differs from Range by keepting a read lock locked during the whole call. +func (m *Map[K, V]) Range2(f func(key K, value V) bool) { + m.mutex.RLock() + defer m.mutex.RUnlock() + for key, value := range m.data { + ok := f(key, value) + if !ok { + return + } + } +} + +// StoreWithFunc creates a new element and stores it in the map under the given key. +// +// The createFunc is invoked under a write lock. +func (m *Map[K, V]) StoreWithFunc(key K, createFunc func() V) { + m.mutex.Lock() + defer m.mutex.Unlock() + m.data[key] = createFunc() +} + +// LoadWithFunc tries to load element for key from the map, if it exists then the onload functions is invoked on it. +// +// The onLoadFunc is invoked under a read lock. +func (m *Map[K, V]) LoadWithFunc(key K, onLoadFunc func(value V) V) (V, bool) { + m.mutex.RLock() + defer m.mutex.RUnlock() + value, ok := m.data[key] + if ok && onLoadFunc != nil { + value = onLoadFunc(value) + } + return value, ok +} + +// LoadOrStoreWithFunc loads an existing element from the map or creates a new element and stores it in the map +// +// The onLoadFunc or createFunc are invoked under a write lock. +func (m *Map[K, V]) LoadOrStoreWithFunc(key K, onLoadFunc func(value V) V, createFunc func() V) (actual V, loaded bool) { + m.mutex.Lock() + defer m.mutex.Unlock() + v, ok := m.data[key] + if ok { + if onLoadFunc != nil { + v = onLoadFunc(v) + } + return v, true + } + v = createFunc() + m.data[key] = v + return v, false +} + +// ReplaceWithFunc checks whether key exists in the map, invokes the onReplaceFunc callback on the pair (value, ok) and either deletes or stores the element +// in the map based on the returned values from the onReplaceFunc callback. +// +// The onReplaceFunc callback is invoked under a write lock. +func (m *Map[K, V]) ReplaceWithFunc(key K, onReplaceFunc func(oldValue V, oldLoaded bool) (newValue V, doDelete bool)) (oldValue V, oldLoaded bool) { + m.mutex.Lock() + defer m.mutex.Unlock() + v, ok := m.data[key] + newValue, del := onReplaceFunc(v, ok) + if del { + delete(m.data, key) + return v, ok + } + m.data[key] = newValue + return v, ok +} + +// DeleteWithFunc removes the key from the map and if a value existed invokes the onDeleteFunc callback on the removed value. +// +// The onDeleteFunc callback is invoked under a write lock. +func (m *Map[K, V]) DeleteWithFunc(key K, onDeleteFunc func(value V)) { + _, _ = m.LoadAndDeleteWithFunc(key, func(value V) V { + onDeleteFunc(value) + return value + }) +} + +// LoadAndDeleteWithFunc removes the key from the map and if a value existed invokes the onLoadFunc callback on the removed and return it. +// +// The onLoadFunc callback is invoked under a write lock. +func (m *Map[K, V]) LoadAndDeleteWithFunc(key K, onLoadFunc func(value V) V) (V, bool) { + var v V + var loaded bool + m.ReplaceWithFunc(key, func(oldValue V, oldLoaded bool) (newValue V, doDelete bool) { + if oldLoaded { + loaded = true + v = onLoadFunc(oldValue) + return v, true + } + return oldValue, true + }) + return v, loaded +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/renovate.json b/vendor/github.com/plgd-dev/go-coap/v3/renovate.json similarity index 100% rename from vendor/github.com/plgd-dev/go-coap/v2/renovate.json rename to vendor/github.com/plgd-dev/go-coap/v3/renovate.json diff --git a/vendor/github.com/plgd-dev/go-coap/v3/server.go b/vendor/github.com/plgd-dev/go-coap/v3/server.go new file mode 100644 index 00000000000..1df5ab69049 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/server.go @@ -0,0 +1,161 @@ +// Package coap provides a CoAP client and server. +package coap + +import ( + "crypto/tls" + "fmt" + + piondtls "github.com/pion/dtls/v2" + "github.com/plgd-dev/go-coap/v3/dtls" + dtlsServer "github.com/plgd-dev/go-coap/v3/dtls/server" + "github.com/plgd-dev/go-coap/v3/mux" + "github.com/plgd-dev/go-coap/v3/net" + "github.com/plgd-dev/go-coap/v3/options" + "github.com/plgd-dev/go-coap/v3/tcp" + tcpServer "github.com/plgd-dev/go-coap/v3/tcp/server" + "github.com/plgd-dev/go-coap/v3/udp" + udpServer "github.com/plgd-dev/go-coap/v3/udp/server" +) + +// ListenAndServe Starts a server on address and network specified Invoke handler +// for incoming queries. +func ListenAndServe(network string, addr string, handler mux.Handler) (err error) { + switch network { + case "udp", "udp4", "udp6", "": + l, err := net.NewListenUDP(network, addr) + if err != nil { + return err + } + defer func() { + if errC := l.Close(); errC != nil && err == nil { + err = errC + } + }() + s := udp.NewServer(options.WithMux(handler)) + return s.Serve(l) + case "tcp", "tcp4", "tcp6": + l, err := net.NewTCPListener(network, addr) + if err != nil { + return err + } + defer func() { + if errC := l.Close(); errC != nil && err == nil { + err = errC + } + }() + s := tcp.NewServer(options.WithMux(handler)) + return s.Serve(l) + default: + return fmt.Errorf("invalid network (%v)", network) + } +} + +// ListenAndServeTCPTLS Starts a server on address and network over TLS specified Invoke handler +// for incoming queries. +func ListenAndServeTCPTLS(network, addr string, config *tls.Config, handler mux.Handler) (err error) { + l, err := net.NewTLSListener(network, addr, config) + if err != nil { + return err + } + defer func() { + if errC := l.Close(); errC != nil && err == nil { + err = errC + } + }() + s := tcp.NewServer(options.WithMux(handler)) + return s.Serve(l) +} + +// ListenAndServeDTLS Starts a server on address and network over DTLS specified Invoke handler +// for incoming queries. +func ListenAndServeDTLS(network string, addr string, config *piondtls.Config, handler mux.Handler) (err error) { + l, err := net.NewDTLSListener(network, addr, config) + if err != nil { + return err + } + defer func() { + if errC := l.Close(); errC != nil && err == nil { + err = errC + } + }() + s := dtls.NewServer(options.WithMux(handler)) + return s.Serve(l) +} + +// ListenAndServeWithOption Starts a server on address and network specified Invoke options +// for incoming queries. The options is only support tcpServer.Option and udpServer.Option +func ListenAndServeWithOptions(network, addr string, opts ...any) (err error) { + tcpOptions := []tcpServer.Option{} + udpOptions := []udpServer.Option{} + for _, opt := range opts { + switch o := opt.(type) { + case tcpServer.Option: + tcpOptions = append(tcpOptions, o) + case udpServer.Option: + udpOptions = append(udpOptions, o) + default: + return fmt.Errorf("only support tcpServer.Option and udpServer.Option") + } + } + + switch network { + case "udp", "udp4", "udp6", "": + l, err := net.NewListenUDP(network, addr) + if err != nil { + return err + } + defer func() { + if errC := l.Close(); errC != nil && err == nil { + err = errC + } + }() + s := udp.NewServer(udpOptions...) + return s.Serve(l) + case "tcp", "tcp4", "tcp6": + l, err := net.NewTCPListener(network, addr) + if err != nil { + return err + } + defer func() { + if errC := l.Close(); errC != nil && err == nil { + err = errC + } + }() + s := tcp.NewServer(tcpOptions...) + return s.Serve(l) + default: + return fmt.Errorf("invalid network (%v)", network) + } +} + +// ListenAndServeTCPTLSWithOptions Starts a server on address and network over TLS specified Invoke options +// for incoming queries. +func ListenAndServeTCPTLSWithOptions(network, addr string, config *tls.Config, opts ...tcpServer.Option) (err error) { + l, err := net.NewTLSListener(network, addr, config) + if err != nil { + return err + } + defer func() { + if errC := l.Close(); errC != nil && err == nil { + err = errC + } + }() + s := tcp.NewServer(opts...) + return s.Serve(l) +} + +// ListenAndServeDTLSWithOptions Starts a server on address and network over DTLS specified Invoke options +// for incoming queries. +func ListenAndServeDTLSWithOptions(network string, addr string, config *piondtls.Config, opts ...dtlsServer.Option) (err error) { + l, err := net.NewDTLSListener(network, addr, config) + if err != nil { + return err + } + defer func() { + if errC := l.Close(); errC != nil && err == nil { + err = errC + } + }() + s := dtls.NewServer(opts...) + return s.Serve(l) +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/sonar-project.properties b/vendor/github.com/plgd-dev/go-coap/v3/sonar-project.properties similarity index 97% rename from vendor/github.com/plgd-dev/go-coap/v2/sonar-project.properties rename to vendor/github.com/plgd-dev/go-coap/v3/sonar-project.properties index be469d8c4a4..b1b8facfc27 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/sonar-project.properties +++ b/vendor/github.com/plgd-dev/go-coap/v3/sonar-project.properties @@ -11,7 +11,7 @@ sonar.organization=plgd-dev sonar.python.version=3.8 sonar.sources=. -sonar.exclusions=**/*_test.go,**/*.pb.go,**/*.pb.gw.go,**/options.go +sonar.exclusions=**/*_test.go,**/*.pb.go,**/*.pb.gw.go,**/options.go,**/main.go,v3/** sonar.tests=. sonar.test.inclusions=**/*_test.go diff --git a/vendor/github.com/plgd-dev/go-coap/v3/tcp/client.go b/vendor/github.com/plgd-dev/go-coap/v3/tcp/client.go new file mode 100644 index 00000000000..d407bd78853 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/tcp/client.go @@ -0,0 +1,110 @@ +package tcp + +import ( + "crypto/tls" + "fmt" + "net" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/pool" + coapNet "github.com/plgd-dev/go-coap/v3/net" + "github.com/plgd-dev/go-coap/v3/net/blockwise" + "github.com/plgd-dev/go-coap/v3/net/monitor/inactivity" + "github.com/plgd-dev/go-coap/v3/options" + client "github.com/plgd-dev/go-coap/v3/tcp/client" +) + +// A Option sets options such as credentials, keepalive parameters, etc. +type Option interface { + TCPClientApply(cfg *client.Config) +} + +// Dial creates a client connection to the given target. +func Dial(target string, opts ...Option) (*client.Conn, error) { + cfg := client.DefaultConfig + for _, o := range opts { + o.TCPClientApply(&cfg) + } + + var conn net.Conn + var err error + if cfg.TLSCfg != nil { + conn, err = tls.DialWithDialer(cfg.Dialer, cfg.Net, target, cfg.TLSCfg) + } else { + conn, err = cfg.Dialer.DialContext(cfg.Ctx, cfg.Net, target) + } + if err != nil { + return nil, err + } + opts = append(opts, options.WithCloseSocket()) + return Client(conn, opts...), nil +} + +// Client creates client over tcp/tcp-tls connection. +func Client(conn net.Conn, opts ...Option) *client.Conn { + cfg := client.DefaultConfig + for _, o := range opts { + o.TCPClientApply(&cfg) + } + if cfg.Errors == nil { + cfg.Errors = func(error) { + // default no-op + } + } + if cfg.CreateInactivityMonitor == nil { + cfg.CreateInactivityMonitor = func() client.InactivityMonitor { + return inactivity.NewNilMonitor[*client.Conn]() + } + } + if cfg.MessagePool == nil { + cfg.MessagePool = pool.New(0, 0) + } + errorsFunc := cfg.Errors + cfg.Errors = func(err error) { + if coapNet.IsCancelOrCloseError(err) { + // this error was produced by cancellation context or closing connection. + return + } + errorsFunc(fmt.Errorf("tcp: %w", err)) + } + + createBlockWise := func(cc *client.Conn) *blockwise.BlockWise[*client.Conn] { + return nil + } + if cfg.BlockwiseEnable { + createBlockWise = func(cc *client.Conn) *blockwise.BlockWise[*client.Conn] { + v := cc + return blockwise.New( + v, + cfg.BlockwiseTransferTimeout, + cfg.Errors, + func(token message.Token) (*pool.Message, bool) { + return v.GetObservationRequest(token) + }, + ) + } + } + + l := coapNet.NewConn(conn) + monitor := cfg.CreateInactivityMonitor() + cc := client.NewConn(l, + createBlockWise, + monitor, + &cfg, + ) + + cfg.PeriodicRunner(func(now time.Time) bool { + cc.CheckExpirations(now) + return cc.Context().Err() == nil + }) + + go func() { + err := cc.Run() + if err != nil { + cfg.Errors(fmt.Errorf("%v: %w", cc.RemoteAddr(), err)) + } + }() + + return cc +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/tcp/client/config.go b/vendor/github.com/plgd-dev/go-coap/v3/tcp/client/config.go new file mode 100644 index 00000000000..dd212d989cc --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/tcp/client/config.go @@ -0,0 +1,49 @@ +package client + +import ( + "crypto/tls" + "fmt" + "net" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + "github.com/plgd-dev/go-coap/v3/net/monitor/inactivity" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" + "github.com/plgd-dev/go-coap/v3/options/config" +) + +var DefaultConfig = func() Config { + opts := Config{ + Common: config.NewCommon[*Conn](), + CreateInactivityMonitor: func() InactivityMonitor { + return inactivity.NewNilMonitor[*Conn]() + }, + Dialer: &net.Dialer{Timeout: time.Second * 3}, + Net: "tcp", + ConnectionCacheSize: 2048, + } + opts.Handler = func(w *responsewriter.ResponseWriter[*Conn], r *pool.Message) { + switch r.Code() { + case codes.POST, codes.PUT, codes.GET, codes.DELETE: + if err := w.SetResponse(codes.NotFound, message.TextPlain, nil); err != nil { + opts.Errors(fmt.Errorf("client handler: cannot set response: %w", err)) + } + } + } + return opts +}() + +type Config struct { + config.Common[*Conn] + CreateInactivityMonitor CreateInactivityMonitorFunc + Net string + Dialer *net.Dialer + TLSCfg *tls.Config + Handler HandlerFunc + ConnectionCacheSize uint16 + DisablePeerTCPSignalMessageCSMs bool + CloseSocket bool + DisableTCPSignalMessageCSM bool +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/tcp/client/conn.go b/vendor/github.com/plgd-dev/go-coap/v3/tcp/client/conn.go new file mode 100644 index 00000000000..2f3da223884 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/tcp/client/conn.go @@ -0,0 +1,370 @@ +package client + +import ( + "context" + "errors" + "fmt" + "net" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + coapNet "github.com/plgd-dev/go-coap/v3/net" + "github.com/plgd-dev/go-coap/v3/net/blockwise" + "github.com/plgd-dev/go-coap/v3/net/client" + limitparallelrequests "github.com/plgd-dev/go-coap/v3/net/client/limitParallelRequests" + "github.com/plgd-dev/go-coap/v3/net/observation" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" + coapErrors "github.com/plgd-dev/go-coap/v3/pkg/errors" + coapSync "github.com/plgd-dev/go-coap/v3/pkg/sync" + "go.uber.org/atomic" +) + +type InactivityMonitor interface { + Notify() + CheckInactivity(now time.Time, cc *Conn) +} + +type ( + HandlerFunc = func(*responsewriter.ResponseWriter[*Conn], *pool.Message) + ErrorFunc = func(error) + EventFunc = func() + GetMIDFunc = func() int32 + CreateInactivityMonitorFunc = func() InactivityMonitor +) + +type Notifier interface { + Notify() +} + +// Conn represents a virtual connection to a conceptual endpoint, to perform COAPs commands. +type Conn struct { + *client.Client[*Conn] + session *Session + observationHandler *observation.Handler[*Conn] + processReceivedMessage func(req *pool.Message, cc *Conn, handler HandlerFunc) + tokenHandlerContainer *coapSync.Map[uint64, HandlerFunc] + blockWise *blockwise.BlockWise[*Conn] + blockwiseSZX blockwise.SZX + peerMaxMessageSize atomic.Uint32 + disablePeerTCPSignalMessageCSMs bool + peerBlockWiseTranferEnabled atomic.Bool + + receivedMessageReader *client.ReceivedMessageReader[*Conn] +} + +// NewConn creates connection over session and observation. +func NewConn( + connection *coapNet.Conn, + createBlockWise func(cc *Conn) *blockwise.BlockWise[*Conn], + inactivityMonitor InactivityMonitor, + cfg *Config, +) *Conn { + if cfg.GetToken == nil { + cfg.GetToken = message.GetToken + } + cc := Conn{ + tokenHandlerContainer: coapSync.NewMap[uint64, HandlerFunc](), + blockwiseSZX: cfg.BlockwiseSZX, + disablePeerTCPSignalMessageCSMs: cfg.DisablePeerTCPSignalMessageCSMs, + } + limitParallelRequests := limitparallelrequests.New(cfg.LimitClientParallelRequests, cfg.LimitClientEndpointParallelRequests, cc.do, cc.doObserve) + cc.observationHandler = observation.NewHandler(&cc, cfg.Handler, limitParallelRequests.Do) + cc.Client = client.New(&cc, cc.observationHandler, cfg.GetToken, limitParallelRequests) + cc.blockWise = createBlockWise(&cc) + session := NewSession(cfg.Ctx, + connection, + cfg.MaxMessageSize, + cfg.Errors, + cfg.DisableTCPSignalMessageCSM, + cfg.CloseSocket, + inactivityMonitor, + cfg.ConnectionCacheSize, + cfg.MessagePool, + ) + cc.session = session + if cc.processReceivedMessage == nil { + cc.processReceivedMessage = processReceivedMessage + } + cc.receivedMessageReader = client.NewReceivedMessageReader(&cc, cfg.ReceivedMessageQueueSize) + return &cc +} + +func processReceivedMessage(req *pool.Message, cc *Conn, handler HandlerFunc) { + cc.ProcessReceivedMessageWithHandler(req, handler) +} + +func (cc *Conn) ProcessReceivedMessage(req *pool.Message) { + cc.processReceivedMessage(req, cc, cc.handle) +} + +func (cc *Conn) Session() *Session { + return cc.session +} + +// Close closes connection without wait of ends Run function. +func (cc *Conn) Close() error { + err := cc.session.Close() + if errors.Is(err, net.ErrClosed) { + return nil + } + return err +} + +func (cc *Conn) doInternal(req *pool.Message) (*pool.Message, error) { + token := req.Token() + if token == nil { + return nil, fmt.Errorf("invalid token") + } + respChan := make(chan *pool.Message, 1) + if _, loaded := cc.tokenHandlerContainer.LoadOrStore(token.Hash(), func(w *responsewriter.ResponseWriter[*Conn], r *pool.Message) { + r.Hijack() + select { + case respChan <- r: + default: + } + }); loaded { + return nil, fmt.Errorf("cannot add token handler: %w", coapErrors.ErrKeyAlreadyExists) + } + defer func() { + _, _ = cc.tokenHandlerContainer.LoadAndDelete(token.Hash()) + }() + if err := cc.session.WriteMessage(req); err != nil { + return nil, fmt.Errorf("cannot write request: %w", err) + } + + cc.receivedMessageReader.TryToReplaceLoop() + + select { + case <-req.Context().Done(): + return nil, req.Context().Err() + case <-cc.session.Context().Done(): + return nil, fmt.Errorf("connection was closed: %w", cc.Context().Err()) + case resp := <-respChan: + return resp, nil + } +} + +// Do sends an coap message and returns an coap response. +// +// An error is returned if by failure to speak COAP (such as a network connectivity problem). +// Any status code doesn't cause an error. +// +// Caller is responsible to release request and response. +func (cc *Conn) do(req *pool.Message) (*pool.Message, error) { + if !cc.peerBlockWiseTranferEnabled.Load() || cc.blockWise == nil { + return cc.doInternal(req) + } + resp, err := cc.blockWise.Do(req, cc.blockwiseSZX, cc.session.maxMessageSize, cc.doInternal) + if err != nil { + return nil, err + } + return resp, nil +} + +func (cc *Conn) writeMessage(req *pool.Message) error { + return cc.session.WriteMessage(req) +} + +// WriteMessage sends an coap message. +func (cc *Conn) WriteMessage(req *pool.Message) error { + if !cc.peerBlockWiseTranferEnabled.Load() || cc.blockWise == nil { + return cc.writeMessage(req) + } + return cc.blockWise.WriteMessage(req, cc.blockwiseSZX, cc.Session().maxMessageSize, cc.writeMessage) +} + +// Context returns the client's context. +// +// If connections was closed context is cancelled. +func (cc *Conn) Context() context.Context { + return cc.session.Context() +} + +// AsyncPing sends ping and receivedPong will be called when pong arrives. It returns cancellation of ping operation. +func (cc *Conn) AsyncPing(receivedPong func()) (func(), error) { + token, err := message.GetToken() + if err != nil { + return nil, fmt.Errorf("cannot get token: %w", err) + } + req := cc.session.messagePool.AcquireMessage(cc.Context()) + req.SetToken(token) + req.SetCode(codes.Ping) + defer cc.ReleaseMessage(req) + + if _, loaded := cc.tokenHandlerContainer.LoadOrStore(token.Hash(), func(w *responsewriter.ResponseWriter[*Conn], r *pool.Message) { + if r.Code() == codes.Pong { + receivedPong() + } + }); loaded { + return nil, fmt.Errorf("cannot add token handler: %w", coapErrors.ErrKeyAlreadyExists) + } + removeTokenHandler := func() { + _, _ = cc.tokenHandlerContainer.LoadAndDelete(token.Hash()) + } + err = cc.session.WriteMessage(req) + if err != nil { + removeTokenHandler() + return nil, fmt.Errorf("cannot write request: %w", err) + } + return removeTokenHandler, nil +} + +// Run reads and process requests from a connection, until the connection is not closed. +func (cc *Conn) Run() (err error) { + return cc.session.Run(cc) +} + +// AddOnClose calls function on close connection event. +func (cc *Conn) AddOnClose(f EventFunc) { + cc.session.AddOnClose(f) +} + +// RemoteAddr gets remote address. +func (cc *Conn) RemoteAddr() net.Addr { + return cc.session.RemoteAddr() +} + +func (cc *Conn) LocalAddr() net.Addr { + return cc.session.LocalAddr() +} + +// Sequence acquires sequence number. +func (cc *Conn) Sequence() uint64 { + return cc.session.Sequence() +} + +// SetContextValue stores the value associated with key to context of connection. +func (cc *Conn) SetContextValue(key interface{}, val interface{}) { + cc.session.SetContextValue(key, val) +} + +// Done signalizes that connection is not more processed. +func (cc *Conn) Done() <-chan struct{} { + return cc.session.Done() +} + +// CheckExpirations checks and remove expired items from caches. +func (cc *Conn) CheckExpirations(now time.Time) { + cc.session.CheckExpirations(now, cc) + if cc.blockWise != nil { + cc.blockWise.CheckExpirations(now) + } +} + +func (cc *Conn) AcquireMessage(ctx context.Context) *pool.Message { + return cc.session.AcquireMessage(ctx) +} + +func (cc *Conn) ReleaseMessage(m *pool.Message) { + cc.session.ReleaseMessage(m) +} + +// NetConn returns the underlying connection that is wrapped by cc. The Conn returned is shared by all invocations of NetConn, so do not modify it. +func (cc *Conn) NetConn() net.Conn { + return cc.session.NetConn() +} + +// DoObserve subscribes for every change with request. +func (cc *Conn) doObserve(req *pool.Message, observeFunc func(req *pool.Message)) (client.Observation, error) { + return cc.observationHandler.NewObservation(req, observeFunc) +} + +func (cc *Conn) ProcessReceivedMessageWithHandler(req *pool.Message, handler HandlerFunc) { + origResp := cc.AcquireMessage(cc.Context()) + origResp.SetToken(req.Token()) + w := responsewriter.New(origResp, cc, req.Options()...) + handler(w, req) + defer cc.ReleaseMessage(w.Message()) + if !req.IsHijacked() { + cc.ReleaseMessage(req) + } + if w.Message().IsModified() { + err := cc.Session().WriteMessage(w.Message()) + if err != nil { + if errC := cc.Close(); errC != nil { + cc.Session().errors(fmt.Errorf("cannot close connection: %w", errC)) + } + cc.Session().errors(fmt.Errorf("cannot write response to %v: %w", cc.RemoteAddr(), err)) + } + } +} + +func (cc *Conn) blockwiseHandle(w *responsewriter.ResponseWriter[*Conn], r *pool.Message) { + if h, ok := cc.tokenHandlerContainer.Load(r.Token().Hash()); ok { + h(w, r) + return + } + cc.observationHandler.Handle(w, r) +} + +func (cc *Conn) handle(w *responsewriter.ResponseWriter[*Conn], r *pool.Message) { + if cc.blockWise != nil && cc.peerBlockWiseTranferEnabled.Load() { + cc.blockWise.Handle(w, r, cc.blockwiseSZX, cc.Session().maxMessageSize, cc.blockwiseHandle) + return + } + if h, ok := cc.tokenHandlerContainer.LoadAndDelete(r.Token().Hash()); ok { + h(w, r) + return + } + cc.observationHandler.Handle(w, r) +} + +func (cc *Conn) sendPong(token message.Token) error { + req := cc.AcquireMessage(cc.Context()) + defer cc.ReleaseMessage(req) + req.SetCode(codes.Pong) + req.SetToken(token) + return cc.Session().WriteMessage(req) +} + +func (cc *Conn) handleSignals(r *pool.Message) bool { + switch r.Code() { + case codes.CSM: + if cc.disablePeerTCPSignalMessageCSMs { + return true + } + if size, err := r.GetOptionUint32(message.TCPMaxMessageSize); err == nil { + cc.peerMaxMessageSize.Store(size) + } + if r.HasOption(message.TCPBlockWiseTransfer) { + cc.peerBlockWiseTranferEnabled.Store(true) + } + return true + case codes.Ping: + // if r.HasOption(message.TCPCustody) { + // TODO + // } + if err := cc.sendPong(r.Token()); err != nil && !coapNet.IsConnectionBrokenError(err) { + cc.Session().errors(fmt.Errorf("cannot handle ping signal: %w", err)) + } + return true + case codes.Release: + // if r.HasOption(message.TCPAlternativeAddress) { + // TODO + // } + return true + case codes.Abort: + // if r.HasOption(message.TCPBadCSMOption) { + // TODO + // } + return true + case codes.Pong: + if h, ok := cc.tokenHandlerContainer.LoadAndDelete(r.Token().Hash()); ok { + cc.processReceivedMessage(r, cc, h) + } + return true + } + return false +} + +func (cc *Conn) pushToReceivedMessageQueue(r *pool.Message) { + if cc.handleSignals(r) { + return + } + select { + case cc.receivedMessageReader.C() <- r: + case <-cc.Context().Done(): + } +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/tcp/client/session.go b/vendor/github.com/plgd-dev/go-coap/v3/tcp/client/session.go new file mode 100644 index 00000000000..8332b263d30 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/tcp/client/session.go @@ -0,0 +1,272 @@ +package client + +import ( + "bytes" + "context" + "errors" + "fmt" + "net" + "sync" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + coapNet "github.com/plgd-dev/go-coap/v3/net" + "github.com/plgd-dev/go-coap/v3/net/monitor/inactivity" + "github.com/plgd-dev/go-coap/v3/tcp/coder" + "go.uber.org/atomic" +) + +type Session struct { + // This field needs to be the first in the struct to ensure proper word alignment on 32-bit platforms. + // See: https://golang.org/pkg/sync/atomic/#pkg-note-BUG + sequence atomic.Uint64 + inactivityMonitor InactivityMonitor + errSendCSM error + cancel context.CancelFunc + done chan struct{} + errors ErrorFunc + connection *coapNet.Conn + messagePool *pool.Pool + ctx atomic.Value // TODO: change to atomic.Pointer[context.Context] for go1.19 + maxMessageSize uint32 + private struct { + mutex sync.Mutex + onClose []EventFunc + } + connectionCacheSize uint16 + disableTCPSignalMessageCSM bool + closeSocket bool +} + +func NewSession( + ctx context.Context, + connection *coapNet.Conn, + maxMessageSize uint32, + errors ErrorFunc, + disableTCPSignalMessageCSM bool, + closeSocket bool, + inactivityMonitor InactivityMonitor, + connectionCacheSize uint16, + messagePool *pool.Pool, +) *Session { + ctx, cancel := context.WithCancel(ctx) + if errors == nil { + errors = func(error) { + // default no-op + } + } + if inactivityMonitor == nil { + inactivityMonitor = inactivity.NewNilMonitor[*Conn]() + } + + s := &Session{ + cancel: cancel, + connection: connection, + maxMessageSize: maxMessageSize, + errors: errors, + disableTCPSignalMessageCSM: disableTCPSignalMessageCSM, + closeSocket: closeSocket, + inactivityMonitor: inactivityMonitor, + done: make(chan struct{}), + connectionCacheSize: connectionCacheSize, + messagePool: messagePool, + } + s.ctx.Store(&ctx) + + if !disableTCPSignalMessageCSM { + err := s.sendCSM() + if err != nil { + s.errSendCSM = fmt.Errorf("cannot send CSM: %w", err) + } + } + + return s +} + +// SetContextValue stores the value associated with key to context of connection. +func (s *Session) SetContextValue(key interface{}, val interface{}) { + ctx := context.WithValue(s.Context(), key, val) + s.ctx.Store(&ctx) +} + +// Done signalizes that connection is not more processed. +func (s *Session) Done() <-chan struct{} { + return s.done +} + +func (s *Session) AddOnClose(f EventFunc) { + s.private.mutex.Lock() + defer s.private.mutex.Unlock() + s.private.onClose = append(s.private.onClose, f) +} + +func (s *Session) popOnClose() []EventFunc { + s.private.mutex.Lock() + defer s.private.mutex.Unlock() + tmp := s.private.onClose + s.private.onClose = nil + return tmp +} + +func (s *Session) shutdown() { + defer close(s.done) + for _, f := range s.popOnClose() { + f() + } +} + +func (s *Session) Close() error { + s.cancel() + if s.closeSocket { + return s.connection.Close() + } + return nil +} + +func (s *Session) Sequence() uint64 { + return s.sequence.Inc() +} + +func (s *Session) Context() context.Context { + return *s.ctx.Load().(*context.Context) //nolint:forcetypeassert +} + +func seekBufferToNextMessage(buffer *bytes.Buffer, msgSize int) *bytes.Buffer { + if msgSize == buffer.Len() { + // buffer is empty so reset it + buffer.Reset() + return buffer + } + // rewind to next message + trimmed := 0 + for trimmed != msgSize { + b := make([]byte, 4096) + max := 4096 + if msgSize-trimmed < max { + max = msgSize - trimmed + } + v, _ := buffer.Read(b[:max]) + trimmed += v + } + return buffer +} + +func (s *Session) processBuffer(buffer *bytes.Buffer, cc *Conn) error { + for buffer.Len() > 0 { + var header coder.MessageHeader + _, err := coder.DefaultCoder.DecodeHeader(buffer.Bytes(), &header) + if errors.Is(err, message.ErrShortRead) { + return nil + } + if header.MessageLength > s.maxMessageSize { + return fmt.Errorf("max message size(%v) was exceeded %v", s.maxMessageSize, header.MessageLength) + } + if uint32(buffer.Len()) < header.MessageLength { + return nil + } + req := s.messagePool.AcquireMessage(s.Context()) + read, err := req.UnmarshalWithDecoder(coder.DefaultCoder, buffer.Bytes()[:header.MessageLength]) + if err != nil { + s.messagePool.ReleaseMessage(req) + return fmt.Errorf("cannot unmarshal with header: %w", err) + } + buffer = seekBufferToNextMessage(buffer, read) + req.SetSequence(s.Sequence()) + s.inactivityMonitor.Notify() + cc.pushToReceivedMessageQueue(req) + } + return nil +} + +func (s *Session) WriteMessage(req *pool.Message) error { + data, err := req.MarshalWithEncoder(coder.DefaultCoder) + if err != nil { + return fmt.Errorf("cannot marshal: %w", err) + } + err = s.connection.WriteWithContext(req.Context(), data) + if err != nil { + return fmt.Errorf("cannot write to connection: %w", err) + } + return err +} + +func (s *Session) sendCSM() error { + token, err := message.GetToken() + if err != nil { + return fmt.Errorf("cannot get token: %w", err) + } + req := s.messagePool.AcquireMessage(s.Context()) + defer s.messagePool.ReleaseMessage(req) + req.SetCode(codes.CSM) + req.SetToken(token) + return s.WriteMessage(req) +} + +func shrinkBufferIfNecessary(buffer *bytes.Buffer, maxCap uint16) *bytes.Buffer { + if buffer.Len() == 0 && buffer.Cap() > int(maxCap) { + buffer = bytes.NewBuffer(make([]byte, 0, maxCap)) + } + return buffer +} + +// Run reads and process requests from a connection, until the connection is not closed. +func (s *Session) Run(cc *Conn) (err error) { + defer func() { + err1 := s.Close() + if err == nil { + err = err1 + } + s.shutdown() + }() + if s.errSendCSM != nil { + return s.errSendCSM + } + buffer := bytes.NewBuffer(make([]byte, 0, s.connectionCacheSize)) + readBuf := make([]byte, s.connectionCacheSize) + for { + err = s.processBuffer(buffer, cc) + if err != nil { + return err + } + buffer = shrinkBufferIfNecessary(buffer, s.connectionCacheSize) + readLen, err := s.connection.ReadWithContext(s.Context(), readBuf) + if err != nil { + if coapNet.IsConnectionBrokenError(err) { // other side closed the connection, ignore the error and return + return nil + } + return fmt.Errorf("cannot read from connection: %w", err) + } + if readLen > 0 { + buffer.Write(readBuf[:readLen]) + } + } +} + +// CheckExpirations checks and remove expired items from caches. +func (s *Session) CheckExpirations(now time.Time, cc *Conn) { + s.inactivityMonitor.CheckInactivity(now, cc) +} + +func (s *Session) AcquireMessage(ctx context.Context) *pool.Message { + return s.messagePool.AcquireMessage(ctx) +} + +func (s *Session) ReleaseMessage(m *pool.Message) { + s.messagePool.ReleaseMessage(m) +} + +// RemoteAddr gets remote address. +func (s *Session) RemoteAddr() net.Addr { + return s.connection.RemoteAddr() +} + +func (s *Session) LocalAddr() net.Addr { + return s.connection.LocalAddr() +} + +// NetConn returns the underlying connection that is wrapped by s. The Conn returned is shared by all invocations of NetConn, so do not modify it. +func (s *Session) NetConn() net.Conn { + return s.connection.NetConn() +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/tcp/coder/coder.go b/vendor/github.com/plgd-dev/go-coap/v3/tcp/coder/coder.go new file mode 100644 index 00000000000..9979370cad5 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/tcp/coder/coder.go @@ -0,0 +1,254 @@ +package coder + +import ( + "encoding/binary" + "errors" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" +) + +var DefaultCoder = new(Coder) + +const ( + MessageLength13Base = 13 + MessageLength14Base = 269 + MessageLength15Base = 65805 + messageMaxLen = 0x7fff0000 // Large number that works in 32-bit builds +) + +type Coder struct{} + +type MessageHeader struct { + Token []byte + Length uint32 + MessageLength uint32 + Code codes.Code +} + +func (c *Coder) Size(m message.Message) (int, error) { + size, err := c.Encode(m, nil) + if errors.Is(err, message.ErrTooSmall) { + err = nil + } + return size, err +} + +func getHeader(messageLength int) (uint8, []byte) { + if messageLength < MessageLength13Base { + return uint8(messageLength), nil + } + if messageLength < MessageLength14Base { + extLen := messageLength - MessageLength13Base + extLenBytes := []byte{uint8(extLen)} + return 13, extLenBytes + } + if messageLength < MessageLength15Base { + extLen := messageLength - MessageLength14Base + extLenBytes := make([]byte, 2) + binary.BigEndian.PutUint16(extLenBytes, uint16(extLen)) + return 14, extLenBytes + } + if messageLength < messageMaxLen { + extLen := messageLength - MessageLength15Base + extLenBytes := make([]byte, 4) + binary.BigEndian.PutUint32(extLenBytes, uint32(extLen)) + return 15, extLenBytes + } + return 0, nil +} + +func (c *Coder) Encode(m message.Message, buf []byte) (int, error) { + /* + A CoAP Message message lomessage.OKs like: + + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Len | TKL | Extended Length ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Code | TKL bytes ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Options (if any) ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |1 1 1 1 1 1 1 1| Payload (if any) ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + The size of the Extended Length field is inferred from the value of the + Len field as follows: + + | Len value | Extended Length size | Total length | + +------------+-----------------------+---------------------------+ + | 0-12 | 0 | Len | + | 13 | 1 | Extended Length + 13 | + | 14 | 2 | Extended Length + 269 | + | 15 | 4 | Extended Length + 65805 | + */ + + if len(m.Token) > message.MaxTokenSize { + return -1, message.ErrInvalidTokenLen + } + + payloadLen := len(m.Payload) + if payloadLen > 0 { + // for separator 0xff + payloadLen++ + } + optionsLen, err := m.Options.Marshal(nil) + if !errors.Is(err, message.ErrTooSmall) { + return -1, err + } + bufLen := payloadLen + optionsLen + lenNib, extLenBytes := getHeader(bufLen) + + var hdr [1 + 4 + message.MaxTokenSize + 1]byte + hdrLen := 1 + len(extLenBytes) + len(m.Token) + 1 + hdrOff := 0 + + copyToHdr := func(offset int, data []byte) int { + if len(data) > 0 { + copy(hdr[hdrOff:hdrOff+len(data)], data) + offset += len(data) + } + return offset + } + + // Length and TKL nibbles. + hdr[hdrOff] = uint8(0xf&len(m.Token)) | (lenNib << 4) + hdrOff++ + + // Extended length, if present. + hdrOff = copyToHdr(hdrOff, extLenBytes) + + // Code. + hdr[hdrOff] = byte(m.Code) + hdrOff++ + + // Token. + copyToHdr(hdrOff, m.Token) + + bufLen += hdrLen + if len(buf) < bufLen { + return bufLen, message.ErrTooSmall + } + + copy(buf, hdr[:hdrLen]) + optionsLen, err = m.Options.Marshal(buf[hdrLen:]) + switch { + case err == nil: + case errors.Is(err, message.ErrTooSmall): + return bufLen, err + default: + return -1, err + } + if len(m.Payload) > 0 { + copy(buf[hdrLen+optionsLen:], []byte{0xff}) + copy(buf[hdrLen+optionsLen+1:], m.Payload) + } + + return bufLen, nil +} + +func (c *Coder) DecodeHeader(data []byte, h *MessageHeader) (int, error) { + hdrOff := uint32(0) + if len(data) == 0 { + return -1, message.ErrShortRead + } + + firstByte := data[0] + data = data[1:] + hdrOff++ + + lenNib := (firstByte & 0xf0) >> 4 + tkl := firstByte & 0x0f + + var opLen int + switch { + case lenNib < MessageLength13Base: + opLen = int(lenNib) + case lenNib == 13: + if len(data) < 1 { + return -1, message.ErrShortRead + } + extLen := data[0] + data = data[1:] + hdrOff++ + opLen = MessageLength13Base + int(extLen) + case lenNib == 14: + if len(data) < 2 { + return -1, message.ErrShortRead + } + extLen := binary.BigEndian.Uint16(data) + data = data[2:] + hdrOff += 2 + opLen = MessageLength14Base + int(extLen) + case lenNib == 15: + if len(data) < 4 { + return -1, message.ErrShortRead + } + extLen := binary.BigEndian.Uint32(data) + data = data[4:] + hdrOff += 4 + opLen = MessageLength15Base + int(extLen) + } + + h.MessageLength = hdrOff + 1 + uint32(tkl) + uint32(opLen) + if len(data) < 1 { + return -1, message.ErrShortRead + } + h.Code = codes.Code(data[0]) + data = data[1:] + hdrOff++ + if len(data) < int(tkl) { + return -1, message.ErrShortRead + } + if tkl > 0 { + h.Token = data[:tkl] + } + hdrOff += uint32(tkl) + h.Length = hdrOff + return int(h.Length), nil +} + +func (c *Coder) DecodeWithHeader(data []byte, header MessageHeader, m *message.Message) (int, error) { + optionDefs := message.CoapOptionDefs + processed := header.Length + switch header.Code { + case codes.CSM: + optionDefs = message.TCPSignalCSMOptionDefs + case codes.Ping, codes.Pong: + optionDefs = message.TCPSignalPingPongOptionDefs + case codes.Release: + optionDefs = message.TCPSignalReleaseOptionDefs + case codes.Abort: + optionDefs = message.TCPSignalAbortOptionDefs + } + + proc, err := m.Options.Unmarshal(data, optionDefs) + if err != nil { + return -1, err + } + data = data[proc:] + processed += uint32(proc) + + if len(data) > 0 { + m.Payload = data + } + processed += uint32(len(data)) + m.Code = header.Code + m.Token = header.Token + + return int(processed), nil +} + +func (c *Coder) Decode(data []byte, m *message.Message) (int, error) { + var header MessageHeader + _, err := c.DecodeHeader(data, &header) + if err != nil { + return -1, err + } + if uint32(len(data)) < header.MessageLength { + return -1, message.ErrShortRead + } + return c.DecodeWithHeader(data[header.Length:], header, m) +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/message/error.go b/vendor/github.com/plgd-dev/go-coap/v3/tcp/coder/error.go similarity index 90% rename from vendor/github.com/plgd-dev/go-coap/v2/udp/message/error.go rename to vendor/github.com/plgd-dev/go-coap/v3/tcp/coder/error.go index 9f550aad72c..ac7ae99aadc 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/message/error.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/tcp/coder/error.go @@ -1,4 +1,4 @@ -package message +package coder import "errors" diff --git a/vendor/github.com/plgd-dev/go-coap/v3/tcp/server.go b/vendor/github.com/plgd-dev/go-coap/v3/tcp/server.go new file mode 100644 index 00000000000..d5ebbffcbeb --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/tcp/server.go @@ -0,0 +1,9 @@ +package tcp + +import ( + "github.com/plgd-dev/go-coap/v3/tcp/server" +) + +func NewServer(opt ...server.Option) *server.Server { + return server.New(opt...) +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/tcp/server/config.go b/vendor/github.com/plgd-dev/go-coap/v3/tcp/server/config.go new file mode 100644 index 00000000000..6147ec36e91 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/tcp/server/config.go @@ -0,0 +1,62 @@ +package server + +import ( + "fmt" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + "github.com/plgd-dev/go-coap/v3/net/monitor/inactivity" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" + "github.com/plgd-dev/go-coap/v3/options/config" + "github.com/plgd-dev/go-coap/v3/tcp/client" +) + +// The HandlerFunc type is an adapter to allow the use of +// ordinary functions as COAP handlers. +type HandlerFunc = func(*responsewriter.ResponseWriter[*client.Conn], *pool.Message) + +type ErrorFunc = func(error) + +type GoPoolFunc = func(func()) error + +// OnNewConnFunc is the callback for new connections. +type OnNewConnFunc = func(cc *client.Conn) + +var DefaultConfig = func() Config { + opts := Config{ + Common: config.NewCommon[*client.Conn](), + CreateInactivityMonitor: func() client.InactivityMonitor { + maxRetries := uint32(2) + timeout := time.Second * 16 + onInactive := func(cc *client.Conn) { + _ = cc.Close() + } + keepalive := inactivity.NewKeepAlive(maxRetries, onInactive, func(cc *client.Conn, receivePong func()) (func(), error) { + return cc.AsyncPing(receivePong) + }) + return inactivity.New(timeout/time.Duration(maxRetries+1), keepalive.OnInactive) + }, + OnNewConn: func(cc *client.Conn) { + // do nothing by default + }, + ConnectionCacheSize: 2 * 1024, + } + opts.Handler = func(w *responsewriter.ResponseWriter[*client.Conn], r *pool.Message) { + if err := w.SetResponse(codes.NotFound, message.TextPlain, nil); err != nil { + opts.Errors(fmt.Errorf("server handler: cannot set response: %w", err)) + } + } + return opts +}() + +type Config struct { + config.Common[*client.Conn] + CreateInactivityMonitor client.CreateInactivityMonitorFunc + Handler HandlerFunc + OnNewConn OnNewConnFunc + ConnectionCacheSize uint16 + DisablePeerTCPSignalMessageCSMs bool + DisableTCPSignalMessageCSM bool +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/tcp/server/server.go b/vendor/github.com/plgd-dev/go-coap/v3/tcp/server/server.go new file mode 100644 index 00000000000..a869e298475 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/tcp/server/server.go @@ -0,0 +1,223 @@ +package server + +import ( + "context" + "errors" + "fmt" + "net" + "sync" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/pool" + coapNet "github.com/plgd-dev/go-coap/v3/net" + "github.com/plgd-dev/go-coap/v3/net/blockwise" + "github.com/plgd-dev/go-coap/v3/net/monitor/inactivity" + "github.com/plgd-dev/go-coap/v3/pkg/connections" + "github.com/plgd-dev/go-coap/v3/tcp/client" +) + +// A Option sets options such as credentials, codec and keepalive parameters, etc. +type Option interface { + TCPServerApply(cfg *Config) +} + +// Listener defined used by coap +type Listener interface { + Close() error + AcceptWithContext(ctx context.Context) (net.Conn, error) +} + +type Server struct { + listenMutex sync.Mutex + listen Listener + ctx context.Context + cancel context.CancelFunc + cfg *Config +} + +func New(opt ...Option) *Server { + cfg := DefaultConfig + for _, o := range opt { + o.TCPServerApply(&cfg) + } + + ctx, cancel := context.WithCancel(cfg.Ctx) + + if cfg.CreateInactivityMonitor == nil { + cfg.CreateInactivityMonitor = func() client.InactivityMonitor { + return inactivity.NewNilMonitor[*client.Conn]() + } + } + if cfg.MessagePool == nil { + cfg.MessagePool = pool.New(0, 0) + } + + if cfg.Errors == nil { + cfg.Errors = func(error) { + // default no-op + } + } + if cfg.GetToken == nil { + cfg.GetToken = message.GetToken + } + errorsFunc := cfg.Errors + // assign updated func to opts.errors so opts.handler also uses the updated error handler + cfg.Errors = func(err error) { + if coapNet.IsCancelOrCloseError(err) { + // this error was produced by cancellation context or closing connection. + return + } + errorsFunc(fmt.Errorf("tcp: %w", err)) + } + + return &Server{ + ctx: ctx, + cancel: cancel, + cfg: &cfg, + } +} + +func (s *Server) checkAndSetListener(l Listener) error { + s.listenMutex.Lock() + defer s.listenMutex.Unlock() + if s.listen != nil { + return fmt.Errorf("server already serves listener") + } + s.listen = l + return nil +} + +func (s *Server) popListener() Listener { + s.listenMutex.Lock() + defer s.listenMutex.Unlock() + l := s.listen + s.listen = nil + return l +} + +func (s *Server) checkAcceptError(err error) bool { + if err == nil { + return true + } + switch { + case errors.Is(err, coapNet.ErrListenerIsClosed): + s.Stop() + return false + case errors.Is(err, context.DeadlineExceeded), errors.Is(err, context.Canceled): + select { + case <-s.ctx.Done(): + default: + s.cfg.Errors(fmt.Errorf("cannot accept connection: %w", err)) + return true + } + return false + default: + return true + } +} + +func (s *Server) serveConnection(connections *connections.Connections, rw net.Conn) { + var cc *client.Conn + monitor := s.cfg.CreateInactivityMonitor() + cc = s.createConn(coapNet.NewConn(rw), monitor) + if s.cfg.OnNewConn != nil { + s.cfg.OnNewConn(cc) + } + connections.Store(cc) + defer connections.Delete(cc) + + if err := cc.Run(); err != nil { + s.cfg.Errors(fmt.Errorf("%v: %w", cc.RemoteAddr(), err)) + } +} + +func (s *Server) Serve(l Listener) error { + if s.cfg.BlockwiseSZX > blockwise.SZXBERT { + return fmt.Errorf("invalid blockwiseSZX") + } + + err := s.checkAndSetListener(l) + if err != nil { + return err + } + defer func() { + s.Stop() + }() + var wg sync.WaitGroup + defer wg.Wait() + + connections := connections.New() + s.cfg.PeriodicRunner(func(now time.Time) bool { + connections.CheckExpirations(now) + return s.ctx.Err() == nil + }) + defer connections.Close() + + for { + rw, err := l.AcceptWithContext(s.ctx) + if ok := s.checkAcceptError(err); !ok { + return nil + } + if rw == nil { + continue + } + wg.Add(1) + go func() { + defer wg.Done() + s.serveConnection(connections, rw) + }() + } +} + +// Stop stops server without wait of ends Serve function. +func (s *Server) Stop() { + s.cancel() + l := s.popListener() + if l == nil { + return + } + if err := l.Close(); err != nil { + s.cfg.Errors(fmt.Errorf("cannot close listener: %w", err)) + } +} + +func (s *Server) createConn(connection *coapNet.Conn, monitor client.InactivityMonitor) *client.Conn { + createBlockWise := func(cc *client.Conn) *blockwise.BlockWise[*client.Conn] { + return nil + } + if s.cfg.BlockwiseEnable { + createBlockWise = func(cc *client.Conn) *blockwise.BlockWise[*client.Conn] { + return blockwise.New( + cc, + s.cfg.BlockwiseTransferTimeout, + s.cfg.Errors, + func(token message.Token) (*pool.Message, bool) { + return nil, false + }, + ) + } + } + cfg := client.DefaultConfig + cfg.Ctx = s.ctx + cfg.Handler = s.cfg.Handler + cfg.MaxMessageSize = s.cfg.MaxMessageSize + cfg.Errors = s.cfg.Errors + cfg.BlockwiseSZX = s.cfg.BlockwiseSZX + cfg.DisablePeerTCPSignalMessageCSMs = s.cfg.DisablePeerTCPSignalMessageCSMs + cfg.DisableTCPSignalMessageCSM = s.cfg.DisableTCPSignalMessageCSM + cfg.CloseSocket = true + cfg.ConnectionCacheSize = s.cfg.ConnectionCacheSize + cfg.MessagePool = s.cfg.MessagePool + cfg.GetToken = s.cfg.GetToken + cfg.ProcessReceivedMessage = s.cfg.ProcessReceivedMessage + cfg.ReceivedMessageQueueSize = s.cfg.ReceivedMessageQueueSize + cc := client.NewConn( + connection, + createBlockWise, + monitor, + &cfg, + ) + + return cc +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/udp/client.go b/vendor/github.com/plgd-dev/go-coap/v3/udp/client.go new file mode 100644 index 00000000000..26e7a011c0f --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/udp/client.go @@ -0,0 +1,116 @@ +package udp + +import ( + "context" + "fmt" + "net" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/pool" + coapNet "github.com/plgd-dev/go-coap/v3/net" + "github.com/plgd-dev/go-coap/v3/net/blockwise" + "github.com/plgd-dev/go-coap/v3/net/monitor/inactivity" + "github.com/plgd-dev/go-coap/v3/options" + "github.com/plgd-dev/go-coap/v3/udp/client" + "github.com/plgd-dev/go-coap/v3/udp/server" +) + +// A Option sets options such as credentials, keepalive parameters, etc. +type Option interface { + UDPClientApply(cfg *client.Config) +} + +// Dial creates a client connection to the given target. +func Dial(target string, opts ...Option) (*client.Conn, error) { + cfg := client.DefaultConfig + for _, o := range opts { + o.UDPClientApply(&cfg) + } + c, err := cfg.Dialer.DialContext(cfg.Ctx, cfg.Net, target) + if err != nil { + return nil, err + } + conn, ok := c.(*net.UDPConn) + if !ok { + return nil, fmt.Errorf("unsupported connection type: %T", c) + } + opts = append(opts, options.WithCloseSocket()) + return Client(conn, opts...), nil +} + +// Client creates client over udp connection. +func Client(conn *net.UDPConn, opts ...Option) *client.Conn { + cfg := client.DefaultConfig + for _, o := range opts { + o.UDPClientApply(&cfg) + } + if cfg.Errors == nil { + cfg.Errors = func(error) { + // default no-op + } + } + if cfg.CreateInactivityMonitor == nil { + cfg.CreateInactivityMonitor = func() client.InactivityMonitor { + return inactivity.NewNilMonitor[*client.Conn]() + } + } + if cfg.MessagePool == nil { + cfg.MessagePool = pool.New(0, 0) + } + + errorsFunc := cfg.Errors + cfg.Errors = func(err error) { + if coapNet.IsCancelOrCloseError(err) { + // this error was produced by cancellation context or closing connection. + return + } + errorsFunc(fmt.Errorf("udp: %v: %w", conn.RemoteAddr(), err)) + } + addr, _ := conn.RemoteAddr().(*net.UDPAddr) + createBlockWise := func(cc *client.Conn) *blockwise.BlockWise[*client.Conn] { + return nil + } + if cfg.BlockwiseEnable { + createBlockWise = func(cc *client.Conn) *blockwise.BlockWise[*client.Conn] { + v := cc + return blockwise.New( + v, + cfg.BlockwiseTransferTimeout, + cfg.Errors, + func(token message.Token) (*pool.Message, bool) { + return v.GetObservationRequest(token) + }, + ) + } + } + + monitor := cfg.CreateInactivityMonitor() + l := coapNet.NewUDPConn(cfg.Net, conn, coapNet.WithErrors(cfg.Errors)) + session := server.NewSession(cfg.Ctx, + context.Background(), + l, + addr, + cfg.MaxMessageSize, + cfg.MTU, + cfg.CloseSocket, + ) + cc := client.NewConn(session, + createBlockWise, + monitor, + &cfg, + ) + cfg.PeriodicRunner(func(now time.Time) bool { + cc.CheckExpirations(now) + return cc.Context().Err() == nil + }) + + go func() { + err := cc.Run() + if err != nil { + cfg.Errors(err) + } + }() + + return cc +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/udp/client/config.go b/vendor/github.com/plgd-dev/go-coap/v3/udp/client/config.go new file mode 100644 index 00000000000..2b719f88c42 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/udp/client/config.go @@ -0,0 +1,55 @@ +package client + +import ( + "fmt" + "net" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + "github.com/plgd-dev/go-coap/v3/net/monitor/inactivity" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" + "github.com/plgd-dev/go-coap/v3/options/config" +) + +const DefaultMTU = 1472 + +var DefaultConfig = func() Config { + opts := Config{ + Common: config.NewCommon[*Conn](), + CreateInactivityMonitor: func() InactivityMonitor { + return inactivity.NewNilMonitor[*Conn]() + }, + Dialer: &net.Dialer{Timeout: time.Second * 3}, + Net: "udp", + TransmissionNStart: 1, + TransmissionAcknowledgeTimeout: time.Second * 2, + TransmissionMaxRetransmit: 4, + GetMID: message.GetMID, + MTU: DefaultMTU, + } + opts.Handler = func(w *responsewriter.ResponseWriter[*Conn], r *pool.Message) { + switch r.Code() { + case codes.POST, codes.PUT, codes.GET, codes.DELETE: + if err := w.SetResponse(codes.NotFound, message.TextPlain, nil); err != nil { + opts.Errors(fmt.Errorf("udp client: cannot set response: %w", err)) + } + } + } + return opts +}() + +type Config struct { + config.Common[*Conn] + CreateInactivityMonitor CreateInactivityMonitorFunc + Net string + GetMID GetMIDFunc + Handler HandlerFunc + Dialer *net.Dialer + TransmissionNStart uint32 + TransmissionAcknowledgeTimeout time.Duration + TransmissionMaxRetransmit uint32 + CloseSocket bool + MTU uint16 +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/udp/client/conn.go b/vendor/github.com/plgd-dev/go-coap/v3/udp/client/conn.go new file mode 100644 index 00000000000..8e20d8714f6 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/udp/client/conn.go @@ -0,0 +1,888 @@ +package client + +import ( + "context" + "errors" + "fmt" + "math" + "net" + "sync" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + coapNet "github.com/plgd-dev/go-coap/v3/net" + "github.com/plgd-dev/go-coap/v3/net/blockwise" + "github.com/plgd-dev/go-coap/v3/net/client" + limitparallelrequests "github.com/plgd-dev/go-coap/v3/net/client/limitParallelRequests" + "github.com/plgd-dev/go-coap/v3/net/observation" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" + "github.com/plgd-dev/go-coap/v3/options/config" + "github.com/plgd-dev/go-coap/v3/pkg/cache" + coapErrors "github.com/plgd-dev/go-coap/v3/pkg/errors" + "github.com/plgd-dev/go-coap/v3/pkg/fn" + coapSync "github.com/plgd-dev/go-coap/v3/pkg/sync" + "github.com/plgd-dev/go-coap/v3/udp/coder" + "go.uber.org/atomic" + "golang.org/x/sync/semaphore" +) + +// https://datatracker.ietf.org/doc/html/rfc7252#section-4.8.2 +const ExchangeLifetime = 247 * time.Second + +type ( + HandlerFunc = func(*responsewriter.ResponseWriter[*Conn], *pool.Message) + ErrorFunc = func(error) + EventFunc = func() + GetMIDFunc = func() int32 + CreateInactivityMonitorFunc = func() InactivityMonitor +) + +type InactivityMonitor interface { + Notify() + CheckInactivity(now time.Time, cc *Conn) +} + +type Session interface { + Context() context.Context + Close() error + MaxMessageSize() uint32 + RemoteAddr() net.Addr + LocalAddr() net.Addr + // NetConn returns the underlying connection that is wrapped by Session. The Conn returned is shared by all invocations of NetConn, so do not modify it. + NetConn() net.Conn + WriteMessage(req *pool.Message) error + // WriteMulticast sends multicast to the remote multicast address. + // By default it is sent over all network interfaces and all compatible source IP addresses with hop limit 1. + // Via opts you can specify the network interface, source IP address, and hop limit. + WriteMulticastMessage(req *pool.Message, address *net.UDPAddr, opts ...coapNet.MulticastOption) error + Run(cc *Conn) error + AddOnClose(f EventFunc) + SetContextValue(key interface{}, val interface{}) + Done() <-chan struct{} +} + +type RequestsMap = coapSync.Map[uint64, *pool.Message] + +const ( + errFmtWriteRequest = "cannot write request: %w" + errFmtWriteResponse = "cannot write response: %w" +) + +type midElement struct { + handler HandlerFunc + start time.Time + deadline time.Time + retransmit atomic.Int32 + + private struct { + sync.Mutex + msg *pool.Message + } +} + +func (m *midElement) ReleaseMessage(cc *Conn) { + m.private.Lock() + defer m.private.Unlock() + if m.private.msg != nil { + cc.ReleaseMessage(m.private.msg) + m.private.msg = nil + } +} + +func (m *midElement) IsExpired(now time.Time, maxRetransmit int32) bool { + if !m.deadline.IsZero() && now.After(m.deadline) { + // remove element if deadline is exceeded + return true + } + retransmit := m.retransmit.Load() + return retransmit >= maxRetransmit +} + +func (m *midElement) Retransmit(now time.Time, acknowledgeTimeout time.Duration) bool { + if now.After(m.start.Add(acknowledgeTimeout * time.Duration(m.retransmit.Load()+1))) { + m.retransmit.Inc() + // retransmit + return true + } + // wait for next retransmit + return false +} + +func (m *midElement) GetMessage(cc *Conn) (*pool.Message, bool, error) { + m.private.Lock() + defer m.private.Unlock() + if m.private.msg == nil { + return nil, false, nil + } + msg := cc.AcquireMessage(m.private.msg.Context()) + if err := m.private.msg.Clone(msg); err != nil { + cc.ReleaseMessage(msg) + return nil, false, err + } + return msg, true, nil +} + +// Conn represents a virtual connection to a conceptual endpoint, to perform COAPs commands. +type Conn struct { + // This field needs to be the first in the struct to ensure proper word alignment on 32-bit platforms. + // See: https://golang.org/pkg/sync/atomic/#pkg-note-BUG + sequence atomic.Uint64 + + session Session + *client.Client[*Conn] + inactivityMonitor InactivityMonitor + + blockWise *blockwise.BlockWise[*Conn] + observationHandler *observation.Handler[*Conn] + transmission *Transmission + messagePool *pool.Pool + + processReceivedMessage config.ProcessReceivedMessageFunc[*Conn] + errors ErrorFunc + responseMsgCache *cache.Cache[string, []byte] + msgIDMutex *MutexMap + + tokenHandlerContainer *coapSync.Map[uint64, HandlerFunc] + midHandlerContainer *coapSync.Map[int32, *midElement] + msgID atomic.Uint32 + blockwiseSZX blockwise.SZX + + /* + An outstanding interaction is either a CON for which an ACK has not + yet been received but is still expected (message layer) or a request + for which neither a response nor an Acknowledgment message has yet + been received but is still expected (which may both occur at the same + time, counting as one outstanding interaction). + */ + numOutstandingInteraction *semaphore.Weighted + receivedMessageReader *client.ReceivedMessageReader[*Conn] +} + +// Transmission is a threadsafe container for transmission related parameters +type Transmission struct { + nStart *atomic.Uint32 + acknowledgeTimeout *atomic.Duration + maxRetransmit *atomic.Int32 +} + +// SetTransmissionNStart changing the nStart value will only effect requests queued after the change. The requests waiting here already before the change will get unblocked when enough weight has been released. +func (t *Transmission) SetTransmissionNStart(d uint32) { + t.nStart.Store(d) +} + +func (t *Transmission) SetTransmissionAcknowledgeTimeout(d time.Duration) { + t.acknowledgeTimeout.Store(d) +} + +func (t *Transmission) SetTransmissionMaxRetransmit(d int32) { + t.maxRetransmit.Store(d) +} + +func (cc *Conn) Transmission() *Transmission { + return cc.transmission +} + +// NewConn creates connection over session and observation. +func NewConn( + session Session, + createBlockWise func(cc *Conn) *blockwise.BlockWise[*Conn], + inactivityMonitor InactivityMonitor, + cfg *Config, +) *Conn { + if cfg.Errors == nil { + cfg.Errors = func(error) { + // default no-op + } + } + if cfg.GetMID == nil { + cfg.GetMID = message.GetMID + } + if cfg.GetToken == nil { + cfg.GetToken = message.GetToken + } + if cfg.ReceivedMessageQueueSize < 0 { + cfg.ReceivedMessageQueueSize = 0 + } + + cc := Conn{ + session: session, + transmission: &Transmission{ + atomic.NewUint32(cfg.TransmissionNStart), + atomic.NewDuration(cfg.TransmissionAcknowledgeTimeout), + atomic.NewInt32(int32(cfg.TransmissionMaxRetransmit)), + }, + blockwiseSZX: cfg.BlockwiseSZX, + + tokenHandlerContainer: coapSync.NewMap[uint64, HandlerFunc](), + midHandlerContainer: coapSync.NewMap[int32, *midElement](), + processReceivedMessage: cfg.ProcessReceivedMessage, + errors: cfg.Errors, + msgIDMutex: NewMutexMap(), + responseMsgCache: cache.NewCache[string, []byte](), + inactivityMonitor: inactivityMonitor, + messagePool: cfg.MessagePool, + numOutstandingInteraction: semaphore.NewWeighted(math.MaxInt64), + } + cc.msgID.Store(uint32(cfg.GetMID() - 0xffff/2)) + cc.blockWise = createBlockWise(&cc) + limitParallelRequests := limitparallelrequests.New(cfg.LimitClientParallelRequests, cfg.LimitClientEndpointParallelRequests, cc.do, cc.doObserve) + cc.observationHandler = observation.NewHandler(&cc, cfg.Handler, limitParallelRequests.Do) + cc.Client = client.New(&cc, cc.observationHandler, cfg.GetToken, limitParallelRequests) + if cc.processReceivedMessage == nil { + cc.processReceivedMessage = processReceivedMessage + } + cc.receivedMessageReader = client.NewReceivedMessageReader(&cc, cfg.ReceivedMessageQueueSize) + return &cc +} + +func processReceivedMessage(req *pool.Message, cc *Conn, handler config.HandlerFunc[*Conn]) { + cc.ProcessReceivedMessageWithHandler(req, handler) +} + +func (cc *Conn) ProcessReceivedMessage(req *pool.Message) { + cc.processReceivedMessage(req, cc, cc.handleReq) +} + +func (cc *Conn) Session() Session { + return cc.session +} + +func (cc *Conn) GetMessageID() int32 { + // To prevent collisions during reconnections, it is important to always increment the global counter. + // For example, if a connection (cc) is established and later closed due to inactivity, a new cc may + // be created shortly after. However, if the new cc is initialized with the same message ID as the + // previous one, the receiver may mistakenly treat the incoming message as a duplicate and discard it. + // Hence, by incrementing the global counter, we can ensure unique message IDs and avoid such issues. + message.GetMID() + return int32(uint16(cc.msgID.Inc())) +} + +// Close closes connection without waiting for the end of the Run function. +func (cc *Conn) Close() error { + err := cc.session.Close() + if errors.Is(err, net.ErrClosed) { + return nil + } + return err +} + +func (cc *Conn) doInternal(req *pool.Message) (*pool.Message, error) { + token := req.Token() + if token == nil { + return nil, fmt.Errorf("invalid token") + } + + respChan := make(chan *pool.Message, 1) + if _, loaded := cc.tokenHandlerContainer.LoadOrStore(token.Hash(), func(w *responsewriter.ResponseWriter[*Conn], r *pool.Message) { + r.Hijack() + select { + case respChan <- r: + default: + } + }); loaded { + return nil, fmt.Errorf("cannot add token(%v) handler: %w", token, coapErrors.ErrKeyAlreadyExists) + } + defer func() { + _, _ = cc.tokenHandlerContainer.LoadAndDelete(token.Hash()) + }() + err := cc.writeMessage(req) + if err != nil { + return nil, fmt.Errorf(errFmtWriteRequest, err) + } + cc.receivedMessageReader.TryToReplaceLoop() + select { + case <-req.Context().Done(): + return nil, req.Context().Err() + case <-cc.Context().Done(): + return nil, fmt.Errorf("connection was closed: %w", cc.session.Context().Err()) + case resp := <-respChan: + return resp, nil + } +} + +// Do sends an coap message and returns an coap response. +// +// An error is returned if by failure to speak COAP (such as a network connectivity problem). +// Any status code doesn't cause an error. +// +// Caller is responsible to release request and response. +func (cc *Conn) do(req *pool.Message) (*pool.Message, error) { + if cc.blockWise == nil { + return cc.doInternal(req) + } + resp, err := cc.blockWise.Do(req, cc.blockwiseSZX, cc.session.MaxMessageSize(), func(bwReq *pool.Message) (*pool.Message, error) { + if bwReq.Options().HasOption(message.Block1) || bwReq.Options().HasOption(message.Block2) { + bwReq.SetMessageID(cc.GetMessageID()) + } + return cc.doInternal(bwReq) + }) + if err != nil { + return nil, err + } + return resp, nil +} + +// DoObserve subscribes for every change with request. +func (cc *Conn) doObserve(req *pool.Message, observeFunc func(req *pool.Message)) (client.Observation, error) { + return cc.observationHandler.NewObservation(req, observeFunc) +} + +func (cc *Conn) releaseOutstandingInteraction() { + cc.numOutstandingInteraction.Release(1) +} + +func (cc *Conn) acquireOutstandingInteraction(ctx context.Context) error { + nStart := cc.Transmission().nStart.Load() + if nStart == 0 { + return fmt.Errorf("invalid NStart value %v", nStart) + } + n := math.MaxInt64 - int64(cc.Transmission().nStart.Load()) + 1 + err := cc.numOutstandingInteraction.Acquire(ctx, n) + if err != nil { + return err + } + cc.numOutstandingInteraction.Release(n - 1) + return nil +} + +func (cc *Conn) waitForAcknowledge(req *pool.Message, waitForResponseChan chan struct{}) error { + cc.receivedMessageReader.TryToReplaceLoop() + select { + case <-waitForResponseChan: + return nil + case <-req.Context().Done(): + return req.Context().Err() + case <-cc.Context().Done(): + return fmt.Errorf("connection was closed: %w", cc.Context().Err()) + } +} + +func (cc *Conn) prepareWriteMessage(req *pool.Message, handler HandlerFunc) (func(), error) { + var closeFns fn.FuncList + + // Only confirmable messages ever match an message ID + switch req.Type() { + case message.Confirmable: + msg := cc.AcquireMessage(req.Context()) + if err := req.Clone(msg); err != nil { + cc.ReleaseMessage(msg) + return nil, fmt.Errorf("cannot clone message: %w", err) + } + if req.Code() >= codes.GET && req.Code() <= codes.DELETE { + if err := cc.acquireOutstandingInteraction(req.Context()); err != nil { + return nil, err + } + closeFns = append(closeFns, func() { + cc.releaseOutstandingInteraction() + }) + } + deadline, _ := req.Context().Deadline() + if _, loaded := cc.midHandlerContainer.LoadOrStore(req.MessageID(), &midElement{ + handler: handler, + start: time.Now(), + deadline: deadline, + private: struct { + sync.Mutex + msg *pool.Message + }{msg: msg}, + }); loaded { + closeFns.Execute() + return nil, fmt.Errorf("cannot insert mid(%v) handler: %w", req.MessageID(), coapErrors.ErrKeyAlreadyExists) + } + closeFns = append(closeFns, func() { + _, _ = cc.midHandlerContainer.LoadAndDelete(req.MessageID()) + }) + case message.NonConfirmable: + /* TODO need to acquireOutstandingInteraction + if req.Code() >= codes.GET && req.Code() <= codes.DELETE { + } + */ + } + return closeFns.ToFunction(), nil +} + +func (cc *Conn) writeMessageAsync(req *pool.Message) error { + req.UpsertType(message.Confirmable) + req.UpsertMessageID(cc.GetMessageID()) + closeFn, err := cc.prepareWriteMessage(req, func(w *responsewriter.ResponseWriter[*Conn], r *pool.Message) { + // do nothing + }) + if err != nil { + return err + } + defer closeFn() + if err := cc.session.WriteMessage(req); err != nil { + return fmt.Errorf(errFmtWriteRequest, err) + } + return nil +} + +func (cc *Conn) writeMessage(req *pool.Message) error { + req.UpsertType(message.Confirmable) + req.UpsertMessageID(cc.GetMessageID()) + if req.Type() != message.Confirmable { + return cc.writeMessageAsync(req) + } + respChan := make(chan struct{}) + closeFn, err := cc.prepareWriteMessage(req, func(w *responsewriter.ResponseWriter[*Conn], r *pool.Message) { + close(respChan) + }) + if err != nil { + return err + } + defer closeFn() + if err := cc.session.WriteMessage(req); err != nil { + return fmt.Errorf(errFmtWriteRequest, err) + } + if err := cc.waitForAcknowledge(req, respChan); err != nil { + return fmt.Errorf(errFmtWriteRequest, err) + } + return nil +} + +// WriteMessage sends an coap message. +func (cc *Conn) WriteMessage(req *pool.Message) error { + if cc.blockWise == nil { + return cc.writeMessage(req) + } + return cc.blockWise.WriteMessage(req, cc.blockwiseSZX, cc.session.MaxMessageSize(), func(bwReq *pool.Message) error { + if bwReq.Options().HasOption(message.Block1) || bwReq.Options().HasOption(message.Block2) { + bwReq.SetMessageID(cc.GetMessageID()) + } + return cc.writeMessage(bwReq) + }) +} + +// Context returns the client's context. +// +// If connections was closed context is cancelled. +func (cc *Conn) Context() context.Context { + return cc.session.Context() +} + +// AsyncPing sends ping and receivedPong will be called when pong arrives. It returns cancellation of ping operation. +func (cc *Conn) AsyncPing(receivedPong func()) (func(), error) { + req := cc.AcquireMessage(cc.Context()) + req.SetType(message.Confirmable) + req.SetCode(codes.Empty) + mid := cc.GetMessageID() + req.SetMessageID(mid) + if _, loaded := cc.midHandlerContainer.LoadOrStore(mid, &midElement{ + handler: func(w *responsewriter.ResponseWriter[*Conn], r *pool.Message) { + if r.Type() == message.Reset || r.Type() == message.Acknowledgement { + receivedPong() + } + }, + start: time.Now(), + deadline: time.Time{}, // no deadline + private: struct { + sync.Mutex + msg *pool.Message + }{msg: req}, + }); loaded { + return nil, fmt.Errorf("cannot insert mid(%v) handler: %w", mid, coapErrors.ErrKeyAlreadyExists) + } + removeMidHandler := func() { + if elem, ok := cc.midHandlerContainer.LoadAndDelete(mid); ok { + elem.ReleaseMessage(cc) + } + } + if err := cc.session.WriteMessage(req); err != nil { + removeMidHandler() + return nil, fmt.Errorf(errFmtWriteRequest, err) + } + return removeMidHandler, nil +} + +// Run reads and process requests from a connection, until the connection is closed. +func (cc *Conn) Run() error { + return cc.session.Run(cc) +} + +// AddOnClose calls function on close connection event. +func (cc *Conn) AddOnClose(f EventFunc) { + cc.session.AddOnClose(f) +} + +func (cc *Conn) RemoteAddr() net.Addr { + return cc.session.RemoteAddr() +} + +func (cc *Conn) LocalAddr() net.Addr { + return cc.session.LocalAddr() +} + +func (cc *Conn) sendPong(w *responsewriter.ResponseWriter[*Conn], r *pool.Message) { + if err := w.SetResponse(codes.Empty, message.TextPlain, nil); err != nil { + cc.errors(fmt.Errorf("cannot send pong response: %w", err)) + } + if r.Type() == message.Confirmable { + w.Message().SetType(message.Acknowledgement) + w.Message().SetMessageID(r.MessageID()) + } else { + if w.Message().Type() != message.Reset { + w.Message().SetType(message.NonConfirmable) + } + w.Message().SetMessageID(cc.GetMessageID()) + } +} + +func (cc *Conn) handle(w *responsewriter.ResponseWriter[*Conn], m *pool.Message) { + if m.IsSeparateMessage() { + // msg was processed by token handler - just drop it. + return + } + if cc.blockWise != nil { + cc.blockWise.Handle(w, m, cc.blockwiseSZX, cc.session.MaxMessageSize(), func(rw *responsewriter.ResponseWriter[*Conn], rm *pool.Message) { + if h, ok := cc.tokenHandlerContainer.LoadAndDelete(rm.Token().Hash()); ok { + h(rw, rm) + return + } + cc.observationHandler.Handle(rw, rm) + }) + return + } + if h, ok := cc.tokenHandlerContainer.LoadAndDelete(m.Token().Hash()); ok { + h(w, m) + return + } + cc.observationHandler.Handle(w, m) +} + +// Sequence acquires sequence number. +func (cc *Conn) Sequence() uint64 { + return cc.sequence.Add(1) +} + +func (cc *Conn) responseMsgCacheID(msgID int32) string { + return fmt.Sprintf("resp-%v-%d", cc.RemoteAddr(), msgID) +} + +func (cc *Conn) addResponseToCache(resp *pool.Message) error { + marshaledResp, err := resp.MarshalWithEncoder(coder.DefaultCoder) + if err != nil { + return err + } + cacheMsg := make([]byte, len(marshaledResp)) + copy(cacheMsg, marshaledResp) + cc.responseMsgCache.LoadOrStore(cc.responseMsgCacheID(resp.MessageID()), cache.NewElement(cacheMsg, time.Now().Add(ExchangeLifetime), nil)) + return nil +} + +func (cc *Conn) getResponseFromCache(mid int32, resp *pool.Message) (bool, error) { + cachedResp := cc.responseMsgCache.Load(cc.responseMsgCacheID(mid)) + if cachedResp == nil { + return false, nil + } + if rawMsg := cachedResp.Data(); len(rawMsg) > 0 { + _, err := resp.UnmarshalWithDecoder(coder.DefaultCoder, rawMsg) + if err != nil { + return false, err + } + return true, nil + } + return false, nil +} + +// checkMyMessageID compare client msgID against peer messageID and if it is near < 0xffff/4 then incrase msgID. +// When msgIDs met it can cause issue because cache can send message to which doesn't bellows to request. +func (cc *Conn) checkMyMessageID(req *pool.Message) { + if req.Type() == message.Confirmable { + for { + oldID := cc.msgID.Load() + if uint16(req.MessageID())-uint16(cc.msgID.Load()) >= 0xffff/4 { + return + } + newID := oldID + 0xffff/2 + if cc.msgID.CompareAndSwap(oldID, newID) { + break + } + } + } +} + +func (cc *Conn) checkResponseCache(req *pool.Message, w *responsewriter.ResponseWriter[*Conn]) (bool, error) { + if req.Type() == message.Confirmable || req.Type() == message.NonConfirmable { + if ok, err := cc.getResponseFromCache(req.MessageID(), w.Message()); ok { + w.Message().SetMessageID(req.MessageID()) + w.Message().SetType(message.NonConfirmable) + if req.Type() == message.Confirmable { + // req could be changed from NonConfirmation to confirmation message. + w.Message().SetType(message.Acknowledgement) + } + return true, nil + } else if err != nil { + return false, fmt.Errorf("cannot unmarshal response from cache: %w", err) + } + } + return false, nil +} + +func isPongOrResetResponse(w *responsewriter.ResponseWriter[*Conn]) bool { + return w.Message().IsModified() && (w.Message().Type() == message.Reset || w.Message().Code() == codes.Empty) +} + +func sendJustAcknowledgeMessage(reqType message.Type, w *responsewriter.ResponseWriter[*Conn]) bool { + return reqType == message.Confirmable && !w.Message().IsModified() +} + +func (cc *Conn) processResponse(reqType message.Type, reqMessageID int32, w *responsewriter.ResponseWriter[*Conn]) error { + switch { + case isPongOrResetResponse(w): + if reqType == message.Confirmable { + w.Message().SetType(message.Acknowledgement) + w.Message().SetMessageID(reqMessageID) + } else { + if w.Message().Type() != message.Reset { + w.Message().SetType(message.NonConfirmable) + } + w.Message().SetMessageID(cc.GetMessageID()) + } + return nil + case sendJustAcknowledgeMessage(reqType, w): + // send message to separate(confirm received) message, if response is not modified + w.Message().SetCode(codes.Empty) + w.Message().SetType(message.Acknowledgement) + w.Message().SetMessageID(reqMessageID) + w.Message().SetToken(nil) + err := cc.addResponseToCache(w.Message()) + if err != nil { + return fmt.Errorf("cannot cache response: %w", err) + } + return nil + case !w.Message().IsModified(): + // don't send response + return nil + } + + // send piggybacked response + w.Message().SetType(message.Confirmable) + w.Message().SetMessageID(cc.GetMessageID()) + if reqType == message.Confirmable { + w.Message().SetType(message.Acknowledgement) + w.Message().SetMessageID(reqMessageID) + } + if reqType == message.Confirmable || reqType == message.NonConfirmable { + err := cc.addResponseToCache(w.Message()) + if err != nil { + return fmt.Errorf("cannot cache response: %w", err) + } + } + return nil +} + +func (cc *Conn) handleReq(w *responsewriter.ResponseWriter[*Conn], req *pool.Message) { + defer cc.inactivityMonitor.Notify() + reqMid := req.MessageID() + + // The same message ID can not be handled concurrently + // for deduplication to work + l := cc.msgIDMutex.Lock(reqMid) + defer l.Unlock() + + if ok, err := cc.checkResponseCache(req, w); err != nil { + cc.closeConnection() + cc.errors(fmt.Errorf(errFmtWriteResponse, err)) + return + } else if ok { + return + } + + w.Message().SetModified(false) + reqType := req.Type() + reqMessageID := req.MessageID() + cc.handle(w, req) + + err := cc.processResponse(reqType, reqMessageID, w) + if err != nil { + cc.closeConnection() + cc.errors(fmt.Errorf(errFmtWriteResponse, err)) + } +} + +func (cc *Conn) closeConnection() { + if errC := cc.Close(); errC != nil { + cc.errors(fmt.Errorf("cannot close connection: %w", errC)) + } +} + +func (cc *Conn) ProcessReceivedMessageWithHandler(req *pool.Message, handler config.HandlerFunc[*Conn]) { + defer func() { + if !req.IsHijacked() { + cc.ReleaseMessage(req) + } + }() + resp := cc.AcquireMessage(cc.Context()) + resp.SetToken(req.Token()) + w := responsewriter.New(resp, cc, req.Options()...) + defer func() { + cc.ReleaseMessage(w.Message()) + }() + handler(w, req) + select { + case <-cc.Context().Done(): + return + default: + } + if !w.Message().IsModified() { + // nothing to send + return + } + errW := cc.writeMessageAsync(w.Message()) + if errW != nil { + cc.closeConnection() + cc.errors(fmt.Errorf(errFmtWriteResponse, errW)) + } +} + +func (cc *Conn) handlePong(w *responsewriter.ResponseWriter[*Conn], r *pool.Message) { + cc.sendPong(w, r) +} + +func (cc *Conn) handleSpecialMessages(r *pool.Message) bool { + // ping request + if r.Code() == codes.Empty && r.Type() == message.Confirmable && len(r.Token()) == 0 && len(r.Options()) == 0 && r.Body() == nil { + cc.ProcessReceivedMessageWithHandler(r, cc.handlePong) + return true + } + // if waits for concrete message handler + if elem, ok := cc.midHandlerContainer.LoadAndDelete(r.MessageID()); ok { + elem.ReleaseMessage(cc) + resp := cc.AcquireMessage(cc.Context()) + resp.SetToken(r.Token()) + w := responsewriter.New(resp, cc, r.Options()...) + defer func() { + cc.ReleaseMessage(w.Message()) + }() + elem.handler(w, r) + // we just confirmed that message was processed for cc.writeMessage + // the body of the message is need to be processed by the loopOverReceivedMessageQueue goroutine + return false + } + // separate message + if r.IsSeparateMessage() { + // msg was processed by token handler - just drop it. + return true + } + return false +} + +func (cc *Conn) Process(datagram []byte) error { + if uint32(len(datagram)) > cc.session.MaxMessageSize() { + return fmt.Errorf("max message size(%v) was exceeded %v", cc.session.MaxMessageSize(), len(datagram)) + } + req := cc.AcquireMessage(cc.Context()) + _, err := req.UnmarshalWithDecoder(coder.DefaultCoder, datagram) + if err != nil { + cc.ReleaseMessage(req) + return err + } + req.SetSequence(cc.Sequence()) + cc.checkMyMessageID(req) + cc.inactivityMonitor.Notify() + if cc.handleSpecialMessages(req) { + return nil + } + select { + case cc.receivedMessageReader.C() <- req: + case <-cc.Context().Done(): + } + return nil +} + +// SetContextValue stores the value associated with key to context of connection. +func (cc *Conn) SetContextValue(key interface{}, val interface{}) { + cc.session.SetContextValue(key, val) +} + +// Done signalizes that connection is not more processed. +func (cc *Conn) Done() <-chan struct{} { + return cc.session.Done() +} + +func (cc *Conn) checkMidHandlerContainer(now time.Time, maxRetransmit int32, acknowledgeTimeout time.Duration, key int32, value *midElement) { + if value.IsExpired(now, maxRetransmit) { + cc.midHandlerContainer.Delete(key) + value.ReleaseMessage(cc) + cc.errors(fmt.Errorf(errFmtWriteRequest, context.DeadlineExceeded)) + return + } + if !value.Retransmit(now, acknowledgeTimeout) { + return + } + msg, ok, err := value.GetMessage(cc) + if err != nil { + cc.midHandlerContainer.Delete(key) + value.ReleaseMessage(cc) + cc.errors(fmt.Errorf(errFmtWriteRequest, err)) + return + } + if ok { + defer cc.ReleaseMessage(msg) + err := cc.session.WriteMessage(msg) + if err != nil { + cc.errors(fmt.Errorf(errFmtWriteRequest, err)) + } + } +} + +// CheckExpirations checks and remove expired items from caches. +func (cc *Conn) CheckExpirations(now time.Time) { + cc.inactivityMonitor.CheckInactivity(now, cc) + cc.responseMsgCache.CheckExpirations(now) + if cc.blockWise != nil { + cc.blockWise.CheckExpirations(now) + } + maxRetransmit := cc.transmission.maxRetransmit.Load() + acknowledgeTimeout := cc.transmission.acknowledgeTimeout.Load() + x := struct { + now time.Time + maxRetransmit int32 + acknowledgeTimeout time.Duration + cc *Conn + }{ + now: now, + maxRetransmit: maxRetransmit, + acknowledgeTimeout: acknowledgeTimeout, + cc: cc, + } + cc.midHandlerContainer.Range(func(key int32, value *midElement) bool { + x.cc.checkMidHandlerContainer(x.now, x.maxRetransmit, x.acknowledgeTimeout, key, value) + return true + }) +} + +func (cc *Conn) AcquireMessage(ctx context.Context) *pool.Message { + return cc.messagePool.AcquireMessage(ctx) +} + +func (cc *Conn) ReleaseMessage(m *pool.Message) { + cc.messagePool.ReleaseMessage(m) +} + +// WriteMulticastMessage sends multicast to the remote multicast address. +// By default it is sent over all network interfaces and all compatible source IP addresses with hop limit 1. +// Via opts you can specify the network interface, source IP address, and hop limit. +func (cc *Conn) WriteMulticastMessage(req *pool.Message, address *net.UDPAddr, options ...coapNet.MulticastOption) error { + if req.Type() == message.Confirmable { + return fmt.Errorf("multicast messages cannot be confirmable") + } + req.UpsertMessageID(cc.GetMessageID()) + + err := cc.session.WriteMulticastMessage(req, address, options...) + if err != nil { + return fmt.Errorf(errFmtWriteRequest, err) + } + return nil +} + +func (cc *Conn) InactivityMonitor() InactivityMonitor { + return cc.inactivityMonitor +} + +// NetConn returns the underlying connection that is wrapped by cc. The Conn returned is shared by all invocations of NetConn, so do not modify it. +func (cc *Conn) NetConn() net.Conn { + return cc.session.NetConn() +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/client/mutexmap.go b/vendor/github.com/plgd-dev/go-coap/v3/udp/client/mutexmap.go similarity index 100% rename from vendor/github.com/plgd-dev/go-coap/v2/udp/client/mutexmap.go rename to vendor/github.com/plgd-dev/go-coap/v3/udp/client/mutexmap.go diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/message/message.go b/vendor/github.com/plgd-dev/go-coap/v3/udp/coder/coder.go similarity index 73% rename from vendor/github.com/plgd-dev/go-coap/v2/udp/message/message.go rename to vendor/github.com/plgd-dev/go-coap/v3/udp/coder/coder.go index c61f7a2532f..4b1f0d35de2 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/message/message.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/udp/coder/coder.go @@ -1,27 +1,19 @@ -package message +package coder import ( "encoding/binary" "errors" + "fmt" - "github.com/plgd-dev/go-coap/v2/message" - "github.com/plgd-dev/go-coap/v2/message/codes" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" ) -// TcpMessage is a CoAP MessageBase that can encode itself for Message -// transport. -type Message struct { - Token message.Token - Payload []byte +var DefaultCoder = new(Coder) - Options message.Options //Options must be sorted by ID - Code codes.Code +type Coder struct{} - MessageID uint16 - Type Type -} - -func (m Message) Size() (int, error) { +func (c *Coder) Size(m message.Message) (int, error) { if len(m.Token) > message.MaxTokenSize { return -1, message.ErrInvalidTokenLen } @@ -32,24 +24,14 @@ func (m Message) Size() (int, error) { return -1, err } if payloadLen > 0 { - //for separator 0xff + // for separator 0xff payloadLen++ } size += payloadLen + optionsLen return size, nil } -func (m Message) Marshal() ([]byte, error) { - b := make([]byte, 1024) - l, err := m.MarshalTo(b) - if errors.Is(err, message.ErrTooSmall) { - b = append(b[:0], make([]byte, l)...) - l, err = m.MarshalTo(b) - } - return b[:l], err -} - -func (m Message) MarshalTo(buf []byte) (int, error) { +func (c *Coder) Encode(m message.Message, buf []byte) (int, 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 @@ -63,7 +45,13 @@ func (m Message) MarshalTo(buf []byte) (int, error) { |1 1 1 1 1 1 1 1| Payload (if any) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ - size, err := m.Size() + if !message.ValidateMID(m.MessageID) { + return -1, fmt.Errorf("invalid MessageID(%v)", m.MessageID) + } + if !message.ValidateType(m.Type) { + return -1, fmt.Errorf("invalid Type(%v)", m.Type) + } + size, err := c.Size(m) if err != nil { return -1, err } @@ -72,7 +60,7 @@ func (m Message) MarshalTo(buf []byte) (int, error) { } tmpbuf := []byte{0, 0} - binary.BigEndian.PutUint16(tmpbuf, m.MessageID) + binary.BigEndian.PutUint16(tmpbuf, uint16(m.MessageID)) buf[0] = (1 << 6) | byte(m.Type)<<4 | byte(0xf&len(m.Token)) buf[1] = byte(m.Code) @@ -104,7 +92,7 @@ func (m Message) MarshalTo(buf []byte) (int, error) { return size, nil } -func (m *Message) Unmarshal(data []byte) (int, error) { +func (c *Coder) Decode(data []byte, m *message.Message) (int, error) { size := len(data) if size < 4 { return -1, ErrMessageTruncated @@ -114,7 +102,7 @@ func (m *Message) Unmarshal(data []byte) (int, error) { return -1, ErrMessageInvalidVersion } - typ := Type((data[0] >> 4) & 0x3) + typ := message.Type((data[0] >> 4) & 0x3) tokenLen := int(data[0] & 0xf) if tokenLen > 8 { return -1, message.ErrInvalidTokenLen @@ -146,7 +134,7 @@ func (m *Message) Unmarshal(data []byte) (int, error) { m.Code = code m.Token = token m.Type = typ - m.MessageID = messageID + m.MessageID = int32(messageID) return size, nil } diff --git a/vendor/github.com/plgd-dev/go-coap/v3/udp/coder/error.go b/vendor/github.com/plgd-dev/go-coap/v3/udp/coder/error.go new file mode 100644 index 00000000000..ac7ae99aadc --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/udp/coder/error.go @@ -0,0 +1,8 @@ +package coder + +import "errors" + +var ( + ErrMessageTruncated = errors.New("message is truncated") + ErrMessageInvalidVersion = errors.New("message has invalid version") +) diff --git a/vendor/github.com/plgd-dev/go-coap/v3/udp/server.go b/vendor/github.com/plgd-dev/go-coap/v3/udp/server.go new file mode 100644 index 00000000000..767bfffae1f --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/udp/server.go @@ -0,0 +1,7 @@ +package udp + +import "github.com/plgd-dev/go-coap/v3/udp/server" + +func NewServer(opt ...server.Option) *server.Server { + return server.New(opt...) +} diff --git a/vendor/github.com/plgd-dev/go-coap/v3/udp/server/config.go b/vendor/github.com/plgd-dev/go-coap/v3/udp/server/config.go new file mode 100644 index 00000000000..7cfb498e3df --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/udp/server/config.go @@ -0,0 +1,64 @@ +package server + +import ( + "fmt" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + "github.com/plgd-dev/go-coap/v3/net/monitor/inactivity" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" + "github.com/plgd-dev/go-coap/v3/options/config" + udpClient "github.com/plgd-dev/go-coap/v3/udp/client" +) + +// The HandlerFunc type is an adapter to allow the use of +// ordinary functions as COAP handlers. +type HandlerFunc = func(*responsewriter.ResponseWriter[*udpClient.Conn], *pool.Message) + +type ErrorFunc = func(error) + +// OnNewConnFunc is the callback for new connections. +type OnNewConnFunc = func(cc *udpClient.Conn) + +type GetMIDFunc = func() int32 + +var DefaultConfig = func() Config { + opts := Config{ + Common: config.NewCommon[*udpClient.Conn](), + CreateInactivityMonitor: func() udpClient.InactivityMonitor { + timeout := time.Second * 16 + onInactive := func(cc *udpClient.Conn) { + _ = cc.Close() + } + return inactivity.New(timeout, onInactive) + }, + OnNewConn: func(cc *udpClient.Conn) { + // do nothing by default + }, + TransmissionNStart: 1, + TransmissionAcknowledgeTimeout: time.Second * 2, + TransmissionMaxRetransmit: 4, + GetMID: message.GetMID, + MTU: udpClient.DefaultMTU, + } + opts.Handler = func(w *responsewriter.ResponseWriter[*udpClient.Conn], r *pool.Message) { + if err := w.SetResponse(codes.NotFound, message.TextPlain, nil); err != nil { + opts.Errors(fmt.Errorf("udp server: cannot set response: %w", err)) + } + } + return opts +}() + +type Config struct { + config.Common[*udpClient.Conn] + CreateInactivityMonitor udpClient.CreateInactivityMonitorFunc + GetMID GetMIDFunc + Handler HandlerFunc + OnNewConn OnNewConnFunc + TransmissionNStart uint32 + TransmissionAcknowledgeTimeout time.Duration + TransmissionMaxRetransmit uint32 + MTU uint16 +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/discover.go b/vendor/github.com/plgd-dev/go-coap/v3/udp/server/discover.go similarity index 69% rename from vendor/github.com/plgd-dev/go-coap/v2/udp/discover.go rename to vendor/github.com/plgd-dev/go-coap/v3/udp/server/discover.go index 7914f491c61..aa41af4ce8e 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/discover.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/udp/server/discover.go @@ -1,14 +1,17 @@ -package udp +package server import ( "context" "fmt" "net" - coapNet "github.com/plgd-dev/go-coap/v2/net" - "github.com/plgd-dev/go-coap/v2/udp/client" - "github.com/plgd-dev/go-coap/v2/udp/message" - "github.com/plgd-dev/go-coap/v2/udp/message/pool" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/pool" + coapNet "github.com/plgd-dev/go-coap/v3/net" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" + "github.com/plgd-dev/go-coap/v3/pkg/errors" + "github.com/plgd-dev/go-coap/v3/udp/client" + "github.com/plgd-dev/go-coap/v3/udp/coder" ) // Discover sends GET to multicast or unicast address and waits for responses until context timeouts or server shutdown. @@ -16,14 +19,19 @@ import ( // address where was request sent. For Discover it allows the client to send a response from another address where was request send. // By default it is sent over all network interfaces and all compatible source IP addresses with hop limit 1. // Via opts you can specify the network interface, source IP address, and hop limit. -func (s *Server) Discover(ctx context.Context, address, path string, receiverFunc func(cc *client.ClientConn, resp *pool.Message), opts ...coapNet.MulticastOption) error { - req, err := client.NewGetRequest(ctx, s.messagePool, path) +func (s *Server) Discover(ctx context.Context, address, path string, receiverFunc func(cc *client.Conn, resp *pool.Message), opts ...coapNet.MulticastOption) error { + token, err := s.cfg.GetToken() + if err != nil { + return fmt.Errorf("cannot get token: %w", err) + } + req := s.cfg.MessagePool.AcquireMessage(ctx) + defer s.cfg.MessagePool.ReleaseMessage(req) + err = req.SetupGet(path, token) if err != nil { return fmt.Errorf("cannot create discover request: %w", err) } - req.SetMessageID(s.getMID()) + req.SetMessageID(s.cfg.GetMID()) req.SetType(message.NonConfirmable) - defer s.messagePool.ReleaseMessage(req) return s.DiscoveryRequest(req, address, receiverFunc, opts...) } @@ -32,7 +40,7 @@ func (s *Server) Discover(ctx context.Context, address, path string, receiverFun // address where was request sent. For Discover it allows the client to send a response from another address where was request send. // By default it is sent over all network interfaces and all compatible source IP addresses with hop limit 1. // Via opts you can specify the network interface, source IP address, and hop limit. -func (s *Server) DiscoveryRequest(req *pool.Message, address string, receiverFunc func(cc *client.ClientConn, resp *pool.Message), opts ...coapNet.MulticastOption) error { +func (s *Server) DiscoveryRequest(req *pool.Message, address string, receiverFunc func(cc *client.Conn, resp *pool.Message), opts ...coapNet.MulticastOption) error { token := req.Token() if len(token) == 0 { return fmt.Errorf("invalid token") @@ -46,20 +54,19 @@ func (s *Server) DiscoveryRequest(req *pool.Message, address string, receiverFun return fmt.Errorf("cannot resolve address: %w", err) } - data, err := req.Marshal() + data, err := req.MarshalWithEncoder(coder.DefaultCoder) if err != nil { return fmt.Errorf("cannot marshal req: %w", err) } s.multicastRequests.Store(token.Hash(), req) defer s.multicastRequests.Delete(token.Hash()) - err = s.multicastHandler.Insert(token, func(w *client.ResponseWriter, r *pool.Message) { - receiverFunc(w.ClientConn(), r) - }) - if err != nil { - return err + if _, loaded := s.multicastHandler.LoadOrStore(token.Hash(), func(w *responsewriter.ResponseWriter[*client.Conn], r *pool.Message) { + receiverFunc(w.Conn(), r) + }); loaded { + return errors.ErrKeyAlreadyExists } defer func() { - _, _ = s.multicastHandler.Pop(token) + _, _ = s.multicastHandler.LoadAndDelete(token.Hash()) }() if addr.IP.IsMulticast() { diff --git a/vendor/github.com/plgd-dev/go-coap/v3/udp/server/server.go b/vendor/github.com/plgd-dev/go-coap/v3/udp/server/server.go new file mode 100644 index 00000000000..8bceeab5a68 --- /dev/null +++ b/vendor/github.com/plgd-dev/go-coap/v3/udp/server/server.go @@ -0,0 +1,383 @@ +package server + +import ( + "context" + "fmt" + "net" + "sync" + "time" + + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/pool" + coapNet "github.com/plgd-dev/go-coap/v3/net" + "github.com/plgd-dev/go-coap/v3/net/blockwise" + "github.com/plgd-dev/go-coap/v3/net/monitor/inactivity" + "github.com/plgd-dev/go-coap/v3/net/responsewriter" + "github.com/plgd-dev/go-coap/v3/pkg/cache" + coapSync "github.com/plgd-dev/go-coap/v3/pkg/sync" + "github.com/plgd-dev/go-coap/v3/udp/client" +) + +type Server struct { + doneCtx context.Context + ctx context.Context + multicastRequests *client.RequestsMap + multicastHandler *coapSync.Map[uint64, HandlerFunc] + serverStartedChan chan struct{} + doneCancel context.CancelFunc + cancel context.CancelFunc + responseMsgCache *cache.Cache[string, []byte] + + connsMutex sync.Mutex + conns map[string]*client.Conn + + listenMutex sync.Mutex + listen *coapNet.UDPConn + + cfg *Config +} + +// A Option sets options such as credentials, codec and keepalive parameters, etc. +type Option interface { + UDPServerApply(cfg *Config) +} + +func New(opt ...Option) *Server { + cfg := DefaultConfig + for _, o := range opt { + o.UDPServerApply(&cfg) + } + + if cfg.Errors == nil { + cfg.Errors = func(error) { + // default no-op + } + } + + if cfg.GetMID == nil { + cfg.GetMID = message.GetMID + } + + if cfg.GetToken == nil { + cfg.GetToken = message.GetToken + } + + if cfg.CreateInactivityMonitor == nil { + cfg.CreateInactivityMonitor = func() client.InactivityMonitor { + return inactivity.NewNilMonitor[*client.Conn]() + } + } + if cfg.MessagePool == nil { + cfg.MessagePool = pool.New(0, 0) + } + + ctx, cancel := context.WithCancel(cfg.Ctx) + serverStartedChan := make(chan struct{}) + + doneCtx, doneCancel := context.WithCancel(context.Background()) + errorsFunc := cfg.Errors + cfg.Errors = func(err error) { + if coapNet.IsCancelOrCloseError(err) { + // this error was produced by cancellation context or closing connection. + return + } + errorsFunc(fmt.Errorf("udp: %w", err)) + } + return &Server{ + ctx: ctx, + cancel: cancel, + multicastHandler: coapSync.NewMap[uint64, HandlerFunc](), + multicastRequests: coapSync.NewMap[uint64, *pool.Message](), + serverStartedChan: serverStartedChan, + doneCtx: doneCtx, + doneCancel: doneCancel, + responseMsgCache: cache.NewCache[string, []byte](), + conns: make(map[string]*client.Conn), + + cfg: &cfg, + } +} + +func (s *Server) checkAndSetListener(l *coapNet.UDPConn) error { + s.listenMutex.Lock() + defer s.listenMutex.Unlock() + if s.listen != nil { + return fmt.Errorf("server already serve: %v", s.listen.LocalAddr().String()) + } + s.listen = l + close(s.serverStartedChan) + return nil +} + +func (s *Server) closeConnection(cc *client.Conn) { + if err := cc.Close(); err != nil { + s.cfg.Errors(fmt.Errorf("cannot close connection: %w", err)) + } +} + +func (s *Server) Serve(l *coapNet.UDPConn) error { + if s.cfg.BlockwiseSZX > blockwise.SZX1024 { + return fmt.Errorf("invalid blockwiseSZX") + } + + err := s.checkAndSetListener(l) + if err != nil { + return err + } + + defer func() { + s.closeSessions() + s.doneCancel() + s.listenMutex.Lock() + defer s.listenMutex.Unlock() + s.listen = nil + s.serverStartedChan = make(chan struct{}, 1) + }() + + m := make([]byte, s.cfg.MaxMessageSize) + var wg sync.WaitGroup + + s.cfg.PeriodicRunner(func(now time.Time) bool { + s.handleInactivityMonitors(now) + s.responseMsgCache.CheckExpirations(now) + return s.ctx.Err() == nil + }) + + for { + buf := m + n, raddr, err := l.ReadWithContext(s.ctx, buf) + if err != nil { + wg.Wait() + + select { + case <-s.ctx.Done(): + return nil + default: + if coapNet.IsCancelOrCloseError(err) { + return nil + } + return err + } + } + buf = buf[:n] + cc, err := s.getConn(l, raddr, true) + if err != nil { + s.cfg.Errors(fmt.Errorf("%v: cannot get client connection: %w", raddr, err)) + continue + } + err = cc.Process(buf) + if err != nil { + s.closeConnection(cc) + s.cfg.Errors(fmt.Errorf("%v: cannot process packet: %w", cc.RemoteAddr(), err)) + } + } +} + +func (s *Server) getListener() *coapNet.UDPConn { + s.listenMutex.Lock() + defer s.listenMutex.Unlock() + return s.listen +} + +// Stop stops server without wait of ends Serve function. +func (s *Server) Stop() { + s.cancel() + l := s.getListener() + if l != nil { + if errC := l.Close(); errC != nil { + s.cfg.Errors(fmt.Errorf("cannot close listener: %w", errC)) + } + } + s.closeSessions() +} + +func (s *Server) closeSessions() { + s.connsMutex.Lock() + conns := s.conns + s.conns = make(map[string]*client.Conn) + s.connsMutex.Unlock() + for _, cc := range conns { + s.closeConnection(cc) + if closeFn := getClose(cc); closeFn != nil { + closeFn() + } + } +} + +func (s *Server) conn() *coapNet.UDPConn { + s.listenMutex.Lock() + serverStartedChan := s.serverStartedChan + s.listenMutex.Unlock() + select { + case <-serverStartedChan: + case <-s.ctx.Done(): + } + s.listenMutex.Lock() + defer s.listenMutex.Unlock() + return s.listen +} + +const closeKey = "gocoapCloseConnection" + +func (s *Server) getConns() []*client.Conn { + s.connsMutex.Lock() + defer s.connsMutex.Unlock() + conns := make([]*client.Conn, 0, 32) + for _, c := range s.conns { + conns = append(conns, c) + } + return conns +} + +func (s *Server) handleInactivityMonitors(now time.Time) { + for _, cc := range s.getConns() { + select { + case <-cc.Context().Done(): + if closeFn := getClose(cc); closeFn != nil { + closeFn() + } + continue + default: + cc.CheckExpirations(now) + } + } +} + +func getClose(cc *client.Conn) func() { + v := cc.Context().Value(closeKey) + if v == nil { + return nil + } + closeFn, ok := v.(func()) + if !ok { + panic(fmt.Errorf("invalid type(%T) of context value for key %s", v, closeKey)) + } + return closeFn +} + +func (s *Server) getOrCreateConn(udpConn *coapNet.UDPConn, raddr *net.UDPAddr) (cc *client.Conn, created bool) { + s.connsMutex.Lock() + defer s.connsMutex.Unlock() + key := raddr.String() + cc = s.conns[key] + + if cc != nil { + return cc, false + } + + createBlockWise := func(cc *client.Conn) *blockwise.BlockWise[*client.Conn] { + return nil + } + if s.cfg.BlockwiseEnable { + createBlockWise = func(cc *client.Conn) *blockwise.BlockWise[*client.Conn] { + v := cc + return blockwise.New( + v, + s.cfg.BlockwiseTransferTimeout, + s.cfg.Errors, + func(token message.Token) (*pool.Message, bool) { + msg, ok := v.GetObservationRequest(token) + if ok { + return msg, ok + } + return s.multicastRequests.LoadWithFunc(token.Hash(), func(m *pool.Message) *pool.Message { + msg := v.AcquireMessage(m.Context()) + msg.ResetOptionsTo(m.Options()) + msg.SetCode(m.Code()) + msg.SetToken(m.Token()) + msg.SetMessageID(m.MessageID()) + return msg + }) + }) + } + } + session := NewSession( + s.ctx, + s.doneCtx, + udpConn, + raddr, + s.cfg.MaxMessageSize, + s.cfg.MTU, + false, + ) + monitor := s.cfg.CreateInactivityMonitor() + cfg := client.DefaultConfig + cfg.TransmissionNStart = s.cfg.TransmissionNStart + cfg.TransmissionAcknowledgeTimeout = s.cfg.TransmissionAcknowledgeTimeout + cfg.TransmissionMaxRetransmit = s.cfg.TransmissionMaxRetransmit + cfg.Handler = func(w *responsewriter.ResponseWriter[*client.Conn], r *pool.Message) { + h, ok := s.multicastHandler.Load(r.Token().Hash()) + if ok { + h(w, r) + return + } + s.cfg.Handler(w, r) + } + cfg.BlockwiseSZX = s.cfg.BlockwiseSZX + cfg.Errors = s.cfg.Errors + cfg.GetMID = s.cfg.GetMID + cfg.GetToken = s.cfg.GetToken + cfg.MessagePool = s.cfg.MessagePool + cfg.ProcessReceivedMessage = s.cfg.ProcessReceivedMessage + cfg.ReceivedMessageQueueSize = s.cfg.ReceivedMessageQueueSize + + cc = client.NewConn( + session, + createBlockWise, + monitor, + &cfg, + ) + cc.SetContextValue(closeKey, func() { + if err := session.Close(); err != nil { + s.cfg.Errors(fmt.Errorf("cannot close session: %w", err)) + } + session.shutdown() + }) + cc.AddOnClose(func() { + s.connsMutex.Lock() + defer s.connsMutex.Unlock() + if cc == s.conns[key] { + delete(s.conns, key) + } + }) + s.conns[key] = cc + return cc, true +} + +func (s *Server) getConn(l *coapNet.UDPConn, raddr *net.UDPAddr, firstTime bool) (*client.Conn, error) { + cc, created := s.getOrCreateConn(l, raddr) + if created { + if s.cfg.OnNewConn != nil { + s.cfg.OnNewConn(cc) + } + } else { + // check if client is not expired now + 10ms - if so, close it + // 10ms - The expected maximum time taken by cc.CheckExpirations and cc.InactivityMonitor().Notify() + cc.CheckExpirations(time.Now().Add(10 * time.Millisecond)) + if cc.Context().Err() == nil { + // if client is not closed, extend expiration time + cc.InactivityMonitor().Notify() + } + } + + if cc.Context().Err() != nil { + // connection is closed so we need to create new one + if closeFn := getClose(cc); closeFn != nil { + closeFn() + } + if firstTime { + return s.getConn(l, raddr, false) + } + return nil, fmt.Errorf("connection is closed") + } + return cc, nil +} + +func (s *Server) NewConn(addr *net.UDPAddr) (*client.Conn, error) { + l := s.getListener() + if l == nil { + // server is not started/stopped + return nil, fmt.Errorf("server is not running") + } + return s.getConn(l, addr, true) +} diff --git a/vendor/github.com/plgd-dev/go-coap/v2/udp/session.go b/vendor/github.com/plgd-dev/go-coap/v3/udp/server/session.go similarity index 78% rename from vendor/github.com/plgd-dev/go-coap/v2/udp/session.go rename to vendor/github.com/plgd-dev/go-coap/v3/udp/server/session.go index 712fba22a8c..870ff53c107 100644 --- a/vendor/github.com/plgd-dev/go-coap/v2/udp/session.go +++ b/vendor/github.com/plgd-dev/go-coap/v3/udp/server/session.go @@ -1,4 +1,4 @@ -package udp +package server import ( "context" @@ -7,9 +7,10 @@ import ( "sync" "sync/atomic" - coapNet "github.com/plgd-dev/go-coap/v2/net" - "github.com/plgd-dev/go-coap/v2/udp/client" - "github.com/plgd-dev/go-coap/v2/udp/message/pool" + "github.com/plgd-dev/go-coap/v3/message/pool" + coapNet "github.com/plgd-dev/go-coap/v3/net" + "github.com/plgd-dev/go-coap/v3/udp/client" + "github.com/plgd-dev/go-coap/v3/udp/coder" ) type EventFunc = func() @@ -17,7 +18,7 @@ type EventFunc = func() type Session struct { onClose []EventFunc - ctx atomic.Value + ctx atomic.Value // TODO: change to atomic.Pointer[context.Context] for go1.19 doneCtx context.Context connection *coapNet.UDPConn @@ -28,17 +29,19 @@ type Session struct { mutex sync.Mutex maxMessageSize uint32 + mtu uint16 closeSocket bool } func NewSession( ctx context.Context, + doneCtx context.Context, connection *coapNet.UDPConn, raddr *net.UDPAddr, maxMessageSize uint32, + mtu uint16, closeSocket bool, - doneCtx context.Context, ) *Session { ctx, cancel := context.WithCancel(ctx) @@ -48,6 +51,7 @@ func NewSession( connection: connection, raddr: raddr, maxMessageSize: maxMessageSize, + mtu: mtu, closeSocket: closeSocket, doneCtx: doneCtx, doneCancel: doneCancel, @@ -58,8 +62,6 @@ func NewSession( // SetContextValue stores the value associated with key to context of connection. func (s *Session) SetContextValue(key interface{}, val interface{}) { - s.mutex.Lock() - defer s.mutex.Unlock() ctx := context.WithValue(s.Context(), key, val) s.ctx.Store(&ctx) } @@ -99,11 +101,11 @@ func (s *Session) Close() error { } func (s *Session) Context() context.Context { - return *s.ctx.Load().(*context.Context) + return *s.ctx.Load().(*context.Context) //nolint:forcetypeassert } func (s *Session) WriteMessage(req *pool.Message) error { - data, err := req.Marshal() + data, err := req.MarshalWithEncoder(coder.DefaultCoder) if err != nil { return fmt.Errorf("cannot marshal: %w", err) } @@ -114,7 +116,7 @@ func (s *Session) WriteMessage(req *pool.Message) error { // By default it is sent over all network interfaces and all compatible source IP addresses with hop limit 1. // Via opts you can specify the network interface, source IP address, and hop limit. func (s *Session) WriteMulticastMessage(req *pool.Message, address *net.UDPAddr, opts ...coapNet.MulticastOption) error { - data, err := req.Marshal() + data, err := req.MarshalWithEncoder(coder.DefaultCoder) if err != nil { return fmt.Errorf("cannot marshal: %w", err) } @@ -122,7 +124,7 @@ func (s *Session) WriteMulticastMessage(req *pool.Message, address *net.UDPAddr, return s.connection.WriteMulticast(req.Context(), address, data, opts...) } -func (s *Session) Run(cc *client.ClientConn) (err error) { +func (s *Session) Run(cc *client.Conn) (err error) { defer func() { err1 := s.Close() if err == nil { @@ -130,7 +132,7 @@ func (s *Session) Run(cc *client.ClientConn) (err error) { } s.shutdown() }() - m := make([]byte, s.maxMessageSize) + m := make([]byte, s.mtu) for { buf := m n, _, err := s.connection.ReadWithContext(s.Context(), buf) @@ -156,3 +158,8 @@ func (s *Session) RemoteAddr() net.Addr { func (s *Session) LocalAddr() net.Addr { return s.connection.LocalAddr() } + +// NetConn returns the underlying connection that is wrapped by s. The Conn returned is shared by all invocations of NetConn, so do not modify it. +func (s *Session) NetConn() net.Conn { + return s.connection.NetConn() +} diff --git a/vendor/github.com/plgd-dev/kit/v2/LICENSE b/vendor/github.com/plgd-dev/kit/v2/LICENSE deleted file mode 100644 index 261eeb9e9f8..00000000000 --- a/vendor/github.com/plgd-dev/kit/v2/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/plgd-dev/kit/v2/sync/map.go b/vendor/github.com/plgd-dev/kit/v2/sync/map.go deleted file mode 100644 index b0864376934..00000000000 --- a/vendor/github.com/plgd-dev/kit/v2/sync/map.go +++ /dev/null @@ -1,141 +0,0 @@ -package sync - -import "sync" - -// Map is like a Go map[interface{}]interface{} but is safe for concurrent use by multiple goroutines. -type Map struct { - mutex sync.Mutex - data map[interface{}]interface{} -} - -// NewMap creates map. -func NewMap() *Map { - return &Map{ - data: make(map[interface{}]interface{}), - } -} - -// Delete deletes the value for a key. -func (m *Map) Delete(key interface{}) { - m.DeleteWithFunc(key, nil) -} - -func (m *Map) DeleteWithFunc(key interface{}, onDeleteFunc func(value interface{})) { - m.mutex.Lock() - defer m.mutex.Unlock() - value, ok := m.data[key] - delete(m.data, key) - if ok && onDeleteFunc != nil { - onDeleteFunc(value) - } -} - -// Load returns the value stored in the map for a key, or nil if no value is present. -// The ok result indicates whether value was found in the map. -func (m *Map) Load(key interface{}) (value interface{}, ok bool) { - return m.LoadWithFunc(key, nil) -} - -func (m *Map) LoadWithFunc(key interface{}, onLoadFunc func(value interface{}) interface{}) (value interface{}, ok bool) { - m.mutex.Lock() - defer m.mutex.Unlock() - value, ok = m.data[key] - if ok && onLoadFunc != nil { - value = onLoadFunc(value) - } - return -} - -// LoadOrStore returns the existing value for the key if present. -// Otherwise, it stores and returns the given value. The loaded result is true if the value was loaded, false if stored. -func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) { - return m.LoadOrStoreWithFunc(key, nil, func() interface{} { return value }) -} - -func (m *Map) LoadOrStoreWithFunc(key interface{}, onLoadFunc func(value interface{}) interface{}, createFunc func() interface{}) (actual interface{}, loaded bool) { - m.mutex.Lock() - defer m.mutex.Unlock() - v, ok := m.data[key] - if ok { - if onLoadFunc != nil { - v = onLoadFunc(v) - } - return v, ok - } - v = createFunc() - m.data[key] = v - return v, ok -} - -// Replace replaces the existing value with a new value and returns old value for the key. -func (m *Map) Replace(key, value interface{}) (oldValue interface{}, oldLoaded bool) { - return m.ReplaceWithFunc(key, func(oldValue interface{}, oldLoaded bool) (newValue interface{}, delete bool) { return value, false }) -} - -func (m *Map) ReplaceWithFunc(key interface{}, onReplaceFunc func(oldValue interface{}, oldLoaded bool) (newValue interface{}, delete bool)) (oldValue interface{}, oldLoaded bool) { - m.mutex.Lock() - defer m.mutex.Unlock() - v, ok := m.data[key] - newValue, del := onReplaceFunc(v, ok) - if del { - delete(m.data, key) - return v, ok - } - m.data[key] = newValue - return v, ok -} - -// Range calls f sequentially for each key and value present in the map. If f returns false, range stops the iteration. -func (m *Map) Range(f func(key, value interface{}) bool) { - m.mutex.Lock() - defer m.mutex.Unlock() - for key, value := range m.data { - ok := f(key, value) - if !ok { - return - } - } -} - -// Store sets the value for a key. -func (m *Map) Store(key, value interface{}) { - m.StoreWithFunc(key, func() interface{} { return value }) -} - -func (m *Map) StoreWithFunc(key interface{}, createFunc func() interface{}) { - m.mutex.Lock() - defer m.mutex.Unlock() - m.data[key] = createFunc() -} - -// PullOut loads and deletes the value for a key. -func (m *Map) PullOut(key interface{}) (value interface{}, ok bool) { - return m.PullOutWithFunc(key, nil) -} - -func (m *Map) PullOutWithFunc(key interface{}, onLoadFunc func(value interface{}) interface{}) (value interface{}, ok bool) { - m.mutex.Lock() - defer m.mutex.Unlock() - value, ok = m.data[key] - delete(m.data, key) - if ok && onLoadFunc != nil { - value = onLoadFunc(value) - } - return -} - -// PullOutAll extracts internal map data and replace it with empty map. -func (m *Map) PullOutAll() (data map[interface{}]interface{}) { - m.mutex.Lock() - defer m.mutex.Unlock() - data = m.data - m.data = make(map[interface{}]interface{}) - return -} - -// Length returns number of stored values. -func (m *Map) Length() int { - m.mutex.Lock() - defer m.mutex.Unlock() - return len(m.data) -} diff --git a/vendor/github.com/plgd-dev/kit/v2/sync/once.go b/vendor/github.com/plgd-dev/kit/v2/sync/once.go deleted file mode 100644 index 339b4f6a3a7..00000000000 --- a/vendor/github.com/plgd-dev/kit/v2/sync/once.go +++ /dev/null @@ -1,35 +0,0 @@ -package sync - -import ( - "sync" - "sync/atomic" -) - -// Once performs exactly one action. -// See sync.Once for more details. -type Once struct { - m sync.Mutex - done uint32 -} - -// Done returns true after Try executes succesfully. -func (o *Once) Done() bool { - return atomic.LoadUint32(&o.done) == 1 -} - -// Try executes the function f exactly once for this instance of Once. -// If the function f returns false, it enables further execution attempts. -func (o *Once) Try(f func() bool) { - if atomic.LoadUint32(&o.done) == 1 { - return - } - - o.m.Lock() - defer o.m.Unlock() - if o.done == 1 { - return - } - if f() { - atomic.StoreUint32(&o.done, 1) - } -} diff --git a/vendor/github.com/plgd-dev/kit/v2/sync/pool.go b/vendor/github.com/plgd-dev/kit/v2/sync/pool.go deleted file mode 100644 index ebde9f8e352..00000000000 --- a/vendor/github.com/plgd-dev/kit/v2/sync/pool.go +++ /dev/null @@ -1,81 +0,0 @@ -package sync - -import ( - "context" - "fmt" - "sync" -) - -// Pool is a synchronized key-value store with customizable factory for missing items. -type Pool struct { - mtx sync.Mutex - store map[string]interface{} - create PoolFunc -} - -// PoolFunc is triggered on a miss by GetOrCreate, -// so that it may add the missing item to the pool. -type PoolFunc func(ctx context.Context, key string) (interface{}, error) - -// NewPool creates a pool. -func NewPool() *Pool { - return &Pool{store: make(map[string]interface{})} -} - -// SetFactory sets the pool factory for GetOrCreate. -func (p *Pool) SetFactory(f PoolFunc) { - p.mtx.Lock() - defer p.mtx.Unlock() - - p.create = f -} - -// Put adds an item to the pool. -func (p *Pool) Put(key string, item interface{}) { - p.mtx.Lock() - defer p.mtx.Unlock() - - p.store[key] = item -} - -// Get returns an item from the pool or false. -func (p *Pool) Get(key string) (_ interface{}, ok bool) { - p.mtx.Lock() - defer p.mtx.Unlock() - - item, ok := p.store[key] - return item, ok -} - -// Delete pops an item from the pool or false. -func (p *Pool) Delete(key string) (_ interface{}, ok bool) { - p.mtx.Lock() - defer p.mtx.Unlock() - - item, ok := p.store[key] - delete(p.store, key) - - return item, ok -} - -// GetOrCreate returns an item and calls the factory on a mis. -// Warning: The factory function is called under the lock, -// therefore it must not call any Pool's methods to avoid deadlocks. -func (p *Pool) GetOrCreate(ctx context.Context, key string) (interface{}, error) { - p.mtx.Lock() - defer p.mtx.Unlock() - - if item, ok := p.store[key]; ok { - return item, nil - } - - if p.create == nil { - return nil, fmt.Errorf("missing pool factory") - } - item, err := p.create(ctx, key) - if err != nil { - return nil, fmt.Errorf("could not create pool item %s: %w", key, err) - } - p.store[key] = item - return item, nil -} diff --git a/vendor/github.com/plgd-dev/kit/v2/sync/refcounter.go b/vendor/github.com/plgd-dev/kit/v2/sync/refcounter.go deleted file mode 100644 index d8c78674c0b..00000000000 --- a/vendor/github.com/plgd-dev/kit/v2/sync/refcounter.go +++ /dev/null @@ -1,59 +0,0 @@ -package sync - -import ( - "context" - "sync/atomic" -) - -type ReleaseDataFunc = func(ctx context.Context, data interface{}) error - -type RefCounter struct { - count int64 - data interface{} - releaseDataFunc ReleaseDataFunc -} - -// Data returns data -func (r *RefCounter) Data() interface{} { - v := atomic.LoadInt64(&r.count) - if v <= 0 { - panic("using RefCounter after data released") - } - return r.data -} - -// Acquire increments counter -func (r *RefCounter) Acquire() { - v := atomic.AddInt64(&r.count, 1) - if v <= 1 { - panic("using RefCounter after data released") - } -} - -// Count returns current counter value. -func (r *RefCounter) Count() int64 { - return atomic.LoadInt64(&r.count) -} - -// Release decrements counter, when counter reach 0, releaseDataFunc will be called -func (r *RefCounter) Release(ctx context.Context) error { - v := atomic.AddInt64(&r.count, -1) - if v < 0 { - panic("using RefCounter after data released") - } - if v == 0 { - if r.releaseDataFunc != nil { - return r.releaseDataFunc(ctx, r.data) - } - } - return nil -} - -// NewRefCounter creates RefCounter -func NewRefCounter(data interface{}, releaseDataFunc ReleaseDataFunc) *RefCounter { - return &RefCounter{ - data: data, - count: 1, - releaseDataFunc: releaseDataFunc, - } -} diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index fb4e50224c9..07dddc63cf4 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -2482,3 +2482,102 @@ func SchedGetAttr(pid int, flags uint) (*SchedAttr, error) { } return attr, nil } +<<<<<<< HEAD +======= + +/* + * Unimplemented + */ +// AfsSyscall +// ArchPrctl +// Brk +// ClockNanosleep +// ClockSettime +// Clone +// EpollCtlOld +// EpollPwait +// EpollWaitOld +// Execve +// Fork +// Futex +// GetKernelSyms +// GetMempolicy +// GetRobustList +// GetThreadArea +// Getpmsg +// IoCancel +// IoDestroy +// IoGetevents +// IoSetup +// IoSubmit +// IoprioGet +// IoprioSet +// KexecLoad +// LookupDcookie +// Mbind +// MigratePages +// Mincore +// ModifyLdt +// Mount +// MovePages +// MqGetsetattr +// MqNotify +// MqOpen +// MqTimedreceive +// MqTimedsend +// MqUnlink +// Msgctl +// Msgget +// Msgrcv +// Msgsnd +// Nfsservctl +// Personality +// Pselect6 +// Ptrace +// Putpmsg +// Quotactl +// Readahead +// Readv +// RemapFilePages +// RestartSyscall +// RtSigaction +// RtSigpending +// RtSigqueueinfo +// RtSigreturn +// RtSigsuspend +// RtSigtimedwait +// SchedGetPriorityMax +// SchedGetPriorityMin +// SchedGetparam +// SchedGetscheduler +// SchedRrGetInterval +// SchedSetparam +// SchedYield +// Security +// Semctl +// Semget +// Semop +// Semtimedop +// SetMempolicy +// SetRobustList +// SetThreadArea +// SetTidAddress +// Sigaltstack +// Swapoff +// Swapon +// Sysfs +// TimerCreate +// TimerDelete +// TimerGetoverrun +// TimerGettime +// TimerSettime +// Tkill (obsolete) +// Tuxcall +// Umount2 +// Uselib +// Utimensat +// Vfork +// Vhangup +// Vserver +// _Sysctl +>>>>>>> 03615721 (Add internal server dtls config) diff --git a/vendor/golang.org/x/sys/unix/syscall_netbsd.go b/vendor/golang.org/x/sys/unix/syscall_netbsd.go index 88162099af5..770f89904cd 100644 --- a/vendor/golang.org/x/sys/unix/syscall_netbsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_netbsd.go @@ -369,3 +369,265 @@ const ( func mremap(oldaddr uintptr, oldlength uintptr, newlength uintptr, flags int, newaddr uintptr) (uintptr, error) { return mremapNetBSD(oldaddr, oldlength, newaddr, newlength, flags) } +<<<<<<< HEAD +======= + +/* + * Unimplemented + */ +// ____semctl13 +// __clone +// __fhopen40 +// __fhstat40 +// __fhstatvfs140 +// __fstat30 +// __getcwd +// __getfh30 +// __getlogin +// __lstat30 +// __mount50 +// __msgctl13 +// __msync13 +// __ntp_gettime30 +// __posix_chown +// __posix_fchown +// __posix_lchown +// __posix_rename +// __setlogin +// __shmctl13 +// __sigaction_sigtramp +// __sigaltstack14 +// __sigpending14 +// __sigprocmask14 +// __sigsuspend14 +// __sigtimedwait +// __stat30 +// __syscall +// __vfork14 +// _ksem_close +// _ksem_destroy +// _ksem_getvalue +// _ksem_init +// _ksem_open +// _ksem_post +// _ksem_trywait +// _ksem_unlink +// _ksem_wait +// _lwp_continue +// _lwp_create +// _lwp_ctl +// _lwp_detach +// _lwp_exit +// _lwp_getname +// _lwp_getprivate +// _lwp_kill +// _lwp_park +// _lwp_self +// _lwp_setname +// _lwp_setprivate +// _lwp_suspend +// _lwp_unpark +// _lwp_unpark_all +// _lwp_wait +// _lwp_wakeup +// _pset_bind +// _sched_getaffinity +// _sched_getparam +// _sched_setaffinity +// _sched_setparam +// acct +// aio_cancel +// aio_error +// aio_fsync +// aio_read +// aio_return +// aio_suspend +// aio_write +// break +// clock_getres +// clock_gettime +// clock_settime +// compat_09_ogetdomainname +// compat_09_osetdomainname +// compat_09_ouname +// compat_10_omsgsys +// compat_10_osemsys +// compat_10_oshmsys +// compat_12_fstat12 +// compat_12_getdirentries +// compat_12_lstat12 +// compat_12_msync +// compat_12_oreboot +// compat_12_oswapon +// compat_12_stat12 +// compat_13_sigaction13 +// compat_13_sigaltstack13 +// compat_13_sigpending13 +// compat_13_sigprocmask13 +// compat_13_sigreturn13 +// compat_13_sigsuspend13 +// compat_14___semctl +// compat_14_msgctl +// compat_14_shmctl +// compat_16___sigaction14 +// compat_16___sigreturn14 +// compat_20_fhstatfs +// compat_20_fstatfs +// compat_20_getfsstat +// compat_20_statfs +// compat_30___fhstat30 +// compat_30___fstat13 +// compat_30___lstat13 +// compat_30___stat13 +// compat_30_fhopen +// compat_30_fhstat +// compat_30_fhstatvfs1 +// compat_30_getdents +// compat_30_getfh +// compat_30_ntp_gettime +// compat_30_socket +// compat_40_mount +// compat_43_fstat43 +// compat_43_lstat43 +// compat_43_oaccept +// compat_43_ocreat +// compat_43_oftruncate +// compat_43_ogetdirentries +// compat_43_ogetdtablesize +// compat_43_ogethostid +// compat_43_ogethostname +// compat_43_ogetkerninfo +// compat_43_ogetpagesize +// compat_43_ogetpeername +// compat_43_ogetrlimit +// compat_43_ogetsockname +// compat_43_okillpg +// compat_43_olseek +// compat_43_ommap +// compat_43_oquota +// compat_43_orecv +// compat_43_orecvfrom +// compat_43_orecvmsg +// compat_43_osend +// compat_43_osendmsg +// compat_43_osethostid +// compat_43_osethostname +// compat_43_osigblock +// compat_43_osigsetmask +// compat_43_osigstack +// compat_43_osigvec +// compat_43_otruncate +// compat_43_owait +// compat_43_stat43 +// execve +// extattr_delete_fd +// extattr_delete_file +// extattr_delete_link +// extattr_get_fd +// extattr_get_file +// extattr_get_link +// extattr_list_fd +// extattr_list_file +// extattr_list_link +// extattr_set_fd +// extattr_set_file +// extattr_set_link +// extattrctl +// fchroot +// fdatasync +// fgetxattr +// fktrace +// flistxattr +// fork +// fremovexattr +// fsetxattr +// fstatvfs1 +// fsync_range +// getcontext +// getitimer +// getvfsstat +// getxattr +// ktrace +// lchflags +// lchmod +// lfs_bmapv +// lfs_markv +// lfs_segclean +// lfs_segwait +// lgetxattr +// lio_listio +// listxattr +// llistxattr +// lremovexattr +// lseek +// lsetxattr +// lutimes +// madvise +// mincore +// minherit +// modctl +// mq_close +// mq_getattr +// mq_notify +// mq_open +// mq_receive +// mq_send +// mq_setattr +// mq_timedreceive +// mq_timedsend +// mq_unlink +// msgget +// msgrcv +// msgsnd +// nfssvc +// ntp_adjtime +// pmc_control +// pmc_get_info +// pollts +// preadv +// profil +// pselect +// pset_assign +// pset_create +// pset_destroy +// ptrace +// pwritev +// quotactl +// rasctl +// readv +// reboot +// removexattr +// sa_enable +// sa_preempt +// sa_register +// sa_setconcurrency +// sa_stacks +// sa_yield +// sbrk +// sched_yield +// semconfig +// semget +// semop +// setcontext +// setitimer +// setxattr +// shmat +// shmdt +// shmget +// sstk +// statvfs1 +// swapctl +// sysarch +// syscall +// timer_create +// timer_delete +// timer_getoverrun +// timer_gettime +// timer_settime +// undelete +// utrace +// uuidgen +// vadvise +// vfork +// writev +>>>>>>> 03615721 (Add internal server dtls config) diff --git a/vendor/golang.org/x/sys/unix/syscall_solaris.go b/vendor/golang.org/x/sys/unix/syscall_solaris.go index b99cfa1342f..d0a0b3d59e5 100644 --- a/vendor/golang.org/x/sys/unix/syscall_solaris.go +++ b/vendor/golang.org/x/sys/unix/syscall_solaris.go @@ -698,6 +698,27 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e //sys setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) = libsocket.setsockopt //sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) = libsocket.recvfrom +<<<<<<< HEAD +======= +func readlen(fd int, buf *byte, nbuf int) (n int, err error) { + r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procread)), 3, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf), 0, 0, 0) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +func writelen(fd int, buf *byte, nbuf int) (n int, err error) { + r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procwrite)), 3, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf), 0, 0, 0) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +>>>>>>> 03615721 (Add internal server dtls config) // Event Ports type fileObjCookie struct { diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go index 1b4c97c32a6..96e6c6d57c1 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go @@ -733,10 +733,13 @@ const ( RISCV_HWPROBE_KEY_IMA_EXT_0 = 0x4 RISCV_HWPROBE_IMA_FD = 0x1 RISCV_HWPROBE_IMA_C = 0x2 +<<<<<<< HEAD RISCV_HWPROBE_IMA_V = 0x4 RISCV_HWPROBE_EXT_ZBA = 0x8 RISCV_HWPROBE_EXT_ZBB = 0x10 RISCV_HWPROBE_EXT_ZBS = 0x20 +======= +>>>>>>> 03615721 (Add internal server dtls config) RISCV_HWPROBE_KEY_CPUPERF_0 = 0x5 RISCV_HWPROBE_MISALIGNED_UNKNOWN = 0x0 RISCV_HWPROBE_MISALIGNED_EMULATED = 0x1 diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go index 1f1eade0ac8..6f8c8f2cb50 100644 --- a/vendor/golang.org/x/tools/go/packages/golist.go +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -9,6 +9,10 @@ import ( "context" "encoding/json" "fmt" +<<<<<<< HEAD +======= + "io/ioutil" +>>>>>>> 03615721 (Add internal server dtls config) "log" "os" "path" diff --git a/vendor/modules.txt b/vendor/modules.txt index 1fcc0b59569..c8bf264631a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -673,12 +673,14 @@ github.com/pelletier/go-toml/v2/internal/characters github.com/pelletier/go-toml/v2/internal/danger github.com/pelletier/go-toml/v2/internal/tracker github.com/pelletier/go-toml/v2/unstable -# github.com/pion/dtls/v2 v2.2.7 +# github.com/pion/dtls/v2 v2.2.8-0.20230905141523-2b584af66577 ## 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/net +github.com/pion/dtls/v2/internal/net/udp github.com/pion/dtls/v2/internal/util github.com/pion/dtls/v2/pkg/crypto/ccm github.com/pion/dtls/v2/pkg/crypto/ciphersuite @@ -688,6 +690,7 @@ github.com/pion/dtls/v2/pkg/crypto/hash github.com/pion/dtls/v2/pkg/crypto/prf github.com/pion/dtls/v2/pkg/crypto/signature github.com/pion/dtls/v2/pkg/crypto/signaturehash +github.com/pion/dtls/v2/pkg/net github.com/pion/dtls/v2/pkg/protocol github.com/pion/dtls/v2/pkg/protocol/alert github.com/pion/dtls/v2/pkg/protocol/extension @@ -696,18 +699,23 @@ github.com/pion/dtls/v2/pkg/protocol/recordlayer # github.com/pion/logging v0.2.2 ## explicit; go 1.12 github.com/pion/logging +<<<<<<< HEAD # github.com/pion/transport/v2 v2.2.4 +======= +# github.com/pion/transport/v3 v3.0.1 +>>>>>>> 03615721 (Add internal server dtls config) ## explicit; go 1.12 -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/udp +github.com/pion/transport/v3/deadline +github.com/pion/transport/v3/netctx +github.com/pion/transport/v3/packetio +github.com/pion/transport/v3/replaydetector +github.com/pion/transport/v3/udp # github.com/pkg/errors v0.9.1 ## explicit github.com/pkg/errors -# github.com/plgd-dev/go-coap/v2 v2.6.0 +# github.com/plgd-dev/go-coap/v3 v3.1.5 ## explicit; go 1.18 +<<<<<<< HEAD github.com/plgd-dev/go-coap/v2 github.com/plgd-dev/go-coap/v2/dtls github.com/plgd-dev/go-coap/v2/message @@ -733,6 +741,41 @@ github.com/plgd-dev/go-coap/v2/udp/message/pool ## explicit; go 1.13 github.com/plgd-dev/kit/v2/sync # github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 +======= +github.com/plgd-dev/go-coap/v3 +github.com/plgd-dev/go-coap/v3/dtls +github.com/plgd-dev/go-coap/v3/dtls/server +github.com/plgd-dev/go-coap/v3/message +github.com/plgd-dev/go-coap/v3/message/codes +github.com/plgd-dev/go-coap/v3/message/noresponse +github.com/plgd-dev/go-coap/v3/message/pool +github.com/plgd-dev/go-coap/v3/mux +github.com/plgd-dev/go-coap/v3/net +github.com/plgd-dev/go-coap/v3/net/blockwise +github.com/plgd-dev/go-coap/v3/net/client +github.com/plgd-dev/go-coap/v3/net/client/limitParallelRequests +github.com/plgd-dev/go-coap/v3/net/monitor/inactivity +github.com/plgd-dev/go-coap/v3/net/observation +github.com/plgd-dev/go-coap/v3/net/responsewriter +github.com/plgd-dev/go-coap/v3/options +github.com/plgd-dev/go-coap/v3/options/config +github.com/plgd-dev/go-coap/v3/pkg/cache +github.com/plgd-dev/go-coap/v3/pkg/connections +github.com/plgd-dev/go-coap/v3/pkg/errors +github.com/plgd-dev/go-coap/v3/pkg/fn +github.com/plgd-dev/go-coap/v3/pkg/rand +github.com/plgd-dev/go-coap/v3/pkg/runner/periodic +github.com/plgd-dev/go-coap/v3/pkg/sync +github.com/plgd-dev/go-coap/v3/tcp +github.com/plgd-dev/go-coap/v3/tcp/client +github.com/plgd-dev/go-coap/v3/tcp/coder +github.com/plgd-dev/go-coap/v3/tcp/server +github.com/plgd-dev/go-coap/v3/udp +github.com/plgd-dev/go-coap/v3/udp/client +github.com/plgd-dev/go-coap/v3/udp/coder +github.com/plgd-dev/go-coap/v3/udp/server +# github.com/pmezard/go-difflib v1.0.0 +>>>>>>> 03615721 (Add internal server dtls config) ## explicit github.com/pmezard/go-difflib/difflib # github.com/prometheus/client_golang v1.17.0 @@ -1011,7 +1054,11 @@ go.uber.org/multierr # golang.org/x/arch v0.5.0 ## explicit; go 1.17 golang.org/x/arch/x86/x86asm +<<<<<<< HEAD # golang.org/x/crypto v0.14.0 +======= +# golang.org/x/crypto v0.13.0 +>>>>>>> 03615721 (Add internal server dtls config) ## explicit; go 1.17 golang.org/x/crypto/acme golang.org/x/crypto/acme/autocert @@ -1032,7 +1079,11 @@ golang.org/x/crypto/pbkdf2 golang.org/x/crypto/salsa20/salsa golang.org/x/crypto/scrypt golang.org/x/crypto/sha3 +<<<<<<< HEAD # golang.org/x/exp v0.0.0-20231006140011-7918f672742d +======= +# golang.org/x/exp v0.0.0-20230905200255-921286631fa9 +>>>>>>> 03615721 (Add internal server dtls config) ## explicit; go 1.20 golang.org/x/exp/constraints golang.org/x/exp/maps @@ -1043,7 +1094,11 @@ golang.org/x/exp/slog/internal/buffer # golang.org/x/mod v0.13.0 ## explicit; go 1.18 golang.org/x/mod/semver +<<<<<<< HEAD # golang.org/x/net v0.17.0 +======= +# golang.org/x/net v0.15.0 +>>>>>>> 03615721 (Add internal server dtls config) ## explicit; go 1.17 golang.org/x/net/bpf golang.org/x/net/html @@ -1067,7 +1122,11 @@ golang.org/x/net/trace golang.org/x/sync/errgroup golang.org/x/sync/semaphore golang.org/x/sync/singleflight +<<<<<<< HEAD # golang.org/x/sys v0.13.0 +======= +# golang.org/x/sys v0.12.0 +>>>>>>> 03615721 (Add internal server dtls config) ## explicit; go 1.17 golang.org/x/sys/cpu golang.org/x/sys/execabs @@ -1105,7 +1164,11 @@ golang.org/x/text/width # golang.org/x/time v0.3.0 ## explicit golang.org/x/time/rate +<<<<<<< HEAD # golang.org/x/tools v0.14.0 +======= +# golang.org/x/tools v0.13.0 +>>>>>>> 03615721 (Add internal server dtls config) ## explicit; go 1.18 golang.org/x/tools/cmd/stringer golang.org/x/tools/go/gcexportdata