From ab45edc242519e71ed6ed8f6faf19892dd07dd59 Mon Sep 17 00:00:00 2001 From: dletta Date: Wed, 16 Oct 2024 16:04:08 -0500 Subject: [PATCH 1/6] WIP - Rewriting Session Reassembly --- lib/{inputs => }/input_ws.js | 2 +- package-lock.json | 7 +- .../app_audiocodes/filter_app_audiocodes.js | 933 +++++++++++------- 3 files changed, 572 insertions(+), 370 deletions(-) rename lib/{inputs => }/input_ws.js (98%) diff --git a/lib/inputs/input_ws.js b/lib/input_ws.js similarity index 98% rename from lib/inputs/input_ws.js rename to lib/input_ws.js index ae7d9772..157a47de 100644 --- a/lib/inputs/input_ws.js +++ b/lib/input_ws.js @@ -45,7 +45,7 @@ InputWebsocket.prototype.start = function(callback) { this.emit('data', parsed); }.bind(this), function(data) { this.emit('data', { - 'message': data.trim(), + 'message': data, 'host': ws._socket.remoteAddress, 'ws_port': this.port, 'type': this.type, diff --git a/package-lock.json b/package-lock.json index cf153992..ceb2bb30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,12 @@ "csv-parser": "^2.3.3", "log4node": "0.1.6", "lru-cache": "4.1.x", - "maxmind-geolite-mirror": "1.3.x", "mkdirp": "0.5.1", "optimist": "0.6.1", - "requireg": "^0.2.1", - "ws": "8.18.0" + "requireg": "^0.2.1" + }, + "bin": { + "pastash": "bin/pastash" }, "devDependencies": { "istanbul": "0.4.x", diff --git a/plugins/filters/app_audiocodes/filter_app_audiocodes.js b/plugins/filters/app_audiocodes/filter_app_audiocodes.js index f38f94bc..dafa95f3 100644 --- a/plugins/filters/app_audiocodes/filter_app_audiocodes.js +++ b/plugins/filters/app_audiocodes/filter_app_audiocodes.js @@ -4,416 +4,617 @@ */ var base_filter = require('@pastash/pastash').base_filter, - util = require('util'), - logger = require('@pastash/pastash').logger + util = require('util'), + logger = require('@pastash/pastash').logger var fs = require('fs'), - ini = require('ini') + ini = require('ini') var moment = require('moment') var LRU = require("lru-cache"), - sidcache = new LRU(1000), - expire = 10000 * 60 * 60 + sid_cache = new LRU(1000), + expire = 10000 * 60 * 60 function FilterAppAudiocodes() { - base_filter.BaseFilter.call(this); - this.mergeConfig({ - name: 'AppAudiocodes', - optional_params: ['correlation_hdr','bypass', 'debug', 'file_debug', 'logs', 'localip', 'localport', 'correlation_contact', 'qos', 'autolocal', 'version', 'ini', 'iniwatch'], - default_values: { - 'correlation_contact': false, - 'correlation_hdr': false, - 'debug': false, - 'file_debug': false, - 'bypass': false, - 'logs': false, - 'qos': true, - 'autolocal': false, - 'localip': '127.0.0.1', - 'localport': 5060, - 'version': '7.20A.260.012', - 'ini': false, - 'iniwatch': false - }, - start_hook: this.start, - }); + base_filter.BaseFilter.call(this); + this.mergeConfig({ + name: 'AppAudiocodes', + optional_params: ['correlation_hdr','bypass', 'debug', 'file_debug', 'logs', 'localip', 'localport', 'correlation_contact', 'qos', 'autolocal', 'version', 'ini', 'iniwatch'], + default_values: { + 'correlation_contact': false, + 'correlation_hdr': false, + 'debug': false, + 'file_debug': false, + 'bypass': false, + 'logs': false, + 'qos': true, + 'autolocal': false, + 'localip': '127.0.0.1', + 'localport': 5060, + 'version': '7.20A.260.012', + 'ini': false, + 'iniwatch': false + }, + start_hook: this.start, + }); } util.inherits(FilterAppAudiocodes, base_filter.BaseFilter); FilterAppAudiocodes.prototype.start = function(callback) { - logger.info('Initialized App Audiocodes SysLog to SIP/HEP parser'); - if (this.ini){ - logger.info('Reading INI file to resolver...', this.ini); - try { - this.resolver = parseIni(this.ini); - logger.info('INI Loaded '+this.resolver.interfaces.lenght +' Interfaces'); - logger.info('INI Loaded '+this.resolver.sip.lenght +' SIP Profiles'); - if (this.debug) console.log(this.resolver); - if (this.iniwatch) watchIni(this.ini, this.resolver); - } catch(err) { logger.error(err) } - } - - this.postProcess = function(ipcache,last,type){ - if(!last||!ipcache) return; - last = last.replace(/#012/g, '\r\n').trim() + "\r\n\r\n"; + logger.info('Initialized App Audiocodes SysLog to SIP/HEP parser'); + if (this.ini){ + logger.info('Reading INI file to resolver...', this.ini); + try { + this.resolver = parseIni(this.ini); + logger.info('INI Loaded '+this.resolver.interfaces.lenght +' Interfaces'); + logger.info('INI Loaded '+this.resolver.sip.lenght +' SIP Profiles'); + if (this.debug) console.log(this.resolver); + if (this.iniwatch) watchIni(this.ini, this.resolver); + } catch(err) { logger.error(err) } + } + + this.postProcess = function(ipcache,last,type){ + if(!last||!ipcache) return; + last = last.replace(/#012/g, '\r\n').trim() + "\r\n\r\n"; var rcinfo = { - type: 'HEP', - version: 3, - payload_type: type ? 'LOG' :'SIP', - ip_family: 2, - protocol: 17, - proto_type: type || 1, - correlation_id: ipcache.callId || '', - srcIp: ipcache.srcIp || this.localip, - srcPort: ipcache.srcPort || 0, - dstIp: ipcache.dstIp || this.localip, - dstPort: ipcache.dstPort || 0, - time_sec: ipcache.ts || parseInt(new Date().getTime() / 1000), - time_usec: ipcache.usec || new Date().getMilliseconds() - }; - - // EXTRACT CORRELATION HEADER, IF ANY - if (this.correlation_hdr && rcinfo.proto_type == 1 && last.startsWith('INVITE')) { - var xcid = last.match(this.correlation_hdr+":\s?(.*)\r\n\r\n"); + type: 'HEP', + version: 3, + payload_type: type ? 'LOG' :'SIP', + ip_family: 2, + protocol: 17, + proto_type: type || 1, + correlation_id: ipcache.callId || '', + srcIp: ipcache.srcIp || this.localip, + srcPort: ipcache.srcPort || 0, + dstIp: ipcache.dstIp || this.localip, + dstPort: ipcache.dstPort || 0, + time_sec: ipcache.ts || parseInt(new Date().getTime() / 1000), + time_usec: ipcache.usec || new Date().getMilliseconds() + }; + + // EXTRACT CORRELATION HEADER, IF ANY + if (this.correlation_hdr && rcinfo.proto_type == 1 && last.startsWith('INVITE')) { + var xcid = last.match(this.correlation_hdr+":\s?(.*)\r\n\r\n"); if (xcid && xcid[1]) rcinfo.correlation_id = xcid[1].trim(); if (this.debug) logger.info('auto correlation pick', rcinfo.correlation_id); - } + } - if (this.correlation_contact && rcinfo.proto_type == 1 && last.startsWith('INVITE')) { - var extract = /x-c=(.*?)\//.exec(last); - if (extract[1]) { - rcinfo.correlation_id = extract[1]; - if (this.debug) logger.info('auto correlation pick', rcinfo.correlation_id); - } - } - - if (last.indexOf('2.0/TCP') !== -1 || last.indexOf('2.0/TLS') !== -1 ){ - rcinfo.protocol = 6; - if (this.autolocal) rcinfo.dstPort = 5061; + if (this.correlation_contact && rcinfo.proto_type == 1 && last.startsWith('INVITE')) { + var extract = /x-c=(.*?)\//.exec(last); + if (extract[1]) { + rcinfo.correlation_id = extract[1]; + if (this.debug) logger.info('auto correlation pick', rcinfo.correlation_id); + } + } + + if (last.indexOf('2.0/TCP') !== -1 || last.indexOf('2.0/TLS') !== -1 ){ + rcinfo.protocol = 6; + if (this.autolocal) rcinfo.dstPort = 5061; } if (last && rcinfo) { - var data = { payload: last, rcinfo: rcinfo }; - console.log('FINAL DATA') - console.log(data.payload) - return data; + var data = { payload: last, rcinfo: rcinfo }; + console.log('FINAL DATA') + console.log(data.payload) + return data; } - } - callback(); + } + callback(); }; +/** + * Session Manager + * Object to manage SIP Sessions in cache + */ +let sessionManager = { + evaluateMessage: function (line) { + var seqObj = /.*\[S=(?[0-9]+)\].*/.exec(line) + var sidObj = /\[SID=(?.*?)\]/.exec(line) + if (!seqObj || !sidObj) { + if (this.bypass) return data + throw new Error(`Invalid SIP Message, missing SID or SEQ in Line: ${line}`) + } + let seq = seqObj[1] + let sid = sidObj[1] + let session = {} + if (this.findSession(sid)) { + if (this.debug) logger.info('FOUND SESSION', sid) + session = this.addFragment(sid, seq, line) + } else { + if (this.debug) logger.info('NEW SESSION', sid) + session = this.createSession(sid, seq, line) + } + return session + }, + findSession: function (sid) { + if(sid_cache.has(sid)) { + return sid_cache.get(sid) + } else { + return false + } + }, + createSession:function(sid, seq, message) { + let session = { + sid: sid, + seq: seq, + currentMessage: message, + payloads: [{message: message, seq: seq}] + } + sid_cache.set(sid, session) + return session + }, + addFragment:function(sid, seq, message) { + let session = this.findSession(sid) + /* TODO: add message to payload in sequence */ + if(session) { + session.seq = seq + session.currentMessage = message + session.payloads.push({message: message, seq: seq}) + sid_cache.set(sid, session) + // this.checkComplete(session) + return session + } else { + return false + } + }, + checkComplete:function(session) { + let check = session.payload.match(/\r\n\r\n/g) + console.log('CHECK COMPLETE', check) + if (check.length < 1) { + console.log('NOT complete') + return false + } + return true + } +} + var last = ''; var ipcache = {}; var aliases = {}; var hold; var cache; -var seq; +var seqN; +/** + * Receives a buffer from an input or filter + * @param {buffer} data + * @returns {object} processed data + */ FilterAppAudiocodes.prototype.process = function(data) { + /* Message to String*/ + var line = data.message.toString(); + /* Debug for when we send a text file for debug */ + if (this.file_debug) { + console.log('RECEIVED LINE') + console.log(JSON.stringify(line)) + line = line.replace(/\\n/g, '\n') + line = line.replace(/\\r/g, '\r') + line = line.replace(/"/g, '') + line = line.replace(/\\\"/g, '\"') + console.log('Fixed Line from File Input to syslog input') + console.log(line) + } + + if (this.debug) console.info('DEBUG', line) - var line = data.message.toString(); - if (this.file_debug) { - console.log('RECEIVED LINE') - console.log(JSON.stringify(line)) - line = line.replace(/\\n/g, '\n') - line = line.replace(/\\r/g, '\r') - line = line.replace(/"/g, '') - line = line.replace(/\\\"/g, '\"') - console.log('Fixed Line from File Input to syslog input') - console.log(line) - } - var ipcache = {}; - if (this.debug) console.info('DEBUG', line); - if (this.version === '7.40A.500') { - var message = /.*\[S=([0-9]+)\].*?\[SID=.*?\]\s?(.*)\[Time:.*\]/g - } else { - var message = /^.*?\[S=([0-9]+)\].*?\[SID=.*?\]\s?(.*)\[Time:.*\]$/ - } - var test = message.exec(line.replace(/\r\n/g, '#012')); - if(hold && line && test) { - if (this.debug) logger.error('Next packet number', test[1]) - if (parseInt(test[1]) == seq + 1) { + /* Adjust Regexp for 7.40A.500 format*/ + /* + if (this.version === '7.40A.500') { + var message = /.*\[S=([0-9]+)\].*?\[SID=.*?\]\s?(.*)\[Time:.*\]/g + } else { + var message = /^.*?\[S=([0-9]+)\].*?\[SID=.*?\]\s?(.*)\[Time:.*\]$/ + } + + + var test = message.exec(line.replace(/\r\n/g, '#012')) + + if(hold && line && test) { + if (this.debug) logger.error('Next packet number', test[1]) + if (parseInt(test[1]) == seq + 1) { line = cache + ( test ? test[2] : '') hold = false cache = '' if (this.debug) console.info('reassembled line', line) - } - } - - line = line.replace(/\r\n/g, '#012'); - - var ids = /\[SID=(?.*?):(?.*?):(?.*?)\]/.exec(line) || []; - if (this.debug) logger.error('SESSION SID',ids[3]); - - if (line.indexOf('Incoming SIP Message') !== -1) { - try { - // var regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*--- #012(.*)#012 #012 #012(.*) \[Time:(.*)-(.*)@(.*)\]/g; - var regex; - if (this.version === '7.40A.500') { - regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO\(#[0-99]\) ---- (.*)/g; //7.40A.500.357 - } else if (this.version == '7.20A.256.511') { - regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*--- (.*)(.*)/g; //7.20A.256.511 - } else { - regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*---\s?#012(.*)#012\s?#012(.*)/g; //7.20A.260.012 - } - - if (this.resolver){ - var aliasregex = /SIPInterface #([^\s]+) \((.*)\) (.*) TO/g; - var interface = aliasregex.exec(line) || false; - if (this.resolver && interface){ - var alias = interface[1]; //0 - var group = interface[2]; //some-group - var proto = interface[3]; //UDP,TCP,TLS - - var ifname = this.resolver.sip[group] ? this.resolver.sip[group].NetworkInterface : false; - if (ifname){ - var xlocalip = this.resolver.ifs[ifname] ? this.resolver.ifs[ifname] : false; - var xlocalport = this.resolver.sip[group] ? this.resolver.sip[group][proto+"Port"] : false; - if (this.debug) console.log('!!!!!!!!!!!!!!!!! IN IFNAME MATCH', group, ifname, alias, proto, xlocalip, xlocalport); - } else { - if (this.debug) console.log('!!!!!!!!!!!!!!!!! IN IFNAME FAILURE', group, ifname, alias, proto); - } - } - } - - var ip = regex.exec(line); - if (!ip) { - cache = line.replace(/\[Time.*\]$/,''); - hold = true; - var regpackid = /.*\[S=([0-9]+)\].*/.exec(line); - seq = parseInt(regpackid[1]); - if (this.debug) logger.error('Cached packet number', seq, line); - logger.error('failed parsing Incoming SIP. Cache on!'); - if (this.bypass) return data; - } else { - if (xlocalip && xlocalport){ - ipcache.dstIp = xlocalip; - ipcache.dstPort = parseInt(xlocalport); - } else if (ip[3]) { - /* convert alias to IP:port */ - ipcache.dstIp = aliases[0] || this.localip; - ipcache.dstPort = aliases[1] || this.localport; - } - ipcache.srcIp = ip[2].split(':')[0]; - ipcache.srcPort = ip[2].split(':')[1]; - last = ip[5]; - last += '#012 #012'; - var callid = last.match(/call-id:\s?(.*?)\s?#012/i) || []; - ipcache.callId = callid[1] || ids[3] || ''; - // Cache SID to Call-ID correlation - sidcache.set(ids[3], ipcache.callId, expire); - // Seek final fragment - if(ip[6]?.includes(' SIP Message ') && this.version !== '7.40A.500'){ - hold = true; - cache = line.replace(/\[Time.*\]$/,''); - } - return this.postProcess(ipcache,last); - } - } catch(e) { - logger.error(e, line); } + }*/ - } else if (line.indexOf('Outgoing SIP Message') !== -1) { - try { - var regex; - if (this.version === '7.40A.500') { - regex = /(.*) ---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO\(#.*\) ---- (.*)/g; //7.40A.500.357 - } else if (this.version == '7.20A.256.511') { - regex = /(.*)---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO.*--- (.*)(.*)/g; //7.20A.256.511 - } else { - regex = /(.*)---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO.*---\s?#012(.*)#012\s?#012 (.*)/g; //7.20A.260.012 - } - - if (this.resolver) { - var aliasregex = /SIPInterface #([^\s]+) \((.*)\) (.*) TO/g; - var interface = aliasregex.exec(line) || false; - if (this.resolver && interface) { - var alias = interface[1]; //0 - var group = interface[2]; //some-group - var proto = interface[3]; //UDP,TCP,TLS - - var ifname = this.resolver.sip[group] ? this.resolver.sip[group].NetworkInterface : false; - if (ifname) { - var xlocalip = this.resolver.ifs[ifname] ? this.resolver.ifs[ifname] : false; - var xlocalport = this.resolver.sip[group] ? this.resolver.sip[group][proto+"Port"] : false; - if (this.debug) console.log('!!!!!!!!!!!!!!!!! OUT IFNAME MATCH', group, ifname, alias, proto, xlocalip, xlocalport); - } else { - if (this.debug) console.log('!!!!!!!!!!!!!!!!! OUT IFNAME FAILURE', group, ifname, alias, proto); - } - } - } - - var ip = regex.exec(line); - if (!ip) { - cache = line.replace(/\[Time.*\]$/,''); - hold = true; - var regpackid = /.*\[S=([0-9]+)\].*/.exec(line); - seq = parseInt(regpackid[1]); - if (this.debug) logger.error('Cached packet number', seq, line); - logger.error('failed parsing Outgoing SIP. Cache on!'); - if (this.bypass) return data; - } else { - if (xlocalip && xlocalport){ - ipcache.srcIp = xlocalip; - ipcache.srcPort = parseInt(xlocalport); - } else if (ip[3]) { - /* convert alias to IP:port */ - ipcache.srcIp = aliases[0] || this.localip; - ipcache.srcPort = aliases[1] || this.localport; - } - ipcache.dstIp = ip[2].split(':')[0]; - ipcache.dstPort = ip[2].split(':')[1]; - last = ip[5]; - last += '#012 #012'; - var callid = last.match(/call-id:\s?(.*?)\s?#012/i) || []; - ipcache.callId = callid[1] || ids[3] || ''; - // Cache SID to Call-ID correlation - sidcache.set(ids[3], ipcache.callId, expire); - // Seek final fragment - if(ip[6]?.includes(' SIP Message ') && this.version !== '7.40A.500'){ - hold = true; - cache = line.replace(/\[Time.*\]$/,''); - } - return this.postProcess(ipcache,last); - } - } catch(e) { - logger.error(e, line); - } - } else if (this.autolocal && line.indexOf('Local IP Address =') !== -1) { - var local = line.match(/Local IP Address = (.*?):(.*?),/) || []; - if(local[1]) this.localip = local[1]; - if(local[2]) this.localport = local[2]; - } else if (line.indexOf('CALL_END ') !== -1 && this.logs) { - // Parser TBD page 352 @ https://www.audiocodes.com/media/10312/ltrt-41548-mediant-software-sbc-users-manual-ver-66.pdf - var cdr = line.split(/(\s+\|)/).filter( function(e) { return e.trim().length > 1; } ) - ipcache.callId = cdr[3] || ''; - if (this.debug) logger.info('CALL_END', cdr, ipcache); - if (this.logs) return this.postProcess(ipcache,JSON.stringify(cdr),100); - } else if (line.indexOf('MEDIA_END ') !== -1 && this.qos) { - // Parsed TBD page 353 @ https://www.audiocodes.com/media/10312/ltrt-41548-mediant-software-sbc-users-manual-ver-66.pdf - var qos = line.split(/(\s+\|)/).filter( function(e) { return e.trim().length > 1; } ) - if (qos.length == 25){ - qos.splice(15, 1); - qos.splice(5, 1); - } - logger.info('!!!!!!!!!!!!!! DEBUG MEDIA', qos, qos.length); - if(qos && qos[2] && qos[21]){ - ipcache.callId = qos[2] || ''; - var response = []; - // A-LEG - ipcache.srcIp = qos[7]; - ipcache.srcPort = parseInt(qos[8]); - ipcache.dstIp = qos[9]; - ipcache.dstPort = parseInt(qos[10]); - var local_report = { - "CORRELATION_ID": qos[2], - "RTP_SIP_CALL_ID": qos[2], - "MOS": 4.5 * parseInt(qos[17]) / 127, - "TOTAL_PK": parseInt(qos[11]), - "CODEC_NAME": qos[5], - "DIR":0, - "REPORT_NAME": qos[4] + "_" + qos[7] + ":" + qos[8], - "PARTY":0, - "TYPE":"HANGUP" - }; - response.push(this.postProcess(ipcache,JSON.stringify(local_report),35)); - // B-LEG - ipcache.srcIp = qos[9]; - ipcache.srcPort = parseInt(qos[10]); - ipcache.dstIp = qos[7]; - ipcache.dstPort = parseInt(qos[8]); - var remote_report = { - "CORRELATION_ID": qos[2], - "RTP_SIP_CALL_ID": qos[2], - "MOS": 4.5 * parseInt(qos[18]) / 127, - "TOTAL_PK": parseInt(qos[12]), - "CODEC_NAME": qos[5], - "DIR":1, - "REPORT_NAME": qos[4] + "_" + qos[9] + ":" + qos[10], - "PARTY":1, - "TYPE":"HANGUP" - }; - response.push(this.postProcess(ipcache,JSON.stringify(remote_report),35)); - if (this.debug) logger.info('MEDIA_END', response); - if (this.qos) return response; - } else { - logger.error('missing media parameters', qos); - } - } else if (ids[3] && !hold && this.logs) { - if (this.bypass) return data; - // Prepare SIP LOG - if (this.logs) { - ipcache.callId = sidcache.get(ids[3]) || ids[3] || ''; - ipcache.srcIp = this.localip || '127.0.0.1'; - ipcache.srcPort = 514 - ipcache.dstIp = this.localip || '127.0.0.1'; - ipcache.dstPort = 514 - return this.postProcess(ipcache,line,100); - } - } else { - // Discard - if (this.bypass) return data; - } -}; + /* Prepare line for processing */ + line = line.replace(/\r\n/g, '#012') + + /* Create Session or append to Session */ + let session = sessionManager.evaluateMessage(line) + + if (this.debug) logger.error('SESSION SID',session.sid) + + this.sipRouter(session) +} exports.create = function() { - return new FilterAppAudiocodes(); -}; + return new FilterAppAudiocodes() +} + +/* Previous Router + + if (line.indexOf('Incoming SIP Message') !== -1) { + try { + // Set regex based on version + // var regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*--- #012(.*)#012 #012 #012(.*) \[Time:(.*)-(.*)@(.*)\]/g; + var regex; + if (this.version === '7.40A.500') { + regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO\(#[0-99]\) ---- (.*)/g; //7.40A.500.357 + } else if (this.version == '7.20A.256.511') { + regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*--- (.*)(.*)/g; //7.20A.256.511 + } else { + regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*---\s?#012(.*)#012\s?#012(.*)/g; //7.20A.260.012 + } + + if (this.resolver){ + var aliasregex = /SIPInterface #([^\s]+) \((.*)\) (.*) TO/g; + var interface = aliasregex.exec(line) || false; + if (this.resolver && interface){ + var alias = interface[1]; //0 + var group = interface[2]; //some-group + var proto = interface[3]; //UDP,TCP,TLS + + var ifname = this.resolver.sip[group] ? this.resolver.sip[group].NetworkInterface : false; + if (ifname){ + var xlocalip = this.resolver.ifs[ifname] ? this.resolver.ifs[ifname] : false; + var xlocalport = this.resolver.sip[group] ? this.resolver.sip[group][proto+"Port"] : false; + if (this.debug) console.log('!!!!!!!!!!!!!!!!! IN IFNAME MATCH', group, ifname, alias, proto, xlocalip, xlocalport); + } else { + if (this.debug) console.log('!!!!!!!!!!!!!!!!! IN IFNAME FAILURE', group, ifname, alias, proto); + } + } + } + // Apply Regexp to line + var ip = regex.exec(line); + console.log('PROCESSED') + console.log(ip) + if (!ip) { + console.log('BAD LINE', line) + cache = line.replace(/\[Time.*\]$/,''); + + hold = true; + var regpackid = /.*\[S=([0-9]+)\].\*\/.exec(line); + seq = parseInt(regpackid[1]); + if (this.debug) logger.error('Cached packet number', seq, line); + logger.error('failed parsing Incoming SIP. Cache on!'); + if (this.bypass) return data; + } else { + if (xlocalip && xlocalport){ + ipcache.dstIp = xlocalip; + ipcache.dstPort = parseInt(xlocalport); + } else if (ip[3]) { + // convert alias to IP:port + ipcache.dstIp = aliases[0] || this.localip; + ipcache.dstPort = aliases[1] || this.localport; + } + ipcache.srcIp = ip[2].split(':')[0]; + ipcache.srcPort = ip[2].split(':')[1]; + last = ip[5]; + last += '#012 #012'; + var callid = last.match(/call-id:\s?(.*?)\s?#012/i) || []; + ipcache.callId = callid[1] || sid[1] || ''; + // Cache SID to Call-ID correlation + sid_cache.set(sid[1], ipcache.callId, expire); + // Seek final fragment + if(ip[6]?.includes(' SIP Message ') && this.version !== '7.40A.500'){ + hold = true; + cache = line.replace(/\[Time.*\]$/,''); + } + return this.postProcess(ipcache,last); + } + } catch(e) { + logger.error(e, line); + } + + } else if (line.indexOf('Outgoing SIP Message') !== -1) { + try { + var regex; + if (this.version === '7.40A.500') { + regex = /(.*) ---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO\(#.*\) ---- (.*)/g; //7.40A.500.357 + } else if (this.version == '7.20A.256.511') { + regex = /(.*)---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO.*--- (.*)(.*)/g; //7.20A.256.511 + } else { + regex = /(.*)---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO.*---\s?#012(.*)#012\s?#012 (.*)/g; //7.20A.260.012 + } + + if (this.resolver) { + var aliasregex = /SIPInterface #([^\s]+) \((.*)\) (.*) TO/g; + var interface = aliasregex.exec(line) || false; + if (this.resolver && interface) { + var alias = interface[1]; //0 + var group = interface[2]; //some-group + var proto = interface[3]; //UDP,TCP,TLS + + var ifname = this.resolver.sip[group] ? this.resolver.sip[group].NetworkInterface : false; + if (ifname) { + var xlocalip = this.resolver.ifs[ifname] ? this.resolver.ifs[ifname] : false; + var xlocalport = this.resolver.sip[group] ? this.resolver.sip[group][proto+"Port"] : false; + if (this.debug) console.log('!!!!!!!!!!!!!!!!! OUT IFNAME MATCH', group, ifname, alias, proto, xlocalip, xlocalport); + } else { + if (this.debug) console.log('!!!!!!!!!!!!!!!!! OUT IFNAME FAILURE', group, ifname, alias, proto); + } + } + } + + var ip = regex.exec(line); + if (!ip) { + cache = line.replace(/\[Time.*\]$/,''); + hold = true; + var regpackid = /.*\[S=([0-9]+)\].\*\/.exec(line); + seq = parseInt(regpackid[1]); + if (this.debug) logger.error('Cached packet number', seq, line); + logger.error('failed parsing Outgoing SIP. Cache on!'); + if (this.bypass) return data; + } else { + if (xlocalip && xlocalport){ + ipcache.srcIp = xlocalip; + ipcache.srcPort = parseInt(xlocalport); + } else if (ip[3]) { + // convert alias to IP:port + ipcache.srcIp = aliases[0] || this.localip; + ipcache.srcPort = aliases[1] || this.localport; + } + ipcache.dstIp = ip[2].split(':')[0]; + ipcache.dstPort = ip[2].split(':')[1]; + last = ip[5]; + last += '#012 #012'; + var callid = last.match(/call-id:\s?(.*?)\s?#012/i) || []; + ipcache.callId = callid[1] || sid[1] || ''; + // Cache SID to Call-ID correlation + sid_cache.set(sid[1], ipcache.callId, expire); + // Seek final fragment + if(ip[6]?.includes(' SIP Message ') && this.version !== '7.40A.500'){ + hold = true; + cache = line.replace(/\[Time.*\]$/,''); + } + return this.postProcess(ipcache,last); + } + } catch(e) { + logger.error(e, line); + } + } else if (this.autolocal && line.indexOf('Local IP Address =') !== -1) { + var local = line.match(/Local IP Address = (.*?):(.*?),/) || []; + if(local[1]) this.localip = local[1]; + if(local[2]) this.localport = local[2]; + } else if (line.indexOf('CALL_END ') !== -1 && this.logs) { + // Parser TBD page 352 @ https://www.audiocodes.com/media/10312/ltrt-41548-mediant-software-sbc-users-manual-ver-66.pdf + var cdr = line.split(/(\s+\|)/).filter( function(e) { return e.trim().length > 1; } ) + ipcache.callId = cdr[3] || ''; + if (this.debug) logger.info('CALL_END', cdr, ipcache); + if (this.logs) return this.postProcess(ipcache,JSON.stringify(cdr),100); + } else if (line.indexOf('MEDIA_END ') !== -1 && this.qos) { + // Parsed TBD page 353 @ https://www.audiocodes.com/media/10312/ltrt-41548-mediant-software-sbc-users-manual-ver-66.pdf + var qos = line.split(/(\s+\|)/).filter( function(e) { return e.trim().length > 1; } ) + if (qos.length == 25){ + qos.splice(15, 1); + qos.splice(5, 1); + } + logger.info('!!!!!!!!!!!!!! DEBUG MEDIA', qos, qos.length); + if(qos && qos[2] && qos[21]){ + ipcache.callId = qos[2] || ''; + var response = []; + // A-LEG + ipcache.srcIp = qos[7]; + ipcache.srcPort = parseInt(qos[8]); + ipcache.dstIp = qos[9]; + ipcache.dstPort = parseInt(qos[10]); + var local_report = { + "CORRELATION_ID": qos[2], + "RTP_SIP_CALL_ID": qos[2], + "MOS": 4.5 * parseInt(qos[17]) / 127, + "TOTAL_PK": parseInt(qos[11]), + "CODEC_NAME": qos[5], + "DIR":0, + "REPORT_NAME": qos[4] + "_" + qos[7] + ":" + qos[8], + "PARTY":0, + "TYPE":"HANGUP" + }; + response.push(this.postProcess(ipcache,JSON.stringify(local_report),35)); + // B-LEG + ipcache.srcIp = qos[9]; + ipcache.srcPort = parseInt(qos[10]); + ipcache.dstIp = qos[7]; + ipcache.dstPort = parseInt(qos[8]); + var remote_report = { + "CORRELATION_ID": qos[2], + "RTP_SIP_CALL_ID": qos[2], + "MOS": 4.5 * parseInt(qos[18]) / 127, + "TOTAL_PK": parseInt(qos[12]), + "CODEC_NAME": qos[5], + "DIR":1, + "REPORT_NAME": qos[4] + "_" + qos[9] + ":" + qos[10], + "PARTY":1, + "TYPE":"HANGUP" + }; + response.push(this.postProcess(ipcache,JSON.stringify(remote_report),35)); + if (this.debug) logger.info('MEDIA_END', response); + if (this.qos) return response; + } else { + logger.error('missing media parameters', qos); + } + } else if (sid && !hold && this.logs) { + if (this.bypass) return data; + // Prepare SIP LOG + if (this.logs) { + ipcache.callId = sid_cache.get(sid) || sid || ''; + ipcache.srcIp = this.localip || '127.0.0.1'; + ipcache.srcPort = 514 + ipcache.dstIp = this.localip || '127.0.0.1'; + ipcache.dstPort = 514 + return this.postProcess(ipcache,line,100); + } + } else { + // Discard + if (this.bypass) return data; + } + +*/ + +FilterAppAudiocodes.prototype.sipRouter = function(session) { + console.log('Routing SIP Session') + if (session.currentMessage.indexOf('Incoming SIP Message') !== -1) { + console.log('Incoming SIP Message') + try { + /* Set regex based on version + var regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*--- #012(.*)#012 #012 #012(.*) \[Time:(.*)-(.*)@(.*)\]/g; */ + let regex + if (this.version === '7.40A.500') { + regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO\(#[0-99]\) ---- (.*)/g; //7.40A.500.357 + } else if (this.version == '7.20A.256.511') { + regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*--- (.*)(.*)/g; //7.20A.256.511 + } else { + regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*---\s?#012(.*)#012\s?#012(.*)/g; //7.20A.260.012 + } + + let resolvedObj = false + if (this.resolver){ + resolvedObj = this.invokeResolver(session) + } + // Apply Regexp to line + let ip = regex.exec(session.currentMessage) + if (!ip) { + console.log('BAD LINE', session.currentMessage) + cache = session.currentMessage.replace(/\[Time.*\]$/,''); + + hold = true; + var regpackid = /.*\[S=([0-9]+)\].*/.exec(session.currentMessage); + seqN = parseInt(regpackid[1]); + if (this.debug) logger.error('Cached packet number', seqN, session.currentMessage); + logger.error('failed parsing Incoming SIP. Cache on!'); + if (this.bypass) return data; + } else { + this.handleSIP(session, ip, 'incoming') + } + } catch (err) { + logger.error(err, line) + } + } else if (session.currentMessage.indexOf('Outgoing SIP Message') !== -1) { + console.log('Outgoing SIP Message') + try { + let regex; + if (this.version === '7.40A.500') { + regex = /(.*) ---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO\(#.*\) ---- (.*)/g; //7.40A.500.357 + } else if (this.version == '7.20A.256.511') { + regex = /(.*)---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO.*--- (.*)(.*)/g; //7.20A.256.511 + } else { + regex = /(.*)---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO.*---\s?#012(.*)#012\s?#012 (.*)/g; //7.20A.260.012 + } + + let resolvedObj = false + if (this.resolver) { + resolvedObj = this.invokeResolver(session) + } + + // Apply Regexp to line + let ip = regex.exec(session.currentMessage) + if (!ip) { + console.log('BAD LINE', session.currentMessage) + cache = session.currentMessage.replace(/\[Time.*\]$/,''); + hold = true; + var regpackid = /.*\[S=([0-9]+)\].*/.exec(session.currentMessage); + seqN = parseInt(regpackid[1]); + if (this.debug) logger.error('Cached packet number', seqN, session.currentMessage); + logger.error('failed parsing Outgoing SIP. Cache on!'); + if (this.bypass) return data; + } else { + this.handleSIP(session, ip, 'outgoing') + } + + } catch (err) { + logger.error(err, line) + } + } else if (this.autolocal && session.currentMessage.indexOf('Local IP Address =') !== -1) { + console.log('Local IP Address') + } else if (session.currentMessage.indexOf('CALL_END ') !== -1) { + console.log('CALL_END') + } else if (session.currentMessage.indexOf('MEDIA_END ') !== -1) { + console.log('MEDIA_END') + } else { + if (this.bypass) return data + } +} +FilterAppAudiocodes.prototype.handleSIP = function(session, ip, direction) { + console.log(direction, session.sid, session.seq, ip[5]) +} +FilterAppAudiocodes.prototype.invokeResolver = function(session, ip) { + if(this.debug) console.log('Invoking Resolver') + let aliasregex = /SIPInterface #([^\s]+) \((.*)\) (.*) TO/g; + let interface = aliasregex.exec(session.currentMessage) || false; + if (this.resolver && interface){ + let alias = interface[1]; //0 + let group = interface[2]; //some-group + let proto = interface[3]; //UDP,TCP,TLS + + let ifname = this.resolver.sip[group] ? this.resolver.sip[group].NetworkInterface : false; + if (ifname){ + let xlocalip = this.resolver.ifs[ifname] ? this.resolver.ifs[ifname] : false; + let xlocalport = this.resolver.sip[group] ? this.resolver.sip[group][proto+"Port"] : false; + if (this.debug) console.log('!!!!!!!!!!!!!!!!! IN IFNAME MATCH', group, ifname, alias, proto, xlocalip, xlocalport); + } else { + if (this.debug) console.log('!!!!!!!!!!!!!!!!! IN IFNAME FAILURE', group, ifname, alias, proto); + } + return {alias, group, proto, ifname, xlocalip, xlocalport} + } else { + return false + } +} const watchIni = function(filePath, ini){ - logger.info('Watching INI for changes...',filePath); - fs.watch(filePath, (event, filename) => { - if (filename && event ==='change'){ - console.log('INI file Changed! Reloading...', filename); - ini = parseIni(filePath); - } - }); + logger.info('Watching INI for changes...',filePath); + fs.watch(filePath, (event, filename) => { + if (filename && event ==='change'){ + console.log('INI file Changed! Reloading...', filename); + ini = parseIni(filePath); + } + }); } const parseIni = function(filePath){ - var config = ini.parse(fs.readFileSync(filePath, 'utf-8')) - - var interface = config.InterfaceTable; - var interface_index = interface['FORMAT Index'].split(', '); delete interface['FORMAT Index']; - var interface_obj = {}; - var count = 0; - Object.entries(interface).forEach(entry => { - const [key, value] = entry; - var values = value.split(', '); - interface_obj[count] = {}; - values.forEach(function(val, link){ - interface_obj[count][interface_index[link]] = val.replace(/^["'](.+(?=["']$))["']$/, '$1'); - }); - count++; - }); - - var ifs = {}; - Object.entries(interface_obj).forEach(entry => { - ifs[entry[1].InterfaceName] = entry[1].IPAddress; - }); - - var sipinterface = config.SIPInterface; - var sipinterface_index = sipinterface['FORMAT Index'].split(', '); delete sipinterface['FORMAT Index']; - var sipinterface_obj = {}; - var count = 0; - Object.entries(sipinterface).forEach(entry => { - const [key, value] = entry; - var values = value.split(', '); - var realm = values[0].replace(/^["'](.+(?=["']$))["']$/, '$1'); delete values[0]; - sipinterface_obj[realm] = {}; - values.forEach(function(val, link){ - sipinterface_obj[realm][sipinterface_index[link]] = val.replace(/^["'](.+(?=["']$))["']$/, '$1'); - }); - count++; - }); - - - if (this.debug) logger.info('INI Interfaces', interface_obj); - if (this.debug) logger.info('INI SIP Interfaces', sipinterface_obj); - - return { interfaces: interface_obj, sip: sipinterface_obj, ifs: ifs } + var config = ini.parse(fs.readFileSync(filePath, 'utf-8')) + + var interface = config.InterfaceTable; + var interface_index = interface['FORMAT Index'].split(', '); delete interface['FORMAT Index']; + var interface_obj = {}; + var count = 0; + Object.entries(interface).forEach(entry => { + const [key, value] = entry; + var values = value.split(', '); + interface_obj[count] = {}; + values.forEach(function(val, link){ + interface_obj[count][interface_index[link]] = val.replace(/^["'](.+(?=["']$))["']$/, '$1'); + }); + count++; + }); + + var ifs = {}; + Object.entries(interface_obj).forEach(entry => { + ifs[entry[1].InterfaceName] = entry[1].IPAddress; + }); + + var sipinterface = config.SIPInterface; + var sipinterface_index = sipinterface['FORMAT Index'].split(', '); delete sipinterface['FORMAT Index']; + var sipinterface_obj = {}; + var count = 0; + Object.entries(sipinterface).forEach(entry => { + const [key, value] = entry; + var values = value.split(', '); + var realm = values[0].replace(/^["'](.+(?=["']$))["']$/, '$1'); delete values[0]; + sipinterface_obj[realm] = {}; + values.forEach(function(val, link){ + sipinterface_obj[realm][sipinterface_index[link]] = val.replace(/^["'](.+(?=["']$))["']$/, '$1'); + }); + count++; + }); + + + if (this.debug) logger.info('INI Interfaces', interface_obj); + if (this.debug) logger.info('INI SIP Interfaces', sipinterface_obj); + + return { interfaces: interface_obj, sip: sipinterface_obj, ifs: ifs } } From 23ea369dc2a1a70839ae80f624ddd869855fb782 Mon Sep 17 00:00:00 2001 From: dletta Date: Fri, 25 Oct 2024 14:37:52 -0500 Subject: [PATCH 2/6] All tested, final step is output format --- .../app_audiocodes/filter_app_audiocodes.js | 658 +++++++++--------- 1 file changed, 320 insertions(+), 338 deletions(-) diff --git a/plugins/filters/app_audiocodes/filter_app_audiocodes.js b/plugins/filters/app_audiocodes/filter_app_audiocodes.js index dafa95f3..c56710ea 100644 --- a/plugins/filters/app_audiocodes/filter_app_audiocodes.js +++ b/plugins/filters/app_audiocodes/filter_app_audiocodes.js @@ -43,7 +43,7 @@ util.inherits(FilterAppAudiocodes, base_filter.BaseFilter); FilterAppAudiocodes.prototype.start = function(callback) { logger.info('Initialized App Audiocodes SysLog to SIP/HEP parser'); - if (this.ini){ + if (this.ini) { logger.info('Reading INI file to resolver...', this.ini); try { this.resolver = parseIni(this.ini); @@ -52,11 +52,11 @@ FilterAppAudiocodes.prototype.start = function(callback) { if (this.debug) console.log(this.resolver); if (this.iniwatch) watchIni(this.ini, this.resolver); } catch(err) { logger.error(err) } - } + } - this.postProcess = function(ipcache,last,type){ - if(!last||!ipcache) return; - last = last.replace(/#012/g, '\r\n').trim() + "\r\n\r\n"; + this.postProcess = function(session, message, type) { + if ( !message||!session ) return + message = message.replace(/#012/g, '\r\n').trim() + '\r\n\r\n' var rcinfo = { type: 'HEP', version: 3, @@ -64,43 +64,43 @@ FilterAppAudiocodes.prototype.start = function(callback) { ip_family: 2, protocol: 17, proto_type: type || 1, - correlation_id: ipcache.callId || '', - srcIp: ipcache.srcIp || this.localip, - srcPort: ipcache.srcPort || 0, - dstIp: ipcache.dstIp || this.localip, - dstPort: ipcache.dstPort || 0, - time_sec: ipcache.ts || parseInt(new Date().getTime() / 1000), - time_usec: ipcache.usec || new Date().getMilliseconds() - }; - + correlation_id: session.callId || '', + srcIp: session.srcIp || this.localip, + srcPort: session.srcPort || 0, + dstIp: session.dstIp || this.localip, + dstPort: session.dstPort || 0, + time_sec: session.ts || parseInt(new Date().getTime() / 1000), + time_usec: session.usec || new Date().getMilliseconds() + } // EXTRACT CORRELATION HEADER, IF ANY - if (this.correlation_hdr && rcinfo.proto_type == 1 && last.startsWith('INVITE')) { - var xcid = last.match(this.correlation_hdr+":\s?(.*)\r\n\r\n"); - if (xcid && xcid[1]) rcinfo.correlation_id = xcid[1].trim(); - if (this.debug) logger.info('auto correlation pick', rcinfo.correlation_id); + if (this.correlation_hdr && rcinfo.proto_type == 1 && message.startsWith('INVITE')) { + var xcid = message.match(this.correlation_hdr+":\s?(.*)\r\n\r\n") + if (xcid && xcid[1]) rcinfo.correlation_id = xcid[1].trim() + if (this.debug) logger.info('auto correlation pick', rcinfo.correlation_id) } - if (this.correlation_contact && rcinfo.proto_type == 1 && last.startsWith('INVITE')) { - var extract = /x-c=(.*?)\//.exec(last); + if (this.correlation_contact && rcinfo.proto_type == 1 && message.startsWith('INVITE')) { + var extract = /x-c=(.*?)\//.exec(message) if (extract[1]) { - rcinfo.correlation_id = extract[1]; - if (this.debug) logger.info('auto correlation pick', rcinfo.correlation_id); + rcinfo.correlation_id = extract[1] + if (this.debug) logger.info('auto correlation pick', rcinfo.correlation_id) } - } + } - if (last.indexOf('2.0/TCP') !== -1 || last.indexOf('2.0/TLS') !== -1 ){ + if (message.indexOf('2.0/TCP') !== -1 || message.indexOf('2.0/TLS') !== -1 ){ rcinfo.protocol = 6; - if (this.autolocal) rcinfo.dstPort = 5061; + if (this.autolocal) rcinfo.dstPort = 5061 } - if (last && rcinfo) { - var data = { payload: last, rcinfo: rcinfo }; - console.log('FINAL DATA') - console.log(data.payload) - return data; + if (message && rcinfo) { + var data = { payload: message, rcinfo: rcinfo } + if (this.debug) console.log('FINAL DATA') + if (this.debug) console.log(data.payload) + this.emit('output', data) + return } - } - callback(); + } + callback(); }; /** @@ -109,73 +109,171 @@ FilterAppAudiocodes.prototype.start = function(callback) { */ let sessionManager = { evaluateMessage: function (line) { + /* Extract SID and SEQ from SIP Message */ var seqObj = /.*\[S=(?[0-9]+)\].*/.exec(line) var sidObj = /\[SID=(?.*?)\]/.exec(line) + if (!seqObj || !sidObj) { if (this.bypass) return data - throw new Error(`Invalid SIP Message, missing SID or SEQ in Line: ${line}`) + logger.error(`Invalid SIP Message, missing SID or SEQ in Line: ${line}`) + return } + /* Unwrap SID and SEQ from Regexp */ let seq = seqObj[1] let sid = sidObj[1] + /* Remove SEQ and SID from line */ + line = line.replace(/\[S=[0-9]+\] \[SID=.*\] /, '') + + + + if ((line.indexOf('Incoming SIP') !== -1 || line.indexOf('Outgoing SIP') !== -1) && line.trim().endsWith('#012#012')) { + if (this.debug) console.log('FULL SIP MESSAGE', line) + return sessionManager.createSession(sid, seq, line) + } + let session = {} + /* Check if we are waiting for another part of this session */ if (this.findSession(sid)) { - if (this.debug) logger.info('FOUND SESSION', sid) + if (this.debug) logger.info('Found existing session', sid) session = this.addFragment(sid, seq, line) } else { - if (this.debug) logger.info('NEW SESSION', sid) + if (this.debug) logger.info('Created new entry', sid) session = this.createSession(sid, seq, line) } + return session }, findSession: function (sid) { - if(sid_cache.has(sid)) { - return sid_cache.get(sid) + if (this.debug) console.log('Finding session with sid: ', sid) + if (sid_cache.has(sid)) { + let session = sid_cache.get(sid) + return session } else { return false } }, - createSession:function(sid, seq, message) { + createSession: function(sid, seq, message) { + if (this.debug) console.log('Creating a session for a fragment', sid, seq) + let messages = message.split(/(?=\(N )/g) let session = { sid: sid, seq: seq, - currentMessage: message, - payloads: [{message: message, seq: seq}] + currentMessage: messages, + buffer: [{message: message, seq: seq}], } sid_cache.set(sid, session) return session }, - addFragment:function(sid, seq, message) { + addFragment: function(sid, seq, message) { let session = this.findSession(sid) - /* TODO: add message to payload in sequence */ - if(session) { - session.seq = seq - session.currentMessage = message - session.payloads.push({message: message, seq: seq}) - sid_cache.set(sid, session) - // this.checkComplete(session) - return session - } else { - return false + if (this.debug) console.log('Adding fragment to session', sid, seq) + let messages = message.split(/(?=\(N )/g) + for (let i = 0; i < messages.length; i++) { + session.buffer.push({message: messages[i], seq: seq + (i / 10)}) + session.buffer = session.buffer.sort((a, b) => a.seq - b.seq) + } + session.currentMessage = this.rawSip(session) + sid_cache.set(sid, session) + return session + }, + rawSip: function(session) { + let rawSIP = [] + /* Determine complete messages based on content, not messages */ + for (let i = 0; i < session.buffer.length; i++) { + let type = this.preScreen(session.buffer[i].message) + if (this.debug) console.log('Prescreening Type for completion check', type) + if (type === 'incoming' || type === 'outgoing') { + rawSIP.push(session.buffer[i].message) + } else if (type === 'incomplete') { + if (i + 1 >= session.buffer.length) { + logger.info('Incomplete SIP Message no more to add at this time, will cache', session.buffer[i].message) + continue + } + let type = this.preScreen(session.buffer[i].message + session.buffer[i + 1].message) + if (this.debug) console.log('Fragmented, looking ahead in buffer') + if (type === 'incoming' || type === 'outgoing') { + rawSIP.push(session.buffer[i].message + session.buffer[i + 1].message) + i++ + continue + } else { + if (this.debug) console.log('Merging Fragments and caching') + session.buffer[i].message += session.buffer[i + 1].message + session.buffer.splice(i + 1, 1) + continue + } + } else { + /* Unknown type should be sent as log */ + rawSIP.push(session.buffer[i].message) + } } + return rawSIP }, - checkComplete:function(session) { - let check = session.payload.match(/\r\n\r\n/g) - console.log('CHECK COMPLETE', check) - if (check.length < 1) { - console.log('NOT complete') - return false + removeFragment: function(session, sipPayload) { + let newBuffer = session.buffer.filter((a) => !sipPayload.includes(a.message)) + if (this.debug) console.log('Removing Fragment from Session', session.sid) + if (newBuffer.length === 0) { + if (this.debug) console.log('Removing Session from Cache', session.sid) + sid_cache.del(session.sid) + return + } + session.buffer = newBuffer + sid_cache.set(session.sid, session) + return + }, + preScreen: function(message) { + if (message.indexOf('Incoming SIP Message') !== -1) { + if (message.endsWith('#012#012')) { + return 'incoming' + } else { + let test = agnostic.exec(message) + if (!test?.groups?.sip) { + return 'incomplete' + } else { + if (test.groups.sip.endsWith('#012#012')) { + return 'incoming' + } else { + if (test.groups.sip.endsWith('#012')) { + test.groups.sip += '#012' + return 'incoming' + } else { + return 'incomplete' + } + } + } + } + } else if (message.indexOf('Outgoing SIP Message') !== -1) { + if (message.endsWith('#012#012')) { + return 'outgoing' + } else { + let test = agnostic.exec(message) + if (!test?.groups?.sip) { + return 'incomplete' + } else { + if (test.groups.sip.endsWith('#012#012')) { + return 'outgoing' + } else { + if (test.groups.sip.endsWith('#012')) { + test.groups.sip += '#012' + return 'outgoing' + } else { + return 'incomplete' + } + } + } + } + } else { + return 'unknown' } - return true } } -var last = ''; -var ipcache = {}; var aliases = {}; -var hold; -var cache; -var seqN; +/** + * Agnostic SIP Message Regexp + * @type {RegExp} Agnostic SIP check + */ +const agnostic = new RegExp(/(?:\(N.*)---- (?:Incoming|Outgoing) SIP Message (?:from|to) (?.*) (?:from|to) SIPInterface #[0-9]+? \((?.*)\) (?:.*) TO[(]?#[0-9]+?[)]? (?:.*)?---[-]?[ ]?(?:#012)?(?.*)*/) /** * Receives a buffer from an input or filter @@ -184,7 +282,8 @@ var seqN; */ FilterAppAudiocodes.prototype.process = function(data) { /* Message to String*/ - var line = data.message.toString(); + var line = data.message.toString() + /* Debug for when we send a text file for debug */ if (this.file_debug) { console.log('RECEIVED LINE') @@ -199,209 +298,128 @@ FilterAppAudiocodes.prototype.process = function(data) { if (this.debug) console.info('DEBUG', line) - /* Adjust Regexp for 7.40A.500 format*/ - /* - if (this.version === '7.40A.500') { - var message = /.*\[S=([0-9]+)\].*?\[SID=.*?\]\s?(.*)\[Time:.*\]/g - } else { - var message = /^.*?\[S=([0-9]+)\].*?\[SID=.*?\]\s?(.*)\[Time:.*\]$/ - } - - - var test = message.exec(line.replace(/\r\n/g, '#012')) - - if(hold && line && test) { - if (this.debug) logger.error('Next packet number', test[1]) - if (parseInt(test[1]) == seq + 1) { - line = cache + ( test ? test[2] : '') - hold = false - cache = '' - if (this.debug) console.info('reassembled line', line) - } - }*/ + /* Remove brinary prefix, Remove trailing timestamp, helps with detection of final fragment */ + try { + line = line.split('<157>')[1] + line = line.split(' [Time:')[0] + } catch (err) { + logger.error('Unknown Event or malformed line') + logger.error(data.message.toString()) + if (this.debug) console.log('ERROR', err) + return + } + /* Prepare line for processing */ line = line.replace(/\r\n/g, '#012') - /* Create Session or append to Session */ - let session = sessionManager.evaluateMessage(line) - - if (this.debug) logger.error('SESSION SID',session.sid) + let messages = this.splitMessages(line) - this.sipRouter(session) + /* Create Session or append to Session */ + messages.forEach((msg) => { + let session = sessionManager.evaluateMessage(msg) + + if (!session) return + + session.currentMessage.forEach((msg) => { + this.sipRouter(session, msg) + }) + }) } exports.create = function() { return new FilterAppAudiocodes() } -/* Previous Router +FilterAppAudiocodes.prototype.splitMessages = function(line) { + let messages = [] + let split = line.split(/(?=\(N )/g) + + /** + * @param {string} first Sequence number and Session ID + * */ + let first = split.shift() + if (split.length > 1) { + split.forEach((msg) => { + let newmsg = first + msg + messages.push(newmsg) + }) + } else { + messages.push(line) + } + return messages +} - if (line.indexOf('Incoming SIP Message') !== -1) { - try { - // Set regex based on version - // var regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*--- #012(.*)#012 #012 #012(.*) \[Time:(.*)-(.*)@(.*)\]/g; - var regex; - if (this.version === '7.40A.500') { - regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO\(#[0-99]\) ---- (.*)/g; //7.40A.500.357 - } else if (this.version == '7.20A.256.511') { - regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*--- (.*)(.*)/g; //7.20A.256.511 - } else { - regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*---\s?#012(.*)#012\s?#012(.*)/g; //7.20A.260.012 - } +FilterAppAudiocodes.prototype.sipRouter = async function(session, message) { + if (this.debug) console.log('Routing SIP Session', session.sid) + if (message.indexOf('Incoming SIP Message') !== -1) { + if (this.debug) console.log('Incoming SIP Message') + try { + let resolvedObj = false if (this.resolver){ - var aliasregex = /SIPInterface #([^\s]+) \((.*)\) (.*) TO/g; - var interface = aliasregex.exec(line) || false; - if (this.resolver && interface){ - var alias = interface[1]; //0 - var group = interface[2]; //some-group - var proto = interface[3]; //UDP,TCP,TLS - - var ifname = this.resolver.sip[group] ? this.resolver.sip[group].NetworkInterface : false; - if (ifname){ - var xlocalip = this.resolver.ifs[ifname] ? this.resolver.ifs[ifname] : false; - var xlocalport = this.resolver.sip[group] ? this.resolver.sip[group][proto+"Port"] : false; - if (this.debug) console.log('!!!!!!!!!!!!!!!!! IN IFNAME MATCH', group, ifname, alias, proto, xlocalip, xlocalport); - } else { - if (this.debug) console.log('!!!!!!!!!!!!!!!!! IN IFNAME FAILURE', group, ifname, alias, proto); - } - } + resolvedObj = this.invokeResolver(session) } - // Apply Regexp to line - var ip = regex.exec(line); - console.log('PROCESSED') - console.log(ip) - if (!ip) { - console.log('BAD LINE', line) - cache = line.replace(/\[Time.*\]$/,''); - - hold = true; - var regpackid = /.*\[S=([0-9]+)\].\*\/.exec(line); - seq = parseInt(regpackid[1]); - if (this.debug) logger.error('Cached packet number', seq, line); - logger.error('failed parsing Incoming SIP. Cache on!'); - if (this.bypass) return data; - } else { - if (xlocalip && xlocalport){ - ipcache.dstIp = xlocalip; - ipcache.dstPort = parseInt(xlocalport); - } else if (ip[3]) { - // convert alias to IP:port - ipcache.dstIp = aliases[0] || this.localip; - ipcache.dstPort = aliases[1] || this.localport; - } - ipcache.srcIp = ip[2].split(':')[0]; - ipcache.srcPort = ip[2].split(':')[1]; - last = ip[5]; - last += '#012 #012'; - var callid = last.match(/call-id:\s?(.*?)\s?#012/i) || []; - ipcache.callId = callid[1] || sid[1] || ''; - // Cache SID to Call-ID correlation - sid_cache.set(sid[1], ipcache.callId, expire); - // Seek final fragment - if(ip[6]?.includes(' SIP Message ') && this.version !== '7.40A.500'){ - hold = true; - cache = line.replace(/\[Time.*\]$/,''); - } - return this.postProcess(ipcache,last); + // Apply Regexp to line + var rawSIP = agnostic.exec(message) + if (!rawSIP || !rawSIP?.groups?.sip) { + if (this.debug) console.log('MISSING SIP') + if (this.debug) console.log( message) + return + } else { + this.handleSIP(session, rawSIP, 'incoming', resolvedObj) } - } catch(e) { - logger.error(e, line); + } catch (err) { + logger.error(err, message) } - - } else if (line.indexOf('Outgoing SIP Message') !== -1) { - try { - var regex; - if (this.version === '7.40A.500') { - regex = /(.*) ---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO\(#.*\) ---- (.*)/g; //7.40A.500.357 - } else if (this.version == '7.20A.256.511') { - regex = /(.*)---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO.*--- (.*)(.*)/g; //7.20A.256.511 - } else { - regex = /(.*)---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO.*---\s?#012(.*)#012\s?#012 (.*)/g; //7.20A.260.012 - } - + } else if (message.indexOf('Outgoing SIP Message') !== -1) { + if (this.debug) console.log('Outgoing SIP Message') + try { + let resolvedObj = false if (this.resolver) { - var aliasregex = /SIPInterface #([^\s]+) \((.*)\) (.*) TO/g; - var interface = aliasregex.exec(line) || false; - if (this.resolver && interface) { - var alias = interface[1]; //0 - var group = interface[2]; //some-group - var proto = interface[3]; //UDP,TCP,TLS - - var ifname = this.resolver.sip[group] ? this.resolver.sip[group].NetworkInterface : false; - if (ifname) { - var xlocalip = this.resolver.ifs[ifname] ? this.resolver.ifs[ifname] : false; - var xlocalport = this.resolver.sip[group] ? this.resolver.sip[group][proto+"Port"] : false; - if (this.debug) console.log('!!!!!!!!!!!!!!!!! OUT IFNAME MATCH', group, ifname, alias, proto, xlocalip, xlocalport); - } else { - if (this.debug) console.log('!!!!!!!!!!!!!!!!! OUT IFNAME FAILURE', group, ifname, alias, proto); - } - } + resolvedObj = this.invokeResolver(session) } - - var ip = regex.exec(line); - if (!ip) { - cache = line.replace(/\[Time.*\]$/,''); - hold = true; - var regpackid = /.*\[S=([0-9]+)\].\*\/.exec(line); - seq = parseInt(regpackid[1]); - if (this.debug) logger.error('Cached packet number', seq, line); - logger.error('failed parsing Outgoing SIP. Cache on!'); - if (this.bypass) return data; - } else { - if (xlocalip && xlocalport){ - ipcache.srcIp = xlocalip; - ipcache.srcPort = parseInt(xlocalport); - } else if (ip[3]) { - // convert alias to IP:port - ipcache.srcIp = aliases[0] || this.localip; - ipcache.srcPort = aliases[1] || this.localport; - } - ipcache.dstIp = ip[2].split(':')[0]; - ipcache.dstPort = ip[2].split(':')[1]; - last = ip[5]; - last += '#012 #012'; - var callid = last.match(/call-id:\s?(.*?)\s?#012/i) || []; - ipcache.callId = callid[1] || sid[1] || ''; - // Cache SID to Call-ID correlation - sid_cache.set(sid[1], ipcache.callId, expire); - // Seek final fragment - if(ip[6]?.includes(' SIP Message ') && this.version !== '7.40A.500'){ - hold = true; - cache = line.replace(/\[Time.*\]$/,''); - } - return this.postProcess(ipcache,last); + // Apply Regexp to line + var rawSIP = agnostic.exec(message) + if (!rawSIP || !rawSIP?.groups?.sip) { + if (this.debug) console.log('MISSING SIP') + if (this.debug) console.log( message) + return + } else { + this.handleSIP(session, rawSIP, 'outgoing', resolvedObj) } - } catch(e) { - logger.error(e, line); + } catch (err) { + logger.error(err, message) } - } else if (this.autolocal && line.indexOf('Local IP Address =') !== -1) { - var local = line.match(/Local IP Address = (.*?):(.*?),/) || []; - if(local[1]) this.localip = local[1]; - if(local[2]) this.localport = local[2]; - } else if (line.indexOf('CALL_END ') !== -1 && this.logs) { + } else if (this.autolocal && message.indexOf('Local IP Address =') !== -1) { + console.log('Local IP Address') + var local = message.match(/Local IP Address = (.*?):(.*?),/) || [] + if (local[1]) this.localip = local[1] + if (local[2]) this.localport = local[2] + } else if (message.indexOf('CALL_END ') !== -1) { + console.log('CALL_END') // Parser TBD page 352 @ https://www.audiocodes.com/media/10312/ltrt-41548-mediant-software-sbc-users-manual-ver-66.pdf - var cdr = line.split(/(\s+\|)/).filter( function(e) { return e.trim().length > 1; } ) - ipcache.callId = cdr[3] || ''; - if (this.debug) logger.info('CALL_END', cdr, ipcache); - if (this.logs) return this.postProcess(ipcache,JSON.stringify(cdr),100); - } else if (line.indexOf('MEDIA_END ') !== -1 && this.qos) { + var cdr = message.split(/(\s+\|)/).filter( function(e) { return e.trim().length > 1; } ) + session.callId = cdr[3] || '' + if (this.debug) logger.info('CALL_END', cdr, session) + if (this.logs) return this.postProcess(session,JSON.stringify(cdr),100) + } else if (message.indexOf('MEDIA_END ') !== -1) { + console.log('MEDIA_END') // Parsed TBD page 353 @ https://www.audiocodes.com/media/10312/ltrt-41548-mediant-software-sbc-users-manual-ver-66.pdf - var qos = line.split(/(\s+\|)/).filter( function(e) { return e.trim().length > 1; } ) + var qos = session.currentMessage.split(/(\s+\|)/).filter( function(e) { return e.trim().length > 1; } ) if (qos.length == 25){ qos.splice(15, 1); qos.splice(5, 1); } logger.info('!!!!!!!!!!!!!! DEBUG MEDIA', qos, qos.length); - if(qos && qos[2] && qos[21]){ - ipcache.callId = qos[2] || ''; + if (qos && qos[2] && qos[21]){ + session.callId = qos[2] || ''; var response = []; // A-LEG - ipcache.srcIp = qos[7]; - ipcache.srcPort = parseInt(qos[8]); - ipcache.dstIp = qos[9]; - ipcache.dstPort = parseInt(qos[10]); + session.srcIp = qos[7]; + session.srcPort = parseInt(qos[8]); + session.dstIp = qos[9]; + session.dstPort = parseInt(qos[10]); var local_report = { "CORRELATION_ID": qos[2], "RTP_SIP_CALL_ID": qos[2], @@ -413,12 +431,12 @@ exports.create = function() { "PARTY":0, "TYPE":"HANGUP" }; - response.push(this.postProcess(ipcache,JSON.stringify(local_report),35)); + response.push(this.postProcess(session,JSON.stringify(local_report),35)); // B-LEG - ipcache.srcIp = qos[9]; - ipcache.srcPort = parseInt(qos[10]); - ipcache.dstIp = qos[7]; - ipcache.dstPort = parseInt(qos[8]); + session.srcIp = qos[9]; + session.srcPort = parseInt(qos[10]); + session.dstIp = qos[7]; + session.dstPort = parseInt(qos[8]); var remote_report = { "CORRELATION_ID": qos[2], "RTP_SIP_CALL_ID": qos[2], @@ -430,120 +448,84 @@ exports.create = function() { "PARTY":1, "TYPE":"HANGUP" }; - response.push(this.postProcess(ipcache,JSON.stringify(remote_report),35)); + response.push(this.postProcess(session,JSON.stringify(remote_report),35)); if (this.debug) logger.info('MEDIA_END', response); if (this.qos) return response; } else { - logger.error('missing media parameters', qos); + logger.error('Missing media parameters', qos); } - } else if (sid && !hold && this.logs) { + } else if (session.sid && this.logs) { if (this.bypass) return data; // Prepare SIP LOG if (this.logs) { - ipcache.callId = sid_cache.get(sid) || sid || ''; - ipcache.srcIp = this.localip || '127.0.0.1'; - ipcache.srcPort = 514 - ipcache.dstIp = this.localip || '127.0.0.1'; - ipcache.dstPort = 514 - return this.postProcess(ipcache,line,100); + var callid = message.match(/call-id:\s?(.*?)\s?#012/i) || [] + session.callId = callid[1] || session.sid || '' + session.srcIp = this.localip || '127.0.0.1' + session.srcPort = 514 + session.dstIp = this.localip || '127.0.0.1' + session.dstPort = 514 + sessionManager.removeFragment(session, message) + return this.postProcess(session, message, 100) } } else { - // Discard - if (this.bypass) return data; + if (this.bypass) return data + if (this.debug) console.log('UNKNOWN', session.sid, message) + // Prepare unknown as log + if (this.logs) { + var callid = message.match(/call-id:\s?(.*?)\s?#012/i) || [] + session.callId = callid[1] || session.sid || '' + session.srcIp = this.localip || '127.0.0.1' + session.srcPort = 514 + session.dstIp = this.localip || '127.0.0.1' + session.dstPort = 514 + sessionManager.removeFragment(session, message) + return this.postProcess(session, message, 100) + } } +} -*/ - -FilterAppAudiocodes.prototype.sipRouter = function(session) { - console.log('Routing SIP Session') - if (session.currentMessage.indexOf('Incoming SIP Message') !== -1) { - console.log('Incoming SIP Message') - try { - /* Set regex based on version - var regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*--- #012(.*)#012 #012 #012(.*) \[Time:(.*)-(.*)@(.*)\]/g; */ - let regex - if (this.version === '7.40A.500') { - regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO\(#[0-99]\) ---- (.*)/g; //7.40A.500.357 - } else if (this.version == '7.20A.256.511') { - regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*--- (.*)(.*)/g; //7.20A.256.511 - } else { - regex = /(.*)---- Incoming SIP Message from (.*) to SIPInterface #[0-99] \((.*)\) (.*) TO.*---\s?#012(.*)#012\s?#012(.*)/g; //7.20A.260.012 - } - - let resolvedObj = false - if (this.resolver){ - resolvedObj = this.invokeResolver(session) - } - // Apply Regexp to line - let ip = regex.exec(session.currentMessage) - if (!ip) { - console.log('BAD LINE', session.currentMessage) - cache = session.currentMessage.replace(/\[Time.*\]$/,''); - - hold = true; - var regpackid = /.*\[S=([0-9]+)\].*/.exec(session.currentMessage); - seqN = parseInt(regpackid[1]); - if (this.debug) logger.error('Cached packet number', seqN, session.currentMessage); - logger.error('failed parsing Incoming SIP. Cache on!'); - if (this.bypass) return data; - } else { - this.handleSIP(session, ip, 'incoming') - } - } catch (err) { - logger.error(err, line) +FilterAppAudiocodes.prototype.handleSIP = async function(session, rawSIP, direction, resolved) { + /* Extract and set src/dst IP and Ports */ + if (resolved.xlocalip && resolved.xlocalport){ + if (direction === 'incoming') { + session.dstIp = resolved.xlocalip + session.dstPort = parseInt(resolved.xlocalport) + } else { + session.srcIp = resolved.xlocalip + session.srcPort = parseInt(resolved.xlocalport) } - } else if (session.currentMessage.indexOf('Outgoing SIP Message') !== -1) { - console.log('Outgoing SIP Message') - try { - let regex; - if (this.version === '7.40A.500') { - regex = /(.*) ---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO\(#.*\) ---- (.*)/g; //7.40A.500.357 - } else if (this.version == '7.20A.256.511') { - regex = /(.*)---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO.*--- (.*)(.*)/g; //7.20A.256.511 - } else { - regex = /(.*)---- Outgoing SIP Message to (.*) from SIPInterface #[0-99] \((.*)\) (.*) TO.*---\s?#012(.*)#012\s?#012 (.*)/g; //7.20A.260.012 - } - - let resolvedObj = false - if (this.resolver) { - resolvedObj = this.invokeResolver(session) - } - - // Apply Regexp to line - let ip = regex.exec(session.currentMessage) - if (!ip) { - console.log('BAD LINE', session.currentMessage) - cache = session.currentMessage.replace(/\[Time.*\]$/,''); - hold = true; - var regpackid = /.*\[S=([0-9]+)\].*/.exec(session.currentMessage); - seqN = parseInt(regpackid[1]); - if (this.debug) logger.error('Cached packet number', seqN, session.currentMessage); - logger.error('failed parsing Outgoing SIP. Cache on!'); - if (this.bypass) return data; - } else { - this.handleSIP(session, ip, 'outgoing') - } - - } catch (err) { - logger.error(err, line) + } else if (rawSIP.groups.alias) { + // convert alias to IP:port + if (direction === 'incoming') { + session.dstIp = aliases[0] || this.localip + session.dstPort = aliases[1] || this.localport + } else { + session.srcIp = aliases[0] || this.localip + session.srcPort = aliases[1] || this.localport } - } else if (this.autolocal && session.currentMessage.indexOf('Local IP Address =') !== -1) { - console.log('Local IP Address') - } else if (session.currentMessage.indexOf('CALL_END ') !== -1) { - console.log('CALL_END') - } else if (session.currentMessage.indexOf('MEDIA_END ') !== -1) { - console.log('MEDIA_END') + } + + if (direction === 'incoming') { + session.srcIp = rawSIP.groups.ip.split(':')[0] + session.srcPort = parseInt(rawSIP.groups.ip.split(':')[1]) } else { - if (this.bypass) return data + session.dstIp = rawSIP.groups.ip.split(':')[0] + session.dstPort = parseInt(rawSIP.groups.ip.split(':')[1]) } -} -FilterAppAudiocodes.prototype.handleSIP = function(session, ip, direction) { - console.log(direction, session.sid, session.seq, ip[5]) + let message = rawSIP.groups.sip + if (message.length < 1) { + logger.error('BAD LINE', rawSIP) + return + } + sessionManager.removeFragment(session, rawSIP.input) + var callid = message.match(/call-id:\s?(.*?)\s?#012/i) || [] + session.callId = callid[1] || session.sid || '' + return this.postProcess(session, message) } FilterAppAudiocodes.prototype.invokeResolver = function(session, ip) { - if(this.debug) console.log('Invoking Resolver') + if (this.debug) console.log('Invoking Resolver') let aliasregex = /SIPInterface #([^\s]+) \((.*)\) (.*) TO/g; let interface = aliasregex.exec(session.currentMessage) || false; if (this.resolver && interface){ @@ -569,7 +551,7 @@ const watchIni = function(filePath, ini){ logger.info('Watching INI for changes...',filePath); fs.watch(filePath, (event, filename) => { if (filename && event ==='change'){ - console.log('INI file Changed! Reloading...', filename); + logger.info('INI file Changed! Reloading...', filename); ini = parseIni(filePath); } }); From 9e6cfaed428161cd729ae3f07df9366a93252d38 Mon Sep 17 00:00:00 2001 From: dletta Date: Sat, 26 Oct 2024 08:18:53 -0500 Subject: [PATCH 3/6] Ready for Testing --- .../app_audiocodes/filter_app_audiocodes.js | 31 ++++++++++--------- plugins/filters/app_audiocodes/package.json | 2 +- plugins/filters/app_audiocodes/readme.md | 2 +- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/plugins/filters/app_audiocodes/filter_app_audiocodes.js b/plugins/filters/app_audiocodes/filter_app_audiocodes.js index c56710ea..09e5026c 100644 --- a/plugins/filters/app_audiocodes/filter_app_audiocodes.js +++ b/plugins/filters/app_audiocodes/filter_app_audiocodes.js @@ -56,7 +56,8 @@ FilterAppAudiocodes.prototype.start = function(callback) { this.postProcess = function(session, message, type) { if ( !message||!session ) return - message = message.replace(/#012/g, '\r\n').trim() + '\r\n\r\n' + message = message.replace(/#012/g, `\r\n`) + var rcinfo = { type: 'HEP', version: 3, @@ -96,7 +97,8 @@ FilterAppAudiocodes.prototype.start = function(callback) { var data = { payload: message, rcinfo: rcinfo } if (this.debug) console.log('FINAL DATA') if (this.debug) console.log(data.payload) - this.emit('output', data) + return data + } else { return } } @@ -282,8 +284,7 @@ const agnostic = new RegExp(/(?:\(N.*)---- (?:Incoming|Outgoing) SIP Message (?: */ FilterAppAudiocodes.prototype.process = function(data) { /* Message to String*/ - var line = data.message.toString() - + var line = data.message.toString('utf8') /* Debug for when we send a text file for debug */ if (this.file_debug) { console.log('RECEIVED LINE') @@ -316,15 +317,15 @@ FilterAppAudiocodes.prototype.process = function(data) { let messages = this.splitMessages(line) /* Create Session or append to Session */ - messages.forEach((msg) => { - let session = sessionManager.evaluateMessage(msg) - + for (let i = 0; i < messages.length; i++) { + let session = sessionManager.evaluateMessage(messages[i]) + if (!session) return - session.currentMessage.forEach((msg) => { - this.sipRouter(session, msg) - }) - }) + for (let y = 0; y < session.currentMessage.length; y++) { + return this.sipRouter(session, session.currentMessage[y]) + } + } } exports.create = function() { @@ -350,7 +351,7 @@ FilterAppAudiocodes.prototype.splitMessages = function(line) { return messages } -FilterAppAudiocodes.prototype.sipRouter = async function(session, message) { +FilterAppAudiocodes.prototype.sipRouter = function(session, message) { if (this.debug) console.log('Routing SIP Session', session.sid) if (message.indexOf('Incoming SIP Message') !== -1) { @@ -367,7 +368,7 @@ FilterAppAudiocodes.prototype.sipRouter = async function(session, message) { if (this.debug) console.log( message) return } else { - this.handleSIP(session, rawSIP, 'incoming', resolvedObj) + return this.handleSIP(session, rawSIP, 'incoming', resolvedObj) } } catch (err) { logger.error(err, message) @@ -386,7 +387,7 @@ FilterAppAudiocodes.prototype.sipRouter = async function(session, message) { if (this.debug) console.log( message) return } else { - this.handleSIP(session, rawSIP, 'outgoing', resolvedObj) + return this.handleSIP(session, rawSIP, 'outgoing', resolvedObj) } } catch (err) { logger.error(err, message) @@ -484,7 +485,7 @@ FilterAppAudiocodes.prototype.sipRouter = async function(session, message) { } } -FilterAppAudiocodes.prototype.handleSIP = async function(session, rawSIP, direction, resolved) { +FilterAppAudiocodes.prototype.handleSIP = function(session, rawSIP, direction, resolved) { /* Extract and set src/dst IP and Ports */ if (resolved.xlocalip && resolved.xlocalport){ if (direction === 'incoming') { diff --git a/plugins/filters/app_audiocodes/package.json b/plugins/filters/app_audiocodes/package.json index 96ee7253..63506764 100644 --- a/plugins/filters/app_audiocodes/package.json +++ b/plugins/filters/app_audiocodes/package.json @@ -1,6 +1,6 @@ { "name": "@pastash/filter_app_audiocodes", - "version": "1.1.5", + "version": "1.1.6", "description": "Audiocodes Syslog plugin for @pastash/pastash", "main": "filter_app_audiocodes.js", "scripts": { diff --git a/plugins/filters/app_audiocodes/readme.md b/plugins/filters/app_audiocodes/readme.md index 4805ce79..bbd4c8ba 100644 --- a/plugins/filters/app_audiocodes/readme.md +++ b/plugins/filters/app_audiocodes/readme.md @@ -87,7 +87,7 @@ Parameters for `app_audiocodes`: * `correlation_contact`: Auto-Extract correlation from Contact x-c. Default : false. * `debug`: Enable debug logs. Default : false. * `file_debug`: Enable debug using file input. (For development) Default : false. -* `version`: Syslog parser version. Supports `7.40A.500` _(or higher)_. Default: 7.20A.260.012 +* `version`: Syslog parser version. Supports `7.40A.500` _(or higher)_. Default: 7.20A.260.012 (OPTIONAL after version 1.1.6) For full instructions consult the [plugin documentation](https://github.com/sipcapture/paStash/blob/next/plugins/filters/app_audiocodes/app_audiocodes.md) From 0308fda2770345faf5eaf11f5e12496d276f564e Mon Sep 17 00:00:00 2001 From: dletta Date: Fri, 1 Nov 2024 12:04:57 -0500 Subject: [PATCH 4/6] fixed split to take into account all priorities of syslog --- plugins/filters/app_audiocodes/filter_app_audiocodes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/filters/app_audiocodes/filter_app_audiocodes.js b/plugins/filters/app_audiocodes/filter_app_audiocodes.js index 09e5026c..300b46f0 100644 --- a/plugins/filters/app_audiocodes/filter_app_audiocodes.js +++ b/plugins/filters/app_audiocodes/filter_app_audiocodes.js @@ -301,7 +301,7 @@ FilterAppAudiocodes.prototype.process = function(data) { /* Remove brinary prefix, Remove trailing timestamp, helps with detection of final fragment */ try { - line = line.split('<157>')[1] + line = line.split(/<\d+>/)[1] line = line.split(' [Time:')[0] } catch (err) { logger.error('Unknown Event or malformed line') From cdbf3a273f357824220054cf97e68f20aeb50bda Mon Sep 17 00:00:00 2001 From: dletta Date: Fri, 1 Nov 2024 14:39:56 -0500 Subject: [PATCH 5/6] cleanup of newlines --- plugins/filters/app_audiocodes/filter_app_audiocodes.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/filters/app_audiocodes/filter_app_audiocodes.js b/plugins/filters/app_audiocodes/filter_app_audiocodes.js index 300b46f0..b9affb61 100644 --- a/plugins/filters/app_audiocodes/filter_app_audiocodes.js +++ b/plugins/filters/app_audiocodes/filter_app_audiocodes.js @@ -56,7 +56,10 @@ FilterAppAudiocodes.prototype.start = function(callback) { this.postProcess = function(session, message, type) { if ( !message||!session ) return - message = message.replace(/#012/g, `\r\n`) + /* If there is 3 \r\n we need to detect it and replace it with 2 */ + message = message.replace(/#012#012#012/, '#012#012') + message = message.replace(/#012/g, `\r\n`) + var rcinfo = { type: 'HEP', From 3fd44f91c875f7f40c9ac48034e182ea30dbc6d3 Mon Sep 17 00:00:00 2001 From: dletta Date: Mon, 4 Nov 2024 12:43:01 -0600 Subject: [PATCH 6/6] Updated Readme and Version --- plugins/filters/app_audiocodes/filter_app_audiocodes.js | 2 +- plugins/filters/app_audiocodes/package.json | 2 +- plugins/filters/app_audiocodes/readme.md | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/filters/app_audiocodes/filter_app_audiocodes.js b/plugins/filters/app_audiocodes/filter_app_audiocodes.js index b9affb61..4a43aaa9 100644 --- a/plugins/filters/app_audiocodes/filter_app_audiocodes.js +++ b/plugins/filters/app_audiocodes/filter_app_audiocodes.js @@ -31,7 +31,7 @@ function FilterAppAudiocodes() { 'autolocal': false, 'localip': '127.0.0.1', 'localport': 5060, - 'version': '7.20A.260.012', + 'version': '7.40A.500', 'ini': false, 'iniwatch': false }, diff --git a/plugins/filters/app_audiocodes/package.json b/plugins/filters/app_audiocodes/package.json index 63506764..221e5960 100644 --- a/plugins/filters/app_audiocodes/package.json +++ b/plugins/filters/app_audiocodes/package.json @@ -1,6 +1,6 @@ { "name": "@pastash/filter_app_audiocodes", - "version": "1.1.6", + "version": "1.2.0", "description": "Audiocodes Syslog plugin for @pastash/pastash", "main": "filter_app_audiocodes.js", "scripts": { diff --git a/plugins/filters/app_audiocodes/readme.md b/plugins/filters/app_audiocodes/readme.md index bbd4c8ba..9d59a08e 100644 --- a/plugins/filters/app_audiocodes/readme.md +++ b/plugins/filters/app_audiocodes/readme.md @@ -47,7 +47,6 @@ input { filter { app_audiocodes{ - version => '7.40A.100.114' debug => false autolocal => true ini => '/path/to/copy/of/audiocodes.ini' @@ -87,7 +86,6 @@ Parameters for `app_audiocodes`: * `correlation_contact`: Auto-Extract correlation from Contact x-c. Default : false. * `debug`: Enable debug logs. Default : false. * `file_debug`: Enable debug using file input. (For development) Default : false. -* `version`: Syslog parser version. Supports `7.40A.500` _(or higher)_. Default: 7.20A.260.012 (OPTIONAL after version 1.1.6) For full instructions consult the [plugin documentation](https://github.com/sipcapture/paStash/blob/next/plugins/filters/app_audiocodes/app_audiocodes.md)