|
30 | 30 | using WebSocketSharp;
|
31 | 31 | using WebSocketSharp.Net.WebSockets;
|
32 | 32 | using WebSocketSharp.Server;
|
| 33 | +using Org.BouncyCastle.Crypto.DtlsSrtp; |
33 | 34 |
|
34 | 35 | namespace SIPSorcery.Examples
|
35 | 36 | {
|
@@ -169,14 +170,20 @@ private static RTCPeerConnection Createpc(WebSocketContext context)
|
169 | 170 | credentialType = RTCIceCredentialType.password
|
170 | 171 | }
|
171 | 172 | },
|
172 |
| - iceTransportPolicy = RTCIceTransportPolicy.relay |
| 173 | + iceTransportPolicy = RTCIceTransportPolicy.all |
173 | 174 | };
|
174 | 175 |
|
175 | 176 | var pc = new RTCPeerConnection(pcConfiguration);
|
176 | 177 |
|
177 | 178 | #if DTLS_IS_ENABLED
|
178 |
| - SIPSorceryMedia.DtlsHandshake dtls = new SIPSorceryMedia.DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH); |
179 |
| - dtls.Debug = true; |
| 179 | + //SIPSorceryMedia.DtlsHandshake dtls = new SIPSorceryMedia.DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH); |
| 180 | + //dtls.Debug = true; |
| 181 | + |
| 182 | + //var certificate = DtlsUtils.CreateSelfSignedCert(); |
| 183 | + //DtlsSrtpTransport dtlsHandle = new DtlsSrtpTransport( |
| 184 | + // pc.IceRole == IceRolesEnum.active ? |
| 185 | + // (IDtlsSrtpPeer)new DtlsSrtpClient(certificate) : |
| 186 | + // (IDtlsSrtpPeer)new DtlsSrtpServer(certificate)); |
180 | 187 | #endif
|
181 | 188 |
|
182 | 189 | // Add inactive audio and video tracks.
|
@@ -216,24 +223,36 @@ private static RTCPeerConnection Createpc(WebSocketContext context)
|
216 | 223 | else
|
217 | 224 | {
|
218 | 225 | #if DTLS_IS_ENABLED
|
219 |
| - if (pc.IceRole == IceRolesEnum.active) |
220 |
| - { |
221 |
| - logger.LogDebug("Starting DLS handshake as client task."); |
222 |
| - _ = Task.Run(() => |
223 |
| - { |
224 |
| - bool handshakedResult = DoDtlsHandshake(pc, dtls, true, pc.RemotePeerDtlsFingerprint); |
225 |
| - logger.LogDebug($"DTLS handshake result {handshakedResult}."); |
226 |
| - }); |
227 |
| - } |
228 |
| - else |
| 226 | + DtlsSrtpTransport dtlsHandle = new DtlsSrtpTransport( |
| 227 | + pc.IceRole == IceRolesEnum.active ? |
| 228 | + (IDtlsSrtpPeer)new DtlsSrtpClient(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH) : |
| 229 | + (IDtlsSrtpPeer)new DtlsSrtpServer(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH)); |
| 230 | + |
| 231 | + logger.LogDebug($"Starting DLS handshake with role {pc.IceRole}."); |
| 232 | + Task.Run(async () => |
229 | 233 | {
|
230 |
| - logger.LogDebug("Starting DLS handshake as server task."); |
231 |
| - _ = Task.Run(() => |
232 |
| - { |
233 |
| - bool handshakedResult = DoDtlsHandshake(pc, dtls, false, pc.RemotePeerDtlsFingerprint); |
234 |
| - logger.LogDebug($"DTLS handshake result {handshakedResult}."); |
235 |
| - }); |
236 |
| - } |
| 234 | + var dtlsResult = await DoDtlsHandshake(pc, dtlsHandle); |
| 235 | + logger.LogDebug($"DTLS handshake result {dtlsResult}."); |
| 236 | + }); |
| 237 | + |
| 238 | + //if (pc.IceRole == IceRolesEnum.active) |
| 239 | + //{ |
| 240 | + // logger.LogDebug("Starting DTLS handshake as client task."); |
| 241 | + // _ = Task.Run(() => |
| 242 | + // { |
| 243 | + // bool handshakedResult = DoDtlsHandshake(pc, dtls, true, pc.RemotePeerDtlsFingerprint); |
| 244 | + // logger.LogDebug($"DTLS handshake result {handshakedResult}."); |
| 245 | + // }); |
| 246 | + //} |
| 247 | + //else |
| 248 | + //{ |
| 249 | + // logger.LogDebug("Starting DTLS handshake as server task."); |
| 250 | + // _ = Task.Run(() => |
| 251 | + // { |
| 252 | + // bool handshakedResult = DoDtlsHandshake(pc, dtls, false, pc.RemotePeerDtlsFingerprint); |
| 253 | + // logger.LogDebug($"DTLS handshake result {handshakedResult}."); |
| 254 | + // }); |
| 255 | + //} |
237 | 256 | #endif
|
238 | 257 | }
|
239 | 258 | }
|
@@ -304,84 +323,121 @@ private static void AddConsoleLogger()
|
304 | 323 |
|
305 | 324 | #if DTLS_IS_ENABLED
|
306 | 325 |
|
307 |
| - /// <summary> |
308 |
| - /// Hands the socket handle to the DTLS context and waits for the handshake to complete. |
309 |
| - /// </summary> |
310 |
| - /// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param> |
311 |
| - private static bool DoDtlsHandshake(RTCPeerConnection pc, SIPSorceryMedia.DtlsHandshake dtls, bool isClient, byte[] sdpFingerprint) |
| 326 | + /* DtlsHandshake requires DtlsSrtpTransport to work. |
| 327 | + * DtlsSrtpTransport is similar to C++ Dtls class combined with Srtp class and can perform Handshake as Server or Client in same call. |
| 328 | + * The constructor of transport require a DtlsStrpClient or DtlsSrtpServer to work and the method DoHandshake require a socket with |
| 329 | + * support to IPV6 Multiplex and a RemoteEndPoint (RemoteEndPoint will be discarded when performing as Server) */ |
| 330 | + private static async Task<bool> DoDtlsHandshake(RTCPeerConnection peerConnection, DtlsSrtpTransport dtlsHandle) |
312 | 331 | {
|
313 |
| - logger.LogDebug("DoDtlsHandshake started."); |
| 332 | + Console.WriteLine("DoDtlsHandshake started."); |
314 | 333 |
|
315 |
| - if (!File.Exists(DTLS_CERTIFICATE_PATH)) |
316 |
| - { |
317 |
| - throw new ApplicationException($"The DTLS certificate file could not be found at {DTLS_CERTIFICATE_PATH}."); |
318 |
| - } |
319 |
| - else if (!File.Exists(DTLS_KEY_PATH)) |
320 |
| - { |
321 |
| - throw new ApplicationException($"The DTLS key file could not be found at {DTLS_KEY_PATH}."); |
322 |
| - } |
| 334 | + var rtpChannel = peerConnection.GetRtpChannel(SDPMediaTypesEnum.audio); |
323 | 335 |
|
324 |
| - int res = 0; |
325 |
| - bool fingerprintMatch = false; |
| 336 | + dtlsHandle.OnDataReady += (buf) => rtpChannel.SendAsync(RTPChannelSocketsEnum.RTP, peerConnection.AudioDestinationEndPoint, buf); |
| 337 | + peerConnection.OnDtlsPacket += (buf) => dtlsHandle.WriteToRecvStream(buf); |
326 | 338 |
|
327 |
| - if (isClient) |
328 |
| - { |
329 |
| - logger.LogDebug($"DTLS client handshake starting to {pc.AudioDestinationEndPoint}."); |
| 339 | + var res = dtlsHandle.DoHandshake(); |
330 | 340 |
|
331 |
| - // For the DTLS handshake to work connect must be called on the socket so openssl knows where to send. |
332 |
| - var rtpSocket = pc.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket; |
333 |
| - rtpSocket.Connect(pc.AudioDestinationEndPoint); |
| 341 | + Console.WriteLine("DtlsContext initialisation result=" + res); |
334 | 342 |
|
335 |
| - byte[] fingerprint = null; |
336 |
| - var peerEP = pc.AudioDestinationEndPoint; |
337 |
| - res = dtls.DoHandshakeAsClient((ulong)rtpSocket.Handle, (short)peerEP.AddressFamily.GetHashCode(), peerEP.Address.GetAddressBytes(), (ushort)peerEP.Port, ref fingerprint); |
338 |
| - if (fingerprint != null) |
339 |
| - { |
340 |
| - logger.LogDebug($"DTLS server fingerprint {ByteBufferInfo.HexStr(fingerprint)}."); |
341 |
| - fingerprintMatch = sdpFingerprint.SequenceEqual(fingerprint); |
342 |
| - } |
343 |
| - } |
344 |
| - else |
| 343 | + if (dtlsHandle.IsHandshakeComplete()) |
345 | 344 | {
|
346 |
| - byte[] fingerprint = null; |
347 |
| - res = dtls.DoHandshakeAsServer((ulong)pc.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket.Handle, ref fingerprint); |
348 |
| - if (fingerprint != null) |
349 |
| - { |
350 |
| - logger.LogDebug($"DTLS client fingerprint {ByteBufferInfo.HexStr(fingerprint)}."); |
351 |
| - fingerprintMatch = sdpFingerprint.SequenceEqual(fingerprint); |
352 |
| - } |
353 |
| - } |
| 345 | + Console.WriteLine("DTLS negotiation complete."); |
354 | 346 |
|
355 |
| - logger.LogDebug("DtlsContext initialisation result=" + res); |
| 347 | + peerConnection.SetSecurityContext( |
| 348 | + dtlsHandle.ProtectRTP, |
| 349 | + dtlsHandle.UnprotectRTP, |
| 350 | + dtlsHandle.ProtectRTCP, |
| 351 | + dtlsHandle.UnprotectRTCP); |
356 | 352 |
|
357 |
| - if (dtls.IsHandshakeComplete()) |
358 |
| - { |
359 |
| - logger.LogDebug("DTLS negotiation complete."); |
| 353 | + await peerConnection.Start(); |
360 | 354 |
|
361 |
| - if (!fingerprintMatch) |
362 |
| - { |
363 |
| - logger.LogWarning("DTLS fingerprint mismatch."); |
364 |
| - return false; |
365 |
| - } |
366 |
| - else |
367 |
| - { |
368 |
| - var srtpSendContext = new SIPSorceryMedia.Srtp(dtls, isClient); |
369 |
| - var srtpReceiveContext = new SIPSorceryMedia.Srtp(dtls, !isClient); |
370 |
| - |
371 |
| - pc.SetSecurityContext( |
372 |
| - srtpSendContext.ProtectRTP, |
373 |
| - srtpReceiveContext.UnprotectRTP, |
374 |
| - srtpSendContext.ProtectRTCP, |
375 |
| - srtpReceiveContext.UnprotectRTCP); |
376 |
| - |
377 |
| - return true; |
378 |
| - } |
| 355 | + return true; |
379 | 356 | }
|
380 | 357 | else
|
381 | 358 | {
|
382 | 359 | return false;
|
383 | 360 | }
|
384 | 361 | }
|
| 362 | + |
| 363 | + /// <summary> |
| 364 | + /// Hands the socket handle to the DTLS context and waits for the handshake to complete. |
| 365 | + /// </summary> |
| 366 | + /// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param> |
| 367 | + //private static bool DoDtlsHandshake(RTCPeerConnection pc, SIPSorceryMedia.DtlsHandshake dtls, bool isClient, byte[] sdpFingerprint) |
| 368 | + //{ |
| 369 | + // logger.LogDebug("DoDtlsHandshake started."); |
| 370 | + |
| 371 | + // if (!File.Exists(DTLS_CERTIFICATE_PATH)) |
| 372 | + // { |
| 373 | + // throw new ApplicationException($"The DTLS certificate file could not be found at {DTLS_CERTIFICATE_PATH}."); |
| 374 | + // } |
| 375 | + // else if (!File.Exists(DTLS_KEY_PATH)) |
| 376 | + // { |
| 377 | + // throw new ApplicationException($"The DTLS key file could not be found at {DTLS_KEY_PATH}."); |
| 378 | + // } |
| 379 | + |
| 380 | + // int res = 0; |
| 381 | + // bool fingerprintMatch = false; |
| 382 | + |
| 383 | + // if (isClient) |
| 384 | + // { |
| 385 | + // logger.LogDebug($"DTLS client handshake starting to {pc.AudioDestinationEndPoint}."); |
| 386 | + |
| 387 | + // // For the DTLS handshake to work connect must be called on the socket so openssl knows where to send. |
| 388 | + // var rtpSocket = pc.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket; |
| 389 | + // rtpSocket.Connect(pc.AudioDestinationEndPoint); |
| 390 | + |
| 391 | + // byte[] fingerprint = null; |
| 392 | + // var peerEP = pc.AudioDestinationEndPoint; |
| 393 | + // res = dtls.DoHandshakeAsClient((ulong)rtpSocket.Handle, (short)peerEP.AddressFamily.GetHashCode(), peerEP.Address.GetAddressBytes(), (ushort)peerEP.Port, ref fingerprint); |
| 394 | + // if (fingerprint != null) |
| 395 | + // { |
| 396 | + // logger.LogDebug($"DTLS server fingerprint {ByteBufferInfo.HexStr(fingerprint)}."); |
| 397 | + // fingerprintMatch = sdpFingerprint.SequenceEqual(fingerprint); |
| 398 | + // } |
| 399 | + // } |
| 400 | + // else |
| 401 | + // { |
| 402 | + // byte[] fingerprint = null; |
| 403 | + // res = dtls.DoHandshakeAsServer((ulong)pc.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket.Handle, ref fingerprint); |
| 404 | + // if (fingerprint != null) |
| 405 | + // { |
| 406 | + // logger.LogDebug($"DTLS client fingerprint {ByteBufferInfo.HexStr(fingerprint)}."); |
| 407 | + // fingerprintMatch = sdpFingerprint.SequenceEqual(fingerprint); |
| 408 | + // } |
| 409 | + // } |
| 410 | + |
| 411 | + // logger.LogDebug("DtlsContext initialisation result=" + res); |
| 412 | + |
| 413 | + // if (dtls.IsHandshakeComplete()) |
| 414 | + // { |
| 415 | + // logger.LogDebug("DTLS negotiation complete."); |
| 416 | + |
| 417 | + // if (!fingerprintMatch) |
| 418 | + // { |
| 419 | + // logger.LogWarning("DTLS fingerprint mismatch."); |
| 420 | + // return false; |
| 421 | + // } |
| 422 | + // else |
| 423 | + // { |
| 424 | + // var srtpSendContext = new SIPSorceryMedia.Srtp(dtls, isClient); |
| 425 | + // var srtpReceiveContext = new SIPSorceryMedia.Srtp(dtls, !isClient); |
| 426 | + |
| 427 | + // pc.SetSecurityContext( |
| 428 | + // srtpSendContext.ProtectRTP, |
| 429 | + // srtpReceiveContext.UnprotectRTP, |
| 430 | + // srtpSendContext.ProtectRTCP, |
| 431 | + // srtpReceiveContext.UnprotectRTCP); |
| 432 | + |
| 433 | + // return true; |
| 434 | + // } |
| 435 | + // } |
| 436 | + // else |
| 437 | + // { |
| 438 | + // return false; |
| 439 | + // } |
| 440 | + //} |
385 | 441 | #endif
|
386 | 442 | }
|
387 | 443 | }
|
0 commit comments