blob: 7bcba67a7cdb3eb7cc35df14f788e758bff87beb [file] [log] [blame]
// Copyright 2015 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Handles file actions and triggers pcap processing.
* Pcap files are converted into PSML and PDML using a ported version of
* tshark which runs under native client. After processing, the information is
* displayed in the document.
*/
'use strict';
/**
* Namespace for the Packet Trace Analyzer app.
*/
var pcap = pcap || {};
/**
* The container for the packet's summary information.
*/
pcap.psmlDocument = new pcap.PsmlDocument();
/**
* The container for the packet's detailed information.
*/
pcap.pdmlDocument = new pcap.PdmlDocument();
/**
* Executes the tshark native executable with the specifed arguments and calls
* back when finished.
*
* For example, executing tshark with the following arguments:
* -r /tmp/input.pcap -T psml -o /tmp/output.psml
* will tell tshark to open input.pcap from the temporary file system,
* generate the packet summary, and write the output to output.psml in the
* temporary file system.
*
* @param {array<string>} args The flags and arguments for the executable.
* @param {function()} callback Called when finished executing.
*/
pcap.executeTshark = function(args, callback) {
var listeners = document.getElementById('listeners');
var div = document.createElement('div');
var listener = document.createElement('div');
var embed = document.createElement('embed');
embed.setAttribute('width', 0);
embed.setAttribute('height', 0);
embed.setAttribute('src', './tshark.nmf');
embed.setAttribute('type', 'application/x-nacl');
embed.setAttribute('PS_VERBOSITY', '0');
embed.setAttribute('PS_EXIT_MESSAGE', 'exit');
embed.setAttribute('PS_TTY_PREFIX', 'a');
embed.setAttribute('PS_STDOUT', '/dev/tty');
embed.setAttribute('PS_STDERR', '/dev/console1');
var argCount = args.length;
for (var i = 0; i < argCount; i++) {
var key = 'arg' + (i + 1);
var value = args[i];
embed.setAttribute(key, value);
}
embed.addEventListener('crash', console.error);
embed.addEventListener('error', console.error);
listener.appendChild(embed);
listener.addEventListener('message', function(message) {
if (message.data.search('exit') >= 0) { /* true when module is finished */
callback();
}
}, true);
div.appendChild(listener);
listeners.appendChild(div);
};
/**
* Handles setting up the listeners for the application.
*/
pcap.onDomContentLoaded = function() {
var pcapFileInput = document.getElementById('pcap_file_input');
if (!pcapFileInput) {
console.error('document.addEventListener: missing pcap file input.');
return;
}
pcapFileInput.addEventListener('change', pcap.onPcapFileInputChanged);
};
/**
* Called when the file input is changed. If a valid file is found, it is
* copied over to the sandboxed temporary file system, and then the PSML and
* PDML are loaded into the document.
*/
pcap.onPcapFileInputChanged = function() {
var file = pcap.getFileFromFileInput('pcap_file_input');
if (file) {
pcap.copyFileToTemporaryFileSystem(file, function() {
pcap.loadPsmlAndPdml(file.name);
});
}
};
/**
* Function to get the file from the file input.
*
* @param {string} fileInputId The ID of the file input.
* @return {fileObject} The file or null if missing.
*/
pcap.getFileFromFileInput = function(fileInputId) {
pcap.clearState();
var pcapFileInput = document.getElementById(fileInputId);
if (!pcapFileInput) {
console.error('process_pcap: missing pcap file input.');
return null;
}
var files = pcapFileInput.files;
if (!files[0]) {
console.log('process_pcap: file selection cancelled.');
return null;
}
else if (files[0].type !== 'application/vnd.tcpdump.pcap') {
pcap.displayError('Invalid file type.');
return null;
}
console.log('process_pcap: got the file: ' + files[0].name);
return files[0];
};
/**
* Function to copy a file to the sandboxed temporary file system.
*
* @param {fileObject} file The file to copy over.
* @param {function()} callback Called when finished copying.
*/
pcap.copyFileToTemporaryFileSystem = function(file, callback) {
window.webkitRequestFileSystem(window.TEMPORARY, 1024 * 1024, function(fs) {
fs.root.getFile(file.name, {create: true}, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
fileWriter.write(file);
fileWriter.onwriteend = callback;
}, console.error);
}, console.error);
}, console.error);
};
/**
* Function to load the PSML and PDML from the pcap file using tshark and
* display the information in the document.
*
* @param {string} filename The name of the file to read.
*/
pcap.loadPsmlAndPdml = function(filename) {
document.getElementById('psml_display').innerText = 'Loading PSML...';
document.getElementById('pdml_display').innerText = 'Loading PDML...';
pcap.loadTsharkTextOutput('psml', filename, 'psml.xml', function(psml) {
if (psml && pcap.psmlDocument.read(psml)) {
pcap.psmlDocument.display();
pcap.loadTsharkTextOutput('pdml', filename, 'pdml.xml', function(pdml) {
if (pdml && pcap.pdmlDocument.read(pdml)) {
pcap.pdmlDocument.display();
} else {
pcap.displayError('Failed to load PDML.');
}
});
} else {
pcap.displayError('Failed to load PSML.');
}
});
};
/**
* Executes tshark and calls back with the resulting xml.
*
* @param {string} type The format of the text output (psml, pdml).
* @param {string} ifilename The input file name to read from.
* @param {string} ofilename The ouptut file name to write to.
* @param {function(Document)} callback Called with the resulting text output.
*/
pcap.loadTsharkTextOutput = function(type, ifilename, ofilename, callback) {
pcap.executeTshark([
'-r',
'/tmp/' + ifilename,
'-T',
type,
'-o',
'/tmp/' + ofilename
], function() {
pcap.readXmlFromFile(ofilename, callback);
});
};
/**
* Opens and reads the specified file. If the file is a valid xml file, it
* calls back with the resulting XML.
*
* @param {string} filename The name of the file to read.
* @param {function(xml)} callback Called with the resulting xml.
*/
pcap.readXmlFromFile = function(filename, callback) {
window.webkitRequestFileSystem(window.TEMPORARY, 1024 * 1024, function(fs) {
fs.root.getFile(filename, {create: false}, function(fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
var parser = new DOMParser();
var xml = parser.parseFromString(reader.result, 'application/xml');
callback(xml);
};
reader.readAsText(file);
}, console.error);
}, console.error);
}, console.error);
};
/**
* Displays an error message to the user.
*
* @param {string} message The error message to display.
*/
pcap.displayError = function(message) {
var errorElement = document.getElementById('error');
errorElement.innerText = message;
};
/**
* Clears the state of the application.
*/
pcap.clearState = function() {
var psmlDisplayEl = document.getElementById('psml_display');
var pdmlDisplayEl = document.getElementById('pdml_display');
var errorEl = document.getElementById('error');
if (!psmlDisplayEl || !pdmlDisplayEl || !errorEl) {
console.error('clearState: missing elements');
return;
}
psmlDisplayEl.innerText = '';
pdmlDisplayEl.innerText = '';
errorEl.innerText = '';
};
/**
* The main entry point to the application.
*/
document.addEventListener('DOMContentLoaded', pcap.onDomContentLoaded);