/src/SanityCheck.js

https://bitbucket.org/anadalg/jssip · JavaScript · 225 lines · 158 code · 31 blank · 36 comment · 31 complexity · e479591d19fa7e3d8172f97de9bfaf78 MD5 · raw file

  1. /**
  2. * @fileoverview Incoming SIP Message Sanity Check
  3. */
  4. /**
  5. * SIP message sanity check.
  6. * @augments JsSIP
  7. * @function
  8. * @param {JsSIP.IncomingMessage} message
  9. * @param {JsSIP.UA} ua
  10. * @param {JsSIP.Transport} transport
  11. * @returns {Boolean}
  12. */
  13. (function(JsSIP) {
  14. var sanityCheck,
  15. LOG_PREFIX = JsSIP.name +' | '+ 'SANITY CHECK' +' | ',
  16. message, ua, transport,
  17. requests = [],
  18. responses = [],
  19. all = [];
  20. /*
  21. * Sanity Check for incoming Messages
  22. *
  23. * Requests:
  24. * - _rfc3261_8_2_2_1_ Receive a Request with a non supported URI scheme
  25. * - _rfc3261_16_3_4_ Receive a Request already sent by us
  26. * Does not look at via sent-by but at jssip_id, which is inserted as
  27. * a prefix in all initial requests generated by the ua
  28. * - _rfc3261_18_3_request_ Body Content-Length
  29. * - _rfc3261_8_2_2_2_ Merged Requests
  30. *
  31. * Responses:
  32. * - _rfc3261_8_1_3_3_ Multiple Via headers
  33. * - _rfc3261_18_1_2_ sent-by mismatch
  34. * - _rfc3261_18_3_response_ Body Content-Length
  35. *
  36. * All:
  37. * - Minimum headers in a SIP message
  38. */
  39. // Sanity Check functions for requests
  40. function rfc3261_8_2_2_1() {
  41. if(message.s('to').uri.scheme !== 'sip') {
  42. reply(416);
  43. return false;
  44. }
  45. }
  46. function rfc3261_16_3_4() {
  47. if(!message.to_tag) {
  48. if(message.call_id.substr(0, 5) === ua.configuration.jssip_id) {
  49. reply(482);
  50. return false;
  51. }
  52. }
  53. }
  54. function rfc3261_18_3_request() {
  55. var len = JsSIP.Utils.str_utf8_length(message.body),
  56. contentLength = message.getHeader('content-length');
  57. if(len < contentLength) {
  58. reply(400);
  59. return false;
  60. }
  61. }
  62. function rfc3261_8_2_2_2() {
  63. var tr, idx,
  64. fromTag = message.from_tag,
  65. call_id = message.call_id,
  66. cseq = message.cseq;
  67. if(!message.to_tag) {
  68. if(message.method === JsSIP.C.INVITE) {
  69. tr = ua.transactions.ist[message.via_branch];
  70. if(!tr) {
  71. return;
  72. } else {
  73. for(idx in ua.transactions.ist) {
  74. tr = ua.transactions.ist[idx];
  75. if(tr.request.from_tag === fromTag && tr.request.call_id === call_id && tr.request.cseq === cseq) {
  76. reply(482);
  77. return false;
  78. }
  79. }
  80. }
  81. } else {
  82. tr = ua.transactions.nist[message.via_branch];
  83. if(!tr) {
  84. return;
  85. } else {
  86. for(idx in ua.transactions.nist) {
  87. tr = ua.transactions.nist[idx];
  88. if(tr.request.from_tag === fromTag && tr.request.call_id === call_id && tr.request.cseq === cseq) {
  89. reply(482);
  90. return false;
  91. }
  92. }
  93. }
  94. }
  95. }
  96. }
  97. // Sanity Check functions for responses
  98. function rfc3261_8_1_3_3() {
  99. if(message.countHeader('via') > 1) {
  100. console.warn(LOG_PREFIX +'More than one Via header field present in the response. Dropping the response');
  101. return false;
  102. }
  103. }
  104. function rfc3261_18_1_2() {
  105. var via_host = ua.configuration.via_host;
  106. if(message.via.host !== via_host) {
  107. console.warn(LOG_PREFIX +'Via host in the response does not match UA Via host value. Dropping the response');
  108. return false;
  109. }
  110. }
  111. function rfc3261_18_3_response() {
  112. var
  113. len = JsSIP.Utils.str_utf8_length(message.body),
  114. contentLength = message.getHeader('content-length');
  115. if(len < contentLength) {
  116. console.warn(LOG_PREFIX +'Message body length is lower than the value in Content-Length header field. Dropping the response');
  117. return false;
  118. }
  119. }
  120. // Sanity Check functions for requests and responses
  121. function minimumHeaders() {
  122. var
  123. mandatoryHeaders = ['from', 'to', 'call_id', 'cseq', 'via'],
  124. idx = mandatoryHeaders.length;
  125. while(idx--) {
  126. if(!message.hasHeader(mandatoryHeaders[idx])) {
  127. console.warn(LOG_PREFIX +'Missing mandatory header field : '+ mandatoryHeaders[idx] +'. Dropping the response');
  128. return false;
  129. }
  130. }
  131. }
  132. // Reply
  133. function reply(status_code) {
  134. var to,
  135. response = "SIP/2.0 " + status_code + " " + JsSIP.C.REASON_PHRASE[status_code] + "\r\n",
  136. via_length = message.countHeader('via'),
  137. idx = 0;
  138. for(idx; idx < via_length; idx++) {
  139. response += "Via: " + message.getHeader('via', idx) + "\r\n";
  140. }
  141. to = message.getHeader('To');
  142. if(!message.to_tag) {
  143. to += ';tag=' + JsSIP.Utils.newTag();
  144. }
  145. response += "To: " + to + "\r\n";
  146. response += "From: " + message.getHeader('From') + "\r\n";
  147. response += "Call-ID: " + message.call_id + "\r\n";
  148. response += "CSeq: " + message.cseq + " " + message.method + "\r\n";
  149. response += "\r\n";
  150. transport.send(response);
  151. }
  152. requests.push(rfc3261_8_2_2_1);
  153. requests.push(rfc3261_16_3_4);
  154. requests.push(rfc3261_18_3_request);
  155. requests.push(rfc3261_8_2_2_2);
  156. responses.push(rfc3261_8_1_3_3);
  157. responses.push(rfc3261_18_1_2);
  158. responses.push(rfc3261_18_3_response);
  159. all.push(minimumHeaders);
  160. sanityCheck = function(m, u, t) {
  161. var len, pass;
  162. message = m;
  163. ua = u;
  164. transport = t;
  165. len = all.length;
  166. while(len--) {
  167. pass = all[len](message);
  168. if(pass === false) {
  169. return false;
  170. }
  171. }
  172. if(message instanceof JsSIP.IncomingRequest) {
  173. len = requests.length;
  174. while(len--) {
  175. pass = requests[len](message);
  176. if(pass === false) {
  177. return false;
  178. }
  179. }
  180. }
  181. else if(message instanceof JsSIP.IncomingResponse) {
  182. len = responses.length;
  183. while(len--) {
  184. pass = responses[len](message);
  185. if(pass === false) {
  186. return false;
  187. }
  188. }
  189. }
  190. //Everything is OK
  191. return true;
  192. };
  193. JsSIP.sanityCheck = sanityCheck;
  194. }(JsSIP));