'use strict'; 'require rpc'; 'require uci'; 'require ui'; 'require fs'; 'require form'; 'require network'; 'require tools.widgets as widgets'; var eventSource, captureFilePoll, hostName; function stopTcpdump() { fs.exec("/usr/libexec/packet_capture_stop").then(function(replay) { if (eventSource) eventSource.close(); }.bind(this)).catch(function(error) { console.log(error); }); } window.addEventListener('beforeunload', stopTcpdump); var callLuciProcessList = rpc.declare({ object: 'luci', method: 'getProcessList', expect: { result: [] } }); var callInitAction = rpc.declare({ object: 'luci', method: 'setInitAction', params: [ 'name', 'action' ], expect: { result: false } }); function addOutput() { var tcpdumpOut = document.querySelectorAll('[id$="tcpdump_out"]')[0]; if (tcpdumpOut) return; var frameEl = E('div', {'class': 'cbi-value'}); frameEl.appendChild(E('textarea', { 'id': 'tcpdump_out', 'class': 'cbi-input-textarea', 'readonly': '', 'style': 'width:100%', 'rows': 30, })); frameEl.firstElementChild.style.fontFamily = 'monospace'; var downloadBtn = document.querySelectorAll('[id$="download_file"]')[0]; if (downloadBtn) downloadBtn.parentNode.insertBefore(frameEl, downloadBtn.nextSibling); } var downloadCaptureFile = function(ev) { var form = E('form', { method: 'post', action: '/cgi-bin/cgi-download', enctype: 'application/x-www-form-urlencoded' }, E('input', { type: 'hidden', name: 'sessionid', value: rpc.getSessionID()}, E('input', { type: 'hidden', name: 'path', value: "/tmp/capture.pcap"}, E('input', { type: 'hidden', name: 'filename', value: hostName + "-" + Date.now() + ".pcap"}, E('input', { type: 'hidden', name: 'mimetype', value: 'application/vnd.tcpdump.pcap'} ))))); ev.currentTarget.parentNode.appendChild(form); form.submit(); form.parentNode.removeChild(form); } function subscribeTcpdump() { if (eventSource) eventSource.close(); eventSource = new EventSource('/ubus/subscribe/tcpdump' + '?' + rpc.getSessionID()); eventSource.onerror = function(event) { eventSource.close(); console.log(event); }; addOutput(); var textOut = document.querySelectorAll('[id$="tcpdump_out"]')[0]; textOut.value = ""; eventSource.addEventListener("tcpdump.data", function(event) { textOut.value = textOut.value + "\n" + JSON.parse(event.data).data; }); } function updateButtons() { var tasks = []; tasks.push(fs.stat("/var/run/packet_capture.pid").then(L.bind(function(res) { var downloadBtn = document.querySelectorAll('[id$="download_file"]')[0]; if (!downloadBtn) return; if (!eventSource || eventSource.readyState == 2) subscribeTcpdump(); var textOut = document.querySelectorAll('[id$="tcpdump_out"]')[0]; if (textOut) textOut.style.borderColor = "green"; var startBtn = document.querySelectorAll('[id$="start_tcpdump"]')[0]; if (startBtn) startBtn.hidden = true; var stopBtn = document.querySelectorAll('[id$="stop_tcpdump"]')[0]; if (stopBtn) stopBtn.hidden = false; return; })).catch(function(error) { var textOut = document.querySelectorAll('[id$="tcpdump_out"]')[0]; if (textOut) textOut.style.borderColor = "red"; var startBtn = document.querySelectorAll('[id$="start_tcpdump"]')[0]; if (startBtn) startBtn.hidden = false; var stopBtn = document.querySelectorAll('[id$="stop_tcpdump"]')[0]; if (stopBtn) stopBtn.hidden = true; if (eventSource) eventSource.close(); })); return Promise.all(tasks); } function updatePollCheckCaptureFileExists() { checkCaptureFileExists(); L.Poll.remove(captureFilePoll); L.Poll.add(L.bind(checkCaptureFileExists, m),5); } function checkCaptureFileExists() { var tasks = []; tasks.push(fs.stat("/tmp/capture.pcap").then(L.bind(function(res) { var downloadBtn = document.querySelector('[data-action="download"]'); if (!downloadBtn) return; var downloadCheckBox = document.querySelectorAll('[data-widget-id$="file"]')[0].checked; if (!downloadCheckBox) { fs.remove("/tmp/capture.pcap").then(function(replay) { downloadBtn.disabled = true;; }.bind(this)).catch(function(error) { console.log(error); }); } else { downloadBtn.disabled = false; } })).catch(function(error) { var downloadBtn = document.querySelector('[data-action="download"]'); if (downloadBtn) downloadBtn.disabled = true; })); return Promise.all(tasks); } return L.view.extend({ load: function() { return Promise.all([ uci.load('system') ]); }, handleDownload: function(ev) { downloadCaptureFile(ev); }, render: function(processes) { var m, s, o; hostName = uci.get('system', '@system[0]', 'hostname'); m = new form.Map('packet_capture', _('Packet Capture - Tcpdump'), _('Capture packets with tcpdump.')); s = m.section(form.TypedSection, 'tcpdump'); s.anonymous = 1; o = s.option(widgets.DeviceSelect, 'interface', _('Interface'), _('')); o.noaliases = true; o.modalonly = true; o.rmempty = false; o.filter = function(section_id, value) { return true; } o = s.option(form.Value, 'filter', _('Filter'), _('Tcpdump filter like protocol, port etc.')); o.modalonly = false; o.datatype = 'and(minlength(1),maxlength(1024))'; o = s.option(form.Value, 'duration', _('Duration'), _('Duration of packet capturing in seconds.')); o.modalonly = false; o.datatype = 'range(1,4294967296)'; o = s.option(form.Value, 'packets', _('Packets'), _('Number of packets to be captured.')); o.modalonly = false; o.datatype = 'range(1,4294967296)'; o = s.option(form.Flag, 'domains', _('Resolve domains'), _("Convert host addresses to names.")); o = s.option(form.Flag, 'verbose', _('Verbose output'), _("Print the link-level header on each dump line.")); o = s.option(form.Flag, 'file', _('Save to file'), _("Save capture to pcap file.")); o = s.option(form.Button, 'start_tcpdump', _('Start tcpdump'), _('')); o.inputstyle = 'apply'; o.onclick = ui.createHandlerFn(this, function(section_id, ev) { var downloadBtn = document.querySelector('[data-action="download"]'); if (!downloadBtn) return; fs.remove("/tmp/capture.pcap").then(function(replay) { downloadBtn.disabled = true;; }.bind(this)).catch(function(error) { console.log(error); }); var iface = document.querySelectorAll('[id$="interface"]')[1].value, filter = document.querySelectorAll('[id$="filter"]')[2].value, packets = document.querySelectorAll('[id$="packets"]')[2].value, duration = document.querySelectorAll('[id$="duration"]')[2].value, verbose = document.querySelectorAll('[data-widget-id$="verbose"]')[0].checked, domains = document.querySelectorAll('[data-widget-id$="domains"]')[0].checked, file = document.querySelectorAll('[data-widget-id$="file"]')[0].checked var args = { "interface": iface, "filter": filter, "packets": packets, "duration": duration, "verbose": verbose, "domains": domains, "file": file } return fs.exec_direct('/usr/libexec/packet_capture_start', [JSON.stringify(args)]).then(function(replay) { var error_position = replay.search("error:"); if (error_position != -1){ ui.showModal(_(replay.substring(error_position + 6, replay.length)), [ E('div', { 'class': 'right' }, [ E('button', { 'class': 'cbi-button cbi-button-negative important', 'click': function(ev) { ui.hideModal(); } }, _('Close')), ]) ]); return; } rpc.list.apply(rpc).then(function(res) { for (var k in res) { if (res[k] == "tcpdump" ) subscribeTcpdump() } }.bind(this)); }.bind(this)).catch(function(error) { console.log(error); }); }); o = s.option(form.Button, 'stop_tcpdump', _('Stop tcpdump'), _('')); o.inputstyle = 'apply'; o.onclick = ui.createHandlerFn(this, function(section_id, ev) { if (!eventSource) return; return fs.exec("/usr/libexec/packet_capture_stop").then(function(replay) { eventSource.close(); }.bind(this)).catch(function(error) { console.log(error); }); }); o = s.option(form.Button, 'download_file', _('Download capture file')); o.inputstyle = 'action important'; o.inputtitle = _('Download'); o.data_action = 'download' o.onclick = this.handleDownload; L.Poll.add(L.bind(updateButtons, m),1); captureFilePoll = L.bind(updatePollCheckCaptureFileExists, m); L.Poll.add(captureFilePoll,1); return m.render(); }, });