private async Task Stage1(VoiceReadyPayload voiceReady) { // IP Discovery this.UdpClient.Setup(this.UdpEndpoint); var pck = new byte[70]; PreparePacket(pck); await this.UdpClient.SendAsync(pck, pck.Length).ConfigureAwait(false); var ipd = await this.UdpClient.ReceiveAsync().ConfigureAwait(false); ReadPacket(ipd, out var ip, out var port); this.DiscoveredEndpoint = new IpEndpoint { Address = ip, Port = port }; this.Discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Endpoint dicovery finished - discovered endpoint is {0}:{1}", ip, port); void PreparePacket(byte[] packet) { var ssrc = this.SSRC; var packetSpan = packet.AsSpan(); MemoryMarshal.Write(packetSpan, ref ssrc); Helpers.ZeroFill(packetSpan); } void ReadPacket(byte[] packet, out System.Net.IPAddress decodedIp, out ushort decodedPort) { var packetSpan = packet.AsSpan(); var ipString = Utilities.UTF8.GetString(packet, 4, 64 /* 70 - 6 */).TrimEnd('\0'); decodedIp = System.Net.IPAddress.Parse(ipString); decodedPort = BinaryPrimitives.ReadUInt16LittleEndian(packetSpan.Slice(68 /* 70 - 2 */)); } // Select voice encryption mode var selectedEncryptionMode = Sodium.SelectMode(voiceReady.Modes); this.SelectedEncryptionMode = selectedEncryptionMode.Value; // Ready this.Discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Selected encryption mode is {0}", selectedEncryptionMode.Key); var vsp = new VoiceDispatch { OpCode = 1, Payload = new VoiceSelectProtocolPayload { Protocol = "udp", Data = new VoiceSelectProtocolPayloadData { Address = this.DiscoveredEndpoint.Address.ToString(), Port = (ushort)this.DiscoveredEndpoint.Port, Mode = selectedEncryptionMode.Key } } }; var vsj = JsonConvert.SerializeObject(vsp, Formatting.None); await this.WsSendAsync(vsj).ConfigureAwait(false); this.SenderTokenSource = new CancellationTokenSource(); this.SenderTask = Task.Run(this.VoiceSenderTask, this.SenderToken); this.ReceiverTokenSource = new CancellationTokenSource(); this.ReceiverTask = Task.Run(this.UdpReceiverTask, this.ReceiverToken); }
private async Task ProcessPayloadAsync(BaseDiscordPayload payload) { LogHandler <WsVoiceClient> .Log.Debug($"Received {Enum.GetName(typeof(VoiceOpType), payload.Op)} payload."); switch (payload.Op) { case VoiceOpType.Ready: Vrp = payload.Data.TryCast <VoiceReadyPayload>(); _udp = new UdpClient(Vrp.IpAddress, Vrp.Port); await _udp.SendDiscoveryAsync(Vrp.Ssrc).ConfigureAwait(false); LogHandler <WsVoiceClient> .Log.Debug($"Sent UDP discovery with {Vrp.Ssrc} ssrc."); _heartBeatTask = HandleHeartbeatAsync(Vrp.HeartbeatInterval); LogHandler <WsVoiceClient> .Log.Debug( $"Started heartbeat task with {Vrp.HeartbeatInterval} interval."); var selectProtocol = new BaseDiscordPayload(VoiceOpType.SelectProtocol, new SelectPayload(Vrp.IpAddress, Vrp.Port)); await _socket.SendAsync(selectProtocol) .ConfigureAwait(false); LogHandler <WsVoiceClient> .Log.Debug($"Sent select protocol with {Vrp.IpAddress}:{Vrp.Port}."); _ = VoiceSenderTask(); break; case VoiceOpType.SessionDescription: var sdp = payload.Data.TryCast <SessionDescriptionPayload>(); if (sdp.Mode != "xsalsa20_poly1305") { return; } _sdp = sdp; SodiumCodec = new SodiumCodec(_sdp.SecretKey); await _socket.SendAsync(new BaseDiscordPayload(VoiceOpType.Speaking, new { delay = 0, speaking = false })).ConfigureAwait(false); _ = SendKeepAliveAsync().ConfigureAwait(false); break; case VoiceOpType.Hello: var helloPayload = payload.Data.TryCast <HelloPayload>(); if (_heartBeatTask != null) { _heartBeatCancel.Cancel(false); _heartBeatTask.Dispose(); _heartBeatTask = null; } _heartBeatTask = HandleHeartbeatAsync(helloPayload.HeartBeatInterval); break; } }
private async Task Stage1(VoiceReadyPayload voiceReady) { #if !NETSTANDARD1_1 // IP Discovery this.UdpClient.Setup(this.ConnectionEndpoint); var pck = new byte[70]; PreparePacket(pck); await this.UdpClient.SendAsync(pck, pck.Length).ConfigureAwait(false); var ipd = await this.UdpClient.ReceiveAsync().ConfigureAwait(false); ReadPacket(ipd, out var ip, out var port); this.DiscoveredEndpoint = new IpEndpoint { Address = ip, Port = port }; this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VNext UDP", $"Endpoint discovery resulted in {ip}:{port}", DateTime.Now); void PreparePacket(byte[] packet) { var ssrc = this.SSRC; var packetSpan = packet.AsSpan(); MemoryMarshal.Write(packetSpan, ref ssrc); Helpers.ZeroFill(packetSpan); } void ReadPacket(byte[] packet, out System.Net.IPAddress decodedIp, out ushort decodedPort) { var packetSpan = packet.AsSpan(); var ipString = new UTF8Encoding(false).GetString(packet, 4, 64 /* 70 - 6 */).TrimEnd('\0'); decodedIp = System.Net.IPAddress.Parse(ipString); decodedPort = BinaryPrimitives.ReadUInt16LittleEndian(packetSpan.Slice(68 /* 70 - 2 */)); } #else this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VNext UDP", $"Voice receive not supported - not performing endpoint discovery", DateTime.Now); await Task.Yield(); // just stop bothering me VS #endif // Select voice encryption mode var selectedEncryptionMode = Sodium.SelectMode(voiceReady.Modes); this.SelectedEncryptionMode = selectedEncryptionMode.Value; // Ready this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VoiceNext", $"Selected encryption mode: {selectedEncryptionMode.Key}", DateTime.Now); var vsp = new VoiceDispatch { OpCode = 1, Payload = new VoiceSelectProtocolPayload { Protocol = "udp", Data = new VoiceSelectProtocolPayloadData { #if !NETSTANDARD1_1 Address = this.DiscoveredEndpoint.Address.ToString(), Port = (ushort)this.DiscoveredEndpoint.Port, #else Address = "0.0.0.0", Port = 0, #endif Mode = selectedEncryptionMode.Key } } }; var vsj = JsonConvert.SerializeObject(vsp, Formatting.None); this.VoiceWs.SendMessage(vsj); this.SenderTokenSource = new CancellationTokenSource(); this.SenderTask = Task.Run(this.VoiceSenderTask, this.SenderToken); this.ReceiverTokenSource = new CancellationTokenSource(); this.ReceiverTask = Task.Run(this.UdpReceiverTask, this.ReceiverToken); }
private async Task Stage1(VoiceReadyPayload voiceReady) { // IP Discovery this.UdpClient.Setup(this.UdpEndpoint); var pck = new byte[70]; PreparePacket(pck); await this.UdpClient.SendAsync(pck, pck.Length).ConfigureAwait(false); // fetch endpoint it it wasn't done already IpEndpoint ownEndpoint; try { var ipd = await this.UdpClient.ReceiveAsync().ConfigureAwait(false); ReadPacket(ipd, out var ip, out var port); ownEndpoint = new IpEndpoint { Address = ip, Port = port }; this.DiscoveredEndpoint = ownEndpoint; this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VNext UDP", $"Endpoint discovery resulted in {ip}:{port}", DateTime.Now); } catch (Exception e) { this.Discord.DebugLogger.LogMessage(LogLevel.Error, "VNext UDP", "Endpoint discovery failed", DateTime.Now, e); // fallback to last value if present if (this.DiscoveredEndpoint.HasValue) { ownEndpoint = this.DiscoveredEndpoint.Value; this.Discord.DebugLogger.LogMessage(LogLevel.Info, "VNext UDP", "Endpoint discovery used old endpoint", DateTime.Now, e); } else { throw e; } } void PreparePacket(byte[] packet) { var ssrc = this.SSRC; var packetSpan = packet.AsSpan(); MemoryMarshal.Write(packetSpan, ref ssrc); Helpers.ZeroFill(packetSpan); } void ReadPacket(byte[] packet, out System.Net.IPAddress decodedIp, out ushort decodedPort) { if (packet.Length != 70) { throw new Exception($"Recieved invalid IP discovery data. Expected length 70 but got {packet.Length}"); } var packetSpan = packet.AsSpan(); var ipString = Utilities.UTF8.GetString(packet, 4, 64 /* 70 - 6 */).TrimEnd('\0'); decodedIp = System.Net.IPAddress.Parse(ipString); decodedPort = BinaryPrimitives.ReadUInt16LittleEndian(packetSpan.Slice(68 /* 70 - 2 */)); } // Select voice encryption mode var selectedEncryptionMode = Sodium.SelectMode(voiceReady.Modes); this.SelectedEncryptionMode = selectedEncryptionMode.Value; // Ready this.Discord.DebugLogger.LogMessage(LogLevel.Debug, "VoiceNext", $"Selected encryption mode: {selectedEncryptionMode.Key}", DateTime.Now); var vsp = new VoiceDispatch { OpCode = 1, Payload = new VoiceSelectProtocolPayload { Protocol = "udp", Data = new VoiceSelectProtocolPayloadData { Address = ownEndpoint.Address.ToString(), Port = (ushort)ownEndpoint.Port, Mode = selectedEncryptionMode.Key } } }; var vsj = JsonConvert.SerializeObject(vsp, Formatting.None); await this.VoiceWs.SendMessageAsync(vsj).ConfigureAwait(false); if (this.SenderTokenSource != null) { this.SenderTokenSource.Cancel(); } this.SenderTokenSource = new CancellationTokenSource(); this.SenderTask = Task.Run(this.VoiceSenderTask, this.SenderToken); if (this.ReceiverTokenSource != null) { this.ReceiverTokenSource.Cancel(); } this.ReceiverTokenSource = new CancellationTokenSource(); this.ReceiverTask = Task.Run(this.UdpReceiverTask, this.ReceiverToken); }