From 7811adca66a84bbfc032f95ac6a0863cd5e961ed Mon Sep 17 00:00:00 2001
From: whm <973418690@qq.com>
Date: Wed, 25 Mar 2026 15:00:14 +0800
Subject: [PATCH] =?UTF-8?q?=E7=9B=B4=E6=92=AD=EF=BC=9A=E5=90=8E=E5=8F=B0?=
=?UTF-8?q?=20JWT=20=E6=8E=A8=E6=B5=81=E3=80=81=E5=89=8D=E5=8F=B0=E7=94=BB?=
=?UTF-8?q?=E4=B8=AD=E7=94=BB=EF=BC=9BWebRTC=20=E6=9C=8D=E5=8A=A1=E4=B8=8E?=
=?UTF-8?q?=20Nginx=20WebSocket=20=E4=BB=A3=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Made-with: Cursor
---
admin/src/layouts/AdminLayout.vue | 3 +-
admin/src/router/index.js | 6 +
admin/src/utils/liveWebRTC.js | 106 +
admin/src/views/sites/HomepageEdit.vue | 27 +-
admin/src/views/sites/LiveBroadcast.vue | 99 +
admin/vite.config.js | 3 +-
nginx/nginx.conf | 12 +
nginx/yuheng.docker.conf.tpl | 15 +
nginx/yuheng.yuxindazhineng.com.conf | 13 +
server/go.mod | 24 +-
server/go.sum | 82 +
server/handlers/auth.go | 64 +-
server/handlers/homepage.go | 4 +-
server/main.go | 3 +
server/models/site.go | 4 +
server/pkg/weblive/config.go | 33 +
server/pkg/weblive/hub.go | 176 +
server/pkg/weblive/ws.go | 278 +
.../vendor/github.com/davecgh/go-spew/LICENSE | 15 +
.../github.com/davecgh/go-spew/spew/bypass.go | 145 +
.../davecgh/go-spew/spew/bypasssafe.go | 38 +
.../github.com/davecgh/go-spew/spew/common.go | 341 +
.../github.com/davecgh/go-spew/spew/config.go | 306 +
.../github.com/davecgh/go-spew/spew/doc.go | 211 +
.../github.com/davecgh/go-spew/spew/dump.go | 509 +
.../github.com/davecgh/go-spew/spew/format.go | 419 +
.../github.com/davecgh/go-spew/spew/spew.go | 148 +
.../github.com/google/uuid/CHANGELOG.md | 10 +
.../github.com/google/uuid/CONTRIBUTING.md | 26 +
.../github.com/google/uuid/CONTRIBUTORS | 9 +
server/vendor/github.com/google/uuid/LICENSE | 27 +
.../vendor/github.com/google/uuid/README.md | 21 +
server/vendor/github.com/google/uuid/dce.go | 80 +
server/vendor/github.com/google/uuid/doc.go | 12 +
server/vendor/github.com/google/uuid/hash.go | 53 +
.../vendor/github.com/google/uuid/marshal.go | 38 +
server/vendor/github.com/google/uuid/node.go | 90 +
.../vendor/github.com/google/uuid/node_js.go | 12 +
.../vendor/github.com/google/uuid/node_net.go | 33 +
server/vendor/github.com/google/uuid/null.go | 118 +
server/vendor/github.com/google/uuid/sql.go | 59 +
server/vendor/github.com/google/uuid/time.go | 123 +
server/vendor/github.com/google/uuid/util.go | 43 +
server/vendor/github.com/google/uuid/uuid.go | 296 +
.../vendor/github.com/google/uuid/version1.go | 44 +
.../vendor/github.com/google/uuid/version4.go | 76 +
.../github.com/gorilla/websocket/.gitignore | 25 +
.../github.com/gorilla/websocket/AUTHORS | 9 +
.../github.com/gorilla/websocket/LICENSE | 22 +
.../github.com/gorilla/websocket/README.md | 33 +
.../github.com/gorilla/websocket/client.go | 434 +
.../gorilla/websocket/compression.go | 148 +
.../github.com/gorilla/websocket/conn.go | 1238 +++
.../github.com/gorilla/websocket/doc.go | 227 +
.../github.com/gorilla/websocket/join.go | 42 +
.../github.com/gorilla/websocket/json.go | 60 +
.../github.com/gorilla/websocket/mask.go | 55 +
.../github.com/gorilla/websocket/mask_safe.go | 16 +
.../github.com/gorilla/websocket/prepared.go | 102 +
.../github.com/gorilla/websocket/proxy.go | 77 +
.../github.com/gorilla/websocket/server.go | 365 +
.../gorilla/websocket/tls_handshake.go | 21 +
.../gorilla/websocket/tls_handshake_116.go | 21 +
.../github.com/gorilla/websocket/util.go | 298 +
.../gorilla/websocket/x_net_proxy.go | 473 +
.../github.com/pion/datachannel/.gitignore | 28 +
.../github.com/pion/datachannel/.golangci.yml | 125 +
.../pion/datachannel/.goreleaser.yml | 5 +
.../github.com/pion/datachannel/LICENSE | 9 +
.../github.com/pion/datachannel/README.md | 34 +
.../github.com/pion/datachannel/codecov.yml | 22 +
.../pion/datachannel/datachannel.go | 404 +
.../github.com/pion/datachannel/errors.go | 27 +
.../github.com/pion/datachannel/message.go | 92 +
.../pion/datachannel/message_channel_ack.go | 29 +
.../pion/datachannel/message_channel_open.go | 147 +
.../github.com/pion/datachannel/renovate.json | 6 +
.../github.com/pion/dtls/v2/.editorconfig | 23 +
.../vendor/github.com/pion/dtls/v2/.gitignore | 28 +
.../github.com/pion/dtls/v2/.golangci.yml | 137 +
.../github.com/pion/dtls/v2/.goreleaser.yml | 5 +
.../github.com/pion/dtls/v2/AUTHORS.txt | 57 +
server/vendor/github.com/pion/dtls/v2/LICENSE | 9 +
.../vendor/github.com/pion/dtls/v2/README.md | 151 +
.../github.com/pion/dtls/v2/certificate.go | 157 +
.../github.com/pion/dtls/v2/cipher_suite.go | 276 +
.../pion/dtls/v2/cipher_suite_go114.go | 44 +
.../github.com/pion/dtls/v2/codecov.yml | 22 +
.../pion/dtls/v2/compression_method.go | 12 +
.../vendor/github.com/pion/dtls/v2/config.go | 253 +
server/vendor/github.com/pion/dtls/v2/conn.go | 1057 ++
.../vendor/github.com/pion/dtls/v2/crypto.go | 228 +
server/vendor/github.com/pion/dtls/v2/dtls.go | 5 +
.../vendor/github.com/pion/dtls/v2/errors.go | 157 +
.../github.com/pion/dtls/v2/errors_errno.go | 22 +
.../github.com/pion/dtls/v2/errors_noerrno.go | 18 +
.../vendor/github.com/pion/dtls/v2/flight.go | 104 +
.../github.com/pion/dtls/v2/flight0handler.go | 138 +
.../github.com/pion/dtls/v2/flight1handler.go | 141 +
.../github.com/pion/dtls/v2/flight2handler.go | 64 +
.../github.com/pion/dtls/v2/flight3handler.go | 291 +
.../pion/dtls/v2/flight4bhandler.go | 144 +
.../github.com/pion/dtls/v2/flight4handler.go | 402 +
.../pion/dtls/v2/flight5bhandler.go | 78 +
.../github.com/pion/dtls/v2/flight5handler.go | 357 +
.../github.com/pion/dtls/v2/flight6handler.go | 85 +
.../github.com/pion/dtls/v2/flighthandler.go | 68 +
.../pion/dtls/v2/fragment_buffer.go | 132 +
.../pion/dtls/v2/handshake_cache.go | 172 +
.../github.com/pion/dtls/v2/handshaker.go | 350 +
.../v2/internal/ciphersuite/aes_128_ccm.go | 33 +
.../v2/internal/ciphersuite/aes_256_ccm.go | 33 +
.../dtls/v2/internal/ciphersuite/aes_ccm.go | 113 +
.../v2/internal/ciphersuite/ciphersuite.go | 98 +
.../tls_ecdhe_ecdsa_with_aes_128_ccm.go | 14 +
.../tls_ecdhe_ecdsa_with_aes_128_ccm8.go | 14 +
...tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go | 108 +
.../tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go | 114 +
...tls_ecdhe_ecdsa_with_aes_256_gcm_sha384.go | 39 +
.../tls_ecdhe_psk_with_aes_128_cbc_sha256.go | 118 +
.../tls_ecdhe_rsa_with_aes_128_gcm_sha256.go | 25 +
.../tls_ecdhe_rsa_with_aes_256_cbc_sha.go | 25 +
.../tls_ecdhe_rsa_with_aes_256_gcm_sha384.go | 25 +
.../tls_psk_with_aes_128_cbc_sha256.go | 113 +
.../ciphersuite/tls_psk_with_aes_128_ccm.go | 14 +
.../ciphersuite/tls_psk_with_aes_128_ccm8.go | 14 +
.../tls_psk_with_aes_128_gcm_sha256.go | 35 +
.../ciphersuite/tls_psk_with_aes_256_ccm8.go | 14 +
.../ciphersuite/types/authentication_type.go | 14 +
.../types/key_exchange_algorithm.go | 20 +
.../pion/dtls/v2/internal/closer/closer.go | 48 +
.../pion/dtls/v2/internal/util/util.go | 42 +
.../github.com/pion/dtls/v2/listener.go | 83 +
.../vendor/github.com/pion/dtls/v2/packet.go | 12 +
.../pion/dtls/v2/pkg/crypto/ccm/ccm.go | 254 +
.../dtls/v2/pkg/crypto/ciphersuite/cbc.go | 177 +
.../dtls/v2/pkg/crypto/ciphersuite/ccm.go | 107 +
.../v2/pkg/crypto/ciphersuite/ciphersuite.go | 76 +
.../dtls/v2/pkg/crypto/ciphersuite/gcm.go | 103 +
.../clientcertificate/client_certificate.go | 25 +
.../dtls/v2/pkg/crypto/elliptic/elliptic.go | 115 +
.../v2/pkg/crypto/fingerprint/fingerprint.go | 53 +
.../dtls/v2/pkg/crypto/fingerprint/hash.go | 41 +
.../pion/dtls/v2/pkg/crypto/hash/hash.go | 129 +
.../pion/dtls/v2/pkg/crypto/prf/prf.go | 255 +
.../dtls/v2/pkg/crypto/signature/signature.go | 27 +
.../v2/pkg/crypto/signaturehash/errors.go | 12 +
.../pkg/crypto/signaturehash/signaturehash.go | 96 +
.../pion/dtls/v2/pkg/protocol/alert/alert.go | 166 +
.../dtls/v2/pkg/protocol/application_data.go | 29 +
.../v2/pkg/protocol/change_cipher_spec.go | 30 +
.../v2/pkg/protocol/compression_method.go | 51 +
.../pion/dtls/v2/pkg/protocol/content.go | 24 +
.../pion/dtls/v2/pkg/protocol/errors.go | 109 +
.../dtls/v2/pkg/protocol/extension/alpn.go | 80 +
.../dtls/v2/pkg/protocol/extension/errors.go | 20 +
.../v2/pkg/protocol/extension/extension.go | 102 +
.../protocol/extension/renegotiation_info.go | 46 +
.../v2/pkg/protocol/extension/server_name.go | 81 +
.../extension/srtp_protection_profile.go | 32 +
.../extension/supported_elliptic_curves.go | 65 +
.../extension/supported_point_formats.go | 65 +
.../supported_signature_algorithms.go | 73 +
.../protocol/extension/use_master_secret.go | 48 +
.../v2/pkg/protocol/extension/use_srtp.go | 62 +
.../v2/pkg/protocol/handshake/cipher_suite.go | 32 +
.../dtls/v2/pkg/protocol/handshake/errors.go | 28 +
.../v2/pkg/protocol/handshake/handshake.go | 150 +
.../dtls/v2/pkg/protocol/handshake/header.go | 53 +
.../protocol/handshake/message_certificate.go | 69 +
.../handshake/message_certificate_request.go | 144 +
.../handshake/message_certificate_verify.go | 64 +
.../handshake/message_client_hello.go | 141 +
.../handshake/message_client_key_exchange.go | 81 +
.../protocol/handshake/message_finished.go | 30 +
.../handshake/message_hello_verify_request.go | 65 +
.../handshake/message_server_hello.go | 122 +
.../handshake/message_server_hello_done.go | 24 +
.../handshake/message_server_key_exchange.go | 148 +
.../dtls/v2/pkg/protocol/handshake/random.go | 52 +
.../v2/pkg/protocol/recordlayer/errors.go | 21 +
.../v2/pkg/protocol/recordlayer/header.go | 64 +
.../pkg/protocol/recordlayer/recordlayer.go | 102 +
.../pion/dtls/v2/pkg/protocol/version.go | 24 +
.../github.com/pion/dtls/v2/renovate.json | 6 +
.../vendor/github.com/pion/dtls/v2/resume.go | 26 +
.../vendor/github.com/pion/dtls/v2/session.go | 24 +
.../pion/dtls/v2/srtp_protection_profile.go | 21 +
.../vendor/github.com/pion/dtls/v2/state.go | 228 +
server/vendor/github.com/pion/dtls/v2/util.go | 41 +
.../vendor/github.com/pion/ice/v2/.gitignore | 28 +
.../github.com/pion/ice/v2/.golangci.yml | 137 +
.../github.com/pion/ice/v2/.goreleaser.yml | 5 +
.../vendor/github.com/pion/ice/v2/AUTHORS.txt | 68 +
server/vendor/github.com/pion/ice/v2/LICENSE | 9 +
.../vendor/github.com/pion/ice/v2/README.md | 34 +
.../github.com/pion/ice/v2/active_tcp.go | 158 +
server/vendor/github.com/pion/ice/v2/addr.go | 71 +
server/vendor/github.com/pion/ice/v2/agent.go | 1303 +++
.../github.com/pion/ice/v2/agent_config.go | 305 +
.../github.com/pion/ice/v2/agent_handlers.go | 183 +
.../github.com/pion/ice/v2/agent_stats.go | 172 +
.../github.com/pion/ice/v2/candidate.go | 72 +
.../github.com/pion/ice/v2/candidate_base.go | 572 +
.../github.com/pion/ice/v2/candidate_host.go | 80 +
.../pion/ice/v2/candidate_peer_reflexive.go | 64 +
.../github.com/pion/ice/v2/candidate_relay.go | 115 +
.../pion/ice/v2/candidate_server_reflexive.go | 61 +
.../github.com/pion/ice/v2/candidatepair.go | 136 +
.../pion/ice/v2/candidatepair_state.go | 40 +
.../pion/ice/v2/candidaterelatedaddress.go | 33 +
.../github.com/pion/ice/v2/candidatetype.go | 65 +
.../vendor/github.com/pion/ice/v2/codecov.yml | 22 +
.../vendor/github.com/pion/ice/v2/context.go | 40 +
.../vendor/github.com/pion/ice/v2/errors.go | 140 +
.../pion/ice/v2/external_ip_mapper.go | 153 +
.../vendor/github.com/pion/ice/v2/gather.go | 720 ++
server/vendor/github.com/pion/ice/v2/ice.go | 90 +
.../github.com/pion/ice/v2/icecontrol.go | 90 +
.../pion/ice/v2/internal/atomic/atomic.go | 23 +
.../pion/ice/v2/internal/fakenet/mock_conn.go | 23 +
.../ice/v2/internal/fakenet/packet_conn.go | 29 +
.../pion/ice/v2/internal/stun/stun.go | 72 +
server/vendor/github.com/pion/ice/v2/mdns.go | 65 +
server/vendor/github.com/pion/ice/v2/net.go | 137 +
.../github.com/pion/ice/v2/networktype.go | 137 +
.../vendor/github.com/pion/ice/v2/priority.go | 36 +
server/vendor/github.com/pion/ice/v2/rand.go | 56 +
.../github.com/pion/ice/v2/renovate.json | 6 +
server/vendor/github.com/pion/ice/v2/role.go | 46 +
.../github.com/pion/ice/v2/selection.go | 312 +
server/vendor/github.com/pion/ice/v2/stats.go | 180 +
.../vendor/github.com/pion/ice/v2/tcp_mux.go | 435 +
.../github.com/pion/ice/v2/tcp_mux_multi.go | 81 +
.../github.com/pion/ice/v2/tcp_packet_conn.go | 324 +
.../vendor/github.com/pion/ice/v2/tcptype.go | 51 +
.../github.com/pion/ice/v2/test_utils.go | 79 +
.../github.com/pion/ice/v2/transport.go | 148 +
.../vendor/github.com/pion/ice/v2/udp_mux.go | 368 +
.../github.com/pion/ice/v2/udp_mux_multi.go | 228 +
.../pion/ice/v2/udp_mux_universal.go | 271 +
.../github.com/pion/ice/v2/udp_muxed_conn.go | 248 +
server/vendor/github.com/pion/ice/v2/url.go | 82 +
.../github.com/pion/ice/v2/usecandidate.go | 26 +
.../github.com/pion/interceptor/.gitignore | 28 +
.../github.com/pion/interceptor/.golangci.yml | 125 +
.../pion/interceptor/.goreleaser.yml | 5 +
.../github.com/pion/interceptor/LICENSE | 9 +
.../github.com/pion/interceptor/README.md | 84 +
.../github.com/pion/interceptor/attributes.go | 68 +
.../github.com/pion/interceptor/chain.go | 78 +
.../github.com/pion/interceptor/codecov.yml | 22 +
.../github.com/pion/interceptor/errors.go | 54 +
.../pion/interceptor/interceptor.go | 102 +
.../pion/interceptor/internal/ntp/ntp.go | 30 +
.../internal/sequencenumber/unwrapper.go | 45 +
.../github.com/pion/interceptor/noop.go | 43 +
.../pion/interceptor/pkg/nack/errors.go | 15 +
.../pkg/nack/generator_interceptor.go | 216 +
.../interceptor/pkg/nack/generator_option.go | 56 +
.../pion/interceptor/pkg/nack/nack.go | 17 +
.../pion/interceptor/pkg/nack/receive_log.go | 138 +
.../pkg/nack/responder_interceptor.go | 149 +
.../interceptor/pkg/nack/responder_option.go | 35 +
.../interceptor/pkg/nack/retainable_packet.go | 132 +
.../pion/interceptor/pkg/nack/send_buffer.go | 104 +
.../pkg/report/receiver_interceptor.go | 184 +
.../interceptor/pkg/report/receiver_option.go | 37 +
.../interceptor/pkg/report/receiver_stream.go | 169 +
.../pion/interceptor/pkg/report/report.go | 5 +
.../pkg/report/sender_interceptor.go | 151 +
.../interceptor/pkg/report/sender_option.go | 63 +
.../interceptor/pkg/report/sender_stream.go | 65 +
.../pion/interceptor/pkg/report/ticker.go | 20 +
.../interceptor/pkg/twcc/arrival_time_map.go | 192 +
.../pkg/twcc/header_extension_interceptor.go | 66 +
.../pkg/twcc/sender_interceptor.go | 207 +
.../pion/interceptor/pkg/twcc/twcc.go | 366 +
.../github.com/pion/interceptor/registry.go | 33 +
.../github.com/pion/interceptor/renovate.json | 6 +
.../github.com/pion/interceptor/streaminfo.go | 37 +
.../github.com/pion/logging/.golangci.yml | 13 +
.../github.com/pion/logging/.travis.yml | 19 +
server/vendor/github.com/pion/logging/LICENSE | 21 +
.../vendor/github.com/pion/logging/README.md | 41 +
.../vendor/github.com/pion/logging/logger.go | 228 +
.../vendor/github.com/pion/logging/scoped.go | 72 +
server/vendor/github.com/pion/mdns/.gitignore | 28 +
.../vendor/github.com/pion/mdns/.golangci.yml | 126 +
.../github.com/pion/mdns/.goreleaser.yml | 5 +
server/vendor/github.com/pion/mdns/LICENSE | 21 +
server/vendor/github.com/pion/mdns/README.md | 72 +
.../vendor/github.com/pion/mdns/codecov.yml | 22 +
server/vendor/github.com/pion/mdns/config.go | 41 +
server/vendor/github.com/pion/mdns/conn.go | 597 ++
server/vendor/github.com/pion/mdns/errors.go | 14 +
server/vendor/github.com/pion/mdns/mdns.go | 5 +
.../vendor/github.com/pion/mdns/renovate.json | 6 +
.../github.com/pion/randutil/.travis.yml | 142 +
.../vendor/github.com/pion/randutil/LICENSE | 21 +
.../vendor/github.com/pion/randutil/README.md | 14 +
.../github.com/pion/randutil/codecov.yml | 20 +
.../vendor/github.com/pion/randutil/crypto.go | 30 +
.../vendor/github.com/pion/randutil/math.go | 72 +
.../github.com/pion/randutil/renovate.json | 15 +
server/vendor/github.com/pion/rtcp/.gitignore | 28 +
.../vendor/github.com/pion/rtcp/.golangci.yml | 126 +
.../github.com/pion/rtcp/.goreleaser.yml | 5 +
server/vendor/github.com/pion/rtcp/LICENSE | 9 +
server/vendor/github.com/pion/rtcp/README.md | 37 +
.../vendor/github.com/pion/rtcp/codecov.yml | 22 +
.../github.com/pion/rtcp/compound_packet.go | 161 +
server/vendor/github.com/pion/rtcp/doc.go | 42 +
server/vendor/github.com/pion/rtcp/errors.go | 38 +
.../github.com/pion/rtcp/extended_report.go | 659 ++
.../pion/rtcp/full_intra_request.go | 116 +
server/vendor/github.com/pion/rtcp/goodbye.go | 159 +
server/vendor/github.com/pion/rtcp/header.go | 147 +
server/vendor/github.com/pion/rtcp/packet.go | 124 +
.../github.com/pion/rtcp/packet_buffer.go | 258 +
.../pion/rtcp/packet_stringifier.go | 106 +
.../pion/rtcp/picture_loss_indication.go | 93 +
.../rtcp/rapid_resynchronization_request.go | 94 +
.../vendor/github.com/pion/rtcp/raw_packet.go | 50 +
.../receiver_estimated_maximum_bitrate.go | 285 +
.../github.com/pion/rtcp/receiver_report.go | 195 +
.../github.com/pion/rtcp/reception_report.go | 133 +
.../vendor/github.com/pion/rtcp/renovate.json | 6 +
server/vendor/github.com/pion/rtcp/rfc8888.go | 340 +
.../github.com/pion/rtcp/sender_report.go | 262 +
.../pion/rtcp/slice_loss_indication.go | 117 +
.../pion/rtcp/source_description.go | 368 +
.../pion/rtcp/transport_layer_cc.go | 566 +
.../pion/rtcp/transport_layer_nack.go | 181 +
server/vendor/github.com/pion/rtcp/util.go | 41 +
server/vendor/github.com/pion/rtp/.gitignore | 28 +
.../vendor/github.com/pion/rtp/.golangci.yml | 125 +
.../github.com/pion/rtp/.goreleaser.yml | 5 +
server/vendor/github.com/pion/rtp/LICENSE | 9 +
server/vendor/github.com/pion/rtp/README.md | 35 +
.../pion/rtp/abscapturetimeextension.go | 106 +
.../pion/rtp/abssendtimeextension.go | 81 +
.../pion/rtp/audiolevelextension.go | 63 +
server/vendor/github.com/pion/rtp/codecov.yml | 22 +
.../pion/rtp/codecs/av1/obu/leb128.go | 85 +
.../github.com/pion/rtp/codecs/av1_packet.go | 204 +
.../github.com/pion/rtp/codecs/codecs.go | 5 +
.../github.com/pion/rtp/codecs/common.go | 39 +
.../github.com/pion/rtp/codecs/error.go | 17 +
.../github.com/pion/rtp/codecs/g711_packet.go | 25 +
.../github.com/pion/rtp/codecs/g722_packet.go | 25 +
.../github.com/pion/rtp/codecs/h264_packet.go | 297 +
.../github.com/pion/rtp/codecs/h265_packet.go | 828 ++
.../github.com/pion/rtp/codecs/opus_packet.go | 49 +
.../github.com/pion/rtp/codecs/vp8_packet.go | 234 +
.../github.com/pion/rtp/codecs/vp9/bits.go | 65 +
.../github.com/pion/rtp/codecs/vp9/header.go | 221 +
.../github.com/pion/rtp/codecs/vp9_packet.go | 511 +
.../github.com/pion/rtp/depacketizer.go | 20 +
server/vendor/github.com/pion/rtp/error.go | 26 +
.../github.com/pion/rtp/header_extension.go | 353 +
server/vendor/github.com/pion/rtp/packet.go | 540 +
.../vendor/github.com/pion/rtp/packetizer.go | 138 +
.../pion/rtp/partitionheadchecker.go | 9 +
.../github.com/pion/rtp/payload_types.go | 68 +
.../pion/rtp/playoutdelayextension.go | 50 +
server/vendor/github.com/pion/rtp/rand.go | 11 +
.../vendor/github.com/pion/rtp/renovate.json | 6 +
server/vendor/github.com/pion/rtp/rtp.go | 5 +
.../vendor/github.com/pion/rtp/sequencer.go | 60 +
.../pion/rtp/transportccextension.go | 42 +
.../github.com/pion/rtp/vlaextension.go | 360 +
server/vendor/github.com/pion/sctp/.gitignore | 28 +
.../vendor/github.com/pion/sctp/.golangci.yml | 125 +
.../github.com/pion/sctp/.goreleaser.yml | 5 +
server/vendor/github.com/pion/sctp/LICENSE | 9 +
server/vendor/github.com/pion/sctp/README.md | 34 +
.../vendor/github.com/pion/sctp/ack_timer.go | 104 +
.../github.com/pion/sctp/association.go | 2747 +++++
.../github.com/pion/sctp/association_stats.go | 94 +
server/vendor/github.com/pion/sctp/chunk.go | 12 +
.../github.com/pion/sctp/chunk_abort.go | 96 +
.../github.com/pion/sctp/chunk_cookie_ack.go | 53 +
.../github.com/pion/sctp/chunk_cookie_echo.go | 54 +
.../github.com/pion/sctp/chunk_error.go | 103 +
.../github.com/pion/sctp/chunk_forward_tsn.go | 151 +
.../github.com/pion/sctp/chunk_heartbeat.go | 86 +
.../pion/sctp/chunk_heartbeat_ack.go | 96 +
.../vendor/github.com/pion/sctp/chunk_init.go | 142 +
.../github.com/pion/sctp/chunk_init_ack.go | 140 +
.../github.com/pion/sctp/chunk_init_common.go | 167 +
.../pion/sctp/chunk_payload_data.go | 212 +
.../github.com/pion/sctp/chunk_reconfig.go | 108 +
.../pion/sctp/chunk_selective_ack.go | 151 +
.../github.com/pion/sctp/chunk_shutdown.go | 72 +
.../pion/sctp/chunk_shutdown_ack.go | 53 +
.../pion/sctp/chunk_shutdown_complete.go | 53 +
.../github.com/pion/sctp/chunkheader.go | 100 +
.../vendor/github.com/pion/sctp/chunktype.go | 70 +
.../vendor/github.com/pion/sctp/codecov.yml | 22 +
.../github.com/pion/sctp/control_queue.go | 32 +
.../github.com/pion/sctp/error_cause.go | 101 +
.../pion/sctp/error_cause_header.go | 57 +
...error_cause_invalid_mandatory_parameter.go | 22 +
.../sctp/error_cause_protocol_violation.go | 57 +
.../error_cause_unrecognized_chunk_type.go | 31 +
.../sctp/error_cause_user_initiated_abort.go | 49 +
server/vendor/github.com/pion/sctp/packet.go | 227 +
server/vendor/github.com/pion/sctp/param.go | 46 +
.../github.com/pion/sctp/param_chunk_list.go | 31 +
.../github.com/pion/sctp/param_ecn_capable.go | 22 +
.../pion/sctp/param_forward_tsn_supported.go | 31 +
.../pion/sctp/param_heartbeat_info.go | 24 +
.../pion/sctp/param_outgoing_reset_request.go | 94 +
.../github.com/pion/sctp/param_random.go | 24 +
.../pion/sctp/param_reconfig_response.go | 98 +
.../sctp/param_requested_hmac_algorithm.go | 84 +
.../pion/sctp/param_state_cookie.go | 49 +
.../pion/sctp/param_supported_extensions.go | 32 +
.../pion/sctp/param_zero_checksum.go | 56 +
.../github.com/pion/sctp/paramheader.go | 101 +
.../vendor/github.com/pion/sctp/paramtype.go | 119 +
.../github.com/pion/sctp/payload_queue.go | 73 +
.../github.com/pion/sctp/pending_queue.go | 142 +
server/vendor/github.com/pion/sctp/queue.go | 70 +
.../github.com/pion/sctp/reassembly_queue.go | 366 +
.../pion/sctp/receive_payload_queue.go | 186 +
.../vendor/github.com/pion/sctp/renovate.json | 6 +
.../vendor/github.com/pion/sctp/rtx_timer.go | 250 +
server/vendor/github.com/pion/sctp/sctp.go | 5 +
server/vendor/github.com/pion/sctp/stream.go | 471 +
server/vendor/github.com/pion/sctp/util.go | 61 +
.../vendor/github.com/pion/sdp/v3/.gitignore | 28 +
.../github.com/pion/sdp/v3/.golangci.yml | 126 +
.../github.com/pion/sdp/v3/.goreleaser.yml | 5 +
server/vendor/github.com/pion/sdp/v3/LICENSE | 9 +
.../vendor/github.com/pion/sdp/v3/README.md | 35 +
.../github.com/pion/sdp/v3/base_lexer.go | 216 +
.../vendor/github.com/pion/sdp/v3/codecov.yml | 22 +
.../pion/sdp/v3/common_description.go | 188 +
.../github.com/pion/sdp/v3/direction.go | 62 +
.../vendor/github.com/pion/sdp/v3/extmap.go | 111 +
server/vendor/github.com/pion/sdp/v3/jsep.go | 210 +
.../vendor/github.com/pion/sdp/v3/marshal.go | 240 +
.../pion/sdp/v3/media_description.go | 128 +
.../github.com/pion/sdp/v3/renovate.json | 6 +
server/vendor/github.com/pion/sdp/v3/sdp.go | 5 +
.../pion/sdp/v3/session_description.go | 201 +
.../pion/sdp/v3/time_description.go | 75 +
.../github.com/pion/sdp/v3/unmarshal.go | 956 ++
.../github.com/pion/sdp/v3/unmarshal_cache.go | 44 +
server/vendor/github.com/pion/sdp/v3/util.go | 360 +
.../vendor/github.com/pion/srtp/v2/.gitignore | 28 +
.../github.com/pion/srtp/v2/.golangci.yml | 137 +
.../github.com/pion/srtp/v2/.goreleaser.yml | 5 +
.../github.com/pion/srtp/v2/AUTHORS.txt | 37 +
server/vendor/github.com/pion/srtp/v2/LICENSE | 9 +
.../vendor/github.com/pion/srtp/v2/README.md | 35 +
.../github.com/pion/srtp/v2/codecov.yml | 22 +
.../vendor/github.com/pion/srtp/v2/context.go | 293 +
.../vendor/github.com/pion/srtp/v2/crypto.go | 45 +
.../vendor/github.com/pion/srtp/v2/errors.go | 53 +
.../github.com/pion/srtp/v2/key_derivation.go | 69 +
.../vendor/github.com/pion/srtp/v2/keying.go | 57 +
.../vendor/github.com/pion/srtp/v2/option.go | 120 +
.../pion/srtp/v2/protection_profile.go | 128 +
.../github.com/pion/srtp/v2/renovate.json | 6 +
.../vendor/github.com/pion/srtp/v2/session.go | 161 +
.../github.com/pion/srtp/v2/session_srtcp.go | 190 +
.../github.com/pion/srtp/v2/session_srtp.go | 200 +
.../vendor/github.com/pion/srtp/v2/srtcp.go | 109 +
server/vendor/github.com/pion/srtp/v2/srtp.go | 109 +
.../github.com/pion/srtp/v2/srtp_cipher.go | 51 +
.../pion/srtp/v2/srtp_cipher_aead_aes_gcm.go | 285 +
.../srtp/v2/srtp_cipher_aes_cm_hmac_sha1.go | 331 +
.../vendor/github.com/pion/srtp/v2/stream.go | 11 +
.../github.com/pion/srtp/v2/stream_srtcp.go | 160 +
.../github.com/pion/srtp/v2/stream_srtp.go | 157 +
server/vendor/github.com/pion/srtp/v2/util.go | 36 +
server/vendor/github.com/pion/stun/.gitignore | 28 +
.../vendor/github.com/pion/stun/.golangci.yml | 137 +
.../github.com/pion/stun/.goreleaser.yml | 5 +
.../vendor/github.com/pion/stun/AUTHORS.txt | 40 +
server/vendor/github.com/pion/stun/LICENSE | 9 +
server/vendor/github.com/pion/stun/Makefile | 39 +
server/vendor/github.com/pion/stun/README.md | 186 +
server/vendor/github.com/pion/stun/addr.go | 163 +
server/vendor/github.com/pion/stun/agent.go | 233 +
.../vendor/github.com/pion/stun/attributes.go | 254 +
.../github.com/pion/stun/attributes_debug.go | 37 +
server/vendor/github.com/pion/stun/checks.go | 53 +
.../github.com/pion/stun/checks_debug.go | 65 +
server/vendor/github.com/pion/stun/client.go | 722 ++
.../vendor/github.com/pion/stun/codecov.yml | 22 +
.../vendor/github.com/pion/stun/errorcode.go | 162 +
server/vendor/github.com/pion/stun/errors.go | 66 +
.../github.com/pion/stun/fingerprint.go | 70 +
.../github.com/pion/stun/fingerprint_debug.go | 22 +
server/vendor/github.com/pion/stun/helpers.go | 109 +
.../vendor/github.com/pion/stun/integrity.go | 126 +
.../github.com/pion/stun/integrity_debug.go | 22 +
.../pion/stun/internal/hmac/hmac.go | 156 +
.../pion/stun/internal/hmac/pool.go | 92 +
.../pion/stun/internal/hmac/vendor.sh | 7 +
server/vendor/github.com/pion/stun/message.go | 622 ++
.../vendor/github.com/pion/stun/renovate.json | 6 +
server/vendor/github.com/pion/stun/stun.go | 54 +
.../vendor/github.com/pion/stun/textattrs.go | 132 +
server/vendor/github.com/pion/stun/uattrs.go | 66 +
server/vendor/github.com/pion/stun/uri.go | 257 +
server/vendor/github.com/pion/stun/xoraddr.go | 150 +
.../github.com/pion/transport/v2/.gitignore | 28 +
.../pion/transport/v2/.golangci.yml | 137 +
.../pion/transport/v2/.goreleaser.yml | 5 +
.../github.com/pion/transport/v2/AUTHORS.txt | 29 +
.../github.com/pion/transport/v2/LICENSE | 9 +
.../github.com/pion/transport/v2/README.md | 34 +
.../github.com/pion/transport/v2/codecov.yml | 22 +
.../pion/transport/v2/connctx/connctx.go | 177 +
.../pion/transport/v2/connctx/pipe.go | 14 +
.../pion/transport/v2/deadline/deadline.go | 124 +
.../pion/transport/v2/deadline/timer.go | 13 +
.../transport/v2/deadline/timer_generic.go | 15 +
.../pion/transport/v2/deadline/timer_js.go | 67 +
.../github.com/pion/transport/v2/net.go | 418 +
.../pion/transport/v2/packetio/buffer.go | 335 +
.../pion/transport/v2/packetio/errors.go | 30 +
.../pion/transport/v2/packetio/hardlimit.go | 9 +
.../transport/v2/packetio/no_hardlimit.go | 9 +
.../pion/transport/v2/renovate.json | 6 +
.../transport/v2/replaydetector/fixedbig.go | 81 +
.../v2/replaydetector/replaydetector.go | 119 +
.../pion/transport/v2/stdnet/net.go | 168 +
.../pion/transport/v2/udp/batchconn.go | 170 +
.../github.com/pion/transport/v2/udp/conn.go | 406 +
.../pion/transport/v2/utils/xor/xor_amd64.go | 29 +
.../pion/transport/v2/utils/xor/xor_amd64.s | 56 +
.../pion/transport/v2/utils/xor/xor_arm.go | 60 +
.../pion/transport/v2/utils/xor/xor_arm.s | 116 +
.../pion/transport/v2/utils/xor/xor_arm64.go | 31 +
.../pion/transport/v2/utils/xor/xor_arm64.s | 69 +
.../transport/v2/utils/xor/xor_generic.go | 78 +
.../pion/transport/v2/utils/xor/xor_ppc64x.go | 29 +
.../pion/transport/v2/utils/xor/xor_ppc64x.s | 87 +
.../pion/transport/v2/vnet/.gitignore | 4 +
.../pion/transport/v2/vnet/README.md | 231 +
.../pion/transport/v2/vnet/chunk.go | 286 +
.../pion/transport/v2/vnet/chunk_queue.go | 68 +
.../github.com/pion/transport/v2/vnet/conn.go | 298 +
.../pion/transport/v2/vnet/conn_map.go | 139 +
.../pion/transport/v2/vnet/delay_filter.go | 78 +
.../pion/transport/v2/vnet/errors.go | 22 +
.../pion/transport/v2/vnet/loss_filter.go | 36 +
.../github.com/pion/transport/v2/vnet/nat.go | 342 +
.../github.com/pion/transport/v2/vnet/net.go | 618 ++
.../pion/transport/v2/vnet/resolver.go | 92 +
.../pion/transport/v2/vnet/router.go | 621 ++
.../github.com/pion/transport/v2/vnet/tbf.go | 167 +
.../pion/transport/v2/vnet/udpproxy.go | 223 +
.../pion/transport/v2/vnet/udpproxy_direct.go | 48 +
.../github.com/pion/transport/v2/vnet/vnet.go | 5 +
.../vendor/github.com/pion/turn/v2/.gitignore | 28 +
.../github.com/pion/turn/v2/.golangci.yml | 137 +
.../github.com/pion/turn/v2/.goreleaser.yml | 5 +
.../github.com/pion/turn/v2/AUTHORS.txt | 48 +
server/vendor/github.com/pion/turn/v2/LICENSE | 9 +
.../vendor/github.com/pion/turn/v2/README.md | 93 +
.../vendor/github.com/pion/turn/v2/client.go | 685 ++
.../github.com/pion/turn/v2/codecov.yml | 22 +
.../vendor/github.com/pion/turn/v2/errors.go | 31 +
.../turn/v2/internal/allocation/allocation.go | 286 +
.../internal/allocation/allocation_manager.go | 218 +
.../v2/internal/allocation/channel_bind.go | 46 +
.../turn/v2/internal/allocation/errors.go | 22 +
.../turn/v2/internal/allocation/five_tuple.go | 63 +
.../turn/v2/internal/allocation/permission.go | 43 +
.../turn/v2/internal/client/allocation.go | 189 +
.../pion/turn/v2/internal/client/binding.go | 155 +
.../pion/turn/v2/internal/client/client.go | 18 +
.../pion/turn/v2/internal/client/errors.go | 43 +
.../turn/v2/internal/client/periodic_timer.go | 85 +
.../turn/v2/internal/client/permission.go | 77 +
.../pion/turn/v2/internal/client/tcp_alloc.go | 371 +
.../pion/turn/v2/internal/client/tcp_conn.go | 49 +
.../turn/v2/internal/client/transaction.go | 188 +
.../pion/turn/v2/internal/client/trylock.go | 27 +
.../pion/turn/v2/internal/client/udp_conn.go | 455 +
.../pion/turn/v2/internal/ipnet/util.go | 55 +
.../pion/turn/v2/internal/proto/addr.go | 68 +
.../pion/turn/v2/internal/proto/chandata.go | 143 +
.../pion/turn/v2/internal/proto/chann.go | 70 +
.../turn/v2/internal/proto/connection_id.go | 42 +
.../pion/turn/v2/internal/proto/data.go | 33 +
.../pion/turn/v2/internal/proto/dontfrag.go | 45 +
.../pion/turn/v2/internal/proto/evenport.go | 58 +
.../pion/turn/v2/internal/proto/lifetime.go | 55 +
.../pion/turn/v2/internal/proto/peeraddr.go | 45 +
.../pion/turn/v2/internal/proto/proto.go | 31 +
.../turn/v2/internal/proto/relayedaddr.go | 43 +
.../pion/turn/v2/internal/proto/reqfamily.go | 64 +
.../pion/turn/v2/internal/proto/reqtrans.go | 72 +
.../pion/turn/v2/internal/proto/rsrvtoken.go | 42 +
.../pion/turn/v2/internal/server/errors.go | 29 +
.../pion/turn/v2/internal/server/nonce.go | 71 +
.../pion/turn/v2/internal/server/server.go | 111 +
.../pion/turn/v2/internal/server/stun.go | 25 +
.../pion/turn/v2/internal/server/turn.go | 376 +
.../pion/turn/v2/internal/server/util.go | 117 +
.../vendor/github.com/pion/turn/v2/lt_cred.go | 59 +
.../turn/v2/relay_address_generator_none.go | 54 +
.../turn/v2/relay_address_generator_range.go | 108 +
.../turn/v2/relay_address_generator_static.go | 68 +
.../github.com/pion/turn/v2/renovate.json | 6 +
.../vendor/github.com/pion/turn/v2/server.go | 210 +
.../github.com/pion/turn/v2/server_config.go | 145 +
.../github.com/pion/turn/v2/stun_conn.go | 127 +
.../github.com/pion/webrtc/v3/.codacy.yaml | 6 +
.../github.com/pion/webrtc/v3/.eslintrc.json | 3 +
.../github.com/pion/webrtc/v3/.gitignore | 28 +
.../github.com/pion/webrtc/v3/.golangci.yml | 137 +
.../github.com/pion/webrtc/v3/.goreleaser.yml | 5 +
.../github.com/pion/webrtc/v3/AUTHORS.txt | 229 +
.../github.com/pion/webrtc/v3/DESIGN.md | 43 +
.../vendor/github.com/pion/webrtc/v3/LICENSE | 9 +
.../github.com/pion/webrtc/v3/README.md | 129 +
.../vendor/github.com/pion/webrtc/v3/api.go | 77 +
.../github.com/pion/webrtc/v3/api_js.go | 35 +
.../github.com/pion/webrtc/v3/atomicbool.go | 31 +
.../github.com/pion/webrtc/v3/bundlepolicy.go | 81 +
.../github.com/pion/webrtc/v3/certificate.go | 234 +
.../github.com/pion/webrtc/v3/codecov.yml | 22 +
.../pion/webrtc/v3/configuration.go | 55 +
.../pion/webrtc/v3/configuration_common.go | 27 +
.../pion/webrtc/v3/configuration_js.go | 39 +
.../github.com/pion/webrtc/v3/constants.go | 52 +
.../github.com/pion/webrtc/v3/datachannel.go | 695 ++
.../pion/webrtc/v3/datachannel_js.go | 323 +
.../pion/webrtc/v3/datachannel_js_detach.go | 75 +
.../pion/webrtc/v3/datachannelinit.go | 36 +
.../pion/webrtc/v3/datachannelmessage.go | 13 +
.../pion/webrtc/v3/datachannelparameters.go | 15 +
.../pion/webrtc/v3/datachannelstate.go | 75 +
.../pion/webrtc/v3/dtlsfingerprint.go | 17 +
.../pion/webrtc/v3/dtlsparameters.go | 10 +
.../github.com/pion/webrtc/v3/dtlsrole.go | 95 +
.../pion/webrtc/v3/dtlstransport.go | 522 +
.../pion/webrtc/v3/dtlstransport_js.go | 31 +
.../pion/webrtc/v3/dtlstransportstate.go | 85 +
.../github.com/pion/webrtc/v3/errors.go | 250 +
.../webrtc/v3/gathering_complete_promise.go | 27 +
.../github.com/pion/webrtc/v3/ice_go.go | 14 +
.../github.com/pion/webrtc/v3/icecandidate.go | 172 +
.../pion/webrtc/v3/icecandidateinit.go | 12 +
.../pion/webrtc/v3/icecandidatepair.go | 32 +
.../pion/webrtc/v3/icecandidatetype.go | 109 +
.../github.com/pion/webrtc/v3/icecomponent.go | 50 +
.../pion/webrtc/v3/iceconnectionstate.go | 97 +
.../pion/webrtc/v3/icecredentialtype.go | 72 +
.../github.com/pion/webrtc/v3/icegatherer.go | 395 +
.../pion/webrtc/v3/icegathererstate.go | 51 +
.../pion/webrtc/v3/icegatheringstate.go | 56 +
.../pion/webrtc/v3/icegatheroptions.go | 10 +
.../github.com/pion/webrtc/v3/icemux.go | 30 +
.../pion/webrtc/v3/iceparameters.go | 12 +
.../github.com/pion/webrtc/v3/iceprotocol.go | 50 +
.../github.com/pion/webrtc/v3/icerole.go | 59 +
.../github.com/pion/webrtc/v3/iceserver.go | 182 +
.../github.com/pion/webrtc/v3/iceserver_js.go | 46 +
.../github.com/pion/webrtc/v3/icetransport.go | 421 +
.../pion/webrtc/v3/icetransport_js.go | 30 +
.../pion/webrtc/v3/icetransportpolicy.go | 68 +
.../pion/webrtc/v3/icetransportstate.go | 110 +
.../github.com/pion/webrtc/v3/interceptor.go | 167 +
.../pion/webrtc/v3/internal/fmtp/av1.go | 41 +
.../pion/webrtc/v3/internal/fmtp/fmtp.go | 112 +
.../pion/webrtc/v3/internal/fmtp/h264.go | 84 +
.../pion/webrtc/v3/internal/fmtp/vp9.go | 41 +
.../pion/webrtc/v3/internal/mux/endpoint.go | 78 +
.../pion/webrtc/v3/internal/mux/mux.go | 163 +
.../pion/webrtc/v3/internal/mux/muxfunc.go | 65 +
.../pion/webrtc/v3/internal/util/util.go | 75 +
.../github.com/pion/webrtc/v3/js_utils.go | 172 +
.../github.com/pion/webrtc/v3/mediaengine.go | 707 ++
.../github.com/pion/webrtc/v3/networktype.go | 107 +
.../pion/webrtc/v3/oauthcredential.go | 18 +
.../pion/webrtc/v3/offeransweroptions.go | 29 +
.../github.com/pion/webrtc/v3/operations.go | 154 +
.../github.com/pion/webrtc/v3/package.json | 11 +
.../pion/webrtc/v3/peerconnection.go | 2641 +++++
.../pion/webrtc/v3/peerconnection_js.go | 774 ++
.../pion/webrtc/v3/peerconnectionstate.go | 86 +
.../pion/webrtc/v3/pkg/media/media.go | 31 +
.../pion/webrtc/v3/pkg/rtcerr/errors.go | 163 +
.../github.com/pion/webrtc/v3/renovate.json | 6 +
.../github.com/pion/webrtc/v3/rtcpfeedback.go | 34 +
.../pion/webrtc/v3/rtcpmuxpolicy.go | 69 +
.../pion/webrtc/v3/rtpcapabilities.go | 12 +
.../github.com/pion/webrtc/v3/rtpcodec.go | 136 +
.../pion/webrtc/v3/rtpcodingparameters.go | 20 +
.../pion/webrtc/v3/rtpdecodingparameters.go | 11 +
.../pion/webrtc/v3/rtpencodingparameters.go | 11 +
.../pion/webrtc/v3/rtpreceiveparameters.go | 9 +
.../github.com/pion/webrtc/v3/rtpreceiver.go | 538 +
.../pion/webrtc/v3/rtpreceiver_go.go | 36 +
.../pion/webrtc/v3/rtpreceiver_js.go | 15 +
.../github.com/pion/webrtc/v3/rtpsender.go | 460 +
.../github.com/pion/webrtc/v3/rtpsender_js.go | 15 +
.../pion/webrtc/v3/rtpsendparameters.go | 10 +
.../pion/webrtc/v3/rtptransceiver.go | 298 +
.../pion/webrtc/v3/rtptransceiver_js.go | 42 +
.../pion/webrtc/v3/rtptransceiverdirection.go | 88 +
.../pion/webrtc/v3/rtptransceiverinit.go | 15 +
.../pion/webrtc/v3/sctpcapabilities.go | 9 +
.../pion/webrtc/v3/sctptransport.go | 451 +
.../pion/webrtc/v3/sctptransport_js.go | 27 +
.../pion/webrtc/v3/sctptransportstate.go | 57 +
.../vendor/github.com/pion/webrtc/v3/sdp.go | 925 ++
.../github.com/pion/webrtc/v3/sdpsemantics.go | 77 +
.../github.com/pion/webrtc/v3/sdptype.go | 103 +
.../pion/webrtc/v3/sessiondescription.go | 24 +
.../pion/webrtc/v3/settingengine.go | 455 +
.../pion/webrtc/v3/settingengine_js.go | 23 +
.../pion/webrtc/v3/signalingstate.go | 191 +
.../pion/webrtc/v3/srtp_writer_future.go | 141 +
.../vendor/github.com/pion/webrtc/v3/stats.go | 2077 ++++
.../github.com/pion/webrtc/v3/stats_go.go | 97 +
.../github.com/pion/webrtc/v3/track_local.go | 114 +
.../pion/webrtc/v3/track_local_static.go | 331 +
.../github.com/pion/webrtc/v3/track_remote.go | 232 +
.../github.com/pion/webrtc/v3/webrtc.go | 20 +
.../github.com/pion/webrtc/v3/yarn.lock | 797 ++
.../github.com/pmezard/go-difflib/LICENSE | 27 +
.../pmezard/go-difflib/difflib/difflib.go | 772 ++
.../github.com/stretchr/testify/LICENSE | 21 +
.../testify/assert/assertion_compare.go | 480 +
.../testify/assert/assertion_format.go | 815 ++
.../testify/assert/assertion_format.go.tmpl | 5 +
.../testify/assert/assertion_forward.go | 1621 +++
.../testify/assert/assertion_forward.go.tmpl | 5 +
.../testify/assert/assertion_order.go | 81 +
.../stretchr/testify/assert/assertions.go | 2105 ++++
.../github.com/stretchr/testify/assert/doc.go | 46 +
.../stretchr/testify/assert/errors.go | 10 +
.../testify/assert/forward_assertions.go | 16 +
.../testify/assert/http_assertions.go | 165 +
.../stretchr/testify/require/doc.go | 29 +
.../testify/require/forward_requirements.go | 16 +
.../stretchr/testify/require/require.go | 2060 ++++
.../stretchr/testify/require/require.go.tmpl | 6 +
.../testify/require/require_forward.go | 1622 +++
.../testify/require/require_forward.go.tmpl | 5 +
.../stretchr/testify/require/requirements.go | 29 +
.../vendor/github.com/wlynxg/anet/.gitignore | 1 +
server/vendor/github.com/wlynxg/anet/LICENSE | 28 +
.../vendor/github.com/wlynxg/anet/README.md | 119 +
.../github.com/wlynxg/anet/README_zh.md | 22 +
.../github.com/wlynxg/anet/interface.go | 30 +
.../wlynxg/anet/interface_android.go | 419 +
.../github.com/wlynxg/anet/netlink_android.go | 179 +
.../golang.org/x/crypto/cryptobyte/asn1.go | 825 ++
.../x/crypto/cryptobyte/asn1/asn1.go | 46 +
.../golang.org/x/crypto/cryptobyte/builder.go | 350 +
.../golang.org/x/crypto/cryptobyte/string.go | 183 +
.../x/crypto/curve25519/curve25519.go | 90 +
server/vendor/golang.org/x/net/bpf/asm.go | 41 +
.../vendor/golang.org/x/net/bpf/constants.go | 222 +
server/vendor/golang.org/x/net/bpf/doc.go | 80 +
.../golang.org/x/net/bpf/instructions.go | 726 ++
server/vendor/golang.org/x/net/bpf/setter.go | 10 +
server/vendor/golang.org/x/net/bpf/vm.go | 150 +
.../golang.org/x/net/bpf/vm_instructions.go | 182 +
.../x/net/dns/dnsmessage/message.go | 2712 +++++
.../golang.org/x/net/http2/transport.go | 9 +
.../golang.org/x/net/internal/iana/const.go | 223 +
.../x/net/internal/socket/cmsghdr.go | 11 +
.../x/net/internal/socket/cmsghdr_bsd.go | 13 +
.../internal/socket/cmsghdr_linux_32bit.go | 13 +
.../internal/socket/cmsghdr_linux_64bit.go | 13 +
.../internal/socket/cmsghdr_solaris_64bit.go | 13 +
.../x/net/internal/socket/cmsghdr_stub.go | 27 +
.../x/net/internal/socket/cmsghdr_unix.go | 21 +
.../net/internal/socket/cmsghdr_zos_s390x.go | 11 +
.../net/internal/socket/complete_dontwait.go | 25 +
.../internal/socket/complete_nodontwait.go | 21 +
.../golang.org/x/net/internal/socket/empty.s | 7 +
.../x/net/internal/socket/error_unix.go | 31 +
.../x/net/internal/socket/error_windows.go | 26 +
.../x/net/internal/socket/iovec_32bit.go | 18 +
.../x/net/internal/socket/iovec_64bit.go | 18 +
.../internal/socket/iovec_solaris_64bit.go | 18 +
.../x/net/internal/socket/iovec_stub.go | 11 +
.../x/net/internal/socket/mmsghdr_stub.go | 21 +
.../x/net/internal/socket/mmsghdr_unix.go | 195 +
.../x/net/internal/socket/msghdr_bsd.go | 39 +
.../x/net/internal/socket/msghdr_bsdvar.go | 16 +
.../x/net/internal/socket/msghdr_linux.go | 36 +
.../net/internal/socket/msghdr_linux_32bit.go | 23 +
.../net/internal/socket/msghdr_linux_64bit.go | 23 +
.../x/net/internal/socket/msghdr_openbsd.go | 14 +
.../internal/socket/msghdr_solaris_64bit.go | 35 +
.../x/net/internal/socket/msghdr_stub.go | 14 +
.../x/net/internal/socket/msghdr_zos_s390x.go | 35 +
.../x/net/internal/socket/norace.go | 12 +
.../golang.org/x/net/internal/socket/race.go | 37 +
.../x/net/internal/socket/rawconn.go | 91 +
.../x/net/internal/socket/rawconn_mmsg.go | 53 +
.../x/net/internal/socket/rawconn_msg.go | 59 +
.../x/net/internal/socket/rawconn_nommsg.go | 15 +
.../x/net/internal/socket/rawconn_nomsg.go | 15 +
.../x/net/internal/socket/socket.go | 280 +
.../golang.org/x/net/internal/socket/sys.go | 23 +
.../x/net/internal/socket/sys_bsd.go | 15 +
.../x/net/internal/socket/sys_const_unix.go | 20 +
.../x/net/internal/socket/sys_linux.go | 22 +
.../x/net/internal/socket/sys_linux_386.go | 28 +
.../x/net/internal/socket/sys_linux_386.s | 11 +
.../x/net/internal/socket/sys_linux_amd64.go | 10 +
.../x/net/internal/socket/sys_linux_arm.go | 10 +
.../x/net/internal/socket/sys_linux_arm64.go | 10 +
.../net/internal/socket/sys_linux_loong64.go | 12 +
.../x/net/internal/socket/sys_linux_mips.go | 10 +
.../x/net/internal/socket/sys_linux_mips64.go | 10 +
.../net/internal/socket/sys_linux_mips64le.go | 10 +
.../x/net/internal/socket/sys_linux_mipsle.go | 10 +
.../x/net/internal/socket/sys_linux_ppc.go | 10 +
.../x/net/internal/socket/sys_linux_ppc64.go | 10 +
.../net/internal/socket/sys_linux_ppc64le.go | 10 +
.../net/internal/socket/sys_linux_riscv64.go | 12 +
.../x/net/internal/socket/sys_linux_s390x.go | 28 +
.../x/net/internal/socket/sys_linux_s390x.s | 11 +
.../x/net/internal/socket/sys_netbsd.go | 25 +
.../x/net/internal/socket/sys_posix.go | 184 +
.../x/net/internal/socket/sys_stub.go | 52 +
.../x/net/internal/socket/sys_unix.go | 121 +
.../x/net/internal/socket/sys_windows.go | 55 +
.../x/net/internal/socket/sys_zos_s390x.go | 66 +
.../x/net/internal/socket/sys_zos_s390x.s | 11 +
.../x/net/internal/socket/zsys_aix_ppc64.go | 39 +
.../net/internal/socket/zsys_darwin_amd64.go | 32 +
.../net/internal/socket/zsys_darwin_arm64.go | 32 +
.../internal/socket/zsys_dragonfly_amd64.go | 32 +
.../x/net/internal/socket/zsys_freebsd_386.go | 30 +
.../net/internal/socket/zsys_freebsd_amd64.go | 32 +
.../x/net/internal/socket/zsys_freebsd_arm.go | 30 +
.../net/internal/socket/zsys_freebsd_arm64.go | 32 +
.../internal/socket/zsys_freebsd_riscv64.go | 30 +
.../x/net/internal/socket/zsys_linux_386.go | 35 +
.../x/net/internal/socket/zsys_linux_amd64.go | 38 +
.../x/net/internal/socket/zsys_linux_arm.go | 35 +
.../x/net/internal/socket/zsys_linux_arm64.go | 38 +
.../net/internal/socket/zsys_linux_loong64.go | 39 +
.../x/net/internal/socket/zsys_linux_mips.go | 35 +
.../net/internal/socket/zsys_linux_mips64.go | 38 +
.../internal/socket/zsys_linux_mips64le.go | 38 +
.../net/internal/socket/zsys_linux_mipsle.go | 35 +
.../x/net/internal/socket/zsys_linux_ppc.go | 35 +
.../x/net/internal/socket/zsys_linux_ppc64.go | 38 +
.../net/internal/socket/zsys_linux_ppc64le.go | 38 +
.../net/internal/socket/zsys_linux_riscv64.go | 39 +
.../x/net/internal/socket/zsys_linux_s390x.go | 38 +
.../x/net/internal/socket/zsys_netbsd_386.go | 35 +
.../net/internal/socket/zsys_netbsd_amd64.go | 38 +
.../x/net/internal/socket/zsys_netbsd_arm.go | 35 +
.../net/internal/socket/zsys_netbsd_arm64.go | 38 +
.../x/net/internal/socket/zsys_openbsd_386.go | 30 +
.../net/internal/socket/zsys_openbsd_amd64.go | 32 +
.../x/net/internal/socket/zsys_openbsd_arm.go | 30 +
.../net/internal/socket/zsys_openbsd_arm64.go | 32 +
.../internal/socket/zsys_openbsd_mips64.go | 30 +
.../net/internal/socket/zsys_openbsd_ppc64.go | 30 +
.../internal/socket/zsys_openbsd_riscv64.go | 30 +
.../net/internal/socket/zsys_solaris_amd64.go | 32 +
.../x/net/internal/socket/zsys_zos_s390x.go | 28 +
.../golang.org/x/net/internal/socks/client.go | 168 +
.../golang.org/x/net/internal/socks/socks.go | 317 +
server/vendor/golang.org/x/net/ipv4/batch.go | 194 +
.../vendor/golang.org/x/net/ipv4/control.go | 144 +
.../golang.org/x/net/ipv4/control_bsd.go | 43 +
.../golang.org/x/net/ipv4/control_pktinfo.go | 41 +
.../golang.org/x/net/ipv4/control_stub.go | 13 +
.../golang.org/x/net/ipv4/control_unix.go | 75 +
.../golang.org/x/net/ipv4/control_windows.go | 12 +
.../golang.org/x/net/ipv4/control_zos.go | 88 +
.../vendor/golang.org/x/net/ipv4/dgramopt.go | 264 +
server/vendor/golang.org/x/net/ipv4/doc.go | 240 +
.../vendor/golang.org/x/net/ipv4/endpoint.go | 186 +
.../golang.org/x/net/ipv4/genericopt.go | 55 +
server/vendor/golang.org/x/net/ipv4/header.go | 172 +
server/vendor/golang.org/x/net/ipv4/helper.go | 77 +
server/vendor/golang.org/x/net/ipv4/iana.go | 38 +
server/vendor/golang.org/x/net/ipv4/icmp.go | 57 +
.../golang.org/x/net/ipv4/icmp_linux.go | 25 +
.../vendor/golang.org/x/net/ipv4/icmp_stub.go | 25 +
server/vendor/golang.org/x/net/ipv4/packet.go | 117 +
.../vendor/golang.org/x/net/ipv4/payload.go | 23 +
.../golang.org/x/net/ipv4/payload_cmsg.go | 84 +
.../golang.org/x/net/ipv4/payload_nocmsg.go | 39 +
.../vendor/golang.org/x/net/ipv4/sockopt.go | 44 +
.../golang.org/x/net/ipv4/sockopt_posix.go | 71 +
.../golang.org/x/net/ipv4/sockopt_stub.go | 42 +
.../vendor/golang.org/x/net/ipv4/sys_aix.go | 43 +
.../golang.org/x/net/ipv4/sys_asmreq.go | 122 +
.../golang.org/x/net/ipv4/sys_asmreq_stub.go | 25 +
.../golang.org/x/net/ipv4/sys_asmreqn.go | 44 +
.../golang.org/x/net/ipv4/sys_asmreqn_stub.go | 21 +
.../vendor/golang.org/x/net/ipv4/sys_bpf.go | 24 +
.../golang.org/x/net/ipv4/sys_bpf_stub.go | 16 +
.../vendor/golang.org/x/net/ipv4/sys_bsd.go | 41 +
.../golang.org/x/net/ipv4/sys_darwin.go | 69 +
.../golang.org/x/net/ipv4/sys_dragonfly.go | 39 +
.../golang.org/x/net/ipv4/sys_freebsd.go | 80 +
.../vendor/golang.org/x/net/ipv4/sys_linux.go | 61 +
.../golang.org/x/net/ipv4/sys_solaris.go | 61 +
.../golang.org/x/net/ipv4/sys_ssmreq.go | 52 +
.../golang.org/x/net/ipv4/sys_ssmreq_stub.go | 21 +
.../vendor/golang.org/x/net/ipv4/sys_stub.go | 13 +
.../golang.org/x/net/ipv4/sys_windows.go | 44 +
.../vendor/golang.org/x/net/ipv4/sys_zos.go | 57 +
.../golang.org/x/net/ipv4/zsys_aix_ppc64.go | 16 +
.../golang.org/x/net/ipv4/zsys_darwin.go | 59 +
.../golang.org/x/net/ipv4/zsys_dragonfly.go | 13 +
.../golang.org/x/net/ipv4/zsys_freebsd_386.go | 52 +
.../x/net/ipv4/zsys_freebsd_amd64.go | 54 +
.../golang.org/x/net/ipv4/zsys_freebsd_arm.go | 54 +
.../x/net/ipv4/zsys_freebsd_arm64.go | 52 +
.../x/net/ipv4/zsys_freebsd_riscv64.go | 52 +
.../golang.org/x/net/ipv4/zsys_linux_386.go | 72 +
.../golang.org/x/net/ipv4/zsys_linux_amd64.go | 74 +
.../golang.org/x/net/ipv4/zsys_linux_arm.go | 72 +
.../golang.org/x/net/ipv4/zsys_linux_arm64.go | 74 +
.../x/net/ipv4/zsys_linux_loong64.go | 76 +
.../golang.org/x/net/ipv4/zsys_linux_mips.go | 72 +
.../x/net/ipv4/zsys_linux_mips64.go | 74 +
.../x/net/ipv4/zsys_linux_mips64le.go | 74 +
.../x/net/ipv4/zsys_linux_mipsle.go | 72 +
.../golang.org/x/net/ipv4/zsys_linux_ppc.go | 72 +
.../golang.org/x/net/ipv4/zsys_linux_ppc64.go | 74 +
.../x/net/ipv4/zsys_linux_ppc64le.go | 74 +
.../x/net/ipv4/zsys_linux_riscv64.go | 76 +
.../golang.org/x/net/ipv4/zsys_linux_s390x.go | 74 +
.../golang.org/x/net/ipv4/zsys_netbsd.go | 13 +
.../golang.org/x/net/ipv4/zsys_openbsd.go | 13 +
.../golang.org/x/net/ipv4/zsys_solaris.go | 57 +
.../golang.org/x/net/ipv4/zsys_zos_s390x.go | 56 +
server/vendor/golang.org/x/net/ipv6/batch.go | 116 +
.../vendor/golang.org/x/net/ipv6/control.go | 187 +
.../x/net/ipv6/control_rfc2292_unix.go | 50 +
.../x/net/ipv6/control_rfc3542_unix.go | 96 +
.../golang.org/x/net/ipv6/control_stub.go | 13 +
.../golang.org/x/net/ipv6/control_unix.go | 55 +
.../golang.org/x/net/ipv6/control_windows.go | 12 +
.../vendor/golang.org/x/net/ipv6/dgramopt.go | 301 +
server/vendor/golang.org/x/net/ipv6/doc.go | 239 +
.../vendor/golang.org/x/net/ipv6/endpoint.go | 127 +
.../golang.org/x/net/ipv6/genericopt.go | 56 +
server/vendor/golang.org/x/net/ipv6/header.go | 55 +
server/vendor/golang.org/x/net/ipv6/helper.go | 58 +
server/vendor/golang.org/x/net/ipv6/iana.go | 86 +
server/vendor/golang.org/x/net/ipv6/icmp.go | 60 +
.../vendor/golang.org/x/net/ipv6/icmp_bsd.go | 29 +
.../golang.org/x/net/ipv6/icmp_linux.go | 27 +
.../golang.org/x/net/ipv6/icmp_solaris.go | 27 +
.../vendor/golang.org/x/net/ipv6/icmp_stub.go | 23 +
.../golang.org/x/net/ipv6/icmp_windows.go | 22 +
.../vendor/golang.org/x/net/ipv6/icmp_zos.go | 29 +
.../vendor/golang.org/x/net/ipv6/payload.go | 23 +
.../golang.org/x/net/ipv6/payload_cmsg.go | 70 +
.../golang.org/x/net/ipv6/payload_nocmsg.go | 38 +
.../vendor/golang.org/x/net/ipv6/sockopt.go | 43 +
.../golang.org/x/net/ipv6/sockopt_posix.go | 89 +
.../golang.org/x/net/ipv6/sockopt_stub.go | 46 +
.../vendor/golang.org/x/net/ipv6/sys_aix.go | 79 +
.../golang.org/x/net/ipv6/sys_asmreq.go | 24 +
.../golang.org/x/net/ipv6/sys_asmreq_stub.go | 17 +
.../vendor/golang.org/x/net/ipv6/sys_bpf.go | 24 +
.../golang.org/x/net/ipv6/sys_bpf_stub.go | 16 +
.../vendor/golang.org/x/net/ipv6/sys_bsd.go | 59 +
.../golang.org/x/net/ipv6/sys_darwin.go | 80 +
.../golang.org/x/net/ipv6/sys_freebsd.go | 94 +
.../vendor/golang.org/x/net/ipv6/sys_linux.go | 76 +
.../golang.org/x/net/ipv6/sys_solaris.go | 76 +
.../golang.org/x/net/ipv6/sys_ssmreq.go | 54 +
.../golang.org/x/net/ipv6/sys_ssmreq_stub.go | 21 +
.../vendor/golang.org/x/net/ipv6/sys_stub.go | 13 +
.../golang.org/x/net/ipv6/sys_windows.go | 68 +
.../vendor/golang.org/x/net/ipv6/sys_zos.go | 72 +
.../golang.org/x/net/ipv6/zsys_aix_ppc64.go | 68 +
.../golang.org/x/net/ipv6/zsys_darwin.go | 64 +
.../golang.org/x/net/ipv6/zsys_dragonfly.go | 42 +
.../golang.org/x/net/ipv6/zsys_freebsd_386.go | 64 +
.../x/net/ipv6/zsys_freebsd_amd64.go | 66 +
.../golang.org/x/net/ipv6/zsys_freebsd_arm.go | 66 +
.../x/net/ipv6/zsys_freebsd_arm64.go | 64 +
.../x/net/ipv6/zsys_freebsd_riscv64.go | 64 +
.../golang.org/x/net/ipv6/zsys_linux_386.go | 72 +
.../golang.org/x/net/ipv6/zsys_linux_amd64.go | 74 +
.../golang.org/x/net/ipv6/zsys_linux_arm.go | 72 +
.../golang.org/x/net/ipv6/zsys_linux_arm64.go | 74 +
.../x/net/ipv6/zsys_linux_loong64.go | 76 +
.../golang.org/x/net/ipv6/zsys_linux_mips.go | 72 +
.../x/net/ipv6/zsys_linux_mips64.go | 74 +
.../x/net/ipv6/zsys_linux_mips64le.go | 74 +
.../x/net/ipv6/zsys_linux_mipsle.go | 72 +
.../golang.org/x/net/ipv6/zsys_linux_ppc.go | 72 +
.../golang.org/x/net/ipv6/zsys_linux_ppc64.go | 74 +
.../x/net/ipv6/zsys_linux_ppc64le.go | 74 +
.../x/net/ipv6/zsys_linux_riscv64.go | 76 +
.../golang.org/x/net/ipv6/zsys_linux_s390x.go | 74 +
.../golang.org/x/net/ipv6/zsys_netbsd.go | 42 +
.../golang.org/x/net/ipv6/zsys_openbsd.go | 42 +
.../golang.org/x/net/ipv6/zsys_solaris.go | 63 +
.../golang.org/x/net/ipv6/zsys_zos_s390x.go | 62 +
server/vendor/golang.org/x/net/proxy/dial.go | 54 +
.../vendor/golang.org/x/net/proxy/direct.go | 31 +
.../vendor/golang.org/x/net/proxy/per_host.go | 155 +
server/vendor/golang.org/x/net/proxy/proxy.go | 149 +
.../vendor/golang.org/x/net/proxy/socks5.go | 42 +
.../golang.org/x/sys/windows/aliases.go | 12 +
.../golang.org/x/sys/windows/dll_windows.go | 415 +
.../golang.org/x/sys/windows/env_windows.go | 57 +
.../golang.org/x/sys/windows/eventlog.go | 20 +
.../golang.org/x/sys/windows/exec_windows.go | 248 +
.../x/sys/windows/memory_windows.go | 48 +
.../golang.org/x/sys/windows/mkerrors.bash | 70 +
.../x/sys/windows/mkknownfolderids.bash | 27 +
.../golang.org/x/sys/windows/mksyscall.go | 9 +
.../vendor/golang.org/x/sys/windows/race.go | 30 +
.../vendor/golang.org/x/sys/windows/race0.go | 25 +
.../x/sys/windows/security_windows.go | 1458 +++
.../golang.org/x/sys/windows/service.go | 257 +
.../x/sys/windows/setupapi_windows.go | 1425 +++
server/vendor/golang.org/x/sys/windows/str.go | 22 +
.../golang.org/x/sys/windows/syscall.go | 104 +
.../x/sys/windows/syscall_windows.go | 1932 ++++
.../golang.org/x/sys/windows/types_windows.go | 3603 +++++++
.../x/sys/windows/types_windows_386.go | 35 +
.../x/sys/windows/types_windows_amd64.go | 34 +
.../x/sys/windows/types_windows_arm.go | 35 +
.../x/sys/windows/types_windows_arm64.go | 34 +
.../x/sys/windows/zerrors_windows.go | 9468 +++++++++++++++++
.../x/sys/windows/zknownfolderids_windows.go | 149 +
.../x/sys/windows/zsyscall_windows.go | 4686 ++++++++
server/vendor/modules.txt | 129 +-
web/src/App.vue | 5 +
web/src/components/LiveFloatingPlayer.vue | 216 +
web/src/router/index.js | 6 +
web/src/utils/liveWebRTC.js | 148 +
web/src/views/Home.vue | 105 +-
web/src/views/LiveRoom.vue | 228 +
web/vite.config.js | 3 +-
1050 files changed, 146524 insertions(+), 37 deletions(-)
create mode 100644 admin/src/utils/liveWebRTC.js
create mode 100644 admin/src/views/sites/LiveBroadcast.vue
create mode 100644 server/pkg/weblive/config.go
create mode 100644 server/pkg/weblive/hub.go
create mode 100644 server/pkg/weblive/ws.go
create mode 100644 server/vendor/github.com/davecgh/go-spew/LICENSE
create mode 100644 server/vendor/github.com/davecgh/go-spew/spew/bypass.go
create mode 100644 server/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
create mode 100644 server/vendor/github.com/davecgh/go-spew/spew/common.go
create mode 100644 server/vendor/github.com/davecgh/go-spew/spew/config.go
create mode 100644 server/vendor/github.com/davecgh/go-spew/spew/doc.go
create mode 100644 server/vendor/github.com/davecgh/go-spew/spew/dump.go
create mode 100644 server/vendor/github.com/davecgh/go-spew/spew/format.go
create mode 100644 server/vendor/github.com/davecgh/go-spew/spew/spew.go
create mode 100644 server/vendor/github.com/google/uuid/CHANGELOG.md
create mode 100644 server/vendor/github.com/google/uuid/CONTRIBUTING.md
create mode 100644 server/vendor/github.com/google/uuid/CONTRIBUTORS
create mode 100644 server/vendor/github.com/google/uuid/LICENSE
create mode 100644 server/vendor/github.com/google/uuid/README.md
create mode 100644 server/vendor/github.com/google/uuid/dce.go
create mode 100644 server/vendor/github.com/google/uuid/doc.go
create mode 100644 server/vendor/github.com/google/uuid/hash.go
create mode 100644 server/vendor/github.com/google/uuid/marshal.go
create mode 100644 server/vendor/github.com/google/uuid/node.go
create mode 100644 server/vendor/github.com/google/uuid/node_js.go
create mode 100644 server/vendor/github.com/google/uuid/node_net.go
create mode 100644 server/vendor/github.com/google/uuid/null.go
create mode 100644 server/vendor/github.com/google/uuid/sql.go
create mode 100644 server/vendor/github.com/google/uuid/time.go
create mode 100644 server/vendor/github.com/google/uuid/util.go
create mode 100644 server/vendor/github.com/google/uuid/uuid.go
create mode 100644 server/vendor/github.com/google/uuid/version1.go
create mode 100644 server/vendor/github.com/google/uuid/version4.go
create mode 100644 server/vendor/github.com/gorilla/websocket/.gitignore
create mode 100644 server/vendor/github.com/gorilla/websocket/AUTHORS
create mode 100644 server/vendor/github.com/gorilla/websocket/LICENSE
create mode 100644 server/vendor/github.com/gorilla/websocket/README.md
create mode 100644 server/vendor/github.com/gorilla/websocket/client.go
create mode 100644 server/vendor/github.com/gorilla/websocket/compression.go
create mode 100644 server/vendor/github.com/gorilla/websocket/conn.go
create mode 100644 server/vendor/github.com/gorilla/websocket/doc.go
create mode 100644 server/vendor/github.com/gorilla/websocket/join.go
create mode 100644 server/vendor/github.com/gorilla/websocket/json.go
create mode 100644 server/vendor/github.com/gorilla/websocket/mask.go
create mode 100644 server/vendor/github.com/gorilla/websocket/mask_safe.go
create mode 100644 server/vendor/github.com/gorilla/websocket/prepared.go
create mode 100644 server/vendor/github.com/gorilla/websocket/proxy.go
create mode 100644 server/vendor/github.com/gorilla/websocket/server.go
create mode 100644 server/vendor/github.com/gorilla/websocket/tls_handshake.go
create mode 100644 server/vendor/github.com/gorilla/websocket/tls_handshake_116.go
create mode 100644 server/vendor/github.com/gorilla/websocket/util.go
create mode 100644 server/vendor/github.com/gorilla/websocket/x_net_proxy.go
create mode 100644 server/vendor/github.com/pion/datachannel/.gitignore
create mode 100644 server/vendor/github.com/pion/datachannel/.golangci.yml
create mode 100644 server/vendor/github.com/pion/datachannel/.goreleaser.yml
create mode 100644 server/vendor/github.com/pion/datachannel/LICENSE
create mode 100644 server/vendor/github.com/pion/datachannel/README.md
create mode 100644 server/vendor/github.com/pion/datachannel/codecov.yml
create mode 100644 server/vendor/github.com/pion/datachannel/datachannel.go
create mode 100644 server/vendor/github.com/pion/datachannel/errors.go
create mode 100644 server/vendor/github.com/pion/datachannel/message.go
create mode 100644 server/vendor/github.com/pion/datachannel/message_channel_ack.go
create mode 100644 server/vendor/github.com/pion/datachannel/message_channel_open.go
create mode 100644 server/vendor/github.com/pion/datachannel/renovate.json
create mode 100644 server/vendor/github.com/pion/dtls/v2/.editorconfig
create mode 100644 server/vendor/github.com/pion/dtls/v2/.gitignore
create mode 100644 server/vendor/github.com/pion/dtls/v2/.golangci.yml
create mode 100644 server/vendor/github.com/pion/dtls/v2/.goreleaser.yml
create mode 100644 server/vendor/github.com/pion/dtls/v2/AUTHORS.txt
create mode 100644 server/vendor/github.com/pion/dtls/v2/LICENSE
create mode 100644 server/vendor/github.com/pion/dtls/v2/README.md
create mode 100644 server/vendor/github.com/pion/dtls/v2/certificate.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/cipher_suite.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/cipher_suite_go114.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/codecov.yml
create mode 100644 server/vendor/github.com/pion/dtls/v2/compression_method.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/config.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/conn.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/crypto.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/dtls.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/errors.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/errors_errno.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/errors_noerrno.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/flight.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/flight0handler.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/flight1handler.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/flight2handler.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/flight3handler.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/flight4bhandler.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/flight4handler.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/flight5bhandler.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/flight5handler.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/flight6handler.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/flighthandler.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/fragment_buffer.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/handshake_cache.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/handshaker.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_256_ccm.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_ccm.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_gcm_sha384.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_psk_with_aes_128_cbc_sha256.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_gcm_sha384.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_256_ccm8.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/types/authentication_type.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/types/key_exchange_algorithm.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/closer/closer.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/internal/util/util.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/listener.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/packet.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/alpn.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/pkg/protocol/version.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/renovate.json
create mode 100644 server/vendor/github.com/pion/dtls/v2/resume.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/session.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/srtp_protection_profile.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/state.go
create mode 100644 server/vendor/github.com/pion/dtls/v2/util.go
create mode 100644 server/vendor/github.com/pion/ice/v2/.gitignore
create mode 100644 server/vendor/github.com/pion/ice/v2/.golangci.yml
create mode 100644 server/vendor/github.com/pion/ice/v2/.goreleaser.yml
create mode 100644 server/vendor/github.com/pion/ice/v2/AUTHORS.txt
create mode 100644 server/vendor/github.com/pion/ice/v2/LICENSE
create mode 100644 server/vendor/github.com/pion/ice/v2/README.md
create mode 100644 server/vendor/github.com/pion/ice/v2/active_tcp.go
create mode 100644 server/vendor/github.com/pion/ice/v2/addr.go
create mode 100644 server/vendor/github.com/pion/ice/v2/agent.go
create mode 100644 server/vendor/github.com/pion/ice/v2/agent_config.go
create mode 100644 server/vendor/github.com/pion/ice/v2/agent_handlers.go
create mode 100644 server/vendor/github.com/pion/ice/v2/agent_stats.go
create mode 100644 server/vendor/github.com/pion/ice/v2/candidate.go
create mode 100644 server/vendor/github.com/pion/ice/v2/candidate_base.go
create mode 100644 server/vendor/github.com/pion/ice/v2/candidate_host.go
create mode 100644 server/vendor/github.com/pion/ice/v2/candidate_peer_reflexive.go
create mode 100644 server/vendor/github.com/pion/ice/v2/candidate_relay.go
create mode 100644 server/vendor/github.com/pion/ice/v2/candidate_server_reflexive.go
create mode 100644 server/vendor/github.com/pion/ice/v2/candidatepair.go
create mode 100644 server/vendor/github.com/pion/ice/v2/candidatepair_state.go
create mode 100644 server/vendor/github.com/pion/ice/v2/candidaterelatedaddress.go
create mode 100644 server/vendor/github.com/pion/ice/v2/candidatetype.go
create mode 100644 server/vendor/github.com/pion/ice/v2/codecov.yml
create mode 100644 server/vendor/github.com/pion/ice/v2/context.go
create mode 100644 server/vendor/github.com/pion/ice/v2/errors.go
create mode 100644 server/vendor/github.com/pion/ice/v2/external_ip_mapper.go
create mode 100644 server/vendor/github.com/pion/ice/v2/gather.go
create mode 100644 server/vendor/github.com/pion/ice/v2/ice.go
create mode 100644 server/vendor/github.com/pion/ice/v2/icecontrol.go
create mode 100644 server/vendor/github.com/pion/ice/v2/internal/atomic/atomic.go
create mode 100644 server/vendor/github.com/pion/ice/v2/internal/fakenet/mock_conn.go
create mode 100644 server/vendor/github.com/pion/ice/v2/internal/fakenet/packet_conn.go
create mode 100644 server/vendor/github.com/pion/ice/v2/internal/stun/stun.go
create mode 100644 server/vendor/github.com/pion/ice/v2/mdns.go
create mode 100644 server/vendor/github.com/pion/ice/v2/net.go
create mode 100644 server/vendor/github.com/pion/ice/v2/networktype.go
create mode 100644 server/vendor/github.com/pion/ice/v2/priority.go
create mode 100644 server/vendor/github.com/pion/ice/v2/rand.go
create mode 100644 server/vendor/github.com/pion/ice/v2/renovate.json
create mode 100644 server/vendor/github.com/pion/ice/v2/role.go
create mode 100644 server/vendor/github.com/pion/ice/v2/selection.go
create mode 100644 server/vendor/github.com/pion/ice/v2/stats.go
create mode 100644 server/vendor/github.com/pion/ice/v2/tcp_mux.go
create mode 100644 server/vendor/github.com/pion/ice/v2/tcp_mux_multi.go
create mode 100644 server/vendor/github.com/pion/ice/v2/tcp_packet_conn.go
create mode 100644 server/vendor/github.com/pion/ice/v2/tcptype.go
create mode 100644 server/vendor/github.com/pion/ice/v2/test_utils.go
create mode 100644 server/vendor/github.com/pion/ice/v2/transport.go
create mode 100644 server/vendor/github.com/pion/ice/v2/udp_mux.go
create mode 100644 server/vendor/github.com/pion/ice/v2/udp_mux_multi.go
create mode 100644 server/vendor/github.com/pion/ice/v2/udp_mux_universal.go
create mode 100644 server/vendor/github.com/pion/ice/v2/udp_muxed_conn.go
create mode 100644 server/vendor/github.com/pion/ice/v2/url.go
create mode 100644 server/vendor/github.com/pion/ice/v2/usecandidate.go
create mode 100644 server/vendor/github.com/pion/interceptor/.gitignore
create mode 100644 server/vendor/github.com/pion/interceptor/.golangci.yml
create mode 100644 server/vendor/github.com/pion/interceptor/.goreleaser.yml
create mode 100644 server/vendor/github.com/pion/interceptor/LICENSE
create mode 100644 server/vendor/github.com/pion/interceptor/README.md
create mode 100644 server/vendor/github.com/pion/interceptor/attributes.go
create mode 100644 server/vendor/github.com/pion/interceptor/chain.go
create mode 100644 server/vendor/github.com/pion/interceptor/codecov.yml
create mode 100644 server/vendor/github.com/pion/interceptor/errors.go
create mode 100644 server/vendor/github.com/pion/interceptor/interceptor.go
create mode 100644 server/vendor/github.com/pion/interceptor/internal/ntp/ntp.go
create mode 100644 server/vendor/github.com/pion/interceptor/internal/sequencenumber/unwrapper.go
create mode 100644 server/vendor/github.com/pion/interceptor/noop.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/nack/errors.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/nack/generator_interceptor.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/nack/generator_option.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/nack/nack.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/nack/receive_log.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/nack/responder_interceptor.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/nack/responder_option.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/nack/retainable_packet.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/nack/send_buffer.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/report/receiver_interceptor.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/report/receiver_option.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/report/receiver_stream.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/report/report.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/report/sender_interceptor.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/report/sender_option.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/report/sender_stream.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/report/ticker.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/twcc/arrival_time_map.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/twcc/header_extension_interceptor.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/twcc/sender_interceptor.go
create mode 100644 server/vendor/github.com/pion/interceptor/pkg/twcc/twcc.go
create mode 100644 server/vendor/github.com/pion/interceptor/registry.go
create mode 100644 server/vendor/github.com/pion/interceptor/renovate.json
create mode 100644 server/vendor/github.com/pion/interceptor/streaminfo.go
create mode 100644 server/vendor/github.com/pion/logging/.golangci.yml
create mode 100644 server/vendor/github.com/pion/logging/.travis.yml
create mode 100644 server/vendor/github.com/pion/logging/LICENSE
create mode 100644 server/vendor/github.com/pion/logging/README.md
create mode 100644 server/vendor/github.com/pion/logging/logger.go
create mode 100644 server/vendor/github.com/pion/logging/scoped.go
create mode 100644 server/vendor/github.com/pion/mdns/.gitignore
create mode 100644 server/vendor/github.com/pion/mdns/.golangci.yml
create mode 100644 server/vendor/github.com/pion/mdns/.goreleaser.yml
create mode 100644 server/vendor/github.com/pion/mdns/LICENSE
create mode 100644 server/vendor/github.com/pion/mdns/README.md
create mode 100644 server/vendor/github.com/pion/mdns/codecov.yml
create mode 100644 server/vendor/github.com/pion/mdns/config.go
create mode 100644 server/vendor/github.com/pion/mdns/conn.go
create mode 100644 server/vendor/github.com/pion/mdns/errors.go
create mode 100644 server/vendor/github.com/pion/mdns/mdns.go
create mode 100644 server/vendor/github.com/pion/mdns/renovate.json
create mode 100644 server/vendor/github.com/pion/randutil/.travis.yml
create mode 100644 server/vendor/github.com/pion/randutil/LICENSE
create mode 100644 server/vendor/github.com/pion/randutil/README.md
create mode 100644 server/vendor/github.com/pion/randutil/codecov.yml
create mode 100644 server/vendor/github.com/pion/randutil/crypto.go
create mode 100644 server/vendor/github.com/pion/randutil/math.go
create mode 100644 server/vendor/github.com/pion/randutil/renovate.json
create mode 100644 server/vendor/github.com/pion/rtcp/.gitignore
create mode 100644 server/vendor/github.com/pion/rtcp/.golangci.yml
create mode 100644 server/vendor/github.com/pion/rtcp/.goreleaser.yml
create mode 100644 server/vendor/github.com/pion/rtcp/LICENSE
create mode 100644 server/vendor/github.com/pion/rtcp/README.md
create mode 100644 server/vendor/github.com/pion/rtcp/codecov.yml
create mode 100644 server/vendor/github.com/pion/rtcp/compound_packet.go
create mode 100644 server/vendor/github.com/pion/rtcp/doc.go
create mode 100644 server/vendor/github.com/pion/rtcp/errors.go
create mode 100644 server/vendor/github.com/pion/rtcp/extended_report.go
create mode 100644 server/vendor/github.com/pion/rtcp/full_intra_request.go
create mode 100644 server/vendor/github.com/pion/rtcp/goodbye.go
create mode 100644 server/vendor/github.com/pion/rtcp/header.go
create mode 100644 server/vendor/github.com/pion/rtcp/packet.go
create mode 100644 server/vendor/github.com/pion/rtcp/packet_buffer.go
create mode 100644 server/vendor/github.com/pion/rtcp/packet_stringifier.go
create mode 100644 server/vendor/github.com/pion/rtcp/picture_loss_indication.go
create mode 100644 server/vendor/github.com/pion/rtcp/rapid_resynchronization_request.go
create mode 100644 server/vendor/github.com/pion/rtcp/raw_packet.go
create mode 100644 server/vendor/github.com/pion/rtcp/receiver_estimated_maximum_bitrate.go
create mode 100644 server/vendor/github.com/pion/rtcp/receiver_report.go
create mode 100644 server/vendor/github.com/pion/rtcp/reception_report.go
create mode 100644 server/vendor/github.com/pion/rtcp/renovate.json
create mode 100644 server/vendor/github.com/pion/rtcp/rfc8888.go
create mode 100644 server/vendor/github.com/pion/rtcp/sender_report.go
create mode 100644 server/vendor/github.com/pion/rtcp/slice_loss_indication.go
create mode 100644 server/vendor/github.com/pion/rtcp/source_description.go
create mode 100644 server/vendor/github.com/pion/rtcp/transport_layer_cc.go
create mode 100644 server/vendor/github.com/pion/rtcp/transport_layer_nack.go
create mode 100644 server/vendor/github.com/pion/rtcp/util.go
create mode 100644 server/vendor/github.com/pion/rtp/.gitignore
create mode 100644 server/vendor/github.com/pion/rtp/.golangci.yml
create mode 100644 server/vendor/github.com/pion/rtp/.goreleaser.yml
create mode 100644 server/vendor/github.com/pion/rtp/LICENSE
create mode 100644 server/vendor/github.com/pion/rtp/README.md
create mode 100644 server/vendor/github.com/pion/rtp/abscapturetimeextension.go
create mode 100644 server/vendor/github.com/pion/rtp/abssendtimeextension.go
create mode 100644 server/vendor/github.com/pion/rtp/audiolevelextension.go
create mode 100644 server/vendor/github.com/pion/rtp/codecov.yml
create mode 100644 server/vendor/github.com/pion/rtp/codecs/av1/obu/leb128.go
create mode 100644 server/vendor/github.com/pion/rtp/codecs/av1_packet.go
create mode 100644 server/vendor/github.com/pion/rtp/codecs/codecs.go
create mode 100644 server/vendor/github.com/pion/rtp/codecs/common.go
create mode 100644 server/vendor/github.com/pion/rtp/codecs/error.go
create mode 100644 server/vendor/github.com/pion/rtp/codecs/g711_packet.go
create mode 100644 server/vendor/github.com/pion/rtp/codecs/g722_packet.go
create mode 100644 server/vendor/github.com/pion/rtp/codecs/h264_packet.go
create mode 100644 server/vendor/github.com/pion/rtp/codecs/h265_packet.go
create mode 100644 server/vendor/github.com/pion/rtp/codecs/opus_packet.go
create mode 100644 server/vendor/github.com/pion/rtp/codecs/vp8_packet.go
create mode 100644 server/vendor/github.com/pion/rtp/codecs/vp9/bits.go
create mode 100644 server/vendor/github.com/pion/rtp/codecs/vp9/header.go
create mode 100644 server/vendor/github.com/pion/rtp/codecs/vp9_packet.go
create mode 100644 server/vendor/github.com/pion/rtp/depacketizer.go
create mode 100644 server/vendor/github.com/pion/rtp/error.go
create mode 100644 server/vendor/github.com/pion/rtp/header_extension.go
create mode 100644 server/vendor/github.com/pion/rtp/packet.go
create mode 100644 server/vendor/github.com/pion/rtp/packetizer.go
create mode 100644 server/vendor/github.com/pion/rtp/partitionheadchecker.go
create mode 100644 server/vendor/github.com/pion/rtp/payload_types.go
create mode 100644 server/vendor/github.com/pion/rtp/playoutdelayextension.go
create mode 100644 server/vendor/github.com/pion/rtp/rand.go
create mode 100644 server/vendor/github.com/pion/rtp/renovate.json
create mode 100644 server/vendor/github.com/pion/rtp/rtp.go
create mode 100644 server/vendor/github.com/pion/rtp/sequencer.go
create mode 100644 server/vendor/github.com/pion/rtp/transportccextension.go
create mode 100644 server/vendor/github.com/pion/rtp/vlaextension.go
create mode 100644 server/vendor/github.com/pion/sctp/.gitignore
create mode 100644 server/vendor/github.com/pion/sctp/.golangci.yml
create mode 100644 server/vendor/github.com/pion/sctp/.goreleaser.yml
create mode 100644 server/vendor/github.com/pion/sctp/LICENSE
create mode 100644 server/vendor/github.com/pion/sctp/README.md
create mode 100644 server/vendor/github.com/pion/sctp/ack_timer.go
create mode 100644 server/vendor/github.com/pion/sctp/association.go
create mode 100644 server/vendor/github.com/pion/sctp/association_stats.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_abort.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_cookie_ack.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_cookie_echo.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_error.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_forward_tsn.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_heartbeat.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_heartbeat_ack.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_init.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_init_ack.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_init_common.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_payload_data.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_reconfig.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_selective_ack.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_shutdown.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_shutdown_ack.go
create mode 100644 server/vendor/github.com/pion/sctp/chunk_shutdown_complete.go
create mode 100644 server/vendor/github.com/pion/sctp/chunkheader.go
create mode 100644 server/vendor/github.com/pion/sctp/chunktype.go
create mode 100644 server/vendor/github.com/pion/sctp/codecov.yml
create mode 100644 server/vendor/github.com/pion/sctp/control_queue.go
create mode 100644 server/vendor/github.com/pion/sctp/error_cause.go
create mode 100644 server/vendor/github.com/pion/sctp/error_cause_header.go
create mode 100644 server/vendor/github.com/pion/sctp/error_cause_invalid_mandatory_parameter.go
create mode 100644 server/vendor/github.com/pion/sctp/error_cause_protocol_violation.go
create mode 100644 server/vendor/github.com/pion/sctp/error_cause_unrecognized_chunk_type.go
create mode 100644 server/vendor/github.com/pion/sctp/error_cause_user_initiated_abort.go
create mode 100644 server/vendor/github.com/pion/sctp/packet.go
create mode 100644 server/vendor/github.com/pion/sctp/param.go
create mode 100644 server/vendor/github.com/pion/sctp/param_chunk_list.go
create mode 100644 server/vendor/github.com/pion/sctp/param_ecn_capable.go
create mode 100644 server/vendor/github.com/pion/sctp/param_forward_tsn_supported.go
create mode 100644 server/vendor/github.com/pion/sctp/param_heartbeat_info.go
create mode 100644 server/vendor/github.com/pion/sctp/param_outgoing_reset_request.go
create mode 100644 server/vendor/github.com/pion/sctp/param_random.go
create mode 100644 server/vendor/github.com/pion/sctp/param_reconfig_response.go
create mode 100644 server/vendor/github.com/pion/sctp/param_requested_hmac_algorithm.go
create mode 100644 server/vendor/github.com/pion/sctp/param_state_cookie.go
create mode 100644 server/vendor/github.com/pion/sctp/param_supported_extensions.go
create mode 100644 server/vendor/github.com/pion/sctp/param_zero_checksum.go
create mode 100644 server/vendor/github.com/pion/sctp/paramheader.go
create mode 100644 server/vendor/github.com/pion/sctp/paramtype.go
create mode 100644 server/vendor/github.com/pion/sctp/payload_queue.go
create mode 100644 server/vendor/github.com/pion/sctp/pending_queue.go
create mode 100644 server/vendor/github.com/pion/sctp/queue.go
create mode 100644 server/vendor/github.com/pion/sctp/reassembly_queue.go
create mode 100644 server/vendor/github.com/pion/sctp/receive_payload_queue.go
create mode 100644 server/vendor/github.com/pion/sctp/renovate.json
create mode 100644 server/vendor/github.com/pion/sctp/rtx_timer.go
create mode 100644 server/vendor/github.com/pion/sctp/sctp.go
create mode 100644 server/vendor/github.com/pion/sctp/stream.go
create mode 100644 server/vendor/github.com/pion/sctp/util.go
create mode 100644 server/vendor/github.com/pion/sdp/v3/.gitignore
create mode 100644 server/vendor/github.com/pion/sdp/v3/.golangci.yml
create mode 100644 server/vendor/github.com/pion/sdp/v3/.goreleaser.yml
create mode 100644 server/vendor/github.com/pion/sdp/v3/LICENSE
create mode 100644 server/vendor/github.com/pion/sdp/v3/README.md
create mode 100644 server/vendor/github.com/pion/sdp/v3/base_lexer.go
create mode 100644 server/vendor/github.com/pion/sdp/v3/codecov.yml
create mode 100644 server/vendor/github.com/pion/sdp/v3/common_description.go
create mode 100644 server/vendor/github.com/pion/sdp/v3/direction.go
create mode 100644 server/vendor/github.com/pion/sdp/v3/extmap.go
create mode 100644 server/vendor/github.com/pion/sdp/v3/jsep.go
create mode 100644 server/vendor/github.com/pion/sdp/v3/marshal.go
create mode 100644 server/vendor/github.com/pion/sdp/v3/media_description.go
create mode 100644 server/vendor/github.com/pion/sdp/v3/renovate.json
create mode 100644 server/vendor/github.com/pion/sdp/v3/sdp.go
create mode 100644 server/vendor/github.com/pion/sdp/v3/session_description.go
create mode 100644 server/vendor/github.com/pion/sdp/v3/time_description.go
create mode 100644 server/vendor/github.com/pion/sdp/v3/unmarshal.go
create mode 100644 server/vendor/github.com/pion/sdp/v3/unmarshal_cache.go
create mode 100644 server/vendor/github.com/pion/sdp/v3/util.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/.gitignore
create mode 100644 server/vendor/github.com/pion/srtp/v2/.golangci.yml
create mode 100644 server/vendor/github.com/pion/srtp/v2/.goreleaser.yml
create mode 100644 server/vendor/github.com/pion/srtp/v2/AUTHORS.txt
create mode 100644 server/vendor/github.com/pion/srtp/v2/LICENSE
create mode 100644 server/vendor/github.com/pion/srtp/v2/README.md
create mode 100644 server/vendor/github.com/pion/srtp/v2/codecov.yml
create mode 100644 server/vendor/github.com/pion/srtp/v2/context.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/crypto.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/errors.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/key_derivation.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/keying.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/option.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/protection_profile.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/renovate.json
create mode 100644 server/vendor/github.com/pion/srtp/v2/session.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/session_srtcp.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/session_srtp.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/srtcp.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/srtp.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/srtp_cipher.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/srtp_cipher_aead_aes_gcm.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/srtp_cipher_aes_cm_hmac_sha1.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/stream.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/stream_srtcp.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/stream_srtp.go
create mode 100644 server/vendor/github.com/pion/srtp/v2/util.go
create mode 100644 server/vendor/github.com/pion/stun/.gitignore
create mode 100644 server/vendor/github.com/pion/stun/.golangci.yml
create mode 100644 server/vendor/github.com/pion/stun/.goreleaser.yml
create mode 100644 server/vendor/github.com/pion/stun/AUTHORS.txt
create mode 100644 server/vendor/github.com/pion/stun/LICENSE
create mode 100644 server/vendor/github.com/pion/stun/Makefile
create mode 100644 server/vendor/github.com/pion/stun/README.md
create mode 100644 server/vendor/github.com/pion/stun/addr.go
create mode 100644 server/vendor/github.com/pion/stun/agent.go
create mode 100644 server/vendor/github.com/pion/stun/attributes.go
create mode 100644 server/vendor/github.com/pion/stun/attributes_debug.go
create mode 100644 server/vendor/github.com/pion/stun/checks.go
create mode 100644 server/vendor/github.com/pion/stun/checks_debug.go
create mode 100644 server/vendor/github.com/pion/stun/client.go
create mode 100644 server/vendor/github.com/pion/stun/codecov.yml
create mode 100644 server/vendor/github.com/pion/stun/errorcode.go
create mode 100644 server/vendor/github.com/pion/stun/errors.go
create mode 100644 server/vendor/github.com/pion/stun/fingerprint.go
create mode 100644 server/vendor/github.com/pion/stun/fingerprint_debug.go
create mode 100644 server/vendor/github.com/pion/stun/helpers.go
create mode 100644 server/vendor/github.com/pion/stun/integrity.go
create mode 100644 server/vendor/github.com/pion/stun/integrity_debug.go
create mode 100644 server/vendor/github.com/pion/stun/internal/hmac/hmac.go
create mode 100644 server/vendor/github.com/pion/stun/internal/hmac/pool.go
create mode 100644 server/vendor/github.com/pion/stun/internal/hmac/vendor.sh
create mode 100644 server/vendor/github.com/pion/stun/message.go
create mode 100644 server/vendor/github.com/pion/stun/renovate.json
create mode 100644 server/vendor/github.com/pion/stun/stun.go
create mode 100644 server/vendor/github.com/pion/stun/textattrs.go
create mode 100644 server/vendor/github.com/pion/stun/uattrs.go
create mode 100644 server/vendor/github.com/pion/stun/uri.go
create mode 100644 server/vendor/github.com/pion/stun/xoraddr.go
create mode 100644 server/vendor/github.com/pion/transport/v2/.gitignore
create mode 100644 server/vendor/github.com/pion/transport/v2/.golangci.yml
create mode 100644 server/vendor/github.com/pion/transport/v2/.goreleaser.yml
create mode 100644 server/vendor/github.com/pion/transport/v2/AUTHORS.txt
create mode 100644 server/vendor/github.com/pion/transport/v2/LICENSE
create mode 100644 server/vendor/github.com/pion/transport/v2/README.md
create mode 100644 server/vendor/github.com/pion/transport/v2/codecov.yml
create mode 100644 server/vendor/github.com/pion/transport/v2/connctx/connctx.go
create mode 100644 server/vendor/github.com/pion/transport/v2/connctx/pipe.go
create mode 100644 server/vendor/github.com/pion/transport/v2/deadline/deadline.go
create mode 100644 server/vendor/github.com/pion/transport/v2/deadline/timer.go
create mode 100644 server/vendor/github.com/pion/transport/v2/deadline/timer_generic.go
create mode 100644 server/vendor/github.com/pion/transport/v2/deadline/timer_js.go
create mode 100644 server/vendor/github.com/pion/transport/v2/net.go
create mode 100644 server/vendor/github.com/pion/transport/v2/packetio/buffer.go
create mode 100644 server/vendor/github.com/pion/transport/v2/packetio/errors.go
create mode 100644 server/vendor/github.com/pion/transport/v2/packetio/hardlimit.go
create mode 100644 server/vendor/github.com/pion/transport/v2/packetio/no_hardlimit.go
create mode 100644 server/vendor/github.com/pion/transport/v2/renovate.json
create mode 100644 server/vendor/github.com/pion/transport/v2/replaydetector/fixedbig.go
create mode 100644 server/vendor/github.com/pion/transport/v2/replaydetector/replaydetector.go
create mode 100644 server/vendor/github.com/pion/transport/v2/stdnet/net.go
create mode 100644 server/vendor/github.com/pion/transport/v2/udp/batchconn.go
create mode 100644 server/vendor/github.com/pion/transport/v2/udp/conn.go
create mode 100644 server/vendor/github.com/pion/transport/v2/utils/xor/xor_amd64.go
create mode 100644 server/vendor/github.com/pion/transport/v2/utils/xor/xor_amd64.s
create mode 100644 server/vendor/github.com/pion/transport/v2/utils/xor/xor_arm.go
create mode 100644 server/vendor/github.com/pion/transport/v2/utils/xor/xor_arm.s
create mode 100644 server/vendor/github.com/pion/transport/v2/utils/xor/xor_arm64.go
create mode 100644 server/vendor/github.com/pion/transport/v2/utils/xor/xor_arm64.s
create mode 100644 server/vendor/github.com/pion/transport/v2/utils/xor/xor_generic.go
create mode 100644 server/vendor/github.com/pion/transport/v2/utils/xor/xor_ppc64x.go
create mode 100644 server/vendor/github.com/pion/transport/v2/utils/xor/xor_ppc64x.s
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/.gitignore
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/README.md
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/chunk.go
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/chunk_queue.go
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/conn.go
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/conn_map.go
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/delay_filter.go
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/errors.go
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/loss_filter.go
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/nat.go
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/net.go
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/resolver.go
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/router.go
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/tbf.go
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/udpproxy.go
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/udpproxy_direct.go
create mode 100644 server/vendor/github.com/pion/transport/v2/vnet/vnet.go
create mode 100644 server/vendor/github.com/pion/turn/v2/.gitignore
create mode 100644 server/vendor/github.com/pion/turn/v2/.golangci.yml
create mode 100644 server/vendor/github.com/pion/turn/v2/.goreleaser.yml
create mode 100644 server/vendor/github.com/pion/turn/v2/AUTHORS.txt
create mode 100644 server/vendor/github.com/pion/turn/v2/LICENSE
create mode 100644 server/vendor/github.com/pion/turn/v2/README.md
create mode 100644 server/vendor/github.com/pion/turn/v2/client.go
create mode 100644 server/vendor/github.com/pion/turn/v2/codecov.yml
create mode 100644 server/vendor/github.com/pion/turn/v2/errors.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/allocation/allocation.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/allocation/allocation_manager.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/allocation/channel_bind.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/allocation/errors.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/allocation/five_tuple.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/allocation/permission.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/client/allocation.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/client/binding.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/client/client.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/client/errors.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/client/periodic_timer.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/client/permission.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/client/tcp_alloc.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/client/tcp_conn.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/client/transaction.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/client/trylock.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/client/udp_conn.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/ipnet/util.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/proto/addr.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/proto/chandata.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/proto/chann.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/proto/connection_id.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/proto/data.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/proto/dontfrag.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/proto/evenport.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/proto/lifetime.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/proto/peeraddr.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/proto/proto.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/proto/relayedaddr.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/proto/reqfamily.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/proto/reqtrans.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/proto/rsrvtoken.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/server/errors.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/server/nonce.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/server/server.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/server/stun.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/server/turn.go
create mode 100644 server/vendor/github.com/pion/turn/v2/internal/server/util.go
create mode 100644 server/vendor/github.com/pion/turn/v2/lt_cred.go
create mode 100644 server/vendor/github.com/pion/turn/v2/relay_address_generator_none.go
create mode 100644 server/vendor/github.com/pion/turn/v2/relay_address_generator_range.go
create mode 100644 server/vendor/github.com/pion/turn/v2/relay_address_generator_static.go
create mode 100644 server/vendor/github.com/pion/turn/v2/renovate.json
create mode 100644 server/vendor/github.com/pion/turn/v2/server.go
create mode 100644 server/vendor/github.com/pion/turn/v2/server_config.go
create mode 100644 server/vendor/github.com/pion/turn/v2/stun_conn.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/.codacy.yaml
create mode 100644 server/vendor/github.com/pion/webrtc/v3/.eslintrc.json
create mode 100644 server/vendor/github.com/pion/webrtc/v3/.gitignore
create mode 100644 server/vendor/github.com/pion/webrtc/v3/.golangci.yml
create mode 100644 server/vendor/github.com/pion/webrtc/v3/.goreleaser.yml
create mode 100644 server/vendor/github.com/pion/webrtc/v3/AUTHORS.txt
create mode 100644 server/vendor/github.com/pion/webrtc/v3/DESIGN.md
create mode 100644 server/vendor/github.com/pion/webrtc/v3/LICENSE
create mode 100644 server/vendor/github.com/pion/webrtc/v3/README.md
create mode 100644 server/vendor/github.com/pion/webrtc/v3/api.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/api_js.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/atomicbool.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/bundlepolicy.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/certificate.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/codecov.yml
create mode 100644 server/vendor/github.com/pion/webrtc/v3/configuration.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/configuration_common.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/configuration_js.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/constants.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/datachannel.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/datachannel_js.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/datachannel_js_detach.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/datachannelinit.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/datachannelmessage.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/datachannelparameters.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/datachannelstate.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/dtlsfingerprint.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/dtlsparameters.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/dtlsrole.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/dtlstransport.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/dtlstransport_js.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/dtlstransportstate.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/errors.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/gathering_complete_promise.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/ice_go.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icecandidate.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icecandidateinit.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icecandidatepair.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icecandidatetype.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icecomponent.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/iceconnectionstate.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icecredentialtype.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icegatherer.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icegathererstate.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icegatheringstate.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icegatheroptions.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icemux.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/iceparameters.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/iceprotocol.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icerole.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/iceserver.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/iceserver_js.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icetransport.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icetransport_js.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icetransportpolicy.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/icetransportstate.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/interceptor.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/internal/fmtp/av1.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/internal/fmtp/fmtp.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/internal/fmtp/h264.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/internal/fmtp/vp9.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/internal/mux/endpoint.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/internal/mux/mux.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/internal/mux/muxfunc.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/internal/util/util.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/js_utils.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/mediaengine.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/networktype.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/oauthcredential.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/offeransweroptions.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/operations.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/package.json
create mode 100644 server/vendor/github.com/pion/webrtc/v3/peerconnection.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/peerconnection_js.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/peerconnectionstate.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/pkg/media/media.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/pkg/rtcerr/errors.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/renovate.json
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtcpfeedback.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtcpmuxpolicy.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtpcapabilities.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtpcodec.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtpcodingparameters.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtpdecodingparameters.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtpencodingparameters.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtpreceiveparameters.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtpreceiver.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtpreceiver_go.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtpreceiver_js.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtpsender.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtpsender_js.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtpsendparameters.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtptransceiver.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtptransceiver_js.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtptransceiverdirection.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/rtptransceiverinit.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/sctpcapabilities.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/sctptransport.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/sctptransport_js.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/sctptransportstate.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/sdp.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/sdpsemantics.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/sdptype.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/sessiondescription.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/settingengine.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/settingengine_js.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/signalingstate.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/srtp_writer_future.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/stats.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/stats_go.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/track_local.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/track_local_static.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/track_remote.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/webrtc.go
create mode 100644 server/vendor/github.com/pion/webrtc/v3/yarn.lock
create mode 100644 server/vendor/github.com/pmezard/go-difflib/LICENSE
create mode 100644 server/vendor/github.com/pmezard/go-difflib/difflib/difflib.go
create mode 100644 server/vendor/github.com/stretchr/testify/LICENSE
create mode 100644 server/vendor/github.com/stretchr/testify/assert/assertion_compare.go
create mode 100644 server/vendor/github.com/stretchr/testify/assert/assertion_format.go
create mode 100644 server/vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl
create mode 100644 server/vendor/github.com/stretchr/testify/assert/assertion_forward.go
create mode 100644 server/vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl
create mode 100644 server/vendor/github.com/stretchr/testify/assert/assertion_order.go
create mode 100644 server/vendor/github.com/stretchr/testify/assert/assertions.go
create mode 100644 server/vendor/github.com/stretchr/testify/assert/doc.go
create mode 100644 server/vendor/github.com/stretchr/testify/assert/errors.go
create mode 100644 server/vendor/github.com/stretchr/testify/assert/forward_assertions.go
create mode 100644 server/vendor/github.com/stretchr/testify/assert/http_assertions.go
create mode 100644 server/vendor/github.com/stretchr/testify/require/doc.go
create mode 100644 server/vendor/github.com/stretchr/testify/require/forward_requirements.go
create mode 100644 server/vendor/github.com/stretchr/testify/require/require.go
create mode 100644 server/vendor/github.com/stretchr/testify/require/require.go.tmpl
create mode 100644 server/vendor/github.com/stretchr/testify/require/require_forward.go
create mode 100644 server/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl
create mode 100644 server/vendor/github.com/stretchr/testify/require/requirements.go
create mode 100644 server/vendor/github.com/wlynxg/anet/.gitignore
create mode 100644 server/vendor/github.com/wlynxg/anet/LICENSE
create mode 100644 server/vendor/github.com/wlynxg/anet/README.md
create mode 100644 server/vendor/github.com/wlynxg/anet/README_zh.md
create mode 100644 server/vendor/github.com/wlynxg/anet/interface.go
create mode 100644 server/vendor/github.com/wlynxg/anet/interface_android.go
create mode 100644 server/vendor/github.com/wlynxg/anet/netlink_android.go
create mode 100644 server/vendor/golang.org/x/crypto/cryptobyte/asn1.go
create mode 100644 server/vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go
create mode 100644 server/vendor/golang.org/x/crypto/cryptobyte/builder.go
create mode 100644 server/vendor/golang.org/x/crypto/cryptobyte/string.go
create mode 100644 server/vendor/golang.org/x/crypto/curve25519/curve25519.go
create mode 100644 server/vendor/golang.org/x/net/bpf/asm.go
create mode 100644 server/vendor/golang.org/x/net/bpf/constants.go
create mode 100644 server/vendor/golang.org/x/net/bpf/doc.go
create mode 100644 server/vendor/golang.org/x/net/bpf/instructions.go
create mode 100644 server/vendor/golang.org/x/net/bpf/setter.go
create mode 100644 server/vendor/golang.org/x/net/bpf/vm.go
create mode 100644 server/vendor/golang.org/x/net/bpf/vm_instructions.go
create mode 100644 server/vendor/golang.org/x/net/dns/dnsmessage/message.go
create mode 100644 server/vendor/golang.org/x/net/internal/iana/const.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/cmsghdr.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/cmsghdr_bsd.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/cmsghdr_stub.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/cmsghdr_zos_s390x.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/complete_dontwait.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/complete_nodontwait.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/empty.s
create mode 100644 server/vendor/golang.org/x/net/internal/socket/error_unix.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/error_windows.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/iovec_32bit.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/iovec_64bit.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/iovec_solaris_64bit.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/iovec_stub.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/mmsghdr_stub.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/msghdr_bsd.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/msghdr_bsdvar.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/msghdr_linux.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/msghdr_linux_32bit.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/msghdr_openbsd.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/msghdr_stub.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/msghdr_zos_s390x.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/norace.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/race.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/rawconn.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/rawconn_msg.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/rawconn_nommsg.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/rawconn_nomsg.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/socket.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_bsd.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_const_unix.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_386.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_386.s
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_amd64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_arm.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_arm64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_loong64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_mips.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_mips64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_mips64le.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_mipsle.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_ppc.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_ppc64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_ppc64le.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_s390x.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_linux_s390x.s
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_netbsd.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_posix.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_stub.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_unix.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_windows.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_zos_s390x.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/sys_zos_s390x.s
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_darwin_amd64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_darwin_arm64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_dragonfly_amd64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_freebsd_386.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_freebsd_amd64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_freebsd_arm.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_freebsd_arm64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_freebsd_riscv64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_linux_386.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_linux_amd64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_linux_arm.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_linux_arm64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_linux_loong64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_linux_mips.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_linux_mips64le.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_linux_mipsle.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_linux_ppc64le.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_linux_s390x.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_netbsd_386.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_netbsd_amd64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_netbsd_arm64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_openbsd_386.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_openbsd_amd64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_openbsd_arm.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_openbsd_arm64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_openbsd_mips64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_openbsd_ppc64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_openbsd_riscv64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_solaris_amd64.go
create mode 100644 server/vendor/golang.org/x/net/internal/socket/zsys_zos_s390x.go
create mode 100644 server/vendor/golang.org/x/net/internal/socks/client.go
create mode 100644 server/vendor/golang.org/x/net/internal/socks/socks.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/batch.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/control.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/control_bsd.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/control_pktinfo.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/control_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/control_unix.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/control_windows.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/control_zos.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/dgramopt.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/doc.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/endpoint.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/genericopt.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/header.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/helper.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/iana.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/icmp.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/icmp_linux.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/icmp_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/packet.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/payload.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/payload_cmsg.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/payload_nocmsg.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sockopt.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sockopt_posix.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sockopt_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_aix.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_asmreq.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_asmreq_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_asmreqn.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_asmreqn_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_bpf.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_bpf_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_bsd.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_darwin.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_dragonfly.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_freebsd.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_linux.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_solaris.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_ssmreq.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_ssmreq_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_windows.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/sys_zos.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_aix_ppc64.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_darwin.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_dragonfly.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_freebsd_386.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_freebsd_amd64.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_freebsd_arm.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_freebsd_arm64.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_freebsd_riscv64.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_linux_386.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_linux_amd64.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_linux_arm.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_linux_arm64.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_linux_loong64.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_linux_mips.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_linux_mips64.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_linux_mips64le.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_linux_mipsle.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_linux_ppc.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_linux_ppc64.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_linux_ppc64le.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_linux_s390x.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_netbsd.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_openbsd.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_solaris.go
create mode 100644 server/vendor/golang.org/x/net/ipv4/zsys_zos_s390x.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/batch.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/control.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/control_rfc2292_unix.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/control_rfc3542_unix.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/control_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/control_unix.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/control_windows.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/dgramopt.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/doc.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/endpoint.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/genericopt.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/header.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/helper.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/iana.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/icmp.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/icmp_bsd.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/icmp_linux.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/icmp_solaris.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/icmp_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/icmp_windows.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/icmp_zos.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/payload.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/payload_cmsg.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/payload_nocmsg.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sockopt.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sockopt_posix.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sockopt_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_aix.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_asmreq.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_asmreq_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_bpf.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_bpf_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_bsd.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_darwin.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_freebsd.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_linux.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_solaris.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_ssmreq.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_ssmreq_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_stub.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_windows.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/sys_zos.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_aix_ppc64.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_darwin.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_dragonfly.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_freebsd_386.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_freebsd_amd64.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_freebsd_arm.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_freebsd_arm64.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_freebsd_riscv64.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_linux_386.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_linux_amd64.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_linux_arm.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_linux_arm64.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_linux_loong64.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_linux_mips.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_linux_mips64.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_linux_mips64le.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_linux_mipsle.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_linux_ppc.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_linux_ppc64.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_linux_ppc64le.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_linux_riscv64.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_linux_s390x.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_netbsd.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_openbsd.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_solaris.go
create mode 100644 server/vendor/golang.org/x/net/ipv6/zsys_zos_s390x.go
create mode 100644 server/vendor/golang.org/x/net/proxy/dial.go
create mode 100644 server/vendor/golang.org/x/net/proxy/direct.go
create mode 100644 server/vendor/golang.org/x/net/proxy/per_host.go
create mode 100644 server/vendor/golang.org/x/net/proxy/proxy.go
create mode 100644 server/vendor/golang.org/x/net/proxy/socks5.go
create mode 100644 server/vendor/golang.org/x/sys/windows/aliases.go
create mode 100644 server/vendor/golang.org/x/sys/windows/dll_windows.go
create mode 100644 server/vendor/golang.org/x/sys/windows/env_windows.go
create mode 100644 server/vendor/golang.org/x/sys/windows/eventlog.go
create mode 100644 server/vendor/golang.org/x/sys/windows/exec_windows.go
create mode 100644 server/vendor/golang.org/x/sys/windows/memory_windows.go
create mode 100644 server/vendor/golang.org/x/sys/windows/mkerrors.bash
create mode 100644 server/vendor/golang.org/x/sys/windows/mkknownfolderids.bash
create mode 100644 server/vendor/golang.org/x/sys/windows/mksyscall.go
create mode 100644 server/vendor/golang.org/x/sys/windows/race.go
create mode 100644 server/vendor/golang.org/x/sys/windows/race0.go
create mode 100644 server/vendor/golang.org/x/sys/windows/security_windows.go
create mode 100644 server/vendor/golang.org/x/sys/windows/service.go
create mode 100644 server/vendor/golang.org/x/sys/windows/setupapi_windows.go
create mode 100644 server/vendor/golang.org/x/sys/windows/str.go
create mode 100644 server/vendor/golang.org/x/sys/windows/syscall.go
create mode 100644 server/vendor/golang.org/x/sys/windows/syscall_windows.go
create mode 100644 server/vendor/golang.org/x/sys/windows/types_windows.go
create mode 100644 server/vendor/golang.org/x/sys/windows/types_windows_386.go
create mode 100644 server/vendor/golang.org/x/sys/windows/types_windows_amd64.go
create mode 100644 server/vendor/golang.org/x/sys/windows/types_windows_arm.go
create mode 100644 server/vendor/golang.org/x/sys/windows/types_windows_arm64.go
create mode 100644 server/vendor/golang.org/x/sys/windows/zerrors_windows.go
create mode 100644 server/vendor/golang.org/x/sys/windows/zknownfolderids_windows.go
create mode 100644 server/vendor/golang.org/x/sys/windows/zsyscall_windows.go
create mode 100644 web/src/components/LiveFloatingPlayer.vue
create mode 100644 web/src/utils/liveWebRTC.js
create mode 100644 web/src/views/LiveRoom.vue
diff --git a/admin/src/layouts/AdminLayout.vue b/admin/src/layouts/AdminLayout.vue
index c4aa425..32f1830 100644
--- a/admin/src/layouts/AdminLayout.vue
+++ b/admin/src/layouts/AdminLayout.vue
@@ -43,7 +43,7 @@
+
+
diff --git a/admin/vite.config.js b/admin/vite.config.js
index 61e1127..314b951 100644
--- a/admin/vite.config.js
+++ b/admin/vite.config.js
@@ -22,7 +22,8 @@ export default defineConfig({
'/api': {
target: 'https://yuheng.yuxindazhineng.com',
changeOrigin: true,
- secure: true
+ secure: true,
+ ws: true
}
}
}
diff --git a/nginx/nginx.conf b/nginx/nginx.conf
index 42e4c70..8f7cb4a 100644
--- a/nginx/nginx.conf
+++ b/nginx/nginx.conf
@@ -6,6 +6,18 @@ server {
# ssl_certificate /etc/nginx/ssl/fullchain.pem;
# ssl_certificate_key /etc/nginx/ssl/privkey.pem;
+ location /api/web/live/ws {
+ proxy_pass http://api:9527;
+ proxy_http_version 1.1;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_read_timeout 86400s;
+ }
+
location /api/ {
proxy_pass http://api:9527;
proxy_http_version 1.1;
diff --git a/nginx/yuheng.docker.conf.tpl b/nginx/yuheng.docker.conf.tpl
index 3c99f13..865ac87 100644
--- a/nginx/yuheng.docker.conf.tpl
+++ b/nginx/yuheng.docker.conf.tpl
@@ -34,6 +34,21 @@ server {
return 301 /admin/;
}
+ # WebRTC 直播信令(WebSocket);须 Upgrade,否则握手失败
+ location /api/web/live/ws {
+ set $upstream_api api;
+ proxy_pass http://$upstream_api:8088;
+ proxy_http_version 1.1;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_read_timeout 86400s;
+ proxy_send_timeout 86400s;
+ }
+
location /api/ {
set $upstream_api api;
proxy_pass http://$upstream_api:8088;
diff --git a/nginx/yuheng.yuxindazhineng.com.conf b/nginx/yuheng.yuxindazhineng.com.conf
index 37b5e0e..a9bd51e 100644
--- a/nginx/yuheng.yuxindazhineng.com.conf
+++ b/nginx/yuheng.yuxindazhineng.com.conf
@@ -24,6 +24,19 @@ server {
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
+ # 直播 WebSocket 信令(经 compose 内 Nginx 再到 api)
+ location /api/web/live/ws {
+ proxy_pass http://127.0.0.1:8443;
+ proxy_http_version 1.1;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ proxy_read_timeout 86400s;
+ }
+
location / {
proxy_pass http://127.0.0.1:8443;
proxy_http_version 1.1;
diff --git a/server/go.mod b/server/go.mod
index fafcd07..febf75b 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -11,6 +11,7 @@ require (
require (
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
@@ -18,6 +19,8 @@ require (
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
+ github.com/google/uuid v1.3.1 // indirect
+ github.com/gorilla/websocket v1.5.3 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.6 // indirect
@@ -27,15 +30,34 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
+ github.com/pion/datachannel v1.5.8 // indirect
+ github.com/pion/dtls/v2 v2.2.12 // indirect
+ github.com/pion/ice/v2 v2.3.36 // indirect
+ github.com/pion/interceptor v0.1.29 // indirect
+ github.com/pion/logging v0.2.2 // indirect
+ github.com/pion/mdns v0.0.12 // indirect
+ github.com/pion/randutil v0.1.0 // indirect
+ github.com/pion/rtcp v1.2.14 // indirect
+ github.com/pion/rtp v1.8.7 // indirect
+ github.com/pion/sctp v1.8.19 // indirect
+ github.com/pion/sdp/v3 v3.0.9 // indirect
+ github.com/pion/srtp/v2 v2.0.20 // indirect
+ github.com/pion/stun v0.6.1 // indirect
+ github.com/pion/transport/v2 v2.2.10 // indirect
+ github.com/pion/turn/v2 v2.1.6 // indirect
+ github.com/pion/webrtc/v3 v3.3.5 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/stretchr/testify v1.9.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
+ github.com/wlynxg/anet v0.0.3 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.2.0 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.33.0 // indirect
- golang.org/x/net v0.21.0 // indirect
+ golang.org/x/net v0.22.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0 // indirect
diff --git a/server/go.sum b/server/go.sum
index 0f12dfc..6e1b715 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -30,6 +30,10 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
+github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
+github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -39,6 +43,9 @@ github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6K
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
@@ -50,11 +57,52 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
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/datachannel v1.5.8 h1:ph1P1NsGkazkjrvyMfhRBUAWMxugJjq2HfQifaOoSNo=
+github.com/pion/datachannel v1.5.8/go.mod h1:PgmdpoaNBLX9HNzNClmdki4DYW5JtI7Yibu8QzbL3tI=
+github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
+github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk=
+github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
+github.com/pion/ice/v2 v2.3.36 h1:SopeXiVbbcooUg2EIR8sq4b13RQ8gzrkkldOVg+bBsc=
+github.com/pion/ice/v2 v2.3.36/go.mod h1:mBF7lnigdqgtB+YHkaY/Y6s6tsyRyo4u4rPGRuOjUBQ=
+github.com/pion/interceptor v0.1.29 h1:39fsnlP1U8gw2JzOFWdfCU82vHvhW9o0rZnZF56wF+M=
+github.com/pion/interceptor v0.1.29/go.mod h1:ri+LGNjRUc5xUNtDEPzfdkmSqISixVTBF/z/Zms/6T4=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=
+github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=
+github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
+github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
+github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
+github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=
+github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
+github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
+github.com/pion/rtp v1.8.7 h1:qslKkG8qxvQ7hqaxkmL7Pl0XcUm+/Er7nMnu6Vq+ZxM=
+github.com/pion/rtp v1.8.7/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
+github.com/pion/sctp v1.8.19 h1:2CYuw+SQ5vkQ9t0HdOPccsCz1GQMDuVy5PglLgKVBW8=
+github.com/pion/sctp v1.8.19/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE=
+github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
+github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
+github.com/pion/srtp/v2 v2.0.20 h1:HNNny4s+OUmG280ETrCdgFndp4ufx3/uy85EawYEhTk=
+github.com/pion/srtp/v2 v2.0.20/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=
+github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
+github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
+github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
+github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
+github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
+github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q=
+github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E=
+github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
+github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
+github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc=
+github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
+github.com/pion/webrtc/v3 v3.3.5 h1:ZsSzaMz/i9nblPdiAkZoP+E6Kmjw+jnyq3bEmU3EtRg=
+github.com/pion/webrtc/v3 v3.3.5/go.mod h1:liNa+E1iwyzyXqNUwvoMRNQ10x8h8FOeJKL8RkIbamE=
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -63,10 +111,15 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/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/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+github.com/wlynxg/anet v0.0.3 h1:PvR53psxFXstc12jelG6f1Lv4MWqE0tI76/hHGjh9rg=
+github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
@@ -85,16 +138,28 @@ golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
+golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
+golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
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.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
+golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -103,20 +168,36 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+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.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
@@ -124,6 +205,7 @@ google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cn
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/server/handlers/auth.go b/server/handlers/auth.go
index cd213af..1fd9079 100644
--- a/server/handlers/auth.go
+++ b/server/handlers/auth.go
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"net/http"
+ "strings"
"time"
"go.mongodb.org/mongo-driver/v2/bson"
@@ -34,6 +35,37 @@ type Claims struct {
jwt.RegisteredClaims
}
+// ParseClaimsFromTokenString 解析 Authorization Bearer 或裸 JWT;失败返回 false
+func ParseClaimsFromTokenString(tokenStr string) (*Claims, bool) {
+ tokenStr = strings.TrimSpace(tokenStr)
+ if len(tokenStr) > 7 && strings.EqualFold(tokenStr[:7], "bearer ") {
+ tokenStr = strings.TrimSpace(tokenStr[7:])
+ }
+ if tokenStr == "" {
+ return nil, false
+ }
+ var claims Claims
+ token, err := jwt.ParseWithClaims(tokenStr, &claims, func(t *jwt.Token) (interface{}, error) {
+ return []byte(jwtSecret), nil
+ })
+ if err != nil || !token.Valid {
+ return nil, false
+ }
+ return &claims, true
+}
+
+// LivePublishAllowed 仅允许已登录后台账号发起 WebRTC 推流(与 AuthRequired 身份范围一致)
+func LivePublishAllowed(tokenStr string) bool {
+ claims, ok := ParseClaimsFromTokenString(tokenStr)
+ if !ok {
+ return false
+ }
+ if claims.RoleID != models.RoleIDSuperAdmin && !(claims.RoleID == models.RoleIDSuperUser && claims.Role == "admin") {
+ return false
+ }
+ return true
+}
+
// Login 后台登录,仅 role_id=9527 超级管理员可登录
func Login(c *gin.Context) {
var input LoginInput
@@ -122,25 +154,13 @@ func AuthRequired() gin.HandlerFunc {
if tokenStr == "" {
tokenStr = c.Query("token")
}
- if len(tokenStr) > 7 && tokenStr[:7] == "Bearer " {
- tokenStr = tokenStr[7:]
- }
- if tokenStr == "" {
+ claims, ok := ParseClaimsFromTokenString(tokenStr)
+ if !ok {
c.JSON(http.StatusUnauthorized, gin.H{"error": "请先登录"})
c.Abort()
return
}
- var claims Claims
- token, err := jwt.ParseWithClaims(tokenStr, &claims, func(t *jwt.Token) (interface{}, error) {
- return []byte(jwtSecret), nil
- })
- if err != nil || !token.Valid {
- c.JSON(http.StatusUnauthorized, gin.H{"error": "登录已过期,请重新登录"})
- c.Abort()
- return
- }
-
// 仅超级管理员或超级用户(role_id=0, role=admin)可访问后台
if claims.RoleID != models.RoleIDSuperAdmin && !(claims.RoleID == models.RoleIDSuperUser && claims.Role == "admin") {
c.JSON(http.StatusForbidden, gin.H{"error": "无后台访问权限"})
@@ -164,25 +184,13 @@ func SuperUserAuthRequired() gin.HandlerFunc {
if tokenStr == "" {
tokenStr = c.Query("token")
}
- if len(tokenStr) > 7 && tokenStr[:7] == "Bearer " {
- tokenStr = tokenStr[7:]
- }
- if tokenStr == "" {
+ claims, ok := ParseClaimsFromTokenString(tokenStr)
+ if !ok {
c.JSON(http.StatusUnauthorized, gin.H{"error": "请先登录"})
c.Abort()
return
}
- var claims Claims
- token, err := jwt.ParseWithClaims(tokenStr, &claims, func(t *jwt.Token) (interface{}, error) {
- return []byte(jwtSecret), nil
- })
- if err != nil || !token.Valid {
- c.JSON(http.StatusUnauthorized, gin.H{"error": "登录已过期,请重新登录"})
- c.Abort()
- return
- }
-
// 仅 role_id=9527 且 role=admin 可配置短信平台
if claims.RoleID != models.RoleIDSuperAdmin || claims.Role != "admin" {
c.JSON(http.StatusForbidden, gin.H{"error": "仅超级管理员可配置短信平台"})
diff --git a/server/handlers/homepage.go b/server/handlers/homepage.go
index d2b693f..34e2a4a 100644
--- a/server/handlers/homepage.go
+++ b/server/handlers/homepage.go
@@ -204,7 +204,9 @@ func defaultHomepageData() models.HomepageData {
{Title: "量子同步", Desc: "跨维度数据同步技术,您的数据在多宇宙中保持一致"},
{Title: "星际防护", Desc: "来自未来的安全加密协议,守护您的数字资产安全"},
},
- FooterText: "© 2024 宇恒一号 · 成都宇信达智能科技有限公司",
+ FooterText: "© 2024 宇恒一号 · 成都宇信达智能科技有限公司",
+ LiveRoomURL: "",
+ LiveRoomTitle: "视频直播",
}
}
diff --git a/server/main.go b/server/main.go
index 64bc887..41fc6c9 100644
--- a/server/main.go
+++ b/server/main.go
@@ -15,6 +15,7 @@ import (
"yh_web/server/models"
"yh_web/server/pkg/logger"
"yh_web/server/pkg/schema"
+ "yh_web/server/pkg/weblive"
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
@@ -219,6 +220,8 @@ func main() {
web.GET("/sites/:site_id/promotion-media/*filepath", handlers.ServePromotionMedia)
// 可下载资源公开下载(首页等链接指向此路径)
web.GET("/sites/:site_id/assets/:asset_id/download", handlers.DownloadSiteAsset)
+ // 站内 WebRTC 直播:信令 + 状态(单房间 MVP)
+ weblive.RegisterRoutes(web)
}
port := os.Getenv("PORT")
diff --git a/server/models/site.go b/server/models/site.go
index 338eecc..37480bf 100644
--- a/server/models/site.go
+++ b/server/models/site.go
@@ -45,6 +45,10 @@ type HomepageData struct {
DownloadAndroidURL string `json:"download_android_url,omitempty"`
// BodyBuilder 首页下方扩展区:与网页积木相同 JSON 字符串 {"version":1,"blocks":[...]},空则仅展示上方模板
BodyBuilder string `json:"body_builder,omitempty"`
+ // 直播:前台 /live 页「进入直播间」跳转的外部地址(抖音/B 站/自建 H5 等);留空则仅提示在后台配置
+ LiveRoomURL string `json:"live_room_url,omitempty"`
+ // 直播页与首页直播模块主标题
+ LiveRoomTitle string `json:"live_room_title,omitempty"`
}
type NavLink struct {
diff --git a/server/pkg/weblive/config.go b/server/pkg/weblive/config.go
new file mode 100644
index 0000000..29dc3f7
--- /dev/null
+++ b/server/pkg/weblive/config.go
@@ -0,0 +1,33 @@
+package weblive
+
+import (
+ "encoding/json"
+ "os"
+ "strings"
+
+ "github.com/pion/webrtc/v3"
+)
+
+// MediaEngine 与 API 构建(全局复用,避免重复注册编解码器)
+func buildAPI() (*webrtc.API, error) {
+ m := &webrtc.MediaEngine{}
+ if err := m.RegisterDefaultCodecs(); err != nil {
+ return nil, err
+ }
+ api := webrtc.NewAPI(webrtc.WithMediaEngine(m))
+ return api, nil
+}
+
+func iceServersFromEnv() []webrtc.ICEServer {
+ raw := strings.TrimSpace(os.Getenv("LIVE_ICE_SERVERS"))
+ if raw == "" {
+ return []webrtc.ICEServer{
+ {URLs: []string{"stun:stun.l.google.com:19302"}},
+ }
+ }
+ var servers []webrtc.ICEServer
+ if err := json.Unmarshal([]byte(raw), &servers); err != nil {
+ return []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}}
+ }
+ return servers
+}
diff --git a/server/pkg/weblive/hub.go b/server/pkg/weblive/hub.go
new file mode 100644
index 0000000..84920b5
--- /dev/null
+++ b/server/pkg/weblive/hub.go
@@ -0,0 +1,176 @@
+package weblive
+
+import (
+ "sync"
+
+ "github.com/gorilla/websocket"
+ "github.com/pion/rtp"
+ "github.com/pion/webrtc/v3"
+)
+
+// trackForwarder 从主播轨读 RTP,复制到所有观众本地轨
+type trackForwarder struct {
+ remote *webrtc.TrackRemote
+ mu sync.Mutex
+ locals map[string]*webrtc.TrackLocalStaticRTP
+ stopCh chan struct{}
+}
+
+func newTrackForwarder(track *webrtc.TrackRemote) *trackForwarder {
+ return &trackForwarder{
+ remote: track,
+ locals: make(map[string]*webrtc.TrackLocalStaticRTP),
+ stopCh: make(chan struct{}),
+ }
+}
+
+func (tf *trackForwarder) addViewer(id string, t *webrtc.TrackLocalStaticRTP) {
+ tf.mu.Lock()
+ defer tf.mu.Unlock()
+ tf.locals[id] = t
+}
+
+func (tf *trackForwarder) removeViewer(id string) {
+ tf.mu.Lock()
+ defer tf.mu.Unlock()
+ delete(tf.locals, id)
+}
+
+func (tf *trackForwarder) close() {
+ select {
+ case <-tf.stopCh:
+ default:
+ close(tf.stopCh)
+ }
+}
+
+func (tf *trackForwarder) runReadLoop() {
+ buf := make([]byte, 1500)
+ for {
+ select {
+ case <-tf.stopCh:
+ return
+ default:
+ }
+ n, _, err := tf.remote.Read(buf)
+ if err != nil {
+ return
+ }
+ tf.mu.Lock()
+ for _, lt := range tf.locals {
+ cp := &rtp.Packet{}
+ if err := cp.Unmarshal(buf[:n]); err != nil {
+ continue
+ }
+ _ = lt.WriteRTP(cp)
+ }
+ tf.mu.Unlock()
+ }
+}
+
+// Hub 单房间:一名主播、多名观众(进程内内存态,重启清空)
+type Hub struct {
+ mu sync.RWMutex
+
+ api *webrtc.API
+ cfg webrtc.Configuration
+
+ publishConn *websocket.Conn
+ pubPC *webrtc.PeerConnection
+ forwarders []*trackForwarder
+
+ viewers map[string]*viewerSession
+}
+
+type viewerSession struct {
+ id string
+ ws *websocket.Conn
+ pc *webrtc.PeerConnection
+ pending []webrtc.ICECandidateInit
+ answered bool
+}
+
+func newHub(api *webrtc.API) *Hub {
+ return &Hub{
+ api: api,
+ cfg: webrtc.Configuration{ICEServers: iceServersFromEnv()},
+ viewers: make(map[string]*viewerSession),
+ }
+}
+
+var (
+ defaultHub *Hub
+ hubOnce sync.Once
+ hubInitErr error
+)
+
+func getHub() (*Hub, error) {
+ hubOnce.Do(func() {
+ var api *webrtc.API
+ api, hubInitErr = buildAPI()
+ if hubInitErr != nil {
+ return
+ }
+ defaultHub = newHub(api)
+ })
+ return defaultHub, hubInitErr
+}
+
+func (h *Hub) clearPublisher() {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+ for _, tf := range h.forwarders {
+ tf.close()
+ }
+ h.forwarders = nil
+ if h.pubPC != nil {
+ _ = h.pubPC.Close()
+ h.pubPC = nil
+ }
+ h.publishConn = nil
+}
+
+func (h *Hub) removeViewer(id string) {
+ h.mu.Lock()
+ vs, ok := h.viewers[id]
+ if ok {
+ delete(h.viewers, id)
+ }
+ for _, tf := range h.forwarders {
+ tf.removeViewer(id)
+ }
+ h.mu.Unlock()
+ if ok && vs != nil && vs.pc != nil {
+ _ = vs.pc.Close()
+ }
+}
+
+func (h *Hub) onPublisherTrack(track *webrtc.TrackRemote) {
+ // 仅转发视频轨,降低协商复杂度
+ if track.Kind() != webrtc.RTPCodecTypeVideo {
+ return
+ }
+ tf := newTrackForwarder(track)
+ h.mu.Lock()
+ h.forwarders = append(h.forwarders, tf)
+ h.mu.Unlock()
+ go tf.runReadLoop()
+ // 观众仅在「已开播」后拉流:首次协商时 attachForwardersToViewerPC 会带上当前全部轨,无需在此重协商
+}
+
+func (h *Hub) attachForwardersToViewerPC(v *viewerSession) {
+ h.mu.RLock()
+ fwd := append([]*trackForwarder(nil), h.forwarders...)
+ h.mu.RUnlock()
+ for _, tf := range fwd {
+ cap := tf.remote.Codec().RTPCodecCapability
+ lt, err := webrtc.NewTrackLocalStaticRTP(cap, tf.remote.ID()+"_"+v.id, tf.remote.StreamID())
+ if err != nil {
+ continue
+ }
+ if _, err := v.pc.AddTrack(lt); err != nil {
+ continue
+ }
+ tf.addViewer(v.id, lt)
+ }
+}
diff --git a/server/pkg/weblive/ws.go b/server/pkg/weblive/ws.go
new file mode 100644
index 0000000..914fecb
--- /dev/null
+++ b/server/pkg/weblive/ws.go
@@ -0,0 +1,278 @@
+package weblive
+
+import (
+ "encoding/json"
+ "log"
+ "net/http"
+ "sync"
+
+ "yh_web/server/handlers"
+
+ "github.com/gin-gonic/gin"
+ "github.com/google/uuid"
+ "github.com/gorilla/websocket"
+ "github.com/pion/webrtc/v3"
+)
+
+var upgrader = websocket.Upgrader{
+ ReadBufferSize: 1024,
+ WriteBufferSize: 1024,
+ CheckOrigin: func(r *http.Request) bool {
+ return true
+ },
+}
+
+type wsEnvelope struct {
+ Type string `json:"type"`
+ SDP string `json:"sdp"`
+ Candidate json.RawMessage `json:"candidate"`
+}
+
+func RegisterRoutes(r gin.IRoutes) {
+ r.GET("/live/status", handleLiveStatus)
+ r.GET("/live/ws", handleLiveWS)
+}
+
+func handleLiveStatus(c *gin.Context) {
+ h, err := getHub()
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, gin.H{"error": "live hub unavailable"})
+ return
+ }
+ h.mu.RLock()
+ live := h.publishConn != nil && h.pubPC != nil && len(h.forwarders) > 0
+ h.mu.RUnlock()
+ c.JSON(http.StatusOK, gin.H{"live": live})
+}
+
+func handleLiveWS(c *gin.Context) {
+ role := c.Query("role")
+ h, err := getHub()
+ if err != nil {
+ c.Status(http.StatusInternalServerError)
+ return
+ }
+ switch role {
+ case "publish":
+ if !handlers.LivePublishAllowed(c.Query("token")) {
+ c.JSON(http.StatusForbidden, gin.H{"error": "请使用管理后台登录后的账号开播(URL 参数 token=JWT)"})
+ return
+ }
+ handlePublisherWS(c, h)
+ case "view":
+ handleViewerWS(c, h)
+ default:
+ c.JSON(http.StatusBadRequest, gin.H{"error": "role must be publish or view"})
+ }
+}
+
+func writeJSON(ws *websocket.Conn, v any) error {
+ b, err := json.Marshal(v)
+ if err != nil {
+ return err
+ }
+ return ws.WriteMessage(websocket.TextMessage, b)
+}
+
+func handlePublisherWS(c *gin.Context, h *Hub) {
+ ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
+ if err != nil {
+ return
+ }
+
+ h.mu.Lock()
+ if h.publishConn != nil {
+ h.mu.Unlock()
+ _ = writeJSON(ws, map[string]string{"type": "error", "message": "已有主播在播,请稍后再试"})
+ _ = ws.Close()
+ return
+ }
+ h.publishConn = ws
+ h.mu.Unlock()
+
+ pc, err := h.api.NewPeerConnection(h.cfg)
+ if err != nil {
+ h.mu.Lock()
+ h.publishConn = nil
+ h.mu.Unlock()
+ _ = ws.Close()
+ return
+ }
+ h.mu.Lock()
+ h.pubPC = pc
+ h.mu.Unlock()
+
+ var iceMu sync.Mutex
+ var iceQueue []webrtc.ICECandidateInit
+
+ sendICE := func(candidate *webrtc.ICECandidate) {
+ if candidate == nil {
+ return
+ }
+ _ = writeJSON(ws, map[string]interface{}{"type": "ice", "candidate": candidate.ToJSON()})
+ }
+
+ pc.OnICECandidate(func(c *webrtc.ICECandidate) { sendICE(c) })
+
+ pc.OnTrack(func(track *webrtc.TrackRemote, _ *webrtc.RTPReceiver) {
+ log.Printf("weblive: publisher track kind=%s", track.Kind().String())
+ h.onPublisherTrack(track)
+ })
+
+ pc.OnConnectionStateChange(func(s webrtc.PeerConnectionState) {
+ if s == webrtc.PeerConnectionStateFailed || s == webrtc.PeerConnectionStateClosed {
+ _ = ws.Close()
+ }
+ })
+
+ defer func() {
+ for _, v := range h.snapshotViewers() {
+ _ = writeJSON(v.ws, map[string]string{"type": "ended", "message": "主播已结束直播"})
+ }
+ h.clearPublisher()
+ _ = ws.Close()
+ }()
+
+ for {
+ _, data, err := ws.ReadMessage()
+ if err != nil {
+ return
+ }
+ var env wsEnvelope
+ if err := json.Unmarshal(data, &env); err != nil {
+ continue
+ }
+ switch env.Type {
+ case "offer":
+ if err := pc.SetRemoteDescription(webrtc.SessionDescription{Type: webrtc.SDPTypeOffer, SDP: env.SDP}); err != nil {
+ _ = writeJSON(ws, map[string]string{"type": "error", "message": "SetRemoteDescription failed"})
+ continue
+ }
+ iceMu.Lock()
+ for _, cand := range iceQueue {
+ _ = pc.AddICECandidate(cand)
+ }
+ iceQueue = nil
+ iceMu.Unlock()
+
+ ans, err := pc.CreateAnswer(nil)
+ if err != nil {
+ continue
+ }
+ if err := pc.SetLocalDescription(ans); err != nil {
+ continue
+ }
+ _ = writeJSON(ws, map[string]string{"type": "answer", "sdp": ans.SDP})
+ case "ice":
+ var init webrtc.ICECandidateInit
+ if err := json.Unmarshal(env.Candidate, &init); err != nil {
+ continue
+ }
+ if pc.RemoteDescription() == nil {
+ iceMu.Lock()
+ iceQueue = append(iceQueue, init)
+ iceMu.Unlock()
+ continue
+ }
+ _ = pc.AddICECandidate(init)
+ }
+ }
+}
+
+func (h *Hub) snapshotViewers() []*viewerSession {
+ h.mu.RLock()
+ defer h.mu.RUnlock()
+ out := make([]*viewerSession, 0, len(h.viewers))
+ for _, v := range h.viewers {
+ out = append(out, v)
+ }
+ return out
+}
+
+func handleViewerWS(c *gin.Context, h *Hub) {
+ ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
+ if err != nil {
+ return
+ }
+ vid := uuid.New().String()
+ vs := &viewerSession{id: vid, ws: ws}
+
+ h.mu.Lock()
+ h.viewers[vid] = vs
+ h.mu.Unlock()
+
+ defer func() {
+ h.removeViewer(vid)
+ _ = ws.Close()
+ }()
+
+ pc, err := h.api.NewPeerConnection(h.cfg)
+ if err != nil {
+ return
+ }
+ vs.pc = pc
+
+ var iceMu sync.Mutex
+ var iceQueue []webrtc.ICECandidateInit
+
+ pc.OnICECandidate(func(cand *webrtc.ICECandidate) {
+ if cand == nil {
+ return
+ }
+ _ = writeJSON(ws, map[string]interface{}{"type": "ice", "candidate": cand.ToJSON()})
+ })
+
+ pc.OnConnectionStateChange(func(s webrtc.PeerConnectionState) {
+ if s == webrtc.PeerConnectionStateFailed || s == webrtc.PeerConnectionStateClosed {
+ _ = ws.Close()
+ }
+ })
+
+ for {
+ _, data, err := ws.ReadMessage()
+ if err != nil {
+ return
+ }
+ var env wsEnvelope
+ if err := json.Unmarshal(data, &env); err != nil {
+ continue
+ }
+ switch env.Type {
+ case "offer":
+ if err := pc.SetRemoteDescription(webrtc.SessionDescription{Type: webrtc.SDPTypeOffer, SDP: env.SDP}); err != nil {
+ _ = writeJSON(ws, map[string]string{"type": "error", "message": "SetRemoteDescription failed"})
+ continue
+ }
+ iceMu.Lock()
+ for _, cand := range iceQueue {
+ _ = pc.AddICECandidate(cand)
+ }
+ iceQueue = nil
+ iceMu.Unlock()
+
+ h.attachForwardersToViewerPC(vs)
+
+ ans, err := pc.CreateAnswer(nil)
+ if err != nil {
+ continue
+ }
+ if err := pc.SetLocalDescription(ans); err != nil {
+ continue
+ }
+ vs.answered = true
+ _ = writeJSON(ws, map[string]string{"type": "answer", "sdp": ans.SDP})
+ case "ice":
+ var init webrtc.ICECandidateInit
+ if err := json.Unmarshal(env.Candidate, &init); err != nil {
+ continue
+ }
+ if pc.RemoteDescription() == nil {
+ iceMu.Lock()
+ iceQueue = append(iceQueue, init)
+ iceMu.Unlock()
+ continue
+ }
+ _ = pc.AddICECandidate(init)
+ }
+ }
+}
diff --git a/server/vendor/github.com/davecgh/go-spew/LICENSE b/server/vendor/github.com/davecgh/go-spew/LICENSE
new file mode 100644
index 0000000..bc52e96
--- /dev/null
+++ b/server/vendor/github.com/davecgh/go-spew/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2012-2016 Dave Collins
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/server/vendor/github.com/davecgh/go-spew/spew/bypass.go b/server/vendor/github.com/davecgh/go-spew/spew/bypass.go
new file mode 100644
index 0000000..7929947
--- /dev/null
+++ b/server/vendor/github.com/davecgh/go-spew/spew/bypass.go
@@ -0,0 +1,145 @@
+// Copyright (c) 2015-2016 Dave Collins
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+// NOTE: Due to the following build constraints, this file will only be compiled
+// when the code is not running on Google App Engine, compiled by GopherJS, and
+// "-tags safe" is not added to the go build command line. The "disableunsafe"
+// tag is deprecated and thus should not be used.
+// Go versions prior to 1.4 are disabled because they use a different layout
+// for interfaces which make the implementation of unsafeReflectValue more complex.
+// +build !js,!appengine,!safe,!disableunsafe,go1.4
+
+package spew
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+const (
+ // UnsafeDisabled is a build-time constant which specifies whether or
+ // not access to the unsafe package is available.
+ UnsafeDisabled = false
+
+ // ptrSize is the size of a pointer on the current arch.
+ ptrSize = unsafe.Sizeof((*byte)(nil))
+)
+
+type flag uintptr
+
+var (
+ // flagRO indicates whether the value field of a reflect.Value
+ // is read-only.
+ flagRO flag
+
+ // flagAddr indicates whether the address of the reflect.Value's
+ // value may be taken.
+ flagAddr flag
+)
+
+// flagKindMask holds the bits that make up the kind
+// part of the flags field. In all the supported versions,
+// it is in the lower 5 bits.
+const flagKindMask = flag(0x1f)
+
+// Different versions of Go have used different
+// bit layouts for the flags type. This table
+// records the known combinations.
+var okFlags = []struct {
+ ro, addr flag
+}{{
+ // From Go 1.4 to 1.5
+ ro: 1 << 5,
+ addr: 1 << 7,
+}, {
+ // Up to Go tip.
+ ro: 1<<5 | 1<<6,
+ addr: 1 << 8,
+}}
+
+var flagValOffset = func() uintptr {
+ field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
+ if !ok {
+ panic("reflect.Value has no flag field")
+ }
+ return field.Offset
+}()
+
+// flagField returns a pointer to the flag field of a reflect.Value.
+func flagField(v *reflect.Value) *flag {
+ return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
+}
+
+// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
+// the typical safety restrictions preventing access to unaddressable and
+// unexported data. It works by digging the raw pointer to the underlying
+// value out of the protected value and generating a new unprotected (unsafe)
+// reflect.Value to it.
+//
+// This allows us to check for implementations of the Stringer and error
+// interfaces to be used for pretty printing ordinarily unaddressable and
+// inaccessible values such as unexported struct fields.
+func unsafeReflectValue(v reflect.Value) reflect.Value {
+ if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
+ return v
+ }
+ flagFieldPtr := flagField(&v)
+ *flagFieldPtr &^= flagRO
+ *flagFieldPtr |= flagAddr
+ return v
+}
+
+// Sanity checks against future reflect package changes
+// to the type or semantics of the Value.flag field.
+func init() {
+ field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
+ if !ok {
+ panic("reflect.Value has no flag field")
+ }
+ if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
+ panic("reflect.Value flag field has changed kind")
+ }
+ type t0 int
+ var t struct {
+ A t0
+ // t0 will have flagEmbedRO set.
+ t0
+ // a will have flagStickyRO set
+ a t0
+ }
+ vA := reflect.ValueOf(t).FieldByName("A")
+ va := reflect.ValueOf(t).FieldByName("a")
+ vt0 := reflect.ValueOf(t).FieldByName("t0")
+
+ // Infer flagRO from the difference between the flags
+ // for the (otherwise identical) fields in t.
+ flagPublic := *flagField(&vA)
+ flagWithRO := *flagField(&va) | *flagField(&vt0)
+ flagRO = flagPublic ^ flagWithRO
+
+ // Infer flagAddr from the difference between a value
+ // taken from a pointer and not.
+ vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
+ flagNoPtr := *flagField(&vA)
+ flagPtr := *flagField(&vPtrA)
+ flagAddr = flagNoPtr ^ flagPtr
+
+ // Check that the inferred flags tally with one of the known versions.
+ for _, f := range okFlags {
+ if flagRO == f.ro && flagAddr == f.addr {
+ return
+ }
+ }
+ panic("reflect.Value read-only flag has changed semantics")
+}
diff --git a/server/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/server/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
new file mode 100644
index 0000000..205c28d
--- /dev/null
+++ b/server/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
@@ -0,0 +1,38 @@
+// Copyright (c) 2015-2016 Dave Collins
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+// NOTE: Due to the following build constraints, this file will only be compiled
+// when the code is running on Google App Engine, compiled by GopherJS, or
+// "-tags safe" is added to the go build command line. The "disableunsafe"
+// tag is deprecated and thus should not be used.
+// +build js appengine safe disableunsafe !go1.4
+
+package spew
+
+import "reflect"
+
+const (
+ // UnsafeDisabled is a build-time constant which specifies whether or
+ // not access to the unsafe package is available.
+ UnsafeDisabled = true
+)
+
+// unsafeReflectValue typically converts the passed reflect.Value into a one
+// that bypasses the typical safety restrictions preventing access to
+// unaddressable and unexported data. However, doing this relies on access to
+// the unsafe package. This is a stub version which simply returns the passed
+// reflect.Value when the unsafe package is not available.
+func unsafeReflectValue(v reflect.Value) reflect.Value {
+ return v
+}
diff --git a/server/vendor/github.com/davecgh/go-spew/spew/common.go b/server/vendor/github.com/davecgh/go-spew/spew/common.go
new file mode 100644
index 0000000..1be8ce9
--- /dev/null
+++ b/server/vendor/github.com/davecgh/go-spew/spew/common.go
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "reflect"
+ "sort"
+ "strconv"
+)
+
+// Some constants in the form of bytes to avoid string overhead. This mirrors
+// the technique used in the fmt package.
+var (
+ panicBytes = []byte("(PANIC=")
+ plusBytes = []byte("+")
+ iBytes = []byte("i")
+ trueBytes = []byte("true")
+ falseBytes = []byte("false")
+ interfaceBytes = []byte("(interface {})")
+ commaNewlineBytes = []byte(",\n")
+ newlineBytes = []byte("\n")
+ openBraceBytes = []byte("{")
+ openBraceNewlineBytes = []byte("{\n")
+ closeBraceBytes = []byte("}")
+ asteriskBytes = []byte("*")
+ colonBytes = []byte(":")
+ colonSpaceBytes = []byte(": ")
+ openParenBytes = []byte("(")
+ closeParenBytes = []byte(")")
+ spaceBytes = []byte(" ")
+ pointerChainBytes = []byte("->")
+ nilAngleBytes = []byte("")
+ maxNewlineBytes = []byte("\n")
+ maxShortBytes = []byte("")
+ circularBytes = []byte("")
+ circularShortBytes = []byte("")
+ invalidAngleBytes = []byte("")
+ openBracketBytes = []byte("[")
+ closeBracketBytes = []byte("]")
+ percentBytes = []byte("%")
+ precisionBytes = []byte(".")
+ openAngleBytes = []byte("<")
+ closeAngleBytes = []byte(">")
+ openMapBytes = []byte("map[")
+ closeMapBytes = []byte("]")
+ lenEqualsBytes = []byte("len=")
+ capEqualsBytes = []byte("cap=")
+)
+
+// hexDigits is used to map a decimal value to a hex digit.
+var hexDigits = "0123456789abcdef"
+
+// catchPanic handles any panics that might occur during the handleMethods
+// calls.
+func catchPanic(w io.Writer, v reflect.Value) {
+ if err := recover(); err != nil {
+ w.Write(panicBytes)
+ fmt.Fprintf(w, "%v", err)
+ w.Write(closeParenBytes)
+ }
+}
+
+// handleMethods attempts to call the Error and String methods on the underlying
+// type the passed reflect.Value represents and outputes the result to Writer w.
+//
+// It handles panics in any called methods by catching and displaying the error
+// as the formatted value.
+func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
+ // We need an interface to check if the type implements the error or
+ // Stringer interface. However, the reflect package won't give us an
+ // interface on certain things like unexported struct fields in order
+ // to enforce visibility rules. We use unsafe, when it's available,
+ // to bypass these restrictions since this package does not mutate the
+ // values.
+ if !v.CanInterface() {
+ if UnsafeDisabled {
+ return false
+ }
+
+ v = unsafeReflectValue(v)
+ }
+
+ // Choose whether or not to do error and Stringer interface lookups against
+ // the base type or a pointer to the base type depending on settings.
+ // Technically calling one of these methods with a pointer receiver can
+ // mutate the value, however, types which choose to satisify an error or
+ // Stringer interface with a pointer receiver should not be mutating their
+ // state inside these interface methods.
+ if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
+ v = unsafeReflectValue(v)
+ }
+ if v.CanAddr() {
+ v = v.Addr()
+ }
+
+ // Is it an error or Stringer?
+ switch iface := v.Interface().(type) {
+ case error:
+ defer catchPanic(w, v)
+ if cs.ContinueOnMethod {
+ w.Write(openParenBytes)
+ w.Write([]byte(iface.Error()))
+ w.Write(closeParenBytes)
+ w.Write(spaceBytes)
+ return false
+ }
+
+ w.Write([]byte(iface.Error()))
+ return true
+
+ case fmt.Stringer:
+ defer catchPanic(w, v)
+ if cs.ContinueOnMethod {
+ w.Write(openParenBytes)
+ w.Write([]byte(iface.String()))
+ w.Write(closeParenBytes)
+ w.Write(spaceBytes)
+ return false
+ }
+ w.Write([]byte(iface.String()))
+ return true
+ }
+ return false
+}
+
+// printBool outputs a boolean value as true or false to Writer w.
+func printBool(w io.Writer, val bool) {
+ if val {
+ w.Write(trueBytes)
+ } else {
+ w.Write(falseBytes)
+ }
+}
+
+// printInt outputs a signed integer value to Writer w.
+func printInt(w io.Writer, val int64, base int) {
+ w.Write([]byte(strconv.FormatInt(val, base)))
+}
+
+// printUint outputs an unsigned integer value to Writer w.
+func printUint(w io.Writer, val uint64, base int) {
+ w.Write([]byte(strconv.FormatUint(val, base)))
+}
+
+// printFloat outputs a floating point value using the specified precision,
+// which is expected to be 32 or 64bit, to Writer w.
+func printFloat(w io.Writer, val float64, precision int) {
+ w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
+}
+
+// printComplex outputs a complex value using the specified float precision
+// for the real and imaginary parts to Writer w.
+func printComplex(w io.Writer, c complex128, floatPrecision int) {
+ r := real(c)
+ w.Write(openParenBytes)
+ w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
+ i := imag(c)
+ if i >= 0 {
+ w.Write(plusBytes)
+ }
+ w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
+ w.Write(iBytes)
+ w.Write(closeParenBytes)
+}
+
+// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
+// prefix to Writer w.
+func printHexPtr(w io.Writer, p uintptr) {
+ // Null pointer.
+ num := uint64(p)
+ if num == 0 {
+ w.Write(nilAngleBytes)
+ return
+ }
+
+ // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
+ buf := make([]byte, 18)
+
+ // It's simpler to construct the hex string right to left.
+ base := uint64(16)
+ i := len(buf) - 1
+ for num >= base {
+ buf[i] = hexDigits[num%base]
+ num /= base
+ i--
+ }
+ buf[i] = hexDigits[num]
+
+ // Add '0x' prefix.
+ i--
+ buf[i] = 'x'
+ i--
+ buf[i] = '0'
+
+ // Strip unused leading bytes.
+ buf = buf[i:]
+ w.Write(buf)
+}
+
+// valuesSorter implements sort.Interface to allow a slice of reflect.Value
+// elements to be sorted.
+type valuesSorter struct {
+ values []reflect.Value
+ strings []string // either nil or same len and values
+ cs *ConfigState
+}
+
+// newValuesSorter initializes a valuesSorter instance, which holds a set of
+// surrogate keys on which the data should be sorted. It uses flags in
+// ConfigState to decide if and how to populate those surrogate keys.
+func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
+ vs := &valuesSorter{values: values, cs: cs}
+ if canSortSimply(vs.values[0].Kind()) {
+ return vs
+ }
+ if !cs.DisableMethods {
+ vs.strings = make([]string, len(values))
+ for i := range vs.values {
+ b := bytes.Buffer{}
+ if !handleMethods(cs, &b, vs.values[i]) {
+ vs.strings = nil
+ break
+ }
+ vs.strings[i] = b.String()
+ }
+ }
+ if vs.strings == nil && cs.SpewKeys {
+ vs.strings = make([]string, len(values))
+ for i := range vs.values {
+ vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
+ }
+ }
+ return vs
+}
+
+// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
+// directly, or whether it should be considered for sorting by surrogate keys
+// (if the ConfigState allows it).
+func canSortSimply(kind reflect.Kind) bool {
+ // This switch parallels valueSortLess, except for the default case.
+ switch kind {
+ case reflect.Bool:
+ return true
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ return true
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ return true
+ case reflect.Float32, reflect.Float64:
+ return true
+ case reflect.String:
+ return true
+ case reflect.Uintptr:
+ return true
+ case reflect.Array:
+ return true
+ }
+ return false
+}
+
+// Len returns the number of values in the slice. It is part of the
+// sort.Interface implementation.
+func (s *valuesSorter) Len() int {
+ return len(s.values)
+}
+
+// Swap swaps the values at the passed indices. It is part of the
+// sort.Interface implementation.
+func (s *valuesSorter) Swap(i, j int) {
+ s.values[i], s.values[j] = s.values[j], s.values[i]
+ if s.strings != nil {
+ s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
+ }
+}
+
+// valueSortLess returns whether the first value should sort before the second
+// value. It is used by valueSorter.Less as part of the sort.Interface
+// implementation.
+func valueSortLess(a, b reflect.Value) bool {
+ switch a.Kind() {
+ case reflect.Bool:
+ return !a.Bool() && b.Bool()
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ return a.Int() < b.Int()
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ return a.Uint() < b.Uint()
+ case reflect.Float32, reflect.Float64:
+ return a.Float() < b.Float()
+ case reflect.String:
+ return a.String() < b.String()
+ case reflect.Uintptr:
+ return a.Uint() < b.Uint()
+ case reflect.Array:
+ // Compare the contents of both arrays.
+ l := a.Len()
+ for i := 0; i < l; i++ {
+ av := a.Index(i)
+ bv := b.Index(i)
+ if av.Interface() == bv.Interface() {
+ continue
+ }
+ return valueSortLess(av, bv)
+ }
+ }
+ return a.String() < b.String()
+}
+
+// Less returns whether the value at index i should sort before the
+// value at index j. It is part of the sort.Interface implementation.
+func (s *valuesSorter) Less(i, j int) bool {
+ if s.strings == nil {
+ return valueSortLess(s.values[i], s.values[j])
+ }
+ return s.strings[i] < s.strings[j]
+}
+
+// sortValues is a sort function that handles both native types and any type that
+// can be converted to error or Stringer. Other inputs are sorted according to
+// their Value.String() value to ensure display stability.
+func sortValues(values []reflect.Value, cs *ConfigState) {
+ if len(values) == 0 {
+ return
+ }
+ sort.Sort(newValuesSorter(values, cs))
+}
diff --git a/server/vendor/github.com/davecgh/go-spew/spew/config.go b/server/vendor/github.com/davecgh/go-spew/spew/config.go
new file mode 100644
index 0000000..2e3d22f
--- /dev/null
+++ b/server/vendor/github.com/davecgh/go-spew/spew/config.go
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+)
+
+// ConfigState houses the configuration options used by spew to format and
+// display values. There is a global instance, Config, that is used to control
+// all top-level Formatter and Dump functionality. Each ConfigState instance
+// provides methods equivalent to the top-level functions.
+//
+// The zero value for ConfigState provides no indentation. You would typically
+// want to set it to a space or a tab.
+//
+// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
+// with default settings. See the documentation of NewDefaultConfig for default
+// values.
+type ConfigState struct {
+ // Indent specifies the string to use for each indentation level. The
+ // global config instance that all top-level functions use set this to a
+ // single space by default. If you would like more indentation, you might
+ // set this to a tab with "\t" or perhaps two spaces with " ".
+ Indent string
+
+ // MaxDepth controls the maximum number of levels to descend into nested
+ // data structures. The default, 0, means there is no limit.
+ //
+ // NOTE: Circular data structures are properly detected, so it is not
+ // necessary to set this value unless you specifically want to limit deeply
+ // nested data structures.
+ MaxDepth int
+
+ // DisableMethods specifies whether or not error and Stringer interfaces are
+ // invoked for types that implement them.
+ DisableMethods bool
+
+ // DisablePointerMethods specifies whether or not to check for and invoke
+ // error and Stringer interfaces on types which only accept a pointer
+ // receiver when the current type is not a pointer.
+ //
+ // NOTE: This might be an unsafe action since calling one of these methods
+ // with a pointer receiver could technically mutate the value, however,
+ // in practice, types which choose to satisify an error or Stringer
+ // interface with a pointer receiver should not be mutating their state
+ // inside these interface methods. As a result, this option relies on
+ // access to the unsafe package, so it will not have any effect when
+ // running in environments without access to the unsafe package such as
+ // Google App Engine or with the "safe" build tag specified.
+ DisablePointerMethods bool
+
+ // DisablePointerAddresses specifies whether to disable the printing of
+ // pointer addresses. This is useful when diffing data structures in tests.
+ DisablePointerAddresses bool
+
+ // DisableCapacities specifies whether to disable the printing of capacities
+ // for arrays, slices, maps and channels. This is useful when diffing
+ // data structures in tests.
+ DisableCapacities bool
+
+ // ContinueOnMethod specifies whether or not recursion should continue once
+ // a custom error or Stringer interface is invoked. The default, false,
+ // means it will print the results of invoking the custom error or Stringer
+ // interface and return immediately instead of continuing to recurse into
+ // the internals of the data type.
+ //
+ // NOTE: This flag does not have any effect if method invocation is disabled
+ // via the DisableMethods or DisablePointerMethods options.
+ ContinueOnMethod bool
+
+ // SortKeys specifies map keys should be sorted before being printed. Use
+ // this to have a more deterministic, diffable output. Note that only
+ // native types (bool, int, uint, floats, uintptr and string) and types
+ // that support the error or Stringer interfaces (if methods are
+ // enabled) are supported, with other types sorted according to the
+ // reflect.Value.String() output which guarantees display stability.
+ SortKeys bool
+
+ // SpewKeys specifies that, as a last resort attempt, map keys should
+ // be spewed to strings and sorted by those strings. This is only
+ // considered if SortKeys is true.
+ SpewKeys bool
+}
+
+// Config is the active configuration of the top-level functions.
+// The configuration can be changed by modifying the contents of spew.Config.
+var Config = ConfigState{Indent: " "}
+
+// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the formatted string as a value that satisfies error. See NewFormatter
+// for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
+ return fmt.Errorf(format, c.convertArgs(a)...)
+}
+
+// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprint(w, c.convertArgs(a)...)
+}
+
+// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
+ return fmt.Fprintf(w, format, c.convertArgs(a)...)
+}
+
+// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
+// passed with a Formatter interface returned by c.NewFormatter. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprintln(w, c.convertArgs(a)...)
+}
+
+// Print is a wrapper for fmt.Print that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
+ return fmt.Print(c.convertArgs(a)...)
+}
+
+// Printf is a wrapper for fmt.Printf that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
+ return fmt.Printf(format, c.convertArgs(a)...)
+}
+
+// Println is a wrapper for fmt.Println that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
+ return fmt.Println(c.convertArgs(a)...)
+}
+
+// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Sprint(a ...interface{}) string {
+ return fmt.Sprint(c.convertArgs(a)...)
+}
+
+// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
+ return fmt.Sprintf(format, c.convertArgs(a)...)
+}
+
+// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
+// were passed with a Formatter interface returned by c.NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Sprintln(a ...interface{}) string {
+ return fmt.Sprintln(c.convertArgs(a)...)
+}
+
+/*
+NewFormatter returns a custom formatter that satisfies the fmt.Formatter
+interface. As a result, it integrates cleanly with standard fmt package
+printing functions. The formatter is useful for inline printing of smaller data
+types similar to the standard %v format specifier.
+
+The custom formatter only responds to the %v (most compact), %+v (adds pointer
+addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
+combinations. Any other verbs such as %x and %q will be sent to the the
+standard fmt package for formatting. In addition, the custom formatter ignores
+the width and precision arguments (however they will still work on the format
+specifiers not handled by the custom formatter).
+
+Typically this function shouldn't be called directly. It is much easier to make
+use of the custom formatter by calling one of the convenience functions such as
+c.Printf, c.Println, or c.Printf.
+*/
+func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
+ return newFormatter(c, v)
+}
+
+// Fdump formats and displays the passed arguments to io.Writer w. It formats
+// exactly the same as Dump.
+func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
+ fdump(c, w, a...)
+}
+
+/*
+Dump displays the passed parameters to standard out with newlines, customizable
+indentation, and additional debug information such as complete types and all
+pointer addresses used to indirect to the final value. It provides the
+following features over the built-in printing facilities provided by the fmt
+package:
+
+ * Pointers are dereferenced and followed
+ * Circular data structures are detected and handled properly
+ * Custom Stringer/error interfaces are optionally invoked, including
+ on unexported types
+ * Custom types which only implement the Stringer/error interfaces via
+ a pointer receiver are optionally invoked when passing non-pointer
+ variables
+ * Byte arrays and slices are dumped like the hexdump -C command which
+ includes offsets, byte values in hex, and ASCII output
+
+The configuration options are controlled by modifying the public members
+of c. See ConfigState for options documentation.
+
+See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
+get the formatted result as a string.
+*/
+func (c *ConfigState) Dump(a ...interface{}) {
+ fdump(c, os.Stdout, a...)
+}
+
+// Sdump returns a string with the passed arguments formatted exactly the same
+// as Dump.
+func (c *ConfigState) Sdump(a ...interface{}) string {
+ var buf bytes.Buffer
+ fdump(c, &buf, a...)
+ return buf.String()
+}
+
+// convertArgs accepts a slice of arguments and returns a slice of the same
+// length with each argument converted to a spew Formatter interface using
+// the ConfigState associated with s.
+func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
+ formatters = make([]interface{}, len(args))
+ for index, arg := range args {
+ formatters[index] = newFormatter(c, arg)
+ }
+ return formatters
+}
+
+// NewDefaultConfig returns a ConfigState with the following default settings.
+//
+// Indent: " "
+// MaxDepth: 0
+// DisableMethods: false
+// DisablePointerMethods: false
+// ContinueOnMethod: false
+// SortKeys: false
+func NewDefaultConfig() *ConfigState {
+ return &ConfigState{Indent: " "}
+}
diff --git a/server/vendor/github.com/davecgh/go-spew/spew/doc.go b/server/vendor/github.com/davecgh/go-spew/spew/doc.go
new file mode 100644
index 0000000..aacaac6
--- /dev/null
+++ b/server/vendor/github.com/davecgh/go-spew/spew/doc.go
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+Package spew implements a deep pretty printer for Go data structures to aid in
+debugging.
+
+A quick overview of the additional features spew provides over the built-in
+printing facilities for Go data types are as follows:
+
+ * Pointers are dereferenced and followed
+ * Circular data structures are detected and handled properly
+ * Custom Stringer/error interfaces are optionally invoked, including
+ on unexported types
+ * Custom types which only implement the Stringer/error interfaces via
+ a pointer receiver are optionally invoked when passing non-pointer
+ variables
+ * Byte arrays and slices are dumped like the hexdump -C command which
+ includes offsets, byte values in hex, and ASCII output (only when using
+ Dump style)
+
+There are two different approaches spew allows for dumping Go data structures:
+
+ * Dump style which prints with newlines, customizable indentation,
+ and additional debug information such as types and all pointer addresses
+ used to indirect to the final value
+ * A custom Formatter interface that integrates cleanly with the standard fmt
+ package and replaces %v, %+v, %#v, and %#+v to provide inline printing
+ similar to the default %v while providing the additional functionality
+ outlined above and passing unsupported format verbs such as %x and %q
+ along to fmt
+
+Quick Start
+
+This section demonstrates how to quickly get started with spew. See the
+sections below for further details on formatting and configuration options.
+
+To dump a variable with full newlines, indentation, type, and pointer
+information use Dump, Fdump, or Sdump:
+ spew.Dump(myVar1, myVar2, ...)
+ spew.Fdump(someWriter, myVar1, myVar2, ...)
+ str := spew.Sdump(myVar1, myVar2, ...)
+
+Alternatively, if you would prefer to use format strings with a compacted inline
+printing style, use the convenience wrappers Printf, Fprintf, etc with
+%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
+%#+v (adds types and pointer addresses):
+ spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+ spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+
+Configuration Options
+
+Configuration of spew is handled by fields in the ConfigState type. For
+convenience, all of the top-level functions use a global state available
+via the spew.Config global.
+
+It is also possible to create a ConfigState instance that provides methods
+equivalent to the top-level functions. This allows concurrent configuration
+options. See the ConfigState documentation for more details.
+
+The following configuration options are available:
+ * Indent
+ String to use for each indentation level for Dump functions.
+ It is a single space by default. A popular alternative is "\t".
+
+ * MaxDepth
+ Maximum number of levels to descend into nested data structures.
+ There is no limit by default.
+
+ * DisableMethods
+ Disables invocation of error and Stringer interface methods.
+ Method invocation is enabled by default.
+
+ * DisablePointerMethods
+ Disables invocation of error and Stringer interface methods on types
+ which only accept pointer receivers from non-pointer variables.
+ Pointer method invocation is enabled by default.
+
+ * DisablePointerAddresses
+ DisablePointerAddresses specifies whether to disable the printing of
+ pointer addresses. This is useful when diffing data structures in tests.
+
+ * DisableCapacities
+ DisableCapacities specifies whether to disable the printing of
+ capacities for arrays, slices, maps and channels. This is useful when
+ diffing data structures in tests.
+
+ * ContinueOnMethod
+ Enables recursion into types after invoking error and Stringer interface
+ methods. Recursion after method invocation is disabled by default.
+
+ * SortKeys
+ Specifies map keys should be sorted before being printed. Use
+ this to have a more deterministic, diffable output. Note that
+ only native types (bool, int, uint, floats, uintptr and string)
+ and types which implement error or Stringer interfaces are
+ supported with other types sorted according to the
+ reflect.Value.String() output which guarantees display
+ stability. Natural map order is used by default.
+
+ * SpewKeys
+ Specifies that, as a last resort attempt, map keys should be
+ spewed to strings and sorted by those strings. This is only
+ considered if SortKeys is true.
+
+Dump Usage
+
+Simply call spew.Dump with a list of variables you want to dump:
+
+ spew.Dump(myVar1, myVar2, ...)
+
+You may also call spew.Fdump if you would prefer to output to an arbitrary
+io.Writer. For example, to dump to standard error:
+
+ spew.Fdump(os.Stderr, myVar1, myVar2, ...)
+
+A third option is to call spew.Sdump to get the formatted output as a string:
+
+ str := spew.Sdump(myVar1, myVar2, ...)
+
+Sample Dump Output
+
+See the Dump example for details on the setup of the types and variables being
+shown here.
+
+ (main.Foo) {
+ unexportedField: (*main.Bar)(0xf84002e210)({
+ flag: (main.Flag) flagTwo,
+ data: (uintptr)
+ }),
+ ExportedField: (map[interface {}]interface {}) (len=1) {
+ (string) (len=3) "one": (bool) true
+ }
+ }
+
+Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
+command as shown.
+ ([]uint8) (len=32 cap=32) {
+ 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
+ 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
+ 00000020 31 32 |12|
+ }
+
+Custom Formatter
+
+Spew provides a custom formatter that implements the fmt.Formatter interface
+so that it integrates cleanly with standard fmt package printing functions. The
+formatter is useful for inline printing of smaller data types similar to the
+standard %v format specifier.
+
+The custom formatter only responds to the %v (most compact), %+v (adds pointer
+addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
+combinations. Any other verbs such as %x and %q will be sent to the the
+standard fmt package for formatting. In addition, the custom formatter ignores
+the width and precision arguments (however they will still work on the format
+specifiers not handled by the custom formatter).
+
+Custom Formatter Usage
+
+The simplest way to make use of the spew custom formatter is to call one of the
+convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
+functions have syntax you are most likely already familiar with:
+
+ spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+ spew.Println(myVar, myVar2)
+ spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+
+See the Index for the full list convenience functions.
+
+Sample Formatter Output
+
+Double pointer to a uint8:
+ %v: <**>5
+ %+v: <**>(0xf8400420d0->0xf8400420c8)5
+ %#v: (**uint8)5
+ %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
+
+Pointer to circular struct with a uint8 field and a pointer to itself:
+ %v: <*>{1 <*>}
+ %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)}
+ %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)}
+ %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)}
+
+See the Printf example for details on the setup of variables being shown
+here.
+
+Errors
+
+Since it is possible for custom Stringer/error interfaces to panic, spew
+detects them and handles them internally by printing the panic information
+inline with the output. Since spew is intended to provide deep pretty printing
+capabilities on structures, it intentionally does not return any errors.
+*/
+package spew
diff --git a/server/vendor/github.com/davecgh/go-spew/spew/dump.go b/server/vendor/github.com/davecgh/go-spew/spew/dump.go
new file mode 100644
index 0000000..f78d89f
--- /dev/null
+++ b/server/vendor/github.com/davecgh/go-spew/spew/dump.go
@@ -0,0 +1,509 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "bytes"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+var (
+ // uint8Type is a reflect.Type representing a uint8. It is used to
+ // convert cgo types to uint8 slices for hexdumping.
+ uint8Type = reflect.TypeOf(uint8(0))
+
+ // cCharRE is a regular expression that matches a cgo char.
+ // It is used to detect character arrays to hexdump them.
+ cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
+
+ // cUnsignedCharRE is a regular expression that matches a cgo unsigned
+ // char. It is used to detect unsigned character arrays to hexdump
+ // them.
+ cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
+
+ // cUint8tCharRE is a regular expression that matches a cgo uint8_t.
+ // It is used to detect uint8_t arrays to hexdump them.
+ cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
+)
+
+// dumpState contains information about the state of a dump operation.
+type dumpState struct {
+ w io.Writer
+ depth int
+ pointers map[uintptr]int
+ ignoreNextType bool
+ ignoreNextIndent bool
+ cs *ConfigState
+}
+
+// indent performs indentation according to the depth level and cs.Indent
+// option.
+func (d *dumpState) indent() {
+ if d.ignoreNextIndent {
+ d.ignoreNextIndent = false
+ return
+ }
+ d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
+}
+
+// unpackValue returns values inside of non-nil interfaces when possible.
+// This is useful for data types like structs, arrays, slices, and maps which
+// can contain varying types packed inside an interface.
+func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
+ if v.Kind() == reflect.Interface && !v.IsNil() {
+ v = v.Elem()
+ }
+ return v
+}
+
+// dumpPtr handles formatting of pointers by indirecting them as necessary.
+func (d *dumpState) dumpPtr(v reflect.Value) {
+ // Remove pointers at or below the current depth from map used to detect
+ // circular refs.
+ for k, depth := range d.pointers {
+ if depth >= d.depth {
+ delete(d.pointers, k)
+ }
+ }
+
+ // Keep list of all dereferenced pointers to show later.
+ pointerChain := make([]uintptr, 0)
+
+ // Figure out how many levels of indirection there are by dereferencing
+ // pointers and unpacking interfaces down the chain while detecting circular
+ // references.
+ nilFound := false
+ cycleFound := false
+ indirects := 0
+ ve := v
+ for ve.Kind() == reflect.Ptr {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ indirects++
+ addr := ve.Pointer()
+ pointerChain = append(pointerChain, addr)
+ if pd, ok := d.pointers[addr]; ok && pd < d.depth {
+ cycleFound = true
+ indirects--
+ break
+ }
+ d.pointers[addr] = d.depth
+
+ ve = ve.Elem()
+ if ve.Kind() == reflect.Interface {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ ve = ve.Elem()
+ }
+ }
+
+ // Display type information.
+ d.w.Write(openParenBytes)
+ d.w.Write(bytes.Repeat(asteriskBytes, indirects))
+ d.w.Write([]byte(ve.Type().String()))
+ d.w.Write(closeParenBytes)
+
+ // Display pointer information.
+ if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
+ d.w.Write(openParenBytes)
+ for i, addr := range pointerChain {
+ if i > 0 {
+ d.w.Write(pointerChainBytes)
+ }
+ printHexPtr(d.w, addr)
+ }
+ d.w.Write(closeParenBytes)
+ }
+
+ // Display dereferenced value.
+ d.w.Write(openParenBytes)
+ switch {
+ case nilFound:
+ d.w.Write(nilAngleBytes)
+
+ case cycleFound:
+ d.w.Write(circularBytes)
+
+ default:
+ d.ignoreNextType = true
+ d.dump(ve)
+ }
+ d.w.Write(closeParenBytes)
+}
+
+// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
+// reflection) arrays and slices are dumped in hexdump -C fashion.
+func (d *dumpState) dumpSlice(v reflect.Value) {
+ // Determine whether this type should be hex dumped or not. Also,
+ // for types which should be hexdumped, try to use the underlying data
+ // first, then fall back to trying to convert them to a uint8 slice.
+ var buf []uint8
+ doConvert := false
+ doHexDump := false
+ numEntries := v.Len()
+ if numEntries > 0 {
+ vt := v.Index(0).Type()
+ vts := vt.String()
+ switch {
+ // C types that need to be converted.
+ case cCharRE.MatchString(vts):
+ fallthrough
+ case cUnsignedCharRE.MatchString(vts):
+ fallthrough
+ case cUint8tCharRE.MatchString(vts):
+ doConvert = true
+
+ // Try to use existing uint8 slices and fall back to converting
+ // and copying if that fails.
+ case vt.Kind() == reflect.Uint8:
+ // We need an addressable interface to convert the type
+ // to a byte slice. However, the reflect package won't
+ // give us an interface on certain things like
+ // unexported struct fields in order to enforce
+ // visibility rules. We use unsafe, when available, to
+ // bypass these restrictions since this package does not
+ // mutate the values.
+ vs := v
+ if !vs.CanInterface() || !vs.CanAddr() {
+ vs = unsafeReflectValue(vs)
+ }
+ if !UnsafeDisabled {
+ vs = vs.Slice(0, numEntries)
+
+ // Use the existing uint8 slice if it can be
+ // type asserted.
+ iface := vs.Interface()
+ if slice, ok := iface.([]uint8); ok {
+ buf = slice
+ doHexDump = true
+ break
+ }
+ }
+
+ // The underlying data needs to be converted if it can't
+ // be type asserted to a uint8 slice.
+ doConvert = true
+ }
+
+ // Copy and convert the underlying type if needed.
+ if doConvert && vt.ConvertibleTo(uint8Type) {
+ // Convert and copy each element into a uint8 byte
+ // slice.
+ buf = make([]uint8, numEntries)
+ for i := 0; i < numEntries; i++ {
+ vv := v.Index(i)
+ buf[i] = uint8(vv.Convert(uint8Type).Uint())
+ }
+ doHexDump = true
+ }
+ }
+
+ // Hexdump the entire slice as needed.
+ if doHexDump {
+ indent := strings.Repeat(d.cs.Indent, d.depth)
+ str := indent + hex.Dump(buf)
+ str = strings.Replace(str, "\n", "\n"+indent, -1)
+ str = strings.TrimRight(str, d.cs.Indent)
+ d.w.Write([]byte(str))
+ return
+ }
+
+ // Recursively call dump for each item.
+ for i := 0; i < numEntries; i++ {
+ d.dump(d.unpackValue(v.Index(i)))
+ if i < (numEntries - 1) {
+ d.w.Write(commaNewlineBytes)
+ } else {
+ d.w.Write(newlineBytes)
+ }
+ }
+}
+
+// dump is the main workhorse for dumping a value. It uses the passed reflect
+// value to figure out what kind of object we are dealing with and formats it
+// appropriately. It is a recursive function, however circular data structures
+// are detected and handled properly.
+func (d *dumpState) dump(v reflect.Value) {
+ // Handle invalid reflect values immediately.
+ kind := v.Kind()
+ if kind == reflect.Invalid {
+ d.w.Write(invalidAngleBytes)
+ return
+ }
+
+ // Handle pointers specially.
+ if kind == reflect.Ptr {
+ d.indent()
+ d.dumpPtr(v)
+ return
+ }
+
+ // Print type information unless already handled elsewhere.
+ if !d.ignoreNextType {
+ d.indent()
+ d.w.Write(openParenBytes)
+ d.w.Write([]byte(v.Type().String()))
+ d.w.Write(closeParenBytes)
+ d.w.Write(spaceBytes)
+ }
+ d.ignoreNextType = false
+
+ // Display length and capacity if the built-in len and cap functions
+ // work with the value's kind and the len/cap itself is non-zero.
+ valueLen, valueCap := 0, 0
+ switch v.Kind() {
+ case reflect.Array, reflect.Slice, reflect.Chan:
+ valueLen, valueCap = v.Len(), v.Cap()
+ case reflect.Map, reflect.String:
+ valueLen = v.Len()
+ }
+ if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
+ d.w.Write(openParenBytes)
+ if valueLen != 0 {
+ d.w.Write(lenEqualsBytes)
+ printInt(d.w, int64(valueLen), 10)
+ }
+ if !d.cs.DisableCapacities && valueCap != 0 {
+ if valueLen != 0 {
+ d.w.Write(spaceBytes)
+ }
+ d.w.Write(capEqualsBytes)
+ printInt(d.w, int64(valueCap), 10)
+ }
+ d.w.Write(closeParenBytes)
+ d.w.Write(spaceBytes)
+ }
+
+ // Call Stringer/error interfaces if they exist and the handle methods flag
+ // is enabled
+ if !d.cs.DisableMethods {
+ if (kind != reflect.Invalid) && (kind != reflect.Interface) {
+ if handled := handleMethods(d.cs, d.w, v); handled {
+ return
+ }
+ }
+ }
+
+ switch kind {
+ case reflect.Invalid:
+ // Do nothing. We should never get here since invalid has already
+ // been handled above.
+
+ case reflect.Bool:
+ printBool(d.w, v.Bool())
+
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ printInt(d.w, v.Int(), 10)
+
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ printUint(d.w, v.Uint(), 10)
+
+ case reflect.Float32:
+ printFloat(d.w, v.Float(), 32)
+
+ case reflect.Float64:
+ printFloat(d.w, v.Float(), 64)
+
+ case reflect.Complex64:
+ printComplex(d.w, v.Complex(), 32)
+
+ case reflect.Complex128:
+ printComplex(d.w, v.Complex(), 64)
+
+ case reflect.Slice:
+ if v.IsNil() {
+ d.w.Write(nilAngleBytes)
+ break
+ }
+ fallthrough
+
+ case reflect.Array:
+ d.w.Write(openBraceNewlineBytes)
+ d.depth++
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
+ d.indent()
+ d.w.Write(maxNewlineBytes)
+ } else {
+ d.dumpSlice(v)
+ }
+ d.depth--
+ d.indent()
+ d.w.Write(closeBraceBytes)
+
+ case reflect.String:
+ d.w.Write([]byte(strconv.Quote(v.String())))
+
+ case reflect.Interface:
+ // The only time we should get here is for nil interfaces due to
+ // unpackValue calls.
+ if v.IsNil() {
+ d.w.Write(nilAngleBytes)
+ }
+
+ case reflect.Ptr:
+ // Do nothing. We should never get here since pointers have already
+ // been handled above.
+
+ case reflect.Map:
+ // nil maps should be indicated as different than empty maps
+ if v.IsNil() {
+ d.w.Write(nilAngleBytes)
+ break
+ }
+
+ d.w.Write(openBraceNewlineBytes)
+ d.depth++
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
+ d.indent()
+ d.w.Write(maxNewlineBytes)
+ } else {
+ numEntries := v.Len()
+ keys := v.MapKeys()
+ if d.cs.SortKeys {
+ sortValues(keys, d.cs)
+ }
+ for i, key := range keys {
+ d.dump(d.unpackValue(key))
+ d.w.Write(colonSpaceBytes)
+ d.ignoreNextIndent = true
+ d.dump(d.unpackValue(v.MapIndex(key)))
+ if i < (numEntries - 1) {
+ d.w.Write(commaNewlineBytes)
+ } else {
+ d.w.Write(newlineBytes)
+ }
+ }
+ }
+ d.depth--
+ d.indent()
+ d.w.Write(closeBraceBytes)
+
+ case reflect.Struct:
+ d.w.Write(openBraceNewlineBytes)
+ d.depth++
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
+ d.indent()
+ d.w.Write(maxNewlineBytes)
+ } else {
+ vt := v.Type()
+ numFields := v.NumField()
+ for i := 0; i < numFields; i++ {
+ d.indent()
+ vtf := vt.Field(i)
+ d.w.Write([]byte(vtf.Name))
+ d.w.Write(colonSpaceBytes)
+ d.ignoreNextIndent = true
+ d.dump(d.unpackValue(v.Field(i)))
+ if i < (numFields - 1) {
+ d.w.Write(commaNewlineBytes)
+ } else {
+ d.w.Write(newlineBytes)
+ }
+ }
+ }
+ d.depth--
+ d.indent()
+ d.w.Write(closeBraceBytes)
+
+ case reflect.Uintptr:
+ printHexPtr(d.w, uintptr(v.Uint()))
+
+ case reflect.UnsafePointer, reflect.Chan, reflect.Func:
+ printHexPtr(d.w, v.Pointer())
+
+ // There were not any other types at the time this code was written, but
+ // fall back to letting the default fmt package handle it in case any new
+ // types are added.
+ default:
+ if v.CanInterface() {
+ fmt.Fprintf(d.w, "%v", v.Interface())
+ } else {
+ fmt.Fprintf(d.w, "%v", v.String())
+ }
+ }
+}
+
+// fdump is a helper function to consolidate the logic from the various public
+// methods which take varying writers and config states.
+func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
+ for _, arg := range a {
+ if arg == nil {
+ w.Write(interfaceBytes)
+ w.Write(spaceBytes)
+ w.Write(nilAngleBytes)
+ w.Write(newlineBytes)
+ continue
+ }
+
+ d := dumpState{w: w, cs: cs}
+ d.pointers = make(map[uintptr]int)
+ d.dump(reflect.ValueOf(arg))
+ d.w.Write(newlineBytes)
+ }
+}
+
+// Fdump formats and displays the passed arguments to io.Writer w. It formats
+// exactly the same as Dump.
+func Fdump(w io.Writer, a ...interface{}) {
+ fdump(&Config, w, a...)
+}
+
+// Sdump returns a string with the passed arguments formatted exactly the same
+// as Dump.
+func Sdump(a ...interface{}) string {
+ var buf bytes.Buffer
+ fdump(&Config, &buf, a...)
+ return buf.String()
+}
+
+/*
+Dump displays the passed parameters to standard out with newlines, customizable
+indentation, and additional debug information such as complete types and all
+pointer addresses used to indirect to the final value. It provides the
+following features over the built-in printing facilities provided by the fmt
+package:
+
+ * Pointers are dereferenced and followed
+ * Circular data structures are detected and handled properly
+ * Custom Stringer/error interfaces are optionally invoked, including
+ on unexported types
+ * Custom types which only implement the Stringer/error interfaces via
+ a pointer receiver are optionally invoked when passing non-pointer
+ variables
+ * Byte arrays and slices are dumped like the hexdump -C command which
+ includes offsets, byte values in hex, and ASCII output
+
+The configuration options are controlled by an exported package global,
+spew.Config. See ConfigState for options documentation.
+
+See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
+get the formatted result as a string.
+*/
+func Dump(a ...interface{}) {
+ fdump(&Config, os.Stdout, a...)
+}
diff --git a/server/vendor/github.com/davecgh/go-spew/spew/format.go b/server/vendor/github.com/davecgh/go-spew/spew/format.go
new file mode 100644
index 0000000..b04edb7
--- /dev/null
+++ b/server/vendor/github.com/davecgh/go-spew/spew/format.go
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+// supportedFlags is a list of all the character flags supported by fmt package.
+const supportedFlags = "0-+# "
+
+// formatState implements the fmt.Formatter interface and contains information
+// about the state of a formatting operation. The NewFormatter function can
+// be used to get a new Formatter which can be used directly as arguments
+// in standard fmt package printing calls.
+type formatState struct {
+ value interface{}
+ fs fmt.State
+ depth int
+ pointers map[uintptr]int
+ ignoreNextType bool
+ cs *ConfigState
+}
+
+// buildDefaultFormat recreates the original format string without precision
+// and width information to pass in to fmt.Sprintf in the case of an
+// unrecognized type. Unless new types are added to the language, this
+// function won't ever be called.
+func (f *formatState) buildDefaultFormat() (format string) {
+ buf := bytes.NewBuffer(percentBytes)
+
+ for _, flag := range supportedFlags {
+ if f.fs.Flag(int(flag)) {
+ buf.WriteRune(flag)
+ }
+ }
+
+ buf.WriteRune('v')
+
+ format = buf.String()
+ return format
+}
+
+// constructOrigFormat recreates the original format string including precision
+// and width information to pass along to the standard fmt package. This allows
+// automatic deferral of all format strings this package doesn't support.
+func (f *formatState) constructOrigFormat(verb rune) (format string) {
+ buf := bytes.NewBuffer(percentBytes)
+
+ for _, flag := range supportedFlags {
+ if f.fs.Flag(int(flag)) {
+ buf.WriteRune(flag)
+ }
+ }
+
+ if width, ok := f.fs.Width(); ok {
+ buf.WriteString(strconv.Itoa(width))
+ }
+
+ if precision, ok := f.fs.Precision(); ok {
+ buf.Write(precisionBytes)
+ buf.WriteString(strconv.Itoa(precision))
+ }
+
+ buf.WriteRune(verb)
+
+ format = buf.String()
+ return format
+}
+
+// unpackValue returns values inside of non-nil interfaces when possible and
+// ensures that types for values which have been unpacked from an interface
+// are displayed when the show types flag is also set.
+// This is useful for data types like structs, arrays, slices, and maps which
+// can contain varying types packed inside an interface.
+func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
+ if v.Kind() == reflect.Interface {
+ f.ignoreNextType = false
+ if !v.IsNil() {
+ v = v.Elem()
+ }
+ }
+ return v
+}
+
+// formatPtr handles formatting of pointers by indirecting them as necessary.
+func (f *formatState) formatPtr(v reflect.Value) {
+ // Display nil if top level pointer is nil.
+ showTypes := f.fs.Flag('#')
+ if v.IsNil() && (!showTypes || f.ignoreNextType) {
+ f.fs.Write(nilAngleBytes)
+ return
+ }
+
+ // Remove pointers at or below the current depth from map used to detect
+ // circular refs.
+ for k, depth := range f.pointers {
+ if depth >= f.depth {
+ delete(f.pointers, k)
+ }
+ }
+
+ // Keep list of all dereferenced pointers to possibly show later.
+ pointerChain := make([]uintptr, 0)
+
+ // Figure out how many levels of indirection there are by derferencing
+ // pointers and unpacking interfaces down the chain while detecting circular
+ // references.
+ nilFound := false
+ cycleFound := false
+ indirects := 0
+ ve := v
+ for ve.Kind() == reflect.Ptr {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ indirects++
+ addr := ve.Pointer()
+ pointerChain = append(pointerChain, addr)
+ if pd, ok := f.pointers[addr]; ok && pd < f.depth {
+ cycleFound = true
+ indirects--
+ break
+ }
+ f.pointers[addr] = f.depth
+
+ ve = ve.Elem()
+ if ve.Kind() == reflect.Interface {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ ve = ve.Elem()
+ }
+ }
+
+ // Display type or indirection level depending on flags.
+ if showTypes && !f.ignoreNextType {
+ f.fs.Write(openParenBytes)
+ f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
+ f.fs.Write([]byte(ve.Type().String()))
+ f.fs.Write(closeParenBytes)
+ } else {
+ if nilFound || cycleFound {
+ indirects += strings.Count(ve.Type().String(), "*")
+ }
+ f.fs.Write(openAngleBytes)
+ f.fs.Write([]byte(strings.Repeat("*", indirects)))
+ f.fs.Write(closeAngleBytes)
+ }
+
+ // Display pointer information depending on flags.
+ if f.fs.Flag('+') && (len(pointerChain) > 0) {
+ f.fs.Write(openParenBytes)
+ for i, addr := range pointerChain {
+ if i > 0 {
+ f.fs.Write(pointerChainBytes)
+ }
+ printHexPtr(f.fs, addr)
+ }
+ f.fs.Write(closeParenBytes)
+ }
+
+ // Display dereferenced value.
+ switch {
+ case nilFound:
+ f.fs.Write(nilAngleBytes)
+
+ case cycleFound:
+ f.fs.Write(circularShortBytes)
+
+ default:
+ f.ignoreNextType = true
+ f.format(ve)
+ }
+}
+
+// format is the main workhorse for providing the Formatter interface. It
+// uses the passed reflect value to figure out what kind of object we are
+// dealing with and formats it appropriately. It is a recursive function,
+// however circular data structures are detected and handled properly.
+func (f *formatState) format(v reflect.Value) {
+ // Handle invalid reflect values immediately.
+ kind := v.Kind()
+ if kind == reflect.Invalid {
+ f.fs.Write(invalidAngleBytes)
+ return
+ }
+
+ // Handle pointers specially.
+ if kind == reflect.Ptr {
+ f.formatPtr(v)
+ return
+ }
+
+ // Print type information unless already handled elsewhere.
+ if !f.ignoreNextType && f.fs.Flag('#') {
+ f.fs.Write(openParenBytes)
+ f.fs.Write([]byte(v.Type().String()))
+ f.fs.Write(closeParenBytes)
+ }
+ f.ignoreNextType = false
+
+ // Call Stringer/error interfaces if they exist and the handle methods
+ // flag is enabled.
+ if !f.cs.DisableMethods {
+ if (kind != reflect.Invalid) && (kind != reflect.Interface) {
+ if handled := handleMethods(f.cs, f.fs, v); handled {
+ return
+ }
+ }
+ }
+
+ switch kind {
+ case reflect.Invalid:
+ // Do nothing. We should never get here since invalid has already
+ // been handled above.
+
+ case reflect.Bool:
+ printBool(f.fs, v.Bool())
+
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ printInt(f.fs, v.Int(), 10)
+
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ printUint(f.fs, v.Uint(), 10)
+
+ case reflect.Float32:
+ printFloat(f.fs, v.Float(), 32)
+
+ case reflect.Float64:
+ printFloat(f.fs, v.Float(), 64)
+
+ case reflect.Complex64:
+ printComplex(f.fs, v.Complex(), 32)
+
+ case reflect.Complex128:
+ printComplex(f.fs, v.Complex(), 64)
+
+ case reflect.Slice:
+ if v.IsNil() {
+ f.fs.Write(nilAngleBytes)
+ break
+ }
+ fallthrough
+
+ case reflect.Array:
+ f.fs.Write(openBracketBytes)
+ f.depth++
+ if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
+ f.fs.Write(maxShortBytes)
+ } else {
+ numEntries := v.Len()
+ for i := 0; i < numEntries; i++ {
+ if i > 0 {
+ f.fs.Write(spaceBytes)
+ }
+ f.ignoreNextType = true
+ f.format(f.unpackValue(v.Index(i)))
+ }
+ }
+ f.depth--
+ f.fs.Write(closeBracketBytes)
+
+ case reflect.String:
+ f.fs.Write([]byte(v.String()))
+
+ case reflect.Interface:
+ // The only time we should get here is for nil interfaces due to
+ // unpackValue calls.
+ if v.IsNil() {
+ f.fs.Write(nilAngleBytes)
+ }
+
+ case reflect.Ptr:
+ // Do nothing. We should never get here since pointers have already
+ // been handled above.
+
+ case reflect.Map:
+ // nil maps should be indicated as different than empty maps
+ if v.IsNil() {
+ f.fs.Write(nilAngleBytes)
+ break
+ }
+
+ f.fs.Write(openMapBytes)
+ f.depth++
+ if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
+ f.fs.Write(maxShortBytes)
+ } else {
+ keys := v.MapKeys()
+ if f.cs.SortKeys {
+ sortValues(keys, f.cs)
+ }
+ for i, key := range keys {
+ if i > 0 {
+ f.fs.Write(spaceBytes)
+ }
+ f.ignoreNextType = true
+ f.format(f.unpackValue(key))
+ f.fs.Write(colonBytes)
+ f.ignoreNextType = true
+ f.format(f.unpackValue(v.MapIndex(key)))
+ }
+ }
+ f.depth--
+ f.fs.Write(closeMapBytes)
+
+ case reflect.Struct:
+ numFields := v.NumField()
+ f.fs.Write(openBraceBytes)
+ f.depth++
+ if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
+ f.fs.Write(maxShortBytes)
+ } else {
+ vt := v.Type()
+ for i := 0; i < numFields; i++ {
+ if i > 0 {
+ f.fs.Write(spaceBytes)
+ }
+ vtf := vt.Field(i)
+ if f.fs.Flag('+') || f.fs.Flag('#') {
+ f.fs.Write([]byte(vtf.Name))
+ f.fs.Write(colonBytes)
+ }
+ f.format(f.unpackValue(v.Field(i)))
+ }
+ }
+ f.depth--
+ f.fs.Write(closeBraceBytes)
+
+ case reflect.Uintptr:
+ printHexPtr(f.fs, uintptr(v.Uint()))
+
+ case reflect.UnsafePointer, reflect.Chan, reflect.Func:
+ printHexPtr(f.fs, v.Pointer())
+
+ // There were not any other types at the time this code was written, but
+ // fall back to letting the default fmt package handle it if any get added.
+ default:
+ format := f.buildDefaultFormat()
+ if v.CanInterface() {
+ fmt.Fprintf(f.fs, format, v.Interface())
+ } else {
+ fmt.Fprintf(f.fs, format, v.String())
+ }
+ }
+}
+
+// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
+// details.
+func (f *formatState) Format(fs fmt.State, verb rune) {
+ f.fs = fs
+
+ // Use standard formatting for verbs that are not v.
+ if verb != 'v' {
+ format := f.constructOrigFormat(verb)
+ fmt.Fprintf(fs, format, f.value)
+ return
+ }
+
+ if f.value == nil {
+ if fs.Flag('#') {
+ fs.Write(interfaceBytes)
+ }
+ fs.Write(nilAngleBytes)
+ return
+ }
+
+ f.format(reflect.ValueOf(f.value))
+}
+
+// newFormatter is a helper function to consolidate the logic from the various
+// public methods which take varying config states.
+func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
+ fs := &formatState{value: v, cs: cs}
+ fs.pointers = make(map[uintptr]int)
+ return fs
+}
+
+/*
+NewFormatter returns a custom formatter that satisfies the fmt.Formatter
+interface. As a result, it integrates cleanly with standard fmt package
+printing functions. The formatter is useful for inline printing of smaller data
+types similar to the standard %v format specifier.
+
+The custom formatter only responds to the %v (most compact), %+v (adds pointer
+addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
+combinations. Any other verbs such as %x and %q will be sent to the the
+standard fmt package for formatting. In addition, the custom formatter ignores
+the width and precision arguments (however they will still work on the format
+specifiers not handled by the custom formatter).
+
+Typically this function shouldn't be called directly. It is much easier to make
+use of the custom formatter by calling one of the convenience functions such as
+Printf, Println, or Fprintf.
+*/
+func NewFormatter(v interface{}) fmt.Formatter {
+ return newFormatter(&Config, v)
+}
diff --git a/server/vendor/github.com/davecgh/go-spew/spew/spew.go b/server/vendor/github.com/davecgh/go-spew/spew/spew.go
new file mode 100644
index 0000000..32c0e33
--- /dev/null
+++ b/server/vendor/github.com/davecgh/go-spew/spew/spew.go
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "fmt"
+ "io"
+)
+
+// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the formatted string as a value that satisfies error. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Errorf(format string, a ...interface{}) (err error) {
+ return fmt.Errorf(format, convertArgs(a)...)
+}
+
+// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
+func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprint(w, convertArgs(a)...)
+}
+
+// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
+ return fmt.Fprintf(w, format, convertArgs(a)...)
+}
+
+// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
+// passed with a default Formatter interface returned by NewFormatter. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
+func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprintln(w, convertArgs(a)...)
+}
+
+// Print is a wrapper for fmt.Print that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
+func Print(a ...interface{}) (n int, err error) {
+ return fmt.Print(convertArgs(a)...)
+}
+
+// Printf is a wrapper for fmt.Printf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Printf(format string, a ...interface{}) (n int, err error) {
+ return fmt.Printf(format, convertArgs(a)...)
+}
+
+// Println is a wrapper for fmt.Println that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
+func Println(a ...interface{}) (n int, err error) {
+ return fmt.Println(convertArgs(a)...)
+}
+
+// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
+func Sprint(a ...interface{}) string {
+ return fmt.Sprint(convertArgs(a)...)
+}
+
+// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Sprintf(format string, a ...interface{}) string {
+ return fmt.Sprintf(format, convertArgs(a)...)
+}
+
+// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
+// were passed with a default Formatter interface returned by NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
+func Sprintln(a ...interface{}) string {
+ return fmt.Sprintln(convertArgs(a)...)
+}
+
+// convertArgs accepts a slice of arguments and returns a slice of the same
+// length with each argument converted to a default spew Formatter interface.
+func convertArgs(args []interface{}) (formatters []interface{}) {
+ formatters = make([]interface{}, len(args))
+ for index, arg := range args {
+ formatters[index] = NewFormatter(arg)
+ }
+ return formatters
+}
diff --git a/server/vendor/github.com/google/uuid/CHANGELOG.md b/server/vendor/github.com/google/uuid/CHANGELOG.md
new file mode 100644
index 0000000..2bd7866
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/CHANGELOG.md
@@ -0,0 +1,10 @@
+# Changelog
+
+## [1.3.1](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) (2023-08-18)
+
+
+### Bug Fixes
+
+* Use .EqualFold() to parse urn prefixed UUIDs ([#118](https://github.com/google/uuid/issues/118)) ([574e687](https://github.com/google/uuid/commit/574e6874943741fb99d41764c705173ada5293f0))
+
+## Changelog
diff --git a/server/vendor/github.com/google/uuid/CONTRIBUTING.md b/server/vendor/github.com/google/uuid/CONTRIBUTING.md
new file mode 100644
index 0000000..5566888
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/CONTRIBUTING.md
@@ -0,0 +1,26 @@
+# How to contribute
+
+We definitely welcome patches and contribution to this project!
+
+### Tips
+
+Commits must be formatted according to the [Conventional Commits Specification](https://www.conventionalcommits.org).
+
+Always try to include a test case! If it is not possible or not necessary,
+please explain why in the pull request description.
+
+### Releasing
+
+Commits that would precipitate a SemVer change, as desrcibed in the Conventional
+Commits Specification, will trigger [`release-please`](https://github.com/google-github-actions/release-please-action)
+to create a release candidate pull request. Once submitted, `release-please`
+will create a release.
+
+For tips on how to work with `release-please`, see its documentation.
+
+### Legal requirements
+
+In order to protect both you and ourselves, you will need to sign the
+[Contributor License Agreement](https://cla.developers.google.com/clas).
+
+You may have already signed it for other Google projects.
diff --git a/server/vendor/github.com/google/uuid/CONTRIBUTORS b/server/vendor/github.com/google/uuid/CONTRIBUTORS
new file mode 100644
index 0000000..b4bb97f
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/CONTRIBUTORS
@@ -0,0 +1,9 @@
+Paul Borman
+bmatsuo
+shawnps
+theory
+jboverfelt
+dsymonds
+cd1
+wallclockbuilder
+dansouza
diff --git a/server/vendor/github.com/google/uuid/LICENSE b/server/vendor/github.com/google/uuid/LICENSE
new file mode 100644
index 0000000..5dc6826
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009,2014 Google Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/server/vendor/github.com/google/uuid/README.md b/server/vendor/github.com/google/uuid/README.md
new file mode 100644
index 0000000..3e9a618
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/README.md
@@ -0,0 +1,21 @@
+# uuid
+The uuid package generates and inspects UUIDs based on
+[RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122)
+and DCE 1.1: Authentication and Security Services.
+
+This package is based on the github.com/pborman/uuid package (previously named
+code.google.com/p/go-uuid). It differs from these earlier packages in that
+a UUID is a 16 byte array rather than a byte slice. One loss due to this
+change is the ability to represent an invalid UUID (vs a NIL UUID).
+
+###### Install
+```sh
+go get github.com/google/uuid
+```
+
+###### Documentation
+[](https://pkg.go.dev/github.com/google/uuid)
+
+Full `go doc` style documentation for the package can be viewed online without
+installing this package by using the GoDoc site here:
+http://pkg.go.dev/github.com/google/uuid
diff --git a/server/vendor/github.com/google/uuid/dce.go b/server/vendor/github.com/google/uuid/dce.go
new file mode 100644
index 0000000..fa820b9
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/dce.go
@@ -0,0 +1,80 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "encoding/binary"
+ "fmt"
+ "os"
+)
+
+// A Domain represents a Version 2 domain
+type Domain byte
+
+// Domain constants for DCE Security (Version 2) UUIDs.
+const (
+ Person = Domain(0)
+ Group = Domain(1)
+ Org = Domain(2)
+)
+
+// NewDCESecurity returns a DCE Security (Version 2) UUID.
+//
+// The domain should be one of Person, Group or Org.
+// On a POSIX system the id should be the users UID for the Person
+// domain and the users GID for the Group. The meaning of id for
+// the domain Org or on non-POSIX systems is site defined.
+//
+// For a given domain/id pair the same token may be returned for up to
+// 7 minutes and 10 seconds.
+func NewDCESecurity(domain Domain, id uint32) (UUID, error) {
+ uuid, err := NewUUID()
+ if err == nil {
+ uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
+ uuid[9] = byte(domain)
+ binary.BigEndian.PutUint32(uuid[0:], id)
+ }
+ return uuid, err
+}
+
+// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
+// domain with the id returned by os.Getuid.
+//
+// NewDCESecurity(Person, uint32(os.Getuid()))
+func NewDCEPerson() (UUID, error) {
+ return NewDCESecurity(Person, uint32(os.Getuid()))
+}
+
+// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
+// domain with the id returned by os.Getgid.
+//
+// NewDCESecurity(Group, uint32(os.Getgid()))
+func NewDCEGroup() (UUID, error) {
+ return NewDCESecurity(Group, uint32(os.Getgid()))
+}
+
+// Domain returns the domain for a Version 2 UUID. Domains are only defined
+// for Version 2 UUIDs.
+func (uuid UUID) Domain() Domain {
+ return Domain(uuid[9])
+}
+
+// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2
+// UUIDs.
+func (uuid UUID) ID() uint32 {
+ return binary.BigEndian.Uint32(uuid[0:4])
+}
+
+func (d Domain) String() string {
+ switch d {
+ case Person:
+ return "Person"
+ case Group:
+ return "Group"
+ case Org:
+ return "Org"
+ }
+ return fmt.Sprintf("Domain%d", int(d))
+}
diff --git a/server/vendor/github.com/google/uuid/doc.go b/server/vendor/github.com/google/uuid/doc.go
new file mode 100644
index 0000000..5b8a4b9
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/doc.go
@@ -0,0 +1,12 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package uuid generates and inspects UUIDs.
+//
+// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security
+// Services.
+//
+// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to
+// maps or compared directly.
+package uuid
diff --git a/server/vendor/github.com/google/uuid/hash.go b/server/vendor/github.com/google/uuid/hash.go
new file mode 100644
index 0000000..b404f4b
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/hash.go
@@ -0,0 +1,53 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "crypto/md5"
+ "crypto/sha1"
+ "hash"
+)
+
+// Well known namespace IDs and UUIDs
+var (
+ NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
+ NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
+ NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
+ NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
+ Nil UUID // empty UUID, all zeros
+)
+
+// NewHash returns a new UUID derived from the hash of space concatenated with
+// data generated by h. The hash should be at least 16 byte in length. The
+// first 16 bytes of the hash are used to form the UUID. The version of the
+// UUID will be the lower 4 bits of version. NewHash is used to implement
+// NewMD5 and NewSHA1.
+func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
+ h.Reset()
+ h.Write(space[:]) //nolint:errcheck
+ h.Write(data) //nolint:errcheck
+ s := h.Sum(nil)
+ var uuid UUID
+ copy(uuid[:], s)
+ uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
+ uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
+ return uuid
+}
+
+// NewMD5 returns a new MD5 (Version 3) UUID based on the
+// supplied name space and data. It is the same as calling:
+//
+// NewHash(md5.New(), space, data, 3)
+func NewMD5(space UUID, data []byte) UUID {
+ return NewHash(md5.New(), space, data, 3)
+}
+
+// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
+// supplied name space and data. It is the same as calling:
+//
+// NewHash(sha1.New(), space, data, 5)
+func NewSHA1(space UUID, data []byte) UUID {
+ return NewHash(sha1.New(), space, data, 5)
+}
diff --git a/server/vendor/github.com/google/uuid/marshal.go b/server/vendor/github.com/google/uuid/marshal.go
new file mode 100644
index 0000000..14bd340
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/marshal.go
@@ -0,0 +1,38 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import "fmt"
+
+// MarshalText implements encoding.TextMarshaler.
+func (uuid UUID) MarshalText() ([]byte, error) {
+ var js [36]byte
+ encodeHex(js[:], uuid)
+ return js[:], nil
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (uuid *UUID) UnmarshalText(data []byte) error {
+ id, err := ParseBytes(data)
+ if err != nil {
+ return err
+ }
+ *uuid = id
+ return nil
+}
+
+// MarshalBinary implements encoding.BinaryMarshaler.
+func (uuid UUID) MarshalBinary() ([]byte, error) {
+ return uuid[:], nil
+}
+
+// UnmarshalBinary implements encoding.BinaryUnmarshaler.
+func (uuid *UUID) UnmarshalBinary(data []byte) error {
+ if len(data) != 16 {
+ return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
+ }
+ copy(uuid[:], data)
+ return nil
+}
diff --git a/server/vendor/github.com/google/uuid/node.go b/server/vendor/github.com/google/uuid/node.go
new file mode 100644
index 0000000..d651a2b
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/node.go
@@ -0,0 +1,90 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "sync"
+)
+
+var (
+ nodeMu sync.Mutex
+ ifname string // name of interface being used
+ nodeID [6]byte // hardware for version 1 UUIDs
+ zeroID [6]byte // nodeID with only 0's
+)
+
+// NodeInterface returns the name of the interface from which the NodeID was
+// derived. The interface "user" is returned if the NodeID was set by
+// SetNodeID.
+func NodeInterface() string {
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
+ return ifname
+}
+
+// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
+// If name is "" then the first usable interface found will be used or a random
+// Node ID will be generated. If a named interface cannot be found then false
+// is returned.
+//
+// SetNodeInterface never fails when name is "".
+func SetNodeInterface(name string) bool {
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
+ return setNodeInterface(name)
+}
+
+func setNodeInterface(name string) bool {
+ iname, addr := getHardwareInterface(name) // null implementation for js
+ if iname != "" && addr != nil {
+ ifname = iname
+ copy(nodeID[:], addr)
+ return true
+ }
+
+ // We found no interfaces with a valid hardware address. If name
+ // does not specify a specific interface generate a random Node ID
+ // (section 4.1.6)
+ if name == "" {
+ ifname = "random"
+ randomBits(nodeID[:])
+ return true
+ }
+ return false
+}
+
+// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
+// if not already set.
+func NodeID() []byte {
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
+ if nodeID == zeroID {
+ setNodeInterface("")
+ }
+ nid := nodeID
+ return nid[:]
+}
+
+// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
+// of id are used. If id is less than 6 bytes then false is returned and the
+// Node ID is not set.
+func SetNodeID(id []byte) bool {
+ if len(id) < 6 {
+ return false
+ }
+ defer nodeMu.Unlock()
+ nodeMu.Lock()
+ copy(nodeID[:], id)
+ ifname = "user"
+ return true
+}
+
+// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
+// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
+func (uuid UUID) NodeID() []byte {
+ var node [6]byte
+ copy(node[:], uuid[10:])
+ return node[:]
+}
diff --git a/server/vendor/github.com/google/uuid/node_js.go b/server/vendor/github.com/google/uuid/node_js.go
new file mode 100644
index 0000000..b2a0bc8
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/node_js.go
@@ -0,0 +1,12 @@
+// Copyright 2017 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build js
+
+package uuid
+
+// getHardwareInterface returns nil values for the JS version of the code.
+// This removes the "net" dependency, because it is not used in the browser.
+// Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
+func getHardwareInterface(name string) (string, []byte) { return "", nil }
diff --git a/server/vendor/github.com/google/uuid/node_net.go b/server/vendor/github.com/google/uuid/node_net.go
new file mode 100644
index 0000000..0cbbcdd
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/node_net.go
@@ -0,0 +1,33 @@
+// Copyright 2017 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !js
+
+package uuid
+
+import "net"
+
+var interfaces []net.Interface // cached list of interfaces
+
+// getHardwareInterface returns the name and hardware address of interface name.
+// If name is "" then the name and hardware address of one of the system's
+// interfaces is returned. If no interfaces are found (name does not exist or
+// there are no interfaces) then "", nil is returned.
+//
+// Only addresses of at least 6 bytes are returned.
+func getHardwareInterface(name string) (string, []byte) {
+ if interfaces == nil {
+ var err error
+ interfaces, err = net.Interfaces()
+ if err != nil {
+ return "", nil
+ }
+ }
+ for _, ifs := range interfaces {
+ if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
+ return ifs.Name, ifs.HardwareAddr
+ }
+ }
+ return "", nil
+}
diff --git a/server/vendor/github.com/google/uuid/null.go b/server/vendor/github.com/google/uuid/null.go
new file mode 100644
index 0000000..d7fcbf2
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/null.go
@@ -0,0 +1,118 @@
+// Copyright 2021 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "bytes"
+ "database/sql/driver"
+ "encoding/json"
+ "fmt"
+)
+
+var jsonNull = []byte("null")
+
+// NullUUID represents a UUID that may be null.
+// NullUUID implements the SQL driver.Scanner interface so
+// it can be used as a scan destination:
+//
+// var u uuid.NullUUID
+// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u)
+// ...
+// if u.Valid {
+// // use u.UUID
+// } else {
+// // NULL value
+// }
+//
+type NullUUID struct {
+ UUID UUID
+ Valid bool // Valid is true if UUID is not NULL
+}
+
+// Scan implements the SQL driver.Scanner interface.
+func (nu *NullUUID) Scan(value interface{}) error {
+ if value == nil {
+ nu.UUID, nu.Valid = Nil, false
+ return nil
+ }
+
+ err := nu.UUID.Scan(value)
+ if err != nil {
+ nu.Valid = false
+ return err
+ }
+
+ nu.Valid = true
+ return nil
+}
+
+// Value implements the driver Valuer interface.
+func (nu NullUUID) Value() (driver.Value, error) {
+ if !nu.Valid {
+ return nil, nil
+ }
+ // Delegate to UUID Value function
+ return nu.UUID.Value()
+}
+
+// MarshalBinary implements encoding.BinaryMarshaler.
+func (nu NullUUID) MarshalBinary() ([]byte, error) {
+ if nu.Valid {
+ return nu.UUID[:], nil
+ }
+
+ return []byte(nil), nil
+}
+
+// UnmarshalBinary implements encoding.BinaryUnmarshaler.
+func (nu *NullUUID) UnmarshalBinary(data []byte) error {
+ if len(data) != 16 {
+ return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
+ }
+ copy(nu.UUID[:], data)
+ nu.Valid = true
+ return nil
+}
+
+// MarshalText implements encoding.TextMarshaler.
+func (nu NullUUID) MarshalText() ([]byte, error) {
+ if nu.Valid {
+ return nu.UUID.MarshalText()
+ }
+
+ return jsonNull, nil
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler.
+func (nu *NullUUID) UnmarshalText(data []byte) error {
+ id, err := ParseBytes(data)
+ if err != nil {
+ nu.Valid = false
+ return err
+ }
+ nu.UUID = id
+ nu.Valid = true
+ return nil
+}
+
+// MarshalJSON implements json.Marshaler.
+func (nu NullUUID) MarshalJSON() ([]byte, error) {
+ if nu.Valid {
+ return json.Marshal(nu.UUID)
+ }
+
+ return jsonNull, nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (nu *NullUUID) UnmarshalJSON(data []byte) error {
+ if bytes.Equal(data, jsonNull) {
+ *nu = NullUUID{}
+ return nil // valid null UUID
+ }
+ err := json.Unmarshal(data, &nu.UUID)
+ nu.Valid = err == nil
+ return err
+}
diff --git a/server/vendor/github.com/google/uuid/sql.go b/server/vendor/github.com/google/uuid/sql.go
new file mode 100644
index 0000000..2e02ec0
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/sql.go
@@ -0,0 +1,59 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "database/sql/driver"
+ "fmt"
+)
+
+// Scan implements sql.Scanner so UUIDs can be read from databases transparently.
+// Currently, database types that map to string and []byte are supported. Please
+// consult database-specific driver documentation for matching types.
+func (uuid *UUID) Scan(src interface{}) error {
+ switch src := src.(type) {
+ case nil:
+ return nil
+
+ case string:
+ // if an empty UUID comes from a table, we return a null UUID
+ if src == "" {
+ return nil
+ }
+
+ // see Parse for required string format
+ u, err := Parse(src)
+ if err != nil {
+ return fmt.Errorf("Scan: %v", err)
+ }
+
+ *uuid = u
+
+ case []byte:
+ // if an empty UUID comes from a table, we return a null UUID
+ if len(src) == 0 {
+ return nil
+ }
+
+ // assumes a simple slice of bytes if 16 bytes
+ // otherwise attempts to parse
+ if len(src) != 16 {
+ return uuid.Scan(string(src))
+ }
+ copy((*uuid)[:], src)
+
+ default:
+ return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
+ }
+
+ return nil
+}
+
+// Value implements sql.Valuer so that UUIDs can be written to databases
+// transparently. Currently, UUIDs map to strings. Please consult
+// database-specific driver documentation for matching types.
+func (uuid UUID) Value() (driver.Value, error) {
+ return uuid.String(), nil
+}
diff --git a/server/vendor/github.com/google/uuid/time.go b/server/vendor/github.com/google/uuid/time.go
new file mode 100644
index 0000000..e6ef06c
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/time.go
@@ -0,0 +1,123 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "encoding/binary"
+ "sync"
+ "time"
+)
+
+// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
+// 1582.
+type Time int64
+
+const (
+ lillian = 2299160 // Julian day of 15 Oct 1582
+ unix = 2440587 // Julian day of 1 Jan 1970
+ epoch = unix - lillian // Days between epochs
+ g1582 = epoch * 86400 // seconds between epochs
+ g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
+)
+
+var (
+ timeMu sync.Mutex
+ lasttime uint64 // last time we returned
+ clockSeq uint16 // clock sequence for this run
+
+ timeNow = time.Now // for testing
+)
+
+// UnixTime converts t the number of seconds and nanoseconds using the Unix
+// epoch of 1 Jan 1970.
+func (t Time) UnixTime() (sec, nsec int64) {
+ sec = int64(t - g1582ns100)
+ nsec = (sec % 10000000) * 100
+ sec /= 10000000
+ return sec, nsec
+}
+
+// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
+// clock sequence as well as adjusting the clock sequence as needed. An error
+// is returned if the current time cannot be determined.
+func GetTime() (Time, uint16, error) {
+ defer timeMu.Unlock()
+ timeMu.Lock()
+ return getTime()
+}
+
+func getTime() (Time, uint16, error) {
+ t := timeNow()
+
+ // If we don't have a clock sequence already, set one.
+ if clockSeq == 0 {
+ setClockSequence(-1)
+ }
+ now := uint64(t.UnixNano()/100) + g1582ns100
+
+ // If time has gone backwards with this clock sequence then we
+ // increment the clock sequence
+ if now <= lasttime {
+ clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000
+ }
+ lasttime = now
+ return Time(now), clockSeq, nil
+}
+
+// ClockSequence returns the current clock sequence, generating one if not
+// already set. The clock sequence is only used for Version 1 UUIDs.
+//
+// The uuid package does not use global static storage for the clock sequence or
+// the last time a UUID was generated. Unless SetClockSequence is used, a new
+// random clock sequence is generated the first time a clock sequence is
+// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1)
+func ClockSequence() int {
+ defer timeMu.Unlock()
+ timeMu.Lock()
+ return clockSequence()
+}
+
+func clockSequence() int {
+ if clockSeq == 0 {
+ setClockSequence(-1)
+ }
+ return int(clockSeq & 0x3fff)
+}
+
+// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to
+// -1 causes a new sequence to be generated.
+func SetClockSequence(seq int) {
+ defer timeMu.Unlock()
+ timeMu.Lock()
+ setClockSequence(seq)
+}
+
+func setClockSequence(seq int) {
+ if seq == -1 {
+ var b [2]byte
+ randomBits(b[:]) // clock sequence
+ seq = int(b[0])<<8 | int(b[1])
+ }
+ oldSeq := clockSeq
+ clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant
+ if oldSeq != clockSeq {
+ lasttime = 0
+ }
+}
+
+// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
+// uuid. The time is only defined for version 1 and 2 UUIDs.
+func (uuid UUID) Time() Time {
+ time := int64(binary.BigEndian.Uint32(uuid[0:4]))
+ time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
+ time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
+ return Time(time)
+}
+
+// ClockSequence returns the clock sequence encoded in uuid.
+// The clock sequence is only well defined for version 1 and 2 UUIDs.
+func (uuid UUID) ClockSequence() int {
+ return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff
+}
diff --git a/server/vendor/github.com/google/uuid/util.go b/server/vendor/github.com/google/uuid/util.go
new file mode 100644
index 0000000..5ea6c73
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/util.go
@@ -0,0 +1,43 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "io"
+)
+
+// randomBits completely fills slice b with random data.
+func randomBits(b []byte) {
+ if _, err := io.ReadFull(rander, b); err != nil {
+ panic(err.Error()) // rand should never fail
+ }
+}
+
+// xvalues returns the value of a byte as a hexadecimal digit or 255.
+var xvalues = [256]byte{
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
+ 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+}
+
+// xtob converts hex characters x1 and x2 into a byte.
+func xtob(x1, x2 byte) (byte, bool) {
+ b1 := xvalues[x1]
+ b2 := xvalues[x2]
+ return (b1 << 4) | b2, b1 != 255 && b2 != 255
+}
diff --git a/server/vendor/github.com/google/uuid/uuid.go b/server/vendor/github.com/google/uuid/uuid.go
new file mode 100644
index 0000000..a56138c
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/uuid.go
@@ -0,0 +1,296 @@
+// Copyright 2018 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "bytes"
+ "crypto/rand"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+ "sync"
+)
+
+// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
+// 4122.
+type UUID [16]byte
+
+// A Version represents a UUID's version.
+type Version byte
+
+// A Variant represents a UUID's variant.
+type Variant byte
+
+// Constants returned by Variant.
+const (
+ Invalid = Variant(iota) // Invalid UUID
+ RFC4122 // The variant specified in RFC4122
+ Reserved // Reserved, NCS backward compatibility.
+ Microsoft // Reserved, Microsoft Corporation backward compatibility.
+ Future // Reserved for future definition.
+)
+
+const randPoolSize = 16 * 16
+
+var (
+ rander = rand.Reader // random function
+ poolEnabled = false
+ poolMu sync.Mutex
+ poolPos = randPoolSize // protected with poolMu
+ pool [randPoolSize]byte // protected with poolMu
+)
+
+type invalidLengthError struct{ len int }
+
+func (err invalidLengthError) Error() string {
+ return fmt.Sprintf("invalid UUID length: %d", err.len)
+}
+
+// IsInvalidLengthError is matcher function for custom error invalidLengthError
+func IsInvalidLengthError(err error) bool {
+ _, ok := err.(invalidLengthError)
+ return ok
+}
+
+// Parse decodes s into a UUID or returns an error. Both the standard UUID
+// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
+// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
+// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex
+// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
+func Parse(s string) (UUID, error) {
+ var uuid UUID
+ switch len(s) {
+ // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ case 36:
+
+ // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ case 36 + 9:
+ if !strings.EqualFold(s[:9], "urn:uuid:") {
+ return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
+ }
+ s = s[9:]
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ case 36 + 2:
+ s = s[1:]
+
+ // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ case 32:
+ var ok bool
+ for i := range uuid {
+ uuid[i], ok = xtob(s[i*2], s[i*2+1])
+ if !ok {
+ return uuid, errors.New("invalid UUID format")
+ }
+ }
+ return uuid, nil
+ default:
+ return uuid, invalidLengthError{len(s)}
+ }
+ // s is now at least 36 bytes long
+ // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
+ return uuid, errors.New("invalid UUID format")
+ }
+ for i, x := range [16]int{
+ 0, 2, 4, 6,
+ 9, 11,
+ 14, 16,
+ 19, 21,
+ 24, 26, 28, 30, 32, 34,
+ } {
+ v, ok := xtob(s[x], s[x+1])
+ if !ok {
+ return uuid, errors.New("invalid UUID format")
+ }
+ uuid[i] = v
+ }
+ return uuid, nil
+}
+
+// ParseBytes is like Parse, except it parses a byte slice instead of a string.
+func ParseBytes(b []byte) (UUID, error) {
+ var uuid UUID
+ switch len(b) {
+ case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ if !bytes.EqualFold(b[:9], []byte("urn:uuid:")) {
+ return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
+ }
+ b = b[9:]
+ case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ b = b[1:]
+ case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ var ok bool
+ for i := 0; i < 32; i += 2 {
+ uuid[i/2], ok = xtob(b[i], b[i+1])
+ if !ok {
+ return uuid, errors.New("invalid UUID format")
+ }
+ }
+ return uuid, nil
+ default:
+ return uuid, invalidLengthError{len(b)}
+ }
+ // s is now at least 36 bytes long
+ // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {
+ return uuid, errors.New("invalid UUID format")
+ }
+ for i, x := range [16]int{
+ 0, 2, 4, 6,
+ 9, 11,
+ 14, 16,
+ 19, 21,
+ 24, 26, 28, 30, 32, 34,
+ } {
+ v, ok := xtob(b[x], b[x+1])
+ if !ok {
+ return uuid, errors.New("invalid UUID format")
+ }
+ uuid[i] = v
+ }
+ return uuid, nil
+}
+
+// MustParse is like Parse but panics if the string cannot be parsed.
+// It simplifies safe initialization of global variables holding compiled UUIDs.
+func MustParse(s string) UUID {
+ uuid, err := Parse(s)
+ if err != nil {
+ panic(`uuid: Parse(` + s + `): ` + err.Error())
+ }
+ return uuid
+}
+
+// FromBytes creates a new UUID from a byte slice. Returns an error if the slice
+// does not have a length of 16. The bytes are copied from the slice.
+func FromBytes(b []byte) (uuid UUID, err error) {
+ err = uuid.UnmarshalBinary(b)
+ return uuid, err
+}
+
+// Must returns uuid if err is nil and panics otherwise.
+func Must(uuid UUID, err error) UUID {
+ if err != nil {
+ panic(err)
+ }
+ return uuid
+}
+
+// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+// , or "" if uuid is invalid.
+func (uuid UUID) String() string {
+ var buf [36]byte
+ encodeHex(buf[:], uuid)
+ return string(buf[:])
+}
+
+// URN returns the RFC 2141 URN form of uuid,
+// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
+func (uuid UUID) URN() string {
+ var buf [36 + 9]byte
+ copy(buf[:], "urn:uuid:")
+ encodeHex(buf[9:], uuid)
+ return string(buf[:])
+}
+
+func encodeHex(dst []byte, uuid UUID) {
+ hex.Encode(dst, uuid[:4])
+ dst[8] = '-'
+ hex.Encode(dst[9:13], uuid[4:6])
+ dst[13] = '-'
+ hex.Encode(dst[14:18], uuid[6:8])
+ dst[18] = '-'
+ hex.Encode(dst[19:23], uuid[8:10])
+ dst[23] = '-'
+ hex.Encode(dst[24:], uuid[10:])
+}
+
+// Variant returns the variant encoded in uuid.
+func (uuid UUID) Variant() Variant {
+ switch {
+ case (uuid[8] & 0xc0) == 0x80:
+ return RFC4122
+ case (uuid[8] & 0xe0) == 0xc0:
+ return Microsoft
+ case (uuid[8] & 0xe0) == 0xe0:
+ return Future
+ default:
+ return Reserved
+ }
+}
+
+// Version returns the version of uuid.
+func (uuid UUID) Version() Version {
+ return Version(uuid[6] >> 4)
+}
+
+func (v Version) String() string {
+ if v > 15 {
+ return fmt.Sprintf("BAD_VERSION_%d", v)
+ }
+ return fmt.Sprintf("VERSION_%d", v)
+}
+
+func (v Variant) String() string {
+ switch v {
+ case RFC4122:
+ return "RFC4122"
+ case Reserved:
+ return "Reserved"
+ case Microsoft:
+ return "Microsoft"
+ case Future:
+ return "Future"
+ case Invalid:
+ return "Invalid"
+ }
+ return fmt.Sprintf("BadVariant%d", int(v))
+}
+
+// SetRand sets the random number generator to r, which implements io.Reader.
+// If r.Read returns an error when the package requests random data then
+// a panic will be issued.
+//
+// Calling SetRand with nil sets the random number generator to the default
+// generator.
+func SetRand(r io.Reader) {
+ if r == nil {
+ rander = rand.Reader
+ return
+ }
+ rander = r
+}
+
+// EnableRandPool enables internal randomness pool used for Random
+// (Version 4) UUID generation. The pool contains random bytes read from
+// the random number generator on demand in batches. Enabling the pool
+// may improve the UUID generation throughput significantly.
+//
+// Since the pool is stored on the Go heap, this feature may be a bad fit
+// for security sensitive applications.
+//
+// Both EnableRandPool and DisableRandPool are not thread-safe and should
+// only be called when there is no possibility that New or any other
+// UUID Version 4 generation function will be called concurrently.
+func EnableRandPool() {
+ poolEnabled = true
+}
+
+// DisableRandPool disables the randomness pool if it was previously
+// enabled with EnableRandPool.
+//
+// Both EnableRandPool and DisableRandPool are not thread-safe and should
+// only be called when there is no possibility that New or any other
+// UUID Version 4 generation function will be called concurrently.
+func DisableRandPool() {
+ poolEnabled = false
+ defer poolMu.Unlock()
+ poolMu.Lock()
+ poolPos = randPoolSize
+}
diff --git a/server/vendor/github.com/google/uuid/version1.go b/server/vendor/github.com/google/uuid/version1.go
new file mode 100644
index 0000000..4631096
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/version1.go
@@ -0,0 +1,44 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "encoding/binary"
+)
+
+// NewUUID returns a Version 1 UUID based on the current NodeID and clock
+// sequence, and the current time. If the NodeID has not been set by SetNodeID
+// or SetNodeInterface then it will be set automatically. If the NodeID cannot
+// be set NewUUID returns nil. If clock sequence has not been set by
+// SetClockSequence then it will be set automatically. If GetTime fails to
+// return the current NewUUID returns nil and an error.
+//
+// In most cases, New should be used.
+func NewUUID() (UUID, error) {
+ var uuid UUID
+ now, seq, err := GetTime()
+ if err != nil {
+ return uuid, err
+ }
+
+ timeLow := uint32(now & 0xffffffff)
+ timeMid := uint16((now >> 32) & 0xffff)
+ timeHi := uint16((now >> 48) & 0x0fff)
+ timeHi |= 0x1000 // Version 1
+
+ binary.BigEndian.PutUint32(uuid[0:], timeLow)
+ binary.BigEndian.PutUint16(uuid[4:], timeMid)
+ binary.BigEndian.PutUint16(uuid[6:], timeHi)
+ binary.BigEndian.PutUint16(uuid[8:], seq)
+
+ nodeMu.Lock()
+ if nodeID == zeroID {
+ setNodeInterface("")
+ }
+ copy(uuid[10:], nodeID[:])
+ nodeMu.Unlock()
+
+ return uuid, nil
+}
diff --git a/server/vendor/github.com/google/uuid/version4.go b/server/vendor/github.com/google/uuid/version4.go
new file mode 100644
index 0000000..7697802
--- /dev/null
+++ b/server/vendor/github.com/google/uuid/version4.go
@@ -0,0 +1,76 @@
+// Copyright 2016 Google Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import "io"
+
+// New creates a new random UUID or panics. New is equivalent to
+// the expression
+//
+// uuid.Must(uuid.NewRandom())
+func New() UUID {
+ return Must(NewRandom())
+}
+
+// NewString creates a new random UUID and returns it as a string or panics.
+// NewString is equivalent to the expression
+//
+// uuid.New().String()
+func NewString() string {
+ return Must(NewRandom()).String()
+}
+
+// NewRandom returns a Random (Version 4) UUID.
+//
+// The strength of the UUIDs is based on the strength of the crypto/rand
+// package.
+//
+// Uses the randomness pool if it was enabled with EnableRandPool.
+//
+// A note about uniqueness derived from the UUID Wikipedia entry:
+//
+// Randomly generated UUIDs have 122 random bits. One's annual risk of being
+// hit by a meteorite is estimated to be one chance in 17 billion, that
+// means the probability is about 0.00000000006 (6 × 10−11),
+// equivalent to the odds of creating a few tens of trillions of UUIDs in a
+// year and having one duplicate.
+func NewRandom() (UUID, error) {
+ if !poolEnabled {
+ return NewRandomFromReader(rander)
+ }
+ return newRandomFromPool()
+}
+
+// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader.
+func NewRandomFromReader(r io.Reader) (UUID, error) {
+ var uuid UUID
+ _, err := io.ReadFull(r, uuid[:])
+ if err != nil {
+ return Nil, err
+ }
+ uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
+ uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
+ return uuid, nil
+}
+
+func newRandomFromPool() (UUID, error) {
+ var uuid UUID
+ poolMu.Lock()
+ if poolPos == randPoolSize {
+ _, err := io.ReadFull(rander, pool[:])
+ if err != nil {
+ poolMu.Unlock()
+ return Nil, err
+ }
+ poolPos = 0
+ }
+ copy(uuid[:], pool[poolPos:(poolPos+16)])
+ poolPos += 16
+ poolMu.Unlock()
+
+ uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
+ uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
+ return uuid, nil
+}
diff --git a/server/vendor/github.com/gorilla/websocket/.gitignore b/server/vendor/github.com/gorilla/websocket/.gitignore
new file mode 100644
index 0000000..cd3fcd1
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/.gitignore
@@ -0,0 +1,25 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+
+.idea/
+*.iml
diff --git a/server/vendor/github.com/gorilla/websocket/AUTHORS b/server/vendor/github.com/gorilla/websocket/AUTHORS
new file mode 100644
index 0000000..1931f40
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/AUTHORS
@@ -0,0 +1,9 @@
+# This is the official list of Gorilla WebSocket authors for copyright
+# purposes.
+#
+# Please keep the list sorted.
+
+Gary Burd
+Google LLC (https://opensource.google.com/)
+Joachim Bauch
+
diff --git a/server/vendor/github.com/gorilla/websocket/LICENSE b/server/vendor/github.com/gorilla/websocket/LICENSE
new file mode 100644
index 0000000..9171c97
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/server/vendor/github.com/gorilla/websocket/README.md b/server/vendor/github.com/gorilla/websocket/README.md
new file mode 100644
index 0000000..d33ed7f
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/README.md
@@ -0,0 +1,33 @@
+# Gorilla WebSocket
+
+[](https://godoc.org/github.com/gorilla/websocket)
+[](https://circleci.com/gh/gorilla/websocket)
+
+Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
+[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
+
+
+### Documentation
+
+* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc)
+* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)
+* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
+* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
+* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)
+
+### Status
+
+The Gorilla WebSocket package provides a complete and tested implementation of
+the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The
+package API is stable.
+
+### Installation
+
+ go get github.com/gorilla/websocket
+
+### Protocol Compliance
+
+The Gorilla WebSocket package passes the server tests in the [Autobahn Test
+Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn
+subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).
+
diff --git a/server/vendor/github.com/gorilla/websocket/client.go b/server/vendor/github.com/gorilla/websocket/client.go
new file mode 100644
index 0000000..04fdafe
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/client.go
@@ -0,0 +1,434 @@
+// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package websocket
+
+import (
+ "bytes"
+ "context"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/http/httptrace"
+ "net/url"
+ "strings"
+ "time"
+)
+
+// ErrBadHandshake is returned when the server response to opening handshake is
+// invalid.
+var ErrBadHandshake = errors.New("websocket: bad handshake")
+
+var errInvalidCompression = errors.New("websocket: invalid compression negotiation")
+
+// NewClient creates a new client connection using the given net connection.
+// The URL u specifies the host and request URI. Use requestHeader to specify
+// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
+// (Cookie). Use the response.Header to get the selected subprotocol
+// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
+//
+// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
+// non-nil *http.Response so that callers can handle redirects, authentication,
+// etc.
+//
+// Deprecated: Use Dialer instead.
+func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) {
+ d := Dialer{
+ ReadBufferSize: readBufSize,
+ WriteBufferSize: writeBufSize,
+ NetDial: func(net, addr string) (net.Conn, error) {
+ return netConn, nil
+ },
+ }
+ return d.Dial(u.String(), requestHeader)
+}
+
+// A Dialer contains options for connecting to WebSocket server.
+//
+// It is safe to call Dialer's methods concurrently.
+type Dialer struct {
+ // NetDial specifies the dial function for creating TCP connections. If
+ // NetDial is nil, net.Dial is used.
+ NetDial func(network, addr string) (net.Conn, error)
+
+ // NetDialContext specifies the dial function for creating TCP connections. If
+ // NetDialContext is nil, NetDial is used.
+ NetDialContext func(ctx context.Context, network, addr string) (net.Conn, error)
+
+ // NetDialTLSContext specifies the dial function for creating TLS/TCP connections. If
+ // NetDialTLSContext is nil, NetDialContext is used.
+ // If NetDialTLSContext is set, Dial assumes the TLS handshake is done there and
+ // TLSClientConfig is ignored.
+ NetDialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error)
+
+ // Proxy specifies a function to return a proxy for a given
+ // Request. If the function returns a non-nil error, the
+ // request is aborted with the provided error.
+ // If Proxy is nil or returns a nil *URL, no proxy is used.
+ Proxy func(*http.Request) (*url.URL, error)
+
+ // TLSClientConfig specifies the TLS configuration to use with tls.Client.
+ // If nil, the default configuration is used.
+ // If either NetDialTLS or NetDialTLSContext are set, Dial assumes the TLS handshake
+ // is done there and TLSClientConfig is ignored.
+ TLSClientConfig *tls.Config
+
+ // HandshakeTimeout specifies the duration for the handshake to complete.
+ HandshakeTimeout time.Duration
+
+ // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer
+ // size is zero, then a useful default size is used. The I/O buffer sizes
+ // do not limit the size of the messages that can be sent or received.
+ ReadBufferSize, WriteBufferSize int
+
+ // WriteBufferPool is a pool of buffers for write operations. If the value
+ // is not set, then write buffers are allocated to the connection for the
+ // lifetime of the connection.
+ //
+ // A pool is most useful when the application has a modest volume of writes
+ // across a large number of connections.
+ //
+ // Applications should use a single pool for each unique value of
+ // WriteBufferSize.
+ WriteBufferPool BufferPool
+
+ // Subprotocols specifies the client's requested subprotocols.
+ Subprotocols []string
+
+ // EnableCompression specifies if the client should attempt to negotiate
+ // per message compression (RFC 7692). Setting this value to true does not
+ // guarantee that compression will be supported. Currently only "no context
+ // takeover" modes are supported.
+ EnableCompression bool
+
+ // Jar specifies the cookie jar.
+ // If Jar is nil, cookies are not sent in requests and ignored
+ // in responses.
+ Jar http.CookieJar
+}
+
+// Dial creates a new client connection by calling DialContext with a background context.
+func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
+ return d.DialContext(context.Background(), urlStr, requestHeader)
+}
+
+var errMalformedURL = errors.New("malformed ws or wss URL")
+
+func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
+ hostPort = u.Host
+ hostNoPort = u.Host
+ if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") {
+ hostNoPort = hostNoPort[:i]
+ } else {
+ switch u.Scheme {
+ case "wss":
+ hostPort += ":443"
+ case "https":
+ hostPort += ":443"
+ default:
+ hostPort += ":80"
+ }
+ }
+ return hostPort, hostNoPort
+}
+
+// DefaultDialer is a dialer with all fields set to the default values.
+var DefaultDialer = &Dialer{
+ Proxy: http.ProxyFromEnvironment,
+ HandshakeTimeout: 45 * time.Second,
+}
+
+// nilDialer is dialer to use when receiver is nil.
+var nilDialer = *DefaultDialer
+
+// DialContext creates a new client connection. Use requestHeader to specify the
+// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie).
+// Use the response.Header to get the selected subprotocol
+// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
+//
+// The context will be used in the request and in the Dialer.
+//
+// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
+// non-nil *http.Response so that callers can handle redirects, authentication,
+// etcetera. The response body may not contain the entire response and does not
+// need to be closed by the application.
+func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
+ if d == nil {
+ d = &nilDialer
+ }
+
+ challengeKey, err := generateChallengeKey()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ u, err := url.Parse(urlStr)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ switch u.Scheme {
+ case "ws":
+ u.Scheme = "http"
+ case "wss":
+ u.Scheme = "https"
+ default:
+ return nil, nil, errMalformedURL
+ }
+
+ if u.User != nil {
+ // User name and password are not allowed in websocket URIs.
+ return nil, nil, errMalformedURL
+ }
+
+ req := &http.Request{
+ Method: http.MethodGet,
+ URL: u,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Header: make(http.Header),
+ Host: u.Host,
+ }
+ req = req.WithContext(ctx)
+
+ // Set the cookies present in the cookie jar of the dialer
+ if d.Jar != nil {
+ for _, cookie := range d.Jar.Cookies(u) {
+ req.AddCookie(cookie)
+ }
+ }
+
+ // Set the request headers using the capitalization for names and values in
+ // RFC examples. Although the capitalization shouldn't matter, there are
+ // servers that depend on it. The Header.Set method is not used because the
+ // method canonicalizes the header names.
+ req.Header["Upgrade"] = []string{"websocket"}
+ req.Header["Connection"] = []string{"Upgrade"}
+ req.Header["Sec-WebSocket-Key"] = []string{challengeKey}
+ req.Header["Sec-WebSocket-Version"] = []string{"13"}
+ if len(d.Subprotocols) > 0 {
+ req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")}
+ }
+ for k, vs := range requestHeader {
+ switch {
+ case k == "Host":
+ if len(vs) > 0 {
+ req.Host = vs[0]
+ }
+ case k == "Upgrade" ||
+ k == "Connection" ||
+ k == "Sec-Websocket-Key" ||
+ k == "Sec-Websocket-Version" ||
+ k == "Sec-Websocket-Extensions" ||
+ (k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
+ return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
+ case k == "Sec-Websocket-Protocol":
+ req.Header["Sec-WebSocket-Protocol"] = vs
+ default:
+ req.Header[k] = vs
+ }
+ }
+
+ if d.EnableCompression {
+ req.Header["Sec-WebSocket-Extensions"] = []string{"permessage-deflate; server_no_context_takeover; client_no_context_takeover"}
+ }
+
+ if d.HandshakeTimeout != 0 {
+ var cancel func()
+ ctx, cancel = context.WithTimeout(ctx, d.HandshakeTimeout)
+ defer cancel()
+ }
+
+ // Get network dial function.
+ var netDial func(network, add string) (net.Conn, error)
+
+ switch u.Scheme {
+ case "http":
+ if d.NetDialContext != nil {
+ netDial = func(network, addr string) (net.Conn, error) {
+ return d.NetDialContext(ctx, network, addr)
+ }
+ } else if d.NetDial != nil {
+ netDial = d.NetDial
+ }
+ case "https":
+ if d.NetDialTLSContext != nil {
+ netDial = func(network, addr string) (net.Conn, error) {
+ return d.NetDialTLSContext(ctx, network, addr)
+ }
+ } else if d.NetDialContext != nil {
+ netDial = func(network, addr string) (net.Conn, error) {
+ return d.NetDialContext(ctx, network, addr)
+ }
+ } else if d.NetDial != nil {
+ netDial = d.NetDial
+ }
+ default:
+ return nil, nil, errMalformedURL
+ }
+
+ if netDial == nil {
+ netDialer := &net.Dialer{}
+ netDial = func(network, addr string) (net.Conn, error) {
+ return netDialer.DialContext(ctx, network, addr)
+ }
+ }
+
+ // If needed, wrap the dial function to set the connection deadline.
+ if deadline, ok := ctx.Deadline(); ok {
+ forwardDial := netDial
+ netDial = func(network, addr string) (net.Conn, error) {
+ c, err := forwardDial(network, addr)
+ if err != nil {
+ return nil, err
+ }
+ err = c.SetDeadline(deadline)
+ if err != nil {
+ c.Close()
+ return nil, err
+ }
+ return c, nil
+ }
+ }
+
+ // If needed, wrap the dial function to connect through a proxy.
+ if d.Proxy != nil {
+ proxyURL, err := d.Proxy(req)
+ if err != nil {
+ return nil, nil, err
+ }
+ if proxyURL != nil {
+ dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial))
+ if err != nil {
+ return nil, nil, err
+ }
+ netDial = dialer.Dial
+ }
+ }
+
+ hostPort, hostNoPort := hostPortNoPort(u)
+ trace := httptrace.ContextClientTrace(ctx)
+ if trace != nil && trace.GetConn != nil {
+ trace.GetConn(hostPort)
+ }
+
+ netConn, err := netDial("tcp", hostPort)
+ if err != nil {
+ return nil, nil, err
+ }
+ if trace != nil && trace.GotConn != nil {
+ trace.GotConn(httptrace.GotConnInfo{
+ Conn: netConn,
+ })
+ }
+
+ defer func() {
+ if netConn != nil {
+ netConn.Close()
+ }
+ }()
+
+ if u.Scheme == "https" && d.NetDialTLSContext == nil {
+ // If NetDialTLSContext is set, assume that the TLS handshake has already been done
+
+ cfg := cloneTLSConfig(d.TLSClientConfig)
+ if cfg.ServerName == "" {
+ cfg.ServerName = hostNoPort
+ }
+ tlsConn := tls.Client(netConn, cfg)
+ netConn = tlsConn
+
+ if trace != nil && trace.TLSHandshakeStart != nil {
+ trace.TLSHandshakeStart()
+ }
+ err := doHandshake(ctx, tlsConn, cfg)
+ if trace != nil && trace.TLSHandshakeDone != nil {
+ trace.TLSHandshakeDone(tlsConn.ConnectionState(), err)
+ }
+
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+
+ conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize, d.WriteBufferPool, nil, nil)
+
+ if err := req.Write(netConn); err != nil {
+ return nil, nil, err
+ }
+
+ if trace != nil && trace.GotFirstResponseByte != nil {
+ if peek, err := conn.br.Peek(1); err == nil && len(peek) == 1 {
+ trace.GotFirstResponseByte()
+ }
+ }
+
+ resp, err := http.ReadResponse(conn.br, req)
+ if err != nil {
+ if d.TLSClientConfig != nil {
+ for _, proto := range d.TLSClientConfig.NextProtos {
+ if proto != "http/1.1" {
+ return nil, nil, fmt.Errorf(
+ "websocket: protocol %q was given but is not supported;"+
+ "sharing tls.Config with net/http Transport can cause this error: %w",
+ proto, err,
+ )
+ }
+ }
+ }
+ return nil, nil, err
+ }
+
+ if d.Jar != nil {
+ if rc := resp.Cookies(); len(rc) > 0 {
+ d.Jar.SetCookies(u, rc)
+ }
+ }
+
+ if resp.StatusCode != 101 ||
+ !tokenListContainsValue(resp.Header, "Upgrade", "websocket") ||
+ !tokenListContainsValue(resp.Header, "Connection", "upgrade") ||
+ resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) {
+ // Before closing the network connection on return from this
+ // function, slurp up some of the response to aid application
+ // debugging.
+ buf := make([]byte, 1024)
+ n, _ := io.ReadFull(resp.Body, buf)
+ resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n]))
+ return nil, resp, ErrBadHandshake
+ }
+
+ for _, ext := range parseExtensions(resp.Header) {
+ if ext[""] != "permessage-deflate" {
+ continue
+ }
+ _, snct := ext["server_no_context_takeover"]
+ _, cnct := ext["client_no_context_takeover"]
+ if !snct || !cnct {
+ return nil, resp, errInvalidCompression
+ }
+ conn.newCompressionWriter = compressNoContextTakeover
+ conn.newDecompressionReader = decompressNoContextTakeover
+ break
+ }
+
+ resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
+ conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
+
+ netConn.SetDeadline(time.Time{})
+ netConn = nil // to avoid close in defer.
+ return conn, resp, nil
+}
+
+func cloneTLSConfig(cfg *tls.Config) *tls.Config {
+ if cfg == nil {
+ return &tls.Config{}
+ }
+ return cfg.Clone()
+}
diff --git a/server/vendor/github.com/gorilla/websocket/compression.go b/server/vendor/github.com/gorilla/websocket/compression.go
new file mode 100644
index 0000000..813ffb1
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/compression.go
@@ -0,0 +1,148 @@
+// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package websocket
+
+import (
+ "compress/flate"
+ "errors"
+ "io"
+ "strings"
+ "sync"
+)
+
+const (
+ minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6
+ maxCompressionLevel = flate.BestCompression
+ defaultCompressionLevel = 1
+)
+
+var (
+ flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool
+ flateReaderPool = sync.Pool{New: func() interface{} {
+ return flate.NewReader(nil)
+ }}
+)
+
+func decompressNoContextTakeover(r io.Reader) io.ReadCloser {
+ const tail =
+ // Add four bytes as specified in RFC
+ "\x00\x00\xff\xff" +
+ // Add final block to squelch unexpected EOF error from flate reader.
+ "\x01\x00\x00\xff\xff"
+
+ fr, _ := flateReaderPool.Get().(io.ReadCloser)
+ fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
+ return &flateReadWrapper{fr}
+}
+
+func isValidCompressionLevel(level int) bool {
+ return minCompressionLevel <= level && level <= maxCompressionLevel
+}
+
+func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser {
+ p := &flateWriterPools[level-minCompressionLevel]
+ tw := &truncWriter{w: w}
+ fw, _ := p.Get().(*flate.Writer)
+ if fw == nil {
+ fw, _ = flate.NewWriter(tw, level)
+ } else {
+ fw.Reset(tw)
+ }
+ return &flateWriteWrapper{fw: fw, tw: tw, p: p}
+}
+
+// truncWriter is an io.Writer that writes all but the last four bytes of the
+// stream to another io.Writer.
+type truncWriter struct {
+ w io.WriteCloser
+ n int
+ p [4]byte
+}
+
+func (w *truncWriter) Write(p []byte) (int, error) {
+ n := 0
+
+ // fill buffer first for simplicity.
+ if w.n < len(w.p) {
+ n = copy(w.p[w.n:], p)
+ p = p[n:]
+ w.n += n
+ if len(p) == 0 {
+ return n, nil
+ }
+ }
+
+ m := len(p)
+ if m > len(w.p) {
+ m = len(w.p)
+ }
+
+ if nn, err := w.w.Write(w.p[:m]); err != nil {
+ return n + nn, err
+ }
+
+ copy(w.p[:], w.p[m:])
+ copy(w.p[len(w.p)-m:], p[len(p)-m:])
+ nn, err := w.w.Write(p[:len(p)-m])
+ return n + nn, err
+}
+
+type flateWriteWrapper struct {
+ fw *flate.Writer
+ tw *truncWriter
+ p *sync.Pool
+}
+
+func (w *flateWriteWrapper) Write(p []byte) (int, error) {
+ if w.fw == nil {
+ return 0, errWriteClosed
+ }
+ return w.fw.Write(p)
+}
+
+func (w *flateWriteWrapper) Close() error {
+ if w.fw == nil {
+ return errWriteClosed
+ }
+ err1 := w.fw.Flush()
+ w.p.Put(w.fw)
+ w.fw = nil
+ if w.tw.p != [4]byte{0, 0, 0xff, 0xff} {
+ return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
+ }
+ err2 := w.tw.w.Close()
+ if err1 != nil {
+ return err1
+ }
+ return err2
+}
+
+type flateReadWrapper struct {
+ fr io.ReadCloser
+}
+
+func (r *flateReadWrapper) Read(p []byte) (int, error) {
+ if r.fr == nil {
+ return 0, io.ErrClosedPipe
+ }
+ n, err := r.fr.Read(p)
+ if err == io.EOF {
+ // Preemptively place the reader back in the pool. This helps with
+ // scenarios where the application does not call NextReader() soon after
+ // this final read.
+ r.Close()
+ }
+ return n, err
+}
+
+func (r *flateReadWrapper) Close() error {
+ if r.fr == nil {
+ return io.ErrClosedPipe
+ }
+ err := r.fr.Close()
+ flateReaderPool.Put(r.fr)
+ r.fr = nil
+ return err
+}
diff --git a/server/vendor/github.com/gorilla/websocket/conn.go b/server/vendor/github.com/gorilla/websocket/conn.go
new file mode 100644
index 0000000..5161ef8
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/conn.go
@@ -0,0 +1,1238 @@
+// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package websocket
+
+import (
+ "bufio"
+ "encoding/binary"
+ "errors"
+ "io"
+ "io/ioutil"
+ "math/rand"
+ "net"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+ "unicode/utf8"
+)
+
+const (
+ // Frame header byte 0 bits from Section 5.2 of RFC 6455
+ finalBit = 1 << 7
+ rsv1Bit = 1 << 6
+ rsv2Bit = 1 << 5
+ rsv3Bit = 1 << 4
+
+ // Frame header byte 1 bits from Section 5.2 of RFC 6455
+ maskBit = 1 << 7
+
+ maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask
+ maxControlFramePayloadSize = 125
+
+ writeWait = time.Second
+
+ defaultReadBufferSize = 4096
+ defaultWriteBufferSize = 4096
+
+ continuationFrame = 0
+ noFrame = -1
+)
+
+// Close codes defined in RFC 6455, section 11.7.
+const (
+ CloseNormalClosure = 1000
+ CloseGoingAway = 1001
+ CloseProtocolError = 1002
+ CloseUnsupportedData = 1003
+ CloseNoStatusReceived = 1005
+ CloseAbnormalClosure = 1006
+ CloseInvalidFramePayloadData = 1007
+ ClosePolicyViolation = 1008
+ CloseMessageTooBig = 1009
+ CloseMandatoryExtension = 1010
+ CloseInternalServerErr = 1011
+ CloseServiceRestart = 1012
+ CloseTryAgainLater = 1013
+ CloseTLSHandshake = 1015
+)
+
+// The message types are defined in RFC 6455, section 11.8.
+const (
+ // TextMessage denotes a text data message. The text message payload is
+ // interpreted as UTF-8 encoded text data.
+ TextMessage = 1
+
+ // BinaryMessage denotes a binary data message.
+ BinaryMessage = 2
+
+ // CloseMessage denotes a close control message. The optional message
+ // payload contains a numeric code and text. Use the FormatCloseMessage
+ // function to format a close message payload.
+ CloseMessage = 8
+
+ // PingMessage denotes a ping control message. The optional message payload
+ // is UTF-8 encoded text.
+ PingMessage = 9
+
+ // PongMessage denotes a pong control message. The optional message payload
+ // is UTF-8 encoded text.
+ PongMessage = 10
+)
+
+// ErrCloseSent is returned when the application writes a message to the
+// connection after sending a close message.
+var ErrCloseSent = errors.New("websocket: close sent")
+
+// ErrReadLimit is returned when reading a message that is larger than the
+// read limit set for the connection.
+var ErrReadLimit = errors.New("websocket: read limit exceeded")
+
+// netError satisfies the net Error interface.
+type netError struct {
+ msg string
+ temporary bool
+ timeout bool
+}
+
+func (e *netError) Error() string { return e.msg }
+func (e *netError) Temporary() bool { return e.temporary }
+func (e *netError) Timeout() bool { return e.timeout }
+
+// CloseError represents a close message.
+type CloseError struct {
+ // Code is defined in RFC 6455, section 11.7.
+ Code int
+
+ // Text is the optional text payload.
+ Text string
+}
+
+func (e *CloseError) Error() string {
+ s := []byte("websocket: close ")
+ s = strconv.AppendInt(s, int64(e.Code), 10)
+ switch e.Code {
+ case CloseNormalClosure:
+ s = append(s, " (normal)"...)
+ case CloseGoingAway:
+ s = append(s, " (going away)"...)
+ case CloseProtocolError:
+ s = append(s, " (protocol error)"...)
+ case CloseUnsupportedData:
+ s = append(s, " (unsupported data)"...)
+ case CloseNoStatusReceived:
+ s = append(s, " (no status)"...)
+ case CloseAbnormalClosure:
+ s = append(s, " (abnormal closure)"...)
+ case CloseInvalidFramePayloadData:
+ s = append(s, " (invalid payload data)"...)
+ case ClosePolicyViolation:
+ s = append(s, " (policy violation)"...)
+ case CloseMessageTooBig:
+ s = append(s, " (message too big)"...)
+ case CloseMandatoryExtension:
+ s = append(s, " (mandatory extension missing)"...)
+ case CloseInternalServerErr:
+ s = append(s, " (internal server error)"...)
+ case CloseTLSHandshake:
+ s = append(s, " (TLS handshake error)"...)
+ }
+ if e.Text != "" {
+ s = append(s, ": "...)
+ s = append(s, e.Text...)
+ }
+ return string(s)
+}
+
+// IsCloseError returns boolean indicating whether the error is a *CloseError
+// with one of the specified codes.
+func IsCloseError(err error, codes ...int) bool {
+ if e, ok := err.(*CloseError); ok {
+ for _, code := range codes {
+ if e.Code == code {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// IsUnexpectedCloseError returns boolean indicating whether the error is a
+// *CloseError with a code not in the list of expected codes.
+func IsUnexpectedCloseError(err error, expectedCodes ...int) bool {
+ if e, ok := err.(*CloseError); ok {
+ for _, code := range expectedCodes {
+ if e.Code == code {
+ return false
+ }
+ }
+ return true
+ }
+ return false
+}
+
+var (
+ errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true, temporary: true}
+ errUnexpectedEOF = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()}
+ errBadWriteOpCode = errors.New("websocket: bad write message type")
+ errWriteClosed = errors.New("websocket: write closed")
+ errInvalidControlFrame = errors.New("websocket: invalid control frame")
+)
+
+func newMaskKey() [4]byte {
+ n := rand.Uint32()
+ return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
+}
+
+func hideTempErr(err error) error {
+ if e, ok := err.(net.Error); ok && e.Temporary() {
+ err = &netError{msg: e.Error(), timeout: e.Timeout()}
+ }
+ return err
+}
+
+func isControl(frameType int) bool {
+ return frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage
+}
+
+func isData(frameType int) bool {
+ return frameType == TextMessage || frameType == BinaryMessage
+}
+
+var validReceivedCloseCodes = map[int]bool{
+ // see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number
+
+ CloseNormalClosure: true,
+ CloseGoingAway: true,
+ CloseProtocolError: true,
+ CloseUnsupportedData: true,
+ CloseNoStatusReceived: false,
+ CloseAbnormalClosure: false,
+ CloseInvalidFramePayloadData: true,
+ ClosePolicyViolation: true,
+ CloseMessageTooBig: true,
+ CloseMandatoryExtension: true,
+ CloseInternalServerErr: true,
+ CloseServiceRestart: true,
+ CloseTryAgainLater: true,
+ CloseTLSHandshake: false,
+}
+
+func isValidReceivedCloseCode(code int) bool {
+ return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)
+}
+
+// BufferPool represents a pool of buffers. The *sync.Pool type satisfies this
+// interface. The type of the value stored in a pool is not specified.
+type BufferPool interface {
+ // Get gets a value from the pool or returns nil if the pool is empty.
+ Get() interface{}
+ // Put adds a value to the pool.
+ Put(interface{})
+}
+
+// writePoolData is the type added to the write buffer pool. This wrapper is
+// used to prevent applications from peeking at and depending on the values
+// added to the pool.
+type writePoolData struct{ buf []byte }
+
+// The Conn type represents a WebSocket connection.
+type Conn struct {
+ conn net.Conn
+ isServer bool
+ subprotocol string
+
+ // Write fields
+ mu chan struct{} // used as mutex to protect write to conn
+ writeBuf []byte // frame is constructed in this buffer.
+ writePool BufferPool
+ writeBufSize int
+ writeDeadline time.Time
+ writer io.WriteCloser // the current writer returned to the application
+ isWriting bool // for best-effort concurrent write detection
+
+ writeErrMu sync.Mutex
+ writeErr error
+
+ enableWriteCompression bool
+ compressionLevel int
+ newCompressionWriter func(io.WriteCloser, int) io.WriteCloser
+
+ // Read fields
+ reader io.ReadCloser // the current reader returned to the application
+ readErr error
+ br *bufio.Reader
+ // bytes remaining in current frame.
+ // set setReadRemaining to safely update this value and prevent overflow
+ readRemaining int64
+ readFinal bool // true the current message has more frames.
+ readLength int64 // Message size.
+ readLimit int64 // Maximum message size.
+ readMaskPos int
+ readMaskKey [4]byte
+ handlePong func(string) error
+ handlePing func(string) error
+ handleClose func(int, string) error
+ readErrCount int
+ messageReader *messageReader // the current low-level reader
+
+ readDecompress bool // whether last read frame had RSV1 set
+ newDecompressionReader func(io.Reader) io.ReadCloser
+}
+
+func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, writeBufferPool BufferPool, br *bufio.Reader, writeBuf []byte) *Conn {
+
+ if br == nil {
+ if readBufferSize == 0 {
+ readBufferSize = defaultReadBufferSize
+ } else if readBufferSize < maxControlFramePayloadSize {
+ // must be large enough for control frame
+ readBufferSize = maxControlFramePayloadSize
+ }
+ br = bufio.NewReaderSize(conn, readBufferSize)
+ }
+
+ if writeBufferSize <= 0 {
+ writeBufferSize = defaultWriteBufferSize
+ }
+ writeBufferSize += maxFrameHeaderSize
+
+ if writeBuf == nil && writeBufferPool == nil {
+ writeBuf = make([]byte, writeBufferSize)
+ }
+
+ mu := make(chan struct{}, 1)
+ mu <- struct{}{}
+ c := &Conn{
+ isServer: isServer,
+ br: br,
+ conn: conn,
+ mu: mu,
+ readFinal: true,
+ writeBuf: writeBuf,
+ writePool: writeBufferPool,
+ writeBufSize: writeBufferSize,
+ enableWriteCompression: true,
+ compressionLevel: defaultCompressionLevel,
+ }
+ c.SetCloseHandler(nil)
+ c.SetPingHandler(nil)
+ c.SetPongHandler(nil)
+ return c
+}
+
+// setReadRemaining tracks the number of bytes remaining on the connection. If n
+// overflows, an ErrReadLimit is returned.
+func (c *Conn) setReadRemaining(n int64) error {
+ if n < 0 {
+ return ErrReadLimit
+ }
+
+ c.readRemaining = n
+ return nil
+}
+
+// Subprotocol returns the negotiated protocol for the connection.
+func (c *Conn) Subprotocol() string {
+ return c.subprotocol
+}
+
+// Close closes the underlying network connection without sending or waiting
+// for a close message.
+func (c *Conn) Close() error {
+ return c.conn.Close()
+}
+
+// LocalAddr returns the local network address.
+func (c *Conn) LocalAddr() net.Addr {
+ return c.conn.LocalAddr()
+}
+
+// RemoteAddr returns the remote network address.
+func (c *Conn) RemoteAddr() net.Addr {
+ return c.conn.RemoteAddr()
+}
+
+// Write methods
+
+func (c *Conn) writeFatal(err error) error {
+ err = hideTempErr(err)
+ c.writeErrMu.Lock()
+ if c.writeErr == nil {
+ c.writeErr = err
+ }
+ c.writeErrMu.Unlock()
+ return err
+}
+
+func (c *Conn) read(n int) ([]byte, error) {
+ p, err := c.br.Peek(n)
+ if err == io.EOF {
+ err = errUnexpectedEOF
+ }
+ c.br.Discard(len(p))
+ return p, err
+}
+
+func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error {
+ <-c.mu
+ defer func() { c.mu <- struct{}{} }()
+
+ c.writeErrMu.Lock()
+ err := c.writeErr
+ c.writeErrMu.Unlock()
+ if err != nil {
+ return err
+ }
+
+ c.conn.SetWriteDeadline(deadline)
+ if len(buf1) == 0 {
+ _, err = c.conn.Write(buf0)
+ } else {
+ err = c.writeBufs(buf0, buf1)
+ }
+ if err != nil {
+ return c.writeFatal(err)
+ }
+ if frameType == CloseMessage {
+ c.writeFatal(ErrCloseSent)
+ }
+ return nil
+}
+
+func (c *Conn) writeBufs(bufs ...[]byte) error {
+ b := net.Buffers(bufs)
+ _, err := b.WriteTo(c.conn)
+ return err
+}
+
+// WriteControl writes a control message with the given deadline. The allowed
+// message types are CloseMessage, PingMessage and PongMessage.
+func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error {
+ if !isControl(messageType) {
+ return errBadWriteOpCode
+ }
+ if len(data) > maxControlFramePayloadSize {
+ return errInvalidControlFrame
+ }
+
+ b0 := byte(messageType) | finalBit
+ b1 := byte(len(data))
+ if !c.isServer {
+ b1 |= maskBit
+ }
+
+ buf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize)
+ buf = append(buf, b0, b1)
+
+ if c.isServer {
+ buf = append(buf, data...)
+ } else {
+ key := newMaskKey()
+ buf = append(buf, key[:]...)
+ buf = append(buf, data...)
+ maskBytes(key, 0, buf[6:])
+ }
+
+ d := 1000 * time.Hour
+ if !deadline.IsZero() {
+ d = deadline.Sub(time.Now())
+ if d < 0 {
+ return errWriteTimeout
+ }
+ }
+
+ timer := time.NewTimer(d)
+ select {
+ case <-c.mu:
+ timer.Stop()
+ case <-timer.C:
+ return errWriteTimeout
+ }
+ defer func() { c.mu <- struct{}{} }()
+
+ c.writeErrMu.Lock()
+ err := c.writeErr
+ c.writeErrMu.Unlock()
+ if err != nil {
+ return err
+ }
+
+ c.conn.SetWriteDeadline(deadline)
+ _, err = c.conn.Write(buf)
+ if err != nil {
+ return c.writeFatal(err)
+ }
+ if messageType == CloseMessage {
+ c.writeFatal(ErrCloseSent)
+ }
+ return err
+}
+
+// beginMessage prepares a connection and message writer for a new message.
+func (c *Conn) beginMessage(mw *messageWriter, messageType int) error {
+ // Close previous writer if not already closed by the application. It's
+ // probably better to return an error in this situation, but we cannot
+ // change this without breaking existing applications.
+ if c.writer != nil {
+ c.writer.Close()
+ c.writer = nil
+ }
+
+ if !isControl(messageType) && !isData(messageType) {
+ return errBadWriteOpCode
+ }
+
+ c.writeErrMu.Lock()
+ err := c.writeErr
+ c.writeErrMu.Unlock()
+ if err != nil {
+ return err
+ }
+
+ mw.c = c
+ mw.frameType = messageType
+ mw.pos = maxFrameHeaderSize
+
+ if c.writeBuf == nil {
+ wpd, ok := c.writePool.Get().(writePoolData)
+ if ok {
+ c.writeBuf = wpd.buf
+ } else {
+ c.writeBuf = make([]byte, c.writeBufSize)
+ }
+ }
+ return nil
+}
+
+// NextWriter returns a writer for the next message to send. The writer's Close
+// method flushes the complete message to the network.
+//
+// There can be at most one open writer on a connection. NextWriter closes the
+// previous writer if the application has not already done so.
+//
+// All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and
+// PongMessage) are supported.
+func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
+ var mw messageWriter
+ if err := c.beginMessage(&mw, messageType); err != nil {
+ return nil, err
+ }
+ c.writer = &mw
+ if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) {
+ w := c.newCompressionWriter(c.writer, c.compressionLevel)
+ mw.compress = true
+ c.writer = w
+ }
+ return c.writer, nil
+}
+
+type messageWriter struct {
+ c *Conn
+ compress bool // whether next call to flushFrame should set RSV1
+ pos int // end of data in writeBuf.
+ frameType int // type of the current frame.
+ err error
+}
+
+func (w *messageWriter) endMessage(err error) error {
+ if w.err != nil {
+ return err
+ }
+ c := w.c
+ w.err = err
+ c.writer = nil
+ if c.writePool != nil {
+ c.writePool.Put(writePoolData{buf: c.writeBuf})
+ c.writeBuf = nil
+ }
+ return err
+}
+
+// flushFrame writes buffered data and extra as a frame to the network. The
+// final argument indicates that this is the last frame in the message.
+func (w *messageWriter) flushFrame(final bool, extra []byte) error {
+ c := w.c
+ length := w.pos - maxFrameHeaderSize + len(extra)
+
+ // Check for invalid control frames.
+ if isControl(w.frameType) &&
+ (!final || length > maxControlFramePayloadSize) {
+ return w.endMessage(errInvalidControlFrame)
+ }
+
+ b0 := byte(w.frameType)
+ if final {
+ b0 |= finalBit
+ }
+ if w.compress {
+ b0 |= rsv1Bit
+ }
+ w.compress = false
+
+ b1 := byte(0)
+ if !c.isServer {
+ b1 |= maskBit
+ }
+
+ // Assume that the frame starts at beginning of c.writeBuf.
+ framePos := 0
+ if c.isServer {
+ // Adjust up if mask not included in the header.
+ framePos = 4
+ }
+
+ switch {
+ case length >= 65536:
+ c.writeBuf[framePos] = b0
+ c.writeBuf[framePos+1] = b1 | 127
+ binary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length))
+ case length > 125:
+ framePos += 6
+ c.writeBuf[framePos] = b0
+ c.writeBuf[framePos+1] = b1 | 126
+ binary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length))
+ default:
+ framePos += 8
+ c.writeBuf[framePos] = b0
+ c.writeBuf[framePos+1] = b1 | byte(length)
+ }
+
+ if !c.isServer {
+ key := newMaskKey()
+ copy(c.writeBuf[maxFrameHeaderSize-4:], key[:])
+ maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos])
+ if len(extra) > 0 {
+ return w.endMessage(c.writeFatal(errors.New("websocket: internal error, extra used in client mode")))
+ }
+ }
+
+ // Write the buffers to the connection with best-effort detection of
+ // concurrent writes. See the concurrency section in the package
+ // documentation for more info.
+
+ if c.isWriting {
+ panic("concurrent write to websocket connection")
+ }
+ c.isWriting = true
+
+ err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra)
+
+ if !c.isWriting {
+ panic("concurrent write to websocket connection")
+ }
+ c.isWriting = false
+
+ if err != nil {
+ return w.endMessage(err)
+ }
+
+ if final {
+ w.endMessage(errWriteClosed)
+ return nil
+ }
+
+ // Setup for next frame.
+ w.pos = maxFrameHeaderSize
+ w.frameType = continuationFrame
+ return nil
+}
+
+func (w *messageWriter) ncopy(max int) (int, error) {
+ n := len(w.c.writeBuf) - w.pos
+ if n <= 0 {
+ if err := w.flushFrame(false, nil); err != nil {
+ return 0, err
+ }
+ n = len(w.c.writeBuf) - w.pos
+ }
+ if n > max {
+ n = max
+ }
+ return n, nil
+}
+
+func (w *messageWriter) Write(p []byte) (int, error) {
+ if w.err != nil {
+ return 0, w.err
+ }
+
+ if len(p) > 2*len(w.c.writeBuf) && w.c.isServer {
+ // Don't buffer large messages.
+ err := w.flushFrame(false, p)
+ if err != nil {
+ return 0, err
+ }
+ return len(p), nil
+ }
+
+ nn := len(p)
+ for len(p) > 0 {
+ n, err := w.ncopy(len(p))
+ if err != nil {
+ return 0, err
+ }
+ copy(w.c.writeBuf[w.pos:], p[:n])
+ w.pos += n
+ p = p[n:]
+ }
+ return nn, nil
+}
+
+func (w *messageWriter) WriteString(p string) (int, error) {
+ if w.err != nil {
+ return 0, w.err
+ }
+
+ nn := len(p)
+ for len(p) > 0 {
+ n, err := w.ncopy(len(p))
+ if err != nil {
+ return 0, err
+ }
+ copy(w.c.writeBuf[w.pos:], p[:n])
+ w.pos += n
+ p = p[n:]
+ }
+ return nn, nil
+}
+
+func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {
+ if w.err != nil {
+ return 0, w.err
+ }
+ for {
+ if w.pos == len(w.c.writeBuf) {
+ err = w.flushFrame(false, nil)
+ if err != nil {
+ break
+ }
+ }
+ var n int
+ n, err = r.Read(w.c.writeBuf[w.pos:])
+ w.pos += n
+ nn += int64(n)
+ if err != nil {
+ if err == io.EOF {
+ err = nil
+ }
+ break
+ }
+ }
+ return nn, err
+}
+
+func (w *messageWriter) Close() error {
+ if w.err != nil {
+ return w.err
+ }
+ return w.flushFrame(true, nil)
+}
+
+// WritePreparedMessage writes prepared message into connection.
+func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error {
+ frameType, frameData, err := pm.frame(prepareKey{
+ isServer: c.isServer,
+ compress: c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType),
+ compressionLevel: c.compressionLevel,
+ })
+ if err != nil {
+ return err
+ }
+ if c.isWriting {
+ panic("concurrent write to websocket connection")
+ }
+ c.isWriting = true
+ err = c.write(frameType, c.writeDeadline, frameData, nil)
+ if !c.isWriting {
+ panic("concurrent write to websocket connection")
+ }
+ c.isWriting = false
+ return err
+}
+
+// WriteMessage is a helper method for getting a writer using NextWriter,
+// writing the message and closing the writer.
+func (c *Conn) WriteMessage(messageType int, data []byte) error {
+
+ if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) {
+ // Fast path with no allocations and single frame.
+
+ var mw messageWriter
+ if err := c.beginMessage(&mw, messageType); err != nil {
+ return err
+ }
+ n := copy(c.writeBuf[mw.pos:], data)
+ mw.pos += n
+ data = data[n:]
+ return mw.flushFrame(true, data)
+ }
+
+ w, err := c.NextWriter(messageType)
+ if err != nil {
+ return err
+ }
+ if _, err = w.Write(data); err != nil {
+ return err
+ }
+ return w.Close()
+}
+
+// SetWriteDeadline sets the write deadline on the underlying network
+// connection. After a write has timed out, the websocket state is corrupt and
+// all future writes will return an error. A zero value for t means writes will
+// not time out.
+func (c *Conn) SetWriteDeadline(t time.Time) error {
+ c.writeDeadline = t
+ return nil
+}
+
+// Read methods
+
+func (c *Conn) advanceFrame() (int, error) {
+ // 1. Skip remainder of previous frame.
+
+ if c.readRemaining > 0 {
+ if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil {
+ return noFrame, err
+ }
+ }
+
+ // 2. Read and parse first two bytes of frame header.
+ // To aid debugging, collect and report all errors in the first two bytes
+ // of the header.
+
+ var errors []string
+
+ p, err := c.read(2)
+ if err != nil {
+ return noFrame, err
+ }
+
+ frameType := int(p[0] & 0xf)
+ final := p[0]&finalBit != 0
+ rsv1 := p[0]&rsv1Bit != 0
+ rsv2 := p[0]&rsv2Bit != 0
+ rsv3 := p[0]&rsv3Bit != 0
+ mask := p[1]&maskBit != 0
+ c.setReadRemaining(int64(p[1] & 0x7f))
+
+ c.readDecompress = false
+ if rsv1 {
+ if c.newDecompressionReader != nil {
+ c.readDecompress = true
+ } else {
+ errors = append(errors, "RSV1 set")
+ }
+ }
+
+ if rsv2 {
+ errors = append(errors, "RSV2 set")
+ }
+
+ if rsv3 {
+ errors = append(errors, "RSV3 set")
+ }
+
+ switch frameType {
+ case CloseMessage, PingMessage, PongMessage:
+ if c.readRemaining > maxControlFramePayloadSize {
+ errors = append(errors, "len > 125 for control")
+ }
+ if !final {
+ errors = append(errors, "FIN not set on control")
+ }
+ case TextMessage, BinaryMessage:
+ if !c.readFinal {
+ errors = append(errors, "data before FIN")
+ }
+ c.readFinal = final
+ case continuationFrame:
+ if c.readFinal {
+ errors = append(errors, "continuation after FIN")
+ }
+ c.readFinal = final
+ default:
+ errors = append(errors, "bad opcode "+strconv.Itoa(frameType))
+ }
+
+ if mask != c.isServer {
+ errors = append(errors, "bad MASK")
+ }
+
+ if len(errors) > 0 {
+ return noFrame, c.handleProtocolError(strings.Join(errors, ", "))
+ }
+
+ // 3. Read and parse frame length as per
+ // https://tools.ietf.org/html/rfc6455#section-5.2
+ //
+ // The length of the "Payload data", in bytes: if 0-125, that is the payload
+ // length.
+ // - If 126, the following 2 bytes interpreted as a 16-bit unsigned
+ // integer are the payload length.
+ // - If 127, the following 8 bytes interpreted as
+ // a 64-bit unsigned integer (the most significant bit MUST be 0) are the
+ // payload length. Multibyte length quantities are expressed in network byte
+ // order.
+
+ switch c.readRemaining {
+ case 126:
+ p, err := c.read(2)
+ if err != nil {
+ return noFrame, err
+ }
+
+ if err := c.setReadRemaining(int64(binary.BigEndian.Uint16(p))); err != nil {
+ return noFrame, err
+ }
+ case 127:
+ p, err := c.read(8)
+ if err != nil {
+ return noFrame, err
+ }
+
+ if err := c.setReadRemaining(int64(binary.BigEndian.Uint64(p))); err != nil {
+ return noFrame, err
+ }
+ }
+
+ // 4. Handle frame masking.
+
+ if mask {
+ c.readMaskPos = 0
+ p, err := c.read(len(c.readMaskKey))
+ if err != nil {
+ return noFrame, err
+ }
+ copy(c.readMaskKey[:], p)
+ }
+
+ // 5. For text and binary messages, enforce read limit and return.
+
+ if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage {
+
+ c.readLength += c.readRemaining
+ // Don't allow readLength to overflow in the presence of a large readRemaining
+ // counter.
+ if c.readLength < 0 {
+ return noFrame, ErrReadLimit
+ }
+
+ if c.readLimit > 0 && c.readLength > c.readLimit {
+ c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait))
+ return noFrame, ErrReadLimit
+ }
+
+ return frameType, nil
+ }
+
+ // 6. Read control frame payload.
+
+ var payload []byte
+ if c.readRemaining > 0 {
+ payload, err = c.read(int(c.readRemaining))
+ c.setReadRemaining(0)
+ if err != nil {
+ return noFrame, err
+ }
+ if c.isServer {
+ maskBytes(c.readMaskKey, 0, payload)
+ }
+ }
+
+ // 7. Process control frame payload.
+
+ switch frameType {
+ case PongMessage:
+ if err := c.handlePong(string(payload)); err != nil {
+ return noFrame, err
+ }
+ case PingMessage:
+ if err := c.handlePing(string(payload)); err != nil {
+ return noFrame, err
+ }
+ case CloseMessage:
+ closeCode := CloseNoStatusReceived
+ closeText := ""
+ if len(payload) >= 2 {
+ closeCode = int(binary.BigEndian.Uint16(payload))
+ if !isValidReceivedCloseCode(closeCode) {
+ return noFrame, c.handleProtocolError("bad close code " + strconv.Itoa(closeCode))
+ }
+ closeText = string(payload[2:])
+ if !utf8.ValidString(closeText) {
+ return noFrame, c.handleProtocolError("invalid utf8 payload in close frame")
+ }
+ }
+ if err := c.handleClose(closeCode, closeText); err != nil {
+ return noFrame, err
+ }
+ return noFrame, &CloseError{Code: closeCode, Text: closeText}
+ }
+
+ return frameType, nil
+}
+
+func (c *Conn) handleProtocolError(message string) error {
+ data := FormatCloseMessage(CloseProtocolError, message)
+ if len(data) > maxControlFramePayloadSize {
+ data = data[:maxControlFramePayloadSize]
+ }
+ c.WriteControl(CloseMessage, data, time.Now().Add(writeWait))
+ return errors.New("websocket: " + message)
+}
+
+// NextReader returns the next data message received from the peer. The
+// returned messageType is either TextMessage or BinaryMessage.
+//
+// There can be at most one open reader on a connection. NextReader discards
+// the previous message if the application has not already consumed it.
+//
+// Applications must break out of the application's read loop when this method
+// returns a non-nil error value. Errors returned from this method are
+// permanent. Once this method returns a non-nil error, all subsequent calls to
+// this method return the same error.
+func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
+ // Close previous reader, only relevant for decompression.
+ if c.reader != nil {
+ c.reader.Close()
+ c.reader = nil
+ }
+
+ c.messageReader = nil
+ c.readLength = 0
+
+ for c.readErr == nil {
+ frameType, err := c.advanceFrame()
+ if err != nil {
+ c.readErr = hideTempErr(err)
+ break
+ }
+
+ if frameType == TextMessage || frameType == BinaryMessage {
+ c.messageReader = &messageReader{c}
+ c.reader = c.messageReader
+ if c.readDecompress {
+ c.reader = c.newDecompressionReader(c.reader)
+ }
+ return frameType, c.reader, nil
+ }
+ }
+
+ // Applications that do handle the error returned from this method spin in
+ // tight loop on connection failure. To help application developers detect
+ // this error, panic on repeated reads to the failed connection.
+ c.readErrCount++
+ if c.readErrCount >= 1000 {
+ panic("repeated read on failed websocket connection")
+ }
+
+ return noFrame, nil, c.readErr
+}
+
+type messageReader struct{ c *Conn }
+
+func (r *messageReader) Read(b []byte) (int, error) {
+ c := r.c
+ if c.messageReader != r {
+ return 0, io.EOF
+ }
+
+ for c.readErr == nil {
+
+ if c.readRemaining > 0 {
+ if int64(len(b)) > c.readRemaining {
+ b = b[:c.readRemaining]
+ }
+ n, err := c.br.Read(b)
+ c.readErr = hideTempErr(err)
+ if c.isServer {
+ c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n])
+ }
+ rem := c.readRemaining
+ rem -= int64(n)
+ c.setReadRemaining(rem)
+ if c.readRemaining > 0 && c.readErr == io.EOF {
+ c.readErr = errUnexpectedEOF
+ }
+ return n, c.readErr
+ }
+
+ if c.readFinal {
+ c.messageReader = nil
+ return 0, io.EOF
+ }
+
+ frameType, err := c.advanceFrame()
+ switch {
+ case err != nil:
+ c.readErr = hideTempErr(err)
+ case frameType == TextMessage || frameType == BinaryMessage:
+ c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader")
+ }
+ }
+
+ err := c.readErr
+ if err == io.EOF && c.messageReader == r {
+ err = errUnexpectedEOF
+ }
+ return 0, err
+}
+
+func (r *messageReader) Close() error {
+ return nil
+}
+
+// ReadMessage is a helper method for getting a reader using NextReader and
+// reading from that reader to a buffer.
+func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
+ var r io.Reader
+ messageType, r, err = c.NextReader()
+ if err != nil {
+ return messageType, nil, err
+ }
+ p, err = ioutil.ReadAll(r)
+ return messageType, p, err
+}
+
+// SetReadDeadline sets the read deadline on the underlying network connection.
+// After a read has timed out, the websocket connection state is corrupt and
+// all future reads will return an error. A zero value for t means reads will
+// not time out.
+func (c *Conn) SetReadDeadline(t time.Time) error {
+ return c.conn.SetReadDeadline(t)
+}
+
+// SetReadLimit sets the maximum size in bytes for a message read from the peer. If a
+// message exceeds the limit, the connection sends a close message to the peer
+// and returns ErrReadLimit to the application.
+func (c *Conn) SetReadLimit(limit int64) {
+ c.readLimit = limit
+}
+
+// CloseHandler returns the current close handler
+func (c *Conn) CloseHandler() func(code int, text string) error {
+ return c.handleClose
+}
+
+// SetCloseHandler sets the handler for close messages received from the peer.
+// The code argument to h is the received close code or CloseNoStatusReceived
+// if the close message is empty. The default close handler sends a close
+// message back to the peer.
+//
+// The handler function is called from the NextReader, ReadMessage and message
+// reader Read methods. The application must read the connection to process
+// close messages as described in the section on Control Messages above.
+//
+// The connection read methods return a CloseError when a close message is
+// received. Most applications should handle close messages as part of their
+// normal error handling. Applications should only set a close handler when the
+// application must perform some action before sending a close message back to
+// the peer.
+func (c *Conn) SetCloseHandler(h func(code int, text string) error) {
+ if h == nil {
+ h = func(code int, text string) error {
+ message := FormatCloseMessage(code, "")
+ c.WriteControl(CloseMessage, message, time.Now().Add(writeWait))
+ return nil
+ }
+ }
+ c.handleClose = h
+}
+
+// PingHandler returns the current ping handler
+func (c *Conn) PingHandler() func(appData string) error {
+ return c.handlePing
+}
+
+// SetPingHandler sets the handler for ping messages received from the peer.
+// The appData argument to h is the PING message application data. The default
+// ping handler sends a pong to the peer.
+//
+// The handler function is called from the NextReader, ReadMessage and message
+// reader Read methods. The application must read the connection to process
+// ping messages as described in the section on Control Messages above.
+func (c *Conn) SetPingHandler(h func(appData string) error) {
+ if h == nil {
+ h = func(message string) error {
+ err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait))
+ if err == ErrCloseSent {
+ return nil
+ } else if e, ok := err.(net.Error); ok && e.Temporary() {
+ return nil
+ }
+ return err
+ }
+ }
+ c.handlePing = h
+}
+
+// PongHandler returns the current pong handler
+func (c *Conn) PongHandler() func(appData string) error {
+ return c.handlePong
+}
+
+// SetPongHandler sets the handler for pong messages received from the peer.
+// The appData argument to h is the PONG message application data. The default
+// pong handler does nothing.
+//
+// The handler function is called from the NextReader, ReadMessage and message
+// reader Read methods. The application must read the connection to process
+// pong messages as described in the section on Control Messages above.
+func (c *Conn) SetPongHandler(h func(appData string) error) {
+ if h == nil {
+ h = func(string) error { return nil }
+ }
+ c.handlePong = h
+}
+
+// NetConn returns the underlying connection that is wrapped by c.
+// Note that writing to or reading from this connection directly will corrupt the
+// WebSocket connection.
+func (c *Conn) NetConn() net.Conn {
+ return c.conn
+}
+
+// UnderlyingConn returns the internal net.Conn. This can be used to further
+// modifications to connection specific flags.
+// Deprecated: Use the NetConn method.
+func (c *Conn) UnderlyingConn() net.Conn {
+ return c.conn
+}
+
+// EnableWriteCompression enables and disables write compression of
+// subsequent text and binary messages. This function is a noop if
+// compression was not negotiated with the peer.
+func (c *Conn) EnableWriteCompression(enable bool) {
+ c.enableWriteCompression = enable
+}
+
+// SetCompressionLevel sets the flate compression level for subsequent text and
+// binary messages. This function is a noop if compression was not negotiated
+// with the peer. See the compress/flate package for a description of
+// compression levels.
+func (c *Conn) SetCompressionLevel(level int) error {
+ if !isValidCompressionLevel(level) {
+ return errors.New("websocket: invalid compression level")
+ }
+ c.compressionLevel = level
+ return nil
+}
+
+// FormatCloseMessage formats closeCode and text as a WebSocket close message.
+// An empty message is returned for code CloseNoStatusReceived.
+func FormatCloseMessage(closeCode int, text string) []byte {
+ if closeCode == CloseNoStatusReceived {
+ // Return empty message because it's illegal to send
+ // CloseNoStatusReceived. Return non-nil value in case application
+ // checks for nil.
+ return []byte{}
+ }
+ buf := make([]byte, 2+len(text))
+ binary.BigEndian.PutUint16(buf, uint16(closeCode))
+ copy(buf[2:], text)
+ return buf
+}
diff --git a/server/vendor/github.com/gorilla/websocket/doc.go b/server/vendor/github.com/gorilla/websocket/doc.go
new file mode 100644
index 0000000..8db0cef
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/doc.go
@@ -0,0 +1,227 @@
+// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package websocket implements the WebSocket protocol defined in RFC 6455.
+//
+// Overview
+//
+// The Conn type represents a WebSocket connection. A server application calls
+// the Upgrader.Upgrade method from an HTTP request handler to get a *Conn:
+//
+// var upgrader = websocket.Upgrader{
+// ReadBufferSize: 1024,
+// WriteBufferSize: 1024,
+// }
+//
+// func handler(w http.ResponseWriter, r *http.Request) {
+// conn, err := upgrader.Upgrade(w, r, nil)
+// if err != nil {
+// log.Println(err)
+// return
+// }
+// ... Use conn to send and receive messages.
+// }
+//
+// Call the connection's WriteMessage and ReadMessage methods to send and
+// receive messages as a slice of bytes. This snippet of code shows how to echo
+// messages using these methods:
+//
+// for {
+// messageType, p, err := conn.ReadMessage()
+// if err != nil {
+// log.Println(err)
+// return
+// }
+// if err := conn.WriteMessage(messageType, p); err != nil {
+// log.Println(err)
+// return
+// }
+// }
+//
+// In above snippet of code, p is a []byte and messageType is an int with value
+// websocket.BinaryMessage or websocket.TextMessage.
+//
+// An application can also send and receive messages using the io.WriteCloser
+// and io.Reader interfaces. To send a message, call the connection NextWriter
+// method to get an io.WriteCloser, write the message to the writer and close
+// the writer when done. To receive a message, call the connection NextReader
+// method to get an io.Reader and read until io.EOF is returned. This snippet
+// shows how to echo messages using the NextWriter and NextReader methods:
+//
+// for {
+// messageType, r, err := conn.NextReader()
+// if err != nil {
+// return
+// }
+// w, err := conn.NextWriter(messageType)
+// if err != nil {
+// return err
+// }
+// if _, err := io.Copy(w, r); err != nil {
+// return err
+// }
+// if err := w.Close(); err != nil {
+// return err
+// }
+// }
+//
+// Data Messages
+//
+// The WebSocket protocol distinguishes between text and binary data messages.
+// Text messages are interpreted as UTF-8 encoded text. The interpretation of
+// binary messages is left to the application.
+//
+// This package uses the TextMessage and BinaryMessage integer constants to
+// identify the two data message types. The ReadMessage and NextReader methods
+// return the type of the received message. The messageType argument to the
+// WriteMessage and NextWriter methods specifies the type of a sent message.
+//
+// It is the application's responsibility to ensure that text messages are
+// valid UTF-8 encoded text.
+//
+// Control Messages
+//
+// The WebSocket protocol defines three types of control messages: close, ping
+// and pong. Call the connection WriteControl, WriteMessage or NextWriter
+// methods to send a control message to the peer.
+//
+// Connections handle received close messages by calling the handler function
+// set with the SetCloseHandler method and by returning a *CloseError from the
+// NextReader, ReadMessage or the message Read method. The default close
+// handler sends a close message to the peer.
+//
+// Connections handle received ping messages by calling the handler function
+// set with the SetPingHandler method. The default ping handler sends a pong
+// message to the peer.
+//
+// Connections handle received pong messages by calling the handler function
+// set with the SetPongHandler method. The default pong handler does nothing.
+// If an application sends ping messages, then the application should set a
+// pong handler to receive the corresponding pong.
+//
+// The control message handler functions are called from the NextReader,
+// ReadMessage and message reader Read methods. The default close and ping
+// handlers can block these methods for a short time when the handler writes to
+// the connection.
+//
+// The application must read the connection to process close, ping and pong
+// messages sent from the peer. If the application is not otherwise interested
+// in messages from the peer, then the application should start a goroutine to
+// read and discard messages from the peer. A simple example is:
+//
+// func readLoop(c *websocket.Conn) {
+// for {
+// if _, _, err := c.NextReader(); err != nil {
+// c.Close()
+// break
+// }
+// }
+// }
+//
+// Concurrency
+//
+// Connections support one concurrent reader and one concurrent writer.
+//
+// Applications are responsible for ensuring that no more than one goroutine
+// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
+// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and
+// that no more than one goroutine calls the read methods (NextReader,
+// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler)
+// concurrently.
+//
+// The Close and WriteControl methods can be called concurrently with all other
+// methods.
+//
+// Origin Considerations
+//
+// Web browsers allow Javascript applications to open a WebSocket connection to
+// any host. It's up to the server to enforce an origin policy using the Origin
+// request header sent by the browser.
+//
+// The Upgrader calls the function specified in the CheckOrigin field to check
+// the origin. If the CheckOrigin function returns false, then the Upgrade
+// method fails the WebSocket handshake with HTTP status 403.
+//
+// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail
+// the handshake if the Origin request header is present and the Origin host is
+// not equal to the Host request header.
+//
+// The deprecated package-level Upgrade function does not perform origin
+// checking. The application is responsible for checking the Origin header
+// before calling the Upgrade function.
+//
+// Buffers
+//
+// Connections buffer network input and output to reduce the number
+// of system calls when reading or writing messages.
+//
+// Write buffers are also used for constructing WebSocket frames. See RFC 6455,
+// Section 5 for a discussion of message framing. A WebSocket frame header is
+// written to the network each time a write buffer is flushed to the network.
+// Decreasing the size of the write buffer can increase the amount of framing
+// overhead on the connection.
+//
+// The buffer sizes in bytes are specified by the ReadBufferSize and
+// WriteBufferSize fields in the Dialer and Upgrader. The Dialer uses a default
+// size of 4096 when a buffer size field is set to zero. The Upgrader reuses
+// buffers created by the HTTP server when a buffer size field is set to zero.
+// The HTTP server buffers have a size of 4096 at the time of this writing.
+//
+// The buffer sizes do not limit the size of a message that can be read or
+// written by a connection.
+//
+// Buffers are held for the lifetime of the connection by default. If the
+// Dialer or Upgrader WriteBufferPool field is set, then a connection holds the
+// write buffer only when writing a message.
+//
+// Applications should tune the buffer sizes to balance memory use and
+// performance. Increasing the buffer size uses more memory, but can reduce the
+// number of system calls to read or write the network. In the case of writing,
+// increasing the buffer size can reduce the number of frame headers written to
+// the network.
+//
+// Some guidelines for setting buffer parameters are:
+//
+// Limit the buffer sizes to the maximum expected message size. Buffers larger
+// than the largest message do not provide any benefit.
+//
+// Depending on the distribution of message sizes, setting the buffer size to
+// a value less than the maximum expected message size can greatly reduce memory
+// use with a small impact on performance. Here's an example: If 99% of the
+// messages are smaller than 256 bytes and the maximum message size is 512
+// bytes, then a buffer size of 256 bytes will result in 1.01 more system calls
+// than a buffer size of 512 bytes. The memory savings is 50%.
+//
+// A write buffer pool is useful when the application has a modest number
+// writes over a large number of connections. when buffers are pooled, a larger
+// buffer size has a reduced impact on total memory use and has the benefit of
+// reducing system calls and frame overhead.
+//
+// Compression EXPERIMENTAL
+//
+// Per message compression extensions (RFC 7692) are experimentally supported
+// by this package in a limited capacity. Setting the EnableCompression option
+// to true in Dialer or Upgrader will attempt to negotiate per message deflate
+// support.
+//
+// var upgrader = websocket.Upgrader{
+// EnableCompression: true,
+// }
+//
+// If compression was successfully negotiated with the connection's peer, any
+// message received in compressed form will be automatically decompressed.
+// All Read methods will return uncompressed bytes.
+//
+// Per message compression of messages written to a connection can be enabled
+// or disabled by calling the corresponding Conn method:
+//
+// conn.EnableWriteCompression(false)
+//
+// Currently this package does not support compression with "context takeover".
+// This means that messages must be compressed and decompressed in isolation,
+// without retaining sliding window or dictionary state across messages. For
+// more details refer to RFC 7692.
+//
+// Use of compression is experimental and may result in decreased performance.
+package websocket
diff --git a/server/vendor/github.com/gorilla/websocket/join.go b/server/vendor/github.com/gorilla/websocket/join.go
new file mode 100644
index 0000000..c64f8c8
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/join.go
@@ -0,0 +1,42 @@
+// Copyright 2019 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package websocket
+
+import (
+ "io"
+ "strings"
+)
+
+// JoinMessages concatenates received messages to create a single io.Reader.
+// The string term is appended to each message. The returned reader does not
+// support concurrent calls to the Read method.
+func JoinMessages(c *Conn, term string) io.Reader {
+ return &joinReader{c: c, term: term}
+}
+
+type joinReader struct {
+ c *Conn
+ term string
+ r io.Reader
+}
+
+func (r *joinReader) Read(p []byte) (int, error) {
+ if r.r == nil {
+ var err error
+ _, r.r, err = r.c.NextReader()
+ if err != nil {
+ return 0, err
+ }
+ if r.term != "" {
+ r.r = io.MultiReader(r.r, strings.NewReader(r.term))
+ }
+ }
+ n, err := r.r.Read(p)
+ if err == io.EOF {
+ err = nil
+ r.r = nil
+ }
+ return n, err
+}
diff --git a/server/vendor/github.com/gorilla/websocket/json.go b/server/vendor/github.com/gorilla/websocket/json.go
new file mode 100644
index 0000000..dc2c1f6
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/json.go
@@ -0,0 +1,60 @@
+// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package websocket
+
+import (
+ "encoding/json"
+ "io"
+)
+
+// WriteJSON writes the JSON encoding of v as a message.
+//
+// Deprecated: Use c.WriteJSON instead.
+func WriteJSON(c *Conn, v interface{}) error {
+ return c.WriteJSON(v)
+}
+
+// WriteJSON writes the JSON encoding of v as a message.
+//
+// See the documentation for encoding/json Marshal for details about the
+// conversion of Go values to JSON.
+func (c *Conn) WriteJSON(v interface{}) error {
+ w, err := c.NextWriter(TextMessage)
+ if err != nil {
+ return err
+ }
+ err1 := json.NewEncoder(w).Encode(v)
+ err2 := w.Close()
+ if err1 != nil {
+ return err1
+ }
+ return err2
+}
+
+// ReadJSON reads the next JSON-encoded message from the connection and stores
+// it in the value pointed to by v.
+//
+// Deprecated: Use c.ReadJSON instead.
+func ReadJSON(c *Conn, v interface{}) error {
+ return c.ReadJSON(v)
+}
+
+// ReadJSON reads the next JSON-encoded message from the connection and stores
+// it in the value pointed to by v.
+//
+// See the documentation for the encoding/json Unmarshal function for details
+// about the conversion of JSON to a Go value.
+func (c *Conn) ReadJSON(v interface{}) error {
+ _, r, err := c.NextReader()
+ if err != nil {
+ return err
+ }
+ err = json.NewDecoder(r).Decode(v)
+ if err == io.EOF {
+ // One value is expected in the message.
+ err = io.ErrUnexpectedEOF
+ }
+ return err
+}
diff --git a/server/vendor/github.com/gorilla/websocket/mask.go b/server/vendor/github.com/gorilla/websocket/mask.go
new file mode 100644
index 0000000..d0742bf
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/mask.go
@@ -0,0 +1,55 @@
+// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+//go:build !appengine
+// +build !appengine
+
+package websocket
+
+import "unsafe"
+
+const wordSize = int(unsafe.Sizeof(uintptr(0)))
+
+func maskBytes(key [4]byte, pos int, b []byte) int {
+ // Mask one byte at a time for small buffers.
+ if len(b) < 2*wordSize {
+ for i := range b {
+ b[i] ^= key[pos&3]
+ pos++
+ }
+ return pos & 3
+ }
+
+ // Mask one byte at a time to word boundary.
+ if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {
+ n = wordSize - n
+ for i := range b[:n] {
+ b[i] ^= key[pos&3]
+ pos++
+ }
+ b = b[n:]
+ }
+
+ // Create aligned word size key.
+ var k [wordSize]byte
+ for i := range k {
+ k[i] = key[(pos+i)&3]
+ }
+ kw := *(*uintptr)(unsafe.Pointer(&k))
+
+ // Mask one word at a time.
+ n := (len(b) / wordSize) * wordSize
+ for i := 0; i < n; i += wordSize {
+ *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw
+ }
+
+ // Mask one byte at a time for remaining bytes.
+ b = b[n:]
+ for i := range b {
+ b[i] ^= key[pos&3]
+ pos++
+ }
+
+ return pos & 3
+}
diff --git a/server/vendor/github.com/gorilla/websocket/mask_safe.go b/server/vendor/github.com/gorilla/websocket/mask_safe.go
new file mode 100644
index 0000000..36250ca
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/mask_safe.go
@@ -0,0 +1,16 @@
+// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
+// this source code is governed by a BSD-style license that can be found in the
+// LICENSE file.
+
+//go:build appengine
+// +build appengine
+
+package websocket
+
+func maskBytes(key [4]byte, pos int, b []byte) int {
+ for i := range b {
+ b[i] ^= key[pos&3]
+ pos++
+ }
+ return pos & 3
+}
diff --git a/server/vendor/github.com/gorilla/websocket/prepared.go b/server/vendor/github.com/gorilla/websocket/prepared.go
new file mode 100644
index 0000000..c854225
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/prepared.go
@@ -0,0 +1,102 @@
+// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package websocket
+
+import (
+ "bytes"
+ "net"
+ "sync"
+ "time"
+)
+
+// PreparedMessage caches on the wire representations of a message payload.
+// Use PreparedMessage to efficiently send a message payload to multiple
+// connections. PreparedMessage is especially useful when compression is used
+// because the CPU and memory expensive compression operation can be executed
+// once for a given set of compression options.
+type PreparedMessage struct {
+ messageType int
+ data []byte
+ mu sync.Mutex
+ frames map[prepareKey]*preparedFrame
+}
+
+// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage.
+type prepareKey struct {
+ isServer bool
+ compress bool
+ compressionLevel int
+}
+
+// preparedFrame contains data in wire representation.
+type preparedFrame struct {
+ once sync.Once
+ data []byte
+}
+
+// NewPreparedMessage returns an initialized PreparedMessage. You can then send
+// it to connection using WritePreparedMessage method. Valid wire
+// representation will be calculated lazily only once for a set of current
+// connection options.
+func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) {
+ pm := &PreparedMessage{
+ messageType: messageType,
+ frames: make(map[prepareKey]*preparedFrame),
+ data: data,
+ }
+
+ // Prepare a plain server frame.
+ _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false})
+ if err != nil {
+ return nil, err
+ }
+
+ // To protect against caller modifying the data argument, remember the data
+ // copied to the plain server frame.
+ pm.data = frameData[len(frameData)-len(data):]
+ return pm, nil
+}
+
+func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {
+ pm.mu.Lock()
+ frame, ok := pm.frames[key]
+ if !ok {
+ frame = &preparedFrame{}
+ pm.frames[key] = frame
+ }
+ pm.mu.Unlock()
+
+ var err error
+ frame.once.Do(func() {
+ // Prepare a frame using a 'fake' connection.
+ // TODO: Refactor code in conn.go to allow more direct construction of
+ // the frame.
+ mu := make(chan struct{}, 1)
+ mu <- struct{}{}
+ var nc prepareConn
+ c := &Conn{
+ conn: &nc,
+ mu: mu,
+ isServer: key.isServer,
+ compressionLevel: key.compressionLevel,
+ enableWriteCompression: true,
+ writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize),
+ }
+ if key.compress {
+ c.newCompressionWriter = compressNoContextTakeover
+ }
+ err = c.WriteMessage(pm.messageType, pm.data)
+ frame.data = nc.buf.Bytes()
+ })
+ return pm.messageType, frame.data, err
+}
+
+type prepareConn struct {
+ buf bytes.Buffer
+ net.Conn
+}
+
+func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) }
+func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil }
diff --git a/server/vendor/github.com/gorilla/websocket/proxy.go b/server/vendor/github.com/gorilla/websocket/proxy.go
new file mode 100644
index 0000000..e0f466b
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/proxy.go
@@ -0,0 +1,77 @@
+// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package websocket
+
+import (
+ "bufio"
+ "encoding/base64"
+ "errors"
+ "net"
+ "net/http"
+ "net/url"
+ "strings"
+)
+
+type netDialerFunc func(network, addr string) (net.Conn, error)
+
+func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) {
+ return fn(network, addr)
+}
+
+func init() {
+ proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) {
+ return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil
+ })
+}
+
+type httpProxyDialer struct {
+ proxyURL *url.URL
+ forwardDial func(network, addr string) (net.Conn, error)
+}
+
+func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) {
+ hostPort, _ := hostPortNoPort(hpd.proxyURL)
+ conn, err := hpd.forwardDial(network, hostPort)
+ if err != nil {
+ return nil, err
+ }
+
+ connectHeader := make(http.Header)
+ if user := hpd.proxyURL.User; user != nil {
+ proxyUser := user.Username()
+ if proxyPassword, passwordSet := user.Password(); passwordSet {
+ credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
+ connectHeader.Set("Proxy-Authorization", "Basic "+credential)
+ }
+ }
+
+ connectReq := &http.Request{
+ Method: http.MethodConnect,
+ URL: &url.URL{Opaque: addr},
+ Host: addr,
+ Header: connectHeader,
+ }
+
+ if err := connectReq.Write(conn); err != nil {
+ conn.Close()
+ return nil, err
+ }
+
+ // Read response. It's OK to use and discard buffered reader here becaue
+ // the remote server does not speak until spoken to.
+ br := bufio.NewReader(conn)
+ resp, err := http.ReadResponse(br, connectReq)
+ if err != nil {
+ conn.Close()
+ return nil, err
+ }
+
+ if resp.StatusCode != 200 {
+ conn.Close()
+ f := strings.SplitN(resp.Status, " ", 2)
+ return nil, errors.New(f[1])
+ }
+ return conn, nil
+}
diff --git a/server/vendor/github.com/gorilla/websocket/server.go b/server/vendor/github.com/gorilla/websocket/server.go
new file mode 100644
index 0000000..bb33597
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/server.go
@@ -0,0 +1,365 @@
+// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package websocket
+
+import (
+ "bufio"
+ "errors"
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+)
+
+// HandshakeError describes an error with the handshake from the peer.
+type HandshakeError struct {
+ message string
+}
+
+func (e HandshakeError) Error() string { return e.message }
+
+// Upgrader specifies parameters for upgrading an HTTP connection to a
+// WebSocket connection.
+//
+// It is safe to call Upgrader's methods concurrently.
+type Upgrader struct {
+ // HandshakeTimeout specifies the duration for the handshake to complete.
+ HandshakeTimeout time.Duration
+
+ // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer
+ // size is zero, then buffers allocated by the HTTP server are used. The
+ // I/O buffer sizes do not limit the size of the messages that can be sent
+ // or received.
+ ReadBufferSize, WriteBufferSize int
+
+ // WriteBufferPool is a pool of buffers for write operations. If the value
+ // is not set, then write buffers are allocated to the connection for the
+ // lifetime of the connection.
+ //
+ // A pool is most useful when the application has a modest volume of writes
+ // across a large number of connections.
+ //
+ // Applications should use a single pool for each unique value of
+ // WriteBufferSize.
+ WriteBufferPool BufferPool
+
+ // Subprotocols specifies the server's supported protocols in order of
+ // preference. If this field is not nil, then the Upgrade method negotiates a
+ // subprotocol by selecting the first match in this list with a protocol
+ // requested by the client. If there's no match, then no protocol is
+ // negotiated (the Sec-Websocket-Protocol header is not included in the
+ // handshake response).
+ Subprotocols []string
+
+ // Error specifies the function for generating HTTP error responses. If Error
+ // is nil, then http.Error is used to generate the HTTP response.
+ Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
+
+ // CheckOrigin returns true if the request Origin header is acceptable. If
+ // CheckOrigin is nil, then a safe default is used: return false if the
+ // Origin request header is present and the origin host is not equal to
+ // request Host header.
+ //
+ // A CheckOrigin function should carefully validate the request origin to
+ // prevent cross-site request forgery.
+ CheckOrigin func(r *http.Request) bool
+
+ // EnableCompression specify if the server should attempt to negotiate per
+ // message compression (RFC 7692). Setting this value to true does not
+ // guarantee that compression will be supported. Currently only "no context
+ // takeover" modes are supported.
+ EnableCompression bool
+}
+
+func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {
+ err := HandshakeError{reason}
+ if u.Error != nil {
+ u.Error(w, r, status, err)
+ } else {
+ w.Header().Set("Sec-Websocket-Version", "13")
+ http.Error(w, http.StatusText(status), status)
+ }
+ return nil, err
+}
+
+// checkSameOrigin returns true if the origin is not set or is equal to the request host.
+func checkSameOrigin(r *http.Request) bool {
+ origin := r.Header["Origin"]
+ if len(origin) == 0 {
+ return true
+ }
+ u, err := url.Parse(origin[0])
+ if err != nil {
+ return false
+ }
+ return equalASCIIFold(u.Host, r.Host)
+}
+
+func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {
+ if u.Subprotocols != nil {
+ clientProtocols := Subprotocols(r)
+ for _, serverProtocol := range u.Subprotocols {
+ for _, clientProtocol := range clientProtocols {
+ if clientProtocol == serverProtocol {
+ return clientProtocol
+ }
+ }
+ }
+ } else if responseHeader != nil {
+ return responseHeader.Get("Sec-Websocket-Protocol")
+ }
+ return ""
+}
+
+// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
+//
+// The responseHeader is included in the response to the client's upgrade
+// request. Use the responseHeader to specify cookies (Set-Cookie). To specify
+// subprotocols supported by the server, set Upgrader.Subprotocols directly.
+//
+// If the upgrade fails, then Upgrade replies to the client with an HTTP error
+// response.
+func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
+ const badHandshake = "websocket: the client is not using the websocket protocol: "
+
+ if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
+ return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'upgrade' token not found in 'Connection' header")
+ }
+
+ if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
+ return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header")
+ }
+
+ if r.Method != http.MethodGet {
+ return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"request method is not GET")
+ }
+
+ if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
+ return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
+ }
+
+ if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
+ return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported")
+ }
+
+ checkOrigin := u.CheckOrigin
+ if checkOrigin == nil {
+ checkOrigin = checkSameOrigin
+ }
+ if !checkOrigin(r) {
+ return u.returnError(w, r, http.StatusForbidden, "websocket: request origin not allowed by Upgrader.CheckOrigin")
+ }
+
+ challengeKey := r.Header.Get("Sec-Websocket-Key")
+ if !isValidChallengeKey(challengeKey) {
+ return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header must be Base64 encoded value of 16-byte in length")
+ }
+
+ subprotocol := u.selectSubprotocol(r, responseHeader)
+
+ // Negotiate PMCE
+ var compress bool
+ if u.EnableCompression {
+ for _, ext := range parseExtensions(r.Header) {
+ if ext[""] != "permessage-deflate" {
+ continue
+ }
+ compress = true
+ break
+ }
+ }
+
+ h, ok := w.(http.Hijacker)
+ if !ok {
+ return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
+ }
+ var brw *bufio.ReadWriter
+ netConn, brw, err := h.Hijack()
+ if err != nil {
+ return u.returnError(w, r, http.StatusInternalServerError, err.Error())
+ }
+
+ if brw.Reader.Buffered() > 0 {
+ netConn.Close()
+ return nil, errors.New("websocket: client sent data before handshake is complete")
+ }
+
+ var br *bufio.Reader
+ if u.ReadBufferSize == 0 && bufioReaderSize(netConn, brw.Reader) > 256 {
+ // Reuse hijacked buffered reader as connection reader.
+ br = brw.Reader
+ }
+
+ buf := bufioWriterBuffer(netConn, brw.Writer)
+
+ var writeBuf []byte
+ if u.WriteBufferPool == nil && u.WriteBufferSize == 0 && len(buf) >= maxFrameHeaderSize+256 {
+ // Reuse hijacked write buffer as connection buffer.
+ writeBuf = buf
+ }
+
+ c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize, u.WriteBufferPool, br, writeBuf)
+ c.subprotocol = subprotocol
+
+ if compress {
+ c.newCompressionWriter = compressNoContextTakeover
+ c.newDecompressionReader = decompressNoContextTakeover
+ }
+
+ // Use larger of hijacked buffer and connection write buffer for header.
+ p := buf
+ if len(c.writeBuf) > len(p) {
+ p = c.writeBuf
+ }
+ p = p[:0]
+
+ p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...)
+ p = append(p, computeAcceptKey(challengeKey)...)
+ p = append(p, "\r\n"...)
+ if c.subprotocol != "" {
+ p = append(p, "Sec-WebSocket-Protocol: "...)
+ p = append(p, c.subprotocol...)
+ p = append(p, "\r\n"...)
+ }
+ if compress {
+ p = append(p, "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...)
+ }
+ for k, vs := range responseHeader {
+ if k == "Sec-Websocket-Protocol" {
+ continue
+ }
+ for _, v := range vs {
+ p = append(p, k...)
+ p = append(p, ": "...)
+ for i := 0; i < len(v); i++ {
+ b := v[i]
+ if b <= 31 {
+ // prevent response splitting.
+ b = ' '
+ }
+ p = append(p, b)
+ }
+ p = append(p, "\r\n"...)
+ }
+ }
+ p = append(p, "\r\n"...)
+
+ // Clear deadlines set by HTTP server.
+ netConn.SetDeadline(time.Time{})
+
+ if u.HandshakeTimeout > 0 {
+ netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout))
+ }
+ if _, err = netConn.Write(p); err != nil {
+ netConn.Close()
+ return nil, err
+ }
+ if u.HandshakeTimeout > 0 {
+ netConn.SetWriteDeadline(time.Time{})
+ }
+
+ return c, nil
+}
+
+// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
+//
+// Deprecated: Use websocket.Upgrader instead.
+//
+// Upgrade does not perform origin checking. The application is responsible for
+// checking the Origin header before calling Upgrade. An example implementation
+// of the same origin policy check is:
+//
+// if req.Header.Get("Origin") != "http://"+req.Host {
+// http.Error(w, "Origin not allowed", http.StatusForbidden)
+// return
+// }
+//
+// If the endpoint supports subprotocols, then the application is responsible
+// for negotiating the protocol used on the connection. Use the Subprotocols()
+// function to get the subprotocols requested by the client. Use the
+// Sec-Websocket-Protocol response header to specify the subprotocol selected
+// by the application.
+//
+// The responseHeader is included in the response to the client's upgrade
+// request. Use the responseHeader to specify cookies (Set-Cookie) and the
+// negotiated subprotocol (Sec-Websocket-Protocol).
+//
+// The connection buffers IO to the underlying network connection. The
+// readBufSize and writeBufSize parameters specify the size of the buffers to
+// use. Messages can be larger than the buffers.
+//
+// If the request is not a valid WebSocket handshake, then Upgrade returns an
+// error of type HandshakeError. Applications should handle this error by
+// replying to the client with an HTTP error response.
+func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) {
+ u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize}
+ u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) {
+ // don't return errors to maintain backwards compatibility
+ }
+ u.CheckOrigin = func(r *http.Request) bool {
+ // allow all connections by default
+ return true
+ }
+ return u.Upgrade(w, r, responseHeader)
+}
+
+// Subprotocols returns the subprotocols requested by the client in the
+// Sec-Websocket-Protocol header.
+func Subprotocols(r *http.Request) []string {
+ h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol"))
+ if h == "" {
+ return nil
+ }
+ protocols := strings.Split(h, ",")
+ for i := range protocols {
+ protocols[i] = strings.TrimSpace(protocols[i])
+ }
+ return protocols
+}
+
+// IsWebSocketUpgrade returns true if the client requested upgrade to the
+// WebSocket protocol.
+func IsWebSocketUpgrade(r *http.Request) bool {
+ return tokenListContainsValue(r.Header, "Connection", "upgrade") &&
+ tokenListContainsValue(r.Header, "Upgrade", "websocket")
+}
+
+// bufioReaderSize size returns the size of a bufio.Reader.
+func bufioReaderSize(originalReader io.Reader, br *bufio.Reader) int {
+ // This code assumes that peek on a reset reader returns
+ // bufio.Reader.buf[:0].
+ // TODO: Use bufio.Reader.Size() after Go 1.10
+ br.Reset(originalReader)
+ if p, err := br.Peek(0); err == nil {
+ return cap(p)
+ }
+ return 0
+}
+
+// writeHook is an io.Writer that records the last slice passed to it vio
+// io.Writer.Write.
+type writeHook struct {
+ p []byte
+}
+
+func (wh *writeHook) Write(p []byte) (int, error) {
+ wh.p = p
+ return len(p), nil
+}
+
+// bufioWriterBuffer grabs the buffer from a bufio.Writer.
+func bufioWriterBuffer(originalWriter io.Writer, bw *bufio.Writer) []byte {
+ // This code assumes that bufio.Writer.buf[:1] is passed to the
+ // bufio.Writer's underlying writer.
+ var wh writeHook
+ bw.Reset(&wh)
+ bw.WriteByte(0)
+ bw.Flush()
+
+ bw.Reset(originalWriter)
+
+ return wh.p[:cap(wh.p)]
+}
diff --git a/server/vendor/github.com/gorilla/websocket/tls_handshake.go b/server/vendor/github.com/gorilla/websocket/tls_handshake.go
new file mode 100644
index 0000000..a62b68c
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/tls_handshake.go
@@ -0,0 +1,21 @@
+//go:build go1.17
+// +build go1.17
+
+package websocket
+
+import (
+ "context"
+ "crypto/tls"
+)
+
+func doHandshake(ctx context.Context, tlsConn *tls.Conn, cfg *tls.Config) error {
+ if err := tlsConn.HandshakeContext(ctx); err != nil {
+ return err
+ }
+ if !cfg.InsecureSkipVerify {
+ if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/server/vendor/github.com/gorilla/websocket/tls_handshake_116.go b/server/vendor/github.com/gorilla/websocket/tls_handshake_116.go
new file mode 100644
index 0000000..e1b2b44
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/tls_handshake_116.go
@@ -0,0 +1,21 @@
+//go:build !go1.17
+// +build !go1.17
+
+package websocket
+
+import (
+ "context"
+ "crypto/tls"
+)
+
+func doHandshake(ctx context.Context, tlsConn *tls.Conn, cfg *tls.Config) error {
+ if err := tlsConn.Handshake(); err != nil {
+ return err
+ }
+ if !cfg.InsecureSkipVerify {
+ if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/server/vendor/github.com/gorilla/websocket/util.go b/server/vendor/github.com/gorilla/websocket/util.go
new file mode 100644
index 0000000..31a5dee
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/util.go
@@ -0,0 +1,298 @@
+// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package websocket
+
+import (
+ "crypto/rand"
+ "crypto/sha1"
+ "encoding/base64"
+ "io"
+ "net/http"
+ "strings"
+ "unicode/utf8"
+)
+
+var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
+
+func computeAcceptKey(challengeKey string) string {
+ h := sha1.New()
+ h.Write([]byte(challengeKey))
+ h.Write(keyGUID)
+ return base64.StdEncoding.EncodeToString(h.Sum(nil))
+}
+
+func generateChallengeKey() (string, error) {
+ p := make([]byte, 16)
+ if _, err := io.ReadFull(rand.Reader, p); err != nil {
+ return "", err
+ }
+ return base64.StdEncoding.EncodeToString(p), nil
+}
+
+// Token octets per RFC 2616.
+var isTokenOctet = [256]bool{
+ '!': true,
+ '#': true,
+ '$': true,
+ '%': true,
+ '&': true,
+ '\'': true,
+ '*': true,
+ '+': true,
+ '-': true,
+ '.': true,
+ '0': true,
+ '1': true,
+ '2': true,
+ '3': true,
+ '4': true,
+ '5': true,
+ '6': true,
+ '7': true,
+ '8': true,
+ '9': true,
+ 'A': true,
+ 'B': true,
+ 'C': true,
+ 'D': true,
+ 'E': true,
+ 'F': true,
+ 'G': true,
+ 'H': true,
+ 'I': true,
+ 'J': true,
+ 'K': true,
+ 'L': true,
+ 'M': true,
+ 'N': true,
+ 'O': true,
+ 'P': true,
+ 'Q': true,
+ 'R': true,
+ 'S': true,
+ 'T': true,
+ 'U': true,
+ 'W': true,
+ 'V': true,
+ 'X': true,
+ 'Y': true,
+ 'Z': true,
+ '^': true,
+ '_': true,
+ '`': true,
+ 'a': true,
+ 'b': true,
+ 'c': true,
+ 'd': true,
+ 'e': true,
+ 'f': true,
+ 'g': true,
+ 'h': true,
+ 'i': true,
+ 'j': true,
+ 'k': true,
+ 'l': true,
+ 'm': true,
+ 'n': true,
+ 'o': true,
+ 'p': true,
+ 'q': true,
+ 'r': true,
+ 's': true,
+ 't': true,
+ 'u': true,
+ 'v': true,
+ 'w': true,
+ 'x': true,
+ 'y': true,
+ 'z': true,
+ '|': true,
+ '~': true,
+}
+
+// skipSpace returns a slice of the string s with all leading RFC 2616 linear
+// whitespace removed.
+func skipSpace(s string) (rest string) {
+ i := 0
+ for ; i < len(s); i++ {
+ if b := s[i]; b != ' ' && b != '\t' {
+ break
+ }
+ }
+ return s[i:]
+}
+
+// nextToken returns the leading RFC 2616 token of s and the string following
+// the token.
+func nextToken(s string) (token, rest string) {
+ i := 0
+ for ; i < len(s); i++ {
+ if !isTokenOctet[s[i]] {
+ break
+ }
+ }
+ return s[:i], s[i:]
+}
+
+// nextTokenOrQuoted returns the leading token or quoted string per RFC 2616
+// and the string following the token or quoted string.
+func nextTokenOrQuoted(s string) (value string, rest string) {
+ if !strings.HasPrefix(s, "\"") {
+ return nextToken(s)
+ }
+ s = s[1:]
+ for i := 0; i < len(s); i++ {
+ switch s[i] {
+ case '"':
+ return s[:i], s[i+1:]
+ case '\\':
+ p := make([]byte, len(s)-1)
+ j := copy(p, s[:i])
+ escape := true
+ for i = i + 1; i < len(s); i++ {
+ b := s[i]
+ switch {
+ case escape:
+ escape = false
+ p[j] = b
+ j++
+ case b == '\\':
+ escape = true
+ case b == '"':
+ return string(p[:j]), s[i+1:]
+ default:
+ p[j] = b
+ j++
+ }
+ }
+ return "", ""
+ }
+ }
+ return "", ""
+}
+
+// equalASCIIFold returns true if s is equal to t with ASCII case folding as
+// defined in RFC 4790.
+func equalASCIIFold(s, t string) bool {
+ for s != "" && t != "" {
+ sr, size := utf8.DecodeRuneInString(s)
+ s = s[size:]
+ tr, size := utf8.DecodeRuneInString(t)
+ t = t[size:]
+ if sr == tr {
+ continue
+ }
+ if 'A' <= sr && sr <= 'Z' {
+ sr = sr + 'a' - 'A'
+ }
+ if 'A' <= tr && tr <= 'Z' {
+ tr = tr + 'a' - 'A'
+ }
+ if sr != tr {
+ return false
+ }
+ }
+ return s == t
+}
+
+// tokenListContainsValue returns true if the 1#token header with the given
+// name contains a token equal to value with ASCII case folding.
+func tokenListContainsValue(header http.Header, name string, value string) bool {
+headers:
+ for _, s := range header[name] {
+ for {
+ var t string
+ t, s = nextToken(skipSpace(s))
+ if t == "" {
+ continue headers
+ }
+ s = skipSpace(s)
+ if s != "" && s[0] != ',' {
+ continue headers
+ }
+ if equalASCIIFold(t, value) {
+ return true
+ }
+ if s == "" {
+ continue headers
+ }
+ s = s[1:]
+ }
+ }
+ return false
+}
+
+// parseExtensions parses WebSocket extensions from a header.
+func parseExtensions(header http.Header) []map[string]string {
+ // From RFC 6455:
+ //
+ // Sec-WebSocket-Extensions = extension-list
+ // extension-list = 1#extension
+ // extension = extension-token *( ";" extension-param )
+ // extension-token = registered-token
+ // registered-token = token
+ // extension-param = token [ "=" (token | quoted-string) ]
+ // ;When using the quoted-string syntax variant, the value
+ // ;after quoted-string unescaping MUST conform to the
+ // ;'token' ABNF.
+
+ var result []map[string]string
+headers:
+ for _, s := range header["Sec-Websocket-Extensions"] {
+ for {
+ var t string
+ t, s = nextToken(skipSpace(s))
+ if t == "" {
+ continue headers
+ }
+ ext := map[string]string{"": t}
+ for {
+ s = skipSpace(s)
+ if !strings.HasPrefix(s, ";") {
+ break
+ }
+ var k string
+ k, s = nextToken(skipSpace(s[1:]))
+ if k == "" {
+ continue headers
+ }
+ s = skipSpace(s)
+ var v string
+ if strings.HasPrefix(s, "=") {
+ v, s = nextTokenOrQuoted(skipSpace(s[1:]))
+ s = skipSpace(s)
+ }
+ if s != "" && s[0] != ',' && s[0] != ';' {
+ continue headers
+ }
+ ext[k] = v
+ }
+ if s != "" && s[0] != ',' {
+ continue headers
+ }
+ result = append(result, ext)
+ if s == "" {
+ continue headers
+ }
+ s = s[1:]
+ }
+ }
+ return result
+}
+
+// isValidChallengeKey checks if the argument meets RFC6455 specification.
+func isValidChallengeKey(s string) bool {
+ // From RFC6455:
+ //
+ // A |Sec-WebSocket-Key| header field with a base64-encoded (see
+ // Section 4 of [RFC4648]) value that, when decoded, is 16 bytes in
+ // length.
+
+ if s == "" {
+ return false
+ }
+ decoded, err := base64.StdEncoding.DecodeString(s)
+ return err == nil && len(decoded) == 16
+}
diff --git a/server/vendor/github.com/gorilla/websocket/x_net_proxy.go b/server/vendor/github.com/gorilla/websocket/x_net_proxy.go
new file mode 100644
index 0000000..2e668f6
--- /dev/null
+++ b/server/vendor/github.com/gorilla/websocket/x_net_proxy.go
@@ -0,0 +1,473 @@
+// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
+//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy
+
+// Package proxy provides support for a variety of protocols to proxy network
+// data.
+//
+
+package websocket
+
+import (
+ "errors"
+ "io"
+ "net"
+ "net/url"
+ "os"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+type proxy_direct struct{}
+
+// Direct is a direct proxy: one that makes network connections directly.
+var proxy_Direct = proxy_direct{}
+
+func (proxy_direct) Dial(network, addr string) (net.Conn, error) {
+ return net.Dial(network, addr)
+}
+
+// A PerHost directs connections to a default Dialer unless the host name
+// requested matches one of a number of exceptions.
+type proxy_PerHost struct {
+ def, bypass proxy_Dialer
+
+ bypassNetworks []*net.IPNet
+ bypassIPs []net.IP
+ bypassZones []string
+ bypassHosts []string
+}
+
+// NewPerHost returns a PerHost Dialer that directs connections to either
+// defaultDialer or bypass, depending on whether the connection matches one of
+// the configured rules.
+func proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost {
+ return &proxy_PerHost{
+ def: defaultDialer,
+ bypass: bypass,
+ }
+}
+
+// Dial connects to the address addr on the given network through either
+// defaultDialer or bypass.
+func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) {
+ host, _, err := net.SplitHostPort(addr)
+ if err != nil {
+ return nil, err
+ }
+
+ return p.dialerForRequest(host).Dial(network, addr)
+}
+
+func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer {
+ if ip := net.ParseIP(host); ip != nil {
+ for _, net := range p.bypassNetworks {
+ if net.Contains(ip) {
+ return p.bypass
+ }
+ }
+ for _, bypassIP := range p.bypassIPs {
+ if bypassIP.Equal(ip) {
+ return p.bypass
+ }
+ }
+ return p.def
+ }
+
+ for _, zone := range p.bypassZones {
+ if strings.HasSuffix(host, zone) {
+ return p.bypass
+ }
+ if host == zone[1:] {
+ // For a zone ".example.com", we match "example.com"
+ // too.
+ return p.bypass
+ }
+ }
+ for _, bypassHost := range p.bypassHosts {
+ if bypassHost == host {
+ return p.bypass
+ }
+ }
+ return p.def
+}
+
+// AddFromString parses a string that contains comma-separated values
+// specifying hosts that should use the bypass proxy. Each value is either an
+// IP address, a CIDR range, a zone (*.example.com) or a host name
+// (localhost). A best effort is made to parse the string and errors are
+// ignored.
+func (p *proxy_PerHost) AddFromString(s string) {
+ hosts := strings.Split(s, ",")
+ for _, host := range hosts {
+ host = strings.TrimSpace(host)
+ if len(host) == 0 {
+ continue
+ }
+ if strings.Contains(host, "/") {
+ // We assume that it's a CIDR address like 127.0.0.0/8
+ if _, net, err := net.ParseCIDR(host); err == nil {
+ p.AddNetwork(net)
+ }
+ continue
+ }
+ if ip := net.ParseIP(host); ip != nil {
+ p.AddIP(ip)
+ continue
+ }
+ if strings.HasPrefix(host, "*.") {
+ p.AddZone(host[1:])
+ continue
+ }
+ p.AddHost(host)
+ }
+}
+
+// AddIP specifies an IP address that will use the bypass proxy. Note that
+// this will only take effect if a literal IP address is dialed. A connection
+// to a named host will never match an IP.
+func (p *proxy_PerHost) AddIP(ip net.IP) {
+ p.bypassIPs = append(p.bypassIPs, ip)
+}
+
+// AddNetwork specifies an IP range that will use the bypass proxy. Note that
+// this will only take effect if a literal IP address is dialed. A connection
+// to a named host will never match.
+func (p *proxy_PerHost) AddNetwork(net *net.IPNet) {
+ p.bypassNetworks = append(p.bypassNetworks, net)
+}
+
+// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
+// "example.com" matches "example.com" and all of its subdomains.
+func (p *proxy_PerHost) AddZone(zone string) {
+ if strings.HasSuffix(zone, ".") {
+ zone = zone[:len(zone)-1]
+ }
+ if !strings.HasPrefix(zone, ".") {
+ zone = "." + zone
+ }
+ p.bypassZones = append(p.bypassZones, zone)
+}
+
+// AddHost specifies a host name that will use the bypass proxy.
+func (p *proxy_PerHost) AddHost(host string) {
+ if strings.HasSuffix(host, ".") {
+ host = host[:len(host)-1]
+ }
+ p.bypassHosts = append(p.bypassHosts, host)
+}
+
+// A Dialer is a means to establish a connection.
+type proxy_Dialer interface {
+ // Dial connects to the given address via the proxy.
+ Dial(network, addr string) (c net.Conn, err error)
+}
+
+// Auth contains authentication parameters that specific Dialers may require.
+type proxy_Auth struct {
+ User, Password string
+}
+
+// FromEnvironment returns the dialer specified by the proxy related variables in
+// the environment.
+func proxy_FromEnvironment() proxy_Dialer {
+ allProxy := proxy_allProxyEnv.Get()
+ if len(allProxy) == 0 {
+ return proxy_Direct
+ }
+
+ proxyURL, err := url.Parse(allProxy)
+ if err != nil {
+ return proxy_Direct
+ }
+ proxy, err := proxy_FromURL(proxyURL, proxy_Direct)
+ if err != nil {
+ return proxy_Direct
+ }
+
+ noProxy := proxy_noProxyEnv.Get()
+ if len(noProxy) == 0 {
+ return proxy
+ }
+
+ perHost := proxy_NewPerHost(proxy, proxy_Direct)
+ perHost.AddFromString(noProxy)
+ return perHost
+}
+
+// proxySchemes is a map from URL schemes to a function that creates a Dialer
+// from a URL with such a scheme.
+var proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)
+
+// RegisterDialerType takes a URL scheme and a function to generate Dialers from
+// a URL with that scheme and a forwarding Dialer. Registered schemes are used
+// by FromURL.
+func proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) {
+ if proxy_proxySchemes == nil {
+ proxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error))
+ }
+ proxy_proxySchemes[scheme] = f
+}
+
+// FromURL returns a Dialer given a URL specification and an underlying
+// Dialer for it to make network requests.
+func proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) {
+ var auth *proxy_Auth
+ if u.User != nil {
+ auth = new(proxy_Auth)
+ auth.User = u.User.Username()
+ if p, ok := u.User.Password(); ok {
+ auth.Password = p
+ }
+ }
+
+ switch u.Scheme {
+ case "socks5":
+ return proxy_SOCKS5("tcp", u.Host, auth, forward)
+ }
+
+ // If the scheme doesn't match any of the built-in schemes, see if it
+ // was registered by another package.
+ if proxy_proxySchemes != nil {
+ if f, ok := proxy_proxySchemes[u.Scheme]; ok {
+ return f(u, forward)
+ }
+ }
+
+ return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
+}
+
+var (
+ proxy_allProxyEnv = &proxy_envOnce{
+ names: []string{"ALL_PROXY", "all_proxy"},
+ }
+ proxy_noProxyEnv = &proxy_envOnce{
+ names: []string{"NO_PROXY", "no_proxy"},
+ }
+)
+
+// envOnce looks up an environment variable (optionally by multiple
+// names) once. It mitigates expensive lookups on some platforms
+// (e.g. Windows).
+// (Borrowed from net/http/transport.go)
+type proxy_envOnce struct {
+ names []string
+ once sync.Once
+ val string
+}
+
+func (e *proxy_envOnce) Get() string {
+ e.once.Do(e.init)
+ return e.val
+}
+
+func (e *proxy_envOnce) init() {
+ for _, n := range e.names {
+ e.val = os.Getenv(n)
+ if e.val != "" {
+ return
+ }
+ }
+}
+
+// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
+// with an optional username and password. See RFC 1928 and RFC 1929.
+func proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) {
+ s := &proxy_socks5{
+ network: network,
+ addr: addr,
+ forward: forward,
+ }
+ if auth != nil {
+ s.user = auth.User
+ s.password = auth.Password
+ }
+
+ return s, nil
+}
+
+type proxy_socks5 struct {
+ user, password string
+ network, addr string
+ forward proxy_Dialer
+}
+
+const proxy_socks5Version = 5
+
+const (
+ proxy_socks5AuthNone = 0
+ proxy_socks5AuthPassword = 2
+)
+
+const proxy_socks5Connect = 1
+
+const (
+ proxy_socks5IP4 = 1
+ proxy_socks5Domain = 3
+ proxy_socks5IP6 = 4
+)
+
+var proxy_socks5Errors = []string{
+ "",
+ "general failure",
+ "connection forbidden",
+ "network unreachable",
+ "host unreachable",
+ "connection refused",
+ "TTL expired",
+ "command not supported",
+ "address type not supported",
+}
+
+// Dial connects to the address addr on the given network via the SOCKS5 proxy.
+func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) {
+ switch network {
+ case "tcp", "tcp6", "tcp4":
+ default:
+ return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
+ }
+
+ conn, err := s.forward.Dial(s.network, s.addr)
+ if err != nil {
+ return nil, err
+ }
+ if err := s.connect(conn, addr); err != nil {
+ conn.Close()
+ return nil, err
+ }
+ return conn, nil
+}
+
+// connect takes an existing connection to a socks5 proxy server,
+// and commands the server to extend that connection to target,
+// which must be a canonical address with a host and port.
+func (s *proxy_socks5) connect(conn net.Conn, target string) error {
+ host, portStr, err := net.SplitHostPort(target)
+ if err != nil {
+ return err
+ }
+
+ port, err := strconv.Atoi(portStr)
+ if err != nil {
+ return errors.New("proxy: failed to parse port number: " + portStr)
+ }
+ if port < 1 || port > 0xffff {
+ return errors.New("proxy: port number out of range: " + portStr)
+ }
+
+ // the size here is just an estimate
+ buf := make([]byte, 0, 6+len(host))
+
+ buf = append(buf, proxy_socks5Version)
+ if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
+ buf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword)
+ } else {
+ buf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone)
+ }
+
+ if _, err := conn.Write(buf); err != nil {
+ return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ if _, err := io.ReadFull(conn, buf[:2]); err != nil {
+ return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+ if buf[0] != 5 {
+ return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
+ }
+ if buf[1] == 0xff {
+ return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
+ }
+
+ // See RFC 1929
+ if buf[1] == proxy_socks5AuthPassword {
+ buf = buf[:0]
+ buf = append(buf, 1 /* password protocol version */)
+ buf = append(buf, uint8(len(s.user)))
+ buf = append(buf, s.user...)
+ buf = append(buf, uint8(len(s.password)))
+ buf = append(buf, s.password...)
+
+ if _, err := conn.Write(buf); err != nil {
+ return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ if _, err := io.ReadFull(conn, buf[:2]); err != nil {
+ return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ if buf[1] != 0 {
+ return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
+ }
+ }
+
+ buf = buf[:0]
+ buf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */)
+
+ if ip := net.ParseIP(host); ip != nil {
+ if ip4 := ip.To4(); ip4 != nil {
+ buf = append(buf, proxy_socks5IP4)
+ ip = ip4
+ } else {
+ buf = append(buf, proxy_socks5IP6)
+ }
+ buf = append(buf, ip...)
+ } else {
+ if len(host) > 255 {
+ return errors.New("proxy: destination host name too long: " + host)
+ }
+ buf = append(buf, proxy_socks5Domain)
+ buf = append(buf, byte(len(host)))
+ buf = append(buf, host...)
+ }
+ buf = append(buf, byte(port>>8), byte(port))
+
+ if _, err := conn.Write(buf); err != nil {
+ return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ if _, err := io.ReadFull(conn, buf[:4]); err != nil {
+ return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ failure := "unknown error"
+ if int(buf[1]) < len(proxy_socks5Errors) {
+ failure = proxy_socks5Errors[buf[1]]
+ }
+
+ if len(failure) > 0 {
+ return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
+ }
+
+ bytesToDiscard := 0
+ switch buf[3] {
+ case proxy_socks5IP4:
+ bytesToDiscard = net.IPv4len
+ case proxy_socks5IP6:
+ bytesToDiscard = net.IPv6len
+ case proxy_socks5Domain:
+ _, err := io.ReadFull(conn, buf[:1])
+ if err != nil {
+ return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+ bytesToDiscard = int(buf[0])
+ default:
+ return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
+ }
+
+ if cap(buf) < bytesToDiscard {
+ buf = make([]byte, bytesToDiscard)
+ } else {
+ buf = buf[:bytesToDiscard]
+ }
+ if _, err := io.ReadFull(conn, buf); err != nil {
+ return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ // Also need to discard the port number
+ if _, err := io.ReadFull(conn, buf[:2]); err != nil {
+ return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
+ }
+
+ return nil
+}
diff --git a/server/vendor/github.com/pion/datachannel/.gitignore b/server/vendor/github.com/pion/datachannel/.gitignore
new file mode 100644
index 0000000..6e2f206
--- /dev/null
+++ b/server/vendor/github.com/pion/datachannel/.gitignore
@@ -0,0 +1,28 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+### JetBrains IDE ###
+#####################
+.idea/
+
+### Emacs Temporary Files ###
+#############################
+*~
+
+### Folders ###
+###############
+bin/
+vendor/
+node_modules/
+
+### Files ###
+#############
+*.ivf
+*.ogg
+tags
+cover.out
+*.sw[poe]
+*.wasm
+examples/sfu-ws/cert.pem
+examples/sfu-ws/key.pem
+wasm_exec.js
diff --git a/server/vendor/github.com/pion/datachannel/.golangci.yml b/server/vendor/github.com/pion/datachannel/.golangci.yml
new file mode 100644
index 0000000..e06de4d
--- /dev/null
+++ b/server/vendor/github.com/pion/datachannel/.golangci.yml
@@ -0,0 +1,125 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+linters-settings:
+ govet:
+ enable:
+ - shadow
+ misspell:
+ locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+ gomodguard:
+ blocked:
+ modules:
+ - github.com/pkg/errors:
+ recommendations:
+ - errors
+ forbidigo:
+ forbid:
+ - ^fmt.Print(f|ln)?$
+ - ^log.(Panic|Fatal|Print)(f|ln)?$
+ - ^os.Exit$
+ - ^panic$
+ - ^print(ln)?$
+
+linters:
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bidichk # Checks for dangerous unicode character sequences
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - contextcheck # check the function whether use a non-inherited context
+ - decorder # check declaration order and count of types, constants, variables and functions
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - durationcheck # check for two durations multiplied together
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
+ - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
+ - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - forbidigo # Forbids identifiers
+ - forcetypeassert # finds forced type assertions
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - goerr113 # Golang linter to check the errors handling expressions
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - grouper # An analyzer to analyze expression groups.
+ - importas # Enforces consistent import aliases
+ - ineffassign # Detects when assignments to existing variables are not used
+ - misspell # Finds commonly misspelled English words in comments
+ - nilerr # Finds the code that returns nil even if it checks that the error is not nil.
+ - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
+ - noctx # noctx finds sending http request without context.Context
+ - predeclared # find code that shadows one of Go's predeclared identifiers
+ - revive # golint replacement, finds style mistakes
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - stylecheck # Stylecheck is a replacement for golint
+ - tagliatelle # Checks the struct tags.
+ - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
+ - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - wastedassign # wastedassign finds wasted assignment statements
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - containedctx # containedctx is a linter that detects struct contained context.Context field
+ - cyclop # checks function and package cyclomatic complexity
+ - exhaustivestruct # Checks if all struct's fields are initialized
+ - funlen # Tool for detection of long functions
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - gomnd # An analyzer to detect magic numbers.
+ - ifshort # Checks that your code uses short syntax for if-statements whenever possible
+ - ireturn # Accept Interfaces, Return Concrete Types
+ - lll # Reports long lines
+ - maintidx # maintidx measures the maintainability index of each function.
+ - makezero # Finds slice declarations with non-zero initial length
+ - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - nestif # Reports deeply nested if statements
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - promlinter # Check Prometheus metrics naming via promlint
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
+ - varnamelen # checks that the length of a variable's name matches its scope
+ - wrapcheck # Checks that errors returned from external packages are wrapped
+ - wsl # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+ exclude-use-default: false
+ exclude-dirs-use-default: false
+ exclude-rules:
+ # Allow complex tests and examples, better to be self contained
+ - path: (examples|main\.go|_test\.go)
+ linters:
+ - forbidigo
+ - gocognit
+
+ # Allow forbidden identifiers in CLI commands
+ - path: cmd
+ linters:
+ - forbidigo
diff --git a/server/vendor/github.com/pion/datachannel/.goreleaser.yml b/server/vendor/github.com/pion/datachannel/.goreleaser.yml
new file mode 100644
index 0000000..30093e9
--- /dev/null
+++ b/server/vendor/github.com/pion/datachannel/.goreleaser.yml
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+builds:
+- skip: true
diff --git a/server/vendor/github.com/pion/datachannel/LICENSE b/server/vendor/github.com/pion/datachannel/LICENSE
new file mode 100644
index 0000000..491caf6
--- /dev/null
+++ b/server/vendor/github.com/pion/datachannel/LICENSE
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) 2023 The Pion community
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/server/vendor/github.com/pion/datachannel/README.md b/server/vendor/github.com/pion/datachannel/README.md
new file mode 100644
index 0000000..c68be94
--- /dev/null
+++ b/server/vendor/github.com/pion/datachannel/README.md
@@ -0,0 +1,34 @@
+
+
+ Pion Data Channels
+
+
+A Go implementation of WebRTC Data Channels
+
+
+
+
+
+
+
+
+
+
+
+
+### Roadmap
+The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
+
+### Community
+Pion has an active community on the [Slack](https://pion.ly/slack).
+
+Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/server/vendor/github.com/pion/datachannel/codecov.yml b/server/vendor/github.com/pion/datachannel/codecov.yml
new file mode 100644
index 0000000..263e4d4
--- /dev/null
+++ b/server/vendor/github.com/pion/datachannel/codecov.yml
@@ -0,0 +1,22 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+#
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+coverage:
+ status:
+ project:
+ default:
+ # Allow decreasing 2% of total coverage to avoid noise.
+ threshold: 2%
+ patch:
+ default:
+ target: 70%
+ only_pulls: true
+
+ignore:
+ - "examples/*"
+ - "examples/**/*"
diff --git a/server/vendor/github.com/pion/datachannel/datachannel.go b/server/vendor/github.com/pion/datachannel/datachannel.go
new file mode 100644
index 0000000..0050185
--- /dev/null
+++ b/server/vendor/github.com/pion/datachannel/datachannel.go
@@ -0,0 +1,404 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package datachannel implements WebRTC Data Channels
+package datachannel
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/sctp"
+)
+
+const receiveMTU = 8192
+
+// Reader is an extended io.Reader
+// that also returns if the message is text.
+type Reader interface {
+ ReadDataChannel([]byte) (int, bool, error)
+}
+
+// ReadDeadliner extends an io.Reader to expose setting a read deadline.
+type ReadDeadliner interface {
+ SetReadDeadline(time.Time) error
+}
+
+// Writer is an extended io.Writer
+// that also allows indicating if a message is text.
+type Writer interface {
+ WriteDataChannel([]byte, bool) (int, error)
+}
+
+// ReadWriteCloser is an extended io.ReadWriteCloser
+// that also implements our Reader and Writer.
+type ReadWriteCloser interface {
+ io.Reader
+ io.Writer
+ Reader
+ Writer
+ io.Closer
+}
+
+// DataChannel represents a data channel
+type DataChannel struct {
+ Config
+
+ // stats
+ messagesSent uint32
+ messagesReceived uint32
+ bytesSent uint64
+ bytesReceived uint64
+
+ mu sync.Mutex
+ onOpenCompleteHandler func()
+ openCompleteHandlerOnce sync.Once
+
+ stream *sctp.Stream
+ log logging.LeveledLogger
+}
+
+// Config is used to configure the data channel.
+type Config struct {
+ ChannelType ChannelType
+ Negotiated bool
+ Priority uint16
+ ReliabilityParameter uint32
+ Label string
+ Protocol string
+ LoggerFactory logging.LoggerFactory
+}
+
+func newDataChannel(stream *sctp.Stream, config *Config) *DataChannel {
+ return &DataChannel{
+ Config: *config,
+ stream: stream,
+ log: config.LoggerFactory.NewLogger("datachannel"),
+ }
+}
+
+// Dial opens a data channels over SCTP
+func Dial(a *sctp.Association, id uint16, config *Config) (*DataChannel, error) {
+ stream, err := a.OpenStream(id, sctp.PayloadTypeWebRTCBinary)
+ if err != nil {
+ return nil, err
+ }
+
+ dc, err := Client(stream, config)
+ if err != nil {
+ return nil, err
+ }
+
+ return dc, nil
+}
+
+// Client opens a data channel over an SCTP stream
+func Client(stream *sctp.Stream, config *Config) (*DataChannel, error) {
+ msg := &channelOpen{
+ ChannelType: config.ChannelType,
+ Priority: config.Priority,
+ ReliabilityParameter: config.ReliabilityParameter,
+
+ Label: []byte(config.Label),
+ Protocol: []byte(config.Protocol),
+ }
+
+ if !config.Negotiated {
+ rawMsg, err := msg.Marshal()
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal ChannelOpen %w", err)
+ }
+
+ if _, err = stream.WriteSCTP(rawMsg, sctp.PayloadTypeWebRTCDCEP); err != nil {
+ return nil, fmt.Errorf("failed to send ChannelOpen %w", err)
+ }
+ }
+ dc := newDataChannel(stream, config)
+
+ if err := dc.commitReliabilityParams(); err != nil {
+ return nil, err
+ }
+ return dc, nil
+}
+
+// Accept is used to accept incoming data channels over SCTP
+func Accept(a *sctp.Association, config *Config, existingChannels ...*DataChannel) (*DataChannel, error) {
+ stream, err := a.AcceptStream()
+ if err != nil {
+ return nil, err
+ }
+ for _, ch := range existingChannels {
+ if ch.StreamIdentifier() == stream.StreamIdentifier() {
+ ch.stream.SetDefaultPayloadType(sctp.PayloadTypeWebRTCBinary)
+ return ch, nil
+ }
+ }
+
+ stream.SetDefaultPayloadType(sctp.PayloadTypeWebRTCBinary)
+
+ dc, err := Server(stream, config)
+ if err != nil {
+ return nil, err
+ }
+
+ return dc, nil
+}
+
+// Server accepts a data channel over an SCTP stream
+func Server(stream *sctp.Stream, config *Config) (*DataChannel, error) {
+ buffer := make([]byte, receiveMTU)
+ n, ppi, err := stream.ReadSCTP(buffer)
+ if err != nil {
+ return nil, err
+ }
+
+ if ppi != sctp.PayloadTypeWebRTCDCEP {
+ return nil, fmt.Errorf("%w %s", ErrInvalidPayloadProtocolIdentifier, ppi)
+ }
+
+ openMsg, err := parseExpectDataChannelOpen(buffer[:n])
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse DataChannelOpen packet %w", err)
+ }
+
+ config.ChannelType = openMsg.ChannelType
+ config.Priority = openMsg.Priority
+ config.ReliabilityParameter = openMsg.ReliabilityParameter
+ config.Label = string(openMsg.Label)
+ config.Protocol = string(openMsg.Protocol)
+
+ dataChannel := newDataChannel(stream, config)
+
+ err = dataChannel.writeDataChannelAck()
+ if err != nil {
+ return nil, err
+ }
+
+ err = dataChannel.commitReliabilityParams()
+ if err != nil {
+ return nil, err
+ }
+ return dataChannel, nil
+}
+
+// Read reads a packet of len(p) bytes as binary data
+func (c *DataChannel) Read(p []byte) (int, error) {
+ n, _, err := c.ReadDataChannel(p)
+ return n, err
+}
+
+// ReadDataChannel reads a packet of len(p) bytes
+func (c *DataChannel) ReadDataChannel(p []byte) (int, bool, error) {
+ for {
+ n, ppi, err := c.stream.ReadSCTP(p)
+ if errors.Is(err, io.EOF) {
+ // When the peer sees that an incoming stream was
+ // reset, it also resets its corresponding outgoing stream.
+ if closeErr := c.stream.Close(); closeErr != nil {
+ return 0, false, closeErr
+ }
+ }
+ if err != nil {
+ return 0, false, err
+ }
+
+ if ppi == sctp.PayloadTypeWebRTCDCEP {
+ if err = c.handleDCEP(p[:n]); err != nil {
+ c.log.Errorf("Failed to handle DCEP: %s", err.Error())
+ }
+ continue
+ } else if ppi == sctp.PayloadTypeWebRTCBinaryEmpty || ppi == sctp.PayloadTypeWebRTCStringEmpty {
+ n = 0
+ }
+
+ atomic.AddUint32(&c.messagesReceived, 1)
+ atomic.AddUint64(&c.bytesReceived, uint64(n))
+
+ isString := ppi == sctp.PayloadTypeWebRTCString || ppi == sctp.PayloadTypeWebRTCStringEmpty
+ return n, isString, err
+ }
+}
+
+// SetReadDeadline sets a deadline for reads to return
+func (c *DataChannel) SetReadDeadline(t time.Time) error {
+ return c.stream.SetReadDeadline(t)
+}
+
+// MessagesSent returns the number of messages sent
+func (c *DataChannel) MessagesSent() uint32 {
+ return atomic.LoadUint32(&c.messagesSent)
+}
+
+// MessagesReceived returns the number of messages received
+func (c *DataChannel) MessagesReceived() uint32 {
+ return atomic.LoadUint32(&c.messagesReceived)
+}
+
+// OnOpen sets an event handler which is invoked when
+// a DATA_CHANNEL_ACK message is received.
+// The handler is called only on thefor the channel opened
+// https://datatracker.ietf.org/doc/html/draft-ietf-rtcweb-data-protocol-09#section-5.2
+func (c *DataChannel) OnOpen(f func()) {
+ c.mu.Lock()
+ c.openCompleteHandlerOnce = sync.Once{}
+ c.onOpenCompleteHandler = f
+ c.mu.Unlock()
+}
+
+func (c *DataChannel) onOpenComplete() {
+ c.mu.Lock()
+ hdlr := c.onOpenCompleteHandler
+ c.mu.Unlock()
+
+ if hdlr != nil {
+ go c.openCompleteHandlerOnce.Do(func() {
+ hdlr()
+ })
+ }
+}
+
+// BytesSent returns the number of bytes sent
+func (c *DataChannel) BytesSent() uint64 {
+ return atomic.LoadUint64(&c.bytesSent)
+}
+
+// BytesReceived returns the number of bytes received
+func (c *DataChannel) BytesReceived() uint64 {
+ return atomic.LoadUint64(&c.bytesReceived)
+}
+
+// StreamIdentifier returns the Stream identifier associated to the stream.
+func (c *DataChannel) StreamIdentifier() uint16 {
+ return c.stream.StreamIdentifier()
+}
+
+func (c *DataChannel) handleDCEP(data []byte) error {
+ msg, err := parse(data)
+ if err != nil {
+ return fmt.Errorf("failed to parse DataChannel packet %w", err)
+ }
+
+ switch msg := msg.(type) {
+ case *channelAck:
+ c.onOpenComplete()
+ default:
+ return fmt.Errorf("%w, wanted ACK got %v", ErrUnexpectedDataChannelType, msg)
+ }
+
+ return nil
+}
+
+// Write writes len(p) bytes from p as binary data
+func (c *DataChannel) Write(p []byte) (n int, err error) {
+ return c.WriteDataChannel(p, false)
+}
+
+// WriteDataChannel writes len(p) bytes from p
+func (c *DataChannel) WriteDataChannel(p []byte, isString bool) (n int, err error) {
+ // https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-12#section-6.6
+ // SCTP does not support the sending of empty user messages. Therefore,
+ // if an empty message has to be sent, the appropriate PPID (WebRTC
+ // String Empty or WebRTC Binary Empty) is used and the SCTP user
+ // message of one zero byte is sent. When receiving an SCTP user
+ // message with one of these PPIDs, the receiver MUST ignore the SCTP
+ // user message and process it as an empty message.
+ var ppi sctp.PayloadProtocolIdentifier
+ switch {
+ case !isString && len(p) > 0:
+ ppi = sctp.PayloadTypeWebRTCBinary
+ case !isString && len(p) == 0:
+ ppi = sctp.PayloadTypeWebRTCBinaryEmpty
+ case isString && len(p) > 0:
+ ppi = sctp.PayloadTypeWebRTCString
+ case isString && len(p) == 0:
+ ppi = sctp.PayloadTypeWebRTCStringEmpty
+ }
+
+ atomic.AddUint32(&c.messagesSent, 1)
+ atomic.AddUint64(&c.bytesSent, uint64(len(p)))
+
+ if len(p) == 0 {
+ _, err := c.stream.WriteSCTP([]byte{0}, ppi)
+ return 0, err
+ }
+ return c.stream.WriteSCTP(p, ppi)
+}
+
+func (c *DataChannel) writeDataChannelAck() error {
+ ack := channelAck{}
+ ackMsg, err := ack.Marshal()
+ if err != nil {
+ return fmt.Errorf("failed to marshal ChannelOpen ACK: %w", err)
+ }
+
+ if _, err = c.stream.WriteSCTP(ackMsg, sctp.PayloadTypeWebRTCDCEP); err != nil {
+ return fmt.Errorf("failed to send ChannelOpen ACK: %w", err)
+ }
+
+ return err
+}
+
+// Close closes the DataChannel and the underlying SCTP stream.
+func (c *DataChannel) Close() error {
+ // https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13#section-6.7
+ // Closing of a data channel MUST be signaled by resetting the
+ // corresponding outgoing streams [RFC6525]. This means that if one
+ // side decides to close the data channel, it resets the corresponding
+ // outgoing stream. When the peer sees that an incoming stream was
+ // reset, it also resets its corresponding outgoing stream. Once this
+ // is completed, the data channel is closed. Resetting a stream sets
+ // the Stream Sequence Numbers (SSNs) of the stream back to 'zero' with
+ // a corresponding notification to the application layer that the reset
+ // has been performed. Streams are available for reuse after a reset
+ // has been performed.
+ return c.stream.Close()
+}
+
+// BufferedAmount returns the number of bytes of data currently queued to be
+// sent over this stream.
+func (c *DataChannel) BufferedAmount() uint64 {
+ return c.stream.BufferedAmount()
+}
+
+// BufferedAmountLowThreshold returns the number of bytes of buffered outgoing
+// data that is considered "low." Defaults to 0.
+func (c *DataChannel) BufferedAmountLowThreshold() uint64 {
+ return c.stream.BufferedAmountLowThreshold()
+}
+
+// SetBufferedAmountLowThreshold is used to update the threshold.
+// See BufferedAmountLowThreshold().
+func (c *DataChannel) SetBufferedAmountLowThreshold(th uint64) {
+ c.stream.SetBufferedAmountLowThreshold(th)
+}
+
+// OnBufferedAmountLow sets the callback handler which would be called when the
+// number of bytes of outgoing data buffered is lower than the threshold.
+func (c *DataChannel) OnBufferedAmountLow(f func()) {
+ c.stream.OnBufferedAmountLow(f)
+}
+
+func (c *DataChannel) commitReliabilityParams() error {
+ switch c.Config.ChannelType {
+ case ChannelTypeReliable:
+ c.stream.SetReliabilityParams(false, sctp.ReliabilityTypeReliable, c.Config.ReliabilityParameter)
+ case ChannelTypeReliableUnordered:
+ c.stream.SetReliabilityParams(true, sctp.ReliabilityTypeReliable, c.Config.ReliabilityParameter)
+ case ChannelTypePartialReliableRexmit:
+ c.stream.SetReliabilityParams(false, sctp.ReliabilityTypeRexmit, c.Config.ReliabilityParameter)
+ case ChannelTypePartialReliableRexmitUnordered:
+ c.stream.SetReliabilityParams(true, sctp.ReliabilityTypeRexmit, c.Config.ReliabilityParameter)
+ case ChannelTypePartialReliableTimed:
+ c.stream.SetReliabilityParams(false, sctp.ReliabilityTypeTimed, c.Config.ReliabilityParameter)
+ case ChannelTypePartialReliableTimedUnordered:
+ c.stream.SetReliabilityParams(true, sctp.ReliabilityTypeTimed, c.Config.ReliabilityParameter)
+ default:
+ return fmt.Errorf("%w %v", ErrInvalidChannelType, c.Config.ChannelType)
+ }
+ return nil
+}
diff --git a/server/vendor/github.com/pion/datachannel/errors.go b/server/vendor/github.com/pion/datachannel/errors.go
new file mode 100644
index 0000000..2f98bf8
--- /dev/null
+++ b/server/vendor/github.com/pion/datachannel/errors.go
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package datachannel
+
+import "errors"
+
+var (
+ // ErrDataChannelMessageTooShort means that the data isn't long enough to be a valid DataChannel message
+ ErrDataChannelMessageTooShort = errors.New("DataChannel message is not long enough to determine type")
+
+ // ErrInvalidPayloadProtocolIdentifier means that we got a DataChannel messages with a Payload Protocol Identifier
+ // we don't know how to handle
+ ErrInvalidPayloadProtocolIdentifier = errors.New("DataChannel message Payload Protocol Identifier is value we can't handle")
+
+ // ErrInvalidChannelType means that the remote requested a channel type that we don't support
+ ErrInvalidChannelType = errors.New("invalid Channel Type")
+
+ // ErrInvalidMessageType is returned when a DataChannel Message has a type we don't support
+ ErrInvalidMessageType = errors.New("invalid Message Type")
+
+ // ErrExpectedAndActualLengthMismatch is when the declared length and actual length don't match
+ ErrExpectedAndActualLengthMismatch = errors.New("expected and actual length do not match")
+
+ // ErrUnexpectedDataChannelType is when a message type does not match the expected type
+ ErrUnexpectedDataChannelType = errors.New("expected and actual message type does not match")
+)
diff --git a/server/vendor/github.com/pion/datachannel/message.go b/server/vendor/github.com/pion/datachannel/message.go
new file mode 100644
index 0000000..f71ce7b
--- /dev/null
+++ b/server/vendor/github.com/pion/datachannel/message.go
@@ -0,0 +1,92 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package datachannel
+
+import (
+ "fmt"
+)
+
+// message is a parsed DataChannel message
+type message interface {
+ Marshal() ([]byte, error)
+ Unmarshal([]byte) error
+ String() string
+}
+
+// messageType is the first byte in a DataChannel message that specifies type
+type messageType byte
+
+// DataChannel Message Types
+const (
+ dataChannelAck messageType = 0x02
+ dataChannelOpen messageType = 0x03
+)
+
+func (t messageType) String() string {
+ switch t {
+ case dataChannelAck:
+ return "DataChannelAck"
+ case dataChannelOpen:
+ return "DataChannelOpen"
+ default:
+ return fmt.Sprintf("Unknown MessageType: %d", t)
+ }
+}
+
+// parse accepts raw input and returns a DataChannel message
+func parse(raw []byte) (message, error) {
+ if len(raw) == 0 {
+ return nil, ErrDataChannelMessageTooShort
+ }
+
+ var msg message
+ switch messageType(raw[0]) {
+ case dataChannelOpen:
+ msg = &channelOpen{}
+ case dataChannelAck:
+ msg = &channelAck{}
+ default:
+ return nil, fmt.Errorf("%w %v", ErrInvalidMessageType, messageType(raw[0]))
+ }
+
+ if err := msg.Unmarshal(raw); err != nil {
+ return nil, err
+ }
+
+ return msg, nil
+}
+
+// parseExpectDataChannelOpen parses a DataChannelOpen message
+// or throws an error
+func parseExpectDataChannelOpen(raw []byte) (*channelOpen, error) {
+ if len(raw) == 0 {
+ return nil, ErrDataChannelMessageTooShort
+ }
+
+ if actualTyp := messageType(raw[0]); actualTyp != dataChannelOpen {
+ return nil, fmt.Errorf("%w expected(%s) actual(%s)", ErrUnexpectedDataChannelType, actualTyp, dataChannelOpen)
+ }
+
+ msg := &channelOpen{}
+ if err := msg.Unmarshal(raw); err != nil {
+ return nil, err
+ }
+
+ return msg, nil
+}
+
+// TryMarshalUnmarshal attempts to marshal and unmarshal a message. Added for fuzzing.
+func TryMarshalUnmarshal(msg []byte) int {
+ message, err := parse(msg)
+ if err != nil {
+ return 0
+ }
+
+ _, err = message.Marshal()
+ if err != nil {
+ return 0
+ }
+
+ return 1
+}
diff --git a/server/vendor/github.com/pion/datachannel/message_channel_ack.go b/server/vendor/github.com/pion/datachannel/message_channel_ack.go
new file mode 100644
index 0000000..8fe396f
--- /dev/null
+++ b/server/vendor/github.com/pion/datachannel/message_channel_ack.go
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package datachannel
+
+// channelAck is used to ACK a DataChannel open
+type channelAck struct{}
+
+const (
+ channelOpenAckLength = 4
+)
+
+// Marshal returns raw bytes for the given message
+func (c *channelAck) Marshal() ([]byte, error) {
+ raw := make([]byte, channelOpenAckLength)
+ raw[0] = uint8(dataChannelAck)
+
+ return raw, nil
+}
+
+// Unmarshal populates the struct with the given raw data
+func (c *channelAck) Unmarshal(_ []byte) error {
+ // Message type already checked in Parse and there is no further data
+ return nil
+}
+
+func (c channelAck) String() string {
+ return "ACK"
+}
diff --git a/server/vendor/github.com/pion/datachannel/message_channel_open.go b/server/vendor/github.com/pion/datachannel/message_channel_open.go
new file mode 100644
index 0000000..dac58ff
--- /dev/null
+++ b/server/vendor/github.com/pion/datachannel/message_channel_open.go
@@ -0,0 +1,147 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package datachannel
+
+import (
+ "encoding/binary"
+ "fmt"
+)
+
+/*
+channelOpen represents a DATA_CHANNEL_OPEN Message
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Message Type | Channel Type | Priority |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Reliability Parameter |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Label Length | Protocol Length |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| |
+| Label |
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| |
+| Protocol |
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+type channelOpen struct {
+ ChannelType ChannelType
+ Priority uint16
+ ReliabilityParameter uint32
+
+ Label []byte
+ Protocol []byte
+}
+
+const (
+ channelOpenHeaderLength = 12
+)
+
+// ChannelType determines the reliability of the WebRTC DataChannel
+type ChannelType byte
+
+// ChannelType enums
+const (
+ // ChannelTypeReliable determines the Data Channel provides a
+ // reliable in-order bi-directional communication.
+ ChannelTypeReliable ChannelType = 0x00
+ // ChannelTypeReliableUnordered determines the Data Channel
+ // provides a reliable unordered bi-directional communication.
+ ChannelTypeReliableUnordered ChannelType = 0x80
+ // ChannelTypePartialReliableRexmit determines the Data Channel
+ // provides a partially-reliable in-order bi-directional communication.
+ // User messages will not be retransmitted more times than specified in the Reliability Parameter.
+ ChannelTypePartialReliableRexmit ChannelType = 0x01
+ // ChannelTypePartialReliableRexmitUnordered determines
+ // the Data Channel provides a partial reliable unordered bi-directional communication.
+ // User messages will not be retransmitted more times than specified in the Reliability Parameter.
+ ChannelTypePartialReliableRexmitUnordered ChannelType = 0x81
+ // ChannelTypePartialReliableTimed determines the Data Channel
+ // provides a partial reliable in-order bi-directional communication.
+ // User messages might not be transmitted or retransmitted after
+ // a specified life-time given in milli- seconds in the Reliability Parameter.
+ // This life-time starts when providing the user message to the protocol stack.
+ ChannelTypePartialReliableTimed ChannelType = 0x02
+ // The Data Channel provides a partial reliable unordered bi-directional
+ // communication. User messages might not be transmitted or retransmitted
+ // after a specified life-time given in milli- seconds in the Reliability Parameter.
+ // This life-time starts when providing the user message to the protocol stack.
+ ChannelTypePartialReliableTimedUnordered ChannelType = 0x82
+)
+
+func (c ChannelType) String() string {
+ switch c {
+ case ChannelTypeReliable:
+ case ChannelTypeReliableUnordered:
+ return "ReliableUnordered"
+ case ChannelTypePartialReliableRexmit:
+ return "PartialReliableRexmit"
+ case ChannelTypePartialReliableRexmitUnordered:
+ return "PartialReliableRexmitUnordered"
+ case ChannelTypePartialReliableTimed:
+ return "PartialReliableTimed"
+ case ChannelTypePartialReliableTimedUnordered:
+ return "PartialReliableTimedUnordered"
+ }
+ return "Unknown"
+}
+
+// ChannelPriority enums
+const (
+ ChannelPriorityBelowNormal uint16 = 128
+ ChannelPriorityNormal uint16 = 256
+ ChannelPriorityHigh uint16 = 512
+ ChannelPriorityExtraHigh uint16 = 1024
+)
+
+// Marshal returns raw bytes for the given message
+func (c *channelOpen) Marshal() ([]byte, error) {
+ labelLength := len(c.Label)
+ protocolLength := len(c.Protocol)
+
+ totalLen := channelOpenHeaderLength + labelLength + protocolLength
+ raw := make([]byte, totalLen)
+
+ raw[0] = uint8(dataChannelOpen)
+ raw[1] = byte(c.ChannelType)
+ binary.BigEndian.PutUint16(raw[2:], c.Priority)
+ binary.BigEndian.PutUint32(raw[4:], c.ReliabilityParameter)
+ binary.BigEndian.PutUint16(raw[8:], uint16(labelLength))
+ binary.BigEndian.PutUint16(raw[10:], uint16(protocolLength))
+ endLabel := channelOpenHeaderLength + labelLength
+ copy(raw[channelOpenHeaderLength:endLabel], c.Label)
+ copy(raw[endLabel:endLabel+protocolLength], c.Protocol)
+
+ return raw, nil
+}
+
+// Unmarshal populates the struct with the given raw data
+func (c *channelOpen) Unmarshal(raw []byte) error {
+ if len(raw) < channelOpenHeaderLength {
+ return fmt.Errorf("%w expected(%d) actual(%d)", ErrExpectedAndActualLengthMismatch, channelOpenHeaderLength, len(raw))
+ }
+ c.ChannelType = ChannelType(raw[1])
+ c.Priority = binary.BigEndian.Uint16(raw[2:])
+ c.ReliabilityParameter = binary.BigEndian.Uint32(raw[4:])
+
+ labelLength := binary.BigEndian.Uint16(raw[8:])
+ protocolLength := binary.BigEndian.Uint16(raw[10:])
+
+ if expectedLen := channelOpenHeaderLength + int(labelLength) + int(protocolLength); len(raw) != expectedLen {
+ return fmt.Errorf("%w expected(%d) actual(%d)", ErrExpectedAndActualLengthMismatch, expectedLen, len(raw))
+ }
+
+ c.Label = raw[channelOpenHeaderLength : channelOpenHeaderLength+labelLength]
+ c.Protocol = raw[channelOpenHeaderLength+labelLength : channelOpenHeaderLength+labelLength+protocolLength]
+ return nil
+}
+
+func (c channelOpen) String() string {
+ return fmt.Sprintf("Open ChannelType(%s) Priority(%v) ReliabilityParameter(%d) Label(%s) Protocol(%s)", c.ChannelType, c.Priority, c.ReliabilityParameter, string(c.Label), string(c.Protocol))
+}
diff --git a/server/vendor/github.com/pion/datachannel/renovate.json b/server/vendor/github.com/pion/datachannel/renovate.json
new file mode 100644
index 0000000..f1bb98c
--- /dev/null
+++ b/server/vendor/github.com/pion/datachannel/renovate.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": [
+ "github>pion/renovate-config"
+ ]
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/.editorconfig b/server/vendor/github.com/pion/dtls/v2/.editorconfig
new file mode 100644
index 0000000..1dca00f
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/.editorconfig
@@ -0,0 +1,23 @@
+# http://editorconfig.org/
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+root = true
+
+[*]
+charset = utf-8
+insert_final_newline = true
+trim_trailing_whitespace = true
+end_of_line = lf
+
+[*.go]
+indent_style = tab
+indent_size = 4
+
+[{*.yml,*.yaml}]
+indent_style = space
+indent_size = 2
+
+# Makefiles always use tabs for indentation
+[Makefile]
+indent_style = tab
diff --git a/server/vendor/github.com/pion/dtls/v2/.gitignore b/server/vendor/github.com/pion/dtls/v2/.gitignore
new file mode 100644
index 0000000..6e2f206
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/.gitignore
@@ -0,0 +1,28 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+### JetBrains IDE ###
+#####################
+.idea/
+
+### Emacs Temporary Files ###
+#############################
+*~
+
+### Folders ###
+###############
+bin/
+vendor/
+node_modules/
+
+### Files ###
+#############
+*.ivf
+*.ogg
+tags
+cover.out
+*.sw[poe]
+*.wasm
+examples/sfu-ws/cert.pem
+examples/sfu-ws/key.pem
+wasm_exec.js
diff --git a/server/vendor/github.com/pion/dtls/v2/.golangci.yml b/server/vendor/github.com/pion/dtls/v2/.golangci.yml
new file mode 100644
index 0000000..4e3eddf
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/.golangci.yml
@@ -0,0 +1,137 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+ gomodguard:
+ blocked:
+ modules:
+ - github.com/pkg/errors:
+ recommendations:
+ - errors
+ forbidigo:
+ forbid:
+ - ^fmt.Print(f|ln)?$
+ - ^log.(Panic|Fatal|Print)(f|ln)?$
+ - ^os.Exit$
+ - ^panic$
+ - ^print(ln)?$
+
+linters:
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bidichk # Checks for dangerous unicode character sequences
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - contextcheck # check the function whether use a non-inherited context
+ - decorder # check declaration order and count of types, constants, variables and functions
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - durationcheck # check for two durations multiplied together
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
+ - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
+ - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - forbidigo # Forbids identifiers
+ - forcetypeassert # finds forced type assertions
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - goerr113 # Golang linter to check the errors handling expressions
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - grouper # An analyzer to analyze expression groups.
+ - importas # Enforces consistent import aliases
+ - ineffassign # Detects when assignments to existing variables are not used
+ - misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - nilerr # Finds the code that returns nil even if it checks that the error is not nil.
+ - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
+ - noctx # noctx finds sending http request without context.Context
+ - predeclared # find code that shadows one of Go's predeclared identifiers
+ - revive # golint replacement, finds style mistakes
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - stylecheck # Stylecheck is a replacement for golint
+ - tagliatelle # Checks the struct tags.
+ - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
+ - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - wastedassign # wastedassign finds wasted assignment statements
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - containedctx # containedctx is a linter that detects struct contained context.Context field
+ - cyclop # checks function and package cyclomatic complexity
+ - exhaustivestruct # Checks if all struct's fields are initialized
+ - funlen # Tool for detection of long functions
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - gomnd # An analyzer to detect magic numbers.
+ - ifshort # Checks that your code uses short syntax for if-statements whenever possible
+ - ireturn # Accept Interfaces, Return Concrete Types
+ - lll # Reports long lines
+ - maintidx # maintidx measures the maintainability index of each function.
+ - makezero # Finds slice declarations with non-zero initial length
+ - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nestif # Reports deeply nested if statements
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - promlinter # Check Prometheus metrics naming via promlint
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
+ - varnamelen # checks that the length of a variable's name matches its scope
+ - wrapcheck # Checks that errors returned from external packages are wrapped
+ - wsl # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+ exclude-use-default: false
+ exclude-rules:
+ # Allow complex tests, better to be self contained
+ - path: _test\.go
+ linters:
+ - gocognit
+ - forbidigo
+
+ # Allow complex main function in examples
+ - path: examples
+ text: "of func `main` is high"
+ linters:
+ - gocognit
+
+ # Allow forbidden identifiers in examples
+ - path: examples
+ linters:
+ - forbidigo
+
+ # Allow forbidden identifiers in CLI commands
+ - path: cmd
+ linters:
+ - forbidigo
+
+run:
+ skip-dirs-use-default: false
diff --git a/server/vendor/github.com/pion/dtls/v2/.goreleaser.yml b/server/vendor/github.com/pion/dtls/v2/.goreleaser.yml
new file mode 100644
index 0000000..30093e9
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/.goreleaser.yml
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+builds:
+- skip: true
diff --git a/server/vendor/github.com/pion/dtls/v2/AUTHORS.txt b/server/vendor/github.com/pion/dtls/v2/AUTHORS.txt
new file mode 100644
index 0000000..e14fae4
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/AUTHORS.txt
@@ -0,0 +1,57 @@
+# Thank you to everyone that made Pion possible. If you are interested in contributing
+# we would love to have you https://github.com/pion/webrtc/wiki/Contributing
+#
+# This file is auto generated, using git to list all individuals contributors.
+# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting
+Aleksandr Razumov
+alvarowolfx
+Arlo Breault
+Atsushi Watanabe
+backkem
+bjdgyc
+boks1971
+Bragadeesh
+Carson Hoffman
+Cecylia Bocovich
+Chris Hiszpanski
+cnderrauber
+Daniele Sluijters
+folbrich
+Hayden James
+Hugo Arregui
+Hugo Arregui
+igolaizola <11333576+igolaizola@users.noreply.github.com>
+Jeffrey Stoke
+Jeroen de Bruijn
+Jeroen de Bruijn
+Jim Wert
+jinleileiking
+Jozef Kralik
+Julien Salleyron
+Juliusz Chroboczek
+Kegan Dougal
+Kevin Wang
+Lander Noterman
+Len
+Lukas Lihotzki
+ManuelBk <26275612+ManuelBk@users.noreply.github.com>
+Michael Zabka
+Michiel De Backker
+Rachel Chen
+Robert Eperjesi
+Ryan Gordon
+Sam Lancia
+Sean DuBois
+Sean DuBois
+Sean DuBois
+Shelikhoo
+Stefan Tatschner
+Steffen Vogel
+Vadim
+Vadim Filimonov
+wmiao
+ZHENK
+吕海涛
+
+# List of contributors not appearing in Git history
+
diff --git a/server/vendor/github.com/pion/dtls/v2/LICENSE b/server/vendor/github.com/pion/dtls/v2/LICENSE
new file mode 100644
index 0000000..491caf6
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/LICENSE
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) 2023 The Pion community
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/server/vendor/github.com/pion/dtls/v2/README.md b/server/vendor/github.com/pion/dtls/v2/README.md
new file mode 100644
index 0000000..0c06595
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/README.md
@@ -0,0 +1,151 @@
+
+
+ Pion DTLS
+
+
+A Go implementation of DTLS
+
+
+
+
+
+
+
+
+
+
+
+
+
+Native [DTLS 1.2][rfc6347] implementation in the Go programming language.
+
+A long term goal is a professional security review, and maybe an inclusion in stdlib.
+
+### RFCs
+#### Implemented
+- **RFC 6347**: [Datagram Transport Layer Security Version 1.2][rfc6347]
+- **RFC 5705**: [Keying Material Exporters for Transport Layer Security (TLS)][rfc5705]
+- **RFC 7627**: [Transport Layer Security (TLS) - Session Hash and Extended Master Secret Extension][rfc7627]
+- **RFC 7301**: [Transport Layer Security (TLS) - Application-Layer Protocol Negotiation Extension][rfc7301]
+
+[rfc5289]: https://tools.ietf.org/html/rfc5289
+[rfc5487]: https://tools.ietf.org/html/rfc5487
+[rfc5489]: https://tools.ietf.org/html/rfc5489
+[rfc5705]: https://tools.ietf.org/html/rfc5705
+[rfc6347]: https://tools.ietf.org/html/rfc6347
+[rfc6655]: https://tools.ietf.org/html/rfc6655
+[rfc7301]: https://tools.ietf.org/html/rfc7301
+[rfc7627]: https://tools.ietf.org/html/rfc7627
+[rfc8422]: https://tools.ietf.org/html/rfc8422
+
+### Goals/Progress
+This will only be targeting DTLS 1.2, and the most modern/common cipher suites.
+We would love contributions that fall under the 'Planned Features' and any bug fixes!
+
+#### Current features
+* DTLS 1.2 Client/Server
+* Key Exchange via ECDHE(curve25519, nistp256, nistp384) and PSK
+* Packet loss and re-ordering is handled during handshaking
+* Key export ([RFC 5705][rfc5705])
+* Serialization and Resumption of sessions
+* Extended Master Secret extension ([RFC 7627][rfc7627])
+* ALPN extension ([RFC 7301][rfc7301])
+
+#### Supported ciphers
+
+##### ECDHE
+
+* TLS_ECDHE_ECDSA_WITH_AES_128_CCM ([RFC 6655][rfc6655])
+* TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655])
+* TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ([RFC 5289][rfc5289])
+* TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ([RFC 5289][rfc5289])
+* TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ([RFC 5289][rfc5289])
+* TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ([RFC 5289][rfc5289])
+* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ([RFC 8422][rfc8422])
+* TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ([RFC 8422][rfc8422])
+
+##### PSK
+
+* TLS_PSK_WITH_AES_128_CCM ([RFC 6655][rfc6655])
+* TLS_PSK_WITH_AES_128_CCM_8 ([RFC 6655][rfc6655])
+* TLS_PSK_WITH_AES_256_CCM_8 ([RFC 6655][rfc6655])
+* TLS_PSK_WITH_AES_128_GCM_SHA256 ([RFC 5487][rfc5487])
+* TLS_PSK_WITH_AES_128_CBC_SHA256 ([RFC 5487][rfc5487])
+
+##### ECDHE & PSK
+
+* TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 ([RFC 5489][rfc5489])
+
+#### Planned Features
+* Chacha20Poly1305
+
+#### Excluded Features
+* DTLS 1.0
+* Renegotiation
+* Compression
+
+### Using
+
+This library needs at least Go 1.13, and you should have [Go modules
+enabled](https://github.com/golang/go/wiki/Modules).
+
+#### Pion DTLS
+For a DTLS 1.2 Server that listens on 127.0.0.1:4444
+```sh
+go run examples/listen/selfsign/main.go
+```
+
+For a DTLS 1.2 Client that connects to 127.0.0.1:4444
+```sh
+go run examples/dial/selfsign/main.go
+```
+
+#### OpenSSL
+Pion DTLS can connect to itself and OpenSSL.
+```
+ // Generate a certificate
+ openssl ecparam -out key.pem -name prime256v1 -genkey
+ openssl req -new -sha256 -key key.pem -out server.csr
+ openssl x509 -req -sha256 -days 365 -in server.csr -signkey key.pem -out cert.pem
+
+ // Use with examples/dial/selfsign/main.go
+ openssl s_server -dtls1_2 -cert cert.pem -key key.pem -accept 4444
+
+ // Use with examples/listen/selfsign/main.go
+ openssl s_client -dtls1_2 -connect 127.0.0.1:4444 -debug -cert cert.pem -key key.pem
+```
+
+### Using with PSK
+Pion DTLS also comes with examples that do key exchange via PSK
+
+#### Pion DTLS
+```sh
+go run examples/listen/psk/main.go
+```
+
+```sh
+go run examples/dial/psk/main.go
+```
+
+#### OpenSSL
+```
+ // Use with examples/dial/psk/main.go
+ openssl s_server -dtls1_2 -accept 4444 -nocert -psk abc123 -cipher PSK-AES128-CCM8
+
+ // Use with examples/listen/psk/main.go
+ openssl s_client -dtls1_2 -connect 127.0.0.1:4444 -psk abc123 -cipher PSK-AES128-CCM8
+```
+
+### Community
+Pion has an active community on the [Slack](https://pion.ly/slack).
+
+Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible: [AUTHORS.txt](./AUTHORS.txt)
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/server/vendor/github.com/pion/dtls/v2/certificate.go b/server/vendor/github.com/pion/dtls/v2/certificate.go
new file mode 100644
index 0000000..519fc87
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/certificate.go
@@ -0,0 +1,157 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "bytes"
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "strings"
+)
+
+// ClientHelloInfo contains information from a ClientHello message in order to
+// guide application logic in the GetCertificate.
+type ClientHelloInfo struct {
+ // ServerName indicates the name of the server requested by the client
+ // in order to support virtual hosting. ServerName is only set if the
+ // client is using SNI (see RFC 4366, Section 3.1).
+ ServerName string
+
+ // CipherSuites lists the CipherSuites supported by the client (e.g.
+ // TLS_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256).
+ CipherSuites []CipherSuiteID
+}
+
+// CertificateRequestInfo contains information from a server's
+// CertificateRequest message, which is used to demand a certificate and proof
+// of control from a client.
+type CertificateRequestInfo struct {
+ // AcceptableCAs contains zero or more, DER-encoded, X.501
+ // Distinguished Names. These are the names of root or intermediate CAs
+ // that the server wishes the returned certificate to be signed by. An
+ // empty slice indicates that the server has no preference.
+ AcceptableCAs [][]byte
+}
+
+// SupportsCertificate returns nil if the provided certificate is supported by
+// the server that sent the CertificateRequest. Otherwise, it returns an error
+// describing the reason for the incompatibility.
+// NOTE: original src: https://github.com/golang/go/blob/29b9a328d268d53833d2cc063d1d8b4bf6852675/src/crypto/tls/common.go#L1273
+func (cri *CertificateRequestInfo) SupportsCertificate(c *tls.Certificate) error {
+ if len(cri.AcceptableCAs) == 0 {
+ return nil
+ }
+
+ for j, cert := range c.Certificate {
+ x509Cert := c.Leaf
+ // Parse the certificate if this isn't the leaf node, or if
+ // chain.Leaf was nil.
+ if j != 0 || x509Cert == nil {
+ var err error
+ if x509Cert, err = x509.ParseCertificate(cert); err != nil {
+ return fmt.Errorf("failed to parse certificate #%d in the chain: %w", j, err)
+ }
+ }
+
+ for _, ca := range cri.AcceptableCAs {
+ if bytes.Equal(x509Cert.RawIssuer, ca) {
+ return nil
+ }
+ }
+ }
+ return errNotAcceptableCertificateChain
+}
+
+func (c *handshakeConfig) setNameToCertificateLocked() {
+ nameToCertificate := make(map[string]*tls.Certificate)
+ for i := range c.localCertificates {
+ cert := &c.localCertificates[i]
+ x509Cert := cert.Leaf
+ if x509Cert == nil {
+ var parseErr error
+ x509Cert, parseErr = x509.ParseCertificate(cert.Certificate[0])
+ if parseErr != nil {
+ continue
+ }
+ }
+ if len(x509Cert.Subject.CommonName) > 0 {
+ nameToCertificate[strings.ToLower(x509Cert.Subject.CommonName)] = cert
+ }
+ for _, san := range x509Cert.DNSNames {
+ nameToCertificate[strings.ToLower(san)] = cert
+ }
+ }
+ c.nameToCertificate = nameToCertificate
+}
+
+func (c *handshakeConfig) getCertificate(clientHelloInfo *ClientHelloInfo) (*tls.Certificate, error) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if c.localGetCertificate != nil &&
+ (len(c.localCertificates) == 0 || len(clientHelloInfo.ServerName) > 0) {
+ cert, err := c.localGetCertificate(clientHelloInfo)
+ if cert != nil || err != nil {
+ return cert, err
+ }
+ }
+
+ if c.nameToCertificate == nil {
+ c.setNameToCertificateLocked()
+ }
+
+ if len(c.localCertificates) == 0 {
+ return nil, errNoCertificates
+ }
+
+ if len(c.localCertificates) == 1 {
+ // There's only one choice, so no point doing any work.
+ return &c.localCertificates[0], nil
+ }
+
+ if len(clientHelloInfo.ServerName) == 0 {
+ return &c.localCertificates[0], nil
+ }
+
+ name := strings.TrimRight(strings.ToLower(clientHelloInfo.ServerName), ".")
+
+ if cert, ok := c.nameToCertificate[name]; ok {
+ return cert, nil
+ }
+
+ // try replacing labels in the name with wildcards until we get a
+ // match.
+ labels := strings.Split(name, ".")
+ for i := range labels {
+ labels[i] = "*"
+ candidate := strings.Join(labels, ".")
+ if cert, ok := c.nameToCertificate[candidate]; ok {
+ return cert, nil
+ }
+ }
+
+ // If nothing matches, return the first certificate.
+ return &c.localCertificates[0], nil
+}
+
+// NOTE: original src: https://github.com/golang/go/blob/29b9a328d268d53833d2cc063d1d8b4bf6852675/src/crypto/tls/handshake_client.go#L974
+func (c *handshakeConfig) getClientCertificate(cri *CertificateRequestInfo) (*tls.Certificate, error) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if c.localGetClientCertificate != nil {
+ return c.localGetClientCertificate(cri)
+ }
+
+ for i := range c.localCertificates {
+ chain := c.localCertificates[i]
+ if err := cri.SupportsCertificate(&chain); err != nil {
+ continue
+ }
+ return &chain, nil
+ }
+
+ // No acceptable certificate found. Don't send a certificate.
+ return new(tls.Certificate), nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/cipher_suite.go b/server/vendor/github.com/pion/dtls/v2/cipher_suite.go
new file mode 100644
index 0000000..7a5bb4a
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/cipher_suite.go
@@ -0,0 +1,276 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "crypto/tls"
+ "fmt"
+ "hash"
+
+ "github.com/pion/dtls/v2/internal/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// CipherSuiteID is an ID for our supported CipherSuites
+type CipherSuiteID = ciphersuite.ID
+
+// Supported Cipher Suites
+const (
+ // AES-128-CCM
+ TLS_ECDHE_ECDSA_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM //nolint:revive,stylecheck
+ TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 //nolint:revive,stylecheck
+
+ // AES-128-GCM-SHA256
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 //nolint:revive,stylecheck
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 //nolint:revive,stylecheck
+
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 //nolint:revive,stylecheck
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 //nolint:revive,stylecheck
+
+ // AES-256-CBC-SHA
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA //nolint:revive,stylecheck
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CipherSuiteID = ciphersuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA //nolint:revive,stylecheck
+
+ TLS_PSK_WITH_AES_128_CCM CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM //nolint:revive,stylecheck
+ TLS_PSK_WITH_AES_128_CCM_8 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CCM_8 //nolint:revive,stylecheck
+ TLS_PSK_WITH_AES_256_CCM_8 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_256_CCM_8 //nolint:revive,stylecheck
+ TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_GCM_SHA256 //nolint:revive,stylecheck
+ TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_PSK_WITH_AES_128_CBC_SHA256 //nolint:revive,stylecheck
+
+ TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CipherSuiteID = ciphersuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 //nolint:revive,stylecheck
+)
+
+// CipherSuiteAuthenticationType controls what authentication method is using during the handshake for a CipherSuite
+type CipherSuiteAuthenticationType = ciphersuite.AuthenticationType
+
+// AuthenticationType Enums
+const (
+ CipherSuiteAuthenticationTypeCertificate CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypeCertificate
+ CipherSuiteAuthenticationTypePreSharedKey CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypePreSharedKey
+ CipherSuiteAuthenticationTypeAnonymous CipherSuiteAuthenticationType = ciphersuite.AuthenticationTypeAnonymous
+)
+
+// CipherSuiteKeyExchangeAlgorithm controls what exchange algorithm is using during the handshake for a CipherSuite
+type CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithm
+
+// CipherSuiteKeyExchangeAlgorithm Bitmask
+const (
+ CipherSuiteKeyExchangeAlgorithmNone CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmNone
+ CipherSuiteKeyExchangeAlgorithmPsk CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmPsk
+ CipherSuiteKeyExchangeAlgorithmEcdhe CipherSuiteKeyExchangeAlgorithm = ciphersuite.KeyExchangeAlgorithmEcdhe
+)
+
+var _ = allCipherSuites() // Necessary until this function isn't only used by Go 1.14
+
+// CipherSuite is an interface that all DTLS CipherSuites must satisfy
+type CipherSuite interface {
+ // String of CipherSuite, only used for logging
+ String() string
+
+ // ID of CipherSuite.
+ ID() CipherSuiteID
+
+ // What type of Certificate does this CipherSuite use
+ CertificateType() clientcertificate.Type
+
+ // What Hash function is used during verification
+ HashFunc() func() hash.Hash
+
+ // AuthenticationType controls what authentication method is using during the handshake
+ AuthenticationType() CipherSuiteAuthenticationType
+
+ // KeyExchangeAlgorithm controls what exchange algorithm is using during the handshake
+ KeyExchangeAlgorithm() CipherSuiteKeyExchangeAlgorithm
+
+ // ECC (Elliptic Curve Cryptography) determines whether ECC extesions will be send during handshake.
+ // https://datatracker.ietf.org/doc/html/rfc4492#page-10
+ ECC() bool
+
+ // Called when keying material has been generated, should initialize the internal cipher
+ Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error
+ IsInitialized() bool
+ Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error)
+ Decrypt(in []byte) ([]byte, error)
+}
+
+// CipherSuiteName provides the same functionality as tls.CipherSuiteName
+// that appeared first in Go 1.14.
+//
+// Our implementation differs slightly in that it takes in a CiperSuiteID,
+// like the rest of our library, instead of a uint16 like crypto/tls.
+func CipherSuiteName(id CipherSuiteID) string {
+ suite := cipherSuiteForID(id, nil)
+ if suite != nil {
+ return suite.String()
+ }
+ return fmt.Sprintf("0x%04X", uint16(id))
+}
+
+// Taken from https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
+// A cipherSuite is a specific combination of key agreement, cipher and MAC
+// function.
+func cipherSuiteForID(id CipherSuiteID, customCiphers func() []CipherSuite) CipherSuite {
+ switch id { //nolint:exhaustive
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
+ return ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm()
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
+ return ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8()
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ return &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{}
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ return &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{}
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ return &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{}
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ return &ciphersuite.TLSEcdheRsaWithAes256CbcSha{}
+ case TLS_PSK_WITH_AES_128_CCM:
+ return ciphersuite.NewTLSPskWithAes128Ccm()
+ case TLS_PSK_WITH_AES_128_CCM_8:
+ return ciphersuite.NewTLSPskWithAes128Ccm8()
+ case TLS_PSK_WITH_AES_256_CCM_8:
+ return ciphersuite.NewTLSPskWithAes256Ccm8()
+ case TLS_PSK_WITH_AES_128_GCM_SHA256:
+ return &ciphersuite.TLSPskWithAes128GcmSha256{}
+ case TLS_PSK_WITH_AES_128_CBC_SHA256:
+ return &ciphersuite.TLSPskWithAes128CbcSha256{}
+ case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ return &ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{}
+ case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ return &ciphersuite.TLSEcdheRsaWithAes256GcmSha384{}
+ case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+ return ciphersuite.NewTLSEcdhePskWithAes128CbcSha256()
+ }
+
+ if customCiphers != nil {
+ for _, c := range customCiphers() {
+ if c.ID() == id {
+ return c
+ }
+ }
+ }
+
+ return nil
+}
+
+// CipherSuites we support in order of preference
+func defaultCipherSuites() []CipherSuite {
+ return []CipherSuite{
+ &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{},
+ &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{},
+ &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{},
+ &ciphersuite.TLSEcdheRsaWithAes256CbcSha{},
+ &ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{},
+ &ciphersuite.TLSEcdheRsaWithAes256GcmSha384{},
+ }
+}
+
+func allCipherSuites() []CipherSuite {
+ return []CipherSuite{
+ ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm(),
+ ciphersuite.NewTLSEcdheEcdsaWithAes128Ccm8(),
+ &ciphersuite.TLSEcdheEcdsaWithAes128GcmSha256{},
+ &ciphersuite.TLSEcdheRsaWithAes128GcmSha256{},
+ &ciphersuite.TLSEcdheEcdsaWithAes256CbcSha{},
+ &ciphersuite.TLSEcdheRsaWithAes256CbcSha{},
+ ciphersuite.NewTLSPskWithAes128Ccm(),
+ ciphersuite.NewTLSPskWithAes128Ccm8(),
+ ciphersuite.NewTLSPskWithAes256Ccm8(),
+ &ciphersuite.TLSPskWithAes128GcmSha256{},
+ &ciphersuite.TLSEcdheEcdsaWithAes256GcmSha384{},
+ &ciphersuite.TLSEcdheRsaWithAes256GcmSha384{},
+ }
+}
+
+func cipherSuiteIDs(cipherSuites []CipherSuite) []uint16 {
+ rtrn := []uint16{}
+ for _, c := range cipherSuites {
+ rtrn = append(rtrn, uint16(c.ID()))
+ }
+ return rtrn
+}
+
+func parseCipherSuites(userSelectedSuites []CipherSuiteID, customCipherSuites func() []CipherSuite, includeCertificateSuites, includePSKSuites bool) ([]CipherSuite, error) {
+ cipherSuitesForIDs := func(ids []CipherSuiteID) ([]CipherSuite, error) {
+ cipherSuites := []CipherSuite{}
+ for _, id := range ids {
+ c := cipherSuiteForID(id, nil)
+ if c == nil {
+ return nil, &invalidCipherSuiteError{id}
+ }
+ cipherSuites = append(cipherSuites, c)
+ }
+ return cipherSuites, nil
+ }
+
+ var (
+ cipherSuites []CipherSuite
+ err error
+ i int
+ )
+ if userSelectedSuites != nil {
+ cipherSuites, err = cipherSuitesForIDs(userSelectedSuites)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ cipherSuites = defaultCipherSuites()
+ }
+
+ // Put CustomCipherSuites before ID selected suites
+ if customCipherSuites != nil {
+ cipherSuites = append(customCipherSuites(), cipherSuites...)
+ }
+
+ var foundCertificateSuite, foundPSKSuite, foundAnonymousSuite bool
+ for _, c := range cipherSuites {
+ switch {
+ case includeCertificateSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate:
+ foundCertificateSuite = true
+ case includePSKSuites && c.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey:
+ foundPSKSuite = true
+ case c.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous:
+ foundAnonymousSuite = true
+ default:
+ continue
+ }
+ cipherSuites[i] = c
+ i++
+ }
+
+ switch {
+ case includeCertificateSuites && !foundCertificateSuite && !foundAnonymousSuite:
+ return nil, errNoAvailableCertificateCipherSuite
+ case includePSKSuites && !foundPSKSuite:
+ return nil, errNoAvailablePSKCipherSuite
+ case i == 0:
+ return nil, errNoAvailableCipherSuites
+ }
+
+ return cipherSuites[:i], nil
+}
+
+func filterCipherSuitesForCertificate(cert *tls.Certificate, cipherSuites []CipherSuite) []CipherSuite {
+ if cert == nil || cert.PrivateKey == nil {
+ return cipherSuites
+ }
+ var certType clientcertificate.Type
+ switch cert.PrivateKey.(type) {
+ case ed25519.PrivateKey, *ecdsa.PrivateKey:
+ certType = clientcertificate.ECDSASign
+ case *rsa.PrivateKey:
+ certType = clientcertificate.RSASign
+ }
+
+ filtered := []CipherSuite{}
+ for _, c := range cipherSuites {
+ if c.AuthenticationType() != CipherSuiteAuthenticationTypeCertificate || certType == c.CertificateType() {
+ filtered = append(filtered, c)
+ }
+ }
+ return filtered
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/cipher_suite_go114.go b/server/vendor/github.com/pion/dtls/v2/cipher_suite_go114.go
new file mode 100644
index 0000000..fd46d7b
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/cipher_suite_go114.go
@@ -0,0 +1,44 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+//go:build go1.14
+// +build go1.14
+
+package dtls
+
+import (
+ "crypto/tls"
+)
+
+// VersionDTLS12 is the DTLS version in the same style as
+// VersionTLSXX from crypto/tls
+const VersionDTLS12 = 0xfefd
+
+// Convert from our cipherSuite interface to a tls.CipherSuite struct
+func toTLSCipherSuite(c CipherSuite) *tls.CipherSuite {
+ return &tls.CipherSuite{
+ ID: uint16(c.ID()),
+ Name: c.String(),
+ SupportedVersions: []uint16{VersionDTLS12},
+ Insecure: false,
+ }
+}
+
+// CipherSuites returns a list of cipher suites currently implemented by this
+// package, excluding those with security issues, which are returned by
+// InsecureCipherSuites.
+func CipherSuites() []*tls.CipherSuite {
+ suites := allCipherSuites()
+ res := make([]*tls.CipherSuite, len(suites))
+ for i, c := range suites {
+ res[i] = toTLSCipherSuite(c)
+ }
+ return res
+}
+
+// InsecureCipherSuites returns a list of cipher suites currently implemented by
+// this package and which have security issues.
+func InsecureCipherSuites() []*tls.CipherSuite {
+ var res []*tls.CipherSuite
+ return res
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/codecov.yml b/server/vendor/github.com/pion/dtls/v2/codecov.yml
new file mode 100644
index 0000000..263e4d4
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/codecov.yml
@@ -0,0 +1,22 @@
+#
+# DO NOT EDIT THIS FILE
+#
+# It is automatically copied from https://github.com/pion/.goassets repository.
+#
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+coverage:
+ status:
+ project:
+ default:
+ # Allow decreasing 2% of total coverage to avoid noise.
+ threshold: 2%
+ patch:
+ default:
+ target: 70%
+ only_pulls: true
+
+ignore:
+ - "examples/*"
+ - "examples/**/*"
diff --git a/server/vendor/github.com/pion/dtls/v2/compression_method.go b/server/vendor/github.com/pion/dtls/v2/compression_method.go
new file mode 100644
index 0000000..7e44de0
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/compression_method.go
@@ -0,0 +1,12 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import "github.com/pion/dtls/v2/pkg/protocol"
+
+func defaultCompressionMethods() []*protocol.CompressionMethod {
+ return []*protocol.CompressionMethod{
+ {},
+ }
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/config.go b/server/vendor/github.com/pion/dtls/v2/config.go
new file mode 100644
index 0000000..fbc3ee2
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/config.go
@@ -0,0 +1,253 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "context"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "crypto/tls"
+ "crypto/x509"
+ "io"
+ "time"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/logging"
+)
+
+const keyLogLabelTLS12 = "CLIENT_RANDOM"
+
+// Config is used to configure a DTLS client or server.
+// After a Config is passed to a DTLS function it must not be modified.
+type Config struct {
+ // Certificates contains certificate chain to present to the other side of the connection.
+ // Server MUST set this if PSK is non-nil
+ // client SHOULD sets this so CertificateRequests can be handled if PSK is non-nil
+ Certificates []tls.Certificate
+
+ // CipherSuites is a list of supported cipher suites.
+ // If CipherSuites is nil, a default list is used
+ CipherSuites []CipherSuiteID
+
+ // CustomCipherSuites is a list of CipherSuites that can be
+ // provided by the user. This allow users to user Ciphers that are reserved
+ // for private usage.
+ CustomCipherSuites func() []CipherSuite
+
+ // SignatureSchemes contains the signature and hash schemes that the peer requests to verify.
+ SignatureSchemes []tls.SignatureScheme
+
+ // SRTPProtectionProfiles are the supported protection profiles
+ // Clients will send this via use_srtp and assert that the server properly responds
+ // Servers will assert that clients send one of these profiles and will respond as needed
+ SRTPProtectionProfiles []SRTPProtectionProfile
+
+ // ClientAuth determines the server's policy for
+ // TLS Client Authentication. The default is NoClientCert.
+ ClientAuth ClientAuthType
+
+ // RequireExtendedMasterSecret determines if the "Extended Master Secret" extension
+ // should be disabled, requested, or required (default requested).
+ ExtendedMasterSecret ExtendedMasterSecretType
+
+ // FlightInterval controls how often we send outbound handshake messages
+ // defaults to time.Second
+ FlightInterval time.Duration
+
+ // PSK sets the pre-shared key used by this DTLS connection
+ // If PSK is non-nil only PSK CipherSuites will be used
+ PSK PSKCallback
+ PSKIdentityHint []byte
+
+ // InsecureSkipVerify controls whether a client verifies the
+ // server's certificate chain and host name.
+ // If InsecureSkipVerify is true, TLS accepts any certificate
+ // presented by the server and any host name in that certificate.
+ // In this mode, TLS is susceptible to man-in-the-middle attacks.
+ // This should be used only for testing.
+ InsecureSkipVerify bool
+
+ // InsecureHashes allows the use of hashing algorithms that are known
+ // to be vulnerable.
+ InsecureHashes bool
+
+ // VerifyPeerCertificate, if not nil, is called after normal
+ // certificate verification by either a client or server. It
+ // receives the certificate provided by the peer and also a flag
+ // that tells if normal verification has succeedded. If it returns a
+ // non-nil error, the handshake is aborted and that error results.
+ //
+ // If normal verification fails then the handshake will abort before
+ // considering this callback. If normal verification is disabled by
+ // setting InsecureSkipVerify, or (for a server) when ClientAuth is
+ // RequestClientCert or RequireAnyClientCert, then this callback will
+ // be considered but the verifiedChains will always be nil.
+ VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
+
+ // VerifyConnection, if not nil, is called after normal certificate
+ // verification/PSK and after VerifyPeerCertificate by either a TLS client
+ // or server. If it returns a non-nil error, the handshake is aborted
+ // and that error results.
+ //
+ // If normal verification fails then the handshake will abort before
+ // considering this callback. This callback will run for all connections
+ // regardless of InsecureSkipVerify or ClientAuth settings.
+ VerifyConnection func(*State) error
+
+ // RootCAs defines the set of root certificate authorities
+ // that one peer uses when verifying the other peer's certificates.
+ // If RootCAs is nil, TLS uses the host's root CA set.
+ RootCAs *x509.CertPool
+
+ // ClientCAs defines the set of root certificate authorities
+ // that servers use if required to verify a client certificate
+ // by the policy in ClientAuth.
+ ClientCAs *x509.CertPool
+
+ // ServerName is used to verify the hostname on the returned
+ // certificates unless InsecureSkipVerify is given.
+ ServerName string
+
+ LoggerFactory logging.LoggerFactory
+
+ // ConnectContextMaker is a function to make a context used in Dial(),
+ // Client(), Server(), and Accept(). If nil, the default ConnectContextMaker
+ // is used. It can be implemented as following.
+ //
+ // func ConnectContextMaker() (context.Context, func()) {
+ // return context.WithTimeout(context.Background(), 30*time.Second)
+ // }
+ ConnectContextMaker func() (context.Context, func())
+
+ // MTU is the length at which handshake messages will be fragmented to
+ // fit within the maximum transmission unit (default is 1200 bytes)
+ MTU int
+
+ // ReplayProtectionWindow is the size of the replay attack protection window.
+ // Duplication of the sequence number is checked in this window size.
+ // Packet with sequence number older than this value compared to the latest
+ // accepted packet will be discarded. (default is 64)
+ ReplayProtectionWindow int
+
+ // KeyLogWriter optionally specifies a destination for TLS master secrets
+ // in NSS key log format that can be used to allow external programs
+ // such as Wireshark to decrypt TLS connections.
+ // See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format.
+ // Use of KeyLogWriter compromises security and should only be
+ // used for debugging.
+ KeyLogWriter io.Writer
+
+ // SessionStore is the container to store session for resumption.
+ SessionStore SessionStore
+
+ // List of application protocols the peer supports, for ALPN
+ SupportedProtocols []string
+
+ // List of Elliptic Curves to use
+ //
+ // If an ECC ciphersuite is configured and EllipticCurves is empty
+ // it will default to X25519, P-256, P-384 in this specific order.
+ EllipticCurves []elliptic.Curve
+
+ // GetCertificate returns a Certificate based on the given
+ // ClientHelloInfo. It will only be called if the client supplies SNI
+ // information or if Certificates is empty.
+ //
+ // If GetCertificate is nil or returns nil, then the certificate is
+ // retrieved from NameToCertificate. If NameToCertificate is nil, the
+ // best element of Certificates will be used.
+ GetCertificate func(*ClientHelloInfo) (*tls.Certificate, error)
+
+ // GetClientCertificate, if not nil, is called when a server requests a
+ // certificate from a client. If set, the contents of Certificates will
+ // be ignored.
+ //
+ // If GetClientCertificate returns an error, the handshake will be
+ // aborted and that error will be returned. Otherwise
+ // GetClientCertificate must return a non-nil Certificate. If
+ // Certificate.Certificate is empty then no certificate will be sent to
+ // the server. If this is unacceptable to the server then it may abort
+ // the handshake.
+ GetClientCertificate func(*CertificateRequestInfo) (*tls.Certificate, error)
+
+ // InsecureSkipVerifyHello, if true and when acting as server, allow client to
+ // skip hello verify phase and receive ServerHello after initial ClientHello.
+ // This have implication on DoS attack resistance.
+ InsecureSkipVerifyHello bool
+}
+
+func defaultConnectContextMaker() (context.Context, func()) {
+ return context.WithTimeout(context.Background(), 30*time.Second)
+}
+
+func (c *Config) connectContextMaker() (context.Context, func()) {
+ if c.ConnectContextMaker == nil {
+ return defaultConnectContextMaker()
+ }
+ return c.ConnectContextMaker()
+}
+
+func (c *Config) includeCertificateSuites() bool {
+ return c.PSK == nil || len(c.Certificates) > 0 || c.GetCertificate != nil || c.GetClientCertificate != nil
+}
+
+const defaultMTU = 1200 // bytes
+
+var defaultCurves = []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384} //nolint:gochecknoglobals
+
+// PSKCallback is called once we have the remote's PSKIdentityHint.
+// If the remote provided none it will be nil
+type PSKCallback func([]byte) ([]byte, error)
+
+// ClientAuthType declares the policy the server will follow for
+// TLS Client Authentication.
+type ClientAuthType int
+
+// ClientAuthType enums
+const (
+ NoClientCert ClientAuthType = iota
+ RequestClientCert
+ RequireAnyClientCert
+ VerifyClientCertIfGiven
+ RequireAndVerifyClientCert
+)
+
+// ExtendedMasterSecretType declares the policy the client and server
+// will follow for the Extended Master Secret extension
+type ExtendedMasterSecretType int
+
+// ExtendedMasterSecretType enums
+const (
+ RequestExtendedMasterSecret ExtendedMasterSecretType = iota
+ RequireExtendedMasterSecret
+ DisableExtendedMasterSecret
+)
+
+func validateConfig(config *Config) error {
+ switch {
+ case config == nil:
+ return errNoConfigProvided
+ case config.PSKIdentityHint != nil && config.PSK == nil:
+ return errIdentityNoPSK
+ }
+
+ for _, cert := range config.Certificates {
+ if cert.Certificate == nil {
+ return errInvalidCertificate
+ }
+ if cert.PrivateKey != nil {
+ switch cert.PrivateKey.(type) {
+ case ed25519.PrivateKey:
+ case *ecdsa.PrivateKey:
+ case *rsa.PrivateKey:
+ default:
+ return errInvalidPrivateKey
+ }
+ }
+ }
+
+ _, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.includeCertificateSuites(), config.PSK != nil)
+ return err
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/conn.go b/server/vendor/github.com/pion/dtls/v2/conn.go
new file mode 100644
index 0000000..ccea999
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/conn.go
@@ -0,0 +1,1057 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/dtls/v2/internal/closer"
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/crypto/signaturehash"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "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"
+)
+
+const (
+ initialTickerInterval = time.Second
+ cookieLength = 20
+ sessionLength = 32
+ defaultNamedCurve = elliptic.X25519
+ inboundBufferSize = 8192
+ // Default replay protection window is specified by RFC 6347 Section 4.1.2.6
+ defaultReplayProtectionWindow = 64
+ // maxAppDataPacketQueueSize is the maximum number of app data packets we will
+ // enqueue before the handshake is completed
+ maxAppDataPacketQueueSize = 100
+)
+
+func invalidKeyingLabels() map[string]bool {
+ return map[string]bool{
+ "client finished": true,
+ "server finished": true,
+ "master secret": true,
+ "key expansion": true,
+ }
+}
+
+// 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
+
+ maximumTransmissionUnit int
+
+ handshakeCompletedSuccessfully atomic.Value
+
+ encryptedPackets [][]byte
+
+ connectionClosedByUser bool
+ closeLock sync.Mutex
+ closed *closer.Closer
+ handshakeLoopsFinished sync.WaitGroup
+
+ readDeadline *deadline.Deadline
+ writeDeadline *deadline.Deadline
+
+ log logging.LeveledLogger
+
+ reading chan struct{}
+ handshakeRecv chan chan struct{}
+ cancelHandshaker func()
+ cancelHandshakeReader func()
+
+ fsm *handshakeFSM
+
+ replayProtectionWindow uint
+}
+
+func createConn(nextConn net.Conn, config *Config, isClient bool) (*Conn, error) {
+ err := validateConfig(config)
+ if err != nil {
+ return nil, err
+ }
+
+ if nextConn == nil {
+ return nil, errNilNextConn
+ }
+
+ loggerFactory := config.LoggerFactory
+ if loggerFactory == nil {
+ loggerFactory = logging.NewDefaultLoggerFactory()
+ }
+
+ logger := loggerFactory.NewLogger("dtls")
+
+ mtu := config.MTU
+ if mtu <= 0 {
+ mtu = defaultMTU
+ }
+
+ replayProtectionWindow := config.ReplayProtectionWindow
+ if replayProtectionWindow <= 0 {
+ replayProtectionWindow = defaultReplayProtectionWindow
+ }
+
+ c := &Conn{
+ nextConn: connctx.New(nextConn),
+ fragmentBuffer: newFragmentBuffer(),
+ handshakeCache: newHandshakeCache(),
+ maximumTransmissionUnit: mtu,
+
+ decrypted: make(chan interface{}, 1),
+ log: logger,
+
+ readDeadline: deadline.New(),
+ writeDeadline: deadline.New(),
+
+ reading: make(chan struct{}, 1),
+ handshakeRecv: make(chan chan struct{}),
+ closed: closer.NewCloser(),
+ cancelHandshaker: func() {},
+
+ replayProtectionWindow: uint(replayProtectionWindow),
+
+ state: State{
+ isClient: isClient,
+ },
+ }
+
+ c.setRemoteEpoch(0)
+ c.setLocalEpoch(0)
+ return c, nil
+}
+
+func handshakeConn(ctx context.Context, conn *Conn, config *Config, isClient bool, initialState *State) (*Conn, error) {
+ if conn == nil {
+ return nil, errNilNextConn
+ }
+
+ cipherSuites, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.includeCertificateSuites(), config.PSK != nil)
+ if err != nil {
+ return nil, err
+ }
+
+ signatureSchemes, err := signaturehash.ParseSignatureSchemes(config.SignatureSchemes, config.InsecureHashes)
+ if err != nil {
+ return nil, err
+ }
+
+ workerInterval := initialTickerInterval
+ if config.FlightInterval != 0 {
+ workerInterval = config.FlightInterval
+ }
+
+ serverName := config.ServerName
+ // Do not allow the use of an IP address literal as an SNI value.
+ // See RFC 6066, Section 3.
+ if net.ParseIP(serverName) != nil {
+ serverName = ""
+ }
+
+ curves := config.EllipticCurves
+ if len(curves) == 0 {
+ curves = defaultCurves
+ }
+
+ hsCfg := &handshakeConfig{
+ localPSKCallback: config.PSK,
+ localPSKIdentityHint: config.PSKIdentityHint,
+ localCipherSuites: cipherSuites,
+ localSignatureSchemes: signatureSchemes,
+ extendedMasterSecret: config.ExtendedMasterSecret,
+ localSRTPProtectionProfiles: config.SRTPProtectionProfiles,
+ serverName: serverName,
+ supportedProtocols: config.SupportedProtocols,
+ clientAuth: config.ClientAuth,
+ localCertificates: config.Certificates,
+ insecureSkipVerify: config.InsecureSkipVerify,
+ verifyPeerCertificate: config.VerifyPeerCertificate,
+ verifyConnection: config.VerifyConnection,
+ rootCAs: config.RootCAs,
+ clientCAs: config.ClientCAs,
+ customCipherSuites: config.CustomCipherSuites,
+ retransmitInterval: workerInterval,
+ log: conn.log,
+ initialEpoch: 0,
+ keyLogWriter: config.KeyLogWriter,
+ sessionStore: config.SessionStore,
+ ellipticCurves: curves,
+ localGetCertificate: config.GetCertificate,
+ localGetClientCertificate: config.GetClientCertificate,
+ insecureSkipHelloVerify: config.InsecureSkipVerifyHello,
+ }
+
+ // rfc5246#section-7.4.3
+ // In addition, the hash and signature algorithms MUST be compatible
+ // with the key in the server's end-entity certificate.
+ if !isClient {
+ cert, err := hsCfg.getCertificate(&ClientHelloInfo{})
+ if err != nil && !errors.Is(err, errNoCertificates) {
+ return nil, err
+ }
+ hsCfg.localCipherSuites = filterCipherSuitesForCertificate(cert, cipherSuites)
+ }
+
+ var initialFlight flightVal
+ var initialFSMState handshakeState
+
+ if initialState != nil {
+ if conn.state.isClient {
+ initialFlight = flight5
+ } else {
+ initialFlight = flight6
+ }
+ initialFSMState = handshakeFinished
+
+ conn.state = *initialState
+ } else {
+ if conn.state.isClient {
+ initialFlight = flight1
+ } else {
+ initialFlight = flight0
+ }
+ initialFSMState = handshakePreparing
+ }
+ // Do handshake
+ if err := conn.handshake(ctx, hsCfg, initialFlight, initialFSMState); err != nil {
+ return nil, err
+ }
+
+ conn.log.Trace("Handshake Completed")
+
+ return conn, nil
+}
+
+// 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) {
+ ctx, cancel := config.connectContextMaker()
+ defer cancel()
+
+ 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) {
+ ctx, cancel := config.connectContextMaker()
+ defer cancel()
+
+ return ClientWithContext(ctx, conn, 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) {
+ ctx, cancel := config.connectContextMaker()
+ defer cancel()
+
+ return ServerWithContext(ctx, conn, 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)
+ if err != nil {
+ return nil, err
+ }
+ return ClientWithContext(ctx, pConn, config)
+}
+
+// ClientWithContext establishes a DTLS connection over an existing connection.
+func ClientWithContext(ctx context.Context, conn net.Conn, config *Config) (*Conn, error) {
+ switch {
+ case config == nil:
+ return nil, errNoConfigProvided
+ case config.PSK != nil && config.PSKIdentityHint == nil:
+ return nil, errPSKAndIdentityMustBeSetForClient
+ }
+
+ dconn, err := createConn(conn, config, true)
+ if err != nil {
+ return nil, err
+ }
+
+ return handshakeConn(ctx, dconn, config, true, nil)
+}
+
+// ServerWithContext listens for incoming DTLS connections.
+func ServerWithContext(ctx context.Context, conn net.Conn, config *Config) (*Conn, error) {
+ if config == nil {
+ return nil, errNoConfigProvided
+ }
+ dconn, err := createConn(conn, config, false)
+ if err != nil {
+ return nil, err
+ }
+ return handshakeConn(ctx, dconn, config, false, nil)
+}
+
+// Read reads data from the connection.
+func (c *Conn) Read(p []byte) (n int, err error) {
+ if !c.isHandshakeCompletedSuccessfully() {
+ return 0, errHandshakeInProgress
+ }
+
+ select {
+ case <-c.readDeadline.Done():
+ return 0, errDeadlineExceeded
+ default:
+ }
+
+ for {
+ select {
+ case <-c.readDeadline.Done():
+ return 0, errDeadlineExceeded
+ case out, ok := <-c.decrypted:
+ if !ok {
+ return 0, io.EOF
+ }
+ switch val := out.(type) {
+ case ([]byte):
+ if len(p) < len(val) {
+ return 0, errBufferTooSmall
+ }
+ copy(p, val)
+ return len(val), nil
+ case (error):
+ return 0, val
+ }
+ }
+ }
+}
+
+// Write writes len(p) bytes from p to the DTLS connection
+func (c *Conn) Write(p []byte) (int, error) {
+ if c.isConnectionClosed() {
+ return 0, ErrConnClosed
+ }
+
+ select {
+ case <-c.writeDeadline.Done():
+ return 0, errDeadlineExceeded
+ default:
+ }
+
+ if !c.isHandshakeCompletedSuccessfully() {
+ return 0, errHandshakeInProgress
+ }
+
+ return len(p), c.writePackets(c.writeDeadline, []*packet{
+ {
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Epoch: c.state.getLocalEpoch(),
+ Version: protocol.Version1_2,
+ },
+ Content: &protocol.ApplicationData{
+ Data: p,
+ },
+ },
+ shouldEncrypt: true,
+ },
+ })
+}
+
+// Close closes the connection.
+func (c *Conn) Close() error {
+ err := c.close(true) //nolint:contextcheck
+ c.handshakeLoopsFinished.Wait()
+ return err
+}
+
+// ConnectionState returns basic DTLS details about the connection.
+// Note that this replaced the `Export` function of v1.
+func (c *Conn) ConnectionState() State {
+ c.lock.RLock()
+ defer c.lock.RUnlock()
+ return *c.state.clone()
+}
+
+// SelectedSRTPProtectionProfile returns the selected SRTPProtectionProfile
+func (c *Conn) SelectedSRTPProtectionProfile() (SRTPProtectionProfile, bool) {
+ profile := c.state.getSRTPProtectionProfile()
+ if profile == 0 {
+ return 0, false
+ }
+
+ return profile, true
+}
+
+func (c *Conn) writePackets(ctx context.Context, pkts []*packet) error {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ var rawPackets [][]byte
+
+ for _, p := range pkts {
+ if h, ok := p.record.Content.(*handshake.Handshake); ok {
+ handshakeRaw, err := p.record.Marshal()
+ if err != nil {
+ return err
+ }
+
+ 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)
+
+ rawHandshakePackets, err := c.processHandshakePacket(p, h)
+ if err != nil {
+ return err
+ }
+ rawPackets = append(rawPackets, rawHandshakePackets...)
+ } else {
+ rawPacket, err := c.processPacket(p)
+ if err != nil {
+ return err
+ }
+ rawPackets = append(rawPackets, rawPacket)
+ }
+ }
+ if len(rawPackets) == 0 {
+ return nil
+ }
+ compactedRawPackets := c.compactRawPackets(rawPackets)
+
+ for _, compactedRawPackets := range compactedRawPackets {
+ if _, err := c.nextConn.WriteContext(ctx, compactedRawPackets); err != nil {
+ return netError(err)
+ }
+ }
+
+ return nil
+}
+
+func (c *Conn) compactRawPackets(rawPackets [][]byte) [][]byte {
+ // avoid a useless copy in the common case
+ if len(rawPackets) == 1 {
+ return rawPackets
+ }
+
+ combinedRawPackets := make([][]byte, 0)
+ currentCombinedRawPacket := make([]byte, 0)
+
+ for _, rawPacket := range rawPackets {
+ if len(currentCombinedRawPacket) > 0 && len(currentCombinedRawPacket)+len(rawPacket) >= c.maximumTransmissionUnit {
+ combinedRawPackets = append(combinedRawPackets, currentCombinedRawPacket)
+ currentCombinedRawPacket = []byte{}
+ }
+ currentCombinedRawPacket = append(currentCombinedRawPacket, rawPacket...)
+ }
+
+ combinedRawPackets = append(combinedRawPackets, currentCombinedRawPacket)
+
+ return combinedRawPackets
+}
+
+func (c *Conn) processPacket(p *packet) ([]byte, error) {
+ epoch := p.record.Header.Epoch
+ for len(c.state.localSequenceNumber) <= int(epoch) {
+ c.state.localSequenceNumber = append(c.state.localSequenceNumber, uint64(0))
+ }
+ seq := atomic.AddUint64(&c.state.localSequenceNumber[epoch], 1) - 1
+ if seq > recordlayer.MaxSequenceNumber {
+ // RFC 6347 Section 4.1.0
+ // The implementation must either abandon an association or rehandshake
+ // prior to allowing the sequence number to wrap.
+ return nil, errSequenceNumberOverflow
+ }
+ p.record.Header.SequenceNumber = seq
+
+ rawPacket, err := p.record.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ if p.shouldEncrypt {
+ var err error
+ rawPacket, err = c.state.cipherSuite.Encrypt(p.record, rawPacket)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return rawPacket, nil
+}
+
+func (c *Conn) processHandshakePacket(p *packet, h *handshake.Handshake) ([][]byte, error) {
+ rawPackets := make([][]byte, 0)
+
+ handshakeFragments, err := c.fragmentHandshake(h)
+ if err != nil {
+ return nil, err
+ }
+ epoch := p.record.Header.Epoch
+ for len(c.state.localSequenceNumber) <= int(epoch) {
+ c.state.localSequenceNumber = append(c.state.localSequenceNumber, uint64(0))
+ }
+
+ for _, handshakeFragment := range handshakeFragments {
+ seq := atomic.AddUint64(&c.state.localSequenceNumber[epoch], 1) - 1
+ if seq > recordlayer.MaxSequenceNumber {
+ 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,
+ }
+
+ rawPacket, err := recordlayerHeader.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ p.record.Header = *recordlayerHeader
+
+ rawPacket = append(rawPacket, handshakeFragment...)
+ if p.shouldEncrypt {
+ var err error
+ rawPacket, err = c.state.cipherSuite.Encrypt(p.record, rawPacket)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ rawPackets = append(rawPackets, rawPacket)
+ }
+
+ return rawPackets, nil
+}
+
+func (c *Conn) fragmentHandshake(h *handshake.Handshake) ([][]byte, error) {
+ content, err := h.Message.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ fragmentedHandshakes := make([][]byte, 0)
+
+ contentFragments := splitBytes(content, c.maximumTransmissionUnit)
+ if len(contentFragments) == 0 {
+ contentFragments = [][]byte{
+ {},
+ }
+ }
+
+ offset := 0
+ for _, contentFragment := range contentFragments {
+ contentFragmentLen := len(contentFragment)
+
+ headerFragment := &handshake.Header{
+ Type: h.Header.Type,
+ Length: h.Header.Length,
+ MessageSequence: h.Header.MessageSequence,
+ FragmentOffset: uint32(offset),
+ FragmentLength: uint32(contentFragmentLen),
+ }
+
+ offset += contentFragmentLen
+
+ fragmentedHandshake, err := headerFragment.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ fragmentedHandshake = append(fragmentedHandshake, contentFragment...)
+ fragmentedHandshakes = append(fragmentedHandshakes, fragmentedHandshake)
+ }
+
+ return fragmentedHandshakes, nil
+}
+
+var poolReadBuffer = sync.Pool{ //nolint:gochecknoglobals
+ New: func() interface{} {
+ b := make([]byte, inboundBufferSize)
+ return &b
+ },
+}
+
+func (c *Conn) readAndBuffer(ctx context.Context) error {
+ bufptr, ok := poolReadBuffer.Get().(*[]byte)
+ if !ok {
+ return errFailedToAccessPoolReadBuffer
+ }
+ defer poolReadBuffer.Put(bufptr)
+
+ b := *bufptr
+ i, err := c.nextConn.ReadContext(ctx, b)
+ if err != nil {
+ return netError(err)
+ }
+
+ pkts, err := recordlayer.UnpackDatagram(b[:i])
+ if err != nil {
+ return err
+ }
+
+ var hasHandshake bool
+ for _, p := range pkts {
+ hs, alert, err := c.handleIncomingPacket(ctx, p, true)
+ if alert != nil {
+ if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
+ if err == nil {
+ err = alertErr
+ }
+ }
+ }
+ if hs {
+ hasHandshake = true
+ }
+
+ if err != nil {
+ return err
+ }
+ }
+ if hasHandshake {
+ done := make(chan struct{})
+ select {
+ case c.handshakeRecv <- done:
+ // If the other party may retransmit the flight,
+ // we should respond even if it not a new message.
+ <-done
+ case <-c.fsm.Done():
+ }
+ }
+ return nil
+}
+
+func (c *Conn) handleQueuedPackets(ctx context.Context) error {
+ pkts := c.encryptedPackets
+ c.encryptedPackets = nil
+
+ for _, p := range pkts {
+ _, alert, err := c.handleIncomingPacket(ctx, p, false) // don't re-enqueue
+ if alert != nil {
+ if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
+ if err == nil {
+ err = alertErr
+ }
+ }
+ }
+ var e *alertError
+ if errors.As(err, &e) {
+ if e.IsFatalOrCloseNotify() {
+ return e
+ }
+ } else if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (c *Conn) enqueueEncryptedPackets(packet []byte) bool {
+ if len(c.encryptedPackets) < maxAppDataPacketQueueSize {
+ c.encryptedPackets = append(c.encryptedPackets, packet)
+ return true
+ }
+ return false
+}
+
+func (c *Conn) handleIncomingPacket(ctx context.Context, buf []byte, enqueue bool) (bool, *alert.Alert, error) { //nolint:gocognit
+ h := &recordlayer.Header{}
+ if err := h.Unmarshal(buf); err != nil {
+ // Decode error must be silently discarded
+ // [RFC6347 Section-4.1.2.7]
+ c.log.Debugf("discarded broken packet: %v", err)
+ return false, nil, nil
+ }
+ // Validate epoch
+ remoteEpoch := c.state.getRemoteEpoch()
+ if h.Epoch > remoteEpoch {
+ if h.Epoch > remoteEpoch+1 {
+ c.log.Debugf("discarded future packet (epoch: %d, seq: %d)",
+ h.Epoch, h.SequenceNumber,
+ )
+ return false, nil, nil
+ }
+ if enqueue {
+ if ok := c.enqueueEncryptedPackets(buf); ok {
+ c.log.Debug("received packet of next epoch, queuing packet")
+ }
+ }
+ return false, nil, nil
+ }
+
+ // Anti-replay protection
+ for len(c.state.replayDetector) <= int(h.Epoch) {
+ c.state.replayDetector = append(c.state.replayDetector,
+ replaydetector.New(c.replayProtectionWindow, recordlayer.MaxSequenceNumber),
+ )
+ }
+ markPacketAsValid, ok := c.state.replayDetector[int(h.Epoch)].Check(h.SequenceNumber)
+ if !ok {
+ c.log.Debugf("discarded duplicated packet (epoch: %d, seq: %d)",
+ h.Epoch, h.SequenceNumber,
+ )
+ return false, nil, nil
+ }
+
+ // Decrypt
+ if h.Epoch != 0 {
+ if c.state.cipherSuite == nil || !c.state.cipherSuite.IsInitialized() {
+ if enqueue {
+ if ok := c.enqueueEncryptedPackets(buf); ok {
+ c.log.Debug("handshake not finished, queuing packet")
+ }
+ }
+ return false, nil, nil
+ }
+
+ var err error
+ buf, err = c.state.cipherSuite.Decrypt(buf)
+ if err != nil {
+ c.log.Debugf("%s: decrypt failed: %s", srvCliStr(c.state.isClient), err)
+ return false, nil, nil
+ }
+ }
+
+ isHandshake, err := c.fragmentBuffer.push(append([]byte{}, buf...))
+ if err != nil {
+ // Decode error must be silently discarded
+ // [RFC6347 Section-4.1.2.7]
+ c.log.Debugf("defragment failed: %s", err)
+ return false, nil, nil
+ } else if isHandshake {
+ markPacketAsValid()
+ for out, epoch := c.fragmentBuffer.pop(); out != nil; out, epoch = c.fragmentBuffer.pop() {
+ header := &handshake.Header{}
+ if err := header.Unmarshal(out); err != nil {
+ c.log.Debugf("%s: handshake parse failed: %s", srvCliStr(c.state.isClient), err)
+ continue
+ }
+ c.handshakeCache.push(out, epoch, header.MessageSequence, header.Type, !c.state.isClient)
+ }
+
+ return true, nil, nil
+ }
+
+ r := &recordlayer.RecordLayer{}
+ if err := r.Unmarshal(buf); err != nil {
+ return false, &alert.Alert{Level: alert.Fatal, Description: alert.DecodeError}, err
+ }
+
+ switch content := r.Content.(type) {
+ case *alert.Alert:
+ c.log.Tracef("%s: <- %s", srvCliStr(c.state.isClient), content.String())
+ var a *alert.Alert
+ if content.Description == alert.CloseNotify {
+ // Respond with a close_notify [RFC5246 Section 7.2.1]
+ a = &alert.Alert{Level: alert.Warning, Description: alert.CloseNotify}
+ }
+ markPacketAsValid()
+ return false, a, &alertError{content}
+ case *protocol.ChangeCipherSpec:
+ if c.state.cipherSuite == nil || !c.state.cipherSuite.IsInitialized() {
+ if enqueue {
+ if ok := c.enqueueEncryptedPackets(buf); ok {
+ c.log.Debugf("CipherSuite not initialized, queuing packet")
+ }
+ }
+ return false, nil, nil
+ }
+
+ newRemoteEpoch := h.Epoch + 1
+ c.log.Tracef("%s: <- ChangeCipherSpec (epoch: %d)", srvCliStr(c.state.isClient), newRemoteEpoch)
+
+ if c.state.getRemoteEpoch()+1 == newRemoteEpoch {
+ c.setRemoteEpoch(newRemoteEpoch)
+ markPacketAsValid()
+ }
+ case *protocol.ApplicationData:
+ if h.Epoch == 0 {
+ return false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, errApplicationDataEpochZero
+ }
+
+ markPacketAsValid()
+
+ select {
+ case c.decrypted <- content.Data:
+ case <-c.closed.Done():
+ case <-ctx.Done():
+ }
+
+ default:
+ return false, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, fmt.Errorf("%w: %d", errUnhandledContextType, content.ContentType())
+ }
+ return false, nil, nil
+}
+
+func (c *Conn) recvHandshake() <-chan chan struct{} {
+ return c.handshakeRecv
+}
+
+func (c *Conn) notify(ctx context.Context, level alert.Level, desc alert.Description) error {
+ if level == alert.Fatal && len(c.state.SessionID) > 0 {
+ // According to the RFC, we need to delete the stored session.
+ // https://datatracker.ietf.org/doc/html/rfc5246#section-7.2
+ if ss := c.fsm.cfg.sessionStore; ss != nil {
+ c.log.Tracef("clean invalid session: %s", c.state.SessionID)
+ if err := ss.Del(c.sessionKey()); err != nil {
+ return err
+ }
+ }
+ }
+ return c.writePackets(ctx, []*packet{
+ {
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Epoch: c.state.getLocalEpoch(),
+ Version: protocol.Version1_2,
+ },
+ Content: &alert.Alert{
+ Level: level,
+ Description: desc,
+ },
+ },
+ shouldEncrypt: c.isHandshakeCompletedSuccessfully(),
+ },
+ })
+}
+
+func (c *Conn) setHandshakeCompletedSuccessfully() {
+ c.handshakeCompletedSuccessfully.Store(struct{ bool }{true})
+}
+
+func (c *Conn) isHandshakeCompletedSuccessfully() bool {
+ boolean, _ := c.handshakeCompletedSuccessfully.Load().(struct{ bool })
+ return boolean.bool
+}
+
+func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFlight flightVal, initialState handshakeState) error { //nolint:gocognit
+ c.fsm = newHandshakeFSM(&c.state, c.handshakeCache, cfg, initialFlight)
+
+ done := make(chan struct{})
+ ctxRead, cancelRead := context.WithCancel(context.Background())
+ c.cancelHandshakeReader = cancelRead
+ cfg.onFlightState = func(f flightVal, s handshakeState) {
+ if s == handshakeFinished && !c.isHandshakeCompletedSuccessfully() {
+ c.setHandshakeCompletedSuccessfully()
+ close(done)
+ }
+ }
+
+ ctxHs, cancel := context.WithCancel(context.Background())
+ c.cancelHandshaker = cancel
+
+ firstErr := make(chan error, 1)
+
+ c.handshakeLoopsFinished.Add(2)
+
+ // Handshake routine should be live until close.
+ // The other party may request retransmission of the last flight to cope with packet drop.
+ go func() {
+ defer c.handshakeLoopsFinished.Done()
+ err := c.fsm.Run(ctxHs, c, initialState)
+ if !errors.Is(err, context.Canceled) {
+ select {
+ case firstErr <- err:
+ default:
+ }
+ }
+ }()
+ go func() {
+ defer func() {
+ // Escaping read loop.
+ // It's safe to close decrypted channnel now.
+ close(c.decrypted)
+
+ // Force stop handshaker when the underlying connection is closed.
+ cancel()
+ }()
+ defer c.handshakeLoopsFinished.Done()
+ for {
+ if err := c.readAndBuffer(ctxRead); err != nil {
+ var e *alertError
+ if errors.As(err, &e) {
+ if !e.IsFatalOrCloseNotify() {
+ if c.isHandshakeCompletedSuccessfully() {
+ // Pass the error to Read()
+ select {
+ case c.decrypted <- err:
+ case <-c.closed.Done():
+ case <-ctxRead.Done():
+ }
+ }
+ continue // non-fatal alert must not stop read loop
+ }
+ } else {
+ switch {
+ case errors.Is(err, context.DeadlineExceeded), errors.Is(err, context.Canceled), errors.Is(err, io.EOF), errors.Is(err, net.ErrClosed):
+ case errors.Is(err, recordlayer.ErrInvalidPacketLength):
+ // Decode error must be silently discarded
+ // [RFC6347 Section-4.1.2.7]
+ continue
+ default:
+ if c.isHandshakeCompletedSuccessfully() {
+ // Keep read loop and pass the read error to Read()
+ select {
+ case c.decrypted <- err:
+ case <-c.closed.Done():
+ case <-ctxRead.Done():
+ }
+ continue // non-fatal alert must not stop read loop
+ }
+ }
+ }
+
+ select {
+ case firstErr <- err:
+ default:
+ }
+
+ if e != nil {
+ if e.IsFatalOrCloseNotify() {
+ _ = c.close(false) //nolint:contextcheck
+ }
+ }
+ if !c.isConnectionClosed() && errors.Is(err, context.Canceled) {
+ c.log.Trace("handshake timeouts - closing underline connection")
+ _ = c.close(false) //nolint:contextcheck
+ }
+ return
+ }
+ }
+ }()
+
+ select {
+ case err := <-firstErr:
+ cancelRead()
+ cancel()
+ c.handshakeLoopsFinished.Wait()
+ return c.translateHandshakeCtxError(err)
+ case <-ctx.Done():
+ cancelRead()
+ cancel()
+ c.handshakeLoopsFinished.Wait()
+ return c.translateHandshakeCtxError(ctx.Err())
+ case <-done:
+ return nil
+ }
+}
+
+func (c *Conn) translateHandshakeCtxError(err error) error {
+ if err == nil {
+ return nil
+ }
+ if errors.Is(err, context.Canceled) && c.isHandshakeCompletedSuccessfully() {
+ return nil
+ }
+ return &HandshakeError{Err: err}
+}
+
+func (c *Conn) close(byUser bool) error {
+ c.cancelHandshaker()
+ c.cancelHandshakeReader()
+
+ if c.isHandshakeCompletedSuccessfully() && byUser {
+ // Discard error from notify() to return non-error on the first user call of Close()
+ // even if the underlying connection is already closed.
+ _ = c.notify(context.Background(), alert.Warning, alert.CloseNotify)
+ }
+
+ c.closeLock.Lock()
+ // Don't return ErrConnClosed at the first time of the call from user.
+ closedByUser := c.connectionClosedByUser
+ if byUser {
+ c.connectionClosedByUser = true
+ }
+ isClosed := c.isConnectionClosed()
+ c.closed.Close()
+ c.closeLock.Unlock()
+
+ if closedByUser {
+ return ErrConnClosed
+ }
+
+ if isClosed {
+ return nil
+ }
+
+ return c.nextConn.Close()
+}
+
+func (c *Conn) isConnectionClosed() bool {
+ select {
+ case <-c.closed.Done():
+ return true
+ default:
+ return false
+ }
+}
+
+func (c *Conn) setLocalEpoch(epoch uint16) {
+ c.state.localEpoch.Store(epoch)
+}
+
+func (c *Conn) setRemoteEpoch(epoch uint16) {
+ c.state.remoteEpoch.Store(epoch)
+}
+
+// LocalAddr implements net.Conn.LocalAddr
+func (c *Conn) LocalAddr() net.Addr {
+ return c.nextConn.LocalAddr()
+}
+
+// RemoteAddr implements net.Conn.RemoteAddr
+func (c *Conn) RemoteAddr() net.Addr {
+ return c.nextConn.RemoteAddr()
+}
+
+func (c *Conn) sessionKey() []byte {
+ if c.state.isClient {
+ // 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 c.state.SessionID
+}
+
+// SetDeadline implements net.Conn.SetDeadline
+func (c *Conn) SetDeadline(t time.Time) error {
+ c.readDeadline.Set(t)
+ return c.SetWriteDeadline(t)
+}
+
+// SetReadDeadline implements net.Conn.SetReadDeadline
+func (c *Conn) SetReadDeadline(t time.Time) error {
+ c.readDeadline.Set(t)
+ // Read deadline is fully managed by this layer.
+ // Don't set read deadline to underlying connection.
+ return nil
+}
+
+// SetWriteDeadline implements net.Conn.SetWriteDeadline
+func (c *Conn) SetWriteDeadline(t time.Time) error {
+ c.writeDeadline.Set(t)
+ // Write deadline is also fully managed by this layer.
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/crypto.go b/server/vendor/github.com/pion/dtls/v2/crypto.go
new file mode 100644
index 0000000..968910c
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/crypto.go
@@ -0,0 +1,228 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/sha256"
+ "crypto/x509"
+ "encoding/asn1"
+ "encoding/binary"
+ "math/big"
+ "time"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/crypto/hash"
+)
+
+type ecdsaSignature struct {
+ R, S *big.Int
+}
+
+func valueKeyMessage(clientRandom, serverRandom, publicKey []byte, namedCurve elliptic.Curve) []byte {
+ serverECDHParams := make([]byte, 4)
+ serverECDHParams[0] = 3 // named curve
+ binary.BigEndian.PutUint16(serverECDHParams[1:], uint16(namedCurve))
+ serverECDHParams[3] = byte(len(publicKey))
+
+ plaintext := []byte{}
+ plaintext = append(plaintext, clientRandom...)
+ plaintext = append(plaintext, serverRandom...)
+ plaintext = append(plaintext, serverECDHParams...)
+ plaintext = append(plaintext, publicKey...)
+
+ return plaintext
+}
+
+// If the client provided a "signature_algorithms" extension, then all
+// certificates provided by the server MUST be signed by a
+// hash/signature algorithm pair that appears in that extension
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.2
+func generateKeySignature(clientRandom, serverRandom, publicKey []byte, namedCurve elliptic.Curve, privateKey crypto.PrivateKey, hashAlgorithm hash.Algorithm) ([]byte, error) {
+ msg := valueKeyMessage(clientRandom, serverRandom, publicKey, namedCurve)
+ switch p := privateKey.(type) {
+ case ed25519.PrivateKey:
+ // https://crypto.stackexchange.com/a/55483
+ return p.Sign(rand.Reader, msg, crypto.Hash(0))
+ case *ecdsa.PrivateKey:
+ hashed := hashAlgorithm.Digest(msg)
+ return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
+ case *rsa.PrivateKey:
+ hashed := hashAlgorithm.Digest(msg)
+ return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
+ }
+
+ return nil, errKeySignatureGenerateUnimplemented
+}
+
+func verifyKeySignature(message, remoteKeySignature []byte, hashAlgorithm hash.Algorithm, rawCertificates [][]byte) error { //nolint:dupl
+ if len(rawCertificates) == 0 {
+ return errLengthMismatch
+ }
+ certificate, err := x509.ParseCertificate(rawCertificates[0])
+ if err != nil {
+ return err
+ }
+
+ switch p := certificate.PublicKey.(type) {
+ case ed25519.PublicKey:
+ if ok := ed25519.Verify(p, message, remoteKeySignature); !ok {
+ return errKeySignatureMismatch
+ }
+ return nil
+ case *ecdsa.PublicKey:
+ ecdsaSig := &ecdsaSignature{}
+ if _, err := asn1.Unmarshal(remoteKeySignature, ecdsaSig); err != nil {
+ return err
+ }
+ if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
+ return errInvalidECDSASignature
+ }
+ hashed := hashAlgorithm.Digest(message)
+ if !ecdsa.Verify(p, hashed, ecdsaSig.R, ecdsaSig.S) {
+ return errKeySignatureMismatch
+ }
+ return nil
+ case *rsa.PublicKey:
+ switch certificate.SignatureAlgorithm {
+ case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
+ hashed := hashAlgorithm.Digest(message)
+ return rsa.VerifyPKCS1v15(p, hashAlgorithm.CryptoHash(), hashed, remoteKeySignature)
+ default:
+ return errKeySignatureVerifyUnimplemented
+ }
+ }
+
+ return errKeySignatureVerifyUnimplemented
+}
+
+// If the server has sent a CertificateRequest message, the client MUST send the Certificate
+// message. The ClientKeyExchange message is now sent, and the content
+// of that message will depend on the public key algorithm selected
+// between the ClientHello and the ServerHello. If the client has sent
+// a certificate with signing ability, a digitally-signed
+// CertificateVerify message is sent to explicitly verify possession of
+// the private key in the certificate.
+// https://tools.ietf.org/html/rfc5246#section-7.3
+func generateCertificateVerify(handshakeBodies []byte, privateKey crypto.PrivateKey, hashAlgorithm hash.Algorithm) ([]byte, error) {
+ if p, ok := privateKey.(ed25519.PrivateKey); ok {
+ // https://pkg.go.dev/crypto/ed25519#PrivateKey.Sign
+ // Sign signs the given message with priv. Ed25519 performs two passes over
+ // messages to be signed and therefore cannot handle pre-hashed messages.
+ return p.Sign(rand.Reader, handshakeBodies, crypto.Hash(0))
+ }
+
+ h := sha256.New()
+ if _, err := h.Write(handshakeBodies); err != nil {
+ return nil, err
+ }
+ hashed := h.Sum(nil)
+
+ switch p := privateKey.(type) {
+ case *ecdsa.PrivateKey:
+ return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
+ case *rsa.PrivateKey:
+ return p.Sign(rand.Reader, hashed, hashAlgorithm.CryptoHash())
+ }
+
+ return nil, errInvalidSignatureAlgorithm
+}
+
+func verifyCertificateVerify(handshakeBodies []byte, hashAlgorithm hash.Algorithm, remoteKeySignature []byte, rawCertificates [][]byte) error { //nolint:dupl
+ if len(rawCertificates) == 0 {
+ return errLengthMismatch
+ }
+ certificate, err := x509.ParseCertificate(rawCertificates[0])
+ if err != nil {
+ return err
+ }
+
+ switch p := certificate.PublicKey.(type) {
+ case ed25519.PublicKey:
+ if ok := ed25519.Verify(p, handshakeBodies, remoteKeySignature); !ok {
+ return errKeySignatureMismatch
+ }
+ return nil
+ case *ecdsa.PublicKey:
+ ecdsaSig := &ecdsaSignature{}
+ if _, err := asn1.Unmarshal(remoteKeySignature, ecdsaSig); err != nil {
+ return err
+ }
+ if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
+ return errInvalidECDSASignature
+ }
+ hash := hashAlgorithm.Digest(handshakeBodies)
+ if !ecdsa.Verify(p, hash, ecdsaSig.R, ecdsaSig.S) {
+ return errKeySignatureMismatch
+ }
+ return nil
+ case *rsa.PublicKey:
+ switch certificate.SignatureAlgorithm {
+ case x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
+ hash := hashAlgorithm.Digest(handshakeBodies)
+ return rsa.VerifyPKCS1v15(p, hashAlgorithm.CryptoHash(), hash, remoteKeySignature)
+ default:
+ return errKeySignatureVerifyUnimplemented
+ }
+ }
+
+ return errKeySignatureVerifyUnimplemented
+}
+
+func loadCerts(rawCertificates [][]byte) ([]*x509.Certificate, error) {
+ if len(rawCertificates) == 0 {
+ return nil, errLengthMismatch
+ }
+
+ certs := make([]*x509.Certificate, 0, len(rawCertificates))
+ for _, rawCert := range rawCertificates {
+ cert, err := x509.ParseCertificate(rawCert)
+ if err != nil {
+ return nil, err
+ }
+ certs = append(certs, cert)
+ }
+ return certs, nil
+}
+
+func verifyClientCert(rawCertificates [][]byte, roots *x509.CertPool) (chains [][]*x509.Certificate, err error) {
+ certificate, err := loadCerts(rawCertificates)
+ if err != nil {
+ return nil, err
+ }
+ intermediateCAPool := x509.NewCertPool()
+ for _, cert := range certificate[1:] {
+ intermediateCAPool.AddCert(cert)
+ }
+ opts := x509.VerifyOptions{
+ Roots: roots,
+ CurrentTime: time.Now(),
+ Intermediates: intermediateCAPool,
+ KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
+ }
+ return certificate[0].Verify(opts)
+}
+
+func verifyServerCert(rawCertificates [][]byte, roots *x509.CertPool, serverName string) (chains [][]*x509.Certificate, err error) {
+ certificate, err := loadCerts(rawCertificates)
+ if err != nil {
+ return nil, err
+ }
+ intermediateCAPool := x509.NewCertPool()
+ for _, cert := range certificate[1:] {
+ intermediateCAPool.AddCert(cert)
+ }
+ opts := x509.VerifyOptions{
+ Roots: roots,
+ CurrentTime: time.Now(),
+ DNSName: serverName,
+ Intermediates: intermediateCAPool,
+ }
+ return certificate[0].Verify(opts)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/dtls.go b/server/vendor/github.com/pion/dtls/v2/dtls.go
new file mode 100644
index 0000000..b799770
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/dtls.go
@@ -0,0 +1,5 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package dtls implements Datagram Transport Layer Security (DTLS) 1.2
+package dtls
diff --git a/server/vendor/github.com/pion/dtls/v2/errors.go b/server/vendor/github.com/pion/dtls/v2/errors.go
new file mode 100644
index 0000000..025d864
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/errors.go
@@ -0,0 +1,157 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "os"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+)
+
+// Typed errors
+var (
+ ErrConnClosed = &FatalError{Err: errors.New("conn is closed")} //nolint:goerr113
+
+ errDeadlineExceeded = &TimeoutError{Err: fmt.Errorf("read/write timeout: %w", context.DeadlineExceeded)}
+ errInvalidContentType = &TemporaryError{Err: errors.New("invalid content type")} //nolint:goerr113
+
+ errBufferTooSmall = &TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
+ errContextUnsupported = &TemporaryError{Err: errors.New("context is not supported for ExportKeyingMaterial")} //nolint:goerr113
+ errHandshakeInProgress = &TemporaryError{Err: errors.New("handshake is in progress")} //nolint:goerr113
+ errReservedExportKeyingMaterial = &TemporaryError{Err: errors.New("ExportKeyingMaterial can not be used with a reserved label")} //nolint:goerr113
+ errApplicationDataEpochZero = &TemporaryError{Err: errors.New("ApplicationData with epoch of 0")} //nolint:goerr113
+ errUnhandledContextType = &TemporaryError{Err: errors.New("unhandled contentType")} //nolint:goerr113
+
+ errCertificateVerifyNoCertificate = &FatalError{Err: errors.New("client sent certificate verify but we have no certificate to verify")} //nolint:goerr113
+ errCipherSuiteNoIntersection = &FatalError{Err: errors.New("client+server do not support any shared cipher suites")} //nolint:goerr113
+ errClientCertificateNotVerified = &FatalError{Err: errors.New("client sent certificate but did not verify it")} //nolint:goerr113
+ errClientCertificateRequired = &FatalError{Err: errors.New("server required client verification, but got none")} //nolint:goerr113
+ errClientNoMatchingSRTPProfile = &FatalError{Err: errors.New("server responded with SRTP Profile we do not support")} //nolint:goerr113
+ errClientRequiredButNoServerEMS = &FatalError{Err: errors.New("client required Extended Master Secret extension, but server does not support it")} //nolint:goerr113
+ errCookieMismatch = &FatalError{Err: errors.New("client+server cookie does not match")} //nolint:goerr113
+ errIdentityNoPSK = &FatalError{Err: errors.New("PSK Identity Hint provided but PSK is nil")} //nolint:goerr113
+ errInvalidCertificate = &FatalError{Err: errors.New("no certificate provided")} //nolint:goerr113
+ errInvalidCipherSuite = &FatalError{Err: errors.New("invalid or unknown cipher suite")} //nolint:goerr113
+ errInvalidECDSASignature = &FatalError{Err: errors.New("ECDSA signature contained zero or negative values")} //nolint:goerr113
+ errInvalidPrivateKey = &FatalError{Err: errors.New("invalid private key type")} //nolint:goerr113
+ errInvalidSignatureAlgorithm = &FatalError{Err: errors.New("invalid signature algorithm")} //nolint:goerr113
+ errKeySignatureMismatch = &FatalError{Err: errors.New("expected and actual key signature do not match")} //nolint:goerr113
+ errNilNextConn = &FatalError{Err: errors.New("Conn can not be created with a nil nextConn")} //nolint:goerr113
+ errNoAvailableCipherSuites = &FatalError{Err: errors.New("connection can not be created, no CipherSuites satisfy this Config")} //nolint:goerr113
+ errNoAvailablePSKCipherSuite = &FatalError{Err: errors.New("connection can not be created, pre-shared key present but no compatible CipherSuite")} //nolint:goerr113
+ errNoAvailableCertificateCipherSuite = &FatalError{Err: errors.New("connection can not be created, certificate present but no compatible CipherSuite")} //nolint:goerr113
+ errNoAvailableSignatureSchemes = &FatalError{Err: errors.New("connection can not be created, no SignatureScheme satisfy this Config")} //nolint:goerr113
+ errNoCertificates = &FatalError{Err: errors.New("no certificates configured")} //nolint:goerr113
+ errNoConfigProvided = &FatalError{Err: errors.New("no config provided")} //nolint:goerr113
+ errNoSupportedEllipticCurves = &FatalError{Err: errors.New("client requested zero or more elliptic curves that are not supported by the server")} //nolint:goerr113
+ errUnsupportedProtocolVersion = &FatalError{Err: errors.New("unsupported protocol version")} //nolint:goerr113
+ errPSKAndIdentityMustBeSetForClient = &FatalError{Err: errors.New("PSK and PSK Identity Hint must both be set for client")} //nolint:goerr113
+ errRequestedButNoSRTPExtension = &FatalError{Err: errors.New("SRTP support was requested but server did not respond with use_srtp extension")} //nolint:goerr113
+ errServerNoMatchingSRTPProfile = &FatalError{Err: errors.New("client requested SRTP but we have no matching profiles")} //nolint:goerr113
+ errServerRequiredButNoClientEMS = &FatalError{Err: errors.New("server requires the Extended Master Secret extension, but the client does not support it")} //nolint:goerr113
+ errVerifyDataMismatch = &FatalError{Err: errors.New("expected and actual verify data does not match")} //nolint:goerr113
+ errNotAcceptableCertificateChain = &FatalError{Err: errors.New("certificate chain is not signed by an acceptable CA")} //nolint:goerr113
+
+ errInvalidFlight = &InternalError{Err: errors.New("invalid flight number")} //nolint:goerr113
+ errKeySignatureGenerateUnimplemented = &InternalError{Err: errors.New("unable to generate key signature, unimplemented")} //nolint:goerr113
+ errKeySignatureVerifyUnimplemented = &InternalError{Err: errors.New("unable to verify key signature, unimplemented")} //nolint:goerr113
+ errLengthMismatch = &InternalError{Err: errors.New("data length and declared length do not match")} //nolint:goerr113
+ errSequenceNumberOverflow = &InternalError{Err: errors.New("sequence number overflow")} //nolint:goerr113
+ errInvalidFSMTransition = &InternalError{Err: errors.New("invalid state machine transition")} //nolint:goerr113
+ errFailedToAccessPoolReadBuffer = &InternalError{Err: errors.New("failed to access pool read buffer")} //nolint:goerr113
+ errFragmentBufferOverflow = &InternalError{Err: errors.New("fragment buffer overflow")} //nolint:goerr113
+)
+
+// FatalError indicates that the DTLS connection is no longer available.
+// It is mainly caused by wrong configuration of server or client.
+type FatalError = protocol.FatalError
+
+// InternalError indicates and internal error caused by the implementation, and the DTLS connection is no longer available.
+// It is mainly caused by bugs or tried to use unimplemented features.
+type InternalError = protocol.InternalError
+
+// TemporaryError indicates that the DTLS connection is still available, but the request was failed temporary.
+type TemporaryError = protocol.TemporaryError
+
+// TimeoutError indicates that the request was timed out.
+type TimeoutError = protocol.TimeoutError
+
+// HandshakeError indicates that the handshake failed.
+type HandshakeError = protocol.HandshakeError
+
+// errInvalidCipherSuite indicates an attempt at using an unsupported cipher suite.
+type invalidCipherSuiteError struct {
+ id CipherSuiteID
+}
+
+func (e *invalidCipherSuiteError) Error() string {
+ return fmt.Sprintf("CipherSuite with id(%d) is not valid", e.id)
+}
+
+func (e *invalidCipherSuiteError) Is(err error) bool {
+ var other *invalidCipherSuiteError
+ if errors.As(err, &other) {
+ return e.id == other.id
+ }
+ return false
+}
+
+// errAlert wraps DTLS alert notification as an error
+type alertError struct {
+ *alert.Alert
+}
+
+func (e *alertError) Error() string {
+ return fmt.Sprintf("alert: %s", e.Alert.String())
+}
+
+func (e *alertError) IsFatalOrCloseNotify() bool {
+ return e.Level == alert.Fatal || e.Description == alert.CloseNotify
+}
+
+func (e *alertError) Is(err error) bool {
+ var other *alertError
+ if errors.As(err, &other) {
+ return e.Level == other.Level && e.Description == other.Description
+ }
+ return false
+}
+
+// netError translates an error from underlying Conn to corresponding net.Error.
+func netError(err error) error {
+ switch {
+ case errors.Is(err, io.EOF), errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):
+ // Return io.EOF and context errors as is.
+ return err
+ }
+
+ var (
+ ne net.Error
+ opError *net.OpError
+ se *os.SyscallError
+ )
+
+ if errors.As(err, &opError) {
+ if errors.As(opError, &se) {
+ if se.Timeout() {
+ return &TimeoutError{Err: err}
+ }
+ if isOpErrorTemporary(se) {
+ return &TemporaryError{Err: err}
+ }
+ }
+ }
+
+ if errors.As(err, &ne) {
+ return err
+ }
+
+ return &FatalError{Err: err}
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/errors_errno.go b/server/vendor/github.com/pion/dtls/v2/errors_errno.go
new file mode 100644
index 0000000..f8e424e
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/errors_errno.go
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+//go:build aix || darwin || dragonfly || freebsd || linux || nacl || nacljs || netbsd || openbsd || solaris || windows
+// +build aix darwin dragonfly freebsd linux nacl nacljs netbsd openbsd solaris windows
+
+// For systems having syscall.Errno.
+// Update build targets by following command:
+// $ grep -R ECONN $(go env GOROOT)/src/syscall/zerrors_*.go \
+// | tr "." "_" | cut -d"_" -f"2" | sort | uniq
+
+package dtls
+
+import (
+ "errors"
+ "os"
+ "syscall"
+)
+
+func isOpErrorTemporary(err *os.SyscallError) bool {
+ return errors.Is(err.Err, syscall.ECONNREFUSED)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/errors_noerrno.go b/server/vendor/github.com/pion/dtls/v2/errors_noerrno.go
new file mode 100644
index 0000000..844ff1e
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/errors_noerrno.go
@@ -0,0 +1,18 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !nacl && !nacljs && !netbsd && !openbsd && !solaris && !windows
+// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!nacl,!nacljs,!netbsd,!openbsd,!solaris,!windows
+
+// For systems without syscall.Errno.
+// Build targets must be inverse of errors_errno.go
+
+package dtls
+
+import (
+ "os"
+)
+
+func isOpErrorTemporary(err *os.SyscallError) bool {
+ return false
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/flight.go b/server/vendor/github.com/pion/dtls/v2/flight.go
new file mode 100644
index 0000000..cfa58c5
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/flight.go
@@ -0,0 +1,104 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+/*
+ DTLS messages are grouped into a series of message flights, according
+ to the diagrams below. Although each flight of messages may consist
+ of a number of messages, they should be viewed as monolithic for the
+ purpose of timeout and retransmission.
+ https://tools.ietf.org/html/rfc4347#section-4.2.4
+
+ Message flights for full handshake:
+
+ Client Server
+ ------ ------
+ Waiting Flight 0
+
+ ClientHello --------> Flight 1
+
+ <------- HelloVerifyRequest Flight 2
+
+ ClientHello --------> Flight 3
+
+ ServerHello \
+ Certificate* \
+ ServerKeyExchange* Flight 4
+ CertificateRequest* /
+ <-------- ServerHelloDone /
+
+ Certificate* \
+ ClientKeyExchange \
+ CertificateVerify* Flight 5
+ [ChangeCipherSpec] /
+ Finished --------> /
+
+ [ChangeCipherSpec] \ Flight 6
+ <-------- Finished /
+
+ Message flights for session-resuming handshake (no cookie exchange):
+
+ Client Server
+ ------ ------
+ Waiting Flight 0
+
+ ClientHello --------> Flight 1
+
+ ServerHello \
+ [ChangeCipherSpec] Flight 4b
+ <-------- Finished /
+
+ [ChangeCipherSpec] \ Flight 5b
+ Finished --------> /
+
+ [ChangeCipherSpec] \ Flight 6
+ <-------- Finished /
+*/
+
+type flightVal uint8
+
+const (
+ flight0 flightVal = iota + 1
+ flight1
+ flight2
+ flight3
+ flight4
+ flight4b
+ flight5
+ flight5b
+ flight6
+)
+
+func (f flightVal) String() string {
+ switch f {
+ case flight0:
+ return "Flight 0"
+ case flight1:
+ return "Flight 1"
+ case flight2:
+ return "Flight 2"
+ case flight3:
+ return "Flight 3"
+ case flight4:
+ return "Flight 4"
+ case flight4b:
+ return "Flight 4b"
+ case flight5:
+ return "Flight 5"
+ case flight5b:
+ return "Flight 5b"
+ case flight6:
+ return "Flight 6"
+ default:
+ return "Invalid Flight"
+ }
+}
+
+func (f flightVal) isLastSendFlight() bool {
+ return f == flight6 || f == flight5b
+}
+
+func (f flightVal) isLastRecvFlight() bool {
+ return f == flight5 || f == flight4b
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/flight0handler.go b/server/vendor/github.com/pion/dtls/v2/flight0handler.go
new file mode 100644
index 0000000..a061995
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/flight0handler.go
@@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "context"
+ "crypto/rand"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/extension"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+)
+
+func flight0Parse(_ context.Context, _ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
+ seq, msgs, ok := cache.fullPullMap(0, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+ state.handshakeRecvSequence = seq
+
+ var clientHello *handshake.MessageClientHello
+
+ // Validate type
+ if clientHello, ok = msgs[handshake.TypeClientHello].(*handshake.MessageClientHello); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+
+ if !clientHello.Version.Equal(protocol.Version1_2) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
+ }
+
+ state.remoteRandom = clientHello.Random
+
+ cipherSuites := []CipherSuite{}
+ for _, id := range clientHello.CipherSuiteIDs {
+ if c := cipherSuiteForID(CipherSuiteID(id), cfg.customCipherSuites); c != nil {
+ cipherSuites = append(cipherSuites, c)
+ }
+ }
+
+ if state.cipherSuite, ok = findMatchingCipherSuite(cipherSuites, cfg.localCipherSuites); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errCipherSuiteNoIntersection
+ }
+
+ for _, val := range clientHello.Extensions {
+ switch e := val.(type) {
+ case *extension.SupportedEllipticCurves:
+ if len(e.EllipticCurves) == 0 {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoSupportedEllipticCurves
+ }
+ state.namedCurve = e.EllipticCurves[0]
+ case *extension.UseSRTP:
+ profile, ok := findMatchingSRTPProfile(e.ProtectionProfiles, cfg.localSRTPProtectionProfiles)
+ if !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errServerNoMatchingSRTPProfile
+ }
+ state.setSRTPProtectionProfile(profile)
+ case *extension.UseExtendedMasterSecret:
+ if cfg.extendedMasterSecret != DisableExtendedMasterSecret {
+ state.extendedMasterSecret = true
+ }
+ case *extension.ServerName:
+ state.serverName = e.ServerName // remote server name
+ case *extension.ALPN:
+ state.peerSupportedProtocols = e.ProtocolNameList
+ }
+ }
+
+ if cfg.extendedMasterSecret == RequireExtendedMasterSecret && !state.extendedMasterSecret {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errServerRequiredButNoClientEMS
+ }
+
+ if state.localKeypair == nil {
+ var err error
+ state.localKeypair, err = elliptic.GenerateKeypair(state.namedCurve)
+ if err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err
+ }
+ }
+
+ nextFlight := flight2
+
+ if cfg.insecureSkipHelloVerify {
+ nextFlight = flight4
+ }
+
+ return handleHelloResume(clientHello.SessionID, state, cfg, nextFlight)
+}
+
+func handleHelloResume(sessionID []byte, state *State, cfg *handshakeConfig, next flightVal) (flightVal, *alert.Alert, error) {
+ if len(sessionID) > 0 && cfg.sessionStore != nil {
+ if s, err := cfg.sessionStore.Get(sessionID); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ } else if s.ID != nil {
+ cfg.log.Tracef("[handshake] resume session: %x", sessionID)
+
+ state.SessionID = sessionID
+ state.masterSecret = s.Secret
+
+ if err := state.initCipherSuite(); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+
+ clientRandom := state.localRandom.MarshalFixed()
+ cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret)
+
+ return flight4b, nil, nil
+ }
+ }
+ return next, nil, nil
+}
+
+func flight0Generate(_ flightConn, state *State, _ *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
+ // Initialize
+ if !cfg.insecureSkipHelloVerify {
+ state.cookie = make([]byte, cookieLength)
+ if _, err := rand.Read(state.cookie); err != nil {
+ return nil, nil, err
+ }
+ }
+
+ var zeroEpoch uint16
+ state.localEpoch.Store(zeroEpoch)
+ state.remoteEpoch.Store(zeroEpoch)
+ state.namedCurve = defaultNamedCurve
+
+ if err := state.localRandom.Populate(); err != nil {
+ return nil, nil, err
+ }
+
+ return nil, nil, nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/flight1handler.go b/server/vendor/github.com/pion/dtls/v2/flight1handler.go
new file mode 100644
index 0000000..94fdc22
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/flight1handler.go
@@ -0,0 +1,141 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "context"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/extension"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+func flight1Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
+ // HelloVerifyRequest can be skipped by the server,
+ // so allow ServerHello during flight1 also
+ seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeHelloVerifyRequest, cfg.initialEpoch, false, true},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, true},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+
+ if _, ok := msgs[handshake.TypeServerHello]; ok {
+ // Flight1 and flight2 were skipped.
+ // Parse as flight3.
+ return flight3Parse(ctx, c, state, cache, cfg)
+ }
+
+ if h, ok := msgs[handshake.TypeHelloVerifyRequest].(*handshake.MessageHelloVerifyRequest); ok {
+ // DTLS 1.2 clients must not assume that the server will use the protocol version
+ // specified in HelloVerifyRequest message. RFC 6347 Section 4.2.1
+ if !h.Version.Equal(protocol.Version1_0) && !h.Version.Equal(protocol.Version1_2) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
+ }
+ state.cookie = append([]byte{}, h.Cookie...)
+ state.handshakeRecvSequence = seq
+ return flight3, nil, nil
+ }
+
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+}
+
+func flight1Generate(c flightConn, state *State, _ *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
+ var zeroEpoch uint16
+ state.localEpoch.Store(zeroEpoch)
+ state.remoteEpoch.Store(zeroEpoch)
+ state.namedCurve = defaultNamedCurve
+ state.cookie = nil
+
+ if err := state.localRandom.Populate(); err != nil {
+ return nil, nil, err
+ }
+
+ extensions := []extension.Extension{
+ &extension.SupportedSignatureAlgorithms{
+ SignatureHashAlgorithms: cfg.localSignatureSchemes,
+ },
+ &extension.RenegotiationInfo{
+ RenegotiatedConnection: 0,
+ },
+ }
+
+ var setEllipticCurveCryptographyClientHelloExtensions bool
+ for _, c := range cfg.localCipherSuites {
+ if c.ECC() {
+ setEllipticCurveCryptographyClientHelloExtensions = true
+ break
+ }
+ }
+
+ if setEllipticCurveCryptographyClientHelloExtensions {
+ extensions = append(extensions, []extension.Extension{
+ &extension.SupportedEllipticCurves{
+ EllipticCurves: cfg.ellipticCurves,
+ },
+ &extension.SupportedPointFormats{
+ PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed},
+ },
+ }...)
+ }
+
+ if len(cfg.localSRTPProtectionProfiles) > 0 {
+ extensions = append(extensions, &extension.UseSRTP{
+ ProtectionProfiles: cfg.localSRTPProtectionProfiles,
+ })
+ }
+
+ if cfg.extendedMasterSecret == RequestExtendedMasterSecret ||
+ cfg.extendedMasterSecret == RequireExtendedMasterSecret {
+ extensions = append(extensions, &extension.UseExtendedMasterSecret{
+ Supported: true,
+ })
+ }
+
+ if len(cfg.serverName) > 0 {
+ extensions = append(extensions, &extension.ServerName{ServerName: cfg.serverName})
+ }
+
+ if len(cfg.supportedProtocols) > 0 {
+ extensions = append(extensions, &extension.ALPN{ProtocolNameList: cfg.supportedProtocols})
+ }
+
+ if cfg.sessionStore != nil {
+ cfg.log.Tracef("[handshake] try to resume session")
+ if s, err := cfg.sessionStore.Get(c.sessionKey()); err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ } else if s.ID != nil {
+ cfg.log.Tracef("[handshake] get saved session: %x", s.ID)
+
+ state.SessionID = s.ID
+ state.masterSecret = s.Secret
+ }
+ }
+
+ return []*packet{
+ {
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageClientHello{
+ Version: protocol.Version1_2,
+ SessionID: state.SessionID,
+ Cookie: state.cookie,
+ Random: state.localRandom,
+ CipherSuiteIDs: cipherSuiteIDs(cfg.localCipherSuites),
+ CompressionMethods: defaultCompressionMethods(),
+ Extensions: extensions,
+ },
+ },
+ },
+ },
+ }, nil, nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/flight2handler.go b/server/vendor/github.com/pion/dtls/v2/flight2handler.go
new file mode 100644
index 0000000..26e57d2
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/flight2handler.go
@@ -0,0 +1,64 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "bytes"
+ "context"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+func flight2Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
+ seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ )
+ if !ok {
+ // Client may retransmit the first ClientHello when HelloVerifyRequest is dropped.
+ // Parse as flight 0 in this case.
+ return flight0Parse(ctx, c, state, cache, cfg)
+ }
+ state.handshakeRecvSequence = seq
+
+ var clientHello *handshake.MessageClientHello
+
+ // Validate type
+ if clientHello, ok = msgs[handshake.TypeClientHello].(*handshake.MessageClientHello); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+
+ if !clientHello.Version.Equal(protocol.Version1_2) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
+ }
+
+ if len(clientHello.Cookie) == 0 {
+ return 0, nil, nil
+ }
+ if !bytes.Equal(state.cookie, clientHello.Cookie) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.AccessDenied}, errCookieMismatch
+ }
+ return flight4, nil, nil
+}
+
+func flight2Generate(_ flightConn, state *State, _ *handshakeCache, _ *handshakeConfig) ([]*packet, *alert.Alert, error) {
+ state.handshakeSendSequence = 0
+ return []*packet{
+ {
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageHelloVerifyRequest{
+ Version: protocol.Version1_2,
+ Cookie: state.cookie,
+ },
+ },
+ },
+ },
+ }, nil, nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/flight3handler.go b/server/vendor/github.com/pion/dtls/v2/flight3handler.go
new file mode 100644
index 0000000..ab4e730
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/flight3handler.go
@@ -0,0 +1,291 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "bytes"
+ "context"
+
+ "github.com/pion/dtls/v2/internal/ciphersuite/types"
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/extension"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+func flight3Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { //nolint:gocognit
+ // Clients may receive multiple HelloVerifyRequest messages with different cookies.
+ // Clients SHOULD handle this by sending a new ClientHello with a cookie in response
+ // to the new HelloVerifyRequest. RFC 6347 Section 4.2.1
+ seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeHelloVerifyRequest, cfg.initialEpoch, false, true},
+ )
+ if ok {
+ if h, msgOk := msgs[handshake.TypeHelloVerifyRequest].(*handshake.MessageHelloVerifyRequest); msgOk {
+ // DTLS 1.2 clients must not assume that the server will use the protocol version
+ // specified in HelloVerifyRequest message. RFC 6347 Section 4.2.1
+ if !h.Version.Equal(protocol.Version1_0) && !h.Version.Equal(protocol.Version1_2) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
+ }
+ state.cookie = append([]byte{}, h.Cookie...)
+ state.handshakeRecvSequence = seq
+ return flight3, nil, nil
+ }
+ }
+
+ _, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ )
+ if !ok {
+ // Don't have enough messages. Keep reading
+ return 0, nil, nil
+ }
+
+ if h, msgOk := msgs[handshake.TypeServerHello].(*handshake.MessageServerHello); msgOk {
+ if !h.Version.Equal(protocol.Version1_2) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
+ }
+ for _, v := range h.Extensions {
+ switch e := v.(type) {
+ case *extension.UseSRTP:
+ profile, found := findMatchingSRTPProfile(e.ProtectionProfiles, cfg.localSRTPProtectionProfiles)
+ if !found {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, errClientNoMatchingSRTPProfile
+ }
+ state.setSRTPProtectionProfile(profile)
+ case *extension.UseExtendedMasterSecret:
+ if cfg.extendedMasterSecret != DisableExtendedMasterSecret {
+ state.extendedMasterSecret = true
+ }
+ case *extension.ALPN:
+ if len(e.ProtocolNameList) > 1 { // This should be exactly 1, the zero case is handle when unmarshalling
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, extension.ErrALPNInvalidFormat // Meh, internal error?
+ }
+ state.NegotiatedProtocol = e.ProtocolNameList[0]
+ }
+ }
+ if cfg.extendedMasterSecret == RequireExtendedMasterSecret && !state.extendedMasterSecret {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errClientRequiredButNoServerEMS
+ }
+ if len(cfg.localSRTPProtectionProfiles) > 0 && state.getSRTPProtectionProfile() == 0 {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errRequestedButNoSRTPExtension
+ }
+
+ remoteCipherSuite := cipherSuiteForID(CipherSuiteID(*h.CipherSuiteID), cfg.customCipherSuites)
+ if remoteCipherSuite == nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errCipherSuiteNoIntersection
+ }
+
+ selectedCipherSuite, found := findMatchingCipherSuite([]CipherSuite{remoteCipherSuite}, cfg.localCipherSuites)
+ if !found {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
+ }
+
+ state.cipherSuite = selectedCipherSuite
+ state.remoteRandom = h.Random
+ cfg.log.Tracef("[handshake] use cipher suite: %s", selectedCipherSuite.String())
+
+ if len(h.SessionID) > 0 && bytes.Equal(state.SessionID, h.SessionID) {
+ return handleResumption(ctx, c, state, cache, cfg)
+ }
+
+ if len(state.SessionID) > 0 {
+ cfg.log.Tracef("[handshake] clean old session : %s", state.SessionID)
+ if err := cfg.sessionStore.Del(state.SessionID); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ if cfg.sessionStore == nil {
+ state.SessionID = []byte{}
+ } else {
+ state.SessionID = h.SessionID
+ }
+
+ state.masterSecret = []byte{}
+ }
+
+ if cfg.localPSKCallback != nil {
+ seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, true},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
+ )
+ } else {
+ seq, msgs, ok = cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, true},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, true},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
+ )
+ }
+ if !ok {
+ // Don't have enough messages. Keep reading
+ return 0, nil, nil
+ }
+ state.handshakeRecvSequence = seq
+
+ if h, ok := msgs[handshake.TypeCertificate].(*handshake.MessageCertificate); ok {
+ state.PeerCertificates = h.Certificate
+ } else if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errInvalidCertificate
+ }
+
+ if h, ok := msgs[handshake.TypeServerKeyExchange].(*handshake.MessageServerKeyExchange); ok {
+ alertPtr, err := handleServerKeyExchange(c, state, cfg, h)
+ if err != nil {
+ return 0, alertPtr, err
+ }
+ }
+
+ if _, ok := msgs[handshake.TypeCertificateRequest].(*handshake.MessageCertificateRequest); ok {
+ state.remoteRequestedCertificate = true
+ }
+
+ return flight5, nil, nil
+}
+
+func handleResumption(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
+ if err := state.initCipherSuite(); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+
+ // Now, encrypted packets can be handled
+ if err := c.handleQueuedPackets(ctx); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+
+ _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence+1, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+
+ var finished *handshake.MessageFinished
+ if finished, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+ plainText := cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ )
+
+ expectedVerifyData, err := prf.VerifyDataServer(state.masterSecret, plainText, state.cipherSuite.HashFunc())
+ if err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ if !bytes.Equal(expectedVerifyData, finished.VerifyData) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errVerifyDataMismatch
+ }
+
+ clientRandom := state.localRandom.MarshalFixed()
+ cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret)
+
+ return flight5b, nil, nil
+}
+
+func handleServerKeyExchange(_ flightConn, state *State, cfg *handshakeConfig, h *handshake.MessageServerKeyExchange) (*alert.Alert, error) {
+ var err error
+ if state.cipherSuite == nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
+ }
+ if cfg.localPSKCallback != nil {
+ var psk []byte
+ if psk, err = cfg.localPSKCallback(h.IdentityHint); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ state.IdentityHint = h.IdentityHint
+ switch state.cipherSuite.KeyExchangeAlgorithm() {
+ case types.KeyExchangeAlgorithmPsk:
+ state.preMasterSecret = prf.PSKPreMasterSecret(psk)
+ case (types.KeyExchangeAlgorithmEcdhe | types.KeyExchangeAlgorithmPsk):
+ if state.localKeypair, err = elliptic.GenerateKeypair(h.NamedCurve); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ state.preMasterSecret, err = prf.EcdhePSKPreMasterSecret(psk, h.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve)
+ if err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ default:
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
+ }
+ } else {
+ if state.localKeypair, err = elliptic.GenerateKeypair(h.NamedCurve); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+
+ if state.preMasterSecret, err = prf.PreMasterSecret(h.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ return nil, nil //nolint:nilnil
+}
+
+func flight3Generate(_ flightConn, state *State, _ *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
+ extensions := []extension.Extension{
+ &extension.SupportedSignatureAlgorithms{
+ SignatureHashAlgorithms: cfg.localSignatureSchemes,
+ },
+ &extension.RenegotiationInfo{
+ RenegotiatedConnection: 0,
+ },
+ }
+ if state.namedCurve != 0 {
+ extensions = append(extensions, []extension.Extension{
+ &extension.SupportedEllipticCurves{
+ EllipticCurves: []elliptic.Curve{elliptic.X25519, elliptic.P256, elliptic.P384},
+ },
+ &extension.SupportedPointFormats{
+ PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed},
+ },
+ }...)
+ }
+
+ if len(cfg.localSRTPProtectionProfiles) > 0 {
+ extensions = append(extensions, &extension.UseSRTP{
+ ProtectionProfiles: cfg.localSRTPProtectionProfiles,
+ })
+ }
+
+ if cfg.extendedMasterSecret == RequestExtendedMasterSecret ||
+ cfg.extendedMasterSecret == RequireExtendedMasterSecret {
+ extensions = append(extensions, &extension.UseExtendedMasterSecret{
+ Supported: true,
+ })
+ }
+
+ if len(cfg.serverName) > 0 {
+ extensions = append(extensions, &extension.ServerName{ServerName: cfg.serverName})
+ }
+
+ if len(cfg.supportedProtocols) > 0 {
+ extensions = append(extensions, &extension.ALPN{ProtocolNameList: cfg.supportedProtocols})
+ }
+
+ return []*packet{
+ {
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageClientHello{
+ Version: protocol.Version1_2,
+ SessionID: state.SessionID,
+ Cookie: state.cookie,
+ Random: state.localRandom,
+ CipherSuiteIDs: cipherSuiteIDs(cfg.localCipherSuites),
+ CompressionMethods: defaultCompressionMethods(),
+ Extensions: extensions,
+ },
+ },
+ },
+ },
+ }, nil, nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/flight4bhandler.go b/server/vendor/github.com/pion/dtls/v2/flight4bhandler.go
new file mode 100644
index 0000000..6b1b904
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/flight4bhandler.go
@@ -0,0 +1,144 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "bytes"
+ "context"
+
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/extension"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+func flight4bParse(_ context.Context, _ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
+ _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+
+ var finished *handshake.MessageFinished
+ if finished, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+
+ plainText := cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false},
+ )
+
+ expectedVerifyData, err := prf.VerifyDataClient(state.masterSecret, plainText, state.cipherSuite.HashFunc())
+ if err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ if !bytes.Equal(expectedVerifyData, finished.VerifyData) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errVerifyDataMismatch
+ }
+
+ // Other party may re-transmit the last flight. Keep state to be flight4b.
+ return flight4b, nil, nil
+}
+
+func flight4bGenerate(_ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
+ var pkts []*packet
+
+ extensions := []extension.Extension{&extension.RenegotiationInfo{
+ RenegotiatedConnection: 0,
+ }}
+ if (cfg.extendedMasterSecret == RequestExtendedMasterSecret ||
+ cfg.extendedMasterSecret == RequireExtendedMasterSecret) && state.extendedMasterSecret {
+ extensions = append(extensions, &extension.UseExtendedMasterSecret{
+ Supported: true,
+ })
+ }
+ if state.getSRTPProtectionProfile() != 0 {
+ extensions = append(extensions, &extension.UseSRTP{
+ ProtectionProfiles: []SRTPProtectionProfile{state.getSRTPProtectionProfile()},
+ })
+ }
+
+ selectedProto, err := extension.ALPNProtocolSelection(cfg.supportedProtocols, state.peerSupportedProtocols)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.NoApplicationProtocol}, err
+ }
+ if selectedProto != "" {
+ extensions = append(extensions, &extension.ALPN{
+ ProtocolNameList: []string{selectedProto},
+ })
+ state.NegotiatedProtocol = selectedProto
+ }
+
+ cipherSuiteID := uint16(state.cipherSuite.ID())
+ serverHello := &handshake.Handshake{
+ Message: &handshake.MessageServerHello{
+ Version: protocol.Version1_2,
+ Random: state.localRandom,
+ SessionID: state.SessionID,
+ CipherSuiteID: &cipherSuiteID,
+ CompressionMethod: defaultCompressionMethods()[0],
+ Extensions: extensions,
+ },
+ }
+
+ serverHello.Header.MessageSequence = uint16(state.handshakeSendSequence)
+
+ if len(state.localVerifyData) == 0 {
+ plainText := cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ )
+ raw, err := serverHello.Marshal()
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ plainText = append(plainText, raw...)
+
+ state.localVerifyData, err = prf.VerifyDataServer(state.masterSecret, plainText, state.cipherSuite.HashFunc())
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: serverHello,
+ },
+ },
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &protocol.ChangeCipherSpec{},
+ },
+ },
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ Epoch: 1,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageFinished{
+ VerifyData: state.localVerifyData,
+ },
+ },
+ },
+ shouldEncrypt: true,
+ resetLocalSequenceNumber: true,
+ },
+ )
+
+ return pkts, nil, nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/flight4handler.go b/server/vendor/github.com/pion/dtls/v2/flight4handler.go
new file mode 100644
index 0000000..404f7d1
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/flight4handler.go
@@ -0,0 +1,402 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "context"
+ "crypto/rand"
+ "crypto/x509"
+
+ "github.com/pion/dtls/v2/internal/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/crypto/signaturehash"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/extension"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+func flight4Parse(ctx context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) { //nolint:gocognit
+ seq, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, true},
+ handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, true},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+
+ // Validate type
+ var clientKeyExchange *handshake.MessageClientKeyExchange
+ if clientKeyExchange, ok = msgs[handshake.TypeClientKeyExchange].(*handshake.MessageClientKeyExchange); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+
+ if h, hasCert := msgs[handshake.TypeCertificate].(*handshake.MessageCertificate); hasCert {
+ state.PeerCertificates = h.Certificate
+ // If the client offer its certificate, just disable session resumption.
+ // Otherwise, we have to store the certificate identitfication and expire time.
+ // And we have to check whether this certificate expired, revoked or changed.
+ //
+ // https://curl.se/docs/CVE-2016-5419.html
+ state.SessionID = nil
+ }
+
+ if h, hasCertVerify := msgs[handshake.TypeCertificateVerify].(*handshake.MessageCertificateVerify); hasCertVerify {
+ if state.PeerCertificates == nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errCertificateVerifyNoCertificate
+ }
+
+ plainText := cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
+ )
+
+ // Verify that the pair of hash algorithm and signiture is listed.
+ var validSignatureScheme bool
+ for _, ss := range cfg.localSignatureSchemes {
+ if ss.Hash == h.HashAlgorithm && ss.Signature == h.SignatureAlgorithm {
+ validSignatureScheme = true
+ break
+ }
+ }
+ if !validSignatureScheme {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoAvailableSignatureSchemes
+ }
+
+ if err := verifyCertificateVerify(plainText, h.HashAlgorithm, h.Signature, state.PeerCertificates); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ var chains [][]*x509.Certificate
+ var err error
+ var verified bool
+ if cfg.clientAuth >= VerifyClientCertIfGiven {
+ if chains, err = verifyClientCert(state.PeerCertificates, cfg.clientCAs); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ verified = true
+ }
+ if cfg.verifyPeerCertificate != nil {
+ if err := cfg.verifyPeerCertificate(state.PeerCertificates, chains); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ }
+ state.peerCertificatesVerified = verified
+ } else if state.PeerCertificates != nil {
+ // A certificate was received, but we haven't seen a CertificateVerify
+ // keep reading until we receive one
+ return 0, nil, nil
+ }
+
+ if !state.cipherSuite.IsInitialized() {
+ serverRandom := state.localRandom.MarshalFixed()
+ clientRandom := state.remoteRandom.MarshalFixed()
+
+ var err error
+ var preMasterSecret []byte
+ if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypePreSharedKey {
+ var psk []byte
+ if psk, err = cfg.localPSKCallback(clientKeyExchange.IdentityHint); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ state.IdentityHint = clientKeyExchange.IdentityHint
+ switch state.cipherSuite.KeyExchangeAlgorithm() {
+ case CipherSuiteKeyExchangeAlgorithmPsk:
+ preMasterSecret = prf.PSKPreMasterSecret(psk)
+ case (CipherSuiteKeyExchangeAlgorithmPsk | CipherSuiteKeyExchangeAlgorithmEcdhe):
+ if preMasterSecret, err = prf.EcdhePSKPreMasterSecret(psk, clientKeyExchange.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ default:
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidCipherSuite
+ }
+ } else {
+ preMasterSecret, err = prf.PreMasterSecret(clientKeyExchange.PublicKey, state.localKeypair.PrivateKey, state.localKeypair.Curve)
+ if err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err
+ }
+ }
+
+ if state.extendedMasterSecret {
+ var sessionHash []byte
+ sessionHash, err = cache.sessionHash(state.cipherSuite.HashFunc(), cfg.initialEpoch)
+ if err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+
+ state.masterSecret, err = prf.ExtendedMasterSecret(preMasterSecret, sessionHash, state.cipherSuite.HashFunc())
+ if err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ } else {
+ state.masterSecret, err = prf.MasterSecret(preMasterSecret, clientRandom[:], serverRandom[:], state.cipherSuite.HashFunc())
+ if err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ if err := state.cipherSuite.Init(state.masterSecret, clientRandom[:], serverRandom[:], false); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret)
+ }
+
+ if len(state.SessionID) > 0 {
+ s := Session{
+ ID: state.SessionID,
+ Secret: state.masterSecret,
+ }
+ cfg.log.Tracef("[handshake] save new session: %x", s.ID)
+ if err := cfg.sessionStore.Set(state.SessionID, s); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ // Now, encrypted packets can be handled
+ if err := c.handleQueuedPackets(ctx); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+
+ seq, msgs, ok = cache.fullPullMap(seq, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+ state.handshakeRecvSequence = seq
+
+ if _, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+
+ if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeAnonymous {
+ if cfg.verifyConnection != nil {
+ if err := cfg.verifyConnection(state.clone()); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ }
+ return flight6, nil, nil
+ }
+
+ switch cfg.clientAuth {
+ case RequireAnyClientCert:
+ if state.PeerCertificates == nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errClientCertificateRequired
+ }
+ case VerifyClientCertIfGiven:
+ if state.PeerCertificates != nil && !state.peerCertificatesVerified {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, errClientCertificateNotVerified
+ }
+ case RequireAndVerifyClientCert:
+ if state.PeerCertificates == nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errClientCertificateRequired
+ }
+ if !state.peerCertificatesVerified {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, errClientCertificateNotVerified
+ }
+ case NoClientCert, RequestClientCert:
+ // go to flight6
+ }
+ if cfg.verifyConnection != nil {
+ if err := cfg.verifyConnection(state.clone()); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ }
+
+ return flight6, nil, nil
+}
+
+func flight4Generate(_ flightConn, state *State, _ *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
+ extensions := []extension.Extension{&extension.RenegotiationInfo{
+ RenegotiatedConnection: 0,
+ }}
+ if (cfg.extendedMasterSecret == RequestExtendedMasterSecret ||
+ cfg.extendedMasterSecret == RequireExtendedMasterSecret) && state.extendedMasterSecret {
+ extensions = append(extensions, &extension.UseExtendedMasterSecret{
+ Supported: true,
+ })
+ }
+ if state.getSRTPProtectionProfile() != 0 {
+ extensions = append(extensions, &extension.UseSRTP{
+ ProtectionProfiles: []SRTPProtectionProfile{state.getSRTPProtectionProfile()},
+ })
+ }
+ if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate {
+ extensions = append(extensions, &extension.SupportedPointFormats{
+ PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed},
+ })
+ }
+
+ selectedProto, err := extension.ALPNProtocolSelection(cfg.supportedProtocols, state.peerSupportedProtocols)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.NoApplicationProtocol}, err
+ }
+ if selectedProto != "" {
+ extensions = append(extensions, &extension.ALPN{
+ ProtocolNameList: []string{selectedProto},
+ })
+ state.NegotiatedProtocol = selectedProto
+ }
+
+ var pkts []*packet
+ cipherSuiteID := uint16(state.cipherSuite.ID())
+
+ if cfg.sessionStore != nil {
+ state.SessionID = make([]byte, sessionLength)
+ if _, err := rand.Read(state.SessionID); err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ pkts = append(pkts, &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageServerHello{
+ Version: protocol.Version1_2,
+ Random: state.localRandom,
+ SessionID: state.SessionID,
+ CipherSuiteID: &cipherSuiteID,
+ CompressionMethod: defaultCompressionMethods()[0],
+ Extensions: extensions,
+ },
+ },
+ },
+ })
+
+ switch {
+ case state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate:
+ certificate, err := cfg.getCertificate(&ClientHelloInfo{
+ ServerName: state.serverName,
+ CipherSuites: []ciphersuite.ID{state.cipherSuite.ID()},
+ })
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, err
+ }
+
+ pkts = append(pkts, &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageCertificate{
+ Certificate: certificate.Certificate,
+ },
+ },
+ },
+ })
+
+ serverRandom := state.localRandom.MarshalFixed()
+ clientRandom := state.remoteRandom.MarshalFixed()
+
+ // Find compatible signature scheme
+ signatureHashAlgo, err := signaturehash.SelectSignatureScheme(cfg.localSignatureSchemes, certificate.PrivateKey)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, err
+ }
+
+ signature, err := generateKeySignature(clientRandom[:], serverRandom[:], state.localKeypair.PublicKey, state.namedCurve, certificate.PrivateKey, signatureHashAlgo.Hash)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ state.localKeySignature = signature
+
+ pkts = append(pkts, &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageServerKeyExchange{
+ EllipticCurveType: elliptic.CurveTypeNamedCurve,
+ NamedCurve: state.namedCurve,
+ PublicKey: state.localKeypair.PublicKey,
+ HashAlgorithm: signatureHashAlgo.Hash,
+ SignatureAlgorithm: signatureHashAlgo.Signature,
+ Signature: state.localKeySignature,
+ },
+ },
+ },
+ })
+
+ if cfg.clientAuth > NoClientCert {
+ // An empty list of certificateAuthorities signals to
+ // the client that it may send any certificate in response
+ // to our request. When we know the CAs we trust, then
+ // we can send them down, so that the client can choose
+ // an appropriate certificate to give to us.
+ var certificateAuthorities [][]byte
+ if cfg.clientCAs != nil {
+ // nolint:staticcheck // ignoring tlsCert.RootCAs.Subjects is deprecated ERR because cert does not come from SystemCertPool and it's ok if certificate authorities is empty.
+ certificateAuthorities = cfg.clientCAs.Subjects()
+ }
+ pkts = append(pkts, &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageCertificateRequest{
+ CertificateTypes: []clientcertificate.Type{clientcertificate.RSASign, clientcertificate.ECDSASign},
+ SignatureHashAlgorithms: cfg.localSignatureSchemes,
+ CertificateAuthoritiesNames: certificateAuthorities,
+ },
+ },
+ },
+ })
+ }
+ case cfg.localPSKIdentityHint != nil || state.cipherSuite.KeyExchangeAlgorithm().Has(CipherSuiteKeyExchangeAlgorithmEcdhe):
+ // To help the client in selecting which identity to use, the server
+ // can provide a "PSK identity hint" in the ServerKeyExchange message.
+ // If no hint is provided and cipher suite doesn't use elliptic curve,
+ // the ServerKeyExchange message is omitted.
+ //
+ // https://tools.ietf.org/html/rfc4279#section-2
+ srvExchange := &handshake.MessageServerKeyExchange{
+ IdentityHint: cfg.localPSKIdentityHint,
+ }
+ if state.cipherSuite.KeyExchangeAlgorithm().Has(CipherSuiteKeyExchangeAlgorithmEcdhe) {
+ srvExchange.EllipticCurveType = elliptic.CurveTypeNamedCurve
+ srvExchange.NamedCurve = state.namedCurve
+ srvExchange.PublicKey = state.localKeypair.PublicKey
+ }
+ pkts = append(pkts, &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: srvExchange,
+ },
+ },
+ })
+ }
+
+ pkts = append(pkts, &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageServerHelloDone{},
+ },
+ },
+ })
+
+ return pkts, nil, nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/flight5bhandler.go b/server/vendor/github.com/pion/dtls/v2/flight5bhandler.go
new file mode 100644
index 0000000..ddd3732
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/flight5bhandler.go
@@ -0,0 +1,78 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "context"
+
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+func flight5bParse(_ context.Context, _ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
+ _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+
+ if _, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+
+ // Other party may re-transmit the last flight. Keep state to be flight5b.
+ return flight5b, nil, nil
+}
+
+func flight5bGenerate(_ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { //nolint:gocognit
+ var pkts []*packet
+
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &protocol.ChangeCipherSpec{},
+ },
+ })
+
+ if len(state.localVerifyData) == 0 {
+ plainText := cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false},
+ )
+
+ var err error
+ state.localVerifyData, err = prf.VerifyDataClient(state.masterSecret, plainText, state.cipherSuite.HashFunc())
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ Epoch: 1,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageFinished{
+ VerifyData: state.localVerifyData,
+ },
+ },
+ },
+ shouldEncrypt: true,
+ resetLocalSequenceNumber: true,
+ })
+
+ return pkts, nil, nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/flight5handler.go b/server/vendor/github.com/pion/dtls/v2/flight5handler.go
new file mode 100644
index 0000000..e8adf4f
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/flight5handler.go
@@ -0,0 +1,357 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "bytes"
+ "context"
+ "crypto"
+ "crypto/x509"
+
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/crypto/signaturehash"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+func flight5Parse(_ context.Context, c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
+ _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, false, false},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+
+ var finished *handshake.MessageFinished
+ if finished, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+ plainText := cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
+ )
+
+ expectedVerifyData, err := prf.VerifyDataServer(state.masterSecret, plainText, state.cipherSuite.HashFunc())
+ if err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ if !bytes.Equal(expectedVerifyData, finished.VerifyData) {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errVerifyDataMismatch
+ }
+
+ if len(state.SessionID) > 0 {
+ s := Session{
+ ID: state.SessionID,
+ Secret: state.masterSecret,
+ }
+ cfg.log.Tracef("[handshake] save new session: %x", s.ID)
+ if err := cfg.sessionStore.Set(c.sessionKey(), s); err != nil {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ return flight5, nil, nil
+}
+
+func flight5Generate(c flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) { //nolint:gocognit
+ var privateKey crypto.PrivateKey
+ var pkts []*packet
+ if state.remoteRequestedCertificate {
+ _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-2, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false})
+ if !ok {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errClientCertificateRequired
+ }
+ reqInfo := CertificateRequestInfo{}
+ if r, ok := msgs[handshake.TypeCertificateRequest].(*handshake.MessageCertificateRequest); ok {
+ reqInfo.AcceptableCAs = r.CertificateAuthoritiesNames
+ } else {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errClientCertificateRequired
+ }
+ certificate, err := cfg.getClientCertificate(&reqInfo)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, err
+ }
+ if certificate == nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errNotAcceptableCertificateChain
+ }
+ if certificate.Certificate != nil {
+ privateKey = certificate.PrivateKey
+ }
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageCertificate{
+ Certificate: certificate.Certificate,
+ },
+ },
+ },
+ })
+ }
+
+ clientKeyExchange := &handshake.MessageClientKeyExchange{}
+ if cfg.localPSKCallback == nil {
+ clientKeyExchange.PublicKey = state.localKeypair.PublicKey
+ } else {
+ clientKeyExchange.IdentityHint = cfg.localPSKIdentityHint
+ }
+ if state != nil && state.localKeypair != nil && len(state.localKeypair.PublicKey) > 0 {
+ clientKeyExchange.PublicKey = state.localKeypair.PublicKey
+ }
+
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: clientKeyExchange,
+ },
+ },
+ })
+
+ serverKeyExchangeData := cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
+ )
+
+ serverKeyExchange := &handshake.MessageServerKeyExchange{}
+
+ // handshakeMessageServerKeyExchange is optional for PSK
+ if len(serverKeyExchangeData) == 0 {
+ alertPtr, err := handleServerKeyExchange(c, state, cfg, &handshake.MessageServerKeyExchange{})
+ if err != nil {
+ return nil, alertPtr, err
+ }
+ } else {
+ rawHandshake := &handshake.Handshake{
+ KeyExchangeAlgorithm: state.cipherSuite.KeyExchangeAlgorithm(),
+ }
+ err := rawHandshake.Unmarshal(serverKeyExchangeData)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, err
+ }
+
+ switch h := rawHandshake.Message.(type) {
+ case *handshake.MessageServerKeyExchange:
+ serverKeyExchange = h
+ default:
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, errInvalidContentType
+ }
+ }
+
+ // Append not-yet-sent packets
+ merged := []byte{}
+ seqPred := uint16(state.handshakeSendSequence)
+ for _, p := range pkts {
+ h, ok := p.record.Content.(*handshake.Handshake)
+ if !ok {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidContentType
+ }
+ h.Header.MessageSequence = seqPred
+ seqPred++
+ raw, err := h.Marshal()
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ merged = append(merged, raw...)
+ }
+
+ if alertPtr, err := initalizeCipherSuite(state, cache, cfg, serverKeyExchange, merged); err != nil {
+ return nil, alertPtr, err
+ }
+
+ // If the client has sent a certificate with signing ability, a digitally-signed
+ // CertificateVerify message is sent to explicitly verify possession of the
+ // private key in the certificate.
+ if state.remoteRequestedCertificate && privateKey != nil {
+ plainText := append(cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
+ ), merged...)
+
+ // Find compatible signature scheme
+ signatureHashAlgo, err := signaturehash.SelectSignatureScheme(cfg.localSignatureSchemes, privateKey)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, err
+ }
+
+ certVerify, err := generateCertificateVerify(plainText, privateKey, signatureHashAlgo.Hash)
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ state.localCertificatesVerify = certVerify
+
+ p := &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageCertificateVerify{
+ HashAlgorithm: signatureHashAlgo.Hash,
+ SignatureAlgorithm: signatureHashAlgo.Signature,
+ Signature: state.localCertificatesVerify,
+ },
+ },
+ },
+ }
+ pkts = append(pkts, p)
+
+ h, ok := p.record.Content.(*handshake.Handshake)
+ if !ok {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidContentType
+ }
+ h.Header.MessageSequence = seqPred
+ // seqPred++ // this is the last use of seqPred
+ raw, err := h.Marshal()
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ merged = append(merged, raw...)
+ }
+
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &protocol.ChangeCipherSpec{},
+ },
+ })
+
+ if len(state.localVerifyData) == 0 {
+ plainText := cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
+ )
+
+ var err error
+ state.localVerifyData, err = prf.VerifyDataClient(state.masterSecret, append(plainText, merged...), state.cipherSuite.HashFunc())
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ Epoch: 1,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageFinished{
+ VerifyData: state.localVerifyData,
+ },
+ },
+ },
+ shouldEncrypt: true,
+ resetLocalSequenceNumber: true,
+ })
+
+ return pkts, nil, nil
+}
+
+func initalizeCipherSuite(state *State, cache *handshakeCache, cfg *handshakeConfig, h *handshake.MessageServerKeyExchange, sendingPlainText []byte) (*alert.Alert, error) { //nolint:gocognit
+ if state.cipherSuite.IsInitialized() {
+ return nil, nil //nolint
+ }
+
+ clientRandom := state.localRandom.MarshalFixed()
+ serverRandom := state.remoteRandom.MarshalFixed()
+
+ var err error
+
+ if state.extendedMasterSecret {
+ var sessionHash []byte
+ sessionHash, err = cache.sessionHash(state.cipherSuite.HashFunc(), cfg.initialEpoch, sendingPlainText)
+ if err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+
+ state.masterSecret, err = prf.ExtendedMasterSecret(state.preMasterSecret, sessionHash, state.cipherSuite.HashFunc())
+ if err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, err
+ }
+ } else {
+ state.masterSecret, err = prf.MasterSecret(state.preMasterSecret, clientRandom[:], serverRandom[:], state.cipherSuite.HashFunc())
+ if err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ if state.cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate {
+ // Verify that the pair of hash algorithm and signiture is listed.
+ var validSignatureScheme bool
+ for _, ss := range cfg.localSignatureSchemes {
+ if ss.Hash == h.HashAlgorithm && ss.Signature == h.SignatureAlgorithm {
+ validSignatureScheme = true
+ break
+ }
+ }
+ if !validSignatureScheme {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoAvailableSignatureSchemes
+ }
+
+ expectedMsg := valueKeyMessage(clientRandom[:], serverRandom[:], h.PublicKey, h.NamedCurve)
+ if err = verifyKeySignature(expectedMsg, h.Signature, h.HashAlgorithm, state.PeerCertificates); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ var chains [][]*x509.Certificate
+ if !cfg.insecureSkipVerify {
+ if chains, err = verifyServerCert(state.PeerCertificates, cfg.rootCAs, cfg.serverName); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ }
+ if cfg.verifyPeerCertificate != nil {
+ if err = cfg.verifyPeerCertificate(state.PeerCertificates, chains); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ }
+ }
+ if cfg.verifyConnection != nil {
+ if err = cfg.verifyConnection(state.clone()); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, err
+ }
+ }
+
+ if err = state.cipherSuite.Init(state.masterSecret, clientRandom[:], serverRandom[:], true); err != nil {
+ return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+
+ cfg.writeKeyLog(keyLogLabelTLS12, clientRandom[:], state.masterSecret)
+
+ return nil, nil //nolint
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/flight6handler.go b/server/vendor/github.com/pion/dtls/v2/flight6handler.go
new file mode 100644
index 0000000..57ac143
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/flight6handler.go
@@ -0,0 +1,85 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "context"
+
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+func flight6Parse(_ context.Context, _ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) (flightVal, *alert.Alert, error) {
+ _, msgs, ok := cache.fullPullMap(state.handshakeRecvSequence-1, state.cipherSuite,
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
+ )
+ if !ok {
+ // No valid message received. Keep reading
+ return 0, nil, nil
+ }
+
+ if _, ok = msgs[handshake.TypeFinished].(*handshake.MessageFinished); !ok {
+ return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
+ }
+
+ // Other party may re-transmit the last flight. Keep state to be flight6.
+ return flight6, nil, nil
+}
+
+func flight6Generate(_ flightConn, state *State, cache *handshakeCache, cfg *handshakeConfig) ([]*packet, *alert.Alert, error) {
+ var pkts []*packet
+
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ },
+ Content: &protocol.ChangeCipherSpec{},
+ },
+ })
+
+ if len(state.localVerifyData) == 0 {
+ plainText := cache.pullAndMerge(
+ handshakeCachePullRule{handshake.TypeClientHello, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificateRequest, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, cfg.initialEpoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeClientKeyExchange, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeCertificateVerify, cfg.initialEpoch, true, false},
+ handshakeCachePullRule{handshake.TypeFinished, cfg.initialEpoch + 1, true, false},
+ )
+
+ var err error
+ state.localVerifyData, err = prf.VerifyDataServer(state.masterSecret, plainText, state.cipherSuite.HashFunc())
+ if err != nil {
+ return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, err
+ }
+ }
+
+ pkts = append(pkts,
+ &packet{
+ record: &recordlayer.RecordLayer{
+ Header: recordlayer.Header{
+ Version: protocol.Version1_2,
+ Epoch: 1,
+ },
+ Content: &handshake.Handshake{
+ Message: &handshake.MessageFinished{
+ VerifyData: state.localVerifyData,
+ },
+ },
+ },
+ shouldEncrypt: true,
+ resetLocalSequenceNumber: true,
+ },
+ )
+ return pkts, nil, nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/flighthandler.go b/server/vendor/github.com/pion/dtls/v2/flighthandler.go
new file mode 100644
index 0000000..ceb4a99
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/flighthandler.go
@@ -0,0 +1,68 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "context"
+
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+)
+
+// Parse received handshakes and return next flightVal
+type flightParser func(context.Context, flightConn, *State, *handshakeCache, *handshakeConfig) (flightVal, *alert.Alert, error)
+
+// Generate flights
+type flightGenerator func(flightConn, *State, *handshakeCache, *handshakeConfig) ([]*packet, *alert.Alert, error)
+
+func (f flightVal) getFlightParser() (flightParser, error) {
+ switch f {
+ case flight0:
+ return flight0Parse, nil
+ case flight1:
+ return flight1Parse, nil
+ case flight2:
+ return flight2Parse, nil
+ case flight3:
+ return flight3Parse, nil
+ case flight4:
+ return flight4Parse, nil
+ case flight4b:
+ return flight4bParse, nil
+ case flight5:
+ return flight5Parse, nil
+ case flight5b:
+ return flight5bParse, nil
+ case flight6:
+ return flight6Parse, nil
+ default:
+ return nil, errInvalidFlight
+ }
+}
+
+func (f flightVal) getFlightGenerator() (gen flightGenerator, retransmit bool, err error) {
+ switch f {
+ case flight0:
+ return flight0Generate, true, nil
+ case flight1:
+ return flight1Generate, true, nil
+ case flight2:
+ // https://tools.ietf.org/html/rfc6347#section-3.2.1
+ // HelloVerifyRequests must not be retransmitted.
+ return flight2Generate, false, nil
+ case flight3:
+ return flight3Generate, true, nil
+ case flight4:
+ return flight4Generate, true, nil
+ case flight4b:
+ return flight4bGenerate, true, nil
+ case flight5:
+ return flight5Generate, true, nil
+ case flight5b:
+ return flight5bGenerate, true, nil
+ case flight6:
+ return flight6Generate, true, nil
+ default:
+ return nil, false, errInvalidFlight
+ }
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/fragment_buffer.go b/server/vendor/github.com/pion/dtls/v2/fragment_buffer.go
new file mode 100644
index 0000000..f200337
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/fragment_buffer.go
@@ -0,0 +1,132 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// 2 megabytes
+const fragmentBufferMaxSize = 2000000
+
+type fragment struct {
+ recordLayerHeader recordlayer.Header
+ handshakeHeader handshake.Header
+ data []byte
+}
+
+type fragmentBuffer struct {
+ // map of MessageSequenceNumbers that hold slices of fragments
+ cache map[uint16][]*fragment
+
+ currentMessageSequenceNumber uint16
+}
+
+func newFragmentBuffer() *fragmentBuffer {
+ return &fragmentBuffer{cache: map[uint16][]*fragment{}}
+}
+
+// current total size of buffer
+func (f *fragmentBuffer) size() int {
+ size := 0
+ for i := range f.cache {
+ for j := range f.cache[i] {
+ size += len(f.cache[i][j].data)
+ }
+ }
+ return size
+}
+
+// Attempts to push a DTLS packet to the fragmentBuffer
+// when it returns true it means the fragmentBuffer has inserted and the buffer shouldn't be handled
+// when an error returns it is fatal, and the DTLS connection should be stopped
+func (f *fragmentBuffer) push(buf []byte) (bool, error) {
+ if f.size()+len(buf) >= fragmentBufferMaxSize {
+ return false, errFragmentBufferOverflow
+ }
+
+ frag := new(fragment)
+ if err := frag.recordLayerHeader.Unmarshal(buf); err != nil {
+ return false, err
+ }
+
+ // fragment isn't a handshake, we don't need to handle it
+ if frag.recordLayerHeader.ContentType != protocol.ContentTypeHandshake {
+ return false, nil
+ }
+
+ for buf = buf[recordlayer.HeaderSize:]; len(buf) != 0; frag = new(fragment) {
+ if err := frag.handshakeHeader.Unmarshal(buf); err != nil {
+ return false, err
+ }
+
+ if _, ok := f.cache[frag.handshakeHeader.MessageSequence]; !ok {
+ f.cache[frag.handshakeHeader.MessageSequence] = []*fragment{}
+ }
+
+ // end index should be the length of handshake header but if the handshake
+ // was fragmented, we should keep them all
+ end := int(handshake.HeaderLength + frag.handshakeHeader.Length)
+ if size := len(buf); end > size {
+ end = size
+ }
+
+ // Discard all headers, when rebuilding the packet we will re-build
+ frag.data = append([]byte{}, buf[handshake.HeaderLength:end]...)
+ f.cache[frag.handshakeHeader.MessageSequence] = append(f.cache[frag.handshakeHeader.MessageSequence], frag)
+ buf = buf[end:]
+ }
+
+ return true, nil
+}
+
+func (f *fragmentBuffer) pop() (content []byte, epoch uint16) {
+ frags, ok := f.cache[f.currentMessageSequenceNumber]
+ if !ok {
+ return nil, 0
+ }
+
+ // Go doesn't support recursive lambdas
+ var appendMessage func(targetOffset uint32) bool
+
+ rawMessage := []byte{}
+ appendMessage = func(targetOffset uint32) bool {
+ for _, f := range frags {
+ if f.handshakeHeader.FragmentOffset == targetOffset {
+ fragmentEnd := (f.handshakeHeader.FragmentOffset + f.handshakeHeader.FragmentLength)
+ if fragmentEnd != f.handshakeHeader.Length && f.handshakeHeader.FragmentLength != 0 {
+ if !appendMessage(fragmentEnd) {
+ return false
+ }
+ }
+
+ rawMessage = append(f.data, rawMessage...)
+ return true
+ }
+ }
+ return false
+ }
+
+ // Recursively collect up
+ if !appendMessage(0) {
+ return nil, 0
+ }
+
+ firstHeader := frags[0].handshakeHeader
+ firstHeader.FragmentOffset = 0
+ firstHeader.FragmentLength = firstHeader.Length
+
+ rawHeader, err := firstHeader.Marshal()
+ if err != nil {
+ return nil, 0
+ }
+
+ messageEpoch := frags[0].recordLayerHeader.Epoch
+
+ delete(f.cache, f.currentMessageSequenceNumber)
+ f.currentMessageSequenceNumber++
+ return append(rawHeader, rawMessage...), messageEpoch
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/handshake_cache.go b/server/vendor/github.com/pion/dtls/v2/handshake_cache.go
new file mode 100644
index 0000000..8d59605
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/handshake_cache.go
@@ -0,0 +1,172 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "sync"
+
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+)
+
+type handshakeCacheItem struct {
+ typ handshake.Type
+ isClient bool
+ epoch uint16
+ messageSequence uint16
+ data []byte
+}
+
+type handshakeCachePullRule struct {
+ typ handshake.Type
+ epoch uint16
+ isClient bool
+ optional bool
+}
+
+type handshakeCache struct {
+ cache []*handshakeCacheItem
+ mu sync.Mutex
+}
+
+func newHandshakeCache() *handshakeCache {
+ return &handshakeCache{}
+}
+
+func (h *handshakeCache) push(data []byte, epoch, messageSequence uint16, typ handshake.Type, isClient bool) {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+
+ h.cache = append(h.cache, &handshakeCacheItem{
+ data: append([]byte{}, data...),
+ epoch: epoch,
+ messageSequence: messageSequence,
+ typ: typ,
+ isClient: isClient,
+ })
+}
+
+// returns a list handshakes that match the requested rules
+// the list will contain null entries for rules that can't be satisfied
+// multiple entries may match a rule, but only the last match is returned (ie ClientHello with cookies)
+func (h *handshakeCache) pull(rules ...handshakeCachePullRule) []*handshakeCacheItem {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+
+ out := make([]*handshakeCacheItem, len(rules))
+ for i, r := range rules {
+ for _, c := range h.cache {
+ if c.typ == r.typ && c.isClient == r.isClient && c.epoch == r.epoch {
+ switch {
+ case out[i] == nil:
+ out[i] = c
+ case out[i].messageSequence < c.messageSequence:
+ out[i] = c
+ }
+ }
+ }
+ }
+
+ return out
+}
+
+// fullPullMap pulls all handshakes between rules[0] to rules[len(rules)-1] as map.
+func (h *handshakeCache) fullPullMap(startSeq int, cipherSuite CipherSuite, rules ...handshakeCachePullRule) (int, map[handshake.Type]handshake.Message, bool) {
+ h.mu.Lock()
+ defer h.mu.Unlock()
+
+ ci := make(map[handshake.Type]*handshakeCacheItem)
+ for _, r := range rules {
+ var item *handshakeCacheItem
+ for _, c := range h.cache {
+ if c.typ == r.typ && c.isClient == r.isClient && c.epoch == r.epoch {
+ switch {
+ case item == nil:
+ item = c
+ case item.messageSequence < c.messageSequence:
+ item = c
+ }
+ }
+ }
+ if !r.optional && item == nil {
+ // Missing mandatory message.
+ return startSeq, nil, false
+ }
+ ci[r.typ] = item
+ }
+ out := make(map[handshake.Type]handshake.Message)
+ seq := startSeq
+ for _, r := range rules {
+ t := r.typ
+ i := ci[t]
+ if i == nil {
+ continue
+ }
+ var keyExchangeAlgorithm CipherSuiteKeyExchangeAlgorithm
+ if cipherSuite != nil {
+ keyExchangeAlgorithm = cipherSuite.KeyExchangeAlgorithm()
+ }
+ rawHandshake := &handshake.Handshake{
+ KeyExchangeAlgorithm: keyExchangeAlgorithm,
+ }
+ if err := rawHandshake.Unmarshal(i.data); err != nil {
+ return startSeq, nil, false
+ }
+ if uint16(seq) != rawHandshake.Header.MessageSequence {
+ // There is a gap. Some messages are not arrived.
+ return startSeq, nil, false
+ }
+ seq++
+ out[t] = rawHandshake.Message
+ }
+ return seq, out, true
+}
+
+// pullAndMerge calls pull and then merges the results, ignoring any null entries
+func (h *handshakeCache) pullAndMerge(rules ...handshakeCachePullRule) []byte {
+ merged := []byte{}
+
+ for _, p := range h.pull(rules...) {
+ if p != nil {
+ merged = append(merged, p.data...)
+ }
+ }
+ return merged
+}
+
+// sessionHash returns the session hash for Extended Master Secret support
+// https://tools.ietf.org/html/draft-ietf-tls-session-hash-06#section-4
+func (h *handshakeCache) sessionHash(hf prf.HashFunc, epoch uint16, additional ...[]byte) ([]byte, error) {
+ merged := []byte{}
+
+ // Order defined by https://tools.ietf.org/html/rfc5246#section-7.3
+ handshakeBuffer := h.pull(
+ handshakeCachePullRule{handshake.TypeClientHello, epoch, true, false},
+ handshakeCachePullRule{handshake.TypeServerHello, epoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, epoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerKeyExchange, epoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificateRequest, epoch, false, false},
+ handshakeCachePullRule{handshake.TypeServerHelloDone, epoch, false, false},
+ handshakeCachePullRule{handshake.TypeCertificate, epoch, true, false},
+ handshakeCachePullRule{handshake.TypeClientKeyExchange, epoch, true, false},
+ )
+
+ for _, p := range handshakeBuffer {
+ if p == nil {
+ continue
+ }
+
+ merged = append(merged, p.data...)
+ }
+ for _, a := range additional {
+ merged = append(merged, a...)
+ }
+
+ hash := hf()
+ if _, err := hash.Write(merged); err != nil {
+ return []byte{}, err
+ }
+
+ return hash.Sum(nil), nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/handshaker.go b/server/vendor/github.com/pion/dtls/v2/handshaker.go
new file mode 100644
index 0000000..1c6d58f
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/handshaker.go
@@ -0,0 +1,350 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "io"
+ "sync"
+ "time"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/crypto/signaturehash"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+ "github.com/pion/logging"
+)
+
+// [RFC6347 Section-4.2.4]
+// +-----------+
+// +---> | PREPARING | <--------------------+
+// | +-----------+ |
+// | | |
+// | | Buffer next flight |
+// | | |
+// | \|/ |
+// | +-----------+ |
+// | | SENDING |<------------------+ | Send
+// | +-----------+ | | HelloRequest
+// Receive | | | |
+// next | | Send flight | | or
+// flight | +--------+ | |
+// | | | Set retransmit timer | | Receive
+// | | \|/ | | HelloRequest
+// | | +-----------+ | | Send
+// +--)--| WAITING |-------------------+ | ClientHello
+// | | +-----------+ Timer expires | |
+// | | | | |
+// | | +------------------------+ |
+// Receive | | Send Read retransmit |
+// last | | last |
+// flight | | flight |
+// | | |
+// \|/\|/ |
+// +-----------+ |
+// | FINISHED | -------------------------------+
+// +-----------+
+// | /|\
+// | |
+// +---+
+// Read retransmit
+// Retransmit last flight
+
+type handshakeState uint8
+
+const (
+ handshakeErrored handshakeState = iota
+ handshakePreparing
+ handshakeSending
+ handshakeWaiting
+ handshakeFinished
+)
+
+func (s handshakeState) String() string {
+ switch s {
+ case handshakeErrored:
+ return "Errored"
+ case handshakePreparing:
+ return "Preparing"
+ case handshakeSending:
+ return "Sending"
+ case handshakeWaiting:
+ return "Waiting"
+ case handshakeFinished:
+ return "Finished"
+ default:
+ return "Unknown"
+ }
+}
+
+type handshakeFSM struct {
+ currentFlight flightVal
+ flights []*packet
+ retransmit bool
+ state *State
+ cache *handshakeCache
+ cfg *handshakeConfig
+ closed chan struct{}
+}
+
+type handshakeConfig struct {
+ localPSKCallback PSKCallback
+ localPSKIdentityHint []byte
+ localCipherSuites []CipherSuite // Available CipherSuites
+ localSignatureSchemes []signaturehash.Algorithm // Available signature schemes
+ extendedMasterSecret ExtendedMasterSecretType // Policy for the Extended Master Support extension
+ localSRTPProtectionProfiles []SRTPProtectionProfile // Available SRTPProtectionProfiles, if empty no SRTP support
+ serverName string
+ supportedProtocols []string
+ clientAuth ClientAuthType // If we are a client should we request a client certificate
+ localCertificates []tls.Certificate
+ nameToCertificate map[string]*tls.Certificate
+ insecureSkipVerify bool
+ verifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
+ verifyConnection func(*State) error
+ sessionStore SessionStore
+ rootCAs *x509.CertPool
+ clientCAs *x509.CertPool
+ retransmitInterval time.Duration
+ customCipherSuites func() []CipherSuite
+ ellipticCurves []elliptic.Curve
+ insecureSkipHelloVerify bool
+
+ onFlightState func(flightVal, handshakeState)
+ log logging.LeveledLogger
+ keyLogWriter io.Writer
+
+ localGetCertificate func(*ClientHelloInfo) (*tls.Certificate, error)
+ localGetClientCertificate func(*CertificateRequestInfo) (*tls.Certificate, error)
+
+ initialEpoch uint16
+
+ mu sync.Mutex
+}
+
+type flightConn interface {
+ notify(ctx context.Context, level alert.Level, desc alert.Description) error
+ writePackets(context.Context, []*packet) error
+ recvHandshake() <-chan chan struct{}
+ setLocalEpoch(epoch uint16)
+ handleQueuedPackets(context.Context) error
+ sessionKey() []byte
+}
+
+func (c *handshakeConfig) writeKeyLog(label string, clientRandom, secret []byte) {
+ if c.keyLogWriter == nil {
+ return
+ }
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ _, err := c.keyLogWriter.Write([]byte(fmt.Sprintf("%s %x %x\n", label, clientRandom, secret)))
+ if err != nil {
+ c.log.Debugf("failed to write key log file: %s", err)
+ }
+}
+
+func srvCliStr(isClient bool) string {
+ if isClient {
+ return "client"
+ }
+ return "server"
+}
+
+func newHandshakeFSM(
+ s *State, cache *handshakeCache, cfg *handshakeConfig,
+ initialFlight flightVal,
+) *handshakeFSM {
+ return &handshakeFSM{
+ currentFlight: initialFlight,
+ state: s,
+ cache: cache,
+ cfg: cfg,
+ closed: make(chan struct{}),
+ }
+}
+
+func (s *handshakeFSM) Run(ctx context.Context, c flightConn, initialState handshakeState) error {
+ state := initialState
+ defer func() {
+ close(s.closed)
+ }()
+ for {
+ s.cfg.log.Tracef("[handshake:%s] %s: %s", srvCliStr(s.state.isClient), s.currentFlight.String(), state.String())
+ if s.cfg.onFlightState != nil {
+ s.cfg.onFlightState(s.currentFlight, state)
+ }
+ var err error
+ switch state {
+ case handshakePreparing:
+ state, err = s.prepare(ctx, c)
+ case handshakeSending:
+ state, err = s.send(ctx, c)
+ case handshakeWaiting:
+ state, err = s.wait(ctx, c)
+ case handshakeFinished:
+ state, err = s.finish(ctx, c)
+ default:
+ return errInvalidFSMTransition
+ }
+ if err != nil {
+ return err
+ }
+ }
+}
+
+func (s *handshakeFSM) Done() <-chan struct{} {
+ return s.closed
+}
+
+func (s *handshakeFSM) prepare(ctx context.Context, c flightConn) (handshakeState, error) {
+ s.flights = nil
+ // Prepare flights
+ var (
+ a *alert.Alert
+ err error
+ pkts []*packet
+ )
+ gen, retransmit, errFlight := s.currentFlight.getFlightGenerator()
+ if errFlight != nil {
+ err = errFlight
+ a = &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}
+ } else {
+ pkts, a, err = gen(c, s.state, s.cache, s.cfg)
+ s.retransmit = retransmit
+ }
+ if a != nil {
+ if alertErr := c.notify(ctx, a.Level, a.Description); alertErr != nil {
+ if err != nil {
+ err = alertErr
+ }
+ }
+ }
+ if err != nil {
+ return handshakeErrored, err
+ }
+
+ s.flights = pkts
+ epoch := s.cfg.initialEpoch
+ nextEpoch := epoch
+ for _, p := range s.flights {
+ p.record.Header.Epoch += epoch
+ if p.record.Header.Epoch > nextEpoch {
+ nextEpoch = p.record.Header.Epoch
+ }
+ if h, ok := p.record.Content.(*handshake.Handshake); ok {
+ h.Header.MessageSequence = uint16(s.state.handshakeSendSequence)
+ s.state.handshakeSendSequence++
+ }
+ }
+ if epoch != nextEpoch {
+ s.cfg.log.Tracef("[handshake:%s] -> changeCipherSpec (epoch: %d)", srvCliStr(s.state.isClient), nextEpoch)
+ c.setLocalEpoch(nextEpoch)
+ }
+ return handshakeSending, nil
+}
+
+func (s *handshakeFSM) send(ctx context.Context, c flightConn) (handshakeState, error) {
+ // Send flights
+ if err := c.writePackets(ctx, s.flights); err != nil {
+ return handshakeErrored, err
+ }
+
+ if s.currentFlight.isLastSendFlight() {
+ return handshakeFinished, nil
+ }
+ return handshakeWaiting, nil
+}
+
+func (s *handshakeFSM) wait(ctx context.Context, c flightConn) (handshakeState, error) { //nolint:gocognit
+ 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, errFlight
+ }
+
+ retransmitTimer := time.NewTimer(s.cfg.retransmitInterval)
+ for {
+ select {
+ case done := <-c.recvHandshake():
+ nextFlight, alert, err := parse(ctx, c, s.state, s.cache, s.cfg)
+ close(done)
+ if alert != nil {
+ if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
+ if err != nil {
+ err = alertErr
+ }
+ }
+ }
+ if err != nil {
+ return handshakeErrored, err
+ }
+ if nextFlight == 0 {
+ break
+ }
+ s.cfg.log.Tracef("[handshake:%s] %s -> %s", srvCliStr(s.state.isClient), s.currentFlight.String(), nextFlight.String())
+ if nextFlight.isLastRecvFlight() && s.currentFlight == nextFlight {
+ return handshakeFinished, nil
+ }
+ s.currentFlight = nextFlight
+ return handshakePreparing, nil
+
+ case <-retransmitTimer.C:
+ if !s.retransmit {
+ return handshakeWaiting, nil
+ }
+ return handshakeSending, nil
+ case <-ctx.Done():
+ return handshakeErrored, ctx.Err()
+ }
+ }
+}
+
+func (s *handshakeFSM) finish(ctx context.Context, c flightConn) (handshakeState, error) {
+ 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, errFlight
+ }
+
+ retransmitTimer := time.NewTimer(s.cfg.retransmitInterval)
+ select {
+ case done := <-c.recvHandshake():
+ nextFlight, alert, err := parse(ctx, c, s.state, s.cache, s.cfg)
+ close(done)
+ if alert != nil {
+ if alertErr := c.notify(ctx, alert.Level, alert.Description); alertErr != nil {
+ if err != nil {
+ err = alertErr
+ }
+ }
+ }
+ if err != nil {
+ return handshakeErrored, err
+ }
+ if nextFlight == 0 {
+ break
+ }
+ if nextFlight.isLastRecvFlight() && s.currentFlight == nextFlight {
+ return handshakeFinished, nil
+ }
+ <-retransmitTimer.C
+ // Retransmit last flight
+ return handshakeSending, nil
+
+ case <-ctx.Done():
+ return handshakeErrored, ctx.Err()
+ }
+ return handshakeFinished, nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go
new file mode 100644
index 0000000..f78b6dc
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_128_ccm.go
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+)
+
+// Aes128Ccm is a base class used by multiple AES-CCM Ciphers
+type Aes128Ccm struct {
+ AesCcm
+}
+
+func newAes128Ccm(clientCertificateType clientcertificate.Type, id ID, psk bool, cryptoCCMTagLen ciphersuite.CCMTagLen, keyExchangeAlgorithm KeyExchangeAlgorithm, ecc bool) *Aes128Ccm {
+ return &Aes128Ccm{
+ AesCcm: AesCcm{
+ clientCertificateType: clientCertificateType,
+ id: id,
+ psk: psk,
+ cryptoCCMTagLen: cryptoCCMTagLen,
+ keyExchangeAlgorithm: keyExchangeAlgorithm,
+ ecc: ecc,
+ },
+ }
+}
+
+// Init initializes the internal Cipher with keying material
+func (c *Aes128Ccm) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
+ const prfKeyLen = 16
+ return c.AesCcm.Init(masterSecret, clientRandom, serverRandom, isClient, prfKeyLen)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_256_ccm.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_256_ccm.go
new file mode 100644
index 0000000..bb81286
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_256_ccm.go
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+)
+
+// Aes256Ccm is a base class used by multiple AES-CCM Ciphers
+type Aes256Ccm struct {
+ AesCcm
+}
+
+func newAes256Ccm(clientCertificateType clientcertificate.Type, id ID, psk bool, cryptoCCMTagLen ciphersuite.CCMTagLen, keyExchangeAlgorithm KeyExchangeAlgorithm, ecc bool) *Aes256Ccm {
+ return &Aes256Ccm{
+ AesCcm: AesCcm{
+ clientCertificateType: clientCertificateType,
+ id: id,
+ psk: psk,
+ cryptoCCMTagLen: cryptoCCMTagLen,
+ keyExchangeAlgorithm: keyExchangeAlgorithm,
+ ecc: ecc,
+ },
+ }
+}
+
+// Init initializes the internal Cipher with keying material
+func (c *Aes256Ccm) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
+ const prfKeyLen = 32
+ return c.AesCcm.Init(masterSecret, clientRandom, serverRandom, isClient, prfKeyLen)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_ccm.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_ccm.go
new file mode 100644
index 0000000..dc51198
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/aes_ccm.go
@@ -0,0 +1,113 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "crypto/sha256"
+ "fmt"
+ "hash"
+ "sync/atomic"
+
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// AesCcm is a base class used by multiple AES-CCM Ciphers
+type AesCcm struct {
+ ccm atomic.Value // *cryptoCCM
+ clientCertificateType clientcertificate.Type
+ id ID
+ psk bool
+ keyExchangeAlgorithm KeyExchangeAlgorithm
+ cryptoCCMTagLen ciphersuite.CCMTagLen
+ ecc bool
+}
+
+// CertificateType returns what type of certificate this CipherSuite exchanges
+func (c *AesCcm) CertificateType() clientcertificate.Type {
+ return c.clientCertificateType
+}
+
+// ID returns the ID of the CipherSuite
+func (c *AesCcm) ID() ID {
+ return c.id
+}
+
+func (c *AesCcm) String() string {
+ return c.id.String()
+}
+
+// ECC uses Elliptic Curve Cryptography
+func (c *AesCcm) ECC() bool {
+ return c.ecc
+}
+
+// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
+func (c *AesCcm) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
+ return c.keyExchangeAlgorithm
+}
+
+// HashFunc returns the hashing func for this CipherSuite
+func (c *AesCcm) HashFunc() func() hash.Hash {
+ return sha256.New
+}
+
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *AesCcm) AuthenticationType() AuthenticationType {
+ if c.psk {
+ return AuthenticationTypePreSharedKey
+ }
+ return AuthenticationTypeCertificate
+}
+
+// IsInitialized returns if the CipherSuite has keying material and can
+// encrypt/decrypt packets
+func (c *AesCcm) IsInitialized() bool {
+ return c.ccm.Load() != nil
+}
+
+// Init initializes the internal Cipher with keying material
+func (c *AesCcm) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool, prfKeyLen int) error {
+ const (
+ prfMacLen = 0
+ prfIvLen = 4
+ )
+
+ keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
+ if err != nil {
+ return err
+ }
+
+ var ccm *ciphersuite.CCM
+ if isClient {
+ ccm, err = ciphersuite.NewCCM(c.cryptoCCMTagLen, keys.ClientWriteKey, keys.ClientWriteIV, keys.ServerWriteKey, keys.ServerWriteIV)
+ } else {
+ ccm, err = ciphersuite.NewCCM(c.cryptoCCMTagLen, keys.ServerWriteKey, keys.ServerWriteIV, keys.ClientWriteKey, keys.ClientWriteIV)
+ }
+ c.ccm.Store(ccm)
+
+ return err
+}
+
+// Encrypt encrypts a single TLS RecordLayer
+func (c *AesCcm) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
+ cipherSuite, ok := c.ccm.Load().(*ciphersuite.CCM)
+ if !ok {
+ return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
+ }
+
+ return cipherSuite.Encrypt(pkt, raw)
+}
+
+// Decrypt decrypts a single TLS RecordLayer
+func (c *AesCcm) Decrypt(raw []byte) ([]byte, error) {
+ cipherSuite, ok := c.ccm.Load().(*ciphersuite.CCM)
+ if !ok {
+ return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
+ }
+
+ return cipherSuite.Decrypt(raw)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go
new file mode 100644
index 0000000..f44f29f
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/ciphersuite.go
@@ -0,0 +1,98 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package ciphersuite provides TLS Ciphers as registered with the IANA https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4
+package ciphersuite
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/pion/dtls/v2/internal/ciphersuite/types"
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+var errCipherSuiteNotInit = &protocol.TemporaryError{Err: errors.New("CipherSuite has not been initialized")} //nolint:goerr113
+
+// ID is an ID for our supported CipherSuites
+type ID uint16
+
+func (i ID) String() string {
+ switch i {
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
+ return "TLS_ECDHE_ECDSA_WITH_AES_128_CCM"
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
+ return "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8"
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ return "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"
+ case TLS_PSK_WITH_AES_128_CCM:
+ return "TLS_PSK_WITH_AES_128_CCM"
+ case TLS_PSK_WITH_AES_128_CCM_8:
+ return "TLS_PSK_WITH_AES_128_CCM_8"
+ case TLS_PSK_WITH_AES_256_CCM_8:
+ return "TLS_PSK_WITH_AES_256_CCM_8"
+ case TLS_PSK_WITH_AES_128_GCM_SHA256:
+ return "TLS_PSK_WITH_AES_128_GCM_SHA256"
+ case TLS_PSK_WITH_AES_128_CBC_SHA256:
+ return "TLS_PSK_WITH_AES_128_CBC_SHA256"
+ case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ return "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
+ case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ return "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
+ case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+ return "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256"
+ default:
+ return fmt.Sprintf("unknown(%v)", uint16(i))
+ }
+}
+
+// Supported Cipher Suites
+const (
+ // AES-128-CCM
+ TLS_ECDHE_ECDSA_WITH_AES_128_CCM ID = 0xc0ac //nolint:revive,stylecheck
+ TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 ID = 0xc0ae //nolint:revive,stylecheck
+
+ // AES-128-GCM-SHA256
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ID = 0xc02b //nolint:revive,stylecheck
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ID = 0xc02f //nolint:revive,stylecheck
+
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 ID = 0xc02c //nolint:revive,stylecheck
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ID = 0xc030 //nolint:revive,stylecheck
+ // AES-256-CBC-SHA
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ID = 0xc00a //nolint:revive,stylecheck
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ID = 0xc014 //nolint:revive,stylecheck
+
+ TLS_PSK_WITH_AES_128_CCM ID = 0xc0a4 //nolint:revive,stylecheck
+ TLS_PSK_WITH_AES_128_CCM_8 ID = 0xc0a8 //nolint:revive,stylecheck
+ TLS_PSK_WITH_AES_256_CCM_8 ID = 0xc0a9 //nolint:revive,stylecheck
+ TLS_PSK_WITH_AES_128_GCM_SHA256 ID = 0x00a8 //nolint:revive,stylecheck
+ TLS_PSK_WITH_AES_128_CBC_SHA256 ID = 0x00ae //nolint:revive,stylecheck
+
+ TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 ID = 0xC037 //nolint:revive,stylecheck
+)
+
+// AuthenticationType controls what authentication method is using during the handshake
+type AuthenticationType = types.AuthenticationType
+
+// AuthenticationType Enums
+const (
+ AuthenticationTypeCertificate AuthenticationType = types.AuthenticationTypeCertificate
+ AuthenticationTypePreSharedKey AuthenticationType = types.AuthenticationTypePreSharedKey
+ AuthenticationTypeAnonymous AuthenticationType = types.AuthenticationTypeAnonymous
+)
+
+// KeyExchangeAlgorithm controls what exchange algorithm was chosen.
+type KeyExchangeAlgorithm = types.KeyExchangeAlgorithm
+
+// KeyExchangeAlgorithm Bitmask
+const (
+ KeyExchangeAlgorithmNone KeyExchangeAlgorithm = types.KeyExchangeAlgorithmNone
+ KeyExchangeAlgorithmPsk KeyExchangeAlgorithm = types.KeyExchangeAlgorithmPsk
+ KeyExchangeAlgorithmEcdhe KeyExchangeAlgorithm = types.KeyExchangeAlgorithmEcdhe
+)
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go
new file mode 100644
index 0000000..8367b2c
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm.go
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+)
+
+// NewTLSEcdheEcdsaWithAes128Ccm constructs a TLS_ECDHE_ECDSA_WITH_AES_128_CCM Cipher
+func NewTLSEcdheEcdsaWithAes128Ccm() *Aes128Ccm {
+ return newAes128Ccm(clientcertificate.ECDSASign, TLS_ECDHE_ECDSA_WITH_AES_128_CCM, false, ciphersuite.CCMTagLength, KeyExchangeAlgorithmEcdhe, true)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go
new file mode 100644
index 0000000..11b6873
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_ccm8.go
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+)
+
+// NewTLSEcdheEcdsaWithAes128Ccm8 creates a new TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 CipherSuite
+func NewTLSEcdheEcdsaWithAes128Ccm8() *Aes128Ccm {
+ return newAes128Ccm(clientcertificate.ECDSASign, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, false, ciphersuite.CCMTagLength8, KeyExchangeAlgorithmEcdhe, true)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go
new file mode 100644
index 0000000..0c919fe
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_128_gcm_sha256.go
@@ -0,0 +1,108 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "crypto/sha256"
+ "fmt"
+ "hash"
+ "sync/atomic"
+
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// TLSEcdheEcdsaWithAes128GcmSha256 represents a TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuite
+type TLSEcdheEcdsaWithAes128GcmSha256 struct {
+ gcm atomic.Value // *cryptoGCM
+}
+
+// CertificateType returns what type of certficate this CipherSuite exchanges
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) CertificateType() clientcertificate.Type {
+ return clientcertificate.ECDSASign
+}
+
+// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
+ return KeyExchangeAlgorithmEcdhe
+}
+
+// ECC uses Elliptic Curve Cryptography
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) ECC() bool {
+ return true
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) ID() ID {
+ return TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+}
+
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) String() string {
+ return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
+}
+
+// HashFunc returns the hashing func for this CipherSuite
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) HashFunc() func() hash.Hash {
+ return sha256.New
+}
+
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) AuthenticationType() AuthenticationType {
+ return AuthenticationTypeCertificate
+}
+
+// IsInitialized returns if the CipherSuite has keying material and can
+// encrypt/decrypt packets
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) IsInitialized() bool {
+ return c.gcm.Load() != nil
+}
+
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) init(masterSecret, clientRandom, serverRandom []byte, isClient bool, prfMacLen, prfKeyLen, prfIvLen int, hashFunc func() hash.Hash) error {
+ keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, hashFunc)
+ if err != nil {
+ return err
+ }
+
+ var gcm *ciphersuite.GCM
+ if isClient {
+ gcm, err = ciphersuite.NewGCM(keys.ClientWriteKey, keys.ClientWriteIV, keys.ServerWriteKey, keys.ServerWriteIV)
+ } else {
+ gcm, err = ciphersuite.NewGCM(keys.ServerWriteKey, keys.ServerWriteIV, keys.ClientWriteKey, keys.ClientWriteIV)
+ }
+ c.gcm.Store(gcm)
+ return err
+}
+
+// Init initializes the internal Cipher with keying material
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
+ const (
+ prfMacLen = 0
+ prfKeyLen = 16
+ prfIvLen = 4
+ )
+
+ return c.init(masterSecret, clientRandom, serverRandom, isClient, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
+}
+
+// Encrypt encrypts a single TLS RecordLayer
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
+ cipherSuite, ok := c.gcm.Load().(*ciphersuite.GCM)
+ if !ok {
+ return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
+ }
+
+ return cipherSuite.Encrypt(pkt, raw)
+}
+
+// Decrypt decrypts a single TLS RecordLayer
+func (c *TLSEcdheEcdsaWithAes128GcmSha256) Decrypt(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)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go
new file mode 100644
index 0000000..577192c
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_cbc_sha.go
@@ -0,0 +1,114 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "crypto/sha1" //nolint: gosec,gci
+ "crypto/sha256"
+ "fmt"
+ "hash"
+ "sync/atomic"
+
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// TLSEcdheEcdsaWithAes256CbcSha represents a TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA CipherSuite
+type TLSEcdheEcdsaWithAes256CbcSha struct {
+ cbc atomic.Value // *cryptoCBC
+}
+
+// CertificateType returns what type of certficate this CipherSuite exchanges
+func (c *TLSEcdheEcdsaWithAes256CbcSha) CertificateType() clientcertificate.Type {
+ return clientcertificate.ECDSASign
+}
+
+// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
+func (c *TLSEcdheEcdsaWithAes256CbcSha) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
+ return KeyExchangeAlgorithmEcdhe
+}
+
+// ECC uses Elliptic Curve Cryptography
+func (c *TLSEcdheEcdsaWithAes256CbcSha) ECC() bool {
+ return true
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSEcdheEcdsaWithAes256CbcSha) ID() ID {
+ return TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
+}
+
+func (c *TLSEcdheEcdsaWithAes256CbcSha) String() string {
+ return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"
+}
+
+// HashFunc returns the hashing func for this CipherSuite
+func (c *TLSEcdheEcdsaWithAes256CbcSha) HashFunc() func() hash.Hash {
+ return sha256.New
+}
+
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *TLSEcdheEcdsaWithAes256CbcSha) AuthenticationType() AuthenticationType {
+ return AuthenticationTypeCertificate
+}
+
+// IsInitialized returns if the CipherSuite has keying material and can
+// encrypt/decrypt packets
+func (c *TLSEcdheEcdsaWithAes256CbcSha) IsInitialized() bool {
+ return c.cbc.Load() != nil
+}
+
+// Init initializes the internal Cipher with keying material
+func (c *TLSEcdheEcdsaWithAes256CbcSha) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
+ const (
+ prfMacLen = 20
+ prfKeyLen = 32
+ prfIvLen = 16
+ )
+
+ keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
+ if err != nil {
+ return err
+ }
+
+ var cbc *ciphersuite.CBC
+ if isClient {
+ cbc, err = ciphersuite.NewCBC(
+ keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
+ keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
+ sha1.New,
+ )
+ } else {
+ cbc, err = ciphersuite.NewCBC(
+ keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
+ keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
+ sha1.New,
+ )
+ }
+ c.cbc.Store(cbc)
+
+ return err
+}
+
+// Encrypt encrypts a single TLS RecordLayer
+func (c *TLSEcdheEcdsaWithAes256CbcSha) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
+ cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
+ if !ok {
+ return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
+ }
+
+ return cipherSuite.Encrypt(pkt, raw)
+}
+
+// Decrypt decrypts a single TLS RecordLayer
+func (c *TLSEcdheEcdsaWithAes256CbcSha) Decrypt(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)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_gcm_sha384.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_gcm_sha384.go
new file mode 100644
index 0000000..2a3cfa4
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_ecdsa_with_aes_256_gcm_sha384.go
@@ -0,0 +1,39 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "crypto/sha512"
+ "hash"
+)
+
+// TLSEcdheEcdsaWithAes256GcmSha384 represents a TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 CipherSuite
+type TLSEcdheEcdsaWithAes256GcmSha384 struct {
+ TLSEcdheEcdsaWithAes128GcmSha256
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSEcdheEcdsaWithAes256GcmSha384) ID() ID {
+ return TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
+}
+
+func (c *TLSEcdheEcdsaWithAes256GcmSha384) String() string {
+ return "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
+}
+
+// HashFunc returns the hashing func for this CipherSuite
+func (c *TLSEcdheEcdsaWithAes256GcmSha384) HashFunc() func() hash.Hash {
+ return sha512.New384
+}
+
+// Init initializes the internal Cipher with keying material
+func (c *TLSEcdheEcdsaWithAes256GcmSha384) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
+ const (
+ prfMacLen = 0
+ prfKeyLen = 32
+ prfIvLen = 4
+ )
+
+ return c.init(masterSecret, clientRandom, serverRandom, isClient, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_psk_with_aes_128_cbc_sha256.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_psk_with_aes_128_cbc_sha256.go
new file mode 100644
index 0000000..75a2563
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_psk_with_aes_128_cbc_sha256.go
@@ -0,0 +1,118 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "crypto/sha256"
+ "fmt"
+ "hash"
+ "sync/atomic"
+
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// TLSEcdhePskWithAes128CbcSha256 implements the TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 CipherSuite
+type TLSEcdhePskWithAes128CbcSha256 struct {
+ cbc atomic.Value // *cryptoCBC
+}
+
+// NewTLSEcdhePskWithAes128CbcSha256 creates TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 cipher.
+func NewTLSEcdhePskWithAes128CbcSha256() *TLSEcdhePskWithAes128CbcSha256 {
+ return &TLSEcdhePskWithAes128CbcSha256{}
+}
+
+// CertificateType returns what type of certificate this CipherSuite exchanges
+func (c *TLSEcdhePskWithAes128CbcSha256) CertificateType() clientcertificate.Type {
+ return clientcertificate.Type(0)
+}
+
+// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
+func (c *TLSEcdhePskWithAes128CbcSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
+ return (KeyExchangeAlgorithmPsk | KeyExchangeAlgorithmEcdhe)
+}
+
+// ECC uses Elliptic Curve Cryptography
+func (c *TLSEcdhePskWithAes128CbcSha256) ECC() bool {
+ return true
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSEcdhePskWithAes128CbcSha256) ID() ID {
+ return TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
+}
+
+func (c *TLSEcdhePskWithAes128CbcSha256) String() string {
+ return "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA256"
+}
+
+// HashFunc returns the hashing func for this CipherSuite
+func (c *TLSEcdhePskWithAes128CbcSha256) HashFunc() func() hash.Hash {
+ return sha256.New
+}
+
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *TLSEcdhePskWithAes128CbcSha256) AuthenticationType() AuthenticationType {
+ return AuthenticationTypePreSharedKey
+}
+
+// IsInitialized returns if the CipherSuite has keying material and can
+// encrypt/decrypt packets
+func (c *TLSEcdhePskWithAes128CbcSha256) IsInitialized() bool {
+ return c.cbc.Load() != nil
+}
+
+// Init initializes the internal Cipher with keying material
+func (c *TLSEcdhePskWithAes128CbcSha256) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
+ const (
+ prfMacLen = 32
+ prfKeyLen = 16
+ prfIvLen = 16
+ )
+
+ keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
+ if err != nil {
+ return err
+ }
+
+ var cbc *ciphersuite.CBC
+ if isClient {
+ cbc, err = ciphersuite.NewCBC(
+ keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
+ keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
+ c.HashFunc(),
+ )
+ } else {
+ cbc, err = ciphersuite.NewCBC(
+ keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
+ keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
+ c.HashFunc(),
+ )
+ }
+ c.cbc.Store(cbc)
+
+ return err
+}
+
+// Encrypt encrypts a single TLS RecordLayer
+func (c *TLSEcdhePskWithAes128CbcSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
+ cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
+ if !ok { // !c.isInitialized()
+ return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
+ }
+
+ return cipherSuite.Encrypt(pkt, raw)
+}
+
+// Decrypt decrypts a single TLS RecordLayer
+func (c *TLSEcdhePskWithAes128CbcSha256) Decrypt(raw []byte) ([]byte, error) {
+ cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
+ if !ok { // !c.isInitialized()
+ return nil, fmt.Errorf("%w, unable to decrypt", errCipherSuiteNotInit)
+ }
+
+ return cipherSuite.Decrypt(raw)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go
new file mode 100644
index 0000000..478a2e0
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_128_gcm_sha256.go
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+
+// TLSEcdheRsaWithAes128GcmSha256 implements the TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 CipherSuite
+type TLSEcdheRsaWithAes128GcmSha256 struct {
+ TLSEcdheEcdsaWithAes128GcmSha256
+}
+
+// CertificateType returns what type of certificate this CipherSuite exchanges
+func (c *TLSEcdheRsaWithAes128GcmSha256) CertificateType() clientcertificate.Type {
+ return clientcertificate.RSASign
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSEcdheRsaWithAes128GcmSha256) ID() ID {
+ return TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+}
+
+func (c *TLSEcdheRsaWithAes128GcmSha256) String() string {
+ return "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go
new file mode 100644
index 0000000..8e88ee6
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_cbc_sha.go
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+
+// TLSEcdheRsaWithAes256CbcSha implements the TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA CipherSuite
+type TLSEcdheRsaWithAes256CbcSha struct {
+ TLSEcdheEcdsaWithAes256CbcSha
+}
+
+// CertificateType returns what type of certificate this CipherSuite exchanges
+func (c *TLSEcdheRsaWithAes256CbcSha) CertificateType() clientcertificate.Type {
+ return clientcertificate.RSASign
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSEcdheRsaWithAes256CbcSha) ID() ID {
+ return TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
+}
+
+func (c *TLSEcdheRsaWithAes256CbcSha) String() string {
+ return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_gcm_sha384.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_gcm_sha384.go
new file mode 100644
index 0000000..752fb52
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_ecdhe_rsa_with_aes_256_gcm_sha384.go
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+
+// TLSEcdheRsaWithAes256GcmSha384 implements the TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 CipherSuite
+type TLSEcdheRsaWithAes256GcmSha384 struct {
+ TLSEcdheEcdsaWithAes256GcmSha384
+}
+
+// CertificateType returns what type of certificate this CipherSuite exchanges
+func (c *TLSEcdheRsaWithAes256GcmSha384) CertificateType() clientcertificate.Type {
+ return clientcertificate.RSASign
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSEcdheRsaWithAes256GcmSha384) ID() ID {
+ return TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+}
+
+func (c *TLSEcdheRsaWithAes256GcmSha384) String() string {
+ return "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go
new file mode 100644
index 0000000..7336ad9
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_cbc_sha256.go
@@ -0,0 +1,113 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "crypto/sha256"
+ "fmt"
+ "hash"
+ "sync/atomic"
+
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// TLSPskWithAes128CbcSha256 implements the TLS_PSK_WITH_AES_128_CBC_SHA256 CipherSuite
+type TLSPskWithAes128CbcSha256 struct {
+ cbc atomic.Value // *cryptoCBC
+}
+
+// CertificateType returns what type of certificate this CipherSuite exchanges
+func (c *TLSPskWithAes128CbcSha256) CertificateType() clientcertificate.Type {
+ return clientcertificate.Type(0)
+}
+
+// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
+func (c *TLSPskWithAes128CbcSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
+ return KeyExchangeAlgorithmPsk
+}
+
+// ECC uses Elliptic Curve Cryptography
+func (c *TLSPskWithAes128CbcSha256) ECC() bool {
+ return false
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSPskWithAes128CbcSha256) ID() ID {
+ return TLS_PSK_WITH_AES_128_CBC_SHA256
+}
+
+func (c *TLSPskWithAes128CbcSha256) String() string {
+ return "TLS_PSK_WITH_AES_128_CBC_SHA256"
+}
+
+// HashFunc returns the hashing func for this CipherSuite
+func (c *TLSPskWithAes128CbcSha256) HashFunc() func() hash.Hash {
+ return sha256.New
+}
+
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *TLSPskWithAes128CbcSha256) AuthenticationType() AuthenticationType {
+ return AuthenticationTypePreSharedKey
+}
+
+// IsInitialized returns if the CipherSuite has keying material and can
+// encrypt/decrypt packets
+func (c *TLSPskWithAes128CbcSha256) IsInitialized() bool {
+ return c.cbc.Load() != nil
+}
+
+// Init initializes the internal Cipher with keying material
+func (c *TLSPskWithAes128CbcSha256) Init(masterSecret, clientRandom, serverRandom []byte, isClient bool) error {
+ const (
+ prfMacLen = 32
+ prfKeyLen = 16
+ prfIvLen = 16
+ )
+
+ keys, err := prf.GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom, prfMacLen, prfKeyLen, prfIvLen, c.HashFunc())
+ if err != nil {
+ return err
+ }
+
+ var cbc *ciphersuite.CBC
+ if isClient {
+ cbc, err = ciphersuite.NewCBC(
+ keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
+ keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
+ c.HashFunc(),
+ )
+ } else {
+ cbc, err = ciphersuite.NewCBC(
+ keys.ServerWriteKey, keys.ServerWriteIV, keys.ServerMACKey,
+ keys.ClientWriteKey, keys.ClientWriteIV, keys.ClientMACKey,
+ c.HashFunc(),
+ )
+ }
+ c.cbc.Store(cbc)
+
+ return err
+}
+
+// Encrypt encrypts a single TLS RecordLayer
+func (c *TLSPskWithAes128CbcSha256) Encrypt(pkt *recordlayer.RecordLayer, raw []byte) ([]byte, error) {
+ cipherSuite, ok := c.cbc.Load().(*ciphersuite.CBC)
+ if !ok {
+ return nil, fmt.Errorf("%w, unable to encrypt", errCipherSuiteNotInit)
+ }
+
+ return cipherSuite.Encrypt(pkt, raw)
+}
+
+// Decrypt decrypts a single TLS RecordLayer
+func (c *TLSPskWithAes128CbcSha256) Decrypt(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)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go
new file mode 100644
index 0000000..1ded09b
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm.go
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+)
+
+// NewTLSPskWithAes128Ccm returns the TLS_PSK_WITH_AES_128_CCM CipherSuite
+func NewTLSPskWithAes128Ccm() *Aes128Ccm {
+ return newAes128Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_128_CCM, true, ciphersuite.CCMTagLength, KeyExchangeAlgorithmPsk, false)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go
new file mode 100644
index 0000000..4781970
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_ccm8.go
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+)
+
+// NewTLSPskWithAes128Ccm8 returns the TLS_PSK_WITH_AES_128_CCM_8 CipherSuite
+func NewTLSPskWithAes128Ccm8() *Aes128Ccm {
+ return newAes128Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_128_CCM_8, true, ciphersuite.CCMTagLength8, KeyExchangeAlgorithmPsk, false)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go
new file mode 100644
index 0000000..8ab5b89
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_128_gcm_sha256.go
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+
+// TLSPskWithAes128GcmSha256 implements the TLS_PSK_WITH_AES_128_GCM_SHA256 CipherSuite
+type TLSPskWithAes128GcmSha256 struct {
+ TLSEcdheEcdsaWithAes128GcmSha256
+}
+
+// CertificateType returns what type of certificate this CipherSuite exchanges
+func (c *TLSPskWithAes128GcmSha256) CertificateType() clientcertificate.Type {
+ return clientcertificate.Type(0)
+}
+
+// KeyExchangeAlgorithm controls what key exchange algorithm is using during the handshake
+func (c *TLSPskWithAes128GcmSha256) KeyExchangeAlgorithm() KeyExchangeAlgorithm {
+ return KeyExchangeAlgorithmPsk
+}
+
+// ID returns the ID of the CipherSuite
+func (c *TLSPskWithAes128GcmSha256) ID() ID {
+ return TLS_PSK_WITH_AES_128_GCM_SHA256
+}
+
+func (c *TLSPskWithAes128GcmSha256) String() string {
+ return "TLS_PSK_WITH_AES_128_GCM_SHA256"
+}
+
+// AuthenticationType controls what authentication method is using during the handshake
+func (c *TLSPskWithAes128GcmSha256) AuthenticationType() AuthenticationType {
+ return AuthenticationTypePreSharedKey
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_256_ccm8.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_256_ccm8.go
new file mode 100644
index 0000000..32d5030
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/tls_psk_with_aes_256_ccm8.go
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "github.com/pion/dtls/v2/pkg/crypto/ciphersuite"
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+)
+
+// NewTLSPskWithAes256Ccm8 returns the TLS_PSK_WITH_AES_256_CCM_8 CipherSuite
+func NewTLSPskWithAes256Ccm8() *Aes256Ccm {
+ return newAes256Ccm(clientcertificate.Type(0), TLS_PSK_WITH_AES_256_CCM_8, true, ciphersuite.CCMTagLength8, KeyExchangeAlgorithmPsk, false)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/types/authentication_type.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/types/authentication_type.go
new file mode 100644
index 0000000..2da21e6
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/types/authentication_type.go
@@ -0,0 +1,14 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package types
+
+// AuthenticationType controls what authentication method is using during the handshake
+type AuthenticationType int
+
+// AuthenticationType Enums
+const (
+ AuthenticationTypeCertificate AuthenticationType = iota + 1
+ AuthenticationTypePreSharedKey
+ AuthenticationTypeAnonymous
+)
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/types/key_exchange_algorithm.go b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/types/key_exchange_algorithm.go
new file mode 100644
index 0000000..c2c3911
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/ciphersuite/types/key_exchange_algorithm.go
@@ -0,0 +1,20 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package types provides types for TLS Ciphers
+package types
+
+// KeyExchangeAlgorithm controls what exchange algorithm was chosen.
+type KeyExchangeAlgorithm int
+
+// KeyExchangeAlgorithm Bitmask
+const (
+ KeyExchangeAlgorithmNone KeyExchangeAlgorithm = 0
+ KeyExchangeAlgorithmPsk KeyExchangeAlgorithm = iota << 1
+ KeyExchangeAlgorithmEcdhe
+)
+
+// Has check if keyExchangeAlgorithm is supported.
+func (a KeyExchangeAlgorithm) Has(v KeyExchangeAlgorithm) bool {
+ return (a & v) == v
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/closer/closer.go b/server/vendor/github.com/pion/dtls/v2/internal/closer/closer.go
new file mode 100644
index 0000000..bfa171c
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/closer/closer.go
@@ -0,0 +1,48 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package closer provides signaling channel for shutdown
+package closer
+
+import (
+ "context"
+)
+
+// Closer allows for each signaling a channel for shutdown
+type Closer struct {
+ ctx context.Context
+ closeFunc func()
+}
+
+// NewCloser creates a new instance of Closer
+func NewCloser() *Closer {
+ ctx, closeFunc := context.WithCancel(context.Background())
+ return &Closer{
+ ctx: ctx,
+ closeFunc: closeFunc,
+ }
+}
+
+// NewCloserWithParent creates a new instance of Closer with a parent context
+func NewCloserWithParent(ctx context.Context) *Closer {
+ ctx, closeFunc := context.WithCancel(ctx)
+ return &Closer{
+ ctx: ctx,
+ closeFunc: closeFunc,
+ }
+}
+
+// Done returns a channel signaling when it is done
+func (c *Closer) Done() <-chan struct{} {
+ return c.ctx.Done()
+}
+
+// Err returns an error of the context
+func (c *Closer) Err() error {
+ return c.ctx.Err()
+}
+
+// Close sends a signal to trigger the ctx done channel
+func (c *Closer) Close() {
+ c.closeFunc()
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/internal/util/util.go b/server/vendor/github.com/pion/dtls/v2/internal/util/util.go
new file mode 100644
index 0000000..685910f
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/internal/util/util.go
@@ -0,0 +1,42 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package util contains small helpers used across the repo
+package util
+
+import (
+ "encoding/binary"
+)
+
+// BigEndianUint24 returns the value of a big endian uint24
+func BigEndianUint24(raw []byte) uint32 {
+ if len(raw) < 3 {
+ return 0
+ }
+
+ rawCopy := make([]byte, 4)
+ copy(rawCopy[1:], raw)
+ return binary.BigEndian.Uint32(rawCopy)
+}
+
+// PutBigEndianUint24 encodes a uint24 and places into out
+func PutBigEndianUint24(out []byte, in uint32) {
+ tmp := make([]byte, 4)
+ binary.BigEndian.PutUint32(tmp, in)
+ copy(out, tmp[1:])
+}
+
+// PutBigEndianUint48 encodes a uint64 and places into out
+func PutBigEndianUint48(out []byte, in uint64) {
+ tmp := make([]byte, 8)
+ binary.BigEndian.PutUint64(tmp, in)
+ copy(out, tmp[2:])
+}
+
+// Max returns the larger value
+func Max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/listener.go b/server/vendor/github.com/pion/dtls/v2/listener.go
new file mode 100644
index 0000000..190d236
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/listener.go
@@ -0,0 +1,83 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "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
+func Listen(network string, laddr *net.UDPAddr, config *Config) (net.Listener, error) {
+ if err := validateConfig(config); err != nil {
+ return nil, err
+ }
+
+ lc := udp.ListenConfig{
+ AcceptFilter: func(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
+ },
+ }
+ parent, err := lc.Listen(network, laddr)
+ if err != nil {
+ return nil, err
+ }
+ return &listener{
+ config: config,
+ parent: parent,
+ }, nil
+}
+
+// NewListener creates a DTLS listener which accepts connections from an inner Listener.
+func NewListener(inner net.Listener, config *Config) (net.Listener, error) {
+ if err := validateConfig(config); err != nil {
+ return nil, err
+ }
+
+ return &listener{
+ config: config,
+ parent: inner,
+ }, nil
+}
+
+// listener represents a DTLS listener
+type listener struct {
+ config *Config
+ parent net.Listener
+}
+
+// Accept waits for and returns the next connection to the listener.
+// You have to either close or read on all connection that are created.
+// 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()
+ if err != nil {
+ return nil, err
+ }
+ return Server(c, l.config)
+}
+
+// Close closes the listener.
+// Any blocked Accept operations will be unblocked and return errors.
+// Already Accepted connections are not closed.
+func (l *listener) Close() error {
+ return l.parent.Close()
+}
+
+// Addr returns the listener's network address.
+func (l *listener) Addr() net.Addr {
+ return l.parent.Addr()
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/packet.go b/server/vendor/github.com/pion/dtls/v2/packet.go
new file mode 100644
index 0000000..55d6272
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/packet.go
@@ -0,0 +1,12 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+
+type packet struct {
+ record *recordlayer.RecordLayer
+ shouldEncrypt bool
+ resetLocalSequenceNumber bool
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go
new file mode 100644
index 0000000..d6e6fc4
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ccm/ccm.go
@@ -0,0 +1,254 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package ccm implements a CCM, Counter with CBC-MAC
+// as per RFC 3610.
+//
+// See https://tools.ietf.org/html/rfc3610
+//
+// This code was lifted from https://github.com/bocajim/dtls/blob/a3300364a283fcb490d28a93d7fcfa7ba437fbbe/ccm/ccm.go
+// and as such was not written by the Pions authors. Like Pions this
+// code is licensed under MIT.
+//
+// A request for including CCM into the Go standard library
+// can be found as issue #27484 on the https://github.com/golang/go/
+// repository.
+package ccm
+
+import (
+ "crypto/cipher"
+ "crypto/subtle"
+ "encoding/binary"
+ "errors"
+ "math"
+)
+
+// ccm represents a Counter with CBC-MAC with a specific key.
+type ccm struct {
+ b cipher.Block
+ M uint8
+ L uint8
+}
+
+const ccmBlockSize = 16
+
+// CCM is a block cipher in Counter with CBC-MAC mode.
+// Providing authenticated encryption with associated data via the cipher.AEAD interface.
+type CCM interface {
+ cipher.AEAD
+ // MaxLength returns the maxium length of plaintext in calls to Seal.
+ // The maximum length of ciphertext in calls to Open is MaxLength()+Overhead().
+ // The maximum length is related to CCM's `L` parameter (15-noncesize) and
+ // is 1<<(8*L) - 1 (but also limited by the maxium size of an int).
+ MaxLength() int
+}
+
+var (
+ errInvalidBlockSize = errors.New("ccm: NewCCM requires 128-bit block cipher")
+ errInvalidTagSize = errors.New("ccm: tagsize must be 4, 6, 8, 10, 12, 14, or 16")
+ errInvalidNonceSize = errors.New("ccm: invalid nonce size")
+)
+
+// NewCCM returns the given 128-bit block cipher wrapped in CCM.
+// The tagsize must be an even integer between 4 and 16 inclusive
+// and is used as CCM's `M` parameter.
+// The noncesize must be an integer between 7 and 13 inclusive,
+// 15-noncesize is used as CCM's `L` parameter.
+func NewCCM(b cipher.Block, tagsize, noncesize int) (CCM, error) {
+ if b.BlockSize() != ccmBlockSize {
+ return nil, errInvalidBlockSize
+ }
+ if tagsize < 4 || tagsize > 16 || tagsize&1 != 0 {
+ return nil, errInvalidTagSize
+ }
+ lensize := 15 - noncesize
+ if lensize < 2 || lensize > 8 {
+ return nil, errInvalidNonceSize
+ }
+ c := &ccm{b: b, M: uint8(tagsize), L: uint8(lensize)}
+ return c, nil
+}
+
+func (c *ccm) NonceSize() int { return 15 - int(c.L) }
+func (c *ccm) Overhead() int { return int(c.M) }
+func (c *ccm) MaxLength() int { return maxlen(c.L, c.Overhead()) }
+
+func maxlen(l uint8, tagsize int) int {
+ max := (uint64(1) << (8 * l)) - 1
+ if m64 := uint64(math.MaxInt64) - uint64(tagsize); l > 8 || max > m64 {
+ max = m64 // The maximum lentgh on a 64bit arch
+ }
+ if max != uint64(int(max)) {
+ return math.MaxInt32 - tagsize // We have only 32bit int's
+ }
+ return int(max)
+}
+
+// MaxNonceLength returns the maximum nonce length for a given plaintext length.
+// A return value <= 0 indicates that plaintext length is too large for
+// any nonce length.
+func MaxNonceLength(pdatalen int) int {
+ const tagsize = 16
+ for L := 2; L <= 8; L++ {
+ if maxlen(uint8(L), tagsize) >= pdatalen {
+ return 15 - L
+ }
+ }
+ return 0
+}
+
+func (c *ccm) cbcRound(mac, data []byte) {
+ for i := 0; i < ccmBlockSize; i++ {
+ mac[i] ^= data[i]
+ }
+ c.b.Encrypt(mac, mac)
+}
+
+func (c *ccm) cbcData(mac, data []byte) {
+ for len(data) >= ccmBlockSize {
+ c.cbcRound(mac, data[:ccmBlockSize])
+ data = data[ccmBlockSize:]
+ }
+ if len(data) > 0 {
+ var block [ccmBlockSize]byte
+ copy(block[:], data)
+ c.cbcRound(mac, block[:])
+ }
+}
+
+var errPlaintextTooLong = errors.New("ccm: plaintext too large")
+
+func (c *ccm) tag(nonce, plaintext, adata []byte) ([]byte, error) {
+ var mac [ccmBlockSize]byte
+
+ if len(adata) > 0 {
+ mac[0] |= 1 << 6
+ }
+ mac[0] |= (c.M - 2) << 2
+ mac[0] |= c.L - 1
+ if len(nonce) != c.NonceSize() {
+ return nil, errInvalidNonceSize
+ }
+ if len(plaintext) > c.MaxLength() {
+ return nil, errPlaintextTooLong
+ }
+ binary.BigEndian.PutUint64(mac[ccmBlockSize-8:], uint64(len(plaintext)))
+ copy(mac[1:ccmBlockSize-c.L], nonce)
+ c.b.Encrypt(mac[:], mac[:])
+
+ var block [ccmBlockSize]byte
+ if n := uint64(len(adata)); n > 0 {
+ // First adata block includes adata length
+ i := 2
+ if n <= 0xfeff {
+ binary.BigEndian.PutUint16(block[:i], uint16(n))
+ } else {
+ block[0] = 0xfe
+ block[1] = 0xff
+ if n < uint64(1<<32) {
+ i = 2 + 4
+ binary.BigEndian.PutUint32(block[2:i], uint32(n))
+ } else {
+ i = 2 + 8
+ binary.BigEndian.PutUint64(block[2:i], n)
+ }
+ }
+ i = copy(block[i:], adata)
+ c.cbcRound(mac[:], block[:])
+ c.cbcData(mac[:], adata[i:])
+ }
+
+ if len(plaintext) > 0 {
+ c.cbcData(mac[:], plaintext)
+ }
+
+ return mac[:c.M], nil
+}
+
+// sliceForAppend takes a slice and a requested number of bytes. It returns a
+// slice with the contents of the given slice followed by that many bytes and a
+// second slice that aliases into it and contains only the extra bytes. If the
+// original slice has sufficient capacity then no allocation is performed.
+// From crypto/cipher/gcm.go
+func sliceForAppend(in []byte, n int) (head, tail []byte) {
+ if total := len(in) + n; cap(in) >= total {
+ head = in[:total]
+ } else {
+ head = make([]byte, total)
+ copy(head, in)
+ }
+ tail = head[len(in):]
+ return
+}
+
+// Seal encrypts and authenticates plaintext, authenticates the
+// additional data and appends the result to dst, returning the updated
+// slice. The nonce must be NonceSize() bytes long and unique for all
+// time, for a given key.
+// The plaintext must be no longer than MaxLength() bytes long.
+//
+// The plaintext and dst may alias exactly or not at all.
+func (c *ccm) Seal(dst, nonce, plaintext, adata []byte) []byte {
+ tag, err := c.tag(nonce, plaintext, adata)
+ if err != nil {
+ // The cipher.AEAD interface doesn't allow for an error return.
+ panic(err) // nolint
+ }
+
+ var iv, s0 [ccmBlockSize]byte
+ iv[0] = c.L - 1
+ copy(iv[1:ccmBlockSize-c.L], nonce)
+ c.b.Encrypt(s0[:], iv[:])
+ for i := 0; i < int(c.M); i++ {
+ tag[i] ^= s0[i]
+ }
+ iv[len(iv)-1] |= 1
+ stream := cipher.NewCTR(c.b, iv[:])
+ ret, out := sliceForAppend(dst, len(plaintext)+int(c.M))
+ stream.XORKeyStream(out, plaintext)
+ copy(out[len(plaintext):], tag)
+ return ret
+}
+
+var (
+ errOpen = errors.New("ccm: message authentication failed")
+ errCiphertextTooShort = errors.New("ccm: ciphertext too short")
+ errCiphertextTooLong = errors.New("ccm: ciphertext too long")
+)
+
+func (c *ccm) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) {
+ if len(ciphertext) < int(c.M) {
+ return nil, errCiphertextTooShort
+ }
+ if len(ciphertext) > c.MaxLength()+c.Overhead() {
+ return nil, errCiphertextTooLong
+ }
+
+ tag := make([]byte, int(c.M))
+ copy(tag, ciphertext[len(ciphertext)-int(c.M):])
+ ciphertextWithoutTag := ciphertext[:len(ciphertext)-int(c.M)]
+
+ var iv, s0 [ccmBlockSize]byte
+ iv[0] = c.L - 1
+ copy(iv[1:ccmBlockSize-c.L], nonce)
+ c.b.Encrypt(s0[:], iv[:])
+ for i := 0; i < int(c.M); i++ {
+ tag[i] ^= s0[i]
+ }
+ iv[len(iv)-1] |= 1
+ stream := cipher.NewCTR(c.b, iv[:])
+
+ // Cannot decrypt directly to dst since we're not supposed to
+ // reveal the plaintext to the caller if authentication fails.
+ plaintext := make([]byte, len(ciphertextWithoutTag))
+ stream.XORKeyStream(plaintext, ciphertextWithoutTag)
+ expectedTag, err := c.tag(nonce, plaintext, adata)
+ if err != nil {
+ return nil, err
+ }
+
+ if subtle.ConstantTimeCompare(tag, expectedTag) != 1 {
+ return nil, errOpen
+ }
+ return append(dst, plaintext...), nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go
new file mode 100644
index 0000000..460fb14
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/cbc.go
@@ -0,0 +1,177 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import ( //nolint:gci
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/hmac"
+ "crypto/rand"
+ "encoding/binary"
+ "hash"
+
+ "github.com/pion/dtls/v2/internal/util"
+ "github.com/pion/dtls/v2/pkg/crypto/prf"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// block ciphers using cipher block chaining.
+type cbcMode interface {
+ cipher.BlockMode
+ SetIV([]byte)
+}
+
+// CBC Provides an API to Encrypt/Decrypt DTLS 1.2 Packets
+type CBC struct {
+ writeCBC, readCBC cbcMode
+ writeMac, readMac []byte
+ h prf.HashFunc
+}
+
+// NewCBC creates a DTLS CBC Cipher
+func NewCBC(localKey, localWriteIV, localMac, remoteKey, remoteWriteIV, remoteMac []byte, h prf.HashFunc) (*CBC, error) {
+ writeBlock, err := aes.NewCipher(localKey)
+ if err != nil {
+ return nil, err
+ }
+
+ readBlock, err := aes.NewCipher(remoteKey)
+ if err != nil {
+ return nil, err
+ }
+
+ writeCBC, ok := cipher.NewCBCEncrypter(writeBlock, localWriteIV).(cbcMode)
+ if !ok {
+ return nil, errFailedToCast
+ }
+
+ readCBC, ok := cipher.NewCBCDecrypter(readBlock, remoteWriteIV).(cbcMode)
+ if !ok {
+ return nil, errFailedToCast
+ }
+
+ return &CBC{
+ writeCBC: writeCBC,
+ writeMac: localMac,
+
+ readCBC: readCBC,
+ readMac: remoteMac,
+ h: h,
+ }, nil
+}
+
+// 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]
+ 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)
+ if err != nil {
+ return nil, err
+ }
+ payload = append(payload, MAC...)
+
+ // Generate + Append padding
+ padding := make([]byte, blockSize-len(payload)%blockSize)
+ paddingLen := len(padding)
+ for i := 0; i < paddingLen; i++ {
+ padding[i] = byte(paddingLen - 1)
+ }
+ payload = append(payload, padding...)
+
+ // Generate IV
+ iv := make([]byte, blockSize)
+ if _, err := rand.Read(iv); err != nil {
+ return nil, err
+ }
+
+ // Set IV + Encrypt + Prepend IV
+ c.writeCBC.SetIV(iv)
+ c.writeCBC.CryptBlocks(payload, payload)
+ payload = append(iv, payload...)
+
+ // Prepend unencrypte 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))
+
+ return raw, nil
+}
+
+// Decrypt decrypts a DTLS RecordLayer message
+func (c *CBC) Decrypt(in []byte) ([]byte, error) {
+ body := in[recordlayer.HeaderSize:]
+ blockSize := c.readCBC.BlockSize()
+ mac := c.h()
+
+ var h recordlayer.Header
+ err := h.Unmarshal(in)
+ switch {
+ case err != nil:
+ return nil, err
+ case h.ContentType == protocol.ContentTypeChangeCipherSpec:
+ // Nothing to encrypt with ChangeCipherSpec
+ return in, nil
+ case len(body)%blockSize != 0 || len(body) < blockSize+util.Max(mac.Size()+1, blockSize):
+ return nil, errNotEnoughRoomForNonce
+ }
+
+ // Set + remove per record IV
+ c.readCBC.SetIV(body[:blockSize])
+ body = body[blockSize:]
+
+ // Decrypt
+ c.readCBC.CryptBlocks(body, body)
+
+ // Padding+MAC needs to be checked in constant time
+ // Otherwise we reveal information about the level of correctness
+ paddingLen, paddingGood := examinePadding(body)
+ if paddingGood != 255 {
+ return nil, errInvalidMAC
+ }
+
+ macSize := mac.Size()
+ if len(body) < macSize {
+ return nil, errInvalidMAC
+ }
+
+ 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)
+
+ // Compute Local MAC and compare
+ if err != nil || !hmac.Equal(actualMAC, expectedMAC) {
+ return nil, errInvalidMAC
+ }
+
+ return append(in[:recordlayer.HeaderSize], 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) {
+ h := hmac.New(hf, key)
+
+ msg := make([]byte, 13)
+
+ binary.BigEndian.PutUint16(msg, epoch)
+ util.PutBigEndianUint48(msg[2:], sequenceNumber)
+ msg[8] = byte(contentType)
+ msg[9] = protocolVersion.Major
+ msg[10] = protocolVersion.Minor
+ binary.BigEndian.PutUint16(msg[11:], uint16(len(payload)))
+
+ if _, err := h.Write(msg); err != nil {
+ return nil, err
+ } else if _, err := h.Write(payload); err != nil {
+ return nil, err
+ }
+
+ return h.Sum(nil), nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go
new file mode 100644
index 0000000..24050dc
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ccm.go
@@ -0,0 +1,107 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "crypto/aes"
+ "crypto/rand"
+ "encoding/binary"
+ "fmt"
+
+ "github.com/pion/dtls/v2/pkg/crypto/ccm"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+// CCMTagLen is the length of Authentication Tag
+type CCMTagLen int
+
+// CCM Enums
+const (
+ CCMTagLength8 CCMTagLen = 8
+ CCMTagLength CCMTagLen = 16
+ ccmNonceLength = 12
+)
+
+// CCM Provides an API to Encrypt/Decrypt DTLS 1.2 Packets
+type CCM struct {
+ localCCM, remoteCCM ccm.CCM
+ localWriteIV, remoteWriteIV []byte
+ tagLen CCMTagLen
+}
+
+// NewCCM creates a DTLS GCM Cipher
+func NewCCM(tagLen CCMTagLen, localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*CCM, error) {
+ localBlock, err := aes.NewCipher(localKey)
+ if err != nil {
+ return nil, err
+ }
+ localCCM, err := ccm.NewCCM(localBlock, int(tagLen), ccmNonceLength)
+ if err != nil {
+ return nil, err
+ }
+
+ remoteBlock, err := aes.NewCipher(remoteKey)
+ if err != nil {
+ return nil, err
+ }
+ remoteCCM, err := ccm.NewCCM(remoteBlock, int(tagLen), ccmNonceLength)
+ if err != nil {
+ return nil, err
+ }
+
+ return &CCM{
+ localCCM: localCCM,
+ localWriteIV: localWriteIV,
+ remoteCCM: remoteCCM,
+ remoteWriteIV: remoteWriteIV,
+ tagLen: tagLen,
+ }, nil
+}
+
+// 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]
+
+ 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))
+ 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))
+ 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:
+ return nil, err
+ case h.ContentType == protocol.ContentTypeChangeCipherSpec:
+ // Nothing to encrypt with ChangeCipherSpec
+ return in, nil
+ case len(in) <= (8 + recordlayer.HeaderSize):
+ return nil, errNotEnoughRoomForNonce
+ }
+
+ nonce := append(append([]byte{}, c.remoteWriteIV[:4]...), in[recordlayer.HeaderSize:recordlayer.HeaderSize+8]...)
+ out := in[recordlayer.HeaderSize+8:]
+
+ additionalData := generateAEADAdditionalData(&h, len(out)-int(c.tagLen))
+ out, err = c.remoteCCM.Open(out[:0], nonce, out, additionalData)
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v", errDecryptPacket, err) //nolint:errorlint
+ }
+ return append(in[:recordlayer.HeaderSize], out...), nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go
new file mode 100644
index 0000000..9d9fb74
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/ciphersuite.go
@@ -0,0 +1,76 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package ciphersuite provides the crypto operations needed for a DTLS CipherSuite
+package ciphersuite
+
+import (
+ "encoding/binary"
+ "errors"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+var (
+ errNotEnoughRoomForNonce = &protocol.InternalError{Err: errors.New("buffer not long enough to contain nonce")} //nolint:goerr113
+ errDecryptPacket = &protocol.TemporaryError{Err: errors.New("failed to decrypt packet")} //nolint:goerr113
+ errInvalidMAC = &protocol.TemporaryError{Err: errors.New("invalid mac")} //nolint:goerr113
+ errFailedToCast = &protocol.FatalError{Err: errors.New("failed to cast")} //nolint:goerr113
+)
+
+func generateAEADAdditionalData(h *recordlayer.Header, payloadLen int) []byte {
+ 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)
+ binary.BigEndian.PutUint16(additionalData[:], h.Epoch)
+ additionalData[8] = byte(h.ContentType)
+ additionalData[9] = h.Version.Major
+ additionalData[10] = h.Version.Minor
+ binary.BigEndian.PutUint16(additionalData[len(additionalData)-2:], uint16(payloadLen))
+
+ return additionalData[:]
+}
+
+// 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.
+//
+// https://github.com/golang/go/blob/039c2081d1178f90a8fa2f4e6958693129f8de33/src/crypto/tls/conn.go#L245
+func examinePadding(payload []byte) (toRemove int, good byte) {
+ if len(payload) < 1 {
+ return 0, 0
+ }
+
+ paddingLen := payload[len(payload)-1]
+ t := uint(len(payload)-1) - uint(paddingLen)
+ // if len(payload) >= (paddingLen - 1) then the MSB of t is zero
+ good = byte(int32(^t) >> 31)
+
+ // The maximum possible padding length plus the actual length field
+ toCheck := 256
+ // The length of the padded data is public, so we can use an if here
+ if toCheck > len(payload) {
+ toCheck = len(payload)
+ }
+
+ for i := 0; i < toCheck; i++ {
+ t := uint(paddingLen) - uint(i)
+ // if i <= paddingLen then the MSB of t is zero
+ mask := byte(int32(^t) >> 31)
+ b := payload[len(payload)-1-i]
+ good &^= mask&paddingLen ^ mask&b
+ }
+
+ // We AND together the bits of good and replicate the result across
+ // all the bits.
+ good &= good << 4
+ good &= good << 2
+ good &= good << 1
+ good = uint8(int8(good) >> 7)
+
+ toRemove = int(paddingLen) + 1
+
+ return toRemove, good
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go
new file mode 100644
index 0000000..c0fd1f7
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/ciphersuite/gcm.go
@@ -0,0 +1,103 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ciphersuite
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/rand"
+ "encoding/binary"
+ "fmt"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/recordlayer"
+)
+
+const (
+ gcmTagLength = 16
+ gcmNonceLength = 12
+)
+
+// GCM Provides an API to Encrypt/Decrypt DTLS 1.2 Packets
+type GCM struct {
+ localGCM, remoteGCM cipher.AEAD
+ localWriteIV, remoteWriteIV []byte
+}
+
+// NewGCM creates a DTLS GCM Cipher
+func NewGCM(localKey, localWriteIV, remoteKey, remoteWriteIV []byte) (*GCM, error) {
+ localBlock, err := aes.NewCipher(localKey)
+ if err != nil {
+ return nil, err
+ }
+ localGCM, err := cipher.NewGCM(localBlock)
+ if err != nil {
+ return nil, err
+ }
+
+ remoteBlock, err := aes.NewCipher(remoteKey)
+ if err != nil {
+ return nil, err
+ }
+ remoteGCM, err := cipher.NewGCM(remoteBlock)
+ if err != nil {
+ return nil, err
+ }
+
+ return &GCM{
+ localGCM: localGCM,
+ localWriteIV: localWriteIV,
+ remoteGCM: remoteGCM,
+ remoteWriteIV: remoteWriteIV,
+ }, nil
+}
+
+// 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]
+
+ nonce := make([]byte, gcmNonceLength)
+ copy(nonce, g.localWriteIV[:4])
+ if _, err := rand.Read(nonce[4:]); err != nil {
+ return nil, err
+ }
+
+ 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)
+ copy(r[len(raw):], nonce[4:])
+ 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))
+ return r, nil
+}
+
+// Decrypt decrypts a DTLS RecordLayer message
+func (g *GCM) Decrypt(in []byte) ([]byte, error) {
+ var h recordlayer.Header
+ err := h.Unmarshal(in)
+ switch {
+ case err != nil:
+ return nil, err
+ case h.ContentType == protocol.ContentTypeChangeCipherSpec:
+ // Nothing to encrypt with ChangeCipherSpec
+ return in, nil
+ case len(in) <= (8 + recordlayer.HeaderSize):
+ 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:]
+
+ 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
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go
new file mode 100644
index 0000000..ddfa39e
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/clientcertificate/client_certificate.go
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package clientcertificate provides all the support Client Certificate types
+package clientcertificate
+
+// Type is used to communicate what
+// type of certificate is being transported
+//
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-2
+type Type byte
+
+// ClientCertificateType enums
+const (
+ RSASign Type = 1
+ ECDSASign Type = 64
+)
+
+// Types returns all valid ClientCertificate Types
+func Types() map[Type]bool {
+ return map[Type]bool{
+ RSASign: true,
+ ECDSASign: true,
+ }
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go
new file mode 100644
index 0000000..1265238
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/elliptic/elliptic.go
@@ -0,0 +1,115 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package elliptic provides elliptic curve cryptography for DTLS
+package elliptic
+
+import (
+ "crypto/elliptic"
+ "crypto/rand"
+ "errors"
+ "fmt"
+
+ "golang.org/x/crypto/curve25519"
+)
+
+var errInvalidNamedCurve = errors.New("invalid named curve")
+
+// CurvePointFormat is used to represent the IANA registered curve points
+//
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9
+type CurvePointFormat byte
+
+// CurvePointFormat enums
+const (
+ CurvePointFormatUncompressed CurvePointFormat = 0
+)
+
+// Keypair is a Curve with a Private/Public Keypair
+type Keypair struct {
+ Curve Curve
+ PublicKey []byte
+ PrivateKey []byte
+}
+
+// CurveType is used to represent the IANA registered curve types for TLS
+//
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-10
+type CurveType byte
+
+// CurveType enums
+const (
+ CurveTypeNamedCurve CurveType = 0x03
+)
+
+// CurveTypes returns all known curves
+func CurveTypes() map[CurveType]struct{} {
+ return map[CurveType]struct{}{
+ CurveTypeNamedCurve: {},
+ }
+}
+
+// Curve is used to represent the IANA registered curves for TLS
+//
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8
+type Curve uint16
+
+// Curve enums
+const (
+ P256 Curve = 0x0017
+ P384 Curve = 0x0018
+ X25519 Curve = 0x001d
+)
+
+func (c Curve) String() string {
+ switch c {
+ case P256:
+ return "P-256"
+ case P384:
+ return "P-384"
+ case X25519:
+ return "X25519"
+ }
+ return fmt.Sprintf("%#x", uint16(c))
+}
+
+// Curves returns all curves we implement
+func Curves() map[Curve]bool {
+ return map[Curve]bool{
+ X25519: true,
+ P256: true,
+ P384: true,
+ }
+}
+
+// GenerateKeypair generates a keypair for the given Curve
+func GenerateKeypair(c Curve) (*Keypair, error) {
+ switch c { //nolint:revive
+ case X25519:
+ tmp := make([]byte, 32)
+ if _, err := rand.Read(tmp); err != nil {
+ return nil, err
+ }
+
+ var public, private [32]byte
+ copy(private[:], tmp)
+
+ curve25519.ScalarBaseMult(&public, &private)
+ return &Keypair{X25519, public[:], private[:]}, nil
+ case P256:
+ return ellipticCurveKeypair(P256, elliptic.P256(), elliptic.P256())
+ case P384:
+ return ellipticCurveKeypair(P384, elliptic.P384(), elliptic.P384())
+ default:
+ return nil, errInvalidNamedCurve
+ }
+}
+
+func ellipticCurveKeypair(nc Curve, c1, c2 elliptic.Curve) (*Keypair, error) {
+ privateKey, x, y, err := elliptic.GenerateKey(c1, rand.Reader)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Keypair{nc, elliptic.Marshal(c2, x, y), privateKey}, nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go
new file mode 100644
index 0000000..7c66265
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/fingerprint.go
@@ -0,0 +1,53 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package fingerprint provides a helper to create fingerprint string from certificate
+package fingerprint
+
+import (
+ "crypto"
+ "crypto/x509"
+ "errors"
+ "fmt"
+)
+
+var (
+ errHashUnavailable = errors.New("fingerprint: hash algorithm is not linked into the binary")
+ errInvalidFingerprintLength = errors.New("fingerprint: invalid fingerprint length")
+)
+
+// Fingerprint creates a fingerprint for a certificate using the specified hash algorithm
+func Fingerprint(cert *x509.Certificate, algo crypto.Hash) (string, error) {
+ if !algo.Available() {
+ return "", errHashUnavailable
+ }
+ h := algo.New()
+ for i := 0; i < len(cert.Raw); {
+ n, _ := h.Write(cert.Raw[i:])
+ // Hash.Writer is specified to be never returning an error.
+ // https://golang.org/pkg/hash/#Hash
+ i += n
+ }
+ digest := []byte(fmt.Sprintf("%x", h.Sum(nil)))
+
+ digestlen := len(digest)
+ if digestlen == 0 {
+ return "", nil
+ }
+ if digestlen%2 != 0 {
+ return "", errInvalidFingerprintLength
+ }
+ res := make([]byte, digestlen>>1+digestlen-1)
+
+ pos := 0
+ for i, c := range digest {
+ res[pos] = c
+ pos++
+ if (i)%2 != 0 && i < digestlen-1 {
+ res[pos] = byte(':')
+ pos++
+ }
+ }
+
+ return string(res), nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go
new file mode 100644
index 0000000..3f988ff
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/fingerprint/hash.go
@@ -0,0 +1,41 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package fingerprint
+
+import (
+ "crypto"
+ "errors"
+ "strings"
+)
+
+var errInvalidHashAlgorithm = errors.New("fingerprint: invalid hash algorithm")
+
+func nameToHash() map[string]crypto.Hash {
+ return map[string]crypto.Hash{
+ "md5": crypto.MD5, // [RFC3279]
+ "sha-1": crypto.SHA1, // [RFC3279]
+ "sha-224": crypto.SHA224, // [RFC4055]
+ "sha-256": crypto.SHA256, // [RFC4055]
+ "sha-384": crypto.SHA384, // [RFC4055]
+ "sha-512": crypto.SHA512, // [RFC4055]
+ }
+}
+
+// HashFromString allows looking up a hash algorithm by it's string representation
+func HashFromString(s string) (crypto.Hash, error) {
+ if h, ok := nameToHash()[strings.ToLower(s)]; ok {
+ return h, nil
+ }
+ return 0, errInvalidHashAlgorithm
+}
+
+// StringFromHash allows looking up a string representation of the crypto.Hash.
+func StringFromHash(hash crypto.Hash) (string, error) {
+ for s, h := range nameToHash() {
+ if h == hash {
+ return s, nil
+ }
+ }
+ return "", errInvalidHashAlgorithm
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go
new file mode 100644
index 0000000..9966626
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/hash/hash.go
@@ -0,0 +1,129 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package hash provides TLS HashAlgorithm as defined in TLS 1.2
+package hash
+
+import ( //nolint:gci
+ "crypto"
+ "crypto/md5" //nolint:gosec
+ "crypto/sha1" //nolint:gosec
+ "crypto/sha256"
+ "crypto/sha512"
+)
+
+// Algorithm is used to indicate the hash algorithm used
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18
+type Algorithm uint16
+
+// Supported hash algorithms
+const (
+ None Algorithm = 0 // Blacklisted
+ MD5 Algorithm = 1 // Blacklisted
+ SHA1 Algorithm = 2 // Blacklisted
+ SHA224 Algorithm = 3
+ SHA256 Algorithm = 4
+ SHA384 Algorithm = 5
+ SHA512 Algorithm = 6
+ Ed25519 Algorithm = 8
+)
+
+// String makes hashAlgorithm printable
+func (a Algorithm) String() string {
+ switch a {
+ case None:
+ return "none"
+ case MD5:
+ return "md5" // [RFC3279]
+ case SHA1:
+ return "sha-1" // [RFC3279]
+ case SHA224:
+ return "sha-224" // [RFC4055]
+ case SHA256:
+ return "sha-256" // [RFC4055]
+ case SHA384:
+ return "sha-384" // [RFC4055]
+ case SHA512:
+ return "sha-512" // [RFC4055]
+ case Ed25519:
+ return "null"
+ default:
+ return "unknown or unsupported hash algorithm"
+ }
+}
+
+// Digest performs a digest on the passed value
+func (a Algorithm) Digest(b []byte) []byte {
+ switch a {
+ case None:
+ return nil
+ case MD5:
+ hash := md5.Sum(b) // #nosec
+ return hash[:]
+ case SHA1:
+ hash := sha1.Sum(b) // #nosec
+ return hash[:]
+ case SHA224:
+ hash := sha256.Sum224(b)
+ return hash[:]
+ case SHA256:
+ hash := sha256.Sum256(b)
+ return hash[:]
+ case SHA384:
+ hash := sha512.Sum384(b)
+ return hash[:]
+ case SHA512:
+ hash := sha512.Sum512(b)
+ return hash[:]
+ default:
+ return nil
+ }
+}
+
+// Insecure returns if the given HashAlgorithm is considered secure in DTLS 1.2
+func (a Algorithm) Insecure() bool {
+ switch a {
+ case None, MD5, SHA1:
+ return true
+ default:
+ return false
+ }
+}
+
+// CryptoHash returns the crypto.Hash implementation for the given HashAlgorithm
+func (a Algorithm) CryptoHash() crypto.Hash {
+ switch a {
+ case None:
+ return crypto.Hash(0)
+ case MD5:
+ return crypto.MD5
+ case SHA1:
+ return crypto.SHA1
+ case SHA224:
+ return crypto.SHA224
+ case SHA256:
+ return crypto.SHA256
+ case SHA384:
+ return crypto.SHA384
+ case SHA512:
+ return crypto.SHA512
+ case Ed25519:
+ return crypto.Hash(0)
+ default:
+ return crypto.Hash(0)
+ }
+}
+
+// Algorithms returns all the supported Hash Algorithms
+func Algorithms() map[Algorithm]struct{} {
+ return map[Algorithm]struct{}{
+ None: {},
+ MD5: {},
+ SHA1: {},
+ SHA224: {},
+ SHA256: {},
+ SHA384: {},
+ SHA512: {},
+ Ed25519: {},
+ }
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go
new file mode 100644
index 0000000..6e7b3ec
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/prf/prf.go
@@ -0,0 +1,255 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package prf implements TLS 1.2 Pseudorandom functions
+package prf
+
+import ( //nolint:gci
+ ellipticStdlib "crypto/elliptic"
+ "crypto/hmac"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "hash"
+ "math"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "golang.org/x/crypto/curve25519"
+)
+
+const (
+ masterSecretLabel = "master secret"
+ extendedMasterSecretLabel = "extended master secret"
+ keyExpansionLabel = "key expansion"
+ verifyDataClientLabel = "client finished"
+ verifyDataServerLabel = "server finished"
+)
+
+// HashFunc allows callers to decide what hash is used in PRF
+type HashFunc func() hash.Hash
+
+// EncryptionKeys is all the state needed for a TLS CipherSuite
+type EncryptionKeys struct {
+ MasterSecret []byte
+ ClientMACKey []byte
+ ServerMACKey []byte
+ ClientWriteKey []byte
+ ServerWriteKey []byte
+ ClientWriteIV []byte
+ ServerWriteIV []byte
+}
+
+var errInvalidNamedCurve = &protocol.FatalError{Err: errors.New("invalid named curve")} //nolint:goerr113
+
+func (e *EncryptionKeys) String() string {
+ return fmt.Sprintf(`encryptionKeys:
+- masterSecret: %#v
+- clientMACKey: %#v
+- serverMACKey: %#v
+- clientWriteKey: %#v
+- serverWriteKey: %#v
+- clientWriteIV: %#v
+- serverWriteIV: %#v
+`,
+ e.MasterSecret,
+ e.ClientMACKey,
+ e.ServerMACKey,
+ e.ClientWriteKey,
+ e.ServerWriteKey,
+ e.ClientWriteIV,
+ e.ServerWriteIV)
+}
+
+// PSKPreMasterSecret generates the PSK Premaster Secret
+// The premaster secret is formed as follows: if the PSK is N octets
+// long, concatenate a uint16 with the value N, N zero octets, a second
+// uint16 with the value N, and the PSK itself.
+//
+// https://tools.ietf.org/html/rfc4279#section-2
+func PSKPreMasterSecret(psk []byte) []byte {
+ pskLen := uint16(len(psk))
+
+ out := append(make([]byte, 2+pskLen+2), psk...)
+ binary.BigEndian.PutUint16(out, pskLen)
+ binary.BigEndian.PutUint16(out[2+pskLen:], pskLen)
+
+ return out
+}
+
+// EcdhePSKPreMasterSecret implements TLS 1.2 Premaster Secret generation given a psk, a keypair and a curve
+//
+// https://datatracker.ietf.org/doc/html/rfc5489#section-2
+func EcdhePSKPreMasterSecret(psk, publicKey, privateKey []byte, curve elliptic.Curve) ([]byte, error) {
+ preMasterSecret, err := PreMasterSecret(publicKey, privateKey, curve)
+ if err != nil {
+ return nil, err
+ }
+ out := make([]byte, 2+len(preMasterSecret)+2+len(psk))
+
+ // write preMasterSecret length
+ offset := 0
+ binary.BigEndian.PutUint16(out[offset:], uint16(len(preMasterSecret)))
+ offset += 2
+
+ // write preMasterSecret
+ copy(out[offset:], preMasterSecret)
+ offset += len(preMasterSecret)
+
+ // write psk length
+ binary.BigEndian.PutUint16(out[offset:], uint16(len(psk)))
+ offset += 2
+
+ // write psk
+ copy(out[offset:], psk)
+ return out, nil
+}
+
+// PreMasterSecret implements TLS 1.2 Premaster Secret generation given a keypair and a curve
+func PreMasterSecret(publicKey, privateKey []byte, curve elliptic.Curve) ([]byte, error) {
+ switch curve {
+ case elliptic.X25519:
+ return curve25519.X25519(privateKey, publicKey)
+ case elliptic.P256:
+ return ellipticCurvePreMasterSecret(publicKey, privateKey, ellipticStdlib.P256(), ellipticStdlib.P256())
+ case elliptic.P384:
+ return ellipticCurvePreMasterSecret(publicKey, privateKey, ellipticStdlib.P384(), ellipticStdlib.P384())
+ default:
+ return nil, errInvalidNamedCurve
+ }
+}
+
+func ellipticCurvePreMasterSecret(publicKey, privateKey []byte, c1, c2 ellipticStdlib.Curve) ([]byte, error) {
+ x, y := ellipticStdlib.Unmarshal(c1, publicKey)
+ if x == nil || y == nil {
+ return nil, errInvalidNamedCurve
+ }
+
+ result, _ := c2.ScalarMult(x, y, privateKey)
+ preMasterSecret := make([]byte, (c2.Params().BitSize+7)>>3)
+ resultBytes := result.Bytes()
+ copy(preMasterSecret[len(preMasterSecret)-len(resultBytes):], resultBytes)
+ return preMasterSecret, nil
+}
+
+// PHash is PRF is the SHA-256 hash function is used for all cipher suites
+// defined in this TLS 1.2 document and in TLS documents published prior to this
+// document when TLS 1.2 is negotiated. New cipher suites MUST explicitly
+// specify a PRF and, in general, SHOULD use the TLS PRF with SHA-256 or a
+// stronger standard hash function.
+//
+// P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
+// HMAC_hash(secret, A(2) + seed) +
+// HMAC_hash(secret, A(3) + seed) + ...
+//
+// A() is defined as:
+//
+// A(0) = seed
+// A(i) = HMAC_hash(secret, A(i-1))
+//
+// P_hash can be iterated as many times as necessary to produce the
+// required quantity of data. For example, if P_SHA256 is being used to
+// create 80 bytes of data, it will have to be iterated three times
+// (through A(3)), creating 96 bytes of output data; the last 16 bytes
+// of the final iteration will then be discarded, leaving 80 bytes of
+// output data.
+//
+// https://tools.ietf.org/html/rfc4346w
+func PHash(secret, seed []byte, requestedLength int, h HashFunc) ([]byte, error) {
+ hmacSHA256 := func(key, data []byte) ([]byte, error) {
+ mac := hmac.New(h, key)
+ if _, err := mac.Write(data); err != nil {
+ return nil, err
+ }
+ return mac.Sum(nil), nil
+ }
+
+ var err error
+ lastRound := seed
+ out := []byte{}
+
+ iterations := int(math.Ceil(float64(requestedLength) / float64(h().Size())))
+ for i := 0; i < iterations; i++ {
+ lastRound, err = hmacSHA256(secret, lastRound)
+ if err != nil {
+ return nil, err
+ }
+ withSecret, err := hmacSHA256(secret, append(lastRound, seed...))
+ if err != nil {
+ return nil, err
+ }
+ out = append(out, withSecret...)
+ }
+
+ return out[:requestedLength], nil
+}
+
+// ExtendedMasterSecret generates a Extended MasterSecret as defined in
+// https://tools.ietf.org/html/rfc7627
+func ExtendedMasterSecret(preMasterSecret, sessionHash []byte, h HashFunc) ([]byte, error) {
+ seed := append([]byte(extendedMasterSecretLabel), sessionHash...)
+ return PHash(preMasterSecret, seed, 48, h)
+}
+
+// MasterSecret generates a TLS 1.2 MasterSecret
+func MasterSecret(preMasterSecret, clientRandom, serverRandom []byte, h HashFunc) ([]byte, error) {
+ seed := append(append([]byte(masterSecretLabel), clientRandom...), serverRandom...)
+ return PHash(preMasterSecret, seed, 48, h)
+}
+
+// GenerateEncryptionKeys is the final step TLS 1.2 PRF. Given all state generated so far generates
+// the final keys need for encryption
+func GenerateEncryptionKeys(masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int, h HashFunc) (*EncryptionKeys, error) {
+ seed := append(append([]byte(keyExpansionLabel), serverRandom...), clientRandom...)
+ keyMaterial, err := PHash(masterSecret, seed, (2*macLen)+(2*keyLen)+(2*ivLen), h)
+ if err != nil {
+ return nil, err
+ }
+
+ clientMACKey := keyMaterial[:macLen]
+ keyMaterial = keyMaterial[macLen:]
+
+ serverMACKey := keyMaterial[:macLen]
+ keyMaterial = keyMaterial[macLen:]
+
+ clientWriteKey := keyMaterial[:keyLen]
+ keyMaterial = keyMaterial[keyLen:]
+
+ serverWriteKey := keyMaterial[:keyLen]
+ keyMaterial = keyMaterial[keyLen:]
+
+ clientWriteIV := keyMaterial[:ivLen]
+ keyMaterial = keyMaterial[ivLen:]
+
+ serverWriteIV := keyMaterial[:ivLen]
+
+ return &EncryptionKeys{
+ MasterSecret: masterSecret,
+ ClientMACKey: clientMACKey,
+ ServerMACKey: serverMACKey,
+ ClientWriteKey: clientWriteKey,
+ ServerWriteKey: serverWriteKey,
+ ClientWriteIV: clientWriteIV,
+ ServerWriteIV: serverWriteIV,
+ }, nil
+}
+
+func prfVerifyData(masterSecret, handshakeBodies []byte, label string, hashFunc HashFunc) ([]byte, error) {
+ h := hashFunc()
+ if _, err := h.Write(handshakeBodies); err != nil {
+ return nil, err
+ }
+
+ seed := append([]byte(label), h.Sum(nil)...)
+ return PHash(masterSecret, seed, 12, hashFunc)
+}
+
+// VerifyDataClient is caled on the Client Side to either verify or generate the VerifyData message
+func VerifyDataClient(masterSecret, handshakeBodies []byte, h HashFunc) ([]byte, error) {
+ return prfVerifyData(masterSecret, handshakeBodies, verifyDataClientLabel, h)
+}
+
+// VerifyDataServer is caled on the Server Side to either verify or generate the VerifyData message
+func VerifyDataServer(masterSecret, handshakeBodies []byte, h HashFunc) ([]byte, error) {
+ return prfVerifyData(masterSecret, handshakeBodies, verifyDataServerLabel, h)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go
new file mode 100644
index 0000000..fec7fba
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/signature/signature.go
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package signature provides our implemented Signature Algorithms
+package signature
+
+// Algorithm as defined in TLS 1.2
+// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16
+type Algorithm uint16
+
+// SignatureAlgorithm enums
+const (
+ Anonymous Algorithm = 0
+ RSA Algorithm = 1
+ ECDSA Algorithm = 3
+ Ed25519 Algorithm = 7
+)
+
+// Algorithms returns all implemented Signature Algorithms
+func Algorithms() map[Algorithm]struct{} {
+ return map[Algorithm]struct{}{
+ Anonymous: {},
+ RSA: {},
+ ECDSA: {},
+ Ed25519: {},
+ }
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go
new file mode 100644
index 0000000..4aeb3e4
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/errors.go
@@ -0,0 +1,12 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package signaturehash
+
+import "errors"
+
+var (
+ errNoAvailableSignatureSchemes = errors.New("connection can not be created, no SignatureScheme satisfy this Config")
+ errInvalidSignatureAlgorithm = errors.New("invalid signature algorithm")
+ errInvalidHashAlgorithm = errors.New("invalid hash algorithm")
+)
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go
new file mode 100644
index 0000000..2561acc
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/crypto/signaturehash/signaturehash.go
@@ -0,0 +1,96 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package signaturehash provides the SignatureHashAlgorithm as defined in TLS 1.2
+package signaturehash
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/ed25519"
+ "crypto/rsa"
+ "crypto/tls"
+ "fmt"
+
+ "github.com/pion/dtls/v2/pkg/crypto/hash"
+ "github.com/pion/dtls/v2/pkg/crypto/signature"
+)
+
+// Algorithm is a signature/hash algorithm pairs which may be used in
+// digital signatures.
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
+type Algorithm struct {
+ Hash hash.Algorithm
+ Signature signature.Algorithm
+}
+
+// Algorithms are all the know SignatureHash Algorithms
+func Algorithms() []Algorithm {
+ return []Algorithm{
+ {hash.SHA256, signature.ECDSA},
+ {hash.SHA384, signature.ECDSA},
+ {hash.SHA512, signature.ECDSA},
+ {hash.SHA256, signature.RSA},
+ {hash.SHA384, signature.RSA},
+ {hash.SHA512, signature.RSA},
+ {hash.Ed25519, signature.Ed25519},
+ }
+}
+
+// SelectSignatureScheme returns most preferred and compatible scheme.
+func SelectSignatureScheme(sigs []Algorithm, privateKey crypto.PrivateKey) (Algorithm, error) {
+ for _, ss := range sigs {
+ if ss.isCompatible(privateKey) {
+ return ss, nil
+ }
+ }
+ return Algorithm{}, errNoAvailableSignatureSchemes
+}
+
+// isCompatible checks that given private key is compatible with the signature scheme.
+func (a *Algorithm) isCompatible(privateKey crypto.PrivateKey) bool {
+ switch privateKey.(type) {
+ case ed25519.PrivateKey:
+ return a.Signature == signature.Ed25519
+ case *ecdsa.PrivateKey:
+ return a.Signature == signature.ECDSA
+ case *rsa.PrivateKey:
+ return a.Signature == signature.RSA
+ default:
+ return false
+ }
+}
+
+// ParseSignatureSchemes translates []tls.SignatureScheme to []signatureHashAlgorithm.
+// It returns default signature scheme list if no SignatureScheme is passed.
+func ParseSignatureSchemes(sigs []tls.SignatureScheme, insecureHashes bool) ([]Algorithm, error) {
+ if len(sigs) == 0 {
+ return Algorithms(), nil
+ }
+ out := []Algorithm{}
+ for _, ss := range sigs {
+ sig := signature.Algorithm(ss & 0xFF)
+ if _, ok := signature.Algorithms()[sig]; !ok {
+ return nil,
+ fmt.Errorf("SignatureScheme %04x: %w", ss, errInvalidSignatureAlgorithm)
+ }
+ h := hash.Algorithm(ss >> 8)
+ if _, ok := hash.Algorithms()[h]; !ok || (ok && h == hash.None) {
+ return nil, fmt.Errorf("SignatureScheme %04x: %w", ss, errInvalidHashAlgorithm)
+ }
+ if h.Insecure() && !insecureHashes {
+ continue
+ }
+ out = append(out, Algorithm{
+ Hash: h,
+ Signature: sig,
+ })
+ }
+
+ if len(out) == 0 {
+ return nil, errNoAvailableSignatureSchemes
+ }
+
+ return out, nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go
new file mode 100644
index 0000000..91e9f4d
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/alert/alert.go
@@ -0,0 +1,166 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package alert implements TLS alert protocol https://tools.ietf.org/html/rfc5246#section-7.2
+package alert
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+var errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
+
+// Level is the level of the TLS Alert
+type Level byte
+
+// Level enums
+const (
+ Warning Level = 1
+ Fatal Level = 2
+)
+
+func (l Level) String() string {
+ switch l {
+ case Warning:
+ return "Warning"
+ case Fatal:
+ return "Fatal"
+ default:
+ return "Invalid alert level"
+ }
+}
+
+// Description is the extended info of the TLS Alert
+type Description byte
+
+// Description enums
+const (
+ CloseNotify Description = 0
+ UnexpectedMessage Description = 10
+ BadRecordMac Description = 20
+ DecryptionFailed Description = 21
+ RecordOverflow Description = 22
+ DecompressionFailure Description = 30
+ HandshakeFailure Description = 40
+ NoCertificate Description = 41
+ BadCertificate Description = 42
+ UnsupportedCertificate Description = 43
+ CertificateRevoked Description = 44
+ CertificateExpired Description = 45
+ CertificateUnknown Description = 46
+ IllegalParameter Description = 47
+ UnknownCA Description = 48
+ AccessDenied Description = 49
+ DecodeError Description = 50
+ DecryptError Description = 51
+ ExportRestriction Description = 60
+ ProtocolVersion Description = 70
+ InsufficientSecurity Description = 71
+ InternalError Description = 80
+ UserCanceled Description = 90
+ NoRenegotiation Description = 100
+ UnsupportedExtension Description = 110
+ NoApplicationProtocol Description = 120
+)
+
+func (d Description) String() string {
+ switch d {
+ case CloseNotify:
+ return "CloseNotify"
+ case UnexpectedMessage:
+ return "UnexpectedMessage"
+ case BadRecordMac:
+ return "BadRecordMac"
+ case DecryptionFailed:
+ return "DecryptionFailed"
+ case RecordOverflow:
+ return "RecordOverflow"
+ case DecompressionFailure:
+ return "DecompressionFailure"
+ case HandshakeFailure:
+ return "HandshakeFailure"
+ case NoCertificate:
+ return "NoCertificate"
+ case BadCertificate:
+ return "BadCertificate"
+ case UnsupportedCertificate:
+ return "UnsupportedCertificate"
+ case CertificateRevoked:
+ return "CertificateRevoked"
+ case CertificateExpired:
+ return "CertificateExpired"
+ case CertificateUnknown:
+ return "CertificateUnknown"
+ case IllegalParameter:
+ return "IllegalParameter"
+ case UnknownCA:
+ return "UnknownCA"
+ case AccessDenied:
+ return "AccessDenied"
+ case DecodeError:
+ return "DecodeError"
+ case DecryptError:
+ return "DecryptError"
+ case ExportRestriction:
+ return "ExportRestriction"
+ case ProtocolVersion:
+ return "ProtocolVersion"
+ case InsufficientSecurity:
+ return "InsufficientSecurity"
+ case InternalError:
+ return "InternalError"
+ case UserCanceled:
+ return "UserCanceled"
+ case NoRenegotiation:
+ return "NoRenegotiation"
+ case UnsupportedExtension:
+ return "UnsupportedExtension"
+ case NoApplicationProtocol:
+ return "NoApplicationProtocol"
+ default:
+ return "Invalid alert description"
+ }
+}
+
+// Alert is one of the content types supported by the TLS record layer.
+// Alert messages convey the severity of the message
+// (warning or fatal) and a description of the alert. Alert messages
+// with a level of fatal result in the immediate termination of the
+// connection. In this case, other connections corresponding to the
+// session may continue, but the session identifier MUST be invalidated,
+// preventing the failed session from being used to establish new
+// connections. Like other messages, alert messages are encrypted and
+// compressed, as specified by the current connection state.
+// https://tools.ietf.org/html/rfc5246#section-7.2
+type Alert struct {
+ Level Level
+ Description Description
+}
+
+// ContentType returns the ContentType of this Content
+func (a Alert) ContentType() protocol.ContentType {
+ return protocol.ContentTypeAlert
+}
+
+// Marshal returns the encoded alert
+func (a *Alert) Marshal() ([]byte, error) {
+ return []byte{byte(a.Level), byte(a.Description)}, nil
+}
+
+// Unmarshal populates the alert from binary data
+func (a *Alert) Unmarshal(data []byte) error {
+ if len(data) != 2 {
+ return errBufferTooSmall
+ }
+
+ a.Level = Level(data[0])
+ a.Description = Description(data[1])
+ return nil
+}
+
+func (a *Alert) String() string {
+ return fmt.Sprintf("Alert %s: %s", a.Level, a.Description)
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go
new file mode 100644
index 0000000..f422115
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/application_data.go
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package protocol
+
+// ApplicationData messages are carried by the record layer and are
+// fragmented, compressed, and encrypted based on the current connection
+// state. The messages are treated as transparent data to the record
+// layer.
+// https://tools.ietf.org/html/rfc5246#section-10
+type ApplicationData struct {
+ Data []byte
+}
+
+// ContentType returns the ContentType of this content
+func (a ApplicationData) ContentType() ContentType {
+ return ContentTypeApplicationData
+}
+
+// Marshal encodes the ApplicationData to binary
+func (a *ApplicationData) Marshal() ([]byte, error) {
+ return append([]byte{}, a.Data...), nil
+}
+
+// Unmarshal populates the ApplicationData from binary
+func (a *ApplicationData) Unmarshal(data []byte) error {
+ a.Data = append([]byte{}, data...)
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go
new file mode 100644
index 0000000..87f28bc
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/change_cipher_spec.go
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package protocol
+
+// ChangeCipherSpec protocol exists to signal transitions in
+// ciphering strategies. The protocol consists of a single message,
+// which is encrypted and compressed under the current (not the pending)
+// connection state. The message consists of a single byte of value 1.
+// https://tools.ietf.org/html/rfc5246#section-7.1
+type ChangeCipherSpec struct{}
+
+// ContentType returns the ContentType of this content
+func (c ChangeCipherSpec) ContentType() ContentType {
+ return ContentTypeChangeCipherSpec
+}
+
+// Marshal encodes the ChangeCipherSpec to binary
+func (c *ChangeCipherSpec) Marshal() ([]byte, error) {
+ return []byte{0x01}, nil
+}
+
+// Unmarshal populates the ChangeCipherSpec from binary
+func (c *ChangeCipherSpec) Unmarshal(data []byte) error {
+ if len(data) == 1 && data[0] == 0x01 {
+ return nil
+ }
+
+ return errInvalidCipherSpec
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go
new file mode 100644
index 0000000..3478ee3
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/compression_method.go
@@ -0,0 +1,51 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package protocol
+
+// CompressionMethodID is the ID for a CompressionMethod
+type CompressionMethodID byte
+
+const (
+ compressionMethodNull CompressionMethodID = 0
+)
+
+// CompressionMethod represents a TLS Compression Method
+type CompressionMethod struct {
+ ID CompressionMethodID
+}
+
+// CompressionMethods returns all supported CompressionMethods
+func CompressionMethods() map[CompressionMethodID]*CompressionMethod {
+ return map[CompressionMethodID]*CompressionMethod{
+ compressionMethodNull: {ID: compressionMethodNull},
+ }
+}
+
+// DecodeCompressionMethods the given compression methods
+func DecodeCompressionMethods(buf []byte) ([]*CompressionMethod, error) {
+ if len(buf) < 1 {
+ return nil, errBufferTooSmall
+ }
+ compressionMethodsCount := int(buf[0])
+ c := []*CompressionMethod{}
+ for i := 0; i < compressionMethodsCount; i++ {
+ if len(buf) <= i+1 {
+ return nil, errBufferTooSmall
+ }
+ id := CompressionMethodID(buf[i+1])
+ if compressionMethod, ok := CompressionMethods()[id]; ok {
+ c = append(c, compressionMethod)
+ }
+ }
+ return c, nil
+}
+
+// EncodeCompressionMethods the given compression methods
+func EncodeCompressionMethods(c []*CompressionMethod) []byte {
+ out := []byte{byte(len(c))}
+ for i := len(c); i > 0; i-- {
+ out = append(out, byte(c[i-1].ID))
+ }
+ return out
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go
new file mode 100644
index 0000000..92c9db2
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/content.go
@@ -0,0 +1,24 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package protocol
+
+// ContentType represents the IANA Registered ContentTypes
+//
+// https://tools.ietf.org/html/rfc4346#section-6.2.1
+type ContentType uint8
+
+// ContentType enums
+const (
+ ContentTypeChangeCipherSpec ContentType = 20
+ ContentTypeAlert ContentType = 21
+ ContentTypeHandshake ContentType = 22
+ ContentTypeApplicationData ContentType = 23
+)
+
+// Content is the top level distinguisher for a DTLS Datagram
+type Content interface {
+ ContentType() ContentType
+ Marshal() ([]byte, error)
+ Unmarshal(data []byte) error
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go
new file mode 100644
index 0000000..d87aff7
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/errors.go
@@ -0,0 +1,109 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package protocol
+
+import (
+ "errors"
+ "fmt"
+ "net"
+)
+
+var (
+ errBufferTooSmall = &TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
+ errInvalidCipherSpec = &FatalError{Err: errors.New("cipher spec invalid")} //nolint:goerr113
+)
+
+// FatalError indicates that the DTLS connection is no longer available.
+// It is mainly caused by wrong configuration of server or client.
+type FatalError struct {
+ Err error
+}
+
+// InternalError indicates and internal error caused by the implementation, and the DTLS connection is no longer available.
+// It is mainly caused by bugs or tried to use unimplemented features.
+type InternalError struct {
+ Err error
+}
+
+// TemporaryError indicates that the DTLS connection is still available, but the request was failed temporary.
+type TemporaryError struct {
+ Err error
+}
+
+// TimeoutError indicates that the request was timed out.
+type TimeoutError struct {
+ Err error
+}
+
+// HandshakeError indicates that the handshake failed.
+type HandshakeError struct {
+ Err error
+}
+
+// Timeout implements net.Error.Timeout()
+func (*FatalError) Timeout() bool { return false }
+
+// Temporary implements net.Error.Temporary()
+func (*FatalError) Temporary() bool { return false }
+
+// Unwrap implements Go1.13 error unwrapper.
+func (e *FatalError) Unwrap() error { return e.Err }
+
+func (e *FatalError) Error() string { return fmt.Sprintf("dtls fatal: %v", e.Err) }
+
+// Timeout implements net.Error.Timeout()
+func (*InternalError) Timeout() bool { return false }
+
+// Temporary implements net.Error.Temporary()
+func (*InternalError) Temporary() bool { return false }
+
+// Unwrap implements Go1.13 error unwrapper.
+func (e *InternalError) Unwrap() error { return e.Err }
+
+func (e *InternalError) Error() string { return fmt.Sprintf("dtls internal: %v", e.Err) }
+
+// Timeout implements net.Error.Timeout()
+func (*TemporaryError) Timeout() bool { return false }
+
+// Temporary implements net.Error.Temporary()
+func (*TemporaryError) Temporary() bool { return true }
+
+// Unwrap implements Go1.13 error unwrapper.
+func (e *TemporaryError) Unwrap() error { return e.Err }
+
+func (e *TemporaryError) Error() string { return fmt.Sprintf("dtls temporary: %v", e.Err) }
+
+// Timeout implements net.Error.Timeout()
+func (*TimeoutError) Timeout() bool { return true }
+
+// Temporary implements net.Error.Temporary()
+func (*TimeoutError) Temporary() bool { return true }
+
+// Unwrap implements Go1.13 error unwrapper.
+func (e *TimeoutError) Unwrap() error { return e.Err }
+
+func (e *TimeoutError) Error() string { return fmt.Sprintf("dtls timeout: %v", e.Err) }
+
+// Timeout implements net.Error.Timeout()
+func (e *HandshakeError) Timeout() bool {
+ var netErr net.Error
+ if errors.As(e.Err, &netErr) {
+ return netErr.Timeout()
+ }
+ return false
+}
+
+// Temporary implements net.Error.Temporary()
+func (e *HandshakeError) Temporary() bool {
+ var netErr net.Error
+ if errors.As(e.Err, &netErr) {
+ return netErr.Temporary() //nolint
+ }
+ return false
+}
+
+// Unwrap implements Go1.13 error unwrapper.
+func (e *HandshakeError) Unwrap() error { return e.Err }
+
+func (e *HandshakeError) Error() string { return fmt.Sprintf("handshake error: %v", e.Err) }
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/alpn.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/alpn.go
new file mode 100644
index 0000000..e780dc9
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/alpn.go
@@ -0,0 +1,80 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package extension
+
+import (
+ "golang.org/x/crypto/cryptobyte"
+)
+
+// ALPN is a TLS extension for application-layer protocol negotiation within
+// the TLS handshake.
+//
+// https://tools.ietf.org/html/rfc7301
+type ALPN struct {
+ ProtocolNameList []string
+}
+
+// TypeValue returns the extension TypeValue
+func (a ALPN) TypeValue() TypeValue {
+ return ALPNTypeValue
+}
+
+// Marshal encodes the extension
+func (a *ALPN) Marshal() ([]byte, error) {
+ var b cryptobyte.Builder
+ b.AddUint16(uint16(a.TypeValue()))
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ for _, proto := range a.ProtocolNameList {
+ p := proto // Satisfy range scope lint
+ b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(p))
+ })
+ }
+ })
+ })
+ return b.Bytes()
+}
+
+// Unmarshal populates the extension from encoded data
+func (a *ALPN) Unmarshal(data []byte) error {
+ val := cryptobyte.String(data)
+
+ var extension uint16
+ val.ReadUint16(&extension)
+ if TypeValue(extension) != a.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ var extData cryptobyte.String
+ val.ReadUint16LengthPrefixed(&extData)
+
+ var protoList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
+ return ErrALPNInvalidFormat
+ }
+ for !protoList.Empty() {
+ var proto cryptobyte.String
+ if !protoList.ReadUint8LengthPrefixed(&proto) || proto.Empty() {
+ return ErrALPNInvalidFormat
+ }
+ a.ProtocolNameList = append(a.ProtocolNameList, string(proto))
+ }
+ return nil
+}
+
+// ALPNProtocolSelection negotiates a shared protocol according to #3.2 of rfc7301
+func ALPNProtocolSelection(supportedProtocols, peerSupportedProtocols []string) (string, error) {
+ if len(supportedProtocols) == 0 || len(peerSupportedProtocols) == 0 {
+ return "", nil
+ }
+ for _, s := range supportedProtocols {
+ for _, c := range peerSupportedProtocols {
+ if s == c {
+ return s, nil
+ }
+ }
+ }
+ return "", errALPNNoAppProto
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go
new file mode 100644
index 0000000..c5e954c
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/errors.go
@@ -0,0 +1,20 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package extension
+
+import (
+ "errors"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+var (
+ // ErrALPNInvalidFormat is raised when the ALPN format is invalid
+ ErrALPNInvalidFormat = &protocol.FatalError{Err: errors.New("invalid alpn format")} //nolint:goerr113
+ errALPNNoAppProto = &protocol.FatalError{Err: errors.New("no application protocol")} //nolint:goerr113
+ 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
+ errLengthMismatch = &protocol.InternalError{Err: errors.New("data length and declared length do not match")} //nolint:goerr113
+)
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go
new file mode 100644
index 0000000..5173a58
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/extension.go
@@ -0,0 +1,102 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package extension implements the extension values in the ClientHello/ServerHello
+package extension
+
+import "encoding/binary"
+
+// TypeValue is the 2 byte value for a TLS Extension as registered in the IANA
+//
+// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml
+type TypeValue uint16
+
+// TypeValue constants
+const (
+ ServerNameTypeValue TypeValue = 0
+ SupportedEllipticCurvesTypeValue TypeValue = 10
+ SupportedPointFormatsTypeValue TypeValue = 11
+ SupportedSignatureAlgorithmsTypeValue TypeValue = 13
+ UseSRTPTypeValue TypeValue = 14
+ ALPNTypeValue TypeValue = 16
+ UseExtendedMasterSecretTypeValue TypeValue = 23
+ RenegotiationInfoTypeValue TypeValue = 65281
+)
+
+// Extension represents a single TLS extension
+type Extension interface {
+ Marshal() ([]byte, error)
+ Unmarshal(data []byte) error
+ TypeValue() TypeValue
+}
+
+// Unmarshal many extensions at once
+func Unmarshal(buf []byte) ([]Extension, error) {
+ switch {
+ case len(buf) == 0:
+ return []Extension{}, nil
+ case len(buf) < 2:
+ return nil, errBufferTooSmall
+ }
+
+ declaredLen := binary.BigEndian.Uint16(buf)
+ if len(buf)-2 != int(declaredLen) {
+ return nil, errLengthMismatch
+ }
+
+ extensions := []Extension{}
+ unmarshalAndAppend := func(data []byte, e Extension) error {
+ err := e.Unmarshal(data)
+ if err != nil {
+ return err
+ }
+ extensions = append(extensions, e)
+ return nil
+ }
+
+ for offset := 2; offset < len(buf); {
+ if len(buf) < (offset + 2) {
+ return nil, errBufferTooSmall
+ }
+ var err error
+ switch TypeValue(binary.BigEndian.Uint16(buf[offset:])) {
+ case ServerNameTypeValue:
+ err = unmarshalAndAppend(buf[offset:], &ServerName{})
+ case SupportedEllipticCurvesTypeValue:
+ err = unmarshalAndAppend(buf[offset:], &SupportedEllipticCurves{})
+ case UseSRTPTypeValue:
+ err = unmarshalAndAppend(buf[offset:], &UseSRTP{})
+ case ALPNTypeValue:
+ err = unmarshalAndAppend(buf[offset:], &ALPN{})
+ case UseExtendedMasterSecretTypeValue:
+ err = unmarshalAndAppend(buf[offset:], &UseExtendedMasterSecret{})
+ case RenegotiationInfoTypeValue:
+ err = unmarshalAndAppend(buf[offset:], &RenegotiationInfo{})
+ default:
+ }
+ if err != nil {
+ return nil, err
+ }
+ if len(buf) < (offset + 4) {
+ return nil, errBufferTooSmall
+ }
+ extensionLength := binary.BigEndian.Uint16(buf[offset+2:])
+ offset += (4 + int(extensionLength))
+ }
+ return extensions, nil
+}
+
+// Marshal many extensions at once
+func Marshal(e []Extension) ([]byte, error) {
+ extensions := []byte{}
+ for _, e := range e {
+ raw, err := e.Marshal()
+ if err != nil {
+ return nil, err
+ }
+ extensions = append(extensions, raw...)
+ }
+ out := []byte{0x00, 0x00}
+ binary.BigEndian.PutUint16(out, uint16(len(extensions)))
+ return append(out, extensions...), nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go
new file mode 100644
index 0000000..c5092a7
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/renegotiation_info.go
@@ -0,0 +1,46 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package extension
+
+import "encoding/binary"
+
+const (
+ renegotiationInfoHeaderSize = 5
+)
+
+// RenegotiationInfo allows a Client/Server to
+// communicate their renegotation support
+//
+// https://tools.ietf.org/html/rfc5746
+type RenegotiationInfo struct {
+ RenegotiatedConnection uint8
+}
+
+// TypeValue returns the extension TypeValue
+func (r RenegotiationInfo) TypeValue() TypeValue {
+ return RenegotiationInfoTypeValue
+}
+
+// Marshal encodes the extension
+func (r *RenegotiationInfo) Marshal() ([]byte, error) {
+ out := make([]byte, renegotiationInfoHeaderSize)
+
+ binary.BigEndian.PutUint16(out, uint16(r.TypeValue()))
+ binary.BigEndian.PutUint16(out[2:], uint16(1)) // length
+ out[4] = r.RenegotiatedConnection
+ return out, nil
+}
+
+// Unmarshal populates the extension from encoded data
+func (r *RenegotiationInfo) Unmarshal(data []byte) error {
+ if len(data) < renegotiationInfoHeaderSize {
+ return errBufferTooSmall
+ } else if TypeValue(binary.BigEndian.Uint16(data)) != r.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ r.RenegotiatedConnection = data[4]
+
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go
new file mode 100644
index 0000000..183e08e
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/server_name.go
@@ -0,0 +1,81 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package extension
+
+import (
+ "strings"
+
+ "golang.org/x/crypto/cryptobyte"
+)
+
+const serverNameTypeDNSHostName = 0
+
+// ServerName allows the client to inform the server the specific
+// name it wishes to contact. Useful if multiple DNS names resolve
+// to one IP
+//
+// https://tools.ietf.org/html/rfc6066#section-3
+type ServerName struct {
+ ServerName string
+}
+
+// TypeValue returns the extension TypeValue
+func (s ServerName) TypeValue() TypeValue {
+ return ServerNameTypeValue
+}
+
+// Marshal encodes the extension
+func (s *ServerName) Marshal() ([]byte, error) {
+ var b cryptobyte.Builder
+ b.AddUint16(uint16(s.TypeValue()))
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddUint8(serverNameTypeDNSHostName)
+ b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
+ b.AddBytes([]byte(s.ServerName))
+ })
+ })
+ })
+ return b.Bytes()
+}
+
+// Unmarshal populates the extension from encoded data
+func (s *ServerName) Unmarshal(data []byte) error {
+ val := cryptobyte.String(data)
+ var extension uint16
+ val.ReadUint16(&extension)
+ if TypeValue(extension) != s.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ var extData cryptobyte.String
+ val.ReadUint16LengthPrefixed(&extData)
+
+ var nameList cryptobyte.String
+ if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() {
+ return errInvalidSNIFormat
+ }
+ for !nameList.Empty() {
+ var nameType uint8
+ var serverName cryptobyte.String
+ if !nameList.ReadUint8(&nameType) ||
+ !nameList.ReadUint16LengthPrefixed(&serverName) ||
+ serverName.Empty() {
+ return errInvalidSNIFormat
+ }
+ if nameType != serverNameTypeDNSHostName {
+ continue
+ }
+ if len(s.ServerName) != 0 {
+ // Multiple names of the same name_type are prohibited.
+ return errInvalidSNIFormat
+ }
+ s.ServerName = string(serverName)
+ // An SNI value may not include a trailing dot.
+ if strings.HasSuffix(s.ServerName, ".") {
+ return errInvalidSNIFormat
+ }
+ }
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go
new file mode 100644
index 0000000..75a8c2e
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/srtp_protection_profile.go
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package extension
+
+// SRTPProtectionProfile defines the parameters and options that are in effect for the SRTP processing
+// https://tools.ietf.org/html/rfc5764#section-4.1.2
+type SRTPProtectionProfile uint16
+
+const (
+ SRTP_AES128_CM_HMAC_SHA1_80 SRTPProtectionProfile = 0x0001 // nolint
+ SRTP_AES128_CM_HMAC_SHA1_32 SRTPProtectionProfile = 0x0002 // nolint
+ SRTP_AES256_CM_SHA1_80 SRTPProtectionProfile = 0x0003 // nolint
+ SRTP_AES256_CM_SHA1_32 SRTPProtectionProfile = 0x0004 // nolint
+ SRTP_NULL_HMAC_SHA1_80 SRTPProtectionProfile = 0x0005 // nolint
+ SRTP_NULL_HMAC_SHA1_32 SRTPProtectionProfile = 0x0006 // nolint
+ SRTP_AEAD_AES_128_GCM SRTPProtectionProfile = 0x0007 // nolint
+ SRTP_AEAD_AES_256_GCM SRTPProtectionProfile = 0x0008 // nolint
+)
+
+func srtpProtectionProfiles() map[SRTPProtectionProfile]bool {
+ return map[SRTPProtectionProfile]bool{
+ SRTP_AES128_CM_HMAC_SHA1_80: true,
+ SRTP_AES128_CM_HMAC_SHA1_32: true,
+ SRTP_AES256_CM_SHA1_80: true,
+ SRTP_AES256_CM_SHA1_32: true,
+ SRTP_NULL_HMAC_SHA1_80: true,
+ SRTP_NULL_HMAC_SHA1_32: true,
+ SRTP_AEAD_AES_128_GCM: true,
+ SRTP_AEAD_AES_256_GCM: true,
+ }
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go
new file mode 100644
index 0000000..dd9b54f
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_elliptic_curves.go
@@ -0,0 +1,65 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package extension
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+)
+
+const (
+ supportedGroupsHeaderSize = 6
+)
+
+// SupportedEllipticCurves allows a Client/Server to communicate
+// what curves they both support
+//
+// https://tools.ietf.org/html/rfc8422#section-5.1.1
+type SupportedEllipticCurves struct {
+ EllipticCurves []elliptic.Curve
+}
+
+// TypeValue returns the extension TypeValue
+func (s SupportedEllipticCurves) TypeValue() TypeValue {
+ return SupportedEllipticCurvesTypeValue
+}
+
+// Marshal encodes the extension
+func (s *SupportedEllipticCurves) Marshal() ([]byte, error) {
+ out := make([]byte, supportedGroupsHeaderSize)
+
+ binary.BigEndian.PutUint16(out, uint16(s.TypeValue()))
+ binary.BigEndian.PutUint16(out[2:], uint16(2+(len(s.EllipticCurves)*2)))
+ binary.BigEndian.PutUint16(out[4:], uint16(len(s.EllipticCurves)*2))
+
+ for _, v := range s.EllipticCurves {
+ out = append(out, []byte{0x00, 0x00}...)
+ binary.BigEndian.PutUint16(out[len(out)-2:], uint16(v))
+ }
+
+ return out, nil
+}
+
+// Unmarshal populates the extension from encoded data
+func (s *SupportedEllipticCurves) Unmarshal(data []byte) error {
+ if len(data) <= supportedGroupsHeaderSize {
+ return errBufferTooSmall
+ } else if TypeValue(binary.BigEndian.Uint16(data)) != s.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ groupCount := int(binary.BigEndian.Uint16(data[4:]) / 2)
+ if supportedGroupsHeaderSize+(groupCount*2) > len(data) {
+ return errLengthMismatch
+ }
+
+ for i := 0; i < groupCount; i++ {
+ supportedGroupID := elliptic.Curve(binary.BigEndian.Uint16(data[(supportedGroupsHeaderSize + (i * 2)):]))
+ if _, ok := elliptic.Curves()[supportedGroupID]; ok {
+ s.EllipticCurves = append(s.EllipticCurves, supportedGroupID)
+ }
+ }
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go
new file mode 100644
index 0000000..9c2543e
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_point_formats.go
@@ -0,0 +1,65 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package extension
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+)
+
+const (
+ supportedPointFormatsSize = 5
+)
+
+// SupportedPointFormats allows a Client/Server to negotiate
+// the EllipticCurvePointFormats
+//
+// https://tools.ietf.org/html/rfc4492#section-5.1.2
+type SupportedPointFormats struct {
+ PointFormats []elliptic.CurvePointFormat
+}
+
+// TypeValue returns the extension TypeValue
+func (s SupportedPointFormats) TypeValue() TypeValue {
+ return SupportedPointFormatsTypeValue
+}
+
+// Marshal encodes the extension
+func (s *SupportedPointFormats) Marshal() ([]byte, error) {
+ out := make([]byte, supportedPointFormatsSize)
+
+ binary.BigEndian.PutUint16(out, uint16(s.TypeValue()))
+ binary.BigEndian.PutUint16(out[2:], uint16(1+(len(s.PointFormats))))
+ out[4] = byte(len(s.PointFormats))
+
+ for _, v := range s.PointFormats {
+ out = append(out, byte(v))
+ }
+ return out, nil
+}
+
+// Unmarshal populates the extension from encoded data
+func (s *SupportedPointFormats) Unmarshal(data []byte) error {
+ if len(data) <= supportedPointFormatsSize {
+ return errBufferTooSmall
+ } else if TypeValue(binary.BigEndian.Uint16(data)) != s.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ pointFormatCount := int(binary.BigEndian.Uint16(data[4:]))
+ if supportedGroupsHeaderSize+(pointFormatCount) > len(data) {
+ return errLengthMismatch
+ }
+
+ for i := 0; i < pointFormatCount; i++ {
+ p := elliptic.CurvePointFormat(data[supportedPointFormatsSize+i])
+ switch p {
+ case elliptic.CurvePointFormatUncompressed:
+ s.PointFormats = append(s.PointFormats, p)
+ default:
+ }
+ }
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go
new file mode 100644
index 0000000..2ff4b90
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/supported_signature_algorithms.go
@@ -0,0 +1,73 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package extension
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/crypto/hash"
+ "github.com/pion/dtls/v2/pkg/crypto/signature"
+ "github.com/pion/dtls/v2/pkg/crypto/signaturehash"
+)
+
+const (
+ supportedSignatureAlgorithmsHeaderSize = 6
+)
+
+// SupportedSignatureAlgorithms allows a Client/Server to
+// negotiate what SignatureHash Algorithms they both support
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
+type SupportedSignatureAlgorithms struct {
+ SignatureHashAlgorithms []signaturehash.Algorithm
+}
+
+// TypeValue returns the extension TypeValue
+func (s SupportedSignatureAlgorithms) TypeValue() TypeValue {
+ return SupportedSignatureAlgorithmsTypeValue
+}
+
+// Marshal encodes the extension
+func (s *SupportedSignatureAlgorithms) Marshal() ([]byte, error) {
+ out := make([]byte, supportedSignatureAlgorithmsHeaderSize)
+
+ binary.BigEndian.PutUint16(out, uint16(s.TypeValue()))
+ binary.BigEndian.PutUint16(out[2:], uint16(2+(len(s.SignatureHashAlgorithms)*2)))
+ binary.BigEndian.PutUint16(out[4:], uint16(len(s.SignatureHashAlgorithms)*2))
+ for _, v := range s.SignatureHashAlgorithms {
+ out = append(out, []byte{0x00, 0x00}...)
+ out[len(out)-2] = byte(v.Hash)
+ out[len(out)-1] = byte(v.Signature)
+ }
+
+ return out, nil
+}
+
+// Unmarshal populates the extension from encoded data
+func (s *SupportedSignatureAlgorithms) Unmarshal(data []byte) error {
+ if len(data) <= supportedSignatureAlgorithmsHeaderSize {
+ return errBufferTooSmall
+ } else if TypeValue(binary.BigEndian.Uint16(data)) != s.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ algorithmCount := int(binary.BigEndian.Uint16(data[4:]) / 2)
+ if supportedSignatureAlgorithmsHeaderSize+(algorithmCount*2) > len(data) {
+ return errLengthMismatch
+ }
+ for i := 0; i < algorithmCount; i++ {
+ supportedHashAlgorithm := hash.Algorithm(data[supportedSignatureAlgorithmsHeaderSize+(i*2)])
+ supportedSignatureAlgorithm := signature.Algorithm(data[supportedSignatureAlgorithmsHeaderSize+(i*2)+1])
+ if _, ok := hash.Algorithms()[supportedHashAlgorithm]; ok {
+ if _, ok := signature.Algorithms()[supportedSignatureAlgorithm]; ok {
+ s.SignatureHashAlgorithms = append(s.SignatureHashAlgorithms, signaturehash.Algorithm{
+ Hash: supportedHashAlgorithm,
+ Signature: supportedSignatureAlgorithm,
+ })
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go
new file mode 100644
index 0000000..d0b70ca
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_master_secret.go
@@ -0,0 +1,48 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package extension
+
+import "encoding/binary"
+
+const (
+ useExtendedMasterSecretHeaderSize = 4
+)
+
+// UseExtendedMasterSecret defines a TLS extension that contextually binds the
+// master secret to a log of the full handshake that computes it, thus
+// preventing MITM attacks.
+type UseExtendedMasterSecret struct {
+ Supported bool
+}
+
+// TypeValue returns the extension TypeValue
+func (u UseExtendedMasterSecret) TypeValue() TypeValue {
+ return UseExtendedMasterSecretTypeValue
+}
+
+// Marshal encodes the extension
+func (u *UseExtendedMasterSecret) Marshal() ([]byte, error) {
+ if !u.Supported {
+ return []byte{}, nil
+ }
+
+ out := make([]byte, useExtendedMasterSecretHeaderSize)
+
+ binary.BigEndian.PutUint16(out, uint16(u.TypeValue()))
+ binary.BigEndian.PutUint16(out[2:], uint16(0)) // length
+ return out, nil
+}
+
+// Unmarshal populates the extension from encoded data
+func (u *UseExtendedMasterSecret) Unmarshal(data []byte) error {
+ if len(data) < useExtendedMasterSecretHeaderSize {
+ return errBufferTooSmall
+ } else if TypeValue(binary.BigEndian.Uint16(data)) != u.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ u.Supported = true
+
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go
new file mode 100644
index 0000000..ea9f108
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/extension/use_srtp.go
@@ -0,0 +1,62 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package extension
+
+import "encoding/binary"
+
+const (
+ useSRTPHeaderSize = 6
+)
+
+// UseSRTP allows a Client/Server to negotiate what SRTPProtectionProfiles
+// they both support
+//
+// https://tools.ietf.org/html/rfc8422
+type UseSRTP struct {
+ ProtectionProfiles []SRTPProtectionProfile
+}
+
+// TypeValue returns the extension TypeValue
+func (u UseSRTP) TypeValue() TypeValue {
+ return UseSRTPTypeValue
+}
+
+// Marshal encodes the extension
+func (u *UseSRTP) Marshal() ([]byte, error) {
+ out := make([]byte, useSRTPHeaderSize)
+
+ binary.BigEndian.PutUint16(out, uint16(u.TypeValue()))
+ binary.BigEndian.PutUint16(out[2:], uint16(2+(len(u.ProtectionProfiles)*2)+ /* MKI Length */ 1))
+ binary.BigEndian.PutUint16(out[4:], uint16(len(u.ProtectionProfiles)*2))
+
+ for _, v := range u.ProtectionProfiles {
+ out = append(out, []byte{0x00, 0x00}...)
+ binary.BigEndian.PutUint16(out[len(out)-2:], uint16(v))
+ }
+
+ out = append(out, 0x00) /* MKI Length */
+ return out, nil
+}
+
+// Unmarshal populates the extension from encoded data
+func (u *UseSRTP) Unmarshal(data []byte) error {
+ if len(data) <= useSRTPHeaderSize {
+ return errBufferTooSmall
+ } else if TypeValue(binary.BigEndian.Uint16(data)) != u.TypeValue() {
+ return errInvalidExtensionType
+ }
+
+ profileCount := int(binary.BigEndian.Uint16(data[4:]) / 2)
+ if supportedGroupsHeaderSize+(profileCount*2) > len(data) {
+ return errLengthMismatch
+ }
+
+ for i := 0; i < profileCount; i++ {
+ supportedProfile := SRTPProtectionProfile(binary.BigEndian.Uint16(data[(useSRTPHeaderSize + (i * 2)):]))
+ if _, ok := srtpProtectionProfiles()[supportedProfile]; ok {
+ u.ProtectionProfiles = append(u.ProtectionProfiles, supportedProfile)
+ }
+ }
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go
new file mode 100644
index 0000000..b296297
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/cipher_suite.go
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package handshake
+
+import "encoding/binary"
+
+func decodeCipherSuiteIDs(buf []byte) ([]uint16, error) {
+ if len(buf) < 2 {
+ return nil, errBufferTooSmall
+ }
+ cipherSuitesCount := int(binary.BigEndian.Uint16(buf[0:])) / 2
+ rtrn := make([]uint16, cipherSuitesCount)
+ for i := 0; i < cipherSuitesCount; i++ {
+ if len(buf) < (i*2 + 4) {
+ return nil, errBufferTooSmall
+ }
+
+ rtrn[i] = binary.BigEndian.Uint16(buf[(i*2)+2:])
+ }
+ return rtrn, nil
+}
+
+func encodeCipherSuiteIDs(cipherSuiteIDs []uint16) []byte {
+ out := []byte{0x00, 0x00}
+ binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(cipherSuiteIDs)*2))
+ for _, id := range cipherSuiteIDs {
+ out = append(out, []byte{0x00, 0x00}...)
+ binary.BigEndian.PutUint16(out[len(out)-2:], id)
+ }
+ return out
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go
new file mode 100644
index 0000000..1354300
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/errors.go
@@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package handshake
+
+import (
+ "errors"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+// Typed errors
+var (
+ errUnableToMarshalFragmented = &protocol.InternalError{Err: errors.New("unable to marshal fragmented handshakes")} //nolint:goerr113
+ errHandshakeMessageUnset = &protocol.InternalError{Err: errors.New("handshake message unset, unable to marshal")} //nolint:goerr113
+ errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
+ errLengthMismatch = &protocol.InternalError{Err: errors.New("data length and declared length do not match")} //nolint:goerr113
+ errInvalidClientKeyExchange = &protocol.FatalError{Err: errors.New("unable to determine if ClientKeyExchange is a public key or PSK Identity")} //nolint:goerr113
+ errInvalidHashAlgorithm = &protocol.FatalError{Err: errors.New("invalid hash algorithm")} //nolint:goerr113
+ errInvalidSignatureAlgorithm = &protocol.FatalError{Err: errors.New("invalid signature algorithm")} //nolint:goerr113
+ errCookieTooLong = &protocol.FatalError{Err: errors.New("cookie must not be longer then 255 bytes")} //nolint:goerr113
+ errInvalidEllipticCurveType = &protocol.FatalError{Err: errors.New("invalid or unknown elliptic curve type")} //nolint:goerr113
+ errInvalidNamedCurve = &protocol.FatalError{Err: errors.New("invalid named curve")} //nolint:goerr113
+ errCipherSuiteUnset = &protocol.FatalError{Err: errors.New("server hello can not be created without a cipher suite")} //nolint:goerr113
+ errCompressionMethodUnset = &protocol.FatalError{Err: errors.New("server hello can not be created without a compression method")} //nolint:goerr113
+ errInvalidCompressionMethod = &protocol.FatalError{Err: errors.New("invalid or unknown compression method")} //nolint:goerr113
+ errNotImplemented = &protocol.InternalError{Err: errors.New("feature has not been implemented yet")} //nolint:goerr113
+)
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go
new file mode 100644
index 0000000..b1f682b
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/handshake.go
@@ -0,0 +1,150 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package handshake provides the DTLS wire protocol for handshakes
+package handshake
+
+import (
+ "github.com/pion/dtls/v2/internal/ciphersuite/types"
+ "github.com/pion/dtls/v2/internal/util"
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+// Type is the unique identifier for each handshake message
+// https://tools.ietf.org/html/rfc5246#section-7.4
+type Type uint8
+
+// Types of DTLS Handshake messages we know about
+const (
+ TypeHelloRequest Type = 0
+ TypeClientHello Type = 1
+ TypeServerHello Type = 2
+ TypeHelloVerifyRequest Type = 3
+ TypeCertificate Type = 11
+ TypeServerKeyExchange Type = 12
+ TypeCertificateRequest Type = 13
+ TypeServerHelloDone Type = 14
+ TypeCertificateVerify Type = 15
+ TypeClientKeyExchange Type = 16
+ TypeFinished Type = 20
+)
+
+// String returns the string representation of this type
+func (t Type) String() string {
+ switch t {
+ case TypeHelloRequest:
+ return "HelloRequest"
+ case TypeClientHello:
+ return "ClientHello"
+ case TypeServerHello:
+ return "ServerHello"
+ case TypeHelloVerifyRequest:
+ return "HelloVerifyRequest"
+ case TypeCertificate:
+ return "TypeCertificate"
+ case TypeServerKeyExchange:
+ return "ServerKeyExchange"
+ case TypeCertificateRequest:
+ return "CertificateRequest"
+ case TypeServerHelloDone:
+ return "ServerHelloDone"
+ case TypeCertificateVerify:
+ return "CertificateVerify"
+ case TypeClientKeyExchange:
+ return "ClientKeyExchange"
+ case TypeFinished:
+ return "Finished"
+ }
+ return ""
+}
+
+// Message is the body of a Handshake datagram
+type Message interface {
+ Marshal() ([]byte, error)
+ Unmarshal(data []byte) error
+ Type() Type
+}
+
+// Handshake protocol is responsible for selecting a cipher spec and
+// generating a master secret, which together comprise the primary
+// cryptographic parameters associated with a secure session. The
+// handshake protocol can also optionally authenticate parties who have
+// certificates signed by a trusted certificate authority.
+// https://tools.ietf.org/html/rfc5246#section-7.3
+type Handshake struct {
+ Header Header
+ Message Message
+
+ KeyExchangeAlgorithm types.KeyExchangeAlgorithm
+}
+
+// ContentType returns what kind of content this message is carying
+func (h Handshake) ContentType() protocol.ContentType {
+ return protocol.ContentTypeHandshake
+}
+
+// Marshal encodes a handshake into a binary message
+func (h *Handshake) Marshal() ([]byte, error) {
+ if h.Message == nil {
+ return nil, errHandshakeMessageUnset
+ } else if h.Header.FragmentOffset != 0 {
+ return nil, errUnableToMarshalFragmented
+ }
+
+ msg, err := h.Message.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ h.Header.Length = uint32(len(msg))
+ h.Header.FragmentLength = h.Header.Length
+ h.Header.Type = h.Message.Type()
+ header, err := h.Header.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ return append(header, msg...), nil
+}
+
+// Unmarshal decodes a handshake from a binary message
+func (h *Handshake) Unmarshal(data []byte) error {
+ if err := h.Header.Unmarshal(data); err != nil {
+ return err
+ }
+
+ reportedLen := util.BigEndianUint24(data[1:])
+ if uint32(len(data)-HeaderLength) != reportedLen {
+ return errLengthMismatch
+ } else if reportedLen != h.Header.FragmentLength {
+ return errLengthMismatch
+ }
+
+ switch Type(data[0]) {
+ case TypeHelloRequest:
+ return errNotImplemented
+ case TypeClientHello:
+ h.Message = &MessageClientHello{}
+ case TypeHelloVerifyRequest:
+ h.Message = &MessageHelloVerifyRequest{}
+ case TypeServerHello:
+ h.Message = &MessageServerHello{}
+ case TypeCertificate:
+ h.Message = &MessageCertificate{}
+ case TypeServerKeyExchange:
+ h.Message = &MessageServerKeyExchange{KeyExchangeAlgorithm: h.KeyExchangeAlgorithm}
+ case TypeCertificateRequest:
+ h.Message = &MessageCertificateRequest{}
+ case TypeServerHelloDone:
+ h.Message = &MessageServerHelloDone{}
+ case TypeClientKeyExchange:
+ h.Message = &MessageClientKeyExchange{KeyExchangeAlgorithm: h.KeyExchangeAlgorithm}
+ case TypeFinished:
+ h.Message = &MessageFinished{}
+ case TypeCertificateVerify:
+ h.Message = &MessageCertificateVerify{}
+ default:
+ return errNotImplemented
+ }
+ return h.Message.Unmarshal(data[HeaderLength:])
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go
new file mode 100644
index 0000000..4f9a962
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/header.go
@@ -0,0 +1,53 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package handshake
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/internal/util"
+)
+
+// HeaderLength msg_len for Handshake messages assumes an extra
+// 12 bytes for sequence, fragment and version information vs TLS
+const HeaderLength = 12
+
+// Header is the static first 12 bytes of each RecordLayer
+// of type Handshake. These fields allow us to support message loss, reordering, and
+// message fragmentation,
+//
+// https://tools.ietf.org/html/rfc6347#section-4.2.2
+type Header struct {
+ Type Type
+ Length uint32 // uint24 in spec
+ MessageSequence uint16
+ FragmentOffset uint32 // uint24 in spec
+ FragmentLength uint32 // uint24 in spec
+}
+
+// Marshal encodes the Header
+func (h *Header) Marshal() ([]byte, error) {
+ out := make([]byte, HeaderLength)
+
+ out[0] = byte(h.Type)
+ util.PutBigEndianUint24(out[1:], h.Length)
+ binary.BigEndian.PutUint16(out[4:], h.MessageSequence)
+ util.PutBigEndianUint24(out[6:], h.FragmentOffset)
+ util.PutBigEndianUint24(out[9:], h.FragmentLength)
+ return out, nil
+}
+
+// Unmarshal populates the header from encoded data
+func (h *Header) Unmarshal(data []byte) error {
+ if len(data) < HeaderLength {
+ return errBufferTooSmall
+ }
+
+ h.Type = Type(data[0])
+ h.Length = util.BigEndianUint24(data[1:])
+ h.MessageSequence = binary.BigEndian.Uint16(data[4:])
+ h.FragmentOffset = util.BigEndianUint24(data[6:])
+ h.FragmentLength = util.BigEndianUint24(data[9:])
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go
new file mode 100644
index 0000000..d5c861d
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate.go
@@ -0,0 +1,69 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package handshake
+
+import (
+ "github.com/pion/dtls/v2/internal/util"
+)
+
+// MessageCertificate is a DTLS Handshake Message
+// it can contain either a Client or Server Certificate
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.2
+type MessageCertificate struct {
+ Certificate [][]byte
+}
+
+// Type returns the Handshake Type
+func (m MessageCertificate) Type() Type {
+ return TypeCertificate
+}
+
+const (
+ handshakeMessageCertificateLengthFieldSize = 3
+)
+
+// Marshal encodes the Handshake
+func (m *MessageCertificate) Marshal() ([]byte, error) {
+ out := make([]byte, handshakeMessageCertificateLengthFieldSize)
+
+ for _, r := range m.Certificate {
+ // Certificate Length
+ out = append(out, make([]byte, handshakeMessageCertificateLengthFieldSize)...)
+ util.PutBigEndianUint24(out[len(out)-handshakeMessageCertificateLengthFieldSize:], uint32(len(r)))
+
+ // Certificate body
+ out = append(out, append([]byte{}, r...)...)
+ }
+
+ // Total Payload Size
+ util.PutBigEndianUint24(out[0:], uint32(len(out[handshakeMessageCertificateLengthFieldSize:])))
+ return out, nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageCertificate) Unmarshal(data []byte) error {
+ if len(data) < handshakeMessageCertificateLengthFieldSize {
+ return errBufferTooSmall
+ }
+
+ if certificateBodyLen := int(util.BigEndianUint24(data)); certificateBodyLen+handshakeMessageCertificateLengthFieldSize != len(data) {
+ return errLengthMismatch
+ }
+
+ offset := handshakeMessageCertificateLengthFieldSize
+ for offset < len(data) {
+ certificateLen := int(util.BigEndianUint24(data[offset:]))
+ offset += handshakeMessageCertificateLengthFieldSize
+
+ if offset+certificateLen > len(data) {
+ return errLengthMismatch
+ }
+
+ m.Certificate = append(m.Certificate, append([]byte{}, data[offset:offset+certificateLen]...))
+ offset += certificateLen
+ }
+
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go
new file mode 100644
index 0000000..11a44d4
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_request.go
@@ -0,0 +1,144 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package handshake
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/crypto/clientcertificate"
+ "github.com/pion/dtls/v2/pkg/crypto/hash"
+ "github.com/pion/dtls/v2/pkg/crypto/signature"
+ "github.com/pion/dtls/v2/pkg/crypto/signaturehash"
+)
+
+/*
+MessageCertificateRequest is so a non-anonymous server can optionally
+request a certificate from the client, if appropriate for the selected cipher
+suite. This message, if sent, will immediately follow the ServerKeyExchange
+message (if it is sent; otherwise, this message follows the
+server's Certificate message).
+
+https://tools.ietf.org/html/rfc5246#section-7.4.4
+*/
+type MessageCertificateRequest struct {
+ CertificateTypes []clientcertificate.Type
+ SignatureHashAlgorithms []signaturehash.Algorithm
+ CertificateAuthoritiesNames [][]byte
+}
+
+const (
+ messageCertificateRequestMinLength = 5
+)
+
+// Type returns the Handshake Type
+func (m MessageCertificateRequest) Type() Type {
+ return TypeCertificateRequest
+}
+
+// Marshal encodes the Handshake
+func (m *MessageCertificateRequest) Marshal() ([]byte, error) {
+ out := []byte{byte(len(m.CertificateTypes))}
+ for _, v := range m.CertificateTypes {
+ out = append(out, byte(v))
+ }
+
+ out = append(out, []byte{0x00, 0x00}...)
+ binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(m.SignatureHashAlgorithms)*2))
+ for _, v := range m.SignatureHashAlgorithms {
+ out = append(out, byte(v.Hash))
+ out = append(out, byte(v.Signature))
+ }
+
+ // Distinguished Names
+ casLength := 0
+ for _, ca := range m.CertificateAuthoritiesNames {
+ casLength += len(ca) + 2
+ }
+ out = append(out, []byte{0x00, 0x00}...)
+ binary.BigEndian.PutUint16(out[len(out)-2:], uint16(casLength))
+ if casLength > 0 {
+ for _, ca := range m.CertificateAuthoritiesNames {
+ out = append(out, []byte{0x00, 0x00}...)
+ binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(ca)))
+ out = append(out, ca...)
+ }
+ }
+ return out, nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageCertificateRequest) Unmarshal(data []byte) error {
+ if len(data) < messageCertificateRequestMinLength {
+ return errBufferTooSmall
+ }
+
+ offset := 0
+ certificateTypesLength := int(data[0])
+ offset++
+
+ if (offset + certificateTypesLength) > len(data) {
+ return errBufferTooSmall
+ }
+
+ for i := 0; i < certificateTypesLength; i++ {
+ certType := clientcertificate.Type(data[offset+i])
+ if _, ok := clientcertificate.Types()[certType]; ok {
+ m.CertificateTypes = append(m.CertificateTypes, certType)
+ }
+ }
+ offset += certificateTypesLength
+ if len(data) < offset+2 {
+ return errBufferTooSmall
+ }
+ signatureHashAlgorithmsLength := int(binary.BigEndian.Uint16(data[offset:]))
+ offset += 2
+
+ if (offset + signatureHashAlgorithmsLength) > len(data) {
+ return errBufferTooSmall
+ }
+
+ for i := 0; i < signatureHashAlgorithmsLength; i += 2 {
+ if len(data) < (offset + i + 2) {
+ return errBufferTooSmall
+ }
+ h := hash.Algorithm(data[offset+i])
+ s := signature.Algorithm(data[offset+i+1])
+
+ if _, ok := hash.Algorithms()[h]; !ok {
+ continue
+ } else if _, ok := signature.Algorithms()[s]; !ok {
+ continue
+ }
+ m.SignatureHashAlgorithms = append(m.SignatureHashAlgorithms, signaturehash.Algorithm{Signature: s, Hash: h})
+ }
+
+ offset += signatureHashAlgorithmsLength
+ if len(data) < offset+2 {
+ return errBufferTooSmall
+ }
+ casLength := int(binary.BigEndian.Uint16(data[offset:]))
+ offset += 2
+ if (offset + casLength) > len(data) {
+ return errBufferTooSmall
+ }
+ cas := make([]byte, casLength)
+ copy(cas, data[offset:offset+casLength])
+ m.CertificateAuthoritiesNames = nil
+ for len(cas) > 0 {
+ if len(cas) < 2 {
+ return errBufferTooSmall
+ }
+ caLen := binary.BigEndian.Uint16(cas)
+ cas = cas[2:]
+
+ if len(cas) < int(caLen) {
+ return errBufferTooSmall
+ }
+
+ m.CertificateAuthoritiesNames = append(m.CertificateAuthoritiesNames, cas[:caLen])
+ cas = cas[caLen:]
+ }
+
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go
new file mode 100644
index 0000000..9e02a9c
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_certificate_verify.go
@@ -0,0 +1,64 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package handshake
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/crypto/hash"
+ "github.com/pion/dtls/v2/pkg/crypto/signature"
+)
+
+// MessageCertificateVerify provide explicit verification of a
+// client certificate.
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.8
+type MessageCertificateVerify struct {
+ HashAlgorithm hash.Algorithm
+ SignatureAlgorithm signature.Algorithm
+ Signature []byte
+}
+
+const handshakeMessageCertificateVerifyMinLength = 4
+
+// Type returns the Handshake Type
+func (m MessageCertificateVerify) Type() Type {
+ return TypeCertificateVerify
+}
+
+// Marshal encodes the Handshake
+func (m *MessageCertificateVerify) Marshal() ([]byte, error) {
+ out := make([]byte, 1+1+2+len(m.Signature))
+
+ out[0] = byte(m.HashAlgorithm)
+ out[1] = byte(m.SignatureAlgorithm)
+ binary.BigEndian.PutUint16(out[2:], uint16(len(m.Signature)))
+ copy(out[4:], m.Signature)
+ return out, nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageCertificateVerify) Unmarshal(data []byte) error {
+ if len(data) < handshakeMessageCertificateVerifyMinLength {
+ return errBufferTooSmall
+ }
+
+ m.HashAlgorithm = hash.Algorithm(data[0])
+ if _, ok := hash.Algorithms()[m.HashAlgorithm]; !ok {
+ return errInvalidHashAlgorithm
+ }
+
+ m.SignatureAlgorithm = signature.Algorithm(data[1])
+ if _, ok := signature.Algorithms()[m.SignatureAlgorithm]; !ok {
+ return errInvalidSignatureAlgorithm
+ }
+
+ signatureLength := int(binary.BigEndian.Uint16(data[2:]))
+ if (signatureLength + 4) != len(data) {
+ return errBufferTooSmall
+ }
+
+ m.Signature = append([]byte{}, data[4:]...)
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go
new file mode 100644
index 0000000..bea6dd9
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_hello.go
@@ -0,0 +1,141 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package handshake
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/extension"
+)
+
+/*
+MessageClientHello is for when a client first connects to a server it is
+required to send the client hello as its first message. The client can also send a
+client hello in response to a hello request or on its own
+initiative in order to renegotiate the security parameters in an
+existing connection.
+*/
+type MessageClientHello struct {
+ Version protocol.Version
+ Random Random
+ Cookie []byte
+
+ SessionID []byte
+
+ CipherSuiteIDs []uint16
+ CompressionMethods []*protocol.CompressionMethod
+ Extensions []extension.Extension
+}
+
+const handshakeMessageClientHelloVariableWidthStart = 34
+
+// Type returns the Handshake Type
+func (m MessageClientHello) Type() Type {
+ return TypeClientHello
+}
+
+// Marshal encodes the Handshake
+func (m *MessageClientHello) Marshal() ([]byte, error) {
+ if len(m.Cookie) > 255 {
+ return nil, errCookieTooLong
+ }
+
+ out := make([]byte, handshakeMessageClientHelloVariableWidthStart)
+ out[0] = m.Version.Major
+ out[1] = m.Version.Minor
+
+ rand := m.Random.MarshalFixed()
+ copy(out[2:], rand[:])
+
+ out = append(out, byte(len(m.SessionID)))
+ out = append(out, m.SessionID...)
+
+ out = append(out, byte(len(m.Cookie)))
+ out = append(out, m.Cookie...)
+ out = append(out, encodeCipherSuiteIDs(m.CipherSuiteIDs)...)
+ out = append(out, protocol.EncodeCompressionMethods(m.CompressionMethods)...)
+
+ extensions, err := extension.Marshal(m.Extensions)
+ if err != nil {
+ return nil, err
+ }
+
+ return append(out, extensions...), nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageClientHello) Unmarshal(data []byte) error {
+ if len(data) < 2+RandomLength {
+ return errBufferTooSmall
+ }
+
+ m.Version.Major = data[0]
+ m.Version.Minor = data[1]
+
+ var random [RandomLength]byte
+ copy(random[:], data[2:])
+ m.Random.UnmarshalFixed(random)
+
+ // rest of packet has variable width sections
+ currOffset := handshakeMessageClientHelloVariableWidthStart
+
+ currOffset++
+ if len(data) <= currOffset {
+ return errBufferTooSmall
+ }
+ n := int(data[currOffset-1])
+ if len(data) <= currOffset+n {
+ return errBufferTooSmall
+ }
+ m.SessionID = append([]byte{}, data[currOffset:currOffset+n]...)
+ currOffset += len(m.SessionID)
+
+ currOffset++
+ if len(data) <= currOffset {
+ return errBufferTooSmall
+ }
+ n = int(data[currOffset-1])
+ if len(data) <= currOffset+n {
+ return errBufferTooSmall
+ }
+ m.Cookie = append([]byte{}, data[currOffset:currOffset+n]...)
+ currOffset += len(m.Cookie)
+
+ // Cipher Suites
+ if len(data) < currOffset {
+ return errBufferTooSmall
+ }
+ cipherSuiteIDs, err := decodeCipherSuiteIDs(data[currOffset:])
+ if err != nil {
+ return err
+ }
+ m.CipherSuiteIDs = cipherSuiteIDs
+ if len(data) < currOffset+2 {
+ return errBufferTooSmall
+ }
+ currOffset += int(binary.BigEndian.Uint16(data[currOffset:])) + 2
+
+ // Compression Methods
+ if len(data) < currOffset {
+ return errBufferTooSmall
+ }
+ compressionMethods, err := protocol.DecodeCompressionMethods(data[currOffset:])
+ if err != nil {
+ return err
+ }
+ m.CompressionMethods = compressionMethods
+ if len(data) < currOffset {
+ return errBufferTooSmall
+ }
+ currOffset += int(data[currOffset]) + 1
+
+ // Extensions
+ extensions, err := extension.Unmarshal(data[currOffset:])
+ if err != nil {
+ return err
+ }
+ m.Extensions = extensions
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go
new file mode 100644
index 0000000..2abcd5b
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_client_key_exchange.go
@@ -0,0 +1,81 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package handshake
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/internal/ciphersuite/types"
+)
+
+// MessageClientKeyExchange is a DTLS Handshake Message
+// With this message, the premaster secret is set, either by direct
+// transmission of the RSA-encrypted secret or by the transmission of
+// Diffie-Hellman parameters that will allow each side to agree upon
+// the same premaster secret.
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.7
+type MessageClientKeyExchange struct {
+ IdentityHint []byte
+ PublicKey []byte
+
+ // for unmarshaling
+ KeyExchangeAlgorithm types.KeyExchangeAlgorithm
+}
+
+// Type returns the Handshake Type
+func (m MessageClientKeyExchange) Type() Type {
+ return TypeClientKeyExchange
+}
+
+// Marshal encodes the Handshake
+func (m *MessageClientKeyExchange) Marshal() (out []byte, err error) {
+ if m.IdentityHint == nil && m.PublicKey == nil {
+ return nil, errInvalidClientKeyExchange
+ }
+
+ if m.IdentityHint != nil {
+ out = append([]byte{0x00, 0x00}, m.IdentityHint...)
+ binary.BigEndian.PutUint16(out, uint16(len(out)-2))
+ }
+
+ if m.PublicKey != nil {
+ out = append(out, byte(len(m.PublicKey)))
+ out = append(out, m.PublicKey...)
+ }
+
+ return out, nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageClientKeyExchange) Unmarshal(data []byte) error {
+ switch {
+ case len(data) < 2:
+ return errBufferTooSmall
+ case m.KeyExchangeAlgorithm == types.KeyExchangeAlgorithmNone:
+ return errCipherSuiteUnset
+ }
+
+ offset := 0
+ if m.KeyExchangeAlgorithm.Has(types.KeyExchangeAlgorithmPsk) {
+ pskLength := int(binary.BigEndian.Uint16(data))
+ if pskLength > len(data)-2 {
+ return errBufferTooSmall
+ }
+
+ m.IdentityHint = append([]byte{}, data[2:pskLength+2]...)
+ offset += pskLength + 2
+ }
+
+ if m.KeyExchangeAlgorithm.Has(types.KeyExchangeAlgorithmEcdhe) {
+ publicKeyLength := int(data[offset])
+ if publicKeyLength > len(data)-1-offset {
+ return errBufferTooSmall
+ }
+
+ m.PublicKey = append([]byte{}, data[offset+1:]...)
+ }
+
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go
new file mode 100644
index 0000000..255aedd
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_finished.go
@@ -0,0 +1,30 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package handshake
+
+// MessageFinished is a DTLS Handshake Message
+// this message is the first one protected with the just
+// negotiated algorithms, keys, and secrets. Recipients of Finished
+// messages MUST verify that the contents are correct.
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.9
+type MessageFinished struct {
+ VerifyData []byte
+}
+
+// Type returns the Handshake Type
+func (m MessageFinished) Type() Type {
+ return TypeFinished
+}
+
+// Marshal encodes the Handshake
+func (m *MessageFinished) Marshal() ([]byte, error) {
+ return append([]byte{}, m.VerifyData...), nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageFinished) Unmarshal(data []byte) error {
+ m.VerifyData = append([]byte{}, data...)
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go
new file mode 100644
index 0000000..398e59c
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_hello_verify_request.go
@@ -0,0 +1,65 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package handshake
+
+import (
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+// MessageHelloVerifyRequest is as follows:
+//
+// struct {
+// ProtocolVersion server_version;
+// opaque cookie<0..2^8-1>;
+// } HelloVerifyRequest;
+//
+// The HelloVerifyRequest message type is hello_verify_request(3).
+//
+// When the client sends its ClientHello message to the server, the server
+// MAY respond with a HelloVerifyRequest message. This message contains
+// a stateless cookie generated using the technique of [PHOTURIS]. The
+// client MUST retransmit the ClientHello with the cookie added.
+//
+// https://tools.ietf.org/html/rfc6347#section-4.2.1
+type MessageHelloVerifyRequest struct {
+ Version protocol.Version
+ Cookie []byte
+}
+
+// Type returns the Handshake Type
+func (m MessageHelloVerifyRequest) Type() Type {
+ return TypeHelloVerifyRequest
+}
+
+// Marshal encodes the Handshake
+func (m *MessageHelloVerifyRequest) Marshal() ([]byte, error) {
+ if len(m.Cookie) > 255 {
+ return nil, errCookieTooLong
+ }
+
+ out := make([]byte, 3+len(m.Cookie))
+ out[0] = m.Version.Major
+ out[1] = m.Version.Minor
+ out[2] = byte(len(m.Cookie))
+ copy(out[3:], m.Cookie)
+
+ return out, nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageHelloVerifyRequest) Unmarshal(data []byte) error {
+ if len(data) < 3 {
+ return errBufferTooSmall
+ }
+ m.Version.Major = data[0]
+ m.Version.Minor = data[1]
+ cookieLength := int(data[2])
+ if len(data) < cookieLength+3 {
+ return errBufferTooSmall
+ }
+ m.Cookie = make([]byte, cookieLength)
+
+ copy(m.Cookie, data[3:3+cookieLength])
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go
new file mode 100644
index 0000000..caf186d
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello.go
@@ -0,0 +1,122 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package handshake
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/extension"
+)
+
+// MessageServerHello is sent in response to a ClientHello
+// message when it was able to find an acceptable set of algorithms.
+// If it cannot find such a match, it will respond with a handshake
+// failure alert.
+//
+// https://tools.ietf.org/html/rfc5246#section-7.4.1.3
+type MessageServerHello struct {
+ Version protocol.Version
+ Random Random
+
+ SessionID []byte
+
+ CipherSuiteID *uint16
+ CompressionMethod *protocol.CompressionMethod
+ Extensions []extension.Extension
+}
+
+const messageServerHelloVariableWidthStart = 2 + RandomLength
+
+// Type returns the Handshake Type
+func (m MessageServerHello) Type() Type {
+ return TypeServerHello
+}
+
+// Marshal encodes the Handshake
+func (m *MessageServerHello) Marshal() ([]byte, error) {
+ if m.CipherSuiteID == nil {
+ return nil, errCipherSuiteUnset
+ } else if m.CompressionMethod == nil {
+ return nil, errCompressionMethodUnset
+ }
+
+ out := make([]byte, messageServerHelloVariableWidthStart)
+ out[0] = m.Version.Major
+ out[1] = m.Version.Minor
+
+ rand := m.Random.MarshalFixed()
+ copy(out[2:], rand[:])
+
+ out = append(out, byte(len(m.SessionID)))
+ out = append(out, m.SessionID...)
+
+ out = append(out, []byte{0x00, 0x00}...)
+ binary.BigEndian.PutUint16(out[len(out)-2:], *m.CipherSuiteID)
+
+ out = append(out, byte(m.CompressionMethod.ID))
+
+ extensions, err := extension.Marshal(m.Extensions)
+ if err != nil {
+ return nil, err
+ }
+
+ return append(out, extensions...), nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageServerHello) Unmarshal(data []byte) error {
+ if len(data) < 2+RandomLength {
+ return errBufferTooSmall
+ }
+
+ m.Version.Major = data[0]
+ m.Version.Minor = data[1]
+
+ var random [RandomLength]byte
+ copy(random[:], data[2:])
+ m.Random.UnmarshalFixed(random)
+
+ currOffset := messageServerHelloVariableWidthStart
+ currOffset++
+ if len(data) <= currOffset {
+ return errBufferTooSmall
+ }
+
+ n := int(data[currOffset-1])
+ if len(data) <= currOffset+n {
+ return errBufferTooSmall
+ }
+ m.SessionID = append([]byte{}, data[currOffset:currOffset+n]...)
+ currOffset += len(m.SessionID)
+
+ if len(data) < currOffset+2 {
+ return errBufferTooSmall
+ }
+ m.CipherSuiteID = new(uint16)
+ *m.CipherSuiteID = binary.BigEndian.Uint16(data[currOffset:])
+ currOffset += 2
+
+ if len(data) <= currOffset {
+ return errBufferTooSmall
+ }
+ if compressionMethod, ok := protocol.CompressionMethods()[protocol.CompressionMethodID(data[currOffset])]; ok {
+ m.CompressionMethod = compressionMethod
+ currOffset++
+ } else {
+ return errInvalidCompressionMethod
+ }
+
+ if len(data) <= currOffset {
+ m.Extensions = []extension.Extension{}
+ return nil
+ }
+
+ extensions, err := extension.Unmarshal(data[currOffset:])
+ if err != nil {
+ return err
+ }
+ m.Extensions = extensions
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go
new file mode 100644
index 0000000..b187dd4
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_hello_done.go
@@ -0,0 +1,24 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package handshake
+
+// MessageServerHelloDone is final non-encrypted message from server
+// this communicates server has sent all its handshake messages and next
+// should be MessageFinished
+type MessageServerHelloDone struct{}
+
+// Type returns the Handshake Type
+func (m MessageServerHelloDone) Type() Type {
+ return TypeServerHelloDone
+}
+
+// Marshal encodes the Handshake
+func (m *MessageServerHelloDone) Marshal() ([]byte, error) {
+ return []byte{}, nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageServerHelloDone) Unmarshal([]byte) error {
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go
new file mode 100644
index 0000000..82abbe0
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/message_server_key_exchange.go
@@ -0,0 +1,148 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package handshake
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/internal/ciphersuite/types"
+ "github.com/pion/dtls/v2/pkg/crypto/elliptic"
+ "github.com/pion/dtls/v2/pkg/crypto/hash"
+ "github.com/pion/dtls/v2/pkg/crypto/signature"
+)
+
+// MessageServerKeyExchange supports ECDH and PSK
+type MessageServerKeyExchange struct {
+ IdentityHint []byte
+
+ EllipticCurveType elliptic.CurveType
+ NamedCurve elliptic.Curve
+ PublicKey []byte
+ HashAlgorithm hash.Algorithm
+ SignatureAlgorithm signature.Algorithm
+ Signature []byte
+
+ // for unmarshaling
+ KeyExchangeAlgorithm types.KeyExchangeAlgorithm
+}
+
+// Type returns the Handshake Type
+func (m MessageServerKeyExchange) Type() Type {
+ return TypeServerKeyExchange
+}
+
+// Marshal encodes the Handshake
+func (m *MessageServerKeyExchange) Marshal() ([]byte, error) {
+ var out []byte
+ if m.IdentityHint != nil {
+ out = append([]byte{0x00, 0x00}, m.IdentityHint...)
+ binary.BigEndian.PutUint16(out, uint16(len(out)-2))
+ }
+
+ if m.EllipticCurveType == 0 || len(m.PublicKey) == 0 {
+ return out, nil
+ }
+ out = append(out, byte(m.EllipticCurveType), 0x00, 0x00)
+ binary.BigEndian.PutUint16(out[len(out)-2:], uint16(m.NamedCurve))
+
+ out = append(out, byte(len(m.PublicKey)))
+ out = append(out, m.PublicKey...)
+ switch {
+ case m.HashAlgorithm != hash.None && len(m.Signature) == 0:
+ return nil, errInvalidHashAlgorithm
+ case m.HashAlgorithm == hash.None && len(m.Signature) > 0:
+ return nil, errInvalidHashAlgorithm
+ case m.SignatureAlgorithm == signature.Anonymous && (m.HashAlgorithm != hash.None || len(m.Signature) > 0):
+ return nil, errInvalidSignatureAlgorithm
+ case m.SignatureAlgorithm == signature.Anonymous:
+ return out, nil
+ }
+
+ out = append(out, []byte{byte(m.HashAlgorithm), byte(m.SignatureAlgorithm), 0x00, 0x00}...)
+ binary.BigEndian.PutUint16(out[len(out)-2:], uint16(len(m.Signature)))
+ out = append(out, m.Signature...)
+
+ return out, nil
+}
+
+// Unmarshal populates the message from encoded data
+func (m *MessageServerKeyExchange) Unmarshal(data []byte) error {
+ switch {
+ case len(data) < 2:
+ return errBufferTooSmall
+ case m.KeyExchangeAlgorithm == types.KeyExchangeAlgorithmNone:
+ return errCipherSuiteUnset
+ }
+
+ hintLength := binary.BigEndian.Uint16(data)
+ if int(hintLength) <= len(data)-2 && m.KeyExchangeAlgorithm.Has(types.KeyExchangeAlgorithmPsk) {
+ m.IdentityHint = append([]byte{}, data[2:2+hintLength]...)
+ data = data[2+hintLength:]
+ }
+ if m.KeyExchangeAlgorithm == types.KeyExchangeAlgorithmPsk {
+ if len(data) == 0 {
+ return nil
+ }
+ return errLengthMismatch
+ }
+
+ if !m.KeyExchangeAlgorithm.Has(types.KeyExchangeAlgorithmEcdhe) {
+ return errLengthMismatch
+ }
+
+ if _, ok := elliptic.CurveTypes()[elliptic.CurveType(data[0])]; ok {
+ m.EllipticCurveType = elliptic.CurveType(data[0])
+ } else {
+ return errInvalidEllipticCurveType
+ }
+
+ if len(data[1:]) < 2 {
+ return errBufferTooSmall
+ }
+ m.NamedCurve = elliptic.Curve(binary.BigEndian.Uint16(data[1:3]))
+ if _, ok := elliptic.Curves()[m.NamedCurve]; !ok {
+ return errInvalidNamedCurve
+ }
+ if len(data) < 4 {
+ return errBufferTooSmall
+ }
+
+ publicKeyLength := int(data[3])
+ offset := 4 + publicKeyLength
+ if len(data) < offset {
+ return errBufferTooSmall
+ }
+ m.PublicKey = append([]byte{}, data[4:offset]...)
+
+ // Anon connection doesn't contains hashAlgorithm, signatureAlgorithm, signature
+ if len(data) == offset {
+ return nil
+ } else if len(data) <= offset {
+ return errBufferTooSmall
+ }
+
+ m.HashAlgorithm = hash.Algorithm(data[offset])
+ if _, ok := hash.Algorithms()[m.HashAlgorithm]; !ok {
+ return errInvalidHashAlgorithm
+ }
+ offset++
+ if len(data) <= offset {
+ return errBufferTooSmall
+ }
+ m.SignatureAlgorithm = signature.Algorithm(data[offset])
+ if _, ok := signature.Algorithms()[m.SignatureAlgorithm]; !ok {
+ return errInvalidSignatureAlgorithm
+ }
+ offset++
+ if len(data) < offset+2 {
+ return errBufferTooSmall
+ }
+ signatureLength := int(binary.BigEndian.Uint16(data[offset:]))
+ offset += 2
+ if len(data) < offset+signatureLength {
+ return errBufferTooSmall
+ }
+ m.Signature = append([]byte{}, data[offset:offset+signatureLength]...)
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go
new file mode 100644
index 0000000..56f3756
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/handshake/random.go
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package handshake
+
+import (
+ "crypto/rand"
+ "encoding/binary"
+ "time"
+)
+
+// Consts for Random in Handshake
+const (
+ RandomBytesLength = 28
+ RandomLength = RandomBytesLength + 4
+)
+
+// Random value that is used in ClientHello and ServerHello
+//
+// https://tools.ietf.org/html/rfc4346#section-7.4.1.2
+type Random struct {
+ GMTUnixTime time.Time
+ RandomBytes [RandomBytesLength]byte
+}
+
+// MarshalFixed encodes the Handshake
+func (r *Random) MarshalFixed() [RandomLength]byte {
+ var out [RandomLength]byte
+
+ binary.BigEndian.PutUint32(out[0:], uint32(r.GMTUnixTime.Unix()))
+ copy(out[4:], r.RandomBytes[:])
+
+ return out
+}
+
+// UnmarshalFixed populates the message from encoded data
+func (r *Random) UnmarshalFixed(data [RandomLength]byte) {
+ r.GMTUnixTime = time.Unix(int64(binary.BigEndian.Uint32(data[0:])), 0)
+ copy(r.RandomBytes[:], data[4:])
+}
+
+// Populate fills the handshakeRandom with random values
+// may be called multiple times
+func (r *Random) Populate() error {
+ r.GMTUnixTime = time.Now()
+
+ tmp := make([]byte, RandomBytesLength)
+ _, err := rand.Read(tmp)
+ copy(r.RandomBytes[:], tmp)
+
+ return err
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go
new file mode 100644
index 0000000..1c18988
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/errors.go
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package recordlayer implements the TLS Record Layer https://tools.ietf.org/html/rfc5246#section-6
+package recordlayer
+
+import (
+ "errors"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+var (
+ // ErrInvalidPacketLength is returned when the packet length too small or declared length do not match
+ ErrInvalidPacketLength = &protocol.TemporaryError{Err: errors.New("packet length and declared length do not match")} //nolint:goerr113
+
+ errBufferTooSmall = &protocol.TemporaryError{Err: errors.New("buffer is too small")} //nolint:goerr113
+ errSequenceNumberOverflow = &protocol.InternalError{Err: errors.New("sequence number overflow")} //nolint:goerr113
+ errUnsupportedProtocolVersion = &protocol.FatalError{Err: errors.New("unsupported protocol version")} //nolint:goerr113
+ errInvalidContentType = &protocol.TemporaryError{Err: errors.New("invalid content type")} //nolint:goerr113
+)
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go
new file mode 100644
index 0000000..9225250
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/header.go
@@ -0,0 +1,64 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package recordlayer
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/internal/util"
+ "github.com/pion/dtls/v2/pkg/protocol"
+)
+
+// Header implements a TLS RecordLayer header
+type Header struct {
+ ContentType protocol.ContentType
+ ContentLen uint16
+ Version protocol.Version
+ Epoch uint16
+ SequenceNumber uint64 // uint48 in spec
+}
+
+// RecordLayer enums
+const (
+ HeaderSize = 13
+ MaxSequenceNumber = 0x0000FFFFFFFFFFFF
+)
+
+// Marshal encodes a TLS RecordLayer Header to binary
+func (h *Header) Marshal() ([]byte, error) {
+ if h.SequenceNumber > MaxSequenceNumber {
+ return nil, errSequenceNumberOverflow
+ }
+
+ out := make([]byte, HeaderSize)
+ 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)
+ return out, nil
+}
+
+// Unmarshal populates a TLS RecordLayer Header from binary
+func (h *Header) Unmarshal(data []byte) error {
+ if len(data) < HeaderSize {
+ return errBufferTooSmall
+ }
+ h.ContentType = protocol.ContentType(data[0])
+ h.Version.Major = data[1]
+ h.Version.Minor = data[2]
+ h.Epoch = binary.BigEndian.Uint16(data[3:])
+
+ // SequenceNumber is stored as uint48, make into uint64
+ seqCopy := make([]byte, 8)
+ copy(seqCopy[2:], data[5:11])
+ h.SequenceNumber = binary.BigEndian.Uint64(seqCopy)
+
+ if !h.Version.Equal(protocol.Version1_0) && !h.Version.Equal(protocol.Version1_2) {
+ return errUnsupportedProtocolVersion
+ }
+
+ return nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go
new file mode 100644
index 0000000..a570f74
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/recordlayer/recordlayer.go
@@ -0,0 +1,102 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package recordlayer
+
+import (
+ "encoding/binary"
+
+ "github.com/pion/dtls/v2/pkg/protocol"
+ "github.com/pion/dtls/v2/pkg/protocol/alert"
+ "github.com/pion/dtls/v2/pkg/protocol/handshake"
+)
+
+// 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:
+//
+// 1. Handshake messages—used for algorithm negotiation and key establishment.
+// 2. ChangeCipherSpec messages—really part of the handshake but technically a separate kind of message.
+// 3. Alert messages—used to signal that errors have occurred
+// 4. Application layer data
+//
+// The DTLS record layer is extremely similar to that of TLS 1.1. The
+// only change is the inclusion of an explicit sequence number in the
+// record. This sequence number allows the recipient to correctly
+// verify the TLS MAC.
+//
+// https://tools.ietf.org/html/rfc4347#section-4.1
+type RecordLayer struct {
+ Header Header
+ Content protocol.Content
+}
+
+// Marshal encodes the RecordLayer to binary
+func (r *RecordLayer) Marshal() ([]byte, error) {
+ contentRaw, err := r.Content.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ r.Header.ContentLen = uint16(len(contentRaw))
+ r.Header.ContentType = r.Content.ContentType()
+
+ headerRaw, err := r.Header.Marshal()
+ if err != nil {
+ return nil, err
+ }
+
+ return append(headerRaw, contentRaw...), nil
+}
+
+// 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]) {
+ case protocol.ContentTypeChangeCipherSpec:
+ r.Content = &protocol.ChangeCipherSpec{}
+ case protocol.ContentTypeAlert:
+ r.Content = &alert.Alert{}
+ case protocol.ContentTypeHandshake:
+ r.Content = &handshake.Handshake{}
+ case protocol.ContentTypeApplicationData:
+ r.Content = &protocol.ApplicationData{}
+ default:
+ return errInvalidContentType
+ }
+
+ return r.Content.Unmarshal(data[HeaderSize:])
+}
+
+// UnpackDatagram extracts all RecordLayer messages from a single datagram.
+// Note that as with TLS, multiple handshake messages may be placed in
+// the same DTLS record, provided that there is room and that they are
+// part of the same flight. Thus, there are two acceptable ways to pack
+// two DTLS messages into the same datagram: in the same record or in
+// separate records.
+// https://tools.ietf.org/html/rfc6347#section-4.2.3
+func UnpackDatagram(buf []byte) ([][]byte, error) {
+ out := [][]byte{}
+
+ for offset := 0; len(buf) != offset; {
+ if len(buf)-offset <= HeaderSize {
+ return nil, ErrInvalidPacketLength
+ }
+
+ pktLen := (HeaderSize + 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
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/pkg/protocol/version.go b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/version.go
new file mode 100644
index 0000000..c4d94ac
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/pkg/protocol/version.go
@@ -0,0 +1,24 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package protocol provides the DTLS wire format
+package protocol
+
+// Version enums
+var (
+ Version1_0 = Version{Major: 0xfe, Minor: 0xff} //nolint:gochecknoglobals
+ Version1_2 = Version{Major: 0xfe, Minor: 0xfd} //nolint:gochecknoglobals
+)
+
+// Version is the minor/major value in the RecordLayer
+// and ClientHello/ServerHello
+//
+// https://tools.ietf.org/html/rfc4346#section-6.2.1
+type Version struct {
+ Major, Minor uint8
+}
+
+// Equal determines if two protocol versions are equal
+func (v Version) Equal(x Version) bool {
+ return v.Major == x.Major && v.Minor == x.Minor
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/renovate.json b/server/vendor/github.com/pion/dtls/v2/renovate.json
new file mode 100644
index 0000000..f1bb98c
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/renovate.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": [
+ "github>pion/renovate-config"
+ ]
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/resume.go b/server/vendor/github.com/pion/dtls/v2/resume.go
new file mode 100644
index 0000000..f070d75
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/resume.go
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "context"
+ "net"
+)
+
+// Resume imports an already established dtls connection using a specific dtls state
+func Resume(state *State, conn net.Conn, config *Config) (*Conn, error) {
+ if err := state.initCipherSuite(); err != nil {
+ return nil, err
+ }
+ dconn, err := createConn(conn, config, state.isClient)
+ if err != nil {
+ return nil, err
+ }
+ c, err := handshakeConn(context.Background(), dconn, config, state.isClient, state)
+ if err != nil {
+ return nil, err
+ }
+
+ return c, nil
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/session.go b/server/vendor/github.com/pion/dtls/v2/session.go
new file mode 100644
index 0000000..99bf5a4
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/session.go
@@ -0,0 +1,24 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+// Session store data needed in resumption
+type Session struct {
+ // ID store session id
+ ID []byte
+ // Secret store session master secret
+ Secret []byte
+}
+
+// SessionStore defines methods needed for session resumption.
+type SessionStore interface {
+ // Set save a session.
+ // For client, use server name as key.
+ // For server, use session id.
+ Set(key []byte, s Session) error
+ // Get fetch a session.
+ Get(key []byte) (Session, error)
+ // Del clean saved session.
+ Del(key []byte) error
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/srtp_protection_profile.go b/server/vendor/github.com/pion/dtls/v2/srtp_protection_profile.go
new file mode 100644
index 0000000..8b7e471
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/srtp_protection_profile.go
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import "github.com/pion/dtls/v2/pkg/protocol/extension"
+
+// SRTPProtectionProfile defines the parameters and options that are in effect for the SRTP processing
+// https://tools.ietf.org/html/rfc5764#section-4.1.2
+type SRTPProtectionProfile = extension.SRTPProtectionProfile
+
+const (
+ SRTP_AES128_CM_HMAC_SHA1_80 SRTPProtectionProfile = extension.SRTP_AES128_CM_HMAC_SHA1_80 // nolint:revive,stylecheck
+ SRTP_AES128_CM_HMAC_SHA1_32 SRTPProtectionProfile = extension.SRTP_AES128_CM_HMAC_SHA1_32 // nolint:revive,stylecheck
+ SRTP_AES256_CM_SHA1_80 SRTPProtectionProfile = extension.SRTP_AES256_CM_SHA1_80 // nolint:revive,stylecheck
+ SRTP_AES256_CM_SHA1_32 SRTPProtectionProfile = extension.SRTP_AES256_CM_SHA1_32 // nolint:revive,stylecheck
+ SRTP_NULL_HMAC_SHA1_80 SRTPProtectionProfile = extension.SRTP_NULL_HMAC_SHA1_80 // nolint:revive,stylecheck
+ SRTP_NULL_HMAC_SHA1_32 SRTPProtectionProfile = extension.SRTP_NULL_HMAC_SHA1_32 // nolint:revive,stylecheck
+ SRTP_AEAD_AES_128_GCM SRTPProtectionProfile = extension.SRTP_AEAD_AES_128_GCM // nolint:revive,stylecheck
+ SRTP_AEAD_AES_256_GCM SRTPProtectionProfile = extension.SRTP_AEAD_AES_256_GCM // nolint:revive,stylecheck
+)
diff --git a/server/vendor/github.com/pion/dtls/v2/state.go b/server/vendor/github.com/pion/dtls/v2/state.go
new file mode 100644
index 0000000..9f26d43
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/state.go
@@ -0,0 +1,228 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+import (
+ "bytes"
+ "encoding/gob"
+ "sync/atomic"
+
+ "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"
+)
+
+// State holds the dtls connection state and implements both encoding.BinaryMarshaler and encoding.BinaryUnmarshaler
+type State struct {
+ localEpoch, remoteEpoch atomic.Value
+ localSequenceNumber []uint64 // uint48
+ localRandom, remoteRandom handshake.Random
+ masterSecret []byte
+ cipherSuite CipherSuite // nil if a cipherSuite hasn't been chosen
+
+ srtpProtectionProfile atomic.Value // Negotiated SRTPProtectionProfile
+ PeerCertificates [][]byte
+ IdentityHint []byte
+ SessionID []byte
+
+ isClient bool
+
+ preMasterSecret []byte
+ extendedMasterSecret bool
+
+ namedCurve elliptic.Curve
+ localKeypair *elliptic.Keypair
+ cookie []byte
+ handshakeSendSequence int
+ handshakeRecvSequence int
+ serverName string
+ remoteRequestedCertificate bool // Did we get a CertificateRequest
+ localCertificatesVerify []byte // cache CertificateVerify
+ localVerifyData []byte // cached VerifyData
+ localKeySignature []byte // cached keySignature
+ peerCertificatesVerified bool
+
+ replayDetector []replaydetector.ReplayDetector
+
+ peerSupportedProtocols []string
+ NegotiatedProtocol string
+}
+
+type serializedState struct {
+ LocalEpoch uint16
+ RemoteEpoch uint16
+ LocalRandom [handshake.RandomLength]byte
+ RemoteRandom [handshake.RandomLength]byte
+ CipherSuiteID uint16
+ MasterSecret []byte
+ SequenceNumber uint64
+ SRTPProtectionProfile uint16
+ PeerCertificates [][]byte
+ IdentityHint []byte
+ SessionID []byte
+ IsClient bool
+}
+
+func (s *State) clone() *State {
+ serialized := s.serialize()
+ state := &State{}
+ state.deserialize(*serialized)
+
+ return state
+}
+
+func (s *State) serialize() *serializedState {
+ // Marshal random values
+ localRnd := s.localRandom.MarshalFixed()
+ remoteRnd := s.remoteRandom.MarshalFixed()
+
+ epoch := s.getLocalEpoch()
+ return &serializedState{
+ LocalEpoch: s.getLocalEpoch(),
+ RemoteEpoch: s.getRemoteEpoch(),
+ CipherSuiteID: uint16(s.cipherSuite.ID()),
+ MasterSecret: s.masterSecret,
+ SequenceNumber: atomic.LoadUint64(&s.localSequenceNumber[epoch]),
+ LocalRandom: localRnd,
+ RemoteRandom: remoteRnd,
+ SRTPProtectionProfile: uint16(s.getSRTPProtectionProfile()),
+ PeerCertificates: s.PeerCertificates,
+ IdentityHint: s.IdentityHint,
+ SessionID: s.SessionID,
+ IsClient: s.isClient,
+ }
+}
+
+func (s *State) deserialize(serialized serializedState) {
+ // Set epoch values
+ epoch := serialized.LocalEpoch
+ s.localEpoch.Store(serialized.LocalEpoch)
+ s.remoteEpoch.Store(serialized.RemoteEpoch)
+
+ for len(s.localSequenceNumber) <= int(epoch) {
+ s.localSequenceNumber = append(s.localSequenceNumber, uint64(0))
+ }
+
+ // Set random values
+ localRandom := &handshake.Random{}
+ localRandom.UnmarshalFixed(serialized.LocalRandom)
+ s.localRandom = *localRandom
+
+ remoteRandom := &handshake.Random{}
+ remoteRandom.UnmarshalFixed(serialized.RemoteRandom)
+ s.remoteRandom = *remoteRandom
+
+ s.isClient = serialized.IsClient
+
+ // Set master secret
+ s.masterSecret = serialized.MasterSecret
+
+ // Set cipher suite
+ s.cipherSuite = cipherSuiteForID(CipherSuiteID(serialized.CipherSuiteID), nil)
+
+ atomic.StoreUint64(&s.localSequenceNumber[epoch], serialized.SequenceNumber)
+ s.setSRTPProtectionProfile(SRTPProtectionProfile(serialized.SRTPProtectionProfile))
+
+ // Set remote certificate
+ s.PeerCertificates = serialized.PeerCertificates
+ s.IdentityHint = serialized.IdentityHint
+ s.SessionID = serialized.SessionID
+}
+
+func (s *State) initCipherSuite() error {
+ if s.cipherSuite.IsInitialized() {
+ return nil
+ }
+
+ localRandom := s.localRandom.MarshalFixed()
+ remoteRandom := s.remoteRandom.MarshalFixed()
+
+ var err error
+ if s.isClient {
+ err = s.cipherSuite.Init(s.masterSecret, localRandom[:], remoteRandom[:], true)
+ } else {
+ err = s.cipherSuite.Init(s.masterSecret, remoteRandom[:], localRandom[:], false)
+ }
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// MarshalBinary is a binary.BinaryMarshaler.MarshalBinary implementation
+func (s *State) MarshalBinary() ([]byte, error) {
+ serialized := s.serialize()
+
+ var buf bytes.Buffer
+ enc := gob.NewEncoder(&buf)
+ if err := enc.Encode(*serialized); err != nil {
+ return nil, err
+ }
+ return buf.Bytes(), nil
+}
+
+// UnmarshalBinary is a binary.BinaryUnmarshaler.UnmarshalBinary implementation
+func (s *State) UnmarshalBinary(data []byte) error {
+ enc := gob.NewDecoder(bytes.NewBuffer(data))
+ var serialized serializedState
+ if err := enc.Decode(&serialized); err != nil {
+ return err
+ }
+
+ s.deserialize(serialized)
+
+ return s.initCipherSuite()
+}
+
+// ExportKeyingMaterial returns length bytes of exported key material in a new
+// slice as defined in RFC 5705.
+// This allows protocols to use DTLS for key establishment, but
+// then use some of the keying material for their own purposes
+func (s *State) ExportKeyingMaterial(label string, context []byte, length int) ([]byte, error) {
+ if s.getLocalEpoch() == 0 {
+ return nil, errHandshakeInProgress
+ } else if len(context) != 0 {
+ return nil, errContextUnsupported
+ } else if _, ok := invalidKeyingLabels()[label]; ok {
+ return nil, errReservedExportKeyingMaterial
+ }
+
+ localRandom := s.localRandom.MarshalFixed()
+ remoteRandom := s.remoteRandom.MarshalFixed()
+
+ seed := []byte(label)
+ if s.isClient {
+ seed = append(append(seed, localRandom[:]...), remoteRandom[:]...)
+ } else {
+ seed = append(append(seed, remoteRandom[:]...), localRandom[:]...)
+ }
+ return prf.PHash(s.masterSecret, seed, length, s.cipherSuite.HashFunc())
+}
+
+func (s *State) getRemoteEpoch() uint16 {
+ if remoteEpoch, ok := s.remoteEpoch.Load().(uint16); ok {
+ return remoteEpoch
+ }
+ return 0
+}
+
+func (s *State) getLocalEpoch() uint16 {
+ if localEpoch, ok := s.localEpoch.Load().(uint16); ok {
+ return localEpoch
+ }
+ return 0
+}
+
+func (s *State) setSRTPProtectionProfile(profile SRTPProtectionProfile) {
+ s.srtpProtectionProfile.Store(profile)
+}
+
+func (s *State) getSRTPProtectionProfile() SRTPProtectionProfile {
+ if val, ok := s.srtpProtectionProfile.Load().(SRTPProtectionProfile); ok {
+ return val
+ }
+
+ return 0
+}
diff --git a/server/vendor/github.com/pion/dtls/v2/util.go b/server/vendor/github.com/pion/dtls/v2/util.go
new file mode 100644
index 0000000..663c443
--- /dev/null
+++ b/server/vendor/github.com/pion/dtls/v2/util.go
@@ -0,0 +1,41 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package dtls
+
+func findMatchingSRTPProfile(a, b []SRTPProtectionProfile) (SRTPProtectionProfile, bool) {
+ for _, aProfile := range a {
+ for _, bProfile := range b {
+ if aProfile == bProfile {
+ return aProfile, true
+ }
+ }
+ }
+ return 0, false
+}
+
+func findMatchingCipherSuite(a, b []CipherSuite) (CipherSuite, bool) {
+ for _, aSuite := range a {
+ for _, bSuite := range b {
+ if aSuite.ID() == bSuite.ID() {
+ return aSuite, true
+ }
+ }
+ }
+ return nil, false
+}
+
+func splitBytes(bytes []byte, splitLen int) [][]byte {
+ splitBytes := make([][]byte, 0)
+ numBytes := len(bytes)
+ for i := 0; i < numBytes; i += splitLen {
+ j := i + splitLen
+ if j > numBytes {
+ j = numBytes
+ }
+
+ splitBytes = append(splitBytes, bytes[i:j])
+ }
+
+ return splitBytes
+}
diff --git a/server/vendor/github.com/pion/ice/v2/.gitignore b/server/vendor/github.com/pion/ice/v2/.gitignore
new file mode 100644
index 0000000..6e2f206
--- /dev/null
+++ b/server/vendor/github.com/pion/ice/v2/.gitignore
@@ -0,0 +1,28 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+### JetBrains IDE ###
+#####################
+.idea/
+
+### Emacs Temporary Files ###
+#############################
+*~
+
+### Folders ###
+###############
+bin/
+vendor/
+node_modules/
+
+### Files ###
+#############
+*.ivf
+*.ogg
+tags
+cover.out
+*.sw[poe]
+*.wasm
+examples/sfu-ws/cert.pem
+examples/sfu-ws/key.pem
+wasm_exec.js
diff --git a/server/vendor/github.com/pion/ice/v2/.golangci.yml b/server/vendor/github.com/pion/ice/v2/.golangci.yml
new file mode 100644
index 0000000..4e3eddf
--- /dev/null
+++ b/server/vendor/github.com/pion/ice/v2/.golangci.yml
@@ -0,0 +1,137 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+linters-settings:
+ govet:
+ check-shadowing: true
+ misspell:
+ locale: US
+ exhaustive:
+ default-signifies-exhaustive: true
+ gomodguard:
+ blocked:
+ modules:
+ - github.com/pkg/errors:
+ recommendations:
+ - errors
+ forbidigo:
+ forbid:
+ - ^fmt.Print(f|ln)?$
+ - ^log.(Panic|Fatal|Print)(f|ln)?$
+ - ^os.Exit$
+ - ^panic$
+ - ^print(ln)?$
+
+linters:
+ enable:
+ - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers
+ - bidichk # Checks for dangerous unicode character sequences
+ - bodyclose # checks whether HTTP response body is closed successfully
+ - contextcheck # check the function whether use a non-inherited context
+ - decorder # check declaration order and count of types, constants, variables and functions
+ - depguard # Go linter that checks if package imports are in a list of acceptable packages
+ - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+ - dupl # Tool for code clone detection
+ - durationcheck # check for two durations multiplied together
+ - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+ - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted.
+ - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.
+ - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13.
+ - exhaustive # check exhaustiveness of enum switch statements
+ - exportloopref # checks for pointers to enclosing loop variables
+ - forbidigo # Forbids identifiers
+ - forcetypeassert # finds forced type assertions
+ - gci # Gci control golang package import order and make it always deterministic.
+ - gochecknoglobals # Checks that no globals are present in Go code
+ - gochecknoinits # Checks that no init functions are present in Go code
+ - gocognit # Computes and checks the cognitive complexity of functions
+ - goconst # Finds repeated strings that could be replaced by a constant
+ - gocritic # The most opinionated Go source code linter
+ - godox # Tool for detection of FIXME, TODO and other comment keywords
+ - goerr113 # Golang linter to check the errors handling expressions
+ - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+ - gofumpt # Gofumpt checks whether code was gofumpt-ed.
+ - goheader # Checks is file header matches to pattern
+ - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports
+ - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod.
+ - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+ - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+ - gosec # Inspects source code for security problems
+ - gosimple # Linter for Go source code that specializes in simplifying a code
+ - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+ - grouper # An analyzer to analyze expression groups.
+ - importas # Enforces consistent import aliases
+ - ineffassign # Detects when assignments to existing variables are not used
+ - misspell # Finds commonly misspelled English words in comments
+ - nakedret # Finds naked returns in functions greater than a specified function length
+ - nilerr # Finds the code that returns nil even if it checks that the error is not nil.
+ - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value.
+ - noctx # noctx finds sending http request without context.Context
+ - predeclared # find code that shadows one of Go's predeclared identifiers
+ - revive # golint replacement, finds style mistakes
+ - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+ - stylecheck # Stylecheck is a replacement for golint
+ - tagliatelle # Checks the struct tags.
+ - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17
+ - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes
+ - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code
+ - unconvert # Remove unnecessary type conversions
+ - unparam # Reports unused function parameters
+ - unused # Checks Go code for unused constants, variables, functions and types
+ - wastedassign # wastedassign finds wasted assignment statements
+ - whitespace # Tool for detection of leading and trailing whitespace
+ disable:
+ - containedctx # containedctx is a linter that detects struct contained context.Context field
+ - cyclop # checks function and package cyclomatic complexity
+ - exhaustivestruct # Checks if all struct's fields are initialized
+ - funlen # Tool for detection of long functions
+ - gocyclo # Computes and checks the cyclomatic complexity of functions
+ - godot # Check if comments end in a period
+ - gomnd # An analyzer to detect magic numbers.
+ - ifshort # Checks that your code uses short syntax for if-statements whenever possible
+ - ireturn # Accept Interfaces, Return Concrete Types
+ - lll # Reports long lines
+ - maintidx # maintidx measures the maintainability index of each function.
+ - makezero # Finds slice declarations with non-zero initial length
+ - maligned # Tool to detect Go structs that would take less memory if their fields were sorted
+ - nestif # Reports deeply nested if statements
+ - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity
+ - nolintlint # Reports ill-formed or insufficient nolint directives
+ - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test
+ - prealloc # Finds slice declarations that could potentially be preallocated
+ - promlinter # Check Prometheus metrics naming via promlint
+ - rowserrcheck # checks whether Err of rows is checked successfully
+ - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed.
+ - testpackage # linter that makes you use a separate _test package
+ - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers
+ - varnamelen # checks that the length of a variable's name matches its scope
+ - wrapcheck # Checks that errors returned from external packages are wrapped
+ - wsl # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+ exclude-use-default: false
+ exclude-rules:
+ # Allow complex tests, better to be self contained
+ - path: _test\.go
+ linters:
+ - gocognit
+ - forbidigo
+
+ # Allow complex main function in examples
+ - path: examples
+ text: "of func `main` is high"
+ linters:
+ - gocognit
+
+ # Allow forbidden identifiers in examples
+ - path: examples
+ linters:
+ - forbidigo
+
+ # Allow forbidden identifiers in CLI commands
+ - path: cmd
+ linters:
+ - forbidigo
+
+run:
+ skip-dirs-use-default: false
diff --git a/server/vendor/github.com/pion/ice/v2/.goreleaser.yml b/server/vendor/github.com/pion/ice/v2/.goreleaser.yml
new file mode 100644
index 0000000..30093e9
--- /dev/null
+++ b/server/vendor/github.com/pion/ice/v2/.goreleaser.yml
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: 2023 The Pion community
+# SPDX-License-Identifier: MIT
+
+builds:
+- skip: true
diff --git a/server/vendor/github.com/pion/ice/v2/AUTHORS.txt b/server/vendor/github.com/pion/ice/v2/AUTHORS.txt
new file mode 100644
index 0000000..56269cc
--- /dev/null
+++ b/server/vendor/github.com/pion/ice/v2/AUTHORS.txt
@@ -0,0 +1,68 @@
+# Thank you to everyone that made Pion possible. If you are interested in contributing
+# we would love to have you https://github.com/pion/webrtc/wiki/Contributing
+#
+# This file is auto generated, using git to list all individuals contributors.
+# see https://github.com/pion/.goassets/blob/master/scripts/generate-authors.sh for the scripting
+Aaron France
+Adam Kiss
+adwpc
+Aleksandr Razumov
+aler9 <46489434+aler9@users.noreply.github.com>
+Anshul
+Antoine Baché
+ar-hosseinkhani
+Artur Shellunts
+Assad Obaid
+Atsushi Watanabe
+backkem
+boks1971
+buptczq
+cgojin
+Chao Yuan
+cnderrauber
+David Hamilton
+David Zhao
+David Zhao
+Eric Daniels
+Genteure
+Henry
+hexiang
+hn8 <10730886+hn8@users.noreply.github.com>
+Hugo Arregui
+Hugo Arregui
+Jason Maldonis
+Jerko Steiner
+JooYoung
+Juliusz Chroboczek
+Kacper Bąk <56700396+53jk1@users.noreply.github.com>
+Kevin Caffrey
+Konstantin Itskov
+korymiller1489
+Kyle Carberry
+Lander Noterman
+Luke Curley
+Meelap Shah
+Michael MacDonald
+Michael MacDonald
+Mikhail Bragin
+Miroslav Šedivý
+Nevio Vesic
+Ori Bernstein
+Rasmus Hanning
+Robert Eperjesi
+Sam Lancia
+Sam Lancia
+San9H0
+Sean DuBois
+Sean DuBois
+Sebastian Waisbrot
+Sidney San Martín
+Steffen Vogel
+Will Forcey
+Woodrow Douglass
+Yutaka Takeda
+ZHENK
+Zizheng Tai
+
+# List of contributors not appearing in Git history
+
diff --git a/server/vendor/github.com/pion/ice/v2/LICENSE b/server/vendor/github.com/pion/ice/v2/LICENSE
new file mode 100644
index 0000000..491caf6
--- /dev/null
+++ b/server/vendor/github.com/pion/ice/v2/LICENSE
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) 2023 The Pion community
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/server/vendor/github.com/pion/ice/v2/README.md b/server/vendor/github.com/pion/ice/v2/README.md
new file mode 100644
index 0000000..111ca70
--- /dev/null
+++ b/server/vendor/github.com/pion/ice/v2/README.md
@@ -0,0 +1,34 @@
+
+
+ Pion ICE
+
+
+A Go implementation of ICE
+
+
+
+
+
+
+
+
+
+
+
+
+### Roadmap
+The library is used as a part of our WebRTC implementation. Please refer to that [roadmap](https://github.com/pion/webrtc/issues/9) to track our major milestones.
+
+### Community
+Pion has an active community on the [Slack](https://pion.ly/slack).
+
+Follow the [Pion Twitter](https://twitter.com/_pion) for project updates and important WebRTC news.
+
+We are always looking to support **your projects**. Please reach out if you have something to build!
+If you need commercial support or don't want to use public methods you can contact us at [team@pion.ly](mailto:team@pion.ly)
+
+### Contributing
+Check out the [contributing wiki](https://github.com/pion/webrtc/wiki/Contributing) to join the group of amazing people making this project possible: [AUTHORS.txt](./AUTHORS.txt)
+
+### License
+MIT License - see [LICENSE](LICENSE) for full text
diff --git a/server/vendor/github.com/pion/ice/v2/active_tcp.go b/server/vendor/github.com/pion/ice/v2/active_tcp.go
new file mode 100644
index 0000000..024e6e7
--- /dev/null
+++ b/server/vendor/github.com/pion/ice/v2/active_tcp.go
@@ -0,0 +1,158 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ice
+
+import (
+ "context"
+ "io"
+ "net"
+ "sync/atomic"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/pion/transport/v2/packetio"
+)
+
+type activeTCPConn struct {
+ readBuffer, writeBuffer *packetio.Buffer
+ localAddr, remoteAddr atomic.Value
+ closed int32
+}
+
+func newActiveTCPConn(ctx context.Context, localAddress, remoteAddress string, log logging.LeveledLogger) (a *activeTCPConn) {
+ a = &activeTCPConn{
+ readBuffer: packetio.NewBuffer(),
+ writeBuffer: packetio.NewBuffer(),
+ }
+
+ laddr, err := getTCPAddrOnInterface(localAddress)
+ if err != nil {
+ atomic.StoreInt32(&a.closed, 1)
+ log.Infof("Failed to dial TCP address %s: %v", remoteAddress, err)
+ return
+ }
+ a.localAddr.Store(laddr)
+
+ go func() {
+ defer func() {
+ atomic.StoreInt32(&a.closed, 1)
+ }()
+
+ dialer := &net.Dialer{
+ LocalAddr: laddr,
+ }
+ conn, err := dialer.DialContext(ctx, "tcp", remoteAddress)
+ if err != nil {
+ log.Infof("Failed to dial TCP address %s: %v", remoteAddress, err)
+ return
+ }
+
+ a.remoteAddr.Store(conn.RemoteAddr())
+
+ go func() {
+ buff := make([]byte, receiveMTU)
+
+ for atomic.LoadInt32(&a.closed) == 0 {
+ n, err := readStreamingPacket(conn, buff)
+ if err != nil {
+ log.Infof("Failed to read streaming packet: %s", err)
+ break
+ }
+
+ if _, err := a.readBuffer.Write(buff[:n]); err != nil {
+ log.Infof("Failed to write to buffer: %s", err)
+ break
+ }
+ }
+ }()
+
+ buff := make([]byte, receiveMTU)
+
+ for atomic.LoadInt32(&a.closed) == 0 {
+ n, err := a.writeBuffer.Read(buff)
+ if err != nil {
+ log.Infof("Failed to read from buffer: %s", err)
+ break
+ }
+
+ if _, err = writeStreamingPacket(conn, buff[:n]); err != nil {
+ log.Infof("Failed to write streaming packet: %s", err)
+ break
+ }
+ }
+
+ if err := conn.Close(); err != nil {
+ log.Infof("Failed to close connection: %s", err)
+ }
+ }()
+
+ return a
+}
+
+func (a *activeTCPConn) ReadFrom(buff []byte) (n int, srcAddr net.Addr, err error) {
+ if atomic.LoadInt32(&a.closed) == 1 {
+ return 0, nil, io.ErrClosedPipe
+ }
+
+ srcAddr = a.RemoteAddr()
+ n, err = a.readBuffer.Read(buff)
+ return
+}
+
+func (a *activeTCPConn) WriteTo(buff []byte, _ net.Addr) (n int, err error) {
+ if atomic.LoadInt32(&a.closed) == 1 {
+ return 0, io.ErrClosedPipe
+ }
+
+ return a.writeBuffer.Write(buff)
+}
+
+func (a *activeTCPConn) Close() error {
+ atomic.StoreInt32(&a.closed, 1)
+ _ = a.readBuffer.Close()
+ _ = a.writeBuffer.Close()
+ return nil
+}
+
+func (a *activeTCPConn) LocalAddr() net.Addr {
+ if v, ok := a.localAddr.Load().(*net.TCPAddr); ok {
+ return v
+ }
+
+ return &net.TCPAddr{}
+}
+
+func (a *activeTCPConn) RemoteAddr() net.Addr {
+ if v, ok := a.remoteAddr.Load().(*net.TCPAddr); ok {
+ return v
+ }
+
+ return &net.TCPAddr{}
+}
+
+func (a *activeTCPConn) SetDeadline(time.Time) error { return io.EOF }
+func (a *activeTCPConn) SetReadDeadline(time.Time) error { return io.EOF }
+func (a *activeTCPConn) SetWriteDeadline(time.Time) error { return io.EOF }
+
+func getTCPAddrOnInterface(address string) (*net.TCPAddr, error) {
+ addr, err := net.ResolveTCPAddr("tcp", address)
+ if err != nil {
+ return nil, err
+ }
+
+ l, err := net.ListenTCP("tcp", addr)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ _ = l.Close()
+ }()
+
+ tcpAddr, ok := l.Addr().(*net.TCPAddr)
+ if !ok {
+ return nil, errInvalidAddress
+ }
+
+ return tcpAddr, nil
+}
diff --git a/server/vendor/github.com/pion/ice/v2/addr.go b/server/vendor/github.com/pion/ice/v2/addr.go
new file mode 100644
index 0000000..1d70025
--- /dev/null
+++ b/server/vendor/github.com/pion/ice/v2/addr.go
@@ -0,0 +1,71 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+package ice
+
+import (
+ "net"
+)
+
+func parseMulticastAnswerAddr(in net.Addr) (net.IP, bool) {
+ switch addr := in.(type) {
+ case *net.IPAddr:
+ return addr.IP, true
+ case *net.UDPAddr:
+ return addr.IP, true
+ case *net.TCPAddr:
+ return addr.IP, true
+ }
+ return nil, false
+}
+
+func parseAddr(in net.Addr) (net.IP, int, NetworkType, bool) {
+ switch addr := in.(type) {
+ case *net.UDPAddr:
+ return addr.IP, addr.Port, NetworkTypeUDP4, true
+ case *net.TCPAddr:
+ return addr.IP, addr.Port, NetworkTypeTCP4, true
+ }
+ return nil, 0, 0, false
+}
+
+func createAddr(network NetworkType, ip net.IP, port int) net.Addr {
+ switch {
+ case network.IsTCP():
+ return &net.TCPAddr{IP: ip, Port: port}
+ default:
+ return &net.UDPAddr{IP: ip, Port: port}
+ }
+}
+
+func addrEqual(a, b net.Addr) bool {
+ aIP, aPort, aType, aOk := parseAddr(a)
+ if !aOk {
+ return false
+ }
+
+ bIP, bPort, bType, bOk := parseAddr(b)
+ if !bOk {
+ return false
+ }
+
+ return aType == bType && aIP.Equal(bIP) && aPort == bPort
+}
+
+// AddrPort is an IP and a port number.
+type AddrPort [18]byte
+
+func toAddrPort(addr net.Addr) AddrPort {
+ var ap AddrPort
+ switch addr := addr.(type) {
+ case *net.UDPAddr:
+ copy(ap[:16], addr.IP.To16())
+ ap[16] = uint8(addr.Port >> 8)
+ ap[17] = uint8(addr.Port)
+ case *net.TCPAddr:
+ copy(ap[:16], addr.IP.To16())
+ ap[16] = uint8(addr.Port >> 8)
+ ap[17] = uint8(addr.Port)
+ }
+ return ap
+}
diff --git a/server/vendor/github.com/pion/ice/v2/agent.go b/server/vendor/github.com/pion/ice/v2/agent.go
new file mode 100644
index 0000000..c2718fc
--- /dev/null
+++ b/server/vendor/github.com/pion/ice/v2/agent.go
@@ -0,0 +1,1303 @@
+// SPDX-FileCopyrightText: 2023 The Pion community
+// SPDX-License-Identifier: MIT
+
+// Package ice implements the Interactive Connectivity Establishment (ICE)
+// protocol defined in rfc5245.
+package ice
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math"
+ "net"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ atomicx "github.com/pion/ice/v2/internal/atomic"
+ stunx "github.com/pion/ice/v2/internal/stun"
+ "github.com/pion/logging"
+ "github.com/pion/mdns"
+ "github.com/pion/stun"
+ "github.com/pion/transport/v2"
+ "github.com/pion/transport/v2/packetio"
+ "github.com/pion/transport/v2/stdnet"
+ "github.com/pion/transport/v2/vnet"
+ "golang.org/x/net/proxy"
+)
+
+type bindingRequest struct {
+ timestamp time.Time
+ transactionID [stun.TransactionIDSize]byte
+ destination net.Addr
+ isUseCandidate bool
+}
+
+// Agent represents the ICE agent
+type Agent struct {
+ chanTask chan task
+ afterRunFn []func(ctx context.Context)
+ muAfterRun sync.Mutex
+
+ onConnectionStateChangeHdlr atomic.Value // func(ConnectionState)
+ onSelectedCandidatePairChangeHdlr atomic.Value // func(Candidate, Candidate)
+ onCandidateHdlr atomic.Value // func(Candidate)
+
+ // State owned by the taskLoop
+ onConnected chan struct{}
+ onConnectedOnce sync.Once
+
+ // Force candidate to be contacted immediately (instead of waiting for task ticker)
+ forceCandidateContact chan bool
+
+ tieBreaker uint64
+ lite bool
+
+ connectionState ConnectionState
+ gatheringState GatheringState
+
+ mDNSMode MulticastDNSMode
+ mDNSName string
+ mDNSConn *mdns.Conn
+
+ muHaveStarted sync.Mutex
+ startedCh <-chan struct{}
+ startedFn func()
+ isControlling bool
+
+ maxBindingRequests uint16
+
+ hostAcceptanceMinWait time.Duration
+ srflxAcceptanceMinWait time.Duration
+ prflxAcceptanceMinWait time.Duration
+ relayAcceptanceMinWait time.Duration
+
+ tcpPriorityOffset uint16
+ disableActiveTCP bool
+
+ portMin uint16
+ portMax uint16
+
+ candidateTypes []CandidateType
+
+ // How long connectivity checks can fail before the ICE Agent
+ // goes to disconnected
+ disconnectedTimeout time.Duration
+
+ // How long connectivity checks can fail before the ICE Agent
+ // goes to failed
+ failedTimeout time.Duration
+
+ // How often should we send keepalive packets?
+ // 0 means never
+ keepaliveInterval time.Duration
+
+ // How often should we run our internal taskLoop to check for state changes when connecting
+ checkInterval time.Duration
+
+ localUfrag string
+ localPwd string
+ localCandidates map[NetworkType][]Candidate
+
+ remoteUfrag string
+ remotePwd string
+ remoteCandidates map[NetworkType][]Candidate
+
+ checklist []*CandidatePair
+ selector pairCandidateSelector
+
+ selectedPair atomic.Value // *CandidatePair
+
+ urls []*stun.URI
+ networkTypes []NetworkType
+
+ buf *packetio.Buffer
+
+ // LRU of outbound Binding request Transaction IDs
+ pendingBindingRequests []bindingRequest
+
+ // 1:1 D-NAT IP address mapping
+ extIPMapper *externalIPMapper
+
+ // State for closing
+ done chan struct{}
+ taskLoopDone chan struct{}
+ err atomicx.Error
+
+ // Callback that allows user to implement custom behavior
+ // for STUN Binding Requests
+ userBindingRequestHandler func(m *stun.Message, local, remote Candidate, pair *CandidatePair) bool
+
+ gatherCandidateCancel func()
+ gatherCandidateDone chan struct{}
+
+ connectionStateNotifier *handlerNotifier
+ candidateNotifier *handlerNotifier
+ selectedCandidatePairNotifier *handlerNotifier
+
+ loggerFactory logging.LoggerFactory
+ log logging.LeveledLogger
+
+ net transport.Net
+ tcpMux TCPMux
+ udpMux UDPMux
+ udpMuxSrflx UniversalUDPMux
+
+ interfaceFilter func(string) bool
+ ipFilter func(net.IP) bool
+ includeLoopback bool
+
+ insecureSkipVerify bool
+
+ proxyDialer proxy.Dialer
+}
+
+type task struct {
+ fn func(context.Context, *Agent)
+ done chan struct{}
+}
+
+// afterRun registers function to be run after the task.
+func (a *Agent) afterRun(f func(context.Context)) {
+ a.muAfterRun.Lock()
+ a.afterRunFn = append(a.afterRunFn, f)
+ a.muAfterRun.Unlock()
+}
+
+func (a *Agent) getAfterRunFn() []func(context.Context) {
+ a.muAfterRun.Lock()
+ defer a.muAfterRun.Unlock()
+ fns := a.afterRunFn
+ a.afterRunFn = nil
+ return fns
+}
+
+func (a *Agent) ok() error {
+ select {
+ case <-a.done:
+ return a.getErr()
+ default:
+ }
+ return nil
+}
+
+func (a *Agent) getErr() error {
+ if err := a.err.Load(); err != nil {
+ return err
+ }
+ return ErrClosed
+}
+
+// Run task in serial. Blocking tasks must be cancelable by context.
+func (a *Agent) run(ctx context.Context, t func(context.Context, *Agent)) error {
+ if err := a.ok(); err != nil {
+ return err
+ }
+ done := make(chan struct{})
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case a.chanTask <- task{t, done}:
+ <-done
+ return nil
+ }
+}
+
+// taskLoop handles registered tasks and agent close.
+func (a *Agent) taskLoop() {
+ after := func() {
+ for {
+ // Get and run func registered by afterRun().
+ fns := a.getAfterRunFn()
+ if len(fns) == 0 {
+ break
+ }
+ for _, fn := range fns {
+ fn(a.context())
+ }
+ }
+ }
+ defer func() {
+ a.deleteAllCandidates()
+ a.startedFn()
+
+ if err := a.buf.Close(); err != nil {
+ a.log.Warnf("Failed to close buffer: %v", err)
+ }
+
+ a.closeMulticastConn()
+ a.updateConnectionState(ConnectionStateClosed)
+
+ after()
+
+ close(a.taskLoopDone)
+ }()
+
+ for {
+ select {
+ case <-a.done:
+ return
+ case t := <-a.chanTask:
+ t.fn(a.context(), a)
+ close(t.done)
+ after()
+ }
+ }
+}
+
+// NewAgent creates a new Agent
+func NewAgent(config *AgentConfig) (*Agent, error) { //nolint:gocognit
+ var err error
+ if config.PortMax < config.PortMin {
+ return nil, ErrPort
+ }
+
+ mDNSName := config.MulticastDNSHostName
+ if mDNSName == "" {
+ if mDNSName, err = generateMulticastDNSName(); err != nil {
+ return nil, err
+ }
+ }
+
+ if !strings.HasSuffix(mDNSName, ".local") || len(strings.Split(mDNSName, ".")) != 2 {
+ return nil, ErrInvalidMulticastDNSHostName
+ }
+
+ mDNSMode := config.MulticastDNSMode
+ if mDNSMode == 0 {
+ mDNSMode = MulticastDNSModeQueryOnly
+ }
+
+ loggerFactory := config.LoggerFactory
+ if loggerFactory == nil {
+ loggerFactory = logging.NewDefaultLoggerFactory()
+ }
+ log := loggerFactory.NewLogger("ice")
+
+ startedCtx, startedFn := context.WithCancel(context.Background())
+
+ a := &Agent{
+ chanTask: make(chan task),
+ tieBreaker: globalMathRandomGenerator.Uint64(),
+ lite: config.Lite,
+ gatheringState: GatheringStateNew,
+ connectionState: ConnectionStateNew,
+ localCandidates: make(map[NetworkType][]Candidate),
+ remoteCandidates: make(map[NetworkType][]Candidate),
+ urls: config.Urls,
+ networkTypes: config.NetworkTypes,
+ onConnected: make(chan struct{}),
+ buf: packetio.NewBuffer(),
+ done: make(chan struct{}),
+ taskLoopDone: make(chan struct{}),
+ startedCh: startedCtx.Done(),
+ startedFn: startedFn,
+ portMin: config.PortMin,
+ portMax: config.PortMax,
+ loggerFactory: loggerFactory,
+ log: log,
+ net: config.Net,
+ proxyDialer: config.ProxyDialer,
+ tcpMux: config.TCPMux,
+ udpMux: config.UDPMux,
+ udpMuxSrflx: config.UDPMuxSrflx,
+
+ mDNSMode: mDNSMode,
+ mDNSName: mDNSName,
+
+ gatherCandidateCancel: func() {},
+
+ forceCandidateContact: make(chan bool, 1),
+
+ interfaceFilter: config.InterfaceFilter,
+
+ ipFilter: config.IPFilter,
+
+ insecureSkipVerify: config.InsecureSkipVerify,
+
+ includeLoopback: config.IncludeLoopback,
+
+ disableActiveTCP: config.DisableActiveTCP,
+
+ userBindingRequestHandler: config.BindingRequestHandler,
+ }
+ a.connectionStateNotifier = &handlerNotifier{connectionStateFunc: a.onConnectionStateChange, done: make(chan struct{})}
+ a.candidateNotifier = &handlerNotifier{candidateFunc: a.onCandidate, done: make(chan struct{})}
+ a.selectedCandidatePairNotifier = &handlerNotifier{candidatePairFunc: a.onSelectedCandidatePairChange, done: make(chan struct{})}
+
+ if a.net == nil {
+ a.net, err = stdnet.NewNet()
+ if err != nil {
+ return nil, fmt.Errorf("failed to create network: %w", err)
+ }
+ } else if _, isVirtual := a.net.(*vnet.Net); isVirtual {
+ a.log.Warn("Virtual network is enabled")
+ if a.mDNSMode != MulticastDNSModeDisabled {
+ a.log.Warn("Virtual network does not support mDNS yet")
+ }
+ }
+
+ // Opportunistic mDNS: If we can't open the connection, that's ok: we
+ // can continue without it.
+ if a.mDNSConn, a.mDNSMode, err = createMulticastDNS(a.net, mDNSMode, mDNSName, log); err != nil {
+ log.Warnf("Failed to initialize mDNS %s: %v", mDNSName, err)
+ }
+
+ config.initWithDefaults(a)
+
+ // Make sure the buffer doesn't grow indefinitely.
+ // NOTE: We actually won't get anywhere close to this limit.
+ // SRTP will constantly read from the endpoint and drop packets if it's full.
+ a.buf.SetLimitSize(maxBufferSize)
+
+ if a.lite && (len(a.candidateTypes) != 1 || a.candidateTypes[0] != CandidateTypeHost) {
+ a.closeMulticastConn()
+ return nil, ErrLiteUsingNonHostCandidates
+ }
+
+ if config.Urls != nil && len(config.Urls) > 0 && !containsCandidateType(CandidateTypeServerReflexive, a.candidateTypes) && !containsCandidateType(CandidateTypeRelay, a.candidateTypes) {
+ a.closeMulticastConn()
+ return nil, ErrUselessUrlsProvided
+ }
+
+ if err = config.initExtIPMapping(a); err != nil {
+ a.closeMulticastConn()
+ return nil, err
+ }
+
+ go a.taskLoop()
+
+ // Restart is also used to initialize the agent for the first time
+ if err := a.Restart(config.LocalUfrag, config.LocalPwd); err != nil {
+ a.closeMulticastConn()
+ _ = a.Close()
+ return nil, err
+ }
+
+ return a, nil
+}
+
+func (a *Agent) startConnectivityChecks(isControlling bool, remoteUfrag, remotePwd string) error {
+ a.muHaveStarted.Lock()
+ defer a.muHaveStarted.Unlock()
+ select {
+ case <-a.startedCh:
+ return ErrMultipleStart
+ default:
+ }
+ if err := a.SetRemoteCredentials(remoteUfrag, remotePwd); err != nil { //nolint:contextcheck
+ return err
+ }
+
+ a.log.Debugf("Started agent: isControlling? %t, remoteUfrag: %q, remotePwd: %q", isControlling, remoteUfrag, remotePwd)
+
+ return a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ agent.isControlling = isControlling
+ agent.remoteUfrag = remoteUfrag
+ agent.remotePwd = remotePwd
+
+ if isControlling {
+ a.selector = &controllingSelector{agent: a, log: a.log}
+ } else {
+ a.selector = &controlledSelector{agent: a, log: a.log}
+ }
+
+ if a.lite {
+ a.selector = &liteSelector{pairCandidateSelector: a.selector}
+ }
+
+ a.selector.Start()
+ a.startedFn()
+
+ agent.updateConnectionState(ConnectionStateChecking)
+
+ a.requestConnectivityCheck()
+ go a.connectivityChecks() //nolint:contextcheck
+ })
+}
+
+func (a *Agent) connectivityChecks() {
+ lastConnectionState := ConnectionState(0)
+ checkingDuration := time.Time{}
+
+ contact := func() {
+ if err := a.run(a.context(), func(ctx context.Context, a *Agent) {
+ defer func() {
+ lastConnectionState = a.connectionState
+ }()
+
+ switch a.connectionState {
+ case ConnectionStateFailed:
+ // The connection is currently failed so don't send any checks
+ // In the future it may be restarted though
+ return
+ case ConnectionStateChecking:
+ // We have just entered checking for the first time so update our checking timer
+ if lastConnectionState != a.connectionState {
+ checkingDuration = time.Now()
+ }
+
+ // We have been in checking longer then Disconnect+Failed timeout, set the connection to Failed
+ if time.Since(checkingDuration) > a.disconnectedTimeout+a.failedTimeout {
+ a.updateConnectionState(ConnectionStateFailed)
+ return
+ }
+ default:
+ }
+
+ a.selector.ContactCandidates()
+ }); err != nil {
+ a.log.Warnf("Failed to start connectivity checks: %v", err)
+ }
+ }
+
+ t := time.NewTimer(math.MaxInt64)
+ t.Stop()
+
+ for {
+ interval := defaultKeepaliveInterval
+
+ updateInterval := func(x time.Duration) {
+ if x != 0 && (interval == 0 || interval > x) {
+ interval = x
+ }
+ }
+
+ switch lastConnectionState {
+ case ConnectionStateNew, ConnectionStateChecking: // While connecting, check candidates more frequently
+ updateInterval(a.checkInterval)
+ case ConnectionStateConnected, ConnectionStateDisconnected:
+ updateInterval(a.keepaliveInterval)
+ default:
+ }
+ // Ensure we run our task loop as quickly as the minimum of our various configured timeouts
+ updateInterval(a.disconnectedTimeout)
+ updateInterval(a.failedTimeout)
+
+ t.Reset(interval)
+
+ select {
+ case <-a.forceCandidateContact:
+ if !t.Stop() {
+ <-t.C
+ }
+ contact()
+ case <-t.C:
+ contact()
+ case <-a.done:
+ t.Stop()
+ return
+ }
+ }
+}
+
+func (a *Agent) updateConnectionState(newState ConnectionState) {
+ if a.connectionState != newState {
+ // Connection has gone to failed, release all gathered candidates
+ if newState == ConnectionStateFailed {
+ a.removeUfragFromMux()
+ a.checklist = make([]*CandidatePair, 0)
+ a.pendingBindingRequests = make([]bindingRequest, 0)
+ a.setSelectedPair(nil)
+ a.deleteAllCandidates()
+ }
+
+ a.log.Infof("Setting new connection state: %s", newState)
+ a.connectionState = newState
+ a.connectionStateNotifier.EnqueueConnectionState(newState)
+ }
+}
+
+func (a *Agent) setSelectedPair(p *CandidatePair) {
+ if p == nil {
+ var nilPair *CandidatePair
+ a.selectedPair.Store(nilPair)
+ a.log.Tracef("Unset selected candidate pair")
+ return
+ }
+
+ p.nominated = true
+ a.selectedPair.Store(p)
+ a.log.Tracef("Set selected candidate pair: %s", p)
+
+ a.updateConnectionState(ConnectionStateConnected)
+
+ // Notify when the selected pair changes
+ a.selectedCandidatePairNotifier.EnqueueSelectedCandidatePair(p)
+
+ // Signal connected
+ a.onConnectedOnce.Do(func() { close(a.onConnected) })
+}
+
+func (a *Agent) pingAllCandidates() {
+ a.log.Trace("Pinging all candidates")
+
+ if len(a.checklist) == 0 {
+ a.log.Warn("Failed to ping without candidate pairs. Connection is not possible yet.")
+ }
+
+ for _, p := range a.checklist {
+ if p.state == CandidatePairStateWaiting {
+ p.state = CandidatePairStateInProgress
+ } else if p.state != CandidatePairStateInProgress {
+ continue
+ }
+
+ if p.bindingRequestCount > a.maxBindingRequests {
+ a.log.Tracef("Maximum requests reached for pair %s, marking it as failed", p)
+ p.state = CandidatePairStateFailed
+ } else {
+ a.selector.PingCandidate(p.Local, p.Remote)
+ p.bindingRequestCount++
+ }
+ }
+}
+
+func (a *Agent) getBestAvailableCandidatePair() *CandidatePair {
+ var best *CandidatePair
+ for _, p := range a.checklist {
+ if p.state == CandidatePairStateFailed {
+ continue
+ }
+
+ if best == nil {
+ best = p
+ } else if best.priority() < p.priority() {
+ best = p
+ }
+ }
+ return best
+}
+
+func (a *Agent) getBestValidCandidatePair() *CandidatePair {
+ var best *CandidatePair
+ for _, p := range a.checklist {
+ if p.state != CandidatePairStateSucceeded {
+ continue
+ }
+
+ if best == nil {
+ best = p
+ } else if best.priority() < p.priority() {
+ best = p
+ }
+ }
+ return best
+}
+
+func (a *Agent) addPair(local, remote Candidate) *CandidatePair {
+ p := newCandidatePair(local, remote, a.isControlling)
+ a.checklist = append(a.checklist, p)
+ return p
+}
+
+func (a *Agent) findPair(local, remote Candidate) *CandidatePair {
+ for _, p := range a.checklist {
+ if p.Local.Equal(local) && p.Remote.Equal(remote) {
+ return p
+ }
+ }
+ return nil
+}
+
+// validateSelectedPair checks if the selected pair is (still) valid
+// Note: the caller should hold the agent lock.
+func (a *Agent) validateSelectedPair() bool {
+ selectedPair := a.getSelectedPair()
+ if selectedPair == nil {
+ return false
+ }
+
+ disconnectedTime := time.Since(selectedPair.Remote.LastReceived())
+
+ // Only allow transitions to failed if a.failedTimeout is non-zero
+ totalTimeToFailure := a.failedTimeout
+ if totalTimeToFailure != 0 {
+ totalTimeToFailure += a.disconnectedTimeout
+ }
+
+ switch {
+ case totalTimeToFailure != 0 && disconnectedTime > totalTimeToFailure:
+ a.updateConnectionState(ConnectionStateFailed)
+ case a.disconnectedTimeout != 0 && disconnectedTime > a.disconnectedTimeout:
+ a.updateConnectionState(ConnectionStateDisconnected)
+ default:
+ a.updateConnectionState(ConnectionStateConnected)
+ }
+
+ return true
+}
+
+// checkKeepalive sends STUN Binding Indications to the selected pair
+// if no packet has been sent on that pair in the last keepaliveInterval
+// Note: the caller should hold the agent lock.
+func (a *Agent) checkKeepalive() {
+ selectedPair := a.getSelectedPair()
+ if selectedPair == nil {
+ return
+ }
+
+ if (a.keepaliveInterval != 0) &&
+ ((time.Since(selectedPair.Local.LastSent()) > a.keepaliveInterval) ||
+ (time.Since(selectedPair.Remote.LastReceived()) > a.keepaliveInterval)) {
+ // We use binding request instead of indication to support refresh consent schemas
+ // see https://tools.ietf.org/html/rfc7675
+ a.selector.PingCandidate(selectedPair.Local, selectedPair.Remote)
+ }
+}
+
+// AddRemoteCandidate adds a new remote candidate
+func (a *Agent) AddRemoteCandidate(c Candidate) error {
+ if c == nil {
+ return nil
+ }
+
+ // TCP Candidates with TCP type active will probe server passive ones, so
+ // no need to do anything with them.
+ if c.TCPType() == TCPTypeActive {
+ a.log.Infof("Ignoring remote candidate with tcpType active: %s", c)
+ return nil
+ }
+
+ // If we have a mDNS Candidate lets fully resolve it before adding it locally
+ if c.Type() == CandidateTypeHost && strings.HasSuffix(c.Address(), ".local") {
+ if a.mDNSMode == MulticastDNSModeDisabled {
+ a.log.Warnf("Remote mDNS candidate added, but mDNS is disabled: (%s)", c.Address())
+ return nil
+ }
+
+ hostCandidate, ok := c.(*CandidateHost)
+ if !ok {
+ return ErrAddressParseFailed
+ }
+
+ go a.resolveAndAddMulticastCandidate(hostCandidate)
+ return nil
+ }
+
+ go func() {
+ if err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ // nolint: contextcheck
+ agent.addRemoteCandidate(c)
+ }); err != nil {
+ a.log.Warnf("Failed to add remote candidate %s: %v", c.Address(), err)
+ return
+ }
+ }()
+ return nil
+}
+
+func (a *Agent) resolveAndAddMulticastCandidate(c *CandidateHost) {
+ if a.mDNSConn == nil {
+ return
+ }
+ _, src, err := a.mDNSConn.Query(c.context(), c.Address())
+ if err != nil {
+ a.log.Warnf("Failed to discover mDNS candidate %s: %v", c.Address(), err)
+ return
+ }
+
+ ip, ipOk := parseMulticastAnswerAddr(src)
+ if !ipOk {
+ a.log.Warnf("Failed to discover mDNS candidate %s: failed to parse IP", c.Address())
+ return
+ }
+
+ if err = c.setIP(ip); err != nil {
+ a.log.Warnf("Failed to discover mDNS candidate %s: %v", c.Address(), err)
+ return
+ }
+
+ if err = a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ // nolint: contextcheck
+ agent.addRemoteCandidate(c)
+ }); err != nil {
+ a.log.Warnf("Failed to add mDNS candidate %s: %v", c.Address(), err)
+ return
+ }
+}
+
+func (a *Agent) requestConnectivityCheck() {
+ select {
+ case a.forceCandidateContact <- true:
+ default:
+ }
+}
+
+func (a *Agent) addRemotePassiveTCPCandidate(remoteCandidate Candidate) {
+ localIPs, err := localInterfaces(a.net, a.interfaceFilter, a.ipFilter, []NetworkType{remoteCandidate.NetworkType()}, a.includeLoopback)
+ if err != nil {
+ a.log.Warnf("Failed to iterate local interfaces, host candidates will not be gathered %s", err)
+ return
+ }
+
+ for i := range localIPs {
+ conn := newActiveTCPConn(
+ a.context(),
+ net.JoinHostPort(localIPs[i].String(), "0"),
+ net.JoinHostPort(remoteCandidate.Address(), strconv.Itoa(remoteCandidate.Port())),
+ a.log,
+ )
+
+ tcpAddr, ok := conn.LocalAddr().(*net.TCPAddr)
+ if !ok {
+ closeConnAndLog(conn, a.log, "Failed to create Active ICE-TCP Candidate: %v", errInvalidAddress)
+ continue
+ }
+
+ localCandidate, err := NewCandidateHost(&CandidateHostConfig{
+ Network: remoteCandidate.NetworkType().String(),
+ Address: localIPs[i].String(),
+ Port: tcpAddr.Port,
+ Component: ComponentRTP,
+ TCPType: TCPTypeActive,
+ })
+ if err != nil {
+ closeConnAndLog(conn, a.log, "Failed to create Active ICE-TCP Candidate: %v", err)
+ continue
+ }
+
+ localCandidate.start(a, conn, a.startedCh)
+ a.localCandidates[localCandidate.NetworkType()] = append(a.localCandidates[localCandidate.NetworkType()], localCandidate)
+ a.candidateNotifier.EnqueueCandidate(localCandidate)
+
+ a.addPair(localCandidate, remoteCandidate)
+ }
+}
+
+// addRemoteCandidate assumes you are holding the lock (must be execute using a.run)
+func (a *Agent) addRemoteCandidate(c Candidate) {
+ set := a.remoteCandidates[c.NetworkType()]
+
+ for _, candidate := range set {
+ if candidate.Equal(c) {
+ return
+ }
+ }
+
+ tcpNetworkTypeFound := false
+ for _, networkType := range a.networkTypes {
+ if networkType.IsTCP() {
+ tcpNetworkTypeFound = true
+ }
+ }
+
+ if !a.disableActiveTCP && tcpNetworkTypeFound && c.TCPType() == TCPTypePassive {
+ a.addRemotePassiveTCPCandidate(c)
+ }
+
+ set = append(set, c)
+ a.remoteCandidates[c.NetworkType()] = set
+
+ if c.TCPType() != TCPTypePassive {
+ if localCandidates, ok := a.localCandidates[c.NetworkType()]; ok {
+ for _, localCandidate := range localCandidates {
+ a.addPair(localCandidate, c)
+ }
+ }
+ }
+
+ a.requestConnectivityCheck()
+}
+
+func (a *Agent) addCandidate(ctx context.Context, c Candidate, candidateConn net.PacketConn) error {
+ return a.run(ctx, func(ctx context.Context, agent *Agent) {
+ set := a.localCandidates[c.NetworkType()]
+ for _, candidate := range set {
+ if candidate.Equal(c) {
+ a.log.Debugf("Ignore duplicate candidate: %s", c)
+ if err := c.close(); err != nil {
+ a.log.Warnf("Failed to close duplicate candidate: %v", err)
+ }
+ if err := candidateConn.Close(); err != nil {
+ a.log.Warnf("Failed to close duplicate candidate connection: %v", err)
+ }
+ return
+ }
+ }
+
+ c.start(a, candidateConn, a.startedCh)
+
+ set = append(set, c)
+ a.localCandidates[c.NetworkType()] = set
+
+ if remoteCandidates, ok := a.remoteCandidates[c.NetworkType()]; ok {
+ for _, remoteCandidate := range remoteCandidates {
+ a.addPair(c, remoteCandidate)
+ }
+ }
+
+ a.requestConnectivityCheck()
+
+ a.candidateNotifier.EnqueueCandidate(c)
+ })
+}
+
+// GetRemoteCandidates returns the remote candidates
+func (a *Agent) GetRemoteCandidates() ([]Candidate, error) {
+ var res []Candidate
+
+ err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ var candidates []Candidate
+ for _, set := range agent.remoteCandidates {
+ candidates = append(candidates, set...)
+ }
+ res = candidates
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return res, nil
+}
+
+// GetLocalCandidates returns the local candidates
+func (a *Agent) GetLocalCandidates() ([]Candidate, error) {
+ var res []Candidate
+
+ err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ var candidates []Candidate
+ for _, set := range agent.localCandidates {
+ candidates = append(candidates, set...)
+ }
+ res = candidates
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return res, nil
+}
+
+// GetLocalUserCredentials returns the local user credentials
+func (a *Agent) GetLocalUserCredentials() (frag string, pwd string, err error) {
+ valSet := make(chan struct{})
+ err = a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ frag = agent.localUfrag
+ pwd = agent.localPwd
+ close(valSet)
+ })
+
+ if err == nil {
+ <-valSet
+ }
+ return
+}
+
+// GetRemoteUserCredentials returns the remote user credentials
+func (a *Agent) GetRemoteUserCredentials() (frag string, pwd string, err error) {
+ valSet := make(chan struct{})
+ err = a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ frag = agent.remoteUfrag
+ pwd = agent.remotePwd
+ close(valSet)
+ })
+
+ if err == nil {
+ <-valSet
+ }
+ return
+}
+
+func (a *Agent) removeUfragFromMux() {
+ if a.tcpMux != nil {
+ a.tcpMux.RemoveConnByUfrag(a.localUfrag)
+ }
+ if a.udpMux != nil {
+ a.udpMux.RemoveConnByUfrag(a.localUfrag)
+ }
+ if a.udpMuxSrflx != nil {
+ a.udpMuxSrflx.RemoveConnByUfrag(a.localUfrag)
+ }
+}
+
+// Close cleans up the Agent
+func (a *Agent) Close() error {
+ return a.close(false)
+}
+
+// GracefulClose cleans up the Agent and waits for any goroutines it started
+// to complete. This is only safe to call outside of Agent callbacks or if in a callback,
+// in its own goroutine.
+func (a *Agent) GracefulClose() error {
+ return a.close(true)
+}
+
+func (a *Agent) close(graceful bool) error {
+ if err := a.ok(); err != nil {
+ if errors.Is(err, ErrClosed) {
+ return nil
+ }
+ return err
+ }
+
+ a.afterRun(func(context.Context) {
+ a.gatherCandidateCancel()
+ if a.gatherCandidateDone != nil {
+ <-a.gatherCandidateDone
+ }
+ })
+ a.err.Store(ErrClosed)
+
+ a.removeUfragFromMux()
+
+ close(a.done)
+ // the loop is safe to wait on no matter what
+ <-a.taskLoopDone
+
+ // but we are in less control of the notifiers, so we will
+ // pass through `graceful`.
+ a.connectionStateNotifier.Close(graceful)
+ a.candidateNotifier.Close(graceful)
+ a.selectedCandidatePairNotifier.Close(graceful)
+ return nil
+}
+
+// Remove all candidates. This closes any listening sockets
+// and removes both the local and remote candidate lists.
+//
+// This is used for restarts, failures and on close
+func (a *Agent) deleteAllCandidates() {
+ for net, cs := range a.localCandidates {
+ for _, c := range cs {
+ if err := c.close(); err != nil {
+ a.log.Warnf("Failed to close candidate %s: %v", c, err)
+ }
+ }
+ delete(a.localCandidates, net)
+ }
+ for net, cs := range a.remoteCandidates {
+ for _, c := range cs {
+ if err := c.close(); err != nil {
+ a.log.Warnf("Failed to close candidate %s: %v", c, err)
+ }
+ }
+ delete(a.remoteCandidates, net)
+ }
+}
+
+func (a *Agent) findRemoteCandidate(networkType NetworkType, addr net.Addr) Candidate {
+ ip, port, _, ok := parseAddr(addr)
+ if !ok {
+ a.log.Warnf("Failed to parse address: %s", addr)
+ return nil
+ }
+
+ set := a.remoteCandidates[networkType]
+ for _, c := range set {
+ if c.Address() == ip.String() && c.Port() == port {
+ return c
+ }
+ }
+ return nil
+}
+
+func (a *Agent) sendBindingRequest(m *stun.Message, local, remote Candidate) {
+ a.log.Tracef("Ping STUN from %s to %s", local, remote)
+
+ a.invalidatePendingBindingRequests(time.Now())
+ a.pendingBindingRequests = append(a.pendingBindingRequests, bindingRequest{
+ timestamp: time.Now(),
+ transactionID: m.TransactionID,
+ destination: remote.addr(),
+ isUseCandidate: m.Contains(stun.AttrUseCandidate),
+ })
+
+ a.sendSTUN(m, local, remote)
+}
+
+func (a *Agent) sendBindingSuccess(m *stun.Message, local, remote Candidate) {
+ base := remote
+
+ ip, port, _, ok := parseAddr(base.addr())
+ if !ok {
+ a.log.Warnf("Failed to parse address: %s", base.addr())
+ return
+ }
+
+ if out, err := stun.Build(m, stun.BindingSuccess,
+ &stun.XORMappedAddress{
+ IP: ip,
+ Port: port,
+ },
+ stun.NewShortTermIntegrity(a.localPwd),
+ stun.Fingerprint,
+ ); err != nil {
+ a.log.Warnf("Failed to handle inbound ICE from: %s to: %s error: %s", local, remote, err)
+ } else {
+ a.sendSTUN(out, local, remote)
+ }
+}
+
+// Removes pending binding requests that are over maxBindingRequestTimeout old
+//
+// Let HTO be the transaction timeout, which SHOULD be 2*RTT if
+// RTT is known or 500 ms otherwise.
+// https://tools.ietf.org/html/rfc8445#appendix-B.1
+func (a *Agent) invalidatePendingBindingRequests(filterTime time.Time) {
+ initialSize := len(a.pendingBindingRequests)
+
+ temp := a.pendingBindingRequests[:0]
+ for _, bindingRequest := range a.pendingBindingRequests {
+ if filterTime.Sub(bindingRequest.timestamp) < maxBindingRequestTimeout {
+ temp = append(temp, bindingRequest)
+ }
+ }
+
+ a.pendingBindingRequests = temp
+ if bindRequestsRemoved := initialSize - len(a.pendingBindingRequests); bindRequestsRemoved > 0 {
+ a.log.Tracef("Discarded %d binding requests because they expired", bindRequestsRemoved)
+ }
+}
+
+// Assert that the passed TransactionID is in our pendingBindingRequests and returns the destination
+// If the bindingRequest was valid remove it from our pending cache
+func (a *Agent) handleInboundBindingSuccess(id [stun.TransactionIDSize]byte) (bool, *bindingRequest, time.Duration) {
+ a.invalidatePendingBindingRequests(time.Now())
+ for i := range a.pendingBindingRequests {
+ if a.pendingBindingRequests[i].transactionID == id {
+ validBindingRequest := a.pendingBindingRequests[i]
+ a.pendingBindingRequests = append(a.pendingBindingRequests[:i], a.pendingBindingRequests[i+1:]...)
+ return true, &validBindingRequest, time.Since(validBindingRequest.timestamp)
+ }
+ }
+ return false, nil, 0
+}
+
+// handleInbound processes STUN traffic from a remote candidate
+func (a *Agent) handleInbound(m *stun.Message, local Candidate, remote net.Addr) { //nolint:gocognit
+ var err error
+ if m == nil || local == nil {
+ return
+ }
+
+ if m.Type.Method != stun.MethodBinding ||
+ !(m.Type.Class == stun.ClassSuccessResponse ||
+ m.Type.Class == stun.ClassRequest ||
+ m.Type.Class == stun.ClassIndication) {
+ a.log.Tracef("Unhandled STUN from %s to %s class(%s) method(%s)", remote, local, m.Type.Class, m.Type.Method)
+ return
+ }
+
+ if a.isControlling {
+ if m.Contains(stun.AttrICEControlling) {
+ a.log.Debug("Inbound STUN message: isControlling && a.isControlling == true")
+ return
+ } else if m.Contains(stun.AttrUseCandidate) {
+ a.log.Debug("Inbound STUN message: useCandidate && a.isControlling == true")
+ return
+ }
+ } else {
+ if m.Contains(stun.AttrICEControlled) {
+ a.log.Debug("Inbound STUN message: isControlled && a.isControlling == false")
+ return
+ }
+ }
+
+ remoteCandidate := a.findRemoteCandidate(local.NetworkType(), remote)
+ if m.Type.Class == stun.ClassSuccessResponse {
+ if err = stun.MessageIntegrity([]byte(a.remotePwd)).Check(m); err != nil {
+ a.log.Warnf("Discard message from (%s), %v", remote, err)
+ return
+ }
+
+ if remoteCandidate == nil {
+ a.log.Warnf("Discard success message from (%s), no such remote", remote)
+ return
+ }
+
+ a.selector.HandleSuccessResponse(m, local, remoteCandidate, remote)
+ } else if m.Type.Class == stun.ClassRequest {
+ a.log.Tracef("Inbound STUN (Request) from %s to %s, useCandidate: %v", remote, local, m.Contains(stun.AttrUseCandidate))
+
+ if err = stunx.AssertUsername(m, a.localUfrag+":"+a.remoteUfrag); err != nil {
+ a.log.Warnf("Discard message from (%s), %v", remote, err)
+ return
+ } else if err = stun.MessageIntegrity([]byte(a.localPwd)).Check(m); err != nil {
+ a.log.Warnf("Discard message from (%s), %v", remote, err)
+ return
+ }
+
+ if remoteCandidate == nil {
+ ip, port, networkType, ok := parseAddr(remote)
+ if !ok {
+ a.log.Errorf("Failed to create parse remote net.Addr when creating remote prflx candidate")
+ return
+ }
+
+ prflxCandidateConfig := CandidatePeerReflexiveConfig{
+ Network: networkType.String(),
+ Address: ip.String(),
+ Port: port,
+ Component: local.Component(),
+ RelAddr: "",
+ RelPort: 0,
+ }
+
+ prflxCandidate, err := NewCandidatePeerReflexive(&prflxCandidateConfig)
+ if err != nil {
+ a.log.Errorf("Failed to create new remote prflx candidate (%s)", err)
+ return
+ }
+ remoteCandidate = prflxCandidate
+
+ a.log.Debugf("Adding a new peer-reflexive candidate: %s ", remote)
+ a.addRemoteCandidate(remoteCandidate)
+ }
+
+ a.selector.HandleBindingRequest(m, local, remoteCandidate)
+ }
+
+ if remoteCandidate != nil {
+ remoteCandidate.seen(false)
+ }
+}
+
+// validateNonSTUNTraffic processes non STUN traffic from a remote candidate,
+// and returns true if it is an actual remote candidate
+func (a *Agent) validateNonSTUNTraffic(local Candidate, remote net.Addr) (Candidate, bool) {
+ var remoteCandidate Candidate
+ if err := a.run(local.context(), func(ctx context.Context, agent *Agent) {
+ remoteCandidate = a.findRemoteCandidate(local.NetworkType(), remote)
+ if remoteCandidate != nil {
+ remoteCandidate.seen(false)
+ }
+ }); err != nil {
+ a.log.Warnf("Failed to validate remote candidate: %v", err)
+ }
+
+ return remoteCandidate, remoteCandidate != nil
+}
+
+// GetSelectedCandidatePair returns the selected pair or nil if there is none
+func (a *Agent) GetSelectedCandidatePair() (*CandidatePair, error) {
+ selectedPair := a.getSelectedPair()
+ if selectedPair == nil {
+ return nil, nil //nolint:nilnil
+ }
+
+ local, err := selectedPair.Local.copy()
+ if err != nil {
+ return nil, err
+ }
+
+ remote, err := selectedPair.Remote.copy()
+ if err != nil {
+ return nil, err
+ }
+
+ return &CandidatePair{Local: local, Remote: remote}, nil
+}
+
+func (a *Agent) getSelectedPair() *CandidatePair {
+ if selectedPair, ok := a.selectedPair.Load().(*CandidatePair); ok {
+ return selectedPair
+ }
+
+ return nil
+}
+
+func (a *Agent) closeMulticastConn() {
+ if a.mDNSConn != nil {
+ if err := a.mDNSConn.Close(); err != nil {
+ a.log.Warnf("Failed to close mDNS Conn: %v", err)
+ }
+ }
+}
+
+// SetRemoteCredentials sets the credentials of the remote agent
+func (a *Agent) SetRemoteCredentials(remoteUfrag, remotePwd string) error {
+ switch {
+ case remoteUfrag == "":
+ return ErrRemoteUfragEmpty
+ case remotePwd == "":
+ return ErrRemotePwdEmpty
+ }
+
+ return a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ agent.remoteUfrag = remoteUfrag
+ agent.remotePwd = remotePwd
+ })
+}
+
+// Restart restarts the ICE Agent with the provided ufrag/pwd
+// If no ufrag/pwd is provided the Agent will generate one itself
+//
+// If there is a gatherer routine currently running, Restart will
+// cancel it.
+// After a Restart, the user must then call GatherCandidates explicitly
+// to start generating new ones.
+func (a *Agent) Restart(ufrag, pwd string) error {
+ if ufrag == "" {
+ var err error
+ ufrag, err = generateUFrag()
+ if err != nil {
+ return err
+ }
+ }
+ if pwd == "" {
+ var err error
+ pwd, err = generatePwd()
+ if err != nil {
+ return err
+ }
+ }
+
+ if len([]rune(ufrag))*8 < 24 {
+ return ErrLocalUfragInsufficientBits
+ }
+ if len([]rune(pwd))*8 < 128 {
+ return ErrLocalPwdInsufficientBits
+ }
+
+ var err error
+ if runErr := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ if agent.gatheringState == GatheringStateGathering {
+ agent.gatherCandidateCancel()
+ }
+
+ // Clear all agent needed to take back to fresh state
+ a.removeUfragFromMux()
+ agent.localUfrag = ufrag
+ agent.localPwd = pwd
+ agent.remoteUfrag = ""
+ agent.remotePwd = ""
+ a.gatheringState = GatheringStateNew
+ a.checklist = make([]*CandidatePair, 0)
+ a.pendingBindingRequests = make([]bindingRequest, 0)
+ a.setSelectedPair(nil)
+ a.deleteAllCandidates()
+ if a.selector != nil {
+ a.selector.Start()
+ }
+
+ // Restart is used by NewAgent. Accept/Connect should be used to move to checking
+ // for new Agents
+ if a.connectionState != ConnectionStateNew {
+ a.updateConnectionState(ConnectionStateChecking)
+ }
+ }); runErr != nil {
+ return runErr
+ }
+ return err
+}
+
+func (a *Agent) setGatheringState(newState GatheringState) error {
+ done := make(chan struct{})
+ if err := a.run(a.context(), func(ctx context.Context, agent *Agent) {
+ if a.gatheringState != newState && newState == GatheringStateComplete {
+ a.candidateNotifier.EnqueueCandidate(nil)
+ }
+
+ a.gatheringState = newState
+ close(done)
+ }); err != nil {
+ return err
+ }
+
+ <-done
+ return nil
+}
diff --git a/server/vendor/github.com/pion/ice/v2/agent_config.go b/server/vendor/github.com/pion/ice/v2/agent_config.go
new file mode 100644
index 0000000..7f7b67a
--- /dev/null
+++ b/server/vendor/github.com/pion/ice/v2/agent_config.go
@@ -0,0 +1,305 @@
+// SPDX-FileCopyrightText: 2023 The Pion community