feat: merge sip-registra and sip-options-handler

This commit is contained in:
Quan Luu
2022-09-09 16:26:14 +07:00
parent 489f20a2cd
commit da57ab4212
35 changed files with 2064 additions and 13 deletions

23
Dockerfile Normal file
View File

@@ -0,0 +1,23 @@
FROM --platform=linux/amd64 node:18.6.0-alpine as base
RUN apk --update --no-cache add --virtual .builds-deps build-base python3
WORKDIR /opt/app/
FROM base as build
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
FROM base
COPY --from=build /opt/app /opt/app/
ARG NODE_ENV
ENV NODE_ENV $NODE_ENV
CMD [ "node", "app.js" ]

104
README.md Normal file
View File

@@ -0,0 +1,104 @@
# sbc-sip-sidecar ![Build Status](https://github.com/jambonz/sbc-sip-sidecar/workflows/CI/badge.svg)
This application provides a part of the SBC (Session Border Controller) functionality of jambonz. It handles incoming/outgoing REGISTER requests from/to clients/servers (including both sip softphones and WebRTC client applications), incoming OPTIONS. Register Authentication is delegated to customer-side logic via a web callback configured for the account in the jambonz database. Information about active registrations is stored in a redis database.
## registrar database
A redis database is used to hold active registrations. When a register request arrives and is authenticated, the following values are parsed from the request:
- the address of record, or "aor" (e.g, daveh@drachtio.org),
- the sip uri, or "contact" that this user is advertising (e.g. sip:daveh@3.44.3.12:5060)
- the source address and port that sent the REGISTER request to the server
- the transport protocol that should be used to contact the user (e.g. udp, tcp, wss etc)
- the sip address of the drachtio server that received the REGISTER request, and
- the expiration of the registration, in seconds.
- the application callback that should be invoked when a call is placed from this registered device
- the application status callback that should invoked for call events on calls placed from this registered device
A hash value is created from these values and stored with an expiry value equal to the number of seconds granted to the registration (note that when a sip client is detected as being behind a firewall, the application will reduce the granted expires value to 30 seconds, in order to force the client to re-register frequently, however the expiry in redis is set to the longer, originally requested expires value).
The hash value is inserted with a key being the aor:
```
aor => {contact, source, protocol, sbcAddress, call_hook, call_status_hook}, expiry = registration expires value
```
## configuration
Configuration is provided via the [npmjs config](https://www.npmjs.com/package/config) package. The following elements make up the configuration for the application:
##### drachtio server location
```
{
"drachtio": {
"port": 3001,
"secret": "cymru"
},
```
the `drachtio` object specifies the port to listen on for tcp connections from drachtio servers as well as the shared secret that is used to authenticate to the server.
> Note: [outbound connections](https://drachtio.org/docs#outbound-connections) are used for all drachtio applications in jambonz, to allow for easier centralization and clustering of application logic.
##### redis server location
```
"redis": {
"port": 6379,
"host": "127.0.0.1"
},
```
the `redis` object specifies the location of the redis database. Any of the options [defined here](https://www.npmjs.com/package/redis#rediscreateclient) may be supplied, but host and port are minimally required.
Note that in a fully-scaled out environment with multiple SBCs there will be one centralized redis database (or cluster) that stores registrations for all SBCs.
##### application log level
```
"logging": {
"level": "info"
}
```
## http callback
Authenticating users is the responsibility of the client by exposing an http callback. A POST request will be sent to the configured callback (i.e. the value in the `accounts.registration_hook` column in the associated sip realm value in the REGISTER request). The body of the POST will be a json payload including the following information:
```
{
"method": "REGISTER",
"expires": 3600,
"scheme": "digest",
"username": "john",
"realm": "jambonz.org",
"nonce": "157590482938000",
"uri": "sip:172.37.0.10:5060",
"response": "be641cf7951ff23ab04c57907d59f37d",
"qop": "auth",
"nc": "00000001",
"cnonce": "6b8b4567",
"algorithm": "MD5"
}
```
It is the responsibility of the customer-side logic to retrieve the associated password for the given username and to then authenticate the request by calculating a response hash value (per the algorithm described in [RFC 2617](https://tools.ietf.org/html/rfc2617#section-3.2.2)) and comparing it to the response property in the http body.
For example code showing how to calculate the response hash given the above inputs, [see here](https://github.com/jambonz/customer-auth-server/blob/master/lib/utils.js).
For a simple, full-fledged example server doing the same, [see here](https://github.com/jambonz/customer-auth-server).
The customer server SHOULD return a 200 OK response to the http request in all cases with a json body indicating whether the request was successfully authenticated.
The body MUST include a `status` field with a value of either `ok` or `fail`, indicating whether the request was authenticated or not.
```
{"status": "ok"}
```
Additionally, in the case of failure, the body MAY include a `msg` field with a human-readable description of why the authentication failed.
```
{"status": "fail", "msg": "invalid username"}
```
In the case of success, the body MAY include an `expires` value which specifies the duration of time, in seconds, to grant for this registration. If not provided, the expires value in the REGISTER request is used; if provided, however, the value provided must be less than or equal to the duration requested.
```
{"status": "ok", "expires": 300}
```
Additionally in the case of success, the body SHOULD include `call_hook` and `call_status_hook` properties that reference the application URLs to use when calls are placed from this device. If these values are not provided, outbound calling from the device will not be allowed.
## Running the test suite
To run the included test suite, you will need to have a mysql server installed on your laptop/server. You will need to set the MYSQL_ROOT_PASSWORD env variable to the mysql root password before running the tests. The test suite creates a database named 'jambones_test' in your mysql server to run the tests against, and removes it when done.
```
MYSQL_ROOT_PASSWORD=foobar npm test
```

131
app.js
View File

@@ -1,12 +1,13 @@
const assert = require('assert');
assert.ok(process.env.JAMBONES_MYSQL_HOST &&
process.env.JAMBONES_MYSQL_USER &&
process.env.JAMBONES_MYSQL_PASSWORD &&
process.env.JAMBONES_MYSQL_DATABASE, 'missing JAMBONES_MYSQL_XXX env vars');
process.env.JAMBONES_MYSQL_USER &&
process.env.JAMBONES_MYSQL_PASSWORD &&
process.env.JAMBONES_MYSQL_DATABASE, 'missing JAMBONES_MYSQL_XXX env vars');
assert.ok(process.env.JAMBONES_REDIS_HOST, 'missing JAMBONES_REDIS_HOST env var');
assert.ok(process.env.DRACHTIO_HOST, 'missing DRACHTIO_HOST env var');
assert.ok(process.env.DRACHTIO_PORT, 'missing DRACHTIO_PORT env var');
assert.ok(process.env.DRACHTIO_SECRET, 'missing DRACHTIO_SECRET env var');
const opts = Object.assign({
timestamp: () => { return `, "time": "${new Date().toISOString()}"`; }
}, { level: process.env.JAMBONES_LOGLEVEL || 'info' });
@@ -14,10 +15,21 @@ const logger = require('pino')(opts);
const Srf = require('drachtio-srf');
const srf = new Srf();
const setName = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:active-sip`;
const StatsCollector = require('@jambonz/stats-collector');
const stats = new StatsCollector(logger);
const { initLocals, rejectIpv4, checkCache, checkAccountLimits } = require('./lib/middleware');
const responseTime = require('drachtio-mw-response-time');
const regParser = require('drachtio-mw-registration-parser');
const Registrar = require('@jambonz/mw-registrar');
const Emitter = require('events');
const debug = require('debug')('jambonz:sbc-registrar');
const {
lookupAuthHook,
lookupAllVoipCarriers,
lookupSipGatewaysByCarrier,
lookupAccountBySipRealm,
lookupAccountCapacitiesBySid,
addSbcAddress
} = require('@jambonz/db-helpers')({
host: process.env.JAMBONES_MYSQL_HOST,
user: process.env.JAMBONES_MYSQL_USER,
@@ -25,22 +37,45 @@ const {
database: process.env.JAMBONES_MYSQL_DATABASE,
connectionLimit: process.env.JAMBONES_MYSQL_CONNECTION_LIMIT || 10
}, logger);
const {
writeAlerts,
AlertType
} = require('@jambonz/time-series')(logger, {
host: process.env.JAMBONES_TIME_SERIES_HOST,
commitSize: 50,
commitInterval: 'test' === process.env.NODE_ENV ? 7 : 20
});
const {
retrieveSet } = require('@jambonz/realtimedb-helpers')({
addToSet,
removeFromSet,
isMemberOfSet,
retrieveSet
} = require('@jambonz/realtimedb-helpers')({
host: process.env.JAMBONES_REDIS_HOST || 'localhost',
port: process.env.JAMBONES_REDIS_PORT || 6379
}, logger);
srf.locals = {
...srf.locals,
stats,
addToSet, removeFromSet, isMemberOfSet, retrieveSet,
registrar: new Registrar(logger, {
host: process.env.JAMBONES_REDIS_HOST,
port: process.env.JAMBONES_REDIS_PORT || 6379
}),
dbHelpers: {
lookupAuthHook,
lookupAllVoipCarriers,
lookupSipGatewaysByCarrier,
lookupAccountBySipRealm,
lookupAccountCapacitiesBySid
},
realtimeDbHelpers: {
retrieveSet
}
},
writeAlerts,
AlertType
};
srf.connect({ host: process.env.DRACHTIO_HOST, port: process.env.DRACHTIO_PORT, secret: process.env.DRACHTIO_SECRET });
@@ -48,6 +83,17 @@ srf.on('connect', (err, hp) => {
const ativateRegBot = async(err, hp) => {
if (err) return logger.error({ err }, 'Error connecting to drachtio server');
logger.info(`connected to drachtio listening on ${hp}`);
// Add SBC Public IP to Database
const hostports = hp.split(',');
for (const hp of hostports) {
const arr = /^(.*)\/(.*):(\d+)$/.exec(hp);
if (arr && 'udp' === arr[1]) {
logger.info(`adding sbc public address to database: ${arr[2]}`);
addSbcAddress(arr[2]);
}
}
// Only run when I'm the first member in the set Of Actip Sip SBC
const set = await retrieveSet(setName);
const newArray = Array.from(set);
@@ -80,4 +126,77 @@ if (process.env.NODE_ENV === 'test') {
});
}
const rttMetric = (req, res, time) => {
if (res.cached) {
stats.histogram('sbc.registration.cached.response_time', time.toFixed(0), [`status:${res.statusCode}`]);
}
else {
stats.histogram('sbc.registration.total.response_time', time.toFixed(0), [`status:${res.statusCode}`]);
}
};
class RegOutcomeReporter extends Emitter {
constructor() {
super();
this
.on('regHookOutcome', ({ rtt, status }) => {
stats.histogram('app.hook.response_time', rtt, ['hook_type:auth', `status:${status}`]);
if (![200, 403].includes(status)) {
stats.increment('app.hook.error.count', ['hook_type:auth', `status:${status}`]);
}
})
.on('error', async(err, req) => {
logger.error({ err }, 'http webhook failed');
const { account_sid } = req.locals;
if (account_sid) {
let opts = { account_sid };
if (err.code === 'ECONNREFUSED') {
opts = { ...opts, alert_type: AlertType.WEBHOOK_CONNECTION_FAILURE, url: err.hook };
}
else if (err.code === 'ENOTFOUND') {
opts = { ...opts, alert_type: AlertType.WEBHOOK_CONNECTION_FAILURE, url: err.hook };
}
else if (err.name === 'StatusError') {
opts = { ...opts, alert_type: AlertType.WEBHOOK_STATUS_FAILURE, url: err.hook, status: err.statusCode };
}
if (opts.alert_type) {
try {
await writeAlerts(opts);
} catch (err) {
logger.error({ err, opts }, 'Error writing alert');
}
}
}
});
}
}
const authenticator = require('@jambonz/http-authenticator')(lookupAuthHook, logger, {
emitter: new RegOutcomeReporter()
});
// middleware
srf.use('register', [
initLocals,
responseTime(rttMetric),
rejectIpv4(logger),
regParser,
checkCache(logger),
checkAccountLimits(logger),
authenticator]);
srf.use('options', [
initLocals
]);
srf.register(require('./lib/register')({logger}));
srf.options(require('./lib/options')({srf, logger}));
setInterval(async() => {
const count = await srf.locals.registrar.getCountOfUsers();
debug(`count of registered users: ${count}`);
stats.gauge('sbc.users.count', parseInt(count));
}, 30000);
module.exports = { srf, logger };

122
lib/middleware.js Normal file
View File

@@ -0,0 +1,122 @@
const parseUri = require('drachtio-srf').parseUri;
const debug = require('debug')('jambonz:sbc-registrar');
const {NAT_EXPIRES} = require('./utils');
const initLocals = (req, res, next) => {
req.locals = req.locals || {};
next();
};
const rejectIpv4 = (logger) => {
return (req, res, next) => {
const uri = parseUri(req.uri);
if (/^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/.test(uri.host)) {
debug(`rejecting REGISTER from ${req.uri} as it has an ipv4 address and sip realm is required`);
res.send(403);
return req.srf.endSession(req);
}
next();
};
};
const checkCache = (logger) => {
return async(req, res, next) => {
const registration = req.registration;
const uri = parseUri(registration.aor);
const aor = `${uri.user}@${uri.host}`;
req.locals.realm = uri.host;
if (registration.type === 'unregister') return next();
const registrar = req.srf.locals.registrar;
const result = await registrar.query(aor);
if (result) {
// if known valid registration coming from same address, no need to hit the reg callback hook
if (result.proxy === `sip:${req.source_address}:${req.source_port}`) {
debug(`responding to cached register for ${aor}`);
res.cached = true;
res.send(200, {
headers: {
'Contact': req.get('Contact').replace(/expires=\d+/, `expires=${NAT_EXPIRES}`),
'Expires': NAT_EXPIRES
}
});
return req.srf.endSession(req);
}
}
next();
};
};
const checkAccountLimits = (logger) => {
return async(req, res, next) => {
const {lookupAccountBySipRealm, lookupAccountCapacitiesBySid} = req.srf.locals.dbHelpers;
const {realm} = req.locals;
const {registrar, writeAlerts, AlertType} = req.srf.locals;
try {
const account = await lookupAccountBySipRealm(realm);
if (account) {
req.locals = {
...req.locals,
account_sid: account.account_sid,
webhook_secret: account.webhook_secret
};
debug(account, `checkAccountLimits: retrieved account for realm: ${realm}`);
}
else if (process.env.JAMBONES_HOSTING) {
debug(`checkAccountLimits: unknown sip realm ${realm}`);
logger.info(`checkAccountLimits: rejecting register for unknown sip realm: ${realm}`);
return res.send(403);
}
if ('unregister' === req.registration.type || !process.env.JAMBONES_HOSTING) return next();
/* only check limits on the jambonz hosted platform */
const {account_sid} = account;
const capacities = await lookupAccountCapacitiesBySid(account_sid);
debug(JSON.stringify(capacities));
const limit_calls = capacities.find((c) => c.category == 'voice_call_session');
let limit_registrations = limit_calls.quantity * account.device_to_call_ratio;
const extra = capacities.find((c) => c.category == 'device');
if (extra && extra.quantity) limit_registrations += extra.quantity;
debug(`call capacity: ${limit_calls.quantity}, device capacity: ${limit_registrations}`);
if (0 === limit_registrations) {
debug('checkAccountLimits: device calling not allowed for this account');
logger.info({account_sid}, 'checkAccountLimits: device calling not allowed for this account');
writeAlerts({
alert_type: AlertType.DEVICE_LIMIT,
account_sid,
count: 0
}).catch((err) => logger.info({err}, 'checkAccountLimits: error writing alert'));
return res.send(503, 'Max Devices Registered');
}
const deviceCount = await registrar.getCountOfUsers(realm);
if (deviceCount >= limit_registrations) {
debug(account_sid, `checkAccountLimits: limit ${limit_registrations} count ${deviceCount}`);
logger.info({account_sid}, 'checkAccountLimits: registration rejected due to limits');
writeAlerts({
alert_type: AlertType.DEVICE_LIMIT,
account_sid,
count: limit_registrations
}).catch((err) => logger.info({err}, 'checkAccountLimits: error writing alert'));
return res.send(503, 'Max Devices Registered');
}
debug(`checkAccountLimits - passed: devices registered ${deviceCount}, limit is ${limit_registrations}`);
next();
} catch (err) {
logger.error({err, realm}, 'checkAccountLimits: error checking account limits');
res.send(500);
}
};
};
module.exports = {
initLocals,
rejectIpv4,
checkCache,
checkAccountLimits
};

118
lib/options.js Normal file
View File

@@ -0,0 +1,118 @@
const debug = require('debug')('jambonz:sbc-options-handler');
const fsServers = new Map();
const rtpServers = new Map();
module.exports = ({srf, logger}) => {
const {stats, addToSet, removeFromSet, isMemberOfSet, retrieveSet} = srf.locals;
const setNameFs = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:active-fs`;
const setNameRtp = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:active-rtp`;
/* check for expired servers every so often */
setInterval(async() => {
const now = Date.now();
const expires = process.env.EXPIRES_INTERVAL || 60000;
for (const [key, value] of fsServers) {
const duration = now - value;
if (duration > expires) {
fsServers.delete(key);
await removeFromSet(setNameFs, key);
const members = await retrieveSet(setNameFs);
const countOfMembers = members.length;
logger.info({members}, `expired member ${key} from ${setNameFs} we now have ${countOfMembers}`);
}
}
for (const [key, value] of rtpServers) {
const duration = now - value;
if (duration > expires) {
rtpServers.delete(key);
await removeFromSet(setNameRtp, key);
const members = await retrieveSet(setNameRtp);
const countOfMembers = members.length;
logger.info({members}, `expired member ${key} from ${setNameRtp} we now have ${countOfMembers}`);
}
}
}, process.env.CHECK_EXPIRES_INTERVAL || 20000);
/* retrieve the initial list of servers, if any, so we can watch them as well */
const _init = async() => {
try {
const now = Date.now();
const runningFs = await retrieveSet(setNameFs);
const runningRtp = await retrieveSet(setNameRtp);
if (runningFs.length) {
logger.info({runningFs}, 'start watching these FS servers');
for (const ip of runningFs) fsServers.set(ip, now);
}
if (runningRtp.length) {
logger.info({runningRtp}, 'start watching these RTP servers');
for (const ip of runningRtp) rtpServers.set(ip, now);
}
} catch (err) {
logger.error({err}, 'error initializing from redis');
}
};
_init();
return async(req, res) => {
/* OPTIONS ping from internal FS or RTP server? */
const internal = req.has('X-FS-Status') || req.has('X-RTP-Status');
if (!internal) {
debug('got external OPTIONS ping');
res.send(200);
return req.srf.endSession(req);
}
try {
let map, status, countOfMembers;
const h = ['X-FS-Status', 'X-RTP-Status'].find((h) => req.has(h));
if (h) {
const isRtpServer = req.has('X-RTP-Status');
const key = isRtpServer ? req.source_address : `${req.source_address}:${req.source_port}`;
const prefix = isRtpServer ? 'X-RTP' : 'X-FS';
map = isRtpServer ? rtpServers : fsServers;
const setName = isRtpServer ? setNameRtp : setNameFs;
const gaugeName = isRtpServer ? 'rtpservers' : 'featureservers';
status = req.get(`${prefix}-Status`);
if (status === 'open') {
map.set(key, Date.now());
const exists = await isMemberOfSet(setName, key);
if (!exists) {
await addToSet(setName, key);
const members = await retrieveSet(setName);
countOfMembers = members.length;
logger.info({members}, `added new member ${key} to ${setName} we now have ${countOfMembers}`);
debug({members}, `added new member ${key} to ${setName}`);
}
else {
const members = await retrieveSet(setName);
countOfMembers = members.length;
debug(`checkin from existing member ${key} to ${setName}`);
}
}
else {
map.delete(key);
await removeFromSet(setName, key);
const members = await retrieveSet(setName);
countOfMembers = members.length;
logger.info({members}, `removed member ${key} from ${setName} we now have ${countOfMembers}`);
debug({members}, `removed member ${key} from ${setName}`);
}
stats.gauge(gaugeName, map.size);
}
res.send(200, {headers: {
'X-Members': countOfMembers
}});
} catch (err) {
res.send(503);
debug(err);
logger.error({err}, 'Error handling OPTIONS');
}
return req.srf.endSession(req);
};
};

72
lib/register.js Normal file
View File

@@ -0,0 +1,72 @@
const {isUacBehindNat, getSipProtocol, NAT_EXPIRES} = require('./utils');
const parseUri = require('drachtio-srf').parseUri;
const debug = require('debug')('jambonz:sbc-registrar');
module.exports = handler;
function handler({logger}) {
return async(req, res) => {
debug(`received ${req.method} from ${req.protocol}/${req.source_address}:${req.source_port}`);
if ('register' === req.registration.type && '0' !== req.registration.expires) await register(logger, req, res);
else await unregister(logger, req, res);
req.srf.endSession(req);
};
}
async function register(logger, req, res) {
try {
const registrar = req.srf.locals.registrar;
const registration = req.registration;
const uri = parseUri(registration.aor);
const aor = `${uri.user}@${uri.host}`;
let expires = req.authorization.grant.expires || registration.expires;
const grantedExpires = expires;
let contactHdr = req.get('Contact');
// reduce the registration interval if the device is behind a nat
if (isUacBehindNat(req)) {
expires = NAT_EXPIRES;
contactHdr = contactHdr.replace(/expires=\d+/, `expires=${expires}`);
}
const opts = {
contact: req.getParsedHeader('Contact')[0].uri,
sbcAddress: req.server.hostport,
protocol: getSipProtocol(req),
proxy: `sip:${req.source_address}:${req.source_port}`,
callHook: req.authorization.grant.call_hook,
callStatusHook: req.authorization.grant.call_status_hook
};
logger.debug(`adding aor to redis ${aor}`);
const result = await registrar.add(aor, opts, grantedExpires);
debug(`result ${result} from adding ${JSON.stringify(opts)}`);
logger.debug(`successfully added ${aor} to redis, sending 200 OK`);
res.send(200, {
headers: {
'Contact': contactHdr,
'Expires': expires
}
});
} catch (err) {
logger.error({err}, 'Error trying to process REGISTER');
if (!res.finalResponseSent) res.send(500);
}
}
async function unregister(logger, req, res) {
const registrar = req.srf.locals.registrar;
const uri = parseUri(req.registration.aor);
const aor = `${uri.user}@${uri.host}`;
const result = await registrar.remove(aor);
logger.debug({result}, `successfully unregistered ${req.registration.aor}`);
res.send(200, {
headers: {
'Contact': req.get('Contact'),
'Expires': 0
}
});
}

21
lib/utils.js Normal file
View File

@@ -0,0 +1,21 @@
function isUacBehindNat(req) {
// no need for nat handling if wss or tcp being used
if (req.protocol !== 'udp') return false;
// let's keep it simple -- if udp, let's crank down the register interval
return true;
}
function getSipProtocol(req) {
if (req.getParsedHeader('Via')[0].protocol.toLowerCase().startsWith('wss')) return 'wss';
if (req.getParsedHeader('Via')[0].protocol.toLowerCase().startsWith('ws')) return 'ws';
if (req.getParsedHeader('Via')[0].protocol.toLowerCase().startsWith('tcp')) return 'tcp';
if (req.getParsedHeader('Via')[0].protocol.toLowerCase().startsWith('udp')) return 'udp';
}
module.exports = {
isUacBehindNat,
getSipProtocol,
NAT_EXPIRES: 30
};

211
package-lock.json generated
View File

@@ -10,8 +10,14 @@
"license": "ISC",
"dependencies": {
"@jambonz/db-helpers": "^0.6.18",
"@jambonz/http-authenticator": "^0.2.1",
"@jambonz/mw-registrar": "^0.2.2",
"@jambonz/realtimedb-helpers": "^0.4.29",
"@jambonz/stats-collector": "^0.1.6",
"@jambonz/time-series": "^0.1.12",
"debug": "^4.3.4",
"drachtio-mw-registration-parser": "^0.1.0",
"drachtio-mw-response-time": "^1.0.2",
"drachtio-srf": "^4.5.17",
"pino": "^6.14.0"
},
@@ -266,6 +272,28 @@
"uuid": "^8.3.2"
}
},
"node_modules/@jambonz/http-authenticator": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@jambonz/http-authenticator/-/http-authenticator-0.2.2.tgz",
"integrity": "sha512-yl6CajF8c8BOTrXEB/AbTXgqrT6XeymwVZbJWeJG8HZA21UXkKCcM26b8f0P9qqokSvFj0ObjCk22Ks2ytSLNg==",
"dependencies": {
"bent": "^7.3.12",
"debug": "^4.3.1",
"drachtio-srf": "^4.4.63",
"nonce": "^1.0.4",
"qs": "^6.9.4"
}
},
"node_modules/@jambonz/mw-registrar": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@jambonz/mw-registrar/-/mw-registrar-0.2.2.tgz",
"integrity": "sha512-CG0MUVRZZ+tBgB5kvjn5IS1R/OdNV5PpAsxCKE0ehe6XVJ1ap4ch9hSZmEh2q7qPnWu8sR451peF33dnoSeaPA==",
"dependencies": {
"@jambonz/promisify-redis": "^0.0.6",
"debug": "^4.3.1",
"redis": "^3.1.1"
}
},
"node_modules/@jambonz/promisify-redis": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@jambonz/promisify-redis/-/promisify-redis-0.0.6.tgz",
@@ -291,6 +319,15 @@
"redis": "^3.1.2"
}
},
"node_modules/@jambonz/stats-collector": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/@jambonz/stats-collector/-/stats-collector-0.1.6.tgz",
"integrity": "sha512-Qk+kpeb2wravpj3OYPC4N3ML1qoAzARNLfKGZtJ05PTAtfWoZMPnyfPAM9EJHIiVfs2Lec3CXDjEpHna0mc9EA==",
"dependencies": {
"debug": "^4.3.2",
"hot-shots": "^8.5.0"
}
},
"node_modules/@jambonz/time-series": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.1.12.tgz",
@@ -640,6 +677,15 @@
"url": "https://bevry.me/fund"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"optional": true,
"dependencies": {
"file-uri-to-path": "1.0.0"
}
},
"node_modules/bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
@@ -887,6 +933,19 @@
"ignored": "bin/ignored"
}
},
"node_modules/drachtio-mw-registration-parser": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/drachtio-mw-registration-parser/-/drachtio-mw-registration-parser-0.1.0.tgz",
"integrity": "sha512-SNXEbFjOSuvDCArff/fH4xl1424sQTjPVzNE1Lq5LcyV9sVmW9+/CBBwwkFf1QgEtVUvgTa4w/Jx26Amb3hxaA==",
"engines": {
"node": ">= 6.9.3"
}
},
"node_modules/drachtio-mw-response-time": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/drachtio-mw-response-time/-/drachtio-mw-response-time-1.0.2.tgz",
"integrity": "sha512-d+DtKuqhpkSBBkRfwcgS3x26T3lrdgo86yVWZ4wy+K8RtS1faT3hgLBq9EPEYSDkcw76r4klvdDWPhTrPqGAQw=="
},
"node_modules/drachtio-srf": {
"version": "4.5.17",
"resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.5.17.tgz",
@@ -1350,6 +1409,12 @@
"node": "^10.12.0 || >=12.0.0"
}
},
"node_modules/file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"optional": true
},
"node_modules/flat-cache": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
@@ -1689,6 +1754,17 @@
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.2.1.tgz",
"integrity": "sha512-Rf4YVNYpKjZ6ASAmibcwTNciQ5Co5Ztq6iZPEykHpkoflnD/K5ryE/rHehFsTm4NJj8nKDhbi3eKBWGogmNnkg=="
},
"node_modules/hot-shots": {
"version": "8.5.2",
"resolved": "https://registry.npmjs.org/hot-shots/-/hot-shots-8.5.2.tgz",
"integrity": "sha512-1CKCtbYU28KtRriRW+mdOZzKce0WPqU0FOYE4bYs3gD1bFpOrYzQDXfQ09Qz9dJPEltasDOGhFKiYaiuW/j9Dg==",
"engines": {
"node": ">=6.0.0"
},
"optionalDependencies": {
"unix-dgram": "2.0.x"
}
},
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
@@ -2338,6 +2414,12 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A=="
},
"node_modules/nan": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz",
"integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==",
"optional": true
},
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -2376,6 +2458,14 @@
"resolved": "https://registry.npmjs.org/node-noop/-/node-noop-0.0.1.tgz",
"integrity": "sha512-kAUvIRxZyDYFTLqGj+7zqXduG89vtqGmNMt9qDMvYH3H8uNTCOTz5ZN1q2Yg8++fWbzv+ERtYVqaOH42Ag5OpA=="
},
"node_modules/nonce": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/nonce/-/nonce-1.0.4.tgz",
"integrity": "sha512-FVPu+tMZPP91HDwiq1DNhn9WIhg4/uo6mXR0xXAn0IMOxDmjJOkgbH0tm7qtowvAFZofWZRX+9KWZpNURrgtSA==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/object-hash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
@@ -2590,6 +2680,20 @@
"node": ">=6"
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/querystring": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
@@ -3185,6 +3289,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/unix-dgram": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/unix-dgram/-/unix-dgram-2.0.4.tgz",
"integrity": "sha512-7tpK6x7ls7J7pDrrAU63h93R0dVhRbPwiRRCawR10cl+2e1VOvF3bHlVJc6WI1dl/8qk5He673QU+Ogv7bPNaw==",
"hasInstallScript": true,
"optional": true,
"dependencies": {
"bindings": "^1.3.0",
"nan": "^2.13.2"
},
"engines": {
"node": ">=0.10.48"
}
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -3634,6 +3752,28 @@
"uuid": "^8.3.2"
}
},
"@jambonz/http-authenticator": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@jambonz/http-authenticator/-/http-authenticator-0.2.2.tgz",
"integrity": "sha512-yl6CajF8c8BOTrXEB/AbTXgqrT6XeymwVZbJWeJG8HZA21UXkKCcM26b8f0P9qqokSvFj0ObjCk22Ks2ytSLNg==",
"requires": {
"bent": "^7.3.12",
"debug": "^4.3.1",
"drachtio-srf": "^4.4.63",
"nonce": "^1.0.4",
"qs": "^6.9.4"
}
},
"@jambonz/mw-registrar": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@jambonz/mw-registrar/-/mw-registrar-0.2.2.tgz",
"integrity": "sha512-CG0MUVRZZ+tBgB5kvjn5IS1R/OdNV5PpAsxCKE0ehe6XVJ1ap4ch9hSZmEh2q7qPnWu8sR451peF33dnoSeaPA==",
"requires": {
"@jambonz/promisify-redis": "^0.0.6",
"debug": "^4.3.1",
"redis": "^3.1.1"
}
},
"@jambonz/promisify-redis": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@jambonz/promisify-redis/-/promisify-redis-0.0.6.tgz",
@@ -3656,6 +3796,15 @@
"redis": "^3.1.2"
}
},
"@jambonz/stats-collector": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/@jambonz/stats-collector/-/stats-collector-0.1.6.tgz",
"integrity": "sha512-Qk+kpeb2wravpj3OYPC4N3ML1qoAzARNLfKGZtJ05PTAtfWoZMPnyfPAM9EJHIiVfs2Lec3CXDjEpHna0mc9EA==",
"requires": {
"debug": "^4.3.2",
"hot-shots": "^8.5.0"
}
},
"@jambonz/time-series": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/@jambonz/time-series/-/time-series-0.1.12.tgz",
@@ -3924,6 +4073,15 @@
"resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.3.0.tgz",
"integrity": "sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg=="
},
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"optional": true,
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"bn.js": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
@@ -4121,6 +4279,16 @@
"minimatch": "^3.0.4"
}
},
"drachtio-mw-registration-parser": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/drachtio-mw-registration-parser/-/drachtio-mw-registration-parser-0.1.0.tgz",
"integrity": "sha512-SNXEbFjOSuvDCArff/fH4xl1424sQTjPVzNE1Lq5LcyV9sVmW9+/CBBwwkFf1QgEtVUvgTa4w/Jx26Amb3hxaA=="
},
"drachtio-mw-response-time": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/drachtio-mw-response-time/-/drachtio-mw-response-time-1.0.2.tgz",
"integrity": "sha512-d+DtKuqhpkSBBkRfwcgS3x26T3lrdgo86yVWZ4wy+K8RtS1faT3hgLBq9EPEYSDkcw76r4klvdDWPhTrPqGAQw=="
},
"drachtio-srf": {
"version": "4.5.17",
"resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.5.17.tgz",
@@ -4481,6 +4649,12 @@
"flat-cache": "^3.0.4"
}
},
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"optional": true
},
"flat-cache": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
@@ -4735,6 +4909,14 @@
}
}
},
"hot-shots": {
"version": "8.5.2",
"resolved": "https://registry.npmjs.org/hot-shots/-/hot-shots-8.5.2.tgz",
"integrity": "sha512-1CKCtbYU28KtRriRW+mdOZzKce0WPqU0FOYE4bYs3gD1bFpOrYzQDXfQ09Qz9dJPEltasDOGhFKiYaiuW/j9Dg==",
"requires": {
"unix-dgram": "2.0.x"
}
},
"https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
@@ -5232,6 +5414,12 @@
}
}
},
"nan": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz",
"integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==",
"optional": true
},
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -5256,6 +5444,11 @@
"resolved": "https://registry.npmjs.org/node-noop/-/node-noop-0.0.1.tgz",
"integrity": "sha512-kAUvIRxZyDYFTLqGj+7zqXduG89vtqGmNMt9qDMvYH3H8uNTCOTz5ZN1q2Yg8++fWbzv+ERtYVqaOH42Ag5OpA=="
},
"nonce": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/nonce/-/nonce-1.0.4.tgz",
"integrity": "sha512-FVPu+tMZPP91HDwiq1DNhn9WIhg4/uo6mXR0xXAn0IMOxDmjJOkgbH0tm7qtowvAFZofWZRX+9KWZpNURrgtSA=="
},
"object-hash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
@@ -5420,6 +5613,14 @@
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
"qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"requires": {
"side-channel": "^1.0.4"
}
},
"querystring": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
@@ -5852,6 +6053,16 @@
"which-boxed-primitive": "^1.0.2"
}
},
"unix-dgram": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/unix-dgram/-/unix-dgram-2.0.4.tgz",
"integrity": "sha512-7tpK6x7ls7J7pDrrAU63h93R0dVhRbPwiRRCawR10cl+2e1VOvF3bHlVJc6WI1dl/8qk5He673QU+Ogv7bPNaw==",
"optional": true,
"requires": {
"bindings": "^1.3.0",
"nan": "^2.13.2"
}
},
"uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",

View File

@@ -28,8 +28,14 @@
"homepage": "https://github.com/xquanluu/sbc-outbound-handler#readme",
"dependencies": {
"@jambonz/db-helpers": "^0.6.18",
"@jambonz/realtimedb-helpers": "^0.4.29",
"@jambonz/http-authenticator": "^0.2.1",
"@jambonz/mw-registrar": "^0.2.2",
"@jambonz/stats-collector": "^0.1.6",
"@jambonz/time-series": "^0.1.12",
"debug": "^4.3.4",
"drachtio-mw-registration-parser": "^0.1.0",
"drachtio-mw-response-time": "^1.0.2",
"@jambonz/realtimedb-helpers": "^0.4.29",
"drachtio-srf": "^4.5.17",
"pino": "^6.14.0"
},

View File

@@ -1,7 +1,7 @@
version: '3'
networks:
sbc-registrar:
sbc-sip-sidecar:
driver: bridge
ipam:
config:
@@ -20,7 +20,7 @@ services:
timeout: 5s
retries: 10
networks:
sbc-registrar:
sbc-sip-sidecar:
ipv4_address: 172.39.0.2
sbc:
image: drachtio/drachtio-server:latest
@@ -28,17 +28,28 @@ services:
ports:
- "9022:9022/tcp"
networks:
sbc-registrar:
sbc-sip-sidecar:
ipv4_address: 172.39.0.10
depends_on:
mysql:
condition: service_healthy
auth-server:
image: jambonz/customer-auth-server:latest
command: npm start
ports:
- "4000:4000/tcp"
env_file: docker.env
networks:
sbc-sip-sidecar:
ipv4_address: 172.39.0.12
redis:
image: redis:5-alpine
ports:
- "16379:6379/tcp"
networks:
sbc-registrar:
sbc-sip-sidecar:
ipv4_address: 172.39.0.13
sipp-uas-auth-register:
@@ -49,7 +60,7 @@ services:
- ./scenarios:/tmp
tty: true
networks:
sbc-registrar:
sbc-sip-sidecar:
ipv4_address: 172.39.0.14
sipp-uas-auth-register-fail:
@@ -60,5 +71,14 @@ services:
- ./scenarios:/tmp
tty: true
networks:
sbc-registrar:
sbc-sip-sidecar:
ipv4_address: 172.39.0.15
uas:
image: drachtio/sipp:latest
command: sipp -sf /tmp/uas.xml
volumes:
- ./scenarios:/tmp
tty: true
networks:
sbc-sip-sidecar:
ipv4_address: 172.39.0.60

View File

@@ -1,4 +1,6 @@
require('./docker_start');
require('./create-test-db');
require('./regbot-tests');
require('./sip-register-tests');
require('./sip-options-tests');
require('./docker_stop');

View File

@@ -0,0 +1,3 @@
SEQUENTIAL
# user, domain, authentication
jane;jambonz.org;[authentication username=jane password=8765]
1 SEQUENTIAL
2 # user, domain, authentication
3 jane;jambonz.org;[authentication username=jane password=8765]

View File

@@ -0,0 +1,3 @@
SEQUENTIAL
# user, domain, authentication
john;fail.com;[authentication username=john password=1234]
1 SEQUENTIAL
2 # user, domain, authentication
3 john;fail.com;[authentication username=john password=1234]

View File

@@ -0,0 +1,3 @@
SEQUENTIAL
# user, domain, authentication
john;jambonz.org;[authentication username=john password=1234]
1 SEQUENTIAL
2 # user, domain, authentication
3 john;jambonz.org;[authentication username=john password=1234]

View File

@@ -0,0 +1,3 @@
SEQUENTIAL
# user, domain, authentication
jane;jambonz.org;[authentication username=jane password=5678]
1 SEQUENTIAL
2 # user, domain, authentication
3 jane;jambonz.org;[authentication username=jane password=5678]

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uac' scenario. -->
<!-- -->
<scenario name="Basic Sipstone UAC">
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
<!-- generated by sipp. To do so, use [call_id] keyword. -->
<send retrans="500">
<![CDATA[
OPTIONS sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
To: [service] <sip:[service]@[remote_ip]:[remote_port]>
Call-ID: [call_id]
CSeq: 1 OPTIONS
Max-Forwards: 70
Subject: Performance Test
X-FS-Status: open
X-FS-Calls: 0
Content-Length: 0
]]>
</send>
<recv response="200" rtd="true">
<action>
<ereg regexp="1" search_in="hdr" header="X-Members:" check_it="true" assign_to="1"/>
<log message="[$1]"/>
</action>
</recv>
</scenario>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uac' scenario. -->
<!-- -->
<scenario name="Basic Sipstone UAC">
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
<!-- generated by sipp. To do so, use [call_id] keyword. -->
<send retrans="500">
<![CDATA[
OPTIONS sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
To: [service] <sip:[service]@[remote_ip]:[remote_port]>
Call-ID: [call_id]
CSeq: 1 OPTIONS
Max-Forwards: 70
Subject: Performance Test
X-FS-Status: open
X-FS-Calls: 0
Content-Length: 0
]]>
</send>
<recv response="200" rtd="true">
<action>
<ereg regexp="2" search_in="hdr" header="X-Members:" check_it="true" assign_to="1"/>
<log message="[$1]"/>
</action>
</recv>
</scenario>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uac' scenario. -->
<!-- -->
<scenario name="Basic Sipstone UAC">
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
<!-- generated by sipp. To do so, use [call_id] keyword. -->
<send retrans="500">
<![CDATA[
OPTIONS sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
To: [service] <sip:[service]@[remote_ip]:[remote_port]>
Call-ID: [call_id]
CSeq: 1 OPTIONS
Max-Forwards: 70
Subject: Performance Test
X-RTP-Status: open
X-RTP-Calls: 0
Content-Length: 0
]]>
</send>
<recv response="200" rtd="true">
<action>
<ereg regexp="1" search_in="hdr" header="X-Members:" check_it="true" assign_to="1"/>
<log message="[$1]"/>
</action>
</recv>
</scenario>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uac' scenario. -->
<!-- -->
<scenario name="Basic Sipstone UAC">
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
<!-- generated by sipp. To do so, use [call_id] keyword. -->
<send retrans="500">
<![CDATA[
OPTIONS sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
To: [service] <sip:[service]@[remote_ip]:[remote_port]>
Call-ID: [call_id]
CSeq: 1 OPTIONS
Max-Forwards: 70
Subject: Performance Test
X-RTP-Status: open
X-RTP-Calls: 0
Content-Length: 0
]]>
</send>
<recv response="200" rtd="true">
<action>
<ereg regexp="2" search_in="hdr" header="X-Members:" check_it="true" assign_to="1"/>
<log message="[$1]"/>
</action>
</recv>
</scenario>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uac' scenario. -->
<!-- -->
<scenario name="Basic Sipstone UAC">
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
<!-- generated by sipp. To do so, use [call_id] keyword. -->
<send retrans="500">
<![CDATA[
OPTIONS sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
To: [service] <sip:[service]@[remote_ip]:[remote_port]>
Call-ID: [call_id]
CSeq: 1 OPTIONS
Max-Forwards: 70
Subject: Performance Test
Content-Length: 0
]]>
</send>
<recv response="200" rtd="true">
<action>
<ereg regexp="\d+" search_in="hdr" header="X-Members:" check_it_inverse="true" assign_to="1"/>
<log message="[$1]"/>
</action>
</recv>
</scenario>

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uac' scenario. -->
<!-- -->
<scenario name="Basic Sipstone UAC">
<send retrans="500">
<![CDATA[
REGISTER sip:[field1] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
Max-Forwards: 70
From: "sipp" <sip:[field0]@[field1]>;tag=[call_number]
To: "sipp" <sip:[field0]@[field1]>
Call-ID: reg///[call_id]
CSeq: 7 REGISTER
Subject: uac-register-auth-failure-expect-403
Contact: <sip:sipp@[local_ip]:[local_port]>
Expires: 3600
Content-Length: 0
User-Agent: SIPp
]]>
</send>
<recv response="100" optional="true">
</recv>
<recv response="401" auth="true" rtd="true">
</recv>
<send retrans="500">
<![CDATA[
REGISTER sip:[field1] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
Max-Forwards: 70
From: "sipp" <sip:[field0]@[field1]>;tag=[call_number]
To: "sipp" <sip:[field0]@[field1]>
Call-ID: reg///[call_id]
CSeq: 8 REGISTER
Subject: uac-register-auth-failure-expect-403
Contact: <sip:sipp@[local_ip]:[local_port]>
Expires: 3600
Content-Length: 0
User-Agent: SIPp
[field2]
]]>
</send>
<recv response="100" optional="true">
</recv>
<recv response="403">
</recv>
<ResponseTimeRepartition value="10, 20"/>
<CallLengthRepartition value="10"/>
</scenario>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<scenario name="Basic Sipstone UAC">
<send retrans="500">
<![CDATA[
REGISTER sip:[field1] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
Max-Forwards: 70
From: "sipp" <sip:[field0]@[field1]>;tag=[call_number]
To: "sipp" <sip:[field0]@[field1]>
Call-ID: reg///[call_id]
CSeq: 7 REGISTER
Subject: uac-register-auth-failure-expect-403
Contact: <sip:sipp@[local_ip]:[local_port]>
Expires: 3600
Content-Length: 0
User-Agent: SIPp
]]>
</send>
<recv response="100" optional="true">
</recv>
<recv response="503" auth="true" rtd="true">
</recv>
</scenario>

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<scenario name="Basic Sipstone UAC">
<send retrans="500">
<![CDATA[
REGISTER sip:[field1] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
Max-Forwards: 70
From: "sipp" <sip:[field0]@[field1]>;tag=[call_number]
To: "sipp" <sip:[field0]@[field1]>
Call-ID: reg///[call_id]
Subject: uac-register-auth-success
CSeq: 7 REGISTER
Contact: <sip:sipp@[local_ip]:[local_port]>
Expires: 3600
Content-Length: 0
User-Agent: SIPp
]]>
</send>
<recv response="100" optional="true">
</recv>
<recv response="401" auth="true" rtd="true">
</recv>
<send retrans="500">
<![CDATA[
REGISTER sip:[field1] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
Max-Forwards: 70
From: "sipp" <sip:[field0]@[field1]>;tag=[call_number]
To: "sipp" <sip:[field0]@[field1]>
Call-ID: reg///[call_id]
CSeq: 8 REGISTER
Subject: uac-register-auth-success
Contact: <sip:sipp@[local_ip]:[local_port]>
Expires: 3600
Content-Length: 0
User-Agent: SIPp
[field2]
]]>
</send>
<recv response="100" optional="true">
</recv>
<recv response="200">
</recv>
<ResponseTimeRepartition value="10, 20"/>
<CallLengthRepartition value="10"/>
</scenario>

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uac' scenario. -->
<!-- -->
<scenario name="Basic Sipstone UAC">
<send retrans="500">
<![CDATA[
REGISTER sip:[field1] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
Max-Forwards: 70
From: "sipp" <sip:[field0]@[field1]>;tag=[call_number]
To: "sipp" <sip:[field0]@[field1]>
Call-ID: reg2///[call_id]
Subject: uac-register-auth-success
CSeq: 7 REGISTER
Contact: <sip:sipp@[local_ip]:[local_port]>
Expires: 5
Content-Length: 0
User-Agent: SIPp
]]>
</send>
<recv response="100" optional="true">
</recv>
<recv response="401" auth="true" rtd="true">
</recv>
<send retrans="500">
<![CDATA[
REGISTER sip:[field1] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
Max-Forwards: 70
From: "sipp" <sip:[field0]@[field1]>;tag=[call_number]
To: "sipp" <sip:[field0]@[field1]>
Call-ID: reg2///[call_id]
CSeq: 8 REGISTER
Subject: uac-register-auth-success
Contact: <sip:sipp@[local_ip]:[local_port]>
Expires: 5
Content-Length: 0
User-Agent: SIPp
[field2]
]]>
</send>
<recv response="100" optional="true">
</recv>
<recv response="200">
</recv>
<ResponseTimeRepartition value="10, 20"/>
<CallLengthRepartition value="10"/>
</scenario>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<scenario name="Basic Sipstone UAC">
<send retrans="500">
<![CDATA[
REGISTER sip:[field1] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
Max-Forwards: 70
From: "sipp" <sip:[field0]@[field1]>;tag=[call_number]
To: "sipp" <sip:[field0]@[field1]>
Call-ID: reg///[call_id]
CSeq: 7 REGISTER
Contact: <sip:sipp@[local_ip]:[local_port]>
Expires: 3600
Content-Length: 0
User-Agent: SIPp
]]>
</send>
<recv response="100" optional="true">
</recv>
<recv response="403">
</recv>
</scenario>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<scenario name="Basic Sipstone UAC">
<send retrans="500">
<![CDATA[
REGISTER sip:192.169.1.1 SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
Max-Forwards: 70
From: "sipp" <sip:[field0]@[field1]>;tag=[call_number]
To: "sipp" <sip:[field0]@[field1]>
Call-ID: reg///[call_id]
CSeq: 7 REGISTER
Contact: <sip:sipp@[local_ip]:[local_port]>
Expires: 3600
Content-Length: 0
User-Agent: SIPp
]]>
</send>
<recv response="100" optional="true">
</recv>
<recv response="403">
</recv>
</scenario>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uac' scenario. -->
<!-- -->
<scenario name="Basic Sipstone UAC">
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
<!-- generated by sipp. To do so, use [call_id] keyword. -->
<send retrans="500">
<![CDATA[
OPTIONS sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
To: [service] <sip:[service]@[remote_ip]:[remote_port]>
Call-ID: [call_id]
CSeq: 1 OPTIONS
Max-Forwards: 70
Subject: Performance Test
X-FS-Status: closed
X-FS-Calls: 0
Content-Length: 0
]]>
</send>
<recv response="200" rtd="true">
<action>
<ereg regexp="1" search_in="hdr" header="X-Members:" check_it="true" assign_to="1"/>
<log message="[$1]"/>
</action>
</recv>
</scenario>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uac' scenario. -->
<!-- -->
<scenario name="Basic Sipstone UAC">
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
<!-- generated by sipp. To do so, use [call_id] keyword. -->
<send retrans="500">
<![CDATA[
OPTIONS sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
To: [service] <sip:[service]@[remote_ip]:[remote_port]>
Call-ID: [call_id]
CSeq: 1 OPTIONS
Max-Forwards: 70
Subject: Performance Test
X-FS-Status: closed
X-FS-Calls: 0
Content-Length: 0
]]>
</send>
<recv response="200" rtd="true">
<action>
<ereg regexp="0" search_in="hdr" header="X-Members:" check_it="true" assign_to="1"/>
<log message="[$1]"/>
</action>
</recv>
</scenario>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uac' scenario. -->
<!-- -->
<scenario name="Basic Sipstone UAC">
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
<!-- generated by sipp. To do so, use [call_id] keyword. -->
<send retrans="500">
<![CDATA[
OPTIONS sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
To: [service] <sip:[service]@[remote_ip]:[remote_port]>
Call-ID: [call_id]
CSeq: 1 OPTIONS
Max-Forwards: 70
Subject: Performance Test
X-RTP-Status: closed
X-RTP-Calls: 0
Content-Length: 0
]]>
</send>
<recv response="200" rtd="true">
<action>
<ereg regexp="1" search_in="hdr" header="X-Members:" check_it="true" assign_to="1"/>
<log message="[$1]"/>
</action>
</recv>
</scenario>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uac' scenario. -->
<!-- -->
<scenario name="Basic Sipstone UAC">
<!-- In client mode (sipp placing calls), the Call-ID MUST be -->
<!-- generated by sipp. To do so, use [call_id] keyword. -->
<send retrans="500">
<![CDATA[
OPTIONS sip:[service]@[remote_ip]:[remote_port] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[pid]SIPpTag00[call_number]
To: [service] <sip:[service]@[remote_ip]:[remote_port]>
Call-ID: [call_id]
CSeq: 1 OPTIONS
Max-Forwards: 70
Subject: Performance Test
X-RTP-Status: closed
X-RTP-Calls: 0
Content-Length: 0
]]>
</send>
<recv response="200" rtd="true">
<action>
<ereg regexp="0" search_in="hdr" header="X-Members:" check_it="true" assign_to="1"/>
<log message="[$1]"/>
</action>
</recv>
</scenario>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uac' scenario. -->
<!-- -->
<scenario name="Basic Sipstone UAC">
<send retrans="500">
<![CDATA[
REGISTER sip:[field1] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
Max-Forwards: 70
From: "sipp" <sip:[field0]@[field1]>;tag=[call_number]
To: "sipp" <sip:[field0]@[field1]>
Call-ID: reg///[call_id]
CSeq: 7 REGISTER
Contact: <sip:sipp@[local_ip]:[local_port]>
Expires: 0
Content-Length: 0
User-Agent: SIPp
]]>
</send>
<recv response="100" optional="true">
</recv>
<recv response="401" auth="true" rtd="true">
</recv>
<send retrans="500">
<![CDATA[
REGISTER sip:[field1] SIP/2.0
Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]
Max-Forwards: 70
From: "sipp" <sip:[field0]@[field1]>;tag=[call_number]
To: "sipp" <sip:[field0]@[field1]>
Call-ID: reg///[call_id]
CSeq: 8 REGISTER
Contact: <sip:sipp@[local_ip]:[local_port]>
Expires: 0
Content-Length: 0
User-Agent: SIPp
[field2]
]]>
</send>
<recv response="100" optional="true">
</recv>
<recv response="200">
</recv>
<ResponseTimeRepartition value="10, 20"/>
<CallLengthRepartition value="10"/>
</scenario>

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uas' scenario. -->
<!-- -->
<scenario name="Basic UAS responder">
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
<!-- are saved and used for following messages sent. Useful to test -->
<!-- against stateful SIP proxies/B2BUAs. -->
<recv request="REGISTER" crlf="true">
</recv>
<send>
<![CDATA[
SIP/2.0 401 Unauthorized
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
WWW-Authenticate: Digest realm="sip.jambonz.org", nonce="4cdbb733645816512687270b83d2ae5d11e4d9d8"
Content-Length: 0
]]>
</send>
<recv request="REGISTER" crlf="true">
<action>
<verifyauth assign_to="authvalid" username="foo" password="bar" />
</action>
</recv>
<nop hide="true" test="authvalid" next="goodauth" />
<send next="endit">
<![CDATA[
SIP/2.0 403 Forbidden
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
Contact: <sip:[local_ip]:[local_port];transport=[transport]>;expires=60
Content-Type: application/sdp
Content-Length: 0
]]>
</send>
<label id="goodauth"/>
<send>
<![CDATA[
SIP/2.0 200 OK
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
Contact: <sip:[local_ip]:[local_port];transport=[transport]>;expires=60
Content-Type: application/sdp
Content-Length: 0
]]>
</send>
<label id="badauth"/>
<label id="endit"/>
<!-- Keep the call open for a while in case the 200 is lost to be -->
<!-- able to retransmit it if we receive the BYE again. -->
<timewait milliseconds="4000"/>
<!-- definition of the response time repartition table (unit is ms) -->
<ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
<!-- definition of the call length repartition table (unit is ms) -->
<CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
</scenario>

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uas' scenario. -->
<!-- -->
<scenario name="Basic UAS responder">
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
<!-- are saved and used for following messages sent. Useful to test -->
<!-- against stateful SIP proxies/B2BUAs. -->
<recv request="REGISTER" crlf="true">
</recv>
<send>
<![CDATA[
SIP/2.0 401 Unauthorized
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
WWW-Authenticate: Digest realm="sip.jambonz.org", nonce="4cdbb733645816512687270b83d2ae5d11e4d9d8"
Content-Length: 0
]]>
</send>
<recv request="REGISTER" crlf="true">
<action>
<verifyauth assign_to="authvalid" username="foo" password="baz" />
</action>
</recv>
<nop hide="true" test="authvalid" next="goodauth" />
<send next="endit">
<![CDATA[
SIP/2.0 403 Forbidden
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
Contact: <sip:[local_ip]:[local_port];transport=[transport]>;expires=60
Content-Type: application/sdp
Content-Length: 0
]]>
</send>
<label id="goodauth"/>
<send>
<![CDATA[
SIP/2.0 200 OK
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
Contact: <sip:[local_ip]:[local_port];transport=[transport]>;expires=60
Content-Type: application/sdp
Content-Length: 0
]]>
</send>
<label id="badauth"/>
<label id="endit"/>
<!-- Keep the call open for a while in case the 200 is lost to be -->
<!-- able to retransmit it if we receive the BYE again. -->
<timewait milliseconds="4000"/>
<!-- definition of the response time repartition table (unit is ms) -->
<ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
<!-- definition of the call length repartition table (unit is ms) -->
<CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
</scenario>

120
test/scenarios/uas.xml Normal file
View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE scenario SYSTEM "sipp.dtd">
<!-- This program is free software; you can redistribute it and/or -->
<!-- modify it under the terms of the GNU General Public License as -->
<!-- published by the Free Software Foundation; either version 2 of the -->
<!-- License, or (at your option) any later version. -->
<!-- -->
<!-- This program is distributed in the hope that it will be useful, -->
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
<!-- GNU General Public License for more details. -->
<!-- -->
<!-- You should have received a copy of the GNU General Public License -->
<!-- along with this program; if not, write to the -->
<!-- Free Software Foundation, Inc., -->
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
<!-- -->
<!-- Sipp default 'uas' scenario. -->
<!-- -->
<scenario name="Basic UAS responder">
<!-- By adding rrs="true" (Record Route Sets), the route sets -->
<!-- are saved and used for following messages sent. Useful to test -->
<!-- against stateful SIP proxies/B2BUAs. -->
<recv request="INVITE" crlf="true">
<action>
<ereg regexp=".*" search_in="hdr" header="Subject:" assign_to="1" />
</action>
</recv>
<!-- The '[last_*]' keyword is replaced automatically by the -->
<!-- specified header if it was present in the last message received -->
<!-- (except if it was a retransmission). If the header was not -->
<!-- present or if no message has been received, the '[last_*]' -->
<!-- keyword is discarded, and all bytes until the end of the line -->
<!-- are also discarded. -->
<!-- -->
<!-- If the specified header was present several times in the -->
<!-- message, all occurrences are concatenated (CRLF separated) -->
<!-- to be used in place of the '[last_*]' keyword. -->
<send>
<![CDATA[
SIP/2.0 180 Ringing
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
[last_Record-Route:]
Subject:[$1]
Content-Length: 0
]]>
</send>
<send>
<![CDATA[
SIP/2.0 200 OK
[last_Via:]
[last_From:]
[last_To:];tag=[pid]SIPpTag01[call_number]
[last_Call-ID:]
[last_CSeq:]
[last_Record-Route:]
Subject:[$1]
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
Content-Type: application/sdp
Content-Length: [len]
v=0
o=user1 53655765 2353687637 IN IP[local_ip_type] [local_ip]
s=-
c=IN IP[media_ip_type] [media_ip]
t=0 0
m=audio [media_port] RTP/AVP 0
a=rtpmap:0 PCMU/8000
]]>
</send>
<recv request="ACK"
rtd="true"
crlf="true">
</recv>
<recv request="BYE">
</recv>
<send>
<![CDATA[
SIP/2.0 200 OK
[last_Via:]
[last_From:]
[last_To:]
[last_Call-ID:]
[last_CSeq:]
Contact: <sip:[local_ip]:[local_port];transport=[transport]>
Content-Length: 0
]]>
</send>
<!-- Keep the call open for a while in case the 200 is lost to be -->
<!-- able to retransmit it if we receive the BYE again. -->
<timewait milliseconds="4000"/>
<!-- definition of the response time repartition table (unit is ms) -->
<ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
<!-- definition of the call length repartition table (unit is ms) -->
<CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
</scenario>

94
test/sipp.js Normal file
View File

@@ -0,0 +1,94 @@
const { spawn } = require('child_process');
const debug = require('debug')('jambonz:ci');
let network;
const obj = {};
let output = '';
let idx = 1;
function clearOutput() {
output = '';
}
function addOutput(str) {
for (let i = 0; i < str.length; i++) {
if (str.charCodeAt(i) < 128) output += str.charAt(i);
}
}
module.exports = (networkName) => {
network = networkName ;
return obj;
};
obj.output = () => {
return output;
};
obj.sippUac = (file, regObj, bindAddress, injectionFile) => {
const cmd = 'docker';
let args = null;
if(regObj) {
args = [
'run', '--rm', '--net', `${network}`,
'-v', `${__dirname}/scenarios:/tmp/scenarios`,
'drachtio/sipp', 'sipp', `${regObj.remote_host}`, // remote host is require on auth
'-inf', `/tmp/scenarios/${regObj.data_file}`,
'-sf', `/tmp/scenarios/${file}`,
'-m', '1',
'-sleep', '250ms',
'-nostdin',
'-cid_str', `%u-%p@%s-${idx++}`,
'sbc', '-trace_msg'
];
} else if (injectionFile) {
args = [
'run', '-t', '--rm', '--net', `${network}`,
'-v', `${__dirname}/scenarios:/tmp/scenarios`,
'drachtio/sipp', 'sipp', '-sf', `/tmp/scenarios/${file}`,
'-inf', `/tmp/scenarios/${injectionFile}`,
'-m', '1',
'-sleep', '250ms',
'-nostdin',
'-cid_str', `%u-%p@%s-${idx++}`,
'172.39.0.10'];
}
else {
args = [
'run', '-t', '--rm', '--net', `${network}`,
'-v', `${__dirname}/scenarios:/tmp/scenarios`,
'drachtio/sipp', 'sipp', '-sf', `/tmp/scenarios/${file}`,
'-m', '1',
'-sleep', '250ms',
'-nostdin',
'-cid_str', `%u-%p@%s-${idx++}`,
'172.39.0.10'];
}
if (bindAddress) args.splice(5, 0, '--ip', bindAddress);
clearOutput();
return new Promise((resolve, reject) => {
const child_process = spawn(cmd, args, {stdio: ['inherit', 'pipe', 'pipe']});
child_process.on('exit', (code, signal) => {
if (code === 0) {
return resolve();
}
console.log(`sipp exited with non-zero code ${code} signal ${signal}`);
reject(code);
});
child_process.on('error', (error) => {
console.log(`error spawing child process for docker: ${args}`);
});
child_process.stdout.on('data', (data) => {
debug(`stdout: ${data}`);
addOutput(data.toString());
});
child_process.stderr.on('data', (data) => {
debug(`stderr: ${data}`);
addOutput(data.toString());
});
});
};