jssip
Version:
The Javascript SIP library
186 lines (185 loc) • 5.92 kB
JavaScript
;
const Logger = require('./Logger');
const JsSIP_C = require('./Constants');
const SIPMessage = require('./SIPMessage');
const Utils = require('./Utils');
const logger = new Logger('sanityCheck');
// Checks for requests and responses.
const all = [minimumHeaders];
// Checks for requests.
const requests = [
rfc3261_8_2_2_1,
rfc3261_16_3_4,
rfc3261_18_3_request,
rfc3261_8_2_2_2,
];
// Checks for responses.
const responses = [rfc3261_8_1_3_3, rfc3261_18_3_response];
// local variables.
let message;
let ua;
let transport;
module.exports = (m, u, t) => {
message = m;
ua = u;
transport = t;
for (const check of all) {
if (check() === false) {
return false;
}
}
if (message instanceof SIPMessage.IncomingRequest) {
for (const check of requests) {
if (check() === false) {
return false;
}
}
}
else if (message instanceof SIPMessage.IncomingResponse) {
for (const check of responses) {
if (check() === false) {
return false;
}
}
}
// Everything is OK.
return true;
};
/*
* Sanity Check for incoming Messages
*
* Requests:
* - _rfc3261_8_2_2_1_ Receive a Request with a non supported URI scheme
* - _rfc3261_16_3_4_ Receive a Request already sent by us
* Does not look at via sent-by but at jssip_id, which is inserted as
* a prefix in all initial requests generated by the ua
* - _rfc3261_18_3_request_ Body Content-Length
* - _rfc3261_8_2_2_2_ Merged Requests
*
* Responses:
* - _rfc3261_8_1_3_3_ Multiple Via headers
* - _rfc3261_18_3_response_ Body Content-Length
*
* All:
* - Minimum headers in a SIP message
*/
// Sanity Check functions for requests.
function rfc3261_8_2_2_1() {
if (message.s('to').uri.scheme !== 'sip') {
reply(416);
return false;
}
}
function rfc3261_16_3_4() {
if (!message.to_tag) {
if (message.call_id.substr(0, 5) === ua.configuration.jssip_id) {
reply(482);
return false;
}
}
}
function rfc3261_18_3_request() {
const len = Utils.str_utf8_length(message.body);
const contentLength = message.getHeader('content-length');
if (len < contentLength) {
reply(400);
return false;
}
}
function rfc3261_8_2_2_2() {
const fromTag = message.from_tag;
const call_id = message.call_id;
const cseq = message.cseq;
let tr;
// Accept any in-dialog request.
if (message.to_tag) {
return;
}
// INVITE request.
if (message.method === JsSIP_C.INVITE) {
// If the branch matches the key of any IST then assume it is a retransmission
// and ignore the INVITE.
// TODO: we should reply the last response.
if (ua._transactions.ist[message.via_branch]) {
return false;
}
// Otherwise check whether it is a merged request.
else {
for (const transaction in ua._transactions.ist) {
if (Object.prototype.hasOwnProperty.call(ua._transactions.ist, transaction)) {
tr = ua._transactions.ist[transaction];
if (tr.request.from_tag === fromTag &&
tr.request.call_id === call_id &&
tr.request.cseq === cseq) {
reply(482);
return false;
}
}
}
}
}
// Non INVITE request.
// If the branch matches the key of any NIST then assume it is a retransmission
// and ignore the request.
// TODO: we should reply the last response.
else if (ua._transactions.nist[message.via_branch]) {
return false;
}
// Otherwise check whether it is a merged request.
else {
for (const transaction in ua._transactions.nist) {
if (Object.prototype.hasOwnProperty.call(ua._transactions.nist, transaction)) {
tr = ua._transactions.nist[transaction];
if (tr.request.from_tag === fromTag &&
tr.request.call_id === call_id &&
tr.request.cseq === cseq) {
reply(482);
return false;
}
}
}
}
}
// Sanity Check functions for responses.
function rfc3261_8_1_3_3() {
if (message.getHeaders('via').length > 1) {
logger.debug('more than one Via header field present in the response, dropping the response');
return false;
}
}
function rfc3261_18_3_response() {
const len = Utils.str_utf8_length(message.body), contentLength = message.getHeader('content-length');
if (len < contentLength) {
logger.debug('message body length is lower than the value in Content-Length header field, dropping the response');
return false;
}
}
// Sanity Check functions for requests and responses.
function minimumHeaders() {
const mandatoryHeaders = ['from', 'to', 'call_id', 'cseq', 'via'];
for (const header of mandatoryHeaders) {
if (!message.hasHeader(header)) {
logger.debug(`missing mandatory header field : ${header}, dropping the response`);
return false;
}
}
}
// Reply.
function reply(status_code) {
const vias = message.getHeaders('via');
let to;
let response = `SIP/2.0 ${status_code} ${JsSIP_C.REASON_PHRASE[status_code]}\r\n`;
for (const via of vias) {
response += `Via: ${via}\r\n`;
}
to = message.getHeader('To');
if (!message.to_tag) {
to += `;tag=${Utils.newTag()}`;
}
response += `To: ${to}\r\n`;
response += `From: ${message.getHeader('From')}\r\n`;
response += `Call-ID: ${message.call_id}\r\n`;
response += `CSeq: ${message.cseq} ${message.method}\r\n`;
response += '\r\n';
transport.send(response);
}