@babblevoice/babble-drachtio-callmanager 2.1.2 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -12,7 +12,8 @@ const default_options = {
12
12
  "rfc2833": true, /* Enable RFC 2833 - DTMF */
13
13
  "late": false, /* Late negotiation */
14
14
  "registrar": false, /* our registrar object or falsey */
15
- "referauthrequired": true
15
+ "referauthrequired": true,
16
+ "ignoreipv6candidates": true /* ipv6 does not work in projectrtp */
16
17
  }
17
18
 
18
19
  /**
package/lib/call.js CHANGED
@@ -11,6 +11,8 @@ const callstore = require( "./store.js" )
11
11
 
12
12
  const sipauth = require( "@babblevoice/babble-drachtio-auth" )
13
13
 
14
+ const ipv6regex = /^(?:(?:[a-fA-F\d]{1,4}:){7}(?:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-fA-F\d]{1,4}|:)|(?:[a-fA-F\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,2}|:)|(?:[a-fA-F\d]{1,4}:){4}(?:(?::[a-fA-F\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,3}|:)|(?:[a-fA-F\d]{1,4}:){3}(?:(?::[a-fA-F\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,4}|:)|(?:[a-fA-F\d]{1,4}:){2}(?:(?::[a-fA-F\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,5}|:)|(?:[a-fA-F\d]{1,4}:){1}(?:(?::[a-fA-F\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,6}|:)|(?::(?:(?::[a-fA-F\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-fA-F\d]{1,4}){1,7}|:)))(?:%[0-9a-zA-Z]{1,})?$/gm
15
+
14
16
  /*
15
17
  Enum for different reasons for hangup.
16
18
  */
@@ -910,8 +912,15 @@ class call {
910
912
  static async _parsesdpcandidates( target, sdp ) {
911
913
 
912
914
  if( Array.isArray( sdp.media[ 0 ].candidates ) ) {
913
- const candidates = sdp.media[ 0 ].candidates
915
+ let candidates = sdp.media[ 0 ].candidates
914
916
  if( 0 < candidates.length ) {
917
+ if( callmanager.options.ignoreipv6candidates ) {
918
+ candidates = candidates.filter( ( c ) => {
919
+ const ismatch = ipv6regex.test( c.ip )
920
+ return !ismatch
921
+ } )
922
+ }
923
+
915
924
  candidates.sort( ( l, r ) => { return r.priority - l.priority } )
916
925
  target.port = candidates[ 0 ].port
917
926
 
@@ -1356,9 +1365,7 @@ class call {
1356
1365
  .setconnectionaddress( this.channels.audio.local.address )
1357
1366
  .setaudioport( this.channels.audio.local.port )
1358
1367
 
1359
- if( this.options.rfc2833 ) {
1360
- this.sdp.local.addcodecs( "2833" )
1361
- }
1368
+ this.#checkandadd2833()
1362
1369
 
1363
1370
  if( this._iswebrtc ) {
1364
1371
  this.sdp.local.addssrc( this.channels.audio.local.ssrc )
@@ -1379,13 +1386,13 @@ class call {
1379
1386
  if( !this.selectedcodec ) {
1380
1387
  return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
1381
1388
  }
1389
+
1390
+ this.sdp.remote.select( this.selectedcodec )
1382
1391
 
1383
1392
  const remoteaudio = this.sdp.remote.getaudio()
1384
1393
  if( !remoteaudio ) {
1385
1394
  return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
1386
1395
  }
1387
-
1388
- this.sdp.remote.select( this.selectedcodec )
1389
1396
 
1390
1397
  await call._parsesdpcandidates( remoteaudio, this.sdp.remote.sdp )
1391
1398
 
@@ -2494,6 +2501,20 @@ class call {
2494
2501
 
2495
2502
  }
2496
2503
 
2504
+ /**
2505
+ * If we have remote sdp we have to check they support 2833 before we add backand our options permit it.
2506
+ * If we have no remote sdp then we only check our options.
2507
+ */
2508
+ #checkandadd2833() {
2509
+ if( this.sdp.remote ) {
2510
+ if( this.options.rfc2833 && this.sdp.remote.has( "2833" ) ) {
2511
+ this.sdp.local.addcodecs( "2833" )
2512
+ }
2513
+ } else if( this.options.rfc2833 ) {
2514
+ this.sdp.local.addcodecs( "2833" )
2515
+ }
2516
+ }
2517
+
2497
2518
  /**
2498
2519
  *
2499
2520
  */
@@ -2508,9 +2529,7 @@ class call {
2508
2529
  this.sdp.local.setaudioport( this.channels.audio.local.port )
2509
2530
  .setconnectionaddress( this.channels.audio.local.address )
2510
2531
 
2511
- if( this.options.rfc2833 ) {
2512
- this.sdp.local.addcodecs( "2833" )
2513
- }
2532
+ this.#checkandadd2833()
2514
2533
 
2515
2534
  /* DTLS is only supported ( outbound ) on websocket connections */
2516
2535
  this.sip.contact = this.options.contact
package/lib/sdp.js CHANGED
@@ -417,6 +417,30 @@ class sdp {
417
417
  return this
418
418
  }
419
419
 
420
+ /**
421
+ * Gets a list of codecs (that we support) and return as an array of strings.
422
+ * @param { string } type
423
+ * @returns { array< string > } array of codec names in the format [ "pcma" ]
424
+ */
425
+ #codecs( type = "audio" ) {
426
+
427
+ const audio = this.getmedia( type )
428
+
429
+ /* work out an array of codecs on this side in the format of [ "pcma", "pcmu" ] */
430
+ const ourcodecs = []
431
+ for( const pt of audio.payloads ) {
432
+ if( undefined === codecconv[ pt ] ) continue
433
+ if( 97 == pt ) {
434
+ if( -1 == getconfigforpt( audio, 97 ).indexOf( "mode=30" ) )
435
+ ourcodecs.push( codecconv[ pt ] )
436
+ } else {
437
+ ourcodecs.push( codecconv[ pt ] )
438
+ }
439
+ }
440
+
441
+ return ourcodecs
442
+ }
443
+
420
444
  /*
421
445
  Only allow CODECs supported by both sides.
422
446
  other can be:
@@ -436,23 +460,7 @@ class sdp {
436
460
 
437
461
  /* ensure other side is on the format [ "pcma", "pcmu" ] */
438
462
  other = alltocodecname( other )
439
-
440
- let audio = []
441
- for( const m of this.sdp.media ) {
442
- if( "audio" == m.type ) audio = m
443
- }
444
-
445
- /* work out an array of codecs on this side in the format of [ "pcma", "pcmu" ] */
446
- const ourcodecs = []
447
- for( const pt of audio.payloads ) {
448
- if( undefined === codecconv[ pt ] ) continue
449
- if( 97 == pt ) {
450
- if( -1 == getconfigforpt( audio, 97 ).indexOf( "mode=30" ) )
451
- ourcodecs.push( codecconv[ pt ] )
452
- } else {
453
- ourcodecs.push( codecconv[ pt ] )
454
- }
455
- }
463
+ const ourcodecs = this.#codecs()
456
464
 
457
465
  /* intersection */
458
466
  let retval = other.filter( value => ourcodecs.includes( value ) )
@@ -469,6 +477,23 @@ class sdp {
469
477
  return full
470
478
  }
471
479
 
480
+ /**
481
+ * See other param in intersection. Confirms that we have
482
+ * support for at least one of the codecs in codecs
483
+ * @param { array< string > | string } codecs
484
+ */
485
+ has( codecs ) {
486
+
487
+ const ourcodecs = this.#codecs()
488
+ codecs = alltocodecname( codecs )
489
+
490
+ /* intersection */
491
+ if( undefined === codecs.find( value => ourcodecs.includes( value ) ) ) return false
492
+
493
+ return true
494
+
495
+ }
496
+
472
497
  toString() {
473
498
 
474
499
  /* We need to convert payloads back to string to stop a , being added */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babblevoice/babble-drachtio-callmanager",
3
- "version": "2.1.2",
3
+ "version": "2.2.1",
4
4
  "description": "Call processing to create a PBX",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,8 +1,9 @@
1
1
 
2
2
 
3
3
  const expect = require( "chai" ).expect
4
- const sdp = require( "../../lib/sdp.js" )
5
- const call = require( "../../lib/call.js" )
4
+ const sdp = require( "../../lib/sdp" )
5
+ const call = require( "../../lib/call" )
6
+ const callmanager = require( "../../index" )
6
7
 
7
8
  describe( "sdp", function() {
8
9
 
@@ -643,4 +644,133 @@ a=rtcp-mux`
643
644
  expect( bvdesktopinvite200sdpobj.intersection( ourcodecs, true ) ).to.equal( "g722" )
644
645
  expect( magrathea200sdpobj.intersection( ourcodecs, true ) ).to.equal( "pcma" )
645
646
  } )
647
+
648
+ it( "outbound example - inc ipv6", async () => {
649
+
650
+ const sdpstr = `v=0
651
+ o=- 7873703533563891424 2 IN IP4 127.0.0.1
652
+ s=-
653
+ t=0 0
654
+ a=group:BUNDLE 0
655
+ a=extmap-allow-mixed
656
+ a=msid-semantic: WMS a46039f4-1857-410e-b1cc-215c09878068
657
+ m=audio 41645 UDP/TLS/RTP/SAVPF 111 63 9 0 8 13 110 126
658
+ c=IN IP4 86.169.150.38
659
+ a=rtcp:9 IN IP4 0.0.0.0
660
+ a=candidate:532873972 1 udp 2122131711 2a00:23c6:e093:a801:6722:f7bb:aeeb:5e04 39087 typ host generation 0 network-id 4 network-cost 10
661
+ a=candidate:226183667 1 udp 1685987071 86.169.150.38 41645 typ srflx raddr 172.17.0.1 rport 41645 generation 0 network-id 2
662
+ a=ice-ufrag:c97P
663
+ a=ice-pwd:sK0NGVPIIx4/qEX+tCVW5dzH
664
+ a=ice-options:trickle
665
+ a=fingerprint:sha-256 0F:37:28:0F:66:1B:7E:D5:36:A4:EB:2D:D4:A8:6E:33:69:31:3B:D4:7B:71:0B:DE:41:09:D1:6C:1E:56:02:1C
666
+ a=setup:actpass
667
+ a=mid:0
668
+ a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
669
+ a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
670
+ a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
671
+ a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid
672
+ a=sendrecv
673
+ a=msid:a46039f4-1857-410e-b1cc-215c09878068 ce8c9c25-2ea0-4079-aaa5-54f771d53310
674
+ a=rtcp-mux
675
+ a=rtpmap:111 opus/48000/2
676
+ a=rtcp-fb:111 transport-cc
677
+ a=fmtp:111 minptime=10;useinbandfec=1
678
+ a=rtpmap:63 red/48000/2
679
+ a=fmtp:63 111/111
680
+ a=rtpmap:9 G722/8000
681
+ a=rtpmap:0 PCMU/8000
682
+ a=rtpmap:8 PCMA/8000
683
+ a=rtpmap:13 CN/8000
684
+ a=rtpmap:110 telephone-event/48000
685
+ a=rtpmap:126 telephone-event/8000
686
+ a=ssrc:222390620 cname:q7Is0hRbTrTbcMJM
687
+ a=ssrc:222390620 msid:a46039f4-1857-410e-b1cc-215c09878068 ce8c9c25-2ea0-4079-aaa5-54f771d53310
688
+ `
689
+ callmanager.callmanager( { srf: { use: () => {} } } )
690
+ const sdpobj = sdp.create( sdpstr )
691
+ const target = {}
692
+ await call._parsesdpcandidates( target, sdpobj.sdp )
693
+ sdpobj.intersection( "g722", true )
694
+
695
+ /* our default is to ignore IPv6 addresses (until projectrtp supports it) */
696
+ expect( target.port ).to.equal( 41645 )
697
+ expect( target.address ).to.equal( "86.169.150.38" )
698
+
699
+
700
+ } )
701
+
702
+ it( "codec has", async () => {
703
+
704
+ const sdpwith2833 = `v=0
705
+ o=- 4317 0 IN IP4 127.0.0.1
706
+ s=project
707
+ c=IN IP4 13.42.100.66
708
+ t=0 0
709
+ a=msid-semantic: WMS 136f9ec7464dca67065c81da42013e84
710
+ m=audio 10036 UDP/TLS/RTP/SAVPF 9 101
711
+ a=rtpmap:9 G722/8000
712
+ a=rtpmap:101 telephone-event/8000
713
+ a=fmtp:101 0-16
714
+ a=setup:passive
715
+ a=msid:136f9ec7464dca67065c81da42013e84 ff8ebd1be43cdf5170974bf0d1efb772
716
+ a=ptime:20
717
+ a=sendrecv
718
+ a=ice-ufrag:50cf0e6109bad2be
719
+ a=ice-pwd:9Tbl7U92hrzTUluayFbRPYh9
720
+ a=fingerprint:sha-256 12:31:FB:95:BB:00:1A:D8:24:94:71:21:CE:05:57:8F:47:1E:5E:C5:43:41:19:3F:2A:B8:8C:92:07:BB:BC:56
721
+ a=candidate:1 1 udp 255 13.42.100.66 10036 typ host generation 0
722
+ a=ssrc:3127369874 cname:97016b88e9a33a3b465b15cff21fec35
723
+ a=ssrc:3127369874 msid:136f9ec7464dca67065c81da42013e84 ff8ebd1be43cdf5170974bf0d1efb772
724
+ a=ssrc:3127369874 mslabel:136f9ec7464dca67065c81da42013e84
725
+ a=ssrc:3127369874 label:ff8ebd1be43cdf5170974bf0d1efb772
726
+ a=rtcp-mux`
727
+
728
+ const sdpwithout2833 = `v=0
729
+ o=- 4316 0 IN IP4 127.0.0.1
730
+ s=project
731
+ c=IN IP4 18.170.39.61
732
+ t=0 0
733
+ m=audio 10038 RTP/AVP 9 97 0 8
734
+ a=rtpmap:9 G722/8000
735
+ a=rtpmap:97 ilbc/8000
736
+ a=rtpmap:0 PCMU/8000
737
+ a=rtpmap:8 PCMA/8000
738
+ a=fmtp:97 mode=20
739
+ a=ptime:20
740
+ a=sendrecv`
741
+
742
+ const noilbc30 = `v=0
743
+ o=Z 1608292844058 1 IN IP4 192.168.0.141
744
+ s=Z
745
+ c=IN IP4 192.168.0.141
746
+ t=0 0
747
+ m=audio 56802 RTP/AVP 8 0 9 106 101 98 97
748
+ a=rtpmap:106 opus/48000/2
749
+ a=fmtp:106 minptime=20; cbr=1; maxaveragebitrate=40000; useinbandfec=1
750
+ a=rtpmap:101 telephone-event/8000
751
+ a=fmtp:101 0-16
752
+ a=rtpmap:98 telephone-event/48000
753
+ a=fmtp:98 0-16
754
+ a=rtpmap:97 iLBC/8000
755
+ a=fmtp:97 mode=30
756
+ a=sendrecv`
757
+
758
+ const sdpwith2833obj = sdp.create( sdpwith2833 )
759
+ const sdpwithout2833obj = sdp.create( sdpwithout2833 )
760
+ const noilbc30obj = sdp.create( noilbc30 )
761
+
762
+ expect( sdpwith2833obj.has( "2833" ) ).to.be.true
763
+ expect( sdpwith2833obj.has( "pcmu" ) ).to.be.false
764
+ expect( sdpwith2833obj.has( "pcma" ) ).to.be.false
765
+
766
+
767
+ expect( sdpwithout2833obj.has( "2833" ) ).to.be.false
768
+ expect( sdpwithout2833obj.has( "g722" ) ).to.be.true
769
+ expect( sdpwithout2833obj.has( "ilbc" ) ).to.be.true
770
+
771
+ expect( noilbc30obj.has( "ilbc" ) ).to.be.false
772
+ expect( noilbc30obj.has( "2833" ) ).to.be.true
773
+ expect( noilbc30obj.has( "g722" ) ).to.be.true
774
+
775
+ } )
646
776
  } )