1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462 |
- var util = require('util');
- var net = require('net');
- var dns = require('dns');
- var assert = require('assert');
- var dgram = require('dgram');
- var tls = require('tls');
- var os = require('os');
- var crypto = require('crypto');
- var iconv = require('iconv-lite');
- var WebSocket = require('ws');
-
- function debug(e) {
- if (e.stack) {
- util.debug(e + '\n' + e.stack);
- }
- else
- util.debug(util.inspect(e));
- }
-
- function toBase64(s) {
- switch (s.length % 3) {
- case 1:
- s += ' ';
- break;
- case 2:
- s += ' ';
- break;
- default:
- }
-
- return (new Buffer.from(s)).toString('base64').replace(/\//g, '_').replace(/\+/g, '-');
- }
- // Actual stack code begins here
-
- function parseResponse(rs, m) {
- var r = rs.match(/^SIP\/(\d+\.\d+)\s+(\d+)\s*(.*)\s*$/);
-
- if (r) {
- m.version = r[1];
- m.status = +r[2];
- m.reason = r[3];
-
- return m;
- }
- }
-
- function parseRequest(rq, m) {
- var r = rq.match(/^([\w\-.!%*_+`'~]+)\s([^\s]+)\sSIP\s*\/\s*(\d+\.\d+)/);
-
- if (r) {
- m.method = unescape(r[1]);
- m.uri = r[2];
- m.version = r[3];
-
- return m;
- }
- }
-
- function applyRegex(regex, data) {
- regex.lastIndex = data.i;
- var r = regex.exec(data.s);
-
- if (r && (r.index === data.i)) {
- data.i = regex.lastIndex;
- return r;
- }
- }
-
- function parseParams(data, hdr) {
- hdr.params = hdr.params || {};
-
- var re = /\s*;\s*([\w\-.!%*_+`'~]+)(?:\s*=\s*([\w\-.!%*_+`'~]+|"[^"\\]*(\\.[^"\\]*)*"))?/g;
-
- for (var r = applyRegex(re, data); r; r = applyRegex(re, data)) {
- hdr.params[r[1].toLowerCase()] = r[2] || null;
- }
-
- return hdr;
- }
-
- function parseMultiHeader(parser, d, h) {
- h = h || [];
-
- var re = /\s*,\s*/g;
- do {
- h.push(parser(d));
- } while (d.i < d.s.length && applyRegex(re, d));
-
- return h;
- }
-
- function parseGenericHeader(d, h) {
- return h ? h + ',' + d.s : d.s;
- }
-
- function parseAOR(data) {
- var r = applyRegex(/((?:[\w\-.!%*_+`'~]+)(?:\s+[\w\-.!%*_+`'~]+)*|"[^"\\]*(?:\\.[^"\\]*)*")?\s*\<\s*([^>]*)\s*\>|((?:[^\s@"<]@)?[^\s;]+)/g, data);
-
- return parseParams(data, { name: r[1], uri: r[2] || r[3] || '' });
- }
- exports.parseAOR = parseAOR;
-
- function parseAorWithUri(data) {
- var r = parseAOR(data);
- r.uri = parseUri(r.uri);
- return r;
- }
-
- function parseVia(data) {
- var r = applyRegex(/SIP\s*\/\s*(\d+\.\d+)\s*\/\s*([\S]+)\s+([^\s;:]+)(?:\s*:\s*(\d+))?/g, data);
- return parseParams(data, { version: r[1], protocol: r[2], host: r[3], port: r[4] && +r[4] });
- }
-
- function parseCSeq(d) {
- var r = /(\d+)\s*([\S]+)/.exec(d.s);
- return { seq: +r[1], method: unescape(r[2]) };
- }
-
- function parseAuthHeader(d) {
- var r1 = applyRegex(/([^\s]*)\s+/g, d);
- var a = { scheme: r1[1] };
-
- var r2 = applyRegex(/([^\s,"=]*)\s*=\s*([^\s,"]+|"[^"\\]*(?:\\.[^"\\]*)*")\s*/g, d);
- a[r2[1]] = r2[2];
-
- while (r2 = applyRegex(/,\s*([^\s,"=]*)\s*=\s*([^\s,"]+|"[^"\\]*(?:\\.[^"\\]*)*")\s*/g, d)) {
- a[r2[1]] = r2[2];
- }
-
- return a;
- }
-
- function parseAuthenticationInfoHeader(d) {
- var a = {};
- var r = applyRegex(/([^\s,"=]*)\s*=\s*([^\s,"]+|"[^"\\]*(?:\\.[^"\\]*)*")\s*/g, d);
- a[r[1]] = r[2];
-
- while (r = applyRegex(/,\s*([^\s,"=]*)\s*=\s*([^\s,"]+|"[^"\\]*(?:\\.[^"\\]*)*")\s*/g, d)) {
- a[r[1]] = r[2];
- }
- return a;
- }
-
- var compactForm = {
- i: 'call-id',
- m: 'contact',
- e: 'contact-encoding',
- l: 'content-length',
- c: 'content-type',
- f: 'from',
- s: 'subject',
- k: 'supported',
- t: 'to',
- v: 'via'
- };
-
- var parsers = {
- 'to': parseAOR,
- 'from': parseAOR,
- 'contact': function (v, h) {
- if (v == '*')
- return v;
- else
- return parseMultiHeader(parseAOR, v, h);
- },
- 'route': parseMultiHeader.bind(0, parseAorWithUri),
- 'record-route': parseMultiHeader.bind(0, parseAorWithUri),
- 'path': parseMultiHeader.bind(0, parseAorWithUri),
- 'cseq': parseCSeq,
- 'content-length': function (v) { return +v.s; },
- 'via': parseMultiHeader.bind(0, parseVia),
- 'www-authenticate': parseMultiHeader.bind(0, parseAuthHeader),
- 'proxy-authenticate': parseMultiHeader.bind(0, parseAuthHeader),
- 'authorization': parseMultiHeader.bind(0, parseAuthHeader),
- 'proxy-authorization': parseMultiHeader.bind(0, parseAuthHeader),
- 'authentication-info': parseAuthenticationInfoHeader,
- 'refer-to': parseAOR
- };
-
- function parse(data) {
- data = data.split(/\r\n(?![ \t])/);
-
- if (data[0] === '')
- return;
-
- var m = {};
-
- if (!(parseResponse(data[0], m) || parseRequest(data[0], m)))
- return;
-
- m.headers = {};
-
- for (var i = 1; i < data.length; ++i) {
- var r = data[i].match(/^([\S]*?)\s*:\s*([\s\S]*)$/);
- if (!r) {
- return;
- }
-
- var name = unescape(r[1]).toLowerCase();
- name = compactForm[name] || name;
-
- m.headers[name] = (parsers[name] || parseGenericHeader)({ s: r[2], i: 0 }, m.headers[name]);
- }
-
- return m;
- }
-
- function parseUri(s) {
- if (typeof s === 'object')
- return s;
-
- var re = /^(sips?):(?:([^\s>:@]+)(?::([^\s@>]+))?@)?([\w\-\.]+)(?::(\d+))?((?:;[^\s=\?>;]+(?:=[^\s?\;]+)?)*)(?:\?(([^\s&=>]+=[^\s&=>]+)(&[^\s&=>]+=[^\s&=>]+)*))?$/;
-
- var r = re.exec(s);
-
- if (r) {
-
- return {
- schema: r[1],
- user: r[2],
- password: r[3],
- host: r[4],
- port: +r[5],
- params: (r[6].match(/([^;=]+)(=([^;=]+))?/g) || [])
- .map(function (s) { return s.split('='); })
- .reduce(function (params, x) { params[x[0]] = x[1] || null; return params; }, {}),
- headers: ((r[7] || '').match(/[^&=]+=[^&=]+/g) || [])
- .map(function (s) { return s.split('=') })
- .reduce(function (params, x) { params[x[0]] = x[1]; return params; }, {})
- }
- }
- }
-
- exports.parseUri = parseUri;
-
- function stringifyVersion(v) {
- return v || '2.0';
- }
-
- function stringifyParams(params) {
- var s = '';
- for (var n in params) {
- s += ';' + n + (params[n] ? '=' + params[n] : '');
- }
-
- return s;
- }
-
- function stringifyUri(uri) {
- if (typeof uri === 'string')
- return uri;
-
- var s = (uri.schema || 'sip') + ':';
-
- if (uri.user) {
- if (uri.password)
- s += uri.user + ':' + uri.password + '@';
- else
- s += uri.user + '@';
- }
-
- s += uri.host;
-
- if (uri.port)
- s += ':' + uri.port;
-
- if (uri.params)
- s += stringifyParams(uri.params);
-
- if (uri.headers) {
- var h = Object.keys(uri.headers).map(function (x) { return x + '=' + uri.headers[x]; }).join('&');
- if (h.length)
- s += '?' + h;
- }
- return s;
- }
-
- exports.stringifyUri = stringifyUri;
-
- function stringifyAOR(aor) {
- return (aor.name || '') + ' <' + stringifyUri(aor.uri) + '>' + stringifyParams(aor.params);
- }
-
- function stringifyAuthHeader(a) {
- var s = [];
-
- for (var n in a) {
- if (n !== 'scheme' && a[n] !== undefined) {
- s.push(n + '=' + a[n]);
- }
- }
-
- return a.scheme ? a.scheme + ' ' + s.join(',') : s.join(',');
- }
-
- exports.stringifyAuthHeader = stringifyAuthHeader;
-
- var stringifiers = {
- via: function (h) {
- return h.map(function (via) {
- if (via.host) {
- return 'Via: SIP/' + stringifyVersion(via.version) + '/' + via.protocol.toUpperCase() + ' ' + via.host + (via.port ? ':' + via.port : '') + stringifyParams(via.params) + '\r\n';
- }
- else {
- return '';
- }
- }).join('');
- },
- to: function (h) {
- return 'To: ' + stringifyAOR(h) + '\r\n';
- },
- from: function (h) {
- return 'From: ' + stringifyAOR(h) + '\r\n';
- },
- contact: function (h) {
- return 'Contact: ' + ((h !== '*' && h.length) ? h.map(stringifyAOR).join(', ') : '*') + '\r\n';
- },
- route: function (h) {
- return h.length ? 'Route: ' + h.map(stringifyAOR).join(', ') + '\r\n' : '';
- },
- 'record-route': function (h) {
- return h.length ? 'Record-Route: ' + h.map(stringifyAOR).join(', ') + '\r\n' : '';
- },
- 'path': function (h) {
- return h.length ? 'Path: ' + h.map(stringifyAOR).join(', ') + '\r\n' : '';
- },
- cseq: function (cseq) {
- return 'CSeq: ' + cseq.seq + ' ' + cseq.method + '\r\n';
- },
- 'www-authenticate': function (h) {
- return h.map(function (x) { return 'WWW-Authenticate: ' + stringifyAuthHeader(x) + '\r\n'; }).join('');
- },
- 'proxy-authenticate': function (h) {
- return h.map(function (x) { return 'Proxy-Authenticate: ' + stringifyAuthHeader(x) + '\r\n'; }).join('');
- },
- 'authorization': function (h) {
- return h.map(function (x) { return 'Authorization: ' + stringifyAuthHeader(x) + '\r\n' }).join('');
- },
- 'proxy-authorization': function (h) {
- return h.map(function (x) { return 'Proxy-Authorization: ' + stringifyAuthHeader(x) + '\r\n' }).join('');;
- },
- 'authentication-info': function (h) {
- return 'Authentication-Info: ' + stringifyAuthHeader(h) + '\r\n';
- },
- 'refer-to': function (h) { return 'Refer-To: ' + stringifyAOR(h) + '\r\n'; }
- };
-
- function prettifyHeaderName(s) {
- if (s == 'call-id') return 'Call-ID';
-
- return s.replace(/\b([a-z])/g, function (a) { return a.toUpperCase(); });
- }
-
- function stringify(m) {
- var s;
- if (m.status) {
- s = 'SIP/' + stringifyVersion(m.version) + ' ' + m.status + ' ' + m.reason + '\r\n';
- }
- else {
- s = m.method + ' ' + stringifyUri(m.uri) + ' SIP/' + stringifyVersion(m.version) + '\r\n';
- }
-
- m.headers['content-length'] = (m.content || '').length;
-
- for (var n in m.headers) {
- if (typeof m.headers[n] !== "undefined") {
- if (typeof m.headers[n] === 'string' || !stringifiers[n])
- s += prettifyHeaderName(n) + ': ' + m.headers[n] + '\r\n';
- else
- s += stringifiers[n](m.headers[n], n);
- }
- }
-
- s += '\r\n';
-
- if (m.content)
- s += m.content;
-
- return s;
- }
-
- exports.stringify = stringify;
-
- function makeResponse(rq, status, reason, extension) {
- var rs = {
- status: status,
- reason: reason || '',
- version: rq.version,
- headers: {
- via: rq.headers.via,
- to: rq.headers.to,
- from: rq.headers.from,
- 'call-id': rq.headers['call-id'],
- cseq: rq.headers.cseq
- }
- };
-
- if (extension) {
- if (extension.headers) Object.keys(extension.headers).forEach(function (h) { rs.headers[h] = extension.headers[h]; });
- rs.content = extension.content;
- }
-
- return rs;
- }
-
- exports.makeResponse = makeResponse;
-
- function clone(o, deep) {
- if (typeof o === 'object') {
- var r = Array.isArray(o) ? [] : {};
- Object.keys(o).forEach(function (k) { r[k] = deep ? clone(o[k], deep) : o[k]; });
- return r;
- }
-
- return o;
- }
-
- exports.copyMessage = function (msg, deep) {
- if (deep) return clone(msg, true);
-
- var r = {
- uri: deep ? clone(msg.uri, deep) : msg.uri,
- method: msg.method,
- status: msg.status,
- reason: msg.reason,
- headers: clone(msg.headers, deep),
- content: msg.content
- };
-
- // always copy via array
- r.headers.via = clone(msg.headers.via);
-
- return r;
- }
-
- function defaultPort(proto) {
- return proto.toUpperCase() === 'TLS' ? 5061 : 5060;
- }
-
- //转文本
- function makeStreamParser(onMessage, onFlood, maxBytesHeaders, maxContentLength) {
-
- onFlood = onFlood || function () { };
- maxBytesHeaders = maxBytesHeaders || 60480;
- maxContentLength = maxContentLength || 604800;
-
- var m;
- var r = '';
-
- function headers(data) {
- r += data;
-
- if (r.length > maxBytesHeaders) {
-
- r = '';
-
- onFlood();
-
- return;
-
- }
-
- //GB2312
- var a = iconv.decode(Buffer.from(r, 'binary'), 'gb2312').match(/^\s*([\S\s]*?)\r\n\r\n([\S\s]*)$/);
-
- if (a) {
- r = a[2];
- m = parse(a[1]);
-
- if (m && m.headers['content-length'] !== undefined) {
-
- if (m.headers['content-length'] > maxContentLength) {
-
- r = '';
-
- onFlood();
- }
-
- state = content;
- content('');
- }
- else
- headers('');
- }
- }
-
- function content(data) {
- r += data;
-
- if (r.length >= m.headers['content-length']) {
- m.content = r.substring(0, m.headers['content-length']);
-
- onMessage(m);
-
- var s = r.substring(m.headers['content-length']);
- state = headers;
- r = '';
- headers(s);
- }
- }
-
- var state = headers;
-
- return function (data) { state(data); }
-
- }
- exports.makeStreamParser = makeStreamParser;
-
- function parseMessage(s) {
-
- //使用GB2312编码
- var r = iconv.decode(s, 'gb2312').match(/^\s*([\S\s]*?)\r\n\r\n([\S\s]*)$/);
- if (r) {
- var m = parse(r[1]);
-
- if (m) {
- if (m.headers['content-length']) {
- var c = Math.max(0, Math.min(m.headers['content-length'], r[2].length));
- m.content = r[2].substring(0, c);
- }
- else {
- m.content = r[2];
- }
-
- return m;
- }
- }
- }
- exports.parse = parseMessage;
-
- function checkMessage(msg) {
- return (msg.method || (msg.status >= 100 && msg.status <= 999)) &&
- msg.headers &&
- Array.isArray(msg.headers.via) &&
- msg.headers.via.length > 0 &&
- msg.headers['call-id'] &&
- msg.headers.to &&
- msg.headers.from &&
- msg.headers.cseq;
- }
-
- function makeStreamTransport(protocol, maxBytesHeaders, maxContentLength, connect, createServer, callback) {
- var remotes = Object.create(null);
- var flows = Object.create(null);
-
- function init(stream, remote) {
- var remoteid = [remote.address, remote.port].join(),
- flowid = undefined,
- refs = 0;
-
- function register_flow() {
- flowid = [remoteid, stream.localAddress, stream.localPort].join();
- flows[flowid] = remotes[remoteid];
- }
-
- var onMessage = function (m) {
-
- if (checkMessage(m)) {
- if (m.method) m.headers.via[0].params.received = remote.address;
- callback(m, { protocol: remote.protocol, address: stream.remoteAddress, port: stream.remotePort, local: { address: stream.localAddress, port: stream.localPort } }, stream);
- }
- };
-
- var onFlood = function () {
-
- console.log("Flood attempt, destroying stream");
-
- stream.destroy();
- };
-
- stream.setEncoding('binary');
- stream.on('data', makeStreamParser(onMessage, onFlood, maxBytesHeaders, maxContentLength));
-
- stream.on('close', function () {
- if (flowid) delete flows[flowid];
- delete remotes[remoteid];
- });
- stream.on('connect', register_flow);
-
- stream.on('error', function () { });
- stream.on('end', function () {
- if (refs !== 0) stream.emit('error', new Error('remote peer disconnected'));
- stream.end();
- });
-
- stream.on('timeout', function () { if (refs === 0) stream.destroy(); });
- stream.setTimeout(120000);
- stream.setMaxListeners(10000);
-
- remotes[remoteid] = function (onError) {
- ++refs;
- if (onError) stream.on('error', onError);
-
- return {
- release: function () {
- if (onError) stream.removeListener('error', onError);
- if (--refs === 0) stream.emit('no_reference');
- },
- send: function (m) {
- stream.write(stringify(m), 'binary');
- },
- protocol: protocol
- }
- };
-
- if (stream.localPort) register_flow();
-
- return remotes[remoteid];
- }
-
- var server = createServer(function (stream) {
- init(stream, { protocol: protocol, address: stream.remoteAddress, port: stream.remotePort });
- });
-
- return {
- open: function (remote, error) {
- var remoteid = [remote.address, remote.port].join();
-
- if (remoteid in remotes) return remotes[remoteid](error);
-
- return init(connect(remote.port, remote.address), remote)(error);
- },
- get: function (address, error) {
- var c = address.local ? flows[[address.address, address.port, address.local.address, address.local.port].join()]
- : remotes[[address.address, address.port].join()];
-
- return c && c(error);
- },
- destroy: function () { server.close(); }
- };
- }
-
- function makeTlsTransport(options, callback) {
- return makeStreamTransport(
- 'TLS',
- options.maxBytesHeaders,
- options.maxContentLength,
- function (port, host, callback) { return tls.connect(port, host, options.tls, callback); },
- function (callback) {
- var server = tls.createServer(options.tls, callback);
- server.listen(options.tls_port || 5061, options.address);
- return server;
- },
- callback);
- }
-
- function makeTcpTransport(options, callback) {
- return makeStreamTransport(
- 'TCP',
- options.maxBytesHeaders,
- options.maxContentLength,
- function (port, host, callback) { return net.connect(port, host, callback); },
- function (callback) {
- var server = net.createServer(callback);
- server.listen(options.port || 5060, options.address);
- return server;
- },
- callback);
- }
-
- function makeWsTransport(options, callback) {
- var flows = Object.create(null);
- var clients = Object.create(null);
-
-
- function init(ws) {
- var remote = { address: ws._socket.remoteAddress, port: ws._socket.remotePort },
- local = { address: ws._socket.address().address, port: ws._socket.address().port },
- flowid = [remote.address, remote.port, local.address, local.port].join();
-
- flows[flowid] = ws;
-
- ws.on('close', function () { delete flows[flowid]; });
- ws.on('message', function (data) {
- var msg = parseMessage(data);
- if (msg) {
- callback(msg, { protocol: 'WS', address: remote.address, port: remote.port, local: local });
- }
- });
- }
-
- function makeClient(uri) {
- if (clients[uri]) return clients[uri]();
-
- var socket = new WebSocket(uri, 'sip', { procotol: 'sip' }),
- queue = [],
- refs = 0;
-
- function send_connecting(m) { queue.push(stringify(m)); }
- function send_open(m) { socket.send(new Buffer.from(typeof m === 'string' ? m : stringify(m), 'binary')); }
- var send = send_connecting;
-
- socket.on('open', function () {
- init(socket);
- send = send_open;
- queue.splice(0).forEach(send);
- });
-
- function open(onError) {
- ++refs;
- if (onError) socket.on('error', onError);
- return {
- send: function (m) { send(m); },
- release: function () {
- if (onError) socket.removeListener('error', onError);
- if (--refs === 0) socket.terminate();
- },
- protocol: 'WS'
- };
- };
-
- return clients[uri] = open;
- }
-
- if (options.ws_port) {
- if (options.tls) {
- var http = require('https');
- var server = new WebSocket.Server({
- server: http.createServer(options.tls, function (rq, rs) {
- rs.writeHead(200);
- rs.end("");
- }).listen(options.ws_port)
- });
- }
- else {
- var server = new WebSocket.Server({ port: options.ws_port });
- }
-
- server.on('connection', init);
- }
-
- function get(flow) {
- var ws = flows[[flow.address, flow.port, flow.local.address, flow.local.port].join()];
- if (ws) {
- return {
- send: function (m) { ws.send(stringify(m)); },
- release: function () { },
- protocol: 'WS'
- };
- } else {
- console.log("Failed to get ws for target. Target/flow was:");
- console.log(util.inspect(flow));
- console.log("Flows[] were:");
- console.log(util.inspect(flows));
- }
- }
-
- function open(target, onError) {
- if (target.local)
- return get(target);
- else
- return makeClient('ws://' + target.host + ':' + target.port)(onError);
- }
-
- return {
- get: open,
- open: open,
- destroy: function () { server.close(); }
- }
- }
-
- function makeUdpTransport(options, callback) {
- function onMessage(data, rinfo) {
- var msg = parseMessage(data);
-
- if (msg && checkMessage(msg)) {
- if (msg.method) {
- msg.headers.via[0].params.received = rinfo.address;
- if (msg.headers.via[0].params.hasOwnProperty('rport'))
- msg.headers.via[0].params.rport = rinfo.port;
- }
-
- callback(msg, { protocol: 'UDP', address: rinfo.address, port: rinfo.port, local: { address: address, port: port } });
- }
- }
-
- var address = options.address || '0.0.0.0';
- var port = options.port || 5060;
-
- var socket = dgram.createSocket(net.isIPv6(address) ? 'udp6' : 'udp4', onMessage);
- socket.bind(port, address);
-
- function open(remote, error) {
- return {
- send: function (m) {
- var s = stringify(m);
- socket.send(new Buffer.from(s, 'binary'), 0, s.length, remote.port, remote.address);
- },
- protocol: 'UDP',
- release: function () { }
- };
- };
-
- return {
- open: open,
- get: open,
- destroy: function () { socket.close(); }
- }
- }
-
- function makeTransport(options, callback) {
- var protocols = {};
-
- var callbackAndLog = callback;
- if (options.logger && options.logger.recv) {
- callbackAndLog = function (m, remote, stream) {
- options.logger.recv(m, remote);
- callback(m, remote, stream);
- }
- }
-
- if (options.udp === undefined || options.udp)
- protocols.UDP = makeUdpTransport(options, callbackAndLog);
- if (options.tcp === undefined || options.tcp)
- protocols.TCP = makeTcpTransport(options, callbackAndLog);
- if (options.tls)
- protocols.TLS = makeTlsTransport(options, callbackAndLog);
- if (options.ws_port && WebSocket)
- protocols.WS = makeWsTransport(options, callbackAndLog);
-
- function wrap(obj, target) {
- return Object.create(obj, {
- send: {
- value: function (m) {
- if (m.method) {
- m.headers.via[0].host = options.publicAddress || options.address || options.hostname || os.hostname();
- m.headers.via[0].port = options.port || defaultPort(this.protocol);
- m.headers.via[0].protocol = this.protocol;
-
- if (this.protocol === 'UDP' && (!options.hasOwnProperty('rport') || options.rport)) {
- m.headers.via[0].params.rport = null;
- }
- }
- options.logger && options.logger.send && options.logger.send(m, target);
- obj.send(m);
- }
- }
- });
- }
-
- return {
- open: function (target, error) {
- return wrap(protocols[target.protocol.toUpperCase()].open(target, error), target);
- },
- get: function (target, error) {
- var flow = protocols[target.protocol.toUpperCase()].get(target, error);
- return flow && wrap(flow, target);
- },
- send: function (target, message) {
- var cn = this.open(target);
- try {
- cn.send(message);
- }
- finally {
- cn.release();
- }
- },
- destroy: function () {
- var protos = protocols;
- protocols = [];
- Object.keys(protos).forEach(function (key) { protos[key].destroy(); });
- },
- };
- }
-
- exports.makeTransport = makeTransport;
-
- function makeWellBehavingResolver(resolve) {
- var outstanding = Object.create(null);
-
- return function (name, cb) {
- if (outstanding[name]) {
- outstanding[name].push(cb);
- }
- else {
- outstanding[name] = [cb];
-
- resolve(name, function () {
- var o = outstanding[name];
- delete outstanding[name];
- var args = arguments;
- o.forEach(function (x) { x.apply(null, args); });
- });
- }
- };
- };
-
- var resolveSrv = makeWellBehavingResolver(dns.resolveSrv);
- var resolve4 = makeWellBehavingResolver(dns.resolve4);
- var resolve6 = makeWellBehavingResolver(dns.resolve6);
-
- function resolve(uri, action) {
- if (uri.params.transport === 'ws')
- return action([{ protocol: uri.schema === 'sips' ? 'WSS' : 'WS', host: uri.host, port: uri.port || (uri.schema === 'sips' ? 433 : 80) }]);
-
- if (net.isIP(uri.host)) {
- var protocol = uri.params.transport || 'UDP';
- return action([{ protocol: protocol, address: uri.host, port: uri.port || defaultPort(protocol) }]);
- }
-
- function resolve46(host, cb) {
- resolve4(host, function (e4, a4) {
- resolve6(host, function (e6, a6) {
- if ((a4 || a6) && (a4 || a6).length)
- cb(null, (a4 || []).concat(a6 || []));
- else
- cb(e4 || e6, []);
- });
- });
- }
-
- if (uri.port) {
- var protocols = uri.params.transport ? [uri.params.transport] : ['UDP', 'TCP', 'TLS'];
-
- resolve46(uri.host, function (err, address) {
- address = (address || []).map(function (x) { return protocols.map(function (p) { return { protocol: p, address: x, port: uri.port || defaultPort(p) }; }); })
- .reduce(function (arr, v) { return arr.concat(v); }, []);
- action(address);
- });
- }
- else {
- var protocols = uri.params.transport ? [uri.params.transport] : ['tcp', 'udp', 'tls'];
-
- var n = protocols.length;
- var addresses = [];
-
- protocols.forEach(function (proto) {
- resolveSrv('_sip._' + proto + '.' + uri.host, function (e, r) {
- --n;
-
- if (Array.isArray(r)) {
- n += r.length;
- r.forEach(function (srv) {
- resolve46(srv.name, function (e, r) {
- addresses = addresses.concat((r || []).map(function (a) { return { protocol: proto, address: a, port: srv.port }; }));
-
- if ((--n) === 0) // all outstanding requests has completed
- action(addresses);
- });
- });
- }
- else if (0 === n) {
- if (addresses.length) {
- action(addresses);
- }
- else {
- // all srv requests failed
- resolve46(uri.host, function (err, address) {
- address = (address || []).map(function (x) { return protocols.map(function (p) { return { protocol: p, address: x, port: uri.port || defaultPort(p) }; }); })
- .reduce(function (arr, v) { return arr.concat(v); }, []);
- action(address);
- });
- }
- }
- })
- });
- }
- }
-
- exports.resolve = resolve;
-
- //transaction layer
- function generateBranch() {
- return ['z9hG4bK', Math.round(Math.random() * 1000000)].join('');
- }
-
- exports.generateBranch = generateBranch;
-
- function makeSM() {
- var state;
-
- return {
- enter: function (newstate) {
- if (state && state.leave)
- state.leave();
-
- state = newstate;
- Array.prototype.shift.apply(arguments);
- if (state.enter)
- state.enter.apply(this, arguments);
- },
- signal: function (s) {
- if (state && state[s])
- state[Array.prototype.shift.apply(arguments)].apply(state, arguments);
- }
- };
- }
-
- function createInviteServerTransaction(transport, cleanup) {
- var sm = makeSM();
- var rs;
-
- var proceeding = {
- message: function () {
- if (rs) transport(rs);
- },
- send: function (message) {
- rs = message;
-
- if (message.status >= 300)
- sm.enter(completed);
- else if (message.status >= 200)
- sm.enter(accepted);
-
- transport(rs);
- }
- }
-
- var g, h;
- var completed = {
- enter: function () {
- g = setTimeout(function retry(t) {
- g = setTimeout(retry, t * 2, t * 2);
- transport(rs)
- }, 500, 500);
- h = setTimeout(sm.enter.bind(sm, terminated), 32000);
- },
- leave: function () {
- clearTimeout(g);
- clearTimeout(h);
- },
- message: function (m) {
- if (m.method === 'ACK')
- sm.enter(confirmed)
- else
- transport(rs);
- }
- }
-
- var timer_i;
- var confirmed = {
- enter: function () { timer_i = setTimeout(sm.enter.bind(sm, terminated), 5000); },
- leave: function () { clearTimeout(timer_i); }
- };
-
- var l;
- var accepted = {
- enter: function () { l = setTimeout(sm.enter.bind(sm, terminated), 32000); },
- leave: function () { clearTimeout(l); },
- send: function (m) {
- rs = m;
- transport(rs);
- }
- };
-
- var terminated = { enter: cleanup };
-
- sm.enter(proceeding);
-
- return { send: sm.signal.bind(sm, 'send'), message: sm.signal.bind(sm, 'message'), shutdown: function () { sm.enter(terminated); } };
- }
-
- function createServerTransaction(transport, cleanup) {
- var sm = makeSM();
- var rs;
-
- var trying = {
- message: function () { if (rs) transport(rs); },
- send: function (m) {
- rs = m;
- transport(m);
- if (m.status >= 200) sm.enter(completed);
- }
- };
-
- var j;
- var completed = {
- message: function () { transport(rs); },
- enter: function () { j = setTimeout(function () { sm.enter(terminated); }, 32000); },
- leave: function () { clearTimeout(j); }
- };
-
- var terminated = { enter: cleanup };
-
- sm.enter(trying);
-
- return { send: sm.signal.bind(sm, 'send'), message: sm.signal.bind(sm, 'message'), shutdown: function () { sm.enter(terminated); } };
- }
-
- function createInviteClientTransaction(rq, transport, tu, cleanup, options) {
- var sm = makeSM();
-
- var a, b;
- var calling = {
- enter: function () {
- transport(rq);
-
- if (!transport.reliable) {
- a = setTimeout(function resend(t) {
- transport(rq);
- a = setTimeout(resend, t * 2, t * 2);
- }, 500, 500);
- }
-
- b = setTimeout(function () {
- tu(makeResponse(rq, 408));
- sm.enter(terminated);
- }, 32000);
- },
- leave: function () {
- clearTimeout(a);
- clearTimeout(b);
- },
- message: function (message) {
- tu(message);
-
- if (message.status < 200)
- sm.enter(proceeding);
- else if (message.status < 300)
- sm.enter(accepted);
- else
- sm.enter(completed, message);
- }
- };
-
- var proceeding = {
- message: function (message) {
- tu(message);
-
- if (message.status >= 300)
- sm.enter(completed, message);
- else if (message.status >= 200)
- sm.enter(accepted);
- }
- };
-
- var ack = {
- method: 'ACK',
- uri: rq.uri,
- headers: {
- from: rq.headers.from,
- cseq: { method: 'ACK', seq: rq.headers.cseq.seq },
- 'call-id': rq.headers['call-id'],
- via: [rq.headers.via[0]],
- 'max-forwards': (options && options['max-forwards']) || 70
- }
- };
-
- var d;
- var completed = {
- enter: function (rs) {
- ack.headers.to = rs.headers.to;
- transport(ack);
- d = setTimeout(sm.enter.bind(sm, terminated), 32000);
- },
- leave: function () { clearTimeout(d); },
- message: function (message, remote) {
- if (remote) transport(ack); // we don't want to ack internally generated messages
- }
- };
-
- var timer_m;
- var accepted = {
- enter: function () {
- timer_m = setTimeout(function () { sm.enter(terminated); }, 32000);
- },
- leave: function () { clearTimeout(timer_m); },
- message: function (m) {
- if (m.status >= 200 && m.status <= 299)
- tu(m);
- }
- };
-
- var terminated = { enter: cleanup };
-
- process.nextTick(function () { sm.enter(calling); });
-
- return { message: sm.signal.bind(sm, 'message'), shutdown: function () { sm.enter(terminated); } };
- }
-
- function createClientTransaction(rq, transport, tu, cleanup) {
- assert.ok(rq.method !== 'INVITE');
-
- var sm = makeSM();
-
- var e, f;
- var trying = {
- enter: function () {
- transport(rq);
- if (!transport.reliable)
- e = setTimeout(function () { sm.signal('timerE', 500); }, 500);
- f = setTimeout(function () { sm.signal('timerF'); }, 32000);
- },
- leave: function () {
- clearTimeout(e);
- clearTimeout(f);
- },
- message: function (message, remote) {
- if (message.status >= 200)
- sm.enter(completed);
- else
- sm.enter(proceeding);
- tu(message);
- },
- timerE: function (t) {
- transport(rq);
- e = setTimeout(function () { sm.signal('timerE', t * 2); }, t * 2);
- },
- timerF: function () {
- tu(makeResponse(rq, 408));
- sm.enter(terminated);
- }
- };
-
- var proceeding = {
- message: function (message, remote) {
- if (message.status >= 200)
- sm.enter(completed);
- tu(message);
- }
- };
-
- var k;
- var completed = {
- enter: function () { k = setTimeout(function () { sm.enter(terminated); }, 5000); },
- leave: function () { clearTimeout(k); }
- };
-
- var terminated = { enter: cleanup };
-
- process.nextTick(function () { sm.enter(trying); });
-
- return { message: sm.signal.bind(sm, 'message'), shutdown: function () { sm.enter(terminated); } };
- }
-
- function makeTransactionId(m) {
- if (m.method === 'ACK')
- return ['INVITE', m.headers['call-id'], m.headers.via[0].params.branch].join();
- return [m.headers.cseq.method, m.headers['call-id'], m.headers.via[0].params.branch].join();
- }
-
- function makeTransactionLayer(options, transport) {
- var server_transactions = Object.create(null);
- var client_transactions = Object.create(null);
-
- return {
- createServerTransaction: function (rq, cn) {
- var id = makeTransactionId(rq);
-
- return server_transactions[id] = (rq.method === 'INVITE' ? createInviteServerTransaction : createServerTransaction)(
- cn.send.bind(cn),
- function () {
- delete server_transactions[id];
- cn.release();
- });
- },
- createClientTransaction: function (connection, rq, callback) {
- if (rq.method !== 'CANCEL') rq.headers.via[0].params.branch = generateBranch();
-
-
- if (typeof rq.headers.cseq !== 'object')
- rq.headers.cseq = parseCSeq({ s: rq.headers.cseq, i: 0 });
-
- var send = connection.send.bind(connection);
- send.reliable = connection.protocol.toUpperCase() !== 'UDP';
-
- var id = makeTransactionId(rq);
- return client_transactions[id] =
- (rq.method === 'INVITE' ? createInviteClientTransaction : createClientTransaction)(rq, send, callback, function () {
- delete client_transactions[id];
- connection.release();
- },
- options);
- },
- getServer: function (m) {
- return server_transactions[makeTransactionId(m)];
- },
- getClient: function (m) {
- return client_transactions[makeTransactionId(m)];
- },
- destroy: function () {
- Object.keys(client_transactions).forEach(function (x) { client_transactions[x].shutdown(); });
- Object.keys(server_transactions).forEach(function (x) { server_transactions[x].shutdown(); });
- }
- };
- }
-
- exports.makeTransactionLayer = makeTransactionLayer;
-
- function sequentialSearch(transaction, connect, addresses, rq, callback) {
- if (rq.method !== 'CANCEL') {
- if (!rq.headers.via) rq.headers.via = [];
- rq.headers.via.unshift({ params: {} });
- }
-
- var onresponse;
- var lastStatusCode;
- function next() {
- onresponse = searching;
-
- if (addresses.length > 0) {
- try {
- var address = addresses.shift();
- var client = transaction(connect(address, function (err) {
- if (err) {
- console.log("err: ", err);
- }
- client.message(makeResponse(rq, 503));
- }), rq, function () { onresponse.apply(null, arguments); });
- }
- catch (e) {
- onresponse(address.local ? makeResponse(rq, 430) : makeResponse(rq, 503));
- }
- }
- else {
- onresponse = callback;
- onresponse(makeResponse(rq, lastStatusCode || 404));
- }
- }
-
- function searching(rs) {
- lastStatusCode = rs.status;
- if (rs.status === 503)
- return next();
- else if (rs.status > 100)
- onresponse = callback;
-
- callback(rs);
- }
-
- next();
- }
-
- exports.create = function (options, callback) {
- var errorLog = (options.logger && options.logger.error) || function () { };
-
- var transport = makeTransport(options, function (m, remote) {
- try {
- var t = m.method ? transaction.getServer(m) : transaction.getClient(m);
-
- if (!t) {
- if (m.method && m.method !== 'ACK') {
- var t = transaction.createServerTransaction(m, transport.get(remote));
- try {
- callback(m, remote);
- } catch (e) {
- t.send(makeResponse(m, '500', 'Internal Server Error'));
- throw e;
- }
- }
- else if (m.method === 'ACK') {
- callback(m, remote);
- }
- }
- else {
- t.message && t.message(m, remote);
- }
- }
- catch (e) {
- errorLog(e);
- }
- });
-
- var transaction = makeTransactionLayer(options, transport.open.bind(transport));
- var hostname = options.publicAddress || options.address || options.hostname || os.hostname();
- var rbytes = crypto.randomBytes(20);
-
- function encodeFlowToken(flow) {
- var s = [flow.protocol, flow.address, flow.port, flow.local.address, flow.local.port].join();
- var h = crypto.createHmac('sha1', rbytes);
- h.update(s);
- return toBase64([h.digest('base64'), s].join());
- }
-
- function decodeFlowToken(token) {
- var s = (new Buffer.from(token, 'base64')).toString('ascii').split(',');
- if (s.length != 6) return;
-
- var flow = { protocol: s[1], address: s[2], port: +s[3], local: { address: s[4], port: +s[5] } };
-
- return encodeFlowToken(flow) == token ? flow : undefined;
- }
-
- return {
- send: function (m, callback) {
- if (m.method === undefined) {
- var t = transaction.getServer(m);
- t && t.send && t.send(m);
- }
- else {
- var hop = parseUri(m.uri);
-
- if (typeof m.headers.route === 'string')
- m.headers.route = parsers.route({ s: m.headers.route, i: 0 });
-
- if (m.headers.route && m.headers.route.length > 0) {
- hop = parseUri(m.headers.route[0].uri);
- if (hop.host === hostname) {
- m.headers.route.shift();
- }
- else if (hop.params.lr === undefined) {
- m.headers.route.shift();
- m.headers.route.push({ uri: m.uri });
- m.uri = hop;
- }
- }
-
- (function (callback) {
- if (hop.host === hostname) {
- var flow = decodeFlowToken(hop.user);
- callback(flow ? [flow] : []);
- }
- else
- resolve(hop, callback);
- })(function (addresses) {
- if (m.method === 'ACK') {
- if (!Array.isArray(m.headers.via))
- m.headers.via = [];
-
- if (m.headers.via.length === 0)
- m.headers.via.unshift({ params: { branch: generateBranch() } });
-
- if (addresses.length === 0) {
- errorLog(new Error("ACK: couldn't resolve " + stringifyUri(m.uri)));
- return;
- }
-
- var cn = transport.open(addresses[0], errorLog);
- try {
- cn.send(m);
- }
- catch (e) {
- errorLog(e);
- }
- finally {
- cn.release();
- }
- }
- else
- sequentialSearch(transaction.createClientTransaction.bind(transaction), transport.open.bind(transport), addresses, m, callback || function () { });
- });
- }
- },
- encodeFlowUri: function (flow) {
- return { schema: flow.protocol === 'TLS' ? 'sips' : 'sip', user: encodeFlowToken(flow), host: hostname, params: {} };
- },
- decodeFlowUri: function (uri) {
- uri = parseUri(uri);
- return uri.host === hostname ? decodeFlowToken(uri.user) : undefined;
- },
- isFlowUri: function (uri) {
- return !!!decodeFlowUri(uri);
- },
- hostname: function () { return hostname; },
- destroy: function () {
- transaction.destroy();
- transport.destroy();
- }
- }
- }
-
- exports.start = function (options, callback) {
- var r = exports.create(options, callback);
-
- exports.send = r.send;
- exports.stop = r.destroy;
- exports.encodeFlowUri = r.encodeFlowUri;
- exports.decodeFlowUri = r.decodeFlowUri;
- exports.isFlowUri = r.isFlowUri;
- exports.hostname = r.hostname;
- }
|