mirror of
https://github.com/jambonz/sbc-sip-sidecar.git
synced 2025-12-19 04:27:46 +00:00
fix regbot issue with invalid sip_realm (#79)
* fix regbot issue with invalid sip_realm * wip * wip * wip
This commit is contained in:
178
lib/regbot.js
Normal file
178
lib/regbot.js
Normal file
@@ -0,0 +1,178 @@
|
||||
const {
|
||||
JAMBONES_REGBOT_DEFAULT_EXPIRES_INTERVAL,
|
||||
JAMBONES_REGBOT_MIN_EXPIRES_INTERVAL,
|
||||
JAMBONES_REGBOT_CONTACT_USE_IP
|
||||
} = require('./config');
|
||||
const debug = require('debug')('jambonz:sbc-registrar');
|
||||
const {isValidIPv4, isValidDomainOrIP} = require('./utils');
|
||||
const DEFAULT_EXPIRES = (parseInt(JAMBONES_REGBOT_DEFAULT_EXPIRES_INTERVAL) || 3600);
|
||||
const MIN_EXPIRES = (parseInt(JAMBONES_REGBOT_MIN_EXPIRES_INTERVAL) || 30);
|
||||
const assert = require('assert');
|
||||
|
||||
class Regbot {
|
||||
constructor(logger, opts) {
|
||||
this.logger = logger;
|
||||
|
||||
['ipv4', 'port', 'username', 'password', 'sip_realm', 'protocol'].forEach((prop) => this[prop] = opts[prop]);
|
||||
|
||||
this.voip_carrier_sid = opts.voip_carrier_sid;
|
||||
this.username = opts.username;
|
||||
this.password = opts.password;
|
||||
this.sip_realm = opts.sip_realm || opts.ipv4;
|
||||
this.ipv4 = opts.ipv4;
|
||||
this.port = opts.port;
|
||||
this.use_public_ip_in_contact = opts.use_public_ip_in_contact || JAMBONES_REGBOT_CONTACT_USE_IP;
|
||||
this.use_sips_scheme = opts.use_sips_scheme || false;
|
||||
|
||||
this.fromUser = opts.from_user || this.username;
|
||||
const fromDomain = opts.from_domain || this.sip_realm;
|
||||
if (!isValidDomainOrIP(fromDomain)) {
|
||||
throw new Error(`Invalid from_domain ${fromDomain}`);
|
||||
}
|
||||
this.from = `sip:${this.fromUser}@${fromDomain}`;
|
||||
this.aor = `${this.fromUser}@${this.sip_realm}`;
|
||||
this.status = 'none';
|
||||
}
|
||||
|
||||
async start(srf) {
|
||||
const { lookupSystemInformation } = srf.locals.dbHelpers;
|
||||
assert(!this.timer);
|
||||
|
||||
this.logger.info(`starting regbot for ${this.fromUser}@${this.sip_realm}`);
|
||||
try {
|
||||
const info = await lookupSystemInformation();
|
||||
if (info) {
|
||||
this.ourSipDomain = info.sip_domain_name;
|
||||
this.logger.info(`lookup of sip domain from system_information: ${this.ourSipDomain}`);
|
||||
}
|
||||
else {
|
||||
this.logger.info('no system_information found, we will use the realm or public ip as the domain');
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger.info({ err }, 'Error looking up system information');
|
||||
}
|
||||
this.register(srf);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.logger.info(`stopping regbot ${this.fromUser}@${this.sip_realm}`);
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
voip_carrier_sid: this.voip_carrier_sid,
|
||||
username: this.username,
|
||||
fromUser: this.fromUser,
|
||||
sip_realm: this.sip_realm,
|
||||
ipv4: this.ipv4,
|
||||
port: this.port,
|
||||
aor: this.aor,
|
||||
status: this.status
|
||||
};
|
||||
}
|
||||
|
||||
async register(srf) {
|
||||
const { updateVoipCarriersRegisterStatus } = srf.locals.dbHelpers;
|
||||
try {
|
||||
// transport
|
||||
const transport = (this.protocol.includes('/') ? this.protocol.substring(0, this.protocol.indexOf('/')) :
|
||||
this.protocol).toLowerCase();
|
||||
|
||||
// scheme
|
||||
let scheme = 'sip';
|
||||
if (transport === 'tls' && this.use_sips_scheme) scheme = 'sips';
|
||||
|
||||
let publicAddress = srf.locals.sbcPublicIpAddress.udp;
|
||||
if (transport !== 'udp') {
|
||||
if (srf.locals.sbcPublicIpAddress[transport]) {
|
||||
publicAddress = srf.locals.sbcPublicIpAddress[transport];
|
||||
}
|
||||
else if (transport === 'tls') {
|
||||
publicAddress = srf.locals.sbcPublicIpAddress.udp;
|
||||
}
|
||||
}
|
||||
|
||||
let contactAddress = this.aor;
|
||||
if (this.use_public_ip_in_contact) {
|
||||
contactAddress = `${this.fromUser}@${publicAddress}`;
|
||||
}
|
||||
else if (this.ourSipDomain) {
|
||||
contactAddress = `${this.fromUser}@${this.ourSipDomain}`;
|
||||
}
|
||||
|
||||
this.logger.debug(`sending REGISTER for ${this.aor}`);
|
||||
const isIPv4 = isValidIPv4(this.ipv4);
|
||||
|
||||
const proxy = `sip:${this.ipv4}${isIPv4 ? `:${this.port}` : ''};transport=${transport}`;
|
||||
this.logger.debug({isIPv4}, `sending via proxy ${proxy}`);
|
||||
const req = await srf.request(`${scheme}:${this.sip_realm}`, {
|
||||
method: 'REGISTER',
|
||||
proxy,
|
||||
headers: {
|
||||
'From': this.from,
|
||||
'To': this.from,
|
||||
'Contact': `<${scheme}:${contactAddress};transport=${transport}>;expires=${DEFAULT_EXPIRES}`,
|
||||
'Expires': DEFAULT_EXPIRES
|
||||
},
|
||||
auth: {
|
||||
username: this.username,
|
||||
password: this.password
|
||||
}
|
||||
});
|
||||
req.on('response', (res) => {
|
||||
if (res.status !== 200) {
|
||||
this.status = 'fail';
|
||||
this.logger.info(`${this.aor}: got ${res.status} registering to ${this.ipv4}:${this.port}`);
|
||||
this.timer = setTimeout(this.register.bind(this, srf), 30 * 1000);
|
||||
}
|
||||
else {
|
||||
|
||||
// the code parses the SIP headers to get the expires value
|
||||
// if there is a Contact header, it will use the expires value from there
|
||||
// otherwise, it will use the Expires header, acording to the SIP RFC 3261, section 10.2.4 Refreshing Bindings
|
||||
this.status = 'registered';
|
||||
let expires = DEFAULT_EXPIRES;
|
||||
|
||||
if (res.has('Expires')) {
|
||||
expires = parseInt(res.get('Expires'));
|
||||
}
|
||||
|
||||
if (res.has('Contact')) {
|
||||
const contact = res.getParsedHeader('Contact');
|
||||
if (contact.length > 0 && contact[0].params && contact[0].params.expires) {
|
||||
expires = parseInt(contact[0].params.expires);
|
||||
}
|
||||
} else {
|
||||
this.logger.info({ aor: this.aor, ipv4: this.ipv4, port: this.port },
|
||||
'no Contact header in 200 OK');
|
||||
}
|
||||
|
||||
if (isNaN(expires) || expires < MIN_EXPIRES) {
|
||||
this.logger.info({ aor: this.aor, ipv4: this.ipv4, port: this.port },
|
||||
`got expires of ${expires} in 200 OK, too small so setting to ${MIN_EXPIRES}`);
|
||||
expires = MIN_EXPIRES;
|
||||
}
|
||||
debug(`setting timer for next register to ${expires} seconds`);
|
||||
this.timer = setTimeout(this.register.bind(this, srf), (expires / 2) * 1000);
|
||||
}
|
||||
updateVoipCarriersRegisterStatus(this.voip_carrier_sid, JSON.stringify({
|
||||
status: res.status === 200 ? 'ok' : 'fail',
|
||||
reason: `${res.status} ${res.reason}`,
|
||||
cseq: req.get('Cseq'),
|
||||
callId: req.get('Call-Id')
|
||||
}));
|
||||
});
|
||||
} catch (err) {
|
||||
this.logger.error({ err }, `${this.aor}: Error registering to ${this.ipv4}:${this.port}`);
|
||||
this.timer = setTimeout(this.register.bind(this, srf), 60 * 1000);
|
||||
updateVoipCarriersRegisterStatus(this.voip_carrier_sid, JSON.stringify({
|
||||
status: 'fail',
|
||||
reason: err
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Regbot;
|
||||
@@ -1,15 +1,10 @@
|
||||
const debug = require('debug')('jambonz:sbc-registrar');
|
||||
const {
|
||||
JAMBONES_CLUSTER_ID,
|
||||
JAMBONES_REGBOT_CONTACT_USE_IP,
|
||||
JAMBONES_REGBOT_DEFAULT_EXPIRES_INTERVAL,
|
||||
JAMBONES_REGBOT_MIN_EXPIRES_INTERVAL
|
||||
} = require('./config');
|
||||
const assert = require('assert');
|
||||
const short = require('short-uuid');
|
||||
const {isValidIPv4} = require('./utils');
|
||||
const DEFAULT_EXPIRES = (parseInt(JAMBONES_REGBOT_DEFAULT_EXPIRES_INTERVAL) || 3600);
|
||||
const MIN_EXPIRES = (parseInt(JAMBONES_REGBOT_MIN_EXPIRES_INTERVAL) || 30);
|
||||
const Regbot = require('./regbot');
|
||||
|
||||
const MAX_INITIAL_DELAY = 15;
|
||||
const REGBOT_STATUS_CHECK_INTERVAL = 60;
|
||||
const regbotKey = `${(JAMBONES_CLUSTER_ID || 'default')}:regbot-token`;
|
||||
@@ -35,169 +30,6 @@ function pickRelevantCarrierProperties(c) {
|
||||
};
|
||||
}
|
||||
|
||||
class Regbot {
|
||||
constructor(logger, opts) {
|
||||
this.logger = logger;
|
||||
|
||||
['ipv4', 'port', 'username', 'password', 'sip_realm', 'protocol'].forEach((prop) => this[prop] = opts[prop]);
|
||||
|
||||
this.voip_carrier_sid = opts.voip_carrier_sid;
|
||||
this.username = opts.username;
|
||||
this.password = opts.password;
|
||||
this.sip_realm = opts.sip_realm || opts.ipv4;
|
||||
this.ipv4 = opts.ipv4;
|
||||
this.port = opts.port;
|
||||
this.use_public_ip_in_contact = opts.use_public_ip_in_contact || JAMBONES_REGBOT_CONTACT_USE_IP;
|
||||
this.use_sips_scheme = opts.use_sips_scheme || false;
|
||||
|
||||
this.fromUser = opts.from_user || this.username;
|
||||
const fromDomain = opts.from_domain || this.sip_realm;
|
||||
this.from = `sip:${this.fromUser}@${fromDomain}`;
|
||||
this.aor = `${this.fromUser}@${this.sip_realm}`;
|
||||
this.status = 'none';
|
||||
}
|
||||
|
||||
async start(srf) {
|
||||
const { lookupSystemInformation } = srf.locals.dbHelpers;
|
||||
assert(!this.timer);
|
||||
|
||||
this.logger.info(`starting regbot for ${this.fromUser}@${this.sip_realm}`);
|
||||
try {
|
||||
const info = await lookupSystemInformation();
|
||||
if (info) {
|
||||
this.ourSipDomain = info.sip_domain_name;
|
||||
this.logger.info(`lookup of sip domain from system_information: ${this.ourSipDomain}`);
|
||||
}
|
||||
else {
|
||||
this.logger.info('no system_information found, we will use the realm or public ip as the domain');
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger.info({ err }, 'Error looking up system information');
|
||||
}
|
||||
this.register(srf);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.logger.info(`stopping regbot ${this.fromUser}@${this.sip_realm}`);
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
voip_carrier_sid: this.voip_carrier_sid,
|
||||
username: this.username,
|
||||
fromUser: this.fromUser,
|
||||
sip_realm: this.sip_realm,
|
||||
ipv4: this.ipv4,
|
||||
port: this.port,
|
||||
aor: this.aor,
|
||||
status: this.status
|
||||
};
|
||||
}
|
||||
|
||||
async register(srf) {
|
||||
const { updateVoipCarriersRegisterStatus } = srf.locals.dbHelpers;
|
||||
try {
|
||||
// transport
|
||||
const transport = (this.protocol.includes('/') ? this.protocol.substring(0, this.protocol.indexOf('/')) :
|
||||
this.protocol).toLowerCase();
|
||||
|
||||
// scheme
|
||||
let scheme = 'sip';
|
||||
if (transport === 'tls' && this.use_sips_scheme) scheme = 'sips';
|
||||
|
||||
let publicAddress = srf.locals.sbcPublicIpAddress.udp;
|
||||
if (transport !== 'udp') {
|
||||
if (srf.locals.sbcPublicIpAddress[transport]) {
|
||||
publicAddress = srf.locals.sbcPublicIpAddress[transport];
|
||||
}
|
||||
else if (transport === 'tls') {
|
||||
publicAddress = srf.locals.sbcPublicIpAddress.udp;
|
||||
}
|
||||
}
|
||||
|
||||
let contactAddress = this.aor;
|
||||
if (this.use_public_ip_in_contact) {
|
||||
contactAddress = `${this.fromUser}@${publicAddress}`;
|
||||
}
|
||||
else if (this.ourSipDomain) {
|
||||
contactAddress = `${this.fromUser}@${this.ourSipDomain}`;
|
||||
}
|
||||
|
||||
this.logger.debug(`sending REGISTER for ${this.aor}`);
|
||||
const isIPv4 = isValidIPv4(this.ipv4);
|
||||
|
||||
const proxy = `sip:${this.ipv4}${isIPv4 ? `:${this.port}` : ''};transport=${transport}`;
|
||||
this.logger.debug({isIPv4}, `sending via proxy ${proxy}`);
|
||||
const req = await srf.request(`${scheme}:${this.sip_realm}`, {
|
||||
method: 'REGISTER',
|
||||
proxy,
|
||||
headers: {
|
||||
'From': this.from,
|
||||
'To': this.from,
|
||||
'Contact': `<${scheme}:${contactAddress};transport=${transport}>;expires=${DEFAULT_EXPIRES}`,
|
||||
'Expires': DEFAULT_EXPIRES
|
||||
},
|
||||
auth: {
|
||||
username: this.username,
|
||||
password: this.password
|
||||
}
|
||||
});
|
||||
req.on('response', (res) => {
|
||||
if (res.status !== 200) {
|
||||
this.status = 'fail';
|
||||
this.logger.info(`${this.aor}: got ${res.status} registering to ${this.ipv4}:${this.port}`);
|
||||
this.timer = setTimeout(this.register.bind(this, srf), 30 * 1000);
|
||||
}
|
||||
else {
|
||||
|
||||
// the code parses the SIP headers to get the expires value
|
||||
// if there is a Contact header, it will use the expires value from there
|
||||
// otherwise, it will use the Expires header, acording to the SIP RFC 3261, section 10.2.4 Refreshing Bindings
|
||||
this.status = 'registered';
|
||||
let expires = DEFAULT_EXPIRES;
|
||||
|
||||
if (res.has('Expires')) {
|
||||
expires = parseInt(res.get('Expires'));
|
||||
}
|
||||
|
||||
if (res.has('Contact')) {
|
||||
const contact = res.getParsedHeader('Contact');
|
||||
if (contact.length > 0 && contact[0].params && contact[0].params.expires) {
|
||||
expires = parseInt(contact[0].params.expires);
|
||||
}
|
||||
} else {
|
||||
this.logger.info({ aor: this.aor, ipv4: this.ipv4, port: this.port },
|
||||
'no Contact header in 200 OK');
|
||||
}
|
||||
|
||||
if (isNaN(expires) || expires < MIN_EXPIRES) {
|
||||
this.logger.info({ aor: this.aor, ipv4: this.ipv4, port: this.port },
|
||||
`got expires of ${expires} in 200 OK, too small so setting to ${MIN_EXPIRES}`);
|
||||
expires = MIN_EXPIRES;
|
||||
}
|
||||
debug(`setting timer for next register to ${expires} seconds`);
|
||||
this.timer = setTimeout(this.register.bind(this, srf), (expires / 2) * 1000);
|
||||
}
|
||||
updateVoipCarriersRegisterStatus(this.voip_carrier_sid, JSON.stringify({
|
||||
status: res.status === 200 ? 'ok' : 'fail',
|
||||
reason: `${res.status} ${res.reason}`,
|
||||
cseq: req.get('Cseq'),
|
||||
callId: req.get('Call-Id')
|
||||
}));
|
||||
});
|
||||
} catch (err) {
|
||||
this.logger.error({ err }, `${this.aor}: Error registering to ${this.ipv4}:${this.port}`);
|
||||
this.timer = setTimeout(this.register.bind(this, srf), 60 * 1000);
|
||||
updateVoipCarriersRegisterStatus(this.voip_carrier_sid, JSON.stringify({
|
||||
status: 'fail',
|
||||
reason: err
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = async(logger, srf) => {
|
||||
if (initialized) return;
|
||||
initialized = true;
|
||||
@@ -338,21 +170,30 @@ const updateCarrierRegbots = async(logger, srf) => {
|
||||
|
||||
// start new regbots
|
||||
for (const gw of gateways) {
|
||||
const rb = new Regbot(logger, {
|
||||
voip_carrier_sid: gw.carrier.voip_carrier_sid,
|
||||
ipv4: gw.ipv4,
|
||||
port: gw.port,
|
||||
protocol: gw.protocol,
|
||||
use_sips_scheme: gw.use_sips_scheme,
|
||||
username: gw.carrier.register_username,
|
||||
password: gw.carrier.register_password,
|
||||
sip_realm: gw.carrier.register_sip_realm,
|
||||
from_user: gw.carrier.register_from_user,
|
||||
from_domain: gw.carrier.register_from_domain,
|
||||
use_public_ip_in_contact: gw.carrier.register_public_ip_in_contact
|
||||
});
|
||||
regbots.push(rb);
|
||||
rb.start(srf);
|
||||
try {
|
||||
const rb = new Regbot(logger, {
|
||||
voip_carrier_sid: gw.carrier.voip_carrier_sid,
|
||||
ipv4: gw.ipv4,
|
||||
port: gw.port,
|
||||
protocol: gw.protocol,
|
||||
use_sips_scheme: gw.use_sips_scheme,
|
||||
username: gw.carrier.register_username,
|
||||
password: gw.carrier.register_password,
|
||||
sip_realm: gw.carrier.register_sip_realm,
|
||||
from_user: gw.carrier.register_from_user,
|
||||
from_domain: gw.carrier.register_from_domain,
|
||||
use_public_ip_in_contact: gw.carrier.register_public_ip_in_contact
|
||||
});
|
||||
regbots.push(rb);
|
||||
rb.start(srf);
|
||||
} catch (err) {
|
||||
const { updateVoipCarriersRegisterStatus } = srf.locals.dbHelpers;
|
||||
updateVoipCarriersRegisterStatus(gw.carrier.voip_carrier_sid, JSON.stringify({
|
||||
status: 'fail',
|
||||
reason: err.message,
|
||||
}));
|
||||
logger.error({ err }, `Error starting regbot, ignore register for ${this.fr}`);
|
||||
}
|
||||
}
|
||||
logger.debug(`updateCarrierRegbots: we have started ${regbots.length} regbots`);
|
||||
}
|
||||
|
||||
13
lib/utils.js
13
lib/utils.js
@@ -40,10 +40,23 @@ function isValidIPv4(ip) {
|
||||
return ipv4Pattern.test(ip);
|
||||
}
|
||||
|
||||
function isValidDomainOrIP(input) {
|
||||
const domainRegex = /^(?!:\/\/)([a-zA-Z0-9.-]+)(:\d+)?$/;
|
||||
// eslint-disable-next-line max-len
|
||||
const ipRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(:\d+)?$/;
|
||||
|
||||
if (domainRegex.test(input) || ipRegex.test(input)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // Invalid input
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isUacBehindNat,
|
||||
getSipProtocol,
|
||||
addSipGatewayToBlacklist,
|
||||
NAT_EXPIRES: 30,
|
||||
isValidIPv4,
|
||||
isValidDomainOrIP
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
require('./docker_start');
|
||||
require('./create-test-db');
|
||||
// require('./regbot-tests');
|
||||
require('./regbot-unit-test');
|
||||
require('./sip-register-tests');
|
||||
require('./sip-options-tests');
|
||||
require('./docker_stop');
|
||||
|
||||
@@ -4,7 +4,7 @@ const {
|
||||
JAMBONES_REDIS_PORT,
|
||||
JAMBONES_LOGLEVEL,
|
||||
JAMBONES_CLUSTER_ID,
|
||||
} = require('./config');
|
||||
} = require('../lib/config');
|
||||
const clearModule = require('clear-module');
|
||||
const exec = require('child_process').exec;
|
||||
const opts = Object.assign({
|
||||
@@ -195,4 +195,4 @@ test('trunk not register tests when its IP is not in redis cache', (t) => {
|
||||
console.log(`error received: ${err}`);
|
||||
t.error(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
73
test/regbot-unit-test.js
Normal file
73
test/regbot-unit-test.js
Normal file
@@ -0,0 +1,73 @@
|
||||
const test = require('tape');
|
||||
const Regbot = require('../lib/regbot');
|
||||
const {
|
||||
JAMBONES_LOGLEVEL,
|
||||
} = require('../lib/config');
|
||||
const opts = Object.assign({
|
||||
timestamp: () => { return `, "time": "${new Date().toISOString()}"`; }
|
||||
}, { level:JAMBONES_LOGLEVEL || 'info' });
|
||||
const logger = require('pino')(opts);
|
||||
|
||||
test('Cannot create regbot with invalid sip_realm', (t) => {
|
||||
try {
|
||||
|
||||
new Regbot(logger, {
|
||||
ipv4: '2.3.4.5',
|
||||
port: 5060,
|
||||
username: 'user',
|
||||
password: 'password',
|
||||
sip_realm: 'sip:1.2.3.4',
|
||||
protocol: 'udp',
|
||||
});
|
||||
t.fail('Regbot created with invalid sip_realm');
|
||||
} catch (err) {
|
||||
t.ok(err, 'Error received, regbot cannot be created with invalid sip_realm');
|
||||
}
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Can create regbot with valid sip_realm', (t) => {
|
||||
try {
|
||||
new Regbot(logger, {
|
||||
ipv4: '2.3.4.5',
|
||||
port: 5060,
|
||||
username: 'user',
|
||||
password: 'password',
|
||||
sip_realm: '1.2.3.4',
|
||||
protocol: 'udp',
|
||||
});
|
||||
|
||||
new Regbot(logger, {
|
||||
ipv4: '2.3.4.5',
|
||||
port: 5060,
|
||||
username: 'user',
|
||||
password: 'password',
|
||||
sip_realm: '1.2.3.4:5060',
|
||||
protocol: 'udp',
|
||||
});
|
||||
|
||||
new Regbot(logger, {
|
||||
ipv4: '2.3.4.5',
|
||||
port: 5060,
|
||||
username: 'user',
|
||||
password: 'password',
|
||||
sip_realm: 'sip.server.com',
|
||||
protocol: 'udp',
|
||||
});
|
||||
|
||||
new Regbot(logger, {
|
||||
ipv4: '2.3.4.5',
|
||||
port: 5060,
|
||||
username: 'user',
|
||||
password: 'password',
|
||||
sip_realm: 'sip.server.com:5068',
|
||||
protocol: 'udp',
|
||||
});
|
||||
|
||||
t.ok('Regbot can be created with valid sip_realm');
|
||||
|
||||
} catch (err) {
|
||||
t.fail('Regbot is not created with valid sip_realm');}
|
||||
t.end();
|
||||
});
|
||||
Reference in New Issue
Block a user