module.exports = sanityCheck; /** * Dependencies. */ var debug = require('debug')('JsSIP:sanityCheck'); var JsSIP_C = require('./Constants'); var SIPMessage = require('./SIPMessage'); var Utils = require('./Utils'); var message, ua, transport, requests = [], responses = [], all = []; requests.push(rfc3261_8_2_2_1); requests.push(rfc3261_16_3_4); requests.push(rfc3261_18_3_request); requests.push(rfc3261_8_2_2_2); responses.push(rfc3261_8_1_3_3); responses.push(rfc3261_18_3_response); all.push(minimumHeaders); function sanityCheck(m, u, t) { var len, pass; message = m; ua = u; transport = t; len = all.length; while(len--) { pass = all[len](message); if(pass === false) { return false; } } if(message instanceof SIPMessage.IncomingRequest) { len = requests.length; while(len--) { pass = requests[len](message); if(pass === false) { return false; } } } else if(message instanceof SIPMessage.IncomingResponse) { len = responses.length; while(len--) { pass = responses[len](message); if(pass === 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() { var len = Utils.str_utf8_length(message.body), contentLength = message.getHeader('content-length'); if(len < contentLength) { reply(400); return false; } } function rfc3261_8_2_2_2() { var tr, idx, fromTag = message.from_tag, call_id = message.call_id, cseq = message.cseq; // 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(idx in ua.transactions.ist) { tr = ua.transactions.ist[idx]; if(tr.request.from_tag === fromTag && tr.request.call_id === call_id && tr.request.cseq === cseq) { reply(482); return false; } } } } // Non INVITE request. else { // 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. if (ua.transactions.nist[message.via_branch]) { return false; } // Otherwise check whether it is a merged request. else { for(idx in ua.transactions.nist) { tr = ua.transactions.nist[idx]; 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) { debug('more than one Via header field present in the response, dropping the response'); return false; } } function rfc3261_18_3_response() { var len = Utils.str_utf8_length(message.body), contentLength = message.getHeader('content-length'); if(len < contentLength) { 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() { var mandatoryHeaders = ['from', 'to', 'call_id', 'cseq', 'via'], idx = mandatoryHeaders.length; while(idx--) { if(!message.hasHeader(mandatoryHeaders[idx])) { debug('missing mandatory header field : ' + mandatoryHeaders[idx] + ', dropping the response'); return false; } } } // Reply function reply(status_code) { var to, response = "SIP/2.0 " + status_code + " " + JsSIP_C.REASON_PHRASE[status_code] + "\r\n", vias = message.getHeaders('via'), length = vias.length, idx = 0; for(idx; idx < length; idx++) { response += "Via: " + vias[idx] + "\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); }