├── .gitignore ├── bin └── network-monitor ├── docs └── screenshot.png ├── lib ├── args.js ├── log.js ├── constants.js └── ui.js ├── package.json ├── README.MD └── app.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Node modules 2 | node_modules 3 | -------------------------------------------------------------------------------- /bin/network-monitor: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('../app.js').start(); 4 | -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DhavalKapil/network-monitor/HEAD/docs/screenshot.png -------------------------------------------------------------------------------- /lib/args.js: -------------------------------------------------------------------------------- 1 | // Requiring external modules 2 | var argv = require('argv'); 3 | 4 | // Requiring custom modules 5 | var CONST = require('./constants.js'); 6 | 7 | // Setting up argv 8 | argv.option(CONST.OPTIONS); 9 | argv.version(CONST.VERSION); 10 | argv.info(CONST.DESCRIPTION); 11 | 12 | var arguments = argv.run(); 13 | 14 | var params = new Object(); 15 | params.interface = arguments.options.interface || 'any'; 16 | params.filter = arguments.options.filter || 'ip proto \\tcp'; 17 | params.log = arguments.options.log; 18 | 19 | exports.params = params; -------------------------------------------------------------------------------- /lib/log.js: -------------------------------------------------------------------------------- 1 | 2 | // Requiring external modules 3 | var fs = require('fs'); 4 | 5 | // Function to add double quotes to a string if there is a space in between 6 | function clean(str) { 7 | if(str.indexOf(' ')!=-1) { 8 | return '\"' + str + '\"'; 9 | } 10 | 11 | return str; 12 | } 13 | 14 | var writeLog = function(httpRequest, timestamp, logFile) { 15 | var log = clean(httpRequest.headers.Host) + ' '; 16 | log += '[' + timestamp.format('DD/MMM/YYYY:HH:mm:ss ZZ') + '] '; 17 | log += clean(httpRequest.method + ' ' + httpRequest.url + ' HTTP/' + httpRequest.http_version) + ' '; 18 | log += clean(httpRequest.headers['User-Agent']); 19 | log += '\n'; 20 | 21 | // Writing to a file 22 | fs.appendFile(logFile, log, function(err) { 23 | if(err) 24 | throw err; 25 | }); 26 | } 27 | 28 | exports.writeLog = writeLog; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "network-monitor", 3 | "version": "1.0.2", 4 | "description": "Tool to analyze and monitor network traffic", 5 | "keywords": [ 6 | "node", 7 | "network", 8 | "packet", 9 | "monitor", 10 | "analyzer", 11 | "traffic" 12 | ], 13 | "bugs": "https://github.com/DhavalKapil/network-monitor/issues", 14 | "author": "Dhaval Kapil (https://dhavalkapil.com/)", 15 | "license": "MIT", 16 | "bin": { 17 | "network-monitor": "./bin/network-monitor" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/DhavalKapil/network-monitor.git" 22 | }, 23 | "dependencies": { 24 | "pcap": "*", 25 | "argv": "*", 26 | "blessed": "*", 27 | "moment": "*" 28 | }, 29 | "engines": { 30 | "node": ">=0.10.25" 31 | }, 32 | "preferGlobal": true 33 | } 34 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # network-monitor 2 | 3 | > Tool to analyze and monitor network traffic 4 | 5 | - Current support for HTTP traffic 6 | - Log's to a file 7 | - A TUI based responsive display 8 | - Written in Node.js 9 | 10 | ### Screenshot 11 | 12 | ![Screenshot](docs/screenshot.png) 13 | 14 | ## Installation 15 | 16 | ``` 17 | [sudo] npm install network-monitor -g 18 | ``` 19 | 20 | ## Usage 21 | 22 | ``` 23 | [sudo] network-monitor -h 24 | ``` 25 | 26 | ## Dependencies 27 | 28 | - libpcap-dev: library for network traffic capture 29 | - [mranney/node_pcap](https://github.com/mranney/node_pcap): libpcap bindings for node 30 | - [codenothing/argv](https://github.com/codenothing/argv): for command line argument parsing 31 | - [chjj/blessed](https://github.com/chjj/blessed): A curses-like library for node.js. 32 | - [moment/moment](https://github.com/moment/moment): For parsing the time 33 | 34 | ## Common Issue 35 | 36 | ### IPv6 37 | 38 | IPv6 is not yet supported. use the filter `ip proto \tcp` to filter out IPv6 packets. 39 | 40 | ## Developer 41 | [Dhaval Kapil](https://dhavalkapil.com/) 42 | -------------------------------------------------------------------------------- /lib/constants.js: -------------------------------------------------------------------------------- 1 | // Requiring modules 2 | var packageJson = require('../package.json'); 3 | 4 | // Exporting version 5 | exports.VERSION = packageJson.version; 6 | 7 | // Exporting application name 8 | exports.NAME = packageJson.name; 9 | 10 | // Exporting description 11 | exports.DESCRIPTION = packageJson.description; 12 | 13 | // Exporting command line argument options 14 | exports.OPTIONS = [ 15 | { 16 | name: 'interface', 17 | short: 'i', 18 | type: 'string', 19 | description: 'Defines the interface on which to listen to. default value = \'any\'', 20 | example: 'sudo node app.js --interface=eth0 or sudo node app.js -i eth0' 21 | }, 22 | { 23 | name: 'filter', 24 | short: 'f', 25 | type: 'string', 26 | description: 'Defines the filter to be applied on the interface. default value = \'ip proto\\tcp\'', 27 | example: 'sudo node app.js --filter=tcp or sudo node app.js -f tcp' 28 | }, 29 | { 30 | name: 'log', 31 | short: 'l', 32 | type: 'string', 33 | description: 'Defines the file to log the traffic to', 34 | example: 'sudo node app.js --log=File.txt or sudo node app.js -l File.txt' 35 | } 36 | ]; 37 | 38 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | // Requiring external modules 2 | var pcap = require('pcap'); 3 | var moment = require('moment'); 4 | 5 | // Requiring custom modules 6 | var args = require('./lib/args.js'); 7 | var CONST = require('./lib/constants.js'); 8 | var ui = require('./lib/ui.js'); 9 | var log = require('./lib/log.js'); 10 | 11 | // Starting a capture session 12 | var start = function() { 13 | var pcap_session = pcap.createSession(args.params.interface, args.params.filter); 14 | 15 | // Creating a TCP tracker 16 | var tcp_tracker = new pcap.TCP_tracker(); 17 | 18 | tcp_tracker.on('http request', function(session, http) { 19 | var logString = http.request.method + ' ' + http.request.url + ' HTTP/' + http.request.http_version; 20 | var headers = http.request.headers; 21 | 22 | var timestamp = moment(); 23 | 24 | // display log entry in ui 25 | ui.displayLog(logString, headers, timestamp); 26 | 27 | // Log the packet transfer to a file if specified 28 | if(typeof args.params.log !== 'undefined') { 29 | log.writeLog(http.request, timestamp, args.params.log); 30 | } 31 | }); 32 | 33 | // Listening on packets 34 | pcap_session.on('packet', function(raw_packet) { 35 | // Decoding raw packet 36 | var packet = pcap.decode.packet(raw_packet); 37 | 38 | // Track TCP packets 39 | if(packet.link.ip.protocol_name === 'TCP') { 40 | tcp_tracker.track_packet(packet); 41 | } 42 | }); 43 | }; 44 | 45 | exports.start = start; -------------------------------------------------------------------------------- /lib/ui.js: -------------------------------------------------------------------------------- 1 | // Requiring external modules 2 | var blessed = require('blessed'); 3 | 4 | // Requiring custom modules 5 | var CONST = require('./constants.js'); 6 | 7 | // Array storing packet logs in the form of objects 8 | var packet_logs = []; 9 | var packet_headers = []; 10 | 11 | // Current ID of log 12 | var id = 0; 13 | 14 | // Acquiring the screen 15 | var screen = blessed.screen(); 16 | 17 | // The outer box 18 | var outer_box = blessed.box({ 19 | fg: '#999999', 20 | bg: 'default', 21 | border: { 22 | type: 'line', 23 | fg: '#ffffff' 24 | }, 25 | tags: true, 26 | content: '{center}' + CONST.NAME.toUpperCase() + '{/center}\n\n{center}' + CONST.DESCRIPTION + '{/center}', 27 | width: '90%', 28 | height: '95%', 29 | top: 'center', 30 | left: 'center' 31 | }); 32 | 33 | // A list consisting of the logs 34 | var logs = blessed.list({ 35 | parent: outer_box, 36 | width: '90%', 37 | height: '40%', 38 | top: '15%', 39 | left: 'center', 40 | fg: 'blue', 41 | border: { 42 | type: 'line', 43 | }, 44 | scrollbar: { 45 | fg: 'blue', 46 | ch: '|' 47 | }, 48 | selectedBg: '#222222', 49 | mouse: true, 50 | keys: true, 51 | vi: true 52 | }); 53 | 54 | // Adding heading to the logs list 55 | logs.prepend(new blessed.Text({ 56 | left: '5%', 57 | content: ' Logs ' 58 | })); 59 | 60 | // The header details of the requests 61 | var details = blessed.list({ 62 | parent: outer_box, 63 | width: '55%', 64 | height: '40%', 65 | top: '55%', 66 | left: '5%', 67 | fg: 'blue', 68 | border: { 69 | type: 'line' 70 | }, 71 | scrollbar: { 72 | fg: 'blue', 73 | ch: '|' 74 | }, 75 | selectedBg: '#222222', 76 | mouse: true, 77 | keys: true, 78 | vi: true 79 | }); 80 | 81 | // Adding heading to the details 82 | details.prepend(new blessed.Text({ 83 | left: '5%', 84 | content: ' Header details ' 85 | })); 86 | 87 | // ScrollableText to show full text 88 | var display = blessed.scrollabletext({ 89 | parent: outer_box, 90 | width: '30%', 91 | height: '40%', 92 | top: '55%', 93 | left: '65%', 94 | fg: 'blue', 95 | border: { 96 | type: 'line' 97 | }, 98 | tags: true 99 | }); 100 | 101 | // Appending the outer box to the screen 102 | screen.append(outer_box); 103 | 104 | // Adding key handler to escape the application 105 | screen.key(['escape', 'q', 'C-c'], function(ch, key) { 106 | process.exit(0); 107 | }); 108 | 109 | // Rendering the screen to the terminal 110 | screen.render(); 111 | 112 | /****************************************************************************************************************/ 113 | /** 114 | * Event Handlers start 115 | */ 116 | 117 | logs.on('mouseover', function() { 118 | logs.focus(); 119 | }); 120 | 121 | details.on('mouseover', function() { 122 | details.focus(); 123 | }); 124 | 125 | logs.on('scroll', function() { 126 | var value = logs.value; 127 | var id = parseInt(value.substring(1, value.indexOf(' '))); 128 | showHeaders(packet_headers[id]); 129 | showDisplay(value, 'Log'); 130 | screen.render(); 131 | }); 132 | 133 | details.on('scroll', function() { 134 | var value = details.value; 135 | showDisplay(value, 'Header-detail'); 136 | }); 137 | 138 | /** 139 | * Event handlers stop 140 | */ 141 | /****************************************************************************************************************/ 142 | 143 | // Function to pad `str` with leading 'z' 144 | function padLeft(str, width, z) { 145 | z = z || '0'; 146 | str = str + ''; 147 | return str.length>=width?str:(new Array(width - str.length + 1).join(z) + str); 148 | } 149 | 150 | // Function to pad `str` with `z` on right 151 | function padRight(str, width, z) { 152 | z = z || ' '; 153 | str = str + ''; 154 | return str.length>=width?str:(str + new Array(width - str.length + 1).join(z)); 155 | } 156 | 157 | // Function to display the headers 158 | function showHeaders(headers) { 159 | details.clearItems(); 160 | for(var field in headers) { 161 | details.add(field + ' : ' + headers[field]); 162 | } 163 | } 164 | 165 | // Function to show content in `display` 166 | function showDisplay(content, caller) { 167 | display.setContent('{center}{bold}' + caller + '{/bold}{/center}\n\n' + content); 168 | } 169 | 170 | var displayLog = function(log, headers, timestamp) { 171 | 172 | log = '#' + padLeft(id, 4, 0) + ' : [' + timestamp.format('DD/MMM/YYYY:HH:mm:ss ZZ') + '] ' + padRight(headers.Host, 30, ' ') + ' ' + log; 173 | id++; 174 | 175 | // Adding log to packet_logs 176 | packet_logs.push(log); 177 | 178 | // Adding header to packet_headers 179 | packet_headers.push(headers); 180 | 181 | // Displaying the result 182 | logs.add(log); 183 | logs.select(id-1); 184 | }; 185 | 186 | // Exporting variables 187 | exports.displayLog = displayLog; --------------------------------------------------------------------------------