├── .gitignore ├── .jshintrc ├── .travis.yml ├── ChangeLog.md ├── LICENSE ├── README.md ├── index.js ├── lib ├── client.js ├── ports.json ├── portscan.js ├── server.js ├── udp_client.js ├── udp_server.js └── util.js ├── package.json └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | test.* 3 | *.csv 4 | .DS_Store 5 | coverage/* 6 | *.html 7 | npm-debug.log 8 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "boss": true, 3 | "node": true, 4 | "strict": false, 5 | "smarttabs": true, 6 | "maxlen": 80, 7 | "newcap": false, 8 | "undef": true, 9 | "unused": true, 10 | "onecase": true, 11 | "indent": 2, 12 | "sub": true 13 | } 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.11" 4 | - "0.10" 5 | - "0.8" 6 | branches: 7 | only: 8 | - master 9 | notifications: 10 | email: 11 | - joaquim.serafim@gmail.com 12 | script: npm test 13 | before_install: 14 | - npm install -g npm@~1.4.6 15 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | 2013.12.30, Version 1.0.0 2 | 3 | * TCP client, receive and send messages (Netcat.client) 4 | * TCP server, listen on arbitary TCP ports and response to the received messages (Netcat.server) 5 | * PortScan (Netcat.portscan) 6 | * only deal with IPv4 and TCP 7 | 8 | 9 | 2013.12.30, Version 1.0.1 10 | 11 | * Server - event "data" add new param "client" to identify who the sender 12 | 13 | 14 | 2013.12.30, Version 1.0.2 15 | 16 | * Server - implement encoding 17 | * Client - implement encoding 18 | 19 | 20 | 2013.12.31, Version 1.0.3 21 | 22 | * Server - method 'send', when a client close connection and the server is write to socket at the moment throws a exception. 23 | 24 | 25 | 2013.12.31, Version 1.0.4 26 | 27 | * Server - add new event "client_off" fires when a client disconnet, change event "client" to "client_on" 28 | 29 | 30 | 2014.01.01, Version 1.0.5 31 | 32 | * Server - the object initialization '(port, encoding)' now use the parameter options (encoding, socket timeout) '(port, options={encoding, timeout})' 33 | * Client - move the configuration params from method to the constructor class, change the method name 'init' to 'start' 34 | 35 | 2014.01.02, Version 1.1.5 36 | 37 | * Server/Client - to suport receive the original Buffer objects, now 'options' parameter have the new properties 'read_encoding' (default to buffer) and 'write_encoding' (default to ascii) 38 | * Client - method 'end' was removed, 'send' suports close the connection "send(message_to_send, [close_connection], [callback])", close_connection default to false 39 | * PortScan - change to suport the new features from Client 40 | 41 | 42 | 2014.01.23, Version 1.3.5 43 | 44 | * Implemented UDP Protocol / Update Client - Server - PortScan 45 | * License ISC 46 | 47 | 48 | 2014.01.29, Version 1.3.5 49 | 50 | * Add parameter "host" in Server lib 51 | 52 | 53 | 2014.01.31, Version 1.4.5 54 | 55 | * Client/Server - removed option 'write_encoding' now all writes are passed as Buffer. 56 | 57 | 58 | 2014.02.03, Version 1.4.6 59 | 60 | * Client/Server - Fix bug in 'send' method when sending of a value as 'null' 61 | 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The ISC License 2 | 3 | Copyright (c) 2014, Joaquim José F. Serafim 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 15 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-netcat 2 | 3 | 4 | 5 | 6 | [![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg?style=flat-square)](https://travis-ci.org/joaquimserafim/node-netcat)![Code Coverage 100%](https://img.shields.io/badge/code%20coverage-100%25-green.svg?style=flat-square)[![ISC License](https://img.shields.io/badge/license-ISC-blue.svg?style=flat-square)](https://github.com/joaquimserafim/node-netcat/blob/master/LICENSE) 7 | 8 | ## Description 9 | 10 | Arbitrary TCP and UDP connections and listens to be used in Node.js 11 | 12 | 13 | >This module try to implement all that "nc" allows to be used in Node.js, this is a good module to implement simple server/client testing stuff or even to create simple tcp servers and clients. 14 | 15 | 16 | 17 | #### Current features: 18 | * open TCP/UDP connections and sending messages (client) 19 | * listen on arbitary TCP/UDP ports and response to the received messages (server) 20 | * PortScan (portscan) 21 | * TCP only deal with IPV4 22 | 23 | `var Netcat = require('node-netcat')` 24 | 25 | | nc | node-netcat| 26 | |----|------------| 27 | | nc listener (-k -l cmdline) | Necat.server | 28 | | nc host port | Netcat.client | 29 | | nc -z host port_start[-port_end] | Netcat.portscan | 30 | 31 | 32 | 33 | ### Installation 34 | 35 | npm i --save node-netcat 36 | 37 | 38 | ## Netcat -> API 39 | 40 | ### Client 41 | 42 | `Netcat.client(port, host, [options])` 43 | 44 | * **port** {int} required 45 | * **host** {string} required 46 | * **options** 47 | * **timeout** {int} define a connection timeout in miliseconds, default to 60000, 48 | * **read_encoding** {string} the read encoding, default to 'buffer', others values ascii, hex,utf8, base64 49 | 50 | #### start() 51 | client starts the connection 52 | 53 | #### close() 54 | close the connection 55 | 56 | #### send(message, [close_connection], [callback]) 57 | send messages and can close the connection after send the message 58 | 59 | * **message** {string} don't need to be a Buffer 60 | * **close_connection** {boolean} default to false 61 | * **callback** - {function} ? 62 | 63 | #### events 64 | * **open** callback() 65 | * **data** callback(data) 66 | * **error** callback(err) 67 | * **close** callback() 68 | 69 | 70 | ### Server (-k -l) 71 | 72 | `new Netcat.server(port, [host], [options])` 73 | 74 | * **port** {int} required 75 | * **host** {string} required 76 | * **options** 77 | * **timeout** {int} define a connection timeout in miliseconds, default to 60000, 78 | * **read_encoding** {string} the read encoding, default to 'buffer', others values ascii, hex,utf8, base64 79 | 80 | 81 | #### listen() 82 | initialize the server 83 | 84 | #### close() 85 | close the server but must not exists active clients 86 | 87 | #### send(client, message, [close_connection], [callback]) 88 | send messages to a particular client and can close the connection after send the message 89 | 90 | * **message** {string} don't need to be a Buffer 91 | * **close_connection** {boolean} default to false 92 | * **callback** - {function} ? parameter will be executed when the data is finally written out, this may not be immediately 93 | 94 | #### getClients() 95 | return an array with all active clients 96 | 97 | #### events 98 | * **ready** callback() - server it's ready 99 | * **data** callback(data) 100 | * **client_on** callback(client) 101 | * **client_off** callback(client) 102 | * **error** callback(err) 103 | * **close** callback() 104 | 105 | 106 | ### UDP Client (-u) 107 | 108 | `Netcat.udpClient(port, host, [options])` 109 | 110 | * **port** {int} required 111 | * **host** {string} required 112 | * **options** 113 | * **timeout** {int} define a connection timeout in miliseconds, default to 60000, 114 | * **read_encoding** {string} the read encoding, default to 'buffer', others values ascii, hex,utf8, base64 115 | 116 | #### events 117 | * **open** callback() 118 | * **message** callback(message, {port, address}, protocol_family*[ipv4 | ipv6]*) 119 | * **error** callback(err) 120 | * **close** callback() 121 | 122 | #### start() 123 | init the client 124 | 125 | #### close() 126 | close the client 127 | 128 | #### send(message) 129 | send a message and the message should not be a Buffer 130 | 131 | ### A Note about UDP datagram size 132 | 133 | > The maximum size of an IPv4/v6 datagram depends on the MTU (Maximum Transmission Unit) and on the Payload Length field size. 134 | > 135 | > The Payload Length field is 16 bits wide, which means that a normal payload cannot be larger than 64K octets including internet header and data (65,507 bytes = 65,535 − 8 bytes UDP header − 20 bytes IP header); this is generally true for loopback interfaces, but such long datagrams are impractical for most hosts and networks. 136 | > 137 | > The MTU is the largest size a given link layer technology can support for datagrams. For any link, IPv4 mandates a minimum MTU of 68 octets, while the recommended MTU for IPv4 is 576 (typically recommended as the MTU for dial-up type applications), whether they arrive whole or in fragments. 138 | > 139 | > For IPv6, the minimum MTU is 1280 octets, however, the mandatory minimum fragment reassembly buffer size is 1500 octets. The value of 68 octets is very small, since most current link layer technologies have a minimum MTU of 1500 (like Ethernet). 140 | > 141 | > Note that it's impossible to know in advance the MTU of each link through which a packet might travel, and that generally sending a datagram greater than the (receiver) MTU won't work (the packet gets silently dropped, without informing the source that the data did not reach its intended recipient). 142 | 143 | 144 | ### UDP Server (-u -k -l) 145 | 146 | `Netcat.udpServer(port, host, [options])` 147 | 148 | * **port** {int} required 149 | * **host** {string} required 150 | * **options** 151 | * **timeout** {int} define a connection timeout in miliseconds, default to 60000, 152 | * **read_encoding** {string} the read encoding, default to 'buffer', others values ascii, hex,utf8, base64 153 | 154 | #### bind() 155 | binding to a port 156 | 157 | #### close() 158 | 159 | #### events 160 | * **ready** callback() - server it's ready 161 | * **data** callback(data) 162 | * **error** callback(err) 163 | * **close** callback() 164 | 165 | 166 | ### PortScan (-z [port_start-port_end]) 167 | 168 | #### scan.run(host, ports*, callback) 169 | * **host** {string} 170 | * **ports** {int | expression} a single port 80 or between various ports for example: 22-80 171 | * **callback** {function} 172 | 173 | 174 | ## Examples 175 | 176 | ### Client 177 | 178 | ```javascript 179 | var NetcatClient = require('node-netcat').client; 180 | var client = NetcatClient(5000, 'localhost'); 181 | 182 | client.on('open', function () { 183 | console.log('connect'); 184 | client.send('this is a test' + '\n'); 185 | }); 186 | 187 | client.on('data', function (data) { 188 | console.log(data.toString('ascii')); 189 | client.send('Goodbye!!!', true); 190 | }); 191 | 192 | client.on('error', function (err) { 193 | console.log(err); 194 | }); 195 | 196 | client.on('close', function () { 197 | console.log('close'); 198 | }); 199 | 200 | client.start(); 201 | ``` 202 | 203 | ### Server 204 | 205 | ```javascript 206 | var NetcatServer = require('node-netcat').server; 207 | var server = NetcatServer(5000); 208 | 209 | server.on('ready', function() { 210 | console.log('server ready'); 211 | }); 212 | 213 | server.on('data', function(client, data) { 214 | console.log('server rx: ' + data + ' from ' + client); 215 | }); 216 | 217 | server.on('client_on', function(client) { 218 | console.log('client on ', client); 219 | }); 220 | 221 | server.on('client_of', function(client) { 222 | console.log('client off ', client); 223 | }); 224 | 225 | server.on('error', function(err) { 226 | console.log(err); 227 | }); 228 | 229 | server.on('close', function() { 230 | console.log('server closed'); 231 | }); 232 | 233 | server.listen();// start to listening 234 | 235 | // get active clients 236 | var clients = server.getClients(); 237 | 238 | // send messages to clients and close the connection 239 | Object.keys(clients).forEach(function(client) { 240 | server.send(clients[client], 'received ' + data, true); 241 | }); 242 | 243 | // or a normal message 244 | server.send(client, 'message'); 245 | ``` 246 | 247 | ### UDP Client 248 | 249 | ```javascript 250 | var NetcatUdpClient = require('node-netcat').udpClient; 251 | var client = NetcatUdpClient(5000, '127.0.0.1'); 252 | 253 | client.on('open', function() { 254 | console.log('open'); 255 | }); 256 | 257 | client.once('error', function(err) { 258 | console.error('err'); 259 | }); 260 | 261 | client.once('close', function() { 262 | console.log('client, closed'); 263 | }); 264 | 265 | clien.send('Hello World'); 266 | ``` 267 | 268 | ### UDP Server 269 | 270 | ```javascript 271 | var NetcatUdpServer = require('node-netcat').udpServer; 272 | var server = NetcatUdpServer(5000, '127.0.0.1'); 273 | 274 | server.on('data', function(msg, client, protocol) { 275 | console.log('rx: ' + msg + ', from ' + client); 276 | }); 277 | 278 | server.on('ready', function() { 279 | console.log('ready'); 280 | }); 281 | 282 | server.once('error', function(err) { 283 | console.log(err); 284 | }); 285 | 286 | server.once('close', function() { 287 | console.log('close'); 288 | }); 289 | 290 | server.bind(); 291 | 292 | setTimeout(function () { 293 | server.close(); 294 | }, 30000); 295 | ``` 296 | 297 | 298 | ### PortScan 299 | 300 | ```javascript 301 | var scan = require('node-netcat').portscan(); 302 | 303 | scan.run('google.com', '80-81', function(err, res) { 304 | if (err) { 305 | // ERR 306 | } else { 307 | // RES 308 | } 309 | }); 310 | ``` 311 | 312 | 313 | ## Development 314 | 315 | ##### this projet has been set up with a precommit that forces you to follow a code style, no jshint issues and 100% of code coverage before commit 316 | 317 | to run test 318 | ``` js 319 | npm test 320 | ``` 321 | 322 | to run jshint 323 | ``` js 324 | npm run jshint 325 | ``` 326 | 327 | to run code style 328 | ``` js 329 | npm run code-style 330 | ``` 331 | 332 | to run check code coverage 333 | ``` js 334 | npm run check-coverage 335 | ``` 336 | 337 | to open the code coverage report 338 | ``` js 339 | npm run open-coverage 340 | ``` 341 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = { 4 | server : require('./lib/server'), 5 | client : require('./lib/client'), 6 | udpClient : require('./lib/udp_client'), 7 | udpServer : require('./lib/udp_server'), 8 | portscan : require('./lib/portscan') 9 | }; 10 | -------------------------------------------------------------------------------- /lib/client.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var net = require('net'); 4 | var inherits = require('util').inherits; 5 | var EventEmitter = require('events').EventEmitter; 6 | var _ = require('underscore'); 7 | var ut = require('./util'); 8 | 9 | module.exports = Client; 10 | 11 | function Client(port, host, options) { 12 | if (!(this instanceof Client)) { 13 | return new Client(port, host, options); 14 | } 15 | 16 | EventEmitter.call(this); 17 | Client.init.call(this, port, host, options); 18 | } 19 | 20 | inherits(Client, EventEmitter); 21 | 22 | Client.init = function(port, host, options) { 23 | if (!parseInt(port)) { 24 | throw new Error('in node-nc server the port is mandatory!'); 25 | } 26 | 27 | // check args 28 | if (_.isObject(host)) { 29 | options = host; 30 | host = 'localhost'; 31 | } 32 | 33 | this._port = port; 34 | this._host = host || 'localhost'; 35 | this._readEncoding = options && options.readEncoding; 36 | this._timeout = (options && options.timeout) || 30000; 37 | this._asStream = (options && options.asStream) || false; 38 | }; 39 | 40 | Client.prototype.start = function() { 41 | var self = this; 42 | // 43 | // events handlers 44 | // 45 | function connect() { 46 | self.emit('open'); 47 | } 48 | 49 | function data(chunk) { 50 | self.emit('data', chunk); 51 | } 52 | 53 | function error(err) { 54 | self.emit('error', err); 55 | } 56 | 57 | function close() { 58 | self.emit('close'); 59 | } 60 | 61 | function timeout() { 62 | self._client.destroy(); 63 | self.emit('error', {message: 'connect ETIMEDOUT'}); 64 | } 65 | 66 | this._client = net.connect({ 67 | port: self._port, 68 | host: self._host 69 | }, connect); 70 | 71 | // to receive buffer 72 | if (this._readEncoding) { 73 | this._client.setEncoding(this._readEncoding); 74 | } 75 | 76 | this._client.setTimeout(this._timeout); 77 | 78 | // 79 | // register events 80 | // 81 | 82 | process.nextTick(function register() { 83 | this._client.on('data', data); 84 | this._client.on('error', error); 85 | this._client.on('timeout', timeout); 86 | this._client.on('close', close); 87 | }, this); 88 | 89 | // want pass has a stream 90 | if (this.asStream) { 91 | return this._client; 92 | } 93 | }; 94 | 95 | Client.prototype.send = function(msg, end, cb) { 96 | if (typeof end === 'function') { 97 | cb = end; 98 | end = false; 99 | } 100 | 101 | cb = cb || ut.noop; 102 | 103 | // send null 104 | if (!msg) { 105 | msg = new Buffer([0x00]); 106 | } 107 | 108 | if (msg && !Buffer.isBuffer(msg)) { 109 | msg = new Buffer(msg); 110 | } 111 | 112 | // send and close connection 113 | if (end) { 114 | this._client.end(msg); 115 | cb(); 116 | } else { 117 | this._client.write(msg || '', cb); 118 | } 119 | }; 120 | 121 | Client.prototype.close = function() { 122 | this._client.end(); 123 | return this; 124 | }; 125 | -------------------------------------------------------------------------------- /lib/portscan.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('underscore'); 4 | var json = require('./ports.json'); 5 | var TCP = require('./client'); 6 | var UDP = require('./udp_client'); 7 | var lasync = require('lasync'); 8 | 9 | module.exports = PortScan; 10 | 11 | function PortScan() { 12 | if (!(this instanceof PortScan)) { 13 | return new PortScan(); 14 | } 15 | } 16 | 17 | PortScan.prototype.run = function(host, scan, done) { 18 | function connect(cb) { 19 | var port = ports.shift(); 20 | 21 | // error - return main callback 22 | function errorMsg(err, protocol) { 23 | return 'Connection OFF to ' + 24 | host + ' port ' + 25 | port + ', protocol "' + 26 | protocol + '" with message "' + 27 | err + '"'; 28 | } 29 | 30 | function respMsg(desc) { 31 | desc = desc || {prot: null, svc: null, desc: null}; 32 | 33 | return 'Connection ON to ' + host + 34 | ' port ' + port + 35 | ' [' + desc.prot + 36 | '/' + desc.svc + 37 | '/' + desc.desc + ']'; 38 | } 39 | 40 | function runUdp(next) { 41 | var udp = new UDP(port, host, { 42 | type: 'udp4', 43 | timeout: 1000, 44 | 'read_encoding': 'ascii' 45 | }); 46 | 47 | var udpAnswer = ''; 48 | 49 | // rx something 50 | udp.on('data', function(data) { 51 | udpAnswer = data; 52 | }); 53 | 54 | udp.on('open', function() { 55 | udp.send('qwerty'); 56 | }); 57 | 58 | udp.on('error', function(err) { 59 | next(null, {err: errorMsg(err.message, 'udp')}); 60 | }); 61 | 62 | udp.on('close', function() { 63 | if (!udpAnswer) { 64 | return next(null, { 65 | err: errorMsg('"connect ETIMEDOUT"', 'udp') 66 | }); 67 | } 68 | 69 | var desc = _.find(json, function(obj) { 70 | return obj.prot === 'udp' && obj.port === port; 71 | }); 72 | 73 | // return main callback 74 | next(null, {ok: respMsg(desc)}); 75 | }); 76 | 77 | udp.start(); 78 | } 79 | 80 | function runTcp(next) { 81 | var tcp = new TCP(port, host, { 82 | timeout: 1000, 83 | 'read_encoding': 'ascii' 84 | }); 85 | 86 | var desc = _.find(json, function(obj) { 87 | return obj.prot === 'tcp' && obj.port === port; 88 | }); 89 | 90 | // communicate and close the connection 91 | tcp.on('open', function() { 92 | tcp.send('qwerty', true, function() { 93 | // return main callback 94 | next(null, {ok: respMsg(desc)}); 95 | }); 96 | }); 97 | 98 | tcp.on('error', function(err) { 99 | next(null, {err: errorMsg(err.message, 'tcp')}); 100 | }); 101 | 102 | tcp.on('close', function() {}); 103 | tcp.start(); 104 | } 105 | 106 | lasync.parallel([runUdp, runTcp], function(err, result) { 107 | cb(err, result); 108 | }); 109 | } 110 | 111 | // get port start and port end 112 | var scanValue = _.map(scan.split('-'), function(value) { 113 | return value | 0; 114 | }); 115 | 116 | var ports = _.range(_.min(scanValue), _.max(scanValue) + 1); 117 | 118 | // create the list of ports to be scanned 119 | var tasks = _.map(ports, function() { 120 | return _.bind(connect); 121 | }); 122 | 123 | lasync.parallel(tasks, function(err, results) { 124 | if (err) { 125 | done(err); 126 | } else { 127 | _.each(results, function(res) { 128 | _.each(res, function(val) { 129 | done(val.err, val.ok); 130 | }); 131 | }); 132 | } 133 | }); 134 | }; 135 | -------------------------------------------------------------------------------- /lib/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var net = require('net'); 4 | var inherits = require('util').inherits; 5 | var EventEmitter = require('events').EventEmitter; 6 | var _ = require('underscore'); 7 | var ut = require('./util'); 8 | 9 | module.exports = Server; 10 | 11 | function Server(port, host, options) { 12 | if (!(this instanceof Server)) { 13 | return new Server(port, host, options); 14 | } 15 | 16 | EventEmitter.call(this); 17 | Server.init.call(this, port, host, options); 18 | } 19 | 20 | inherits(Server, EventEmitter); 21 | 22 | Server.init = function(port, host, options) { 23 | var self = this; 24 | 25 | if (!parseInt(port)) { 26 | throw new Error('in node-nc server the port is mandatory!'); 27 | } 28 | 29 | // check args 30 | if (_.isObject(host)) { 31 | options = host; 32 | host = 'localhost'; 33 | } 34 | 35 | this._clients = {}; 36 | this._port = port; 37 | this._host = host || 'localhost'; 38 | this.timeout = (options && options.timeout) || 3600000; 39 | 40 | function handler(socket) { 41 | var client = socket.remoteAddress + ':' + socket.remotePort; 42 | 43 | // socket configurations 44 | socket.setKeepAlive(true, 60000); 45 | socket.setTimeout(this._timeout); 46 | 47 | if (options && options.readEncoding) { 48 | socket.setEncoding(options.readEncoding); 49 | } 50 | 51 | // 52 | // client events handlers 53 | // 54 | 55 | function data(chunk) { 56 | self.emit('data', client, chunk); 57 | } 58 | 59 | function timeout() { 60 | socket.destroy(); 61 | } 62 | 63 | function error(err, cl, chunk) { 64 | self.emit('client_error', err, cl, chunk); 65 | } 66 | 67 | function close() { 68 | delete self._clients[client]; 69 | self.emit('client_off', client); 70 | } 71 | 72 | // 73 | // register events 74 | // 75 | 76 | process.nextTick(function register() { 77 | socket.on('data', data); 78 | socket.on('timeout', timeout); 79 | socket.on('error', error); 80 | socket.on('close', close); 81 | }); 82 | } 83 | 84 | this._server = net.createServer(handler); 85 | 86 | // 87 | // server events handlers 88 | // 89 | 90 | function listening() { 91 | self.emit('ready'); 92 | } 93 | 94 | function error(err) { 95 | self.emit('error', err); 96 | } 97 | 98 | function close() { 99 | self.emit('close'); 100 | } 101 | 102 | function conn(socket) { 103 | self._clients[socket.remoteAddress + ':' + socket.remotePort] = socket; 104 | self.emit('client_on', socket.remoteAddress + ':' + socket.remotePort); 105 | } 106 | 107 | // 108 | // register events 109 | // 110 | 111 | process.nextTick(function register() { 112 | this._server.on('listening', listening); 113 | this._server.on('connection', conn); 114 | this._server.on('close', close); 115 | this._server.on('error', error); 116 | }, this); 117 | }; 118 | 119 | Server.prototype.listen = function() { 120 | this._server.listen(this._port, this._host); 121 | }; 122 | 123 | Server.prototype.close = function(cb) { 124 | cb = cb || ut.noop; 125 | this._server.close(cb); 126 | }; 127 | 128 | Server.prototype.send = function(client, msg, end, cb) { 129 | if (typeof end === 'function') { 130 | cb = end; 131 | end = false; 132 | } 133 | 134 | cb = cb || ut.noop; 135 | msg = msg || new Buffer([0x00]); 136 | 137 | if (!Buffer.isBuffer(msg)) { 138 | msg = new Buffer(msg.toString()); 139 | } 140 | 141 | // check client exists 142 | if (_.contains(_.keys(this._clients), client)) { 143 | // client exists 144 | // send and close connection 145 | if (end) { 146 | this._clients[client].end(msg); 147 | cb(); 148 | } else { 149 | this._clients[client].write(msg, cb); 150 | } 151 | } else { 152 | this._clients[client].emit('error', 153 | new Error(), 154 | client, 155 | msg); 156 | cb(); 157 | } 158 | }; 159 | 160 | Server.prototype.getClients = function() { 161 | return _.keys(this._clients); 162 | }; 163 | -------------------------------------------------------------------------------- /lib/udp_client.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var dgram = require('dgram'); 4 | var inherits = require('util').inherits; 5 | var EventEmitter = require('events').EventEmitter; 6 | 7 | module.exports = Client; 8 | 9 | function Client(port, host, options) { 10 | if (!(this instanceof Client)) { 11 | return new Client(port, host, options); 12 | } 13 | 14 | EventEmitter.call(this); 15 | Client.init.call(this, port, host, options); 16 | } 17 | 18 | inherits(Client, EventEmitter); 19 | 20 | Client.init = function(port, host, options) { 21 | var self = this; 22 | 23 | this._port = port | 0; 24 | this._host = host || 'localhost'; 25 | this._timeout = (options && options.timeout) || 0; 26 | this._readEncoding = options && options['read_encoding']; 27 | 28 | this._client = dgram.createSocket((options && options.type) || 'udp4'); 29 | this._open = false; 30 | 31 | // events callabcks 32 | 33 | function message(msg, rinfo) { 34 | self._open = true; 35 | msg = self._readEncoding ? 36 | msg.toString(self._readEncoding) : 37 | msg; 38 | 39 | self.emit('data', msg, { 40 | port: rinfo.port, 41 | host: rinfo.address 42 | }, 43 | rinfo.family.toLowerCase()); 44 | } 45 | 46 | // udp don't have any handshaking dialogues, 47 | // open event it's merely representative in this 48 | function listening() { 49 | self.emit('open'); 50 | } 51 | 52 | function close() { 53 | self.emit('close'); 54 | } 55 | 56 | function error(err) { 57 | self.emit('error', err); 58 | } 59 | 60 | // 61 | // register events 62 | // 63 | 64 | process.nextTick(function register() { 65 | this._client.on('message', message); 66 | this._client.on('listening', listening); 67 | this._client.on('close', close); 68 | this._client.on('error', error); 69 | }, this); 70 | 71 | // timeout 72 | if (this._timeout) { 73 | setTimeout(function() { 74 | this.close(); 75 | }, this._timeout, this); 76 | } 77 | }; 78 | 79 | Client.prototype.send = function(msg, cb) { 80 | var buffer = new Buffer(msg + '\n'); 81 | this._client.send(buffer, 0, buffer.length, this._port, this._host, cb); 82 | }; 83 | 84 | Client.prototype.close = function() { 85 | this._client.close(); 86 | }; 87 | 88 | Client.prototype.start = function() { 89 | var self = this; 90 | this.send('qwerty', function(err) { 91 | if (err) { 92 | self.emit('error', err); 93 | } 94 | }); 95 | }; 96 | -------------------------------------------------------------------------------- /lib/udp_server.js: -------------------------------------------------------------------------------- 1 | var dgram = require('dgram'); 2 | var inherits = require('util').inherits; 3 | var EventEmitter = require('events').EventEmitter; 4 | 5 | module.exports = Server; 6 | 7 | function Server(port, host, socketType) { 8 | if (!(this instanceof Server)) { 9 | return new Server(port, host, socketType); 10 | } 11 | 12 | EventEmitter.call(this); 13 | Server.init.call(this, port, host, socketType); 14 | } 15 | 16 | inherits(Server, EventEmitter); 17 | 18 | Server.init = function(port, host, socketType) { 19 | var self = this; 20 | 21 | self._port = port || 4500; 22 | self._host = host || 'localhost'; 23 | self._socketType = socketType || 'udp4'; 24 | 25 | self._server = dgram.createSocket(this._socketType); 26 | 27 | // events callabcks 28 | 29 | function message(msg, rinfo) { 30 | self.emit('data', msg.toString().replace(/\n$/, ''), { 31 | port: rinfo.port, 32 | host: rinfo.address 33 | }, 34 | rinfo.family.toLowerCase()); 35 | } 36 | 37 | function listening() { 38 | self.emit('ready'); 39 | } 40 | 41 | function close() { 42 | self.emit('close'); 43 | } 44 | 45 | function error(err) { 46 | self.emit('error', err); 47 | } 48 | 49 | // events 50 | self._server.on('message', message); 51 | self._server.on('listening', listening); 52 | self._server.on('close', close); 53 | self._server.on('error', error); 54 | }; 55 | 56 | Server.prototype.send = function(msg, client, cb) { 57 | var buffer = new Buffer(msg + '\n'); 58 | this._server.send(buffer, 0, buffer.length, client.port, client.host, cb); 59 | }; 60 | 61 | Server.prototype.close = function() { 62 | this._server.close(); 63 | return 1; 64 | }; 65 | 66 | Server.prototype.bind = function(cb) { 67 | if (!cb) { 68 | cb = function() {}; 69 | } 70 | this._server.bind(this._port, this._host, cb); 71 | }; 72 | -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | noop: noop 5 | }; 6 | 7 | function noop() { 8 | return function() {}; 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-netcat", 3 | "version": "1.5.8", 4 | "description": "nc/netcat - arbitrary TCP and UDP connections and listens in node.js", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "istanbul cover tape test/*.js", 8 | "jshint": "jshint -c .jshintrc lib/*.js test/*.js", 9 | "code-style": "jscs -p google lib/*.js test/*.js", 10 | "check-coverage": "istanbul check-coverage --statements 100 --functions 100 --lines 100 --branches 100", 11 | "open-coverage": "open coverage/lcov-report/index.html" 12 | }, 13 | "files": [ 14 | "LICENSE", 15 | "README.md", 16 | "index.js", 17 | "lib" 18 | ], 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/joaquimserafim/node-netcat" 22 | }, 23 | "keywords": [ 24 | "netcat", 25 | "nc", 26 | "tcp", 27 | "udp", 28 | "portscan" 29 | ], 30 | "author": "@joaquimserafim", 31 | "license": "ISC", 32 | "devDependencies": { 33 | "istanbul": "^0.3.5", 34 | "jscs": "^1.10.0", 35 | "jshint": "^2.6.0", 36 | "pre-commit": "^1.0.1", 37 | "tape": "^3.5.0" 38 | }, 39 | "dependencies": { 40 | "lasync": "^1.1.0", 41 | "underscore": "^1.7.0" 42 | }, 43 | "pre-commit": [ 44 | "jshint", 45 | "code-style", 46 | "test", 47 | "check-coverage" 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | var Netcat = require('../'); 5 | 6 | var server; 7 | var client; 8 | 9 | test('nc server constructor - dont pass any param', function(assert) { 10 | assert.plan(1); 11 | 12 | try { 13 | server = Netcat.server(); 14 | } catch(ex) { 15 | assert.ok(ex, ex); 16 | } 17 | }); 18 | 19 | test('nc server constructor - pass only port & options', function(assert) { 20 | server = Netcat.server(4000, { 21 | readEncoding: 'utf8', 22 | timeout: 3000 23 | }); 24 | 25 | assert.ok(server); 26 | server.listen(); 27 | 28 | server.once('ready', function() { 29 | assert.pass('server, ready'); 30 | server.close(function() { 31 | assert.pass('server closed'); 32 | assert.end(); 33 | }); 34 | }); 35 | }); 36 | 37 | test('tx to a client has disconnected', function(assert) { 38 | var clients; 39 | server = Netcat.server(4000); 40 | client = Netcat.client(4000); 41 | 42 | assert.ok(server); 43 | assert.ok(client); 44 | 45 | server.listen(); 46 | 47 | server.once('ready', function() { 48 | assert.pass('server, ready'); 49 | client.start(); 50 | }); 51 | 52 | server.on('client_on', function(client) { 53 | assert.ok(client, 'server, client connect ' + client); 54 | clients = server.getClients(); 55 | assert.equal(clients.length, 1); 56 | assert.equal(clients[0], client); 57 | }); 58 | 59 | server.on('client_off', function(client) { 60 | assert.ok(client, 'server, client disconnet ' + client); 61 | }); 62 | 63 | server.on('client_error', function(err, client, chunk) { 64 | console.log(err, client, chunk); 65 | }); 66 | 67 | server.once('error', assert.fail); 68 | 69 | 70 | server.once('close', function() { 71 | assert.pass('server, closed'); 72 | assert.end(); 73 | }); 74 | 75 | client.once('open', function() { 76 | assert.pass('client, ready'); 77 | client.close(); 78 | server.send(client, 'Hello World!', function() { 79 | 80 | //server.close(); 81 | }); 82 | }); 83 | }); 84 | return; 85 | 86 | test('server & client using binary data', function(t) { 87 | t.plan(15); 88 | 89 | server = Netcat.server(4000); 90 | client = Netcat.client(4000); 91 | 92 | server.once('ready', function() { 93 | t.pass('server, ready'); 94 | client.start(); 95 | }); 96 | 97 | server.on('data', function(client, data) { 98 | t.equal(data.length > 0, 99 | true, 100 | 'server, receive data: "' + data + '" from client ' + client); 101 | 102 | // test Buffer 103 | t.equal(Buffer.isBuffer(data), 104 | true, 105 | 'server, is configure to rx as Buffer'); 106 | 107 | var clients = server.getClients(); 108 | t.ok(clients, 'server, exists ' + clients.length + ' client active'); 109 | 110 | // first send some messages without closing the conn 111 | Object.keys(clients).forEach(function(client) { 112 | server.send(clients[client], data, function() { 113 | t.pass('server, send "' + data + '" to client ' + clients[client]); 114 | }); 115 | }); 116 | 117 | // send empty data 118 | Object.keys(clients).forEach(function(client) { 119 | server.send(clients[client], '', function() { 120 | t.pass('server, send "' + data + '" to client ' + clients[client]); 121 | }); 122 | }); 123 | 124 | // now close the conn after tx the message 125 | Object.keys(clients).forEach(function(client) { 126 | server.send(clients[client], data, true, function() { 127 | t.pass('server, send "' + data + '" to client ' + clients[client]); 128 | }); 129 | }); 130 | 131 | setTimeout(function() { server.close(); }, 1000); 132 | }); 133 | 134 | server.on('client_on', function(client) { 135 | t.ok(client, 'server, client connect ' + client); 136 | }); 137 | 138 | server.on('client_off', function(client) { 139 | t.ok(client, 'server, client disconnet ' + client); 140 | }); 141 | 142 | server.once('error', function(err) { t.error(err !== null, err); }); 143 | server.once('close', function() { t.pass('server, closed'); }); 144 | server.listen(); 145 | 146 | // client 147 | client.once('open', function() { 148 | t.pass('client, connected'); 149 | setTimeout(function () { 150 | client.send('Hello World', function() { 151 | t.pass('client, send message'); 152 | }); 153 | }, 100); 154 | }); 155 | 156 | client.on('data', function(data) { 157 | t.equal(data.length > -1, true, 'client, receive data: ' + data); 158 | // test encoding 159 | t.equal(Buffer.isBuffer(data), 160 | true, 161 | 'client, is configure to rx binary data'); 162 | }); 163 | 164 | client.once('close', function() { 165 | t.pass('client, closed'); 166 | }); 167 | }); 168 | 169 | test('server & client using utf8 encoding', function(t) { 170 | t.plan(15); 171 | 172 | server = Netcat.server(4000, {readEncoding: 'utf8'}); 173 | client = Netcat.client(4000, {readEncoding: 'utf8'}); 174 | 175 | server.once('ready', function() { 176 | t.pass('server, ready'); 177 | client.start(); 178 | }); 179 | 180 | server.on('data', function(client, data) { 181 | t.equal(data.length > 0, 182 | true, 183 | 'server, receive data: "' + data + '" from client ' + client); 184 | 185 | // test Buffer 186 | t.equal(typeof data, 187 | 'string', 188 | 'server, is configure to rx string data'); 189 | 190 | var clients = server.getClients(); 191 | t.ok(clients, 'server, exists ' + clients.length + ' client active'); 192 | 193 | // some something without passing the callback 194 | Object.keys(clients).forEach(function(client) { 195 | server.send(clients[client], data); 196 | t.pass('server, send "' + data + '" to client ' + clients[client]); 197 | }); 198 | 199 | // first send some messages without closing the conn 200 | Object.keys(clients).forEach(function(client) { 201 | server.send(clients[client], data, function() { 202 | t.pass('server, send "' + data + '" to client ' + clients[client]); 203 | }); 204 | }); 205 | 206 | // now close the conn after tx the message 207 | Object.keys(clients).forEach(function(client) { 208 | server.send(clients[client], data, true, function() { 209 | t.pass('server, send "' + data + '" to client ' + clients[client]); 210 | }); 211 | }); 212 | 213 | setTimeout(function() { server.close(); }, 1000); 214 | }); 215 | 216 | server.on('client_on', function(client) { 217 | t.ok(client, 'server, client connect ' + client); 218 | }); 219 | 220 | server.on('client_off', function(client) { 221 | t.ok(client, 'server, client disconnet ' + client); 222 | }); 223 | 224 | server.once('error', function(err) { t.error(err !== null, err); }); 225 | server.once('close', function() { t.pass('server, closed'); }); 226 | server.listen(); 227 | 228 | 229 | // client 230 | client.once('open', function() { 231 | t.pass('client, connected'); 232 | setTimeout(function () { 233 | client.send('Hello World', function() { 234 | t.pass('client, send message'); 235 | }); 236 | }, 100); 237 | }); 238 | 239 | client.on('data', function(data) { 240 | t.equal(data.length > -1, true, 'client, receive data: ' + data); 241 | // test encoding 242 | t.equal(typeof data, 243 | 'string', 244 | 'client, is configure to rx string data'); 245 | }); 246 | 247 | client.once('close', function() { 248 | t.pass('client, closed'); 249 | }); 250 | }); 251 | 252 | 253 | test('portscan', function(t) { 254 | t.plan(4);// testing one with success and another with error 255 | 256 | var scan = Netcat.portscan(); 257 | 258 | scan.run('google.com', '80-81', function(err, res) { 259 | // will fail in port 81 260 | if (err) return t.ok(err, err); 261 | 262 | t.ok(res, res); 263 | }); 264 | }); 265 | 266 | 267 | test('upd', function(t) { 268 | t.plan(5); 269 | 270 | server = Netcat.udpServer(5000, '127.0.0.1'); 271 | client = Netcat.udpClient(5000, '127.0.0.1'); 272 | 273 | server.on('data', function(msg, client, protocol) { 274 | t.ok(msg, 275 | 'server, "' + msg + '", ' + JSON.stringify(client) + ', ' + protocol); 276 | setTimeout(function() { server.close(); }, 1200); 277 | }); 278 | 279 | server.on('ready', function() { 280 | t.pass('server, ready'); 281 | 282 | setTimeout(function() { 283 | client.send('Hello World UDP!!!!'); 284 | setTimeout(function() { client.close(); }, 1000); 285 | }, 1000); 286 | }); 287 | 288 | server.once('error', function(err) { t.error(err !== null, err); }); 289 | server.once('close', function() { t.pass('server, closed'); }); 290 | server.bind(); 291 | 292 | 293 | client.on('open', function() { t.pass('client, open'); }); 294 | client.once('error', function(err) { t.error(err !== null, err); }); 295 | client.once('close', function() { t.pass('client, closed'); }); 296 | }); 297 | 298 | 299 | 300 | 301 | --------------------------------------------------------------------------------