| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 | 
							- // @flow
 - 
 - import { NativeModules } from 'react-native';
 - import { RTCPeerConnection, RTCSessionDescription } from 'react-native-webrtc';
 - 
 - const logger = require('jitsi-meet-logger').getLogger(__filename);
 - 
 - /* eslint-disable no-unused-vars */
 - 
 - // Address families.
 - const AF_INET6 = 30; /* IPv6 */
 - 
 - // Protocols (RFC 1700)
 - const IPPROTO_TCP = 6; /* tcp */
 - const IPPROTO_UDP = 17; /* user datagram protocol */
 - 
 - // Protocol families, same as address families for now.
 - const PF_INET6 = AF_INET6;
 - 
 - const SOCK_DGRAM = 2; /* datagram socket */
 - const SOCK_STREAM = 1; /* stream socket */
 - 
 - /* eslint-enable no-unused-vars */
 - 
 - // XXX At the time of this writing extending RTCPeerConnection using ES6 'class'
 - // and 'extends' causes a runtime error related to the attempt to define the
 - // onaddstream property setter. The error mentions that babelHelpers.set is
 - // undefined which appears to be a thing inside React Native's packager. As a
 - // workaround, extend using the pre-ES6 way.
 - 
 - /**
 -  * The RTCPeerConnection provided by react-native-webrtc fires onaddstream
 -  * before it remembers remotedescription (and thus makes it available to API
 -  * clients). Because that appears to be a problem for lib-jitsi-meet which has
 -  * been successfully running on Chrome, Firefox, etc. for a very long
 -  * time, attempt to meet its expectations (by extending RTCPPeerConnection).
 -  *
 -  * @class
 -  */
 - export default function _RTCPeerConnection(...args: any[]) {
 - 
 -     /* eslint-disable indent, no-invalid-this */
 - 
 -     RTCPeerConnection.apply(this, args);
 - 
 -     this.onaddstream = (...args) => // eslint-disable-line no-shadow
 -         (this._onaddstreamQueue
 -                 ? this._queueOnaddstream
 -                 : this._invokeOnaddstream)
 -             .apply(this, args);
 - 
 -     // Shadow RTCPeerConnection's onaddstream but after _RTCPeerConnection has
 -     // assigned to the property in question. Defining the property on
 -     // _RTCPeerConnection's prototype may (or may not, I don't know) work but I
 -     // don't want to try because the following approach appears to work and I
 -     // understand it.
 - 
 -     // $FlowFixMe
 -     Object.defineProperty(this, 'onaddstream', {
 -         configurable: true,
 -         enumerable: true,
 -         get() {
 -             return this._onaddstream;
 -         },
 -         set(value) {
 -             this._onaddstream = value;
 -         }
 -     });
 - 
 -     /* eslint-enable indent, no-invalid-this */
 - }
 - 
 - _RTCPeerConnection.prototype = Object.create(RTCPeerConnection.prototype);
 - _RTCPeerConnection.prototype.constructor = _RTCPeerConnection;
 - 
 - _RTCPeerConnection.prototype._invokeOnaddstream = function(...args) {
 -     const onaddstream = this._onaddstream;
 - 
 -     return onaddstream && onaddstream.apply(this, args);
 - };
 - 
 - _RTCPeerConnection.prototype._invokeQueuedOnaddstream = function(q) {
 -     q && q.forEach(args => {
 -         try {
 -             this._invokeOnaddstream(...args);
 -         } catch (e) {
 -             // TODO Determine whether the combination of the standard
 -             // setRemoteDescription and onaddstream results in a similar
 -             // swallowing of errors.
 -             _LOGE(e);
 -         }
 -     });
 - };
 - 
 - _RTCPeerConnection.prototype._queueOnaddstream = function(...args) {
 -     this._onaddstreamQueue.push(Array.from(args));
 - };
 - 
 - _RTCPeerConnection.prototype.setRemoteDescription = function(description) {
 - 
 -     return (
 -         _synthesizeIPv6Addresses(description)
 -             .catch(reason => {
 -                 reason && _LOGE(reason);
 - 
 -                 return description;
 -             })
 -             .then(value => _setRemoteDescription.bind(this)(value)));
 - 
 - };
 - 
 - /**
 -  * Logs at error level.
 -  *
 -  * @private
 -  * @returns {void}
 -  */
 - function _LOGE(...args) {
 -     logger.error(...args);
 - }
 - 
 - /**
 -  * Adapts react-native-webrtc's {@link RTCPeerConnection#setRemoteDescription}
 -  * implementation which uses the deprecated, callback-based version to the
 -  * {@code Promise}-based version.
 -  *
 -  * @param {RTCSessionDescription} description - The RTCSessionDescription
 -  * which specifies the configuration of the remote end of the connection.
 -  * @private
 -  * @private
 -  * @returns {Promise}
 -  */
 - function _setRemoteDescription(description) {
 -     return new Promise((resolve, reject) => {
 - 
 -         /* eslint-disable no-invalid-this */
 - 
 -         // Ensure I'm not remembering onaddstream invocations from previous
 -         // setRemoteDescription calls. I shouldn't be but... anyway.
 -         this._onaddstreamQueue = [];
 - 
 -         RTCPeerConnection.prototype.setRemoteDescription.call(this, description)
 -             .then((...args) => {
 -                 let q;
 - 
 -                 try {
 -                     resolve(...args);
 -                 } finally {
 -                     q = this._onaddstreamQueue;
 -                     this._onaddstreamQueue = undefined;
 -                 }
 - 
 -                 this._invokeQueuedOnaddstream(q);
 -             }, (...args) => {
 -                 this._onaddstreamQueue = undefined;
 - 
 -                 reject(...args);
 -             });
 - 
 -         /* eslint-enable no-invalid-this */
 -     });
 - }
 - 
 - // XXX The function _synthesizeIPv6FromIPv4Address is not placed relative to the
 - // other functions in the file according to alphabetical sorting rule of the
 - // coding style. But eslint wants constants to be defined before they are used.
 - 
 - /**
 -  * Synthesizes an IPv6 address from a specific IPv4 address.
 -  *
 -  * @param {string} ipv4 - The IPv4 address from which an IPv6 address is to be
 -  * synthesized.
 -  * @returns {Promise<?string>} A {@code Promise} which gets resolved with the
 -  * IPv6 address synthesized from the specified {@code ipv4} or a falsy value to
 -  * be treated as inability to synthesize an IPv6 address from the specified
 -  * {@code ipv4}.
 -  */
 - const _synthesizeIPv6FromIPv4Address: string => Promise<?string> = (function() {
 -     // POSIX.getaddrinfo
 -     const { POSIX } = NativeModules;
 - 
 -     if (POSIX) {
 -         const { getaddrinfo } = POSIX;
 - 
 -         if (typeof getaddrinfo === 'function') {
 -             return ipv4 =>
 -                 getaddrinfo(/* hostname */ ipv4, /* servname */ undefined)
 -                     .then(([ { ai_addr: ipv6 } ]) => ipv6);
 -         }
 -     }
 - 
 -     // NAT64AddrInfo.getIPv6Address
 -     const { NAT64AddrInfo } = NativeModules;
 - 
 -     if (NAT64AddrInfo) {
 -         const { getIPv6Address } = NAT64AddrInfo;
 - 
 -         if (typeof getIPv6Address === 'function') {
 -             return getIPv6Address;
 -         }
 -     }
 - 
 -     // There's no POSIX.getaddrinfo or NAT64AddrInfo.getIPv6Address.
 -     return () =>
 -         Promise.reject(
 -             'The impossible just happened! No POSIX.getaddrinfo or'
 -                 + ' NAT64AddrInfo.getIPv6Address!');
 - })();
 - 
 - /**
 -  * Synthesizes IPv6 addresses on iOS in order to support IPv6 NAT64 networks.
 -  *
 -  * @param {RTCSessionDescription} sdp - The RTCSessionDescription which
 -  * specifies the configuration of the remote end of the connection.
 -  * @private
 -  * @returns {Promise}
 -  */
 - function _synthesizeIPv6Addresses(sdp) {
 -     return (
 -         new Promise(resolve => resolve(_synthesizeIPv6Addresses0(sdp)))
 -             .then(({ ips, lines }) =>
 -                 Promise.all(Array.from(ips.values()))
 -                     .then(() => _synthesizeIPv6Addresses1(sdp, ips, lines))
 -             ));
 - }
 - 
 - /* eslint-disable max-depth */
 - 
 - /**
 -  * Begins the asynchronous synthesis of IPv6 addresses.
 -  *
 -  * @param {RTCSessionDescription} sessionDescription - The RTCSessionDescription
 -  * for which IPv6 addresses will be synthesized.
 -  * @private
 -  * @returns {{
 -  *     ips: Map,
 -  *     lines: Array
 -  * }}
 -  */
 - function _synthesizeIPv6Addresses0(sessionDescription) {
 -     const sdp = sessionDescription.sdp;
 -     let start = 0;
 -     const lines = [];
 -     const ips = new Map();
 - 
 -     do {
 -         const end = sdp.indexOf('\r\n', start);
 -         let line;
 - 
 -         if (end === -1) {
 -             line = sdp.substring(start);
 - 
 -             // Break out of the loop at the end of the iteration.
 -             start = undefined;
 -         } else {
 -             line = sdp.substring(start, end);
 -             start = end + 2;
 -         }
 - 
 -         if (line.startsWith('a=candidate:')) {
 -             const candidate = line.split(' ');
 - 
 -             if (candidate.length >= 10 && candidate[6] === 'typ') {
 -                 const ip4s = [ candidate[4] ];
 -                 let abort = false;
 - 
 -                 for (let i = 8; i < candidate.length; ++i) {
 -                     if (candidate[i] === 'raddr') {
 -                         ip4s.push(candidate[++i]);
 -                         break;
 -                     }
 -                 }
 - 
 -                 for (const ip of ip4s) {
 -                     if (ip.indexOf(':') === -1) {
 -                         ips.has(ip)
 -                             || ips.set(ip, new Promise((resolve, reject) => {
 -                                 const v = ips.get(ip);
 - 
 -                                 if (v && typeof v === 'string') {
 -                                     resolve(v);
 -                                 } else {
 -                                     _synthesizeIPv6FromIPv4Address(ip).then(
 -                                         value => {
 -                                             if (!value
 -                                                     || value.indexOf(':') === -1
 -                                                     || value === ips.get(ip)) {
 -                                                 ips.delete(ip);
 -                                             } else {
 -                                                 ips.set(ip, value);
 -                                             }
 -                                             resolve(value);
 -                                         },
 -                                         reject);
 -                                 }
 -                             }));
 -                     } else {
 -                         abort = true;
 -                         break;
 -                     }
 -                 }
 -                 if (abort) {
 -                     ips.clear();
 -                     break;
 -                 }
 - 
 -                 line = candidate;
 -             }
 -         }
 - 
 -         lines.push(line);
 -     } while (start);
 - 
 -     return {
 -         ips,
 -         lines
 -     };
 - }
 - 
 - /* eslint-enable max-depth */
 - 
 - /**
 -  * Completes the asynchronous synthesis of IPv6 addresses.
 -  *
 -  * @param {RTCSessionDescription} sessionDescription - The RTCSessionDescription
 -  * for which IPv6 addresses are being synthesized.
 -  * @param {Map} ips - A Map of IPv4 addresses found in the specified
 -  * sessionDescription to synthesized IPv6 addresses.
 -  * @param {Array} lines - The lines of the specified sessionDescription.
 -  * @private
 -  * @returns {RTCSessionDescription} A RTCSessionDescription that represents the
 -  * result of the synthesis of IPv6 addresses.
 -  */
 - function _synthesizeIPv6Addresses1(sessionDescription, ips, lines) {
 -     if (ips.size === 0) {
 -         return sessionDescription;
 -     }
 - 
 -     for (let l = 0; l < lines.length; ++l) {
 -         const candidate = lines[l];
 - 
 -         if (typeof candidate !== 'string') {
 -             let ip4 = candidate[4];
 -             let ip6 = ips.get(ip4);
 - 
 -             ip6 && (candidate[4] = ip6);
 - 
 -             for (let i = 8; i < candidate.length; ++i) {
 -                 if (candidate[i] === 'raddr') {
 -                     ip4 = candidate[++i];
 -                     (ip6 = ips.get(ip4)) && (candidate[i] = ip6);
 -                     break;
 -                 }
 -             }
 - 
 -             lines[l] = candidate.join(' ');
 -         }
 -     }
 - 
 -     return new RTCSessionDescription({
 -         sdp: lines.join('\r\n'),
 -         type: sessionDescription.type
 -     });
 - }
 
 
  |