UNPKG

sip.js

Version:

A SIP library for JavaScript

242 lines (241 loc) 9.47 kB
/* eslint-disable no-inner-declarations */ /* eslint-disable @typescript-eslint/no-namespace */ import { Grammar } from "../../grammar/grammar.js"; import { IncomingRequestMessage } from "./incoming-request-message.js"; import { IncomingResponseMessage } from "./incoming-response-message.js"; /** * Extract and parse every header of a SIP message. * @internal */ export var Parser; (function (Parser) { // eslint-disable-next-line @typescript-eslint/no-explicit-any function getHeader(data, headerStart) { // 'start' position of the header. let start = headerStart; // 'end' position of the header. let end = 0; // 'partial end' position of the header. let partialEnd = 0; // End of message. if (data.substring(start, start + 2).match(/(^\r\n)/)) { return -2; } while (end === 0) { // Partial End of Header. partialEnd = data.indexOf("\r\n", start); // 'indexOf' returns -1 if the value to be found never occurs. if (partialEnd === -1) { return partialEnd; } if (!data.substring(partialEnd + 2, partialEnd + 4).match(/(^\r\n)/) && data.charAt(partialEnd + 2).match(/(^\s+)/)) { // Not the end of the message. Continue from the next position. start = partialEnd + 2; } else { end = partialEnd; } } return end; } Parser.getHeader = getHeader; function parseHeader(message, // eslint-disable-next-line @typescript-eslint/no-explicit-any data, headerStart, headerEnd) { const hcolonIndex = data.indexOf(":", headerStart); const headerName = data.substring(headerStart, hcolonIndex).trim(); const headerValue = data.substring(hcolonIndex + 1, headerEnd).trim(); // eslint-disable-next-line @typescript-eslint/no-explicit-any let parsed; // If header-field is well-known, parse it. switch (headerName.toLowerCase()) { case "via": case "v": message.addHeader("via", headerValue); if (message.getHeaders("via").length === 1) { parsed = message.parseHeader("Via"); if (parsed) { message.via = parsed; message.viaBranch = parsed.branch; } } else { parsed = 0; } break; case "from": case "f": message.setHeader("from", headerValue); parsed = message.parseHeader("from"); if (parsed) { message.from = parsed; message.fromTag = parsed.getParam("tag"); } break; case "to": case "t": message.setHeader("to", headerValue); parsed = message.parseHeader("to"); if (parsed) { message.to = parsed; message.toTag = parsed.getParam("tag"); } break; case "record-route": parsed = Grammar.parse(headerValue, "Record_Route"); if (parsed === -1) { parsed = undefined; break; } if (!(parsed instanceof Array)) { parsed = undefined; break; } parsed.forEach((header) => { message.addHeader("record-route", headerValue.substring(header.position, header.offset)); message.headers["Record-Route"][message.getHeaders("record-route").length - 1].parsed = header.parsed; }); break; case "call-id": case "i": message.setHeader("call-id", headerValue); parsed = message.parseHeader("call-id"); if (parsed) { message.callId = headerValue; } break; case "contact": case "m": parsed = Grammar.parse(headerValue, "Contact"); if (parsed === -1) { parsed = undefined; break; } if (!(parsed instanceof Array)) { parsed = undefined; break; } parsed.forEach((header) => { message.addHeader("contact", headerValue.substring(header.position, header.offset)); message.headers.Contact[message.getHeaders("contact").length - 1].parsed = header.parsed; }); break; case "content-length": case "l": message.setHeader("content-length", headerValue); parsed = message.parseHeader("content-length"); break; case "content-type": case "c": message.setHeader("content-type", headerValue); parsed = message.parseHeader("content-type"); break; case "cseq": message.setHeader("cseq", headerValue); parsed = message.parseHeader("cseq"); if (parsed) { message.cseq = parsed.value; } if (message instanceof IncomingResponseMessage) { message.method = parsed.method; } break; case "max-forwards": message.setHeader("max-forwards", headerValue); parsed = message.parseHeader("max-forwards"); break; case "www-authenticate": message.setHeader("www-authenticate", headerValue); parsed = message.parseHeader("www-authenticate"); break; case "proxy-authenticate": message.setHeader("proxy-authenticate", headerValue); parsed = message.parseHeader("proxy-authenticate"); break; case "refer-to": case "r": message.setHeader("refer-to", headerValue); parsed = message.parseHeader("refer-to"); if (parsed) { message.referTo = parsed; } break; default: // Do not parse this header. message.addHeader(headerName.toLowerCase(), headerValue); parsed = 0; } if (parsed === undefined) { return { error: "error parsing header '" + headerName + "'" }; } else { return true; } } Parser.parseHeader = parseHeader; function parseMessage(data, logger) { let headerStart = 0; let headerEnd = data.indexOf("\r\n"); if (headerEnd === -1) { logger.warn("no CRLF found, not a SIP message, discarded"); return; } // Parse first line. Check if it is a Request or a Reply. const firstLine = data.substring(0, headerEnd); const parsed = Grammar.parse(firstLine, "Request_Response"); let message; if (parsed === -1) { logger.warn('error parsing first line of SIP message: "' + firstLine + '"'); return; } else if (!parsed.status_code) { message = new IncomingRequestMessage(); message.method = parsed.method; message.ruri = parsed.uri; } else { message = new IncomingResponseMessage(); message.statusCode = parsed.status_code; message.reasonPhrase = parsed.reason_phrase; } message.data = data; headerStart = headerEnd + 2; // Loop over every line in data. Detect the end of each header and parse // it or simply add to the headers collection. let bodyStart; // eslint-disable-next-line no-constant-condition while (true) { headerEnd = getHeader(data, headerStart); // The SIP message has normally finished. if (headerEnd === -2) { bodyStart = headerStart + 2; break; } else if (headerEnd === -1) { // data.indexOf returned -1 due to a malformed message. logger.error("malformed message"); return; } const parsedHeader = parseHeader(message, data, headerStart, headerEnd); if (parsedHeader && parsedHeader !== true) { logger.error(parsedHeader.error); return; } headerStart = headerEnd + 2; } // RFC3261 18.3. // If there are additional bytes in the transport packet // beyond the end of the body, they MUST be discarded. if (message.hasHeader("content-length")) { message.body = data.substr(bodyStart, Number(message.getHeader("content-length"))); } else { message.body = data.substring(bodyStart); } return message; } Parser.parseMessage = parseMessage; })(Parser = Parser || (Parser = {}));