forked from coolaj86/digd.js
		
	add tcp support
This commit is contained in:
		
							parent
							
								
									bd8b57efd1
								
							
						
					
					
						commit
						c3934acb30
					
				| @ -89,6 +89,8 @@ Options | |||||||
| +time=<seconds>             Sets the timeout for a query in seconds. | +time=<seconds>             Sets the timeout for a query in seconds. | ||||||
| +norecurse                  Set `ra` flag to 0. Do not perform recursion. | +norecurse                  Set `ra` flag to 0. Do not perform recursion. | ||||||
| +aaonly                     Set `aa` flag to 1. Do not respond with non-authoritative responses. | +aaonly                     Set `aa` flag to 1. Do not respond with non-authoritative responses. | ||||||
|  | +notcp                      Disable TCP server (default in v1.2) | ||||||
|  | +tcp                        Enable TCP server (default in v1.3) | ||||||
| 
 | 
 | ||||||
| --debug                     verbose output | --debug                     verbose output | ||||||
| ``` | ``` | ||||||
|  | |||||||
							
								
								
									
										103
									
								
								bin/digd.js
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								bin/digd.js
									
									
									
									
									
								
							| @ -5,9 +5,7 @@ | |||||||
| var cli = require('cli'); | var cli = require('cli'); | ||||||
| var pkg = require('../package.json'); | var pkg = require('../package.json'); | ||||||
| var dig = require('dig.js/dns-request'); | var dig = require('dig.js/dns-request'); | ||||||
| var dgram = require('dgram'); |  | ||||||
| var dnsjs = require('dns-suite'); | var dnsjs = require('dns-suite'); | ||||||
| var crypto = require('crypto'); |  | ||||||
| var common = require('dig.js/common'); | var common = require('dig.js/common'); | ||||||
| var defaultNameservers = require('dns').getServers(); | var defaultNameservers = require('dns').getServers(); | ||||||
| var hexdump; | var hexdump; | ||||||
| @ -49,8 +47,32 @@ cli.main(function (args, cli) { | |||||||
|       cli.norecurse = true; |       cli.norecurse = true; | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |     if (arg === '+notcp') { | ||||||
|  |       if (cli.notcp) { | ||||||
|  |         console.error("'+notcp' was specified more than once"); | ||||||
|  |         process.exit(1); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       cli.notcp = true; | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     if (arg === '+tcp') { | ||||||
|  |       if (cli.tcp) { | ||||||
|  |         console.error("'+tcp' was specified more than once"); | ||||||
|  |         process.exit(1); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       cli.tcp = true; | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |   if (!cli.tcp) { | ||||||
|  |     if (!cli.notcp) { | ||||||
|  |       console.info("[WARNING] Set '+notcp' to disable tcp connections. The default behavior changes to +tcp in v1.3"); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   if (cli.mdns) { |   if (cli.mdns) { | ||||||
|     if (!cli.type) { |     if (!cli.type) { | ||||||
|       cli.type = cli.t = 'PTR'; |       cli.type = cli.t = 'PTR'; | ||||||
| @ -73,32 +95,10 @@ cli.main(function (args, cli) { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   var handlers = {}; |   var dnsd = {}; | ||||||
|   var server = dgram.createSocket({ |   dnsd.onMessage = function (nb, cb) { | ||||||
|     type: cli.udp6 ? 'udp6' : 'udp4' |     var byteOffset = nb._dnsByteOffset || nb.byteOffset; | ||||||
|   , reuseAddr: true |     var queryAb = nb.buffer.slice(byteOffset, byteOffset + nb.byteLength); | ||||||
|   }); |  | ||||||
|   server.bind({ |  | ||||||
|     port: cli.port |  | ||||||
|   , address: cli.address |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   handlers.onError = function (err) { |  | ||||||
|     if ('EACCES' === err.code) { |  | ||||||
|       console.error(""); |  | ||||||
|       console.error("EACCES: Couldn't bind to port. You probably need to use sudo, authbind, or setcap."); |  | ||||||
|       console.error(""); |  | ||||||
|       process.exit(123); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     console.error("error:", err.stack); |  | ||||||
|     server.close(); |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   handlers.onMessage = function (nb, rinfo) { |  | ||||||
|     console.log('[DEBUG] got a message'); |  | ||||||
| 
 |  | ||||||
|     var queryAb = nb.buffer.slice(nb.byteOffset, nb.byteOffset + nb.byteLength); |  | ||||||
|     var query; |     var query; | ||||||
|     var count; |     var count; | ||||||
| 
 | 
 | ||||||
| @ -215,12 +215,11 @@ cli.main(function (args, cli) { | |||||||
|         console.error("Could not write empty DNS response"); |         console.error("Could not write empty DNS response"); | ||||||
|         console.error(e); |         console.error(e); | ||||||
|         console.error(emptyResp); |         console.error(emptyResp); | ||||||
|  |         cb(e, null, '[DEV] response sent (empty)'); | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       server.send(newAb, rinfo.port, rinfo.address, function () { |       cb(null, newAb, '[DEV] response sent (empty)'); | ||||||
|         console.log('[DEV] response sent (empty)', rinfo.port, rinfo.address); |  | ||||||
|       }); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function sendResponse(newPacket) { |     function sendResponse(newPacket) { | ||||||
| @ -232,12 +231,11 @@ cli.main(function (args, cli) { | |||||||
|         console.error("Could not write DNS response from local"); |         console.error("Could not write DNS response from local"); | ||||||
|         console.error(e); |         console.error(e); | ||||||
|         console.error(newPacket); |         console.error(newPacket); | ||||||
|  |         cb(e, null, '[DEV] response sent (local query)'); | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       server.send(newAb, rinfo.port, rinfo.address, function () { |       cb(null, newAb, '[DEV] response sent (local query)'); | ||||||
|         console.log('[DEV] response sent (local query)', rinfo.port, rinfo.address); |  | ||||||
|       }); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function recurse() { |     function recurse() { | ||||||
| @ -290,12 +288,11 @@ cli.main(function (args, cli) { | |||||||
|             } catch(e) { |             } catch(e) { | ||||||
|               console.error("Could not write DNS response"); |               console.error("Could not write DNS response"); | ||||||
|               console.error(newResponse); |               console.error(newResponse); | ||||||
|  |               cb(e, null, '[DEV] response sent'); | ||||||
|               return; |               return; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             server.send(newAb, rinfo.port, rinfo.address, function () { |             cb(null, newAb, '[DEV] response sent'); | ||||||
|               console.log('[DEV] response sent', rinfo.port, rinfo.address); |  | ||||||
|             }); |  | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -335,7 +332,8 @@ cli.main(function (args, cli) { | |||||||
|               console.log('request sent to', res.nameserver); |               console.log('request sent to', res.nameserver); | ||||||
|             } |             } | ||||||
|             */ |             */ | ||||||
|             console.log('[DEV] query sent (recurse)', rinfo.port, rinfo.address); |             //console.log('[DEV] query sent (recurse)', rinfo.port, rinfo.address);
 | ||||||
|  |             //dnsd.onSent('[DEV] query sent (recurse)');
 | ||||||
|           } |           } | ||||||
|         , onTimeout: function (res) { |         , onTimeout: function (res) { | ||||||
|             console.log(";; [" + q.name + "] connection timed out; no servers could be reached"); |             console.log(";; [" + q.name + "] connection timed out; no servers could be reached"); | ||||||
| @ -393,32 +391,24 @@ cli.main(function (args, cli) { | |||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     require('../lib/digd.js').query(engine, query, respondWithResults); |     require('../lib/digd.js').query(engine, query, respondWithResults); | ||||||
| 
 |  | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   handlers.onListening = function () { |   cli.defaultNameservers = defaultNameservers; | ||||||
|     /*jshint validthis:true*/ |   require('../lib/udpd.js').create(cli, dnsd).on('listening', function () { | ||||||
|     var server = this; |  | ||||||
|     cli.chosenNameserver = cli.nameserver; |     cli.chosenNameserver = cli.nameserver; | ||||||
|     var index; |     var index; | ||||||
| 
 | 
 | ||||||
|     if (!cli.chosenNameserver) { |     if (!cli.chosenNameserver) { | ||||||
|       index = crypto.randomBytes(2).readUInt16BE(0) % defaultNameservers.length; |       index = require('crypto').randomBytes(2).readUInt16BE(0) % cli.defaultNameservers.length; | ||||||
|       cli.chosenNameserver = defaultNameservers[index]; |       cli.chosenNameserver = cli.defaultNameservers[index]; | ||||||
|       if (cli.debug) { |       if (cli.debug) { | ||||||
|         console.log('index, defaultNameservers', index, defaultNameservers); |         console.log('index, defaultNameservers', index, cli.defaultNameservers); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 |   }); | ||||||
|     if (cli.mdns || '224.0.0.251' === cli.nameserver) { |   if (cli.tcp /* TODO v1.3 !cli.notcp */) { | ||||||
|       server.setBroadcast(true); |     require('../lib/tcpd.js').create(cli, dnsd); | ||||||
|       server.addMembership(cli.nameserver); |   } | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     console.log(''); |  | ||||||
|     console.log('Bound and Listening:'); |  | ||||||
|     console.log(server.address().address + '#' + server.address().port + ' (' + server.type + ')'); |  | ||||||
|   }; |  | ||||||
| 
 | 
 | ||||||
|   console.log(''); |   console.log(''); | ||||||
|   if (!cli.nocmd) { |   if (!cli.nocmd) { | ||||||
| @ -426,7 +416,4 @@ cli.main(function (args, cli) { | |||||||
|     console.log(';; global options: +cmd'); |     console.log(';; global options: +cmd'); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   server.on('error', handlers.onError); |  | ||||||
|   server.on('message', handlers.onMessage); |  | ||||||
|   server.on('listening', handlers.onListening); |  | ||||||
| }); | }); | ||||||
|  | |||||||
							
								
								
									
										70
									
								
								lib/tcpd.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								lib/tcpd.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | |||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | module.exports.create = function (cli, dnsd) { | ||||||
|  |   function runTcp() { | ||||||
|  |     var tcpServer = require('net').createServer({ }, function (c) { | ||||||
|  |       c.on('error', function (err) { | ||||||
|  |         console.warn("TCP Connection Error:"); | ||||||
|  |         console.warn(err); | ||||||
|  |       }); | ||||||
|  |       c.on('data', function (nb) { | ||||||
|  |         //console.log('TCP data.length:', nb.length);
 | ||||||
|  |         //console.log(nb.toString('hex'));
 | ||||||
|  | 
 | ||||||
|  |         // DNS packets include a 2-byte length header
 | ||||||
|  |         var count = nb.length; | ||||||
|  |         var length = nb[0] << 8; | ||||||
|  |         length = length | nb[1]; | ||||||
|  |         count -= 2; | ||||||
|  |         // TODO slice?
 | ||||||
|  |         nb._dnsByteOffset = nb.byteOffset + 2; | ||||||
|  | 
 | ||||||
|  |         if (length !== count) { | ||||||
|  |           console.error("Handling TCP packets > 512 bytes not implemented."); | ||||||
|  |           c.end(); | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // TODO pad two bytes for lengths
 | ||||||
|  |         dnsd.onMessage(nb, function (err, newAb, dbgmsg) { | ||||||
|  |           var lenbuf = Buffer.from([ newAb.length >> 8, newAb.length & 255 ]); | ||||||
|  |           // TODO XXX generate legit error packet
 | ||||||
|  |           if (err) { console.error("Error", err); c.end(); return; } | ||||||
|  |           console.log('TCP ' + dbgmsg); | ||||||
|  | 
 | ||||||
|  |           c.write(lenbuf); | ||||||
|  |           c.end(newAb); | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  |       c.on('end', function () { | ||||||
|  |         console.log('TCP client disconnected from server'); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     tcpServer.on('error', function (err) { | ||||||
|  |       if ('EADDRINUSE' === err.code) { | ||||||
|  |         console.error("Port '" + cli.port + "' is already in use."); | ||||||
|  |         tcpServer.close(); | ||||||
|  |         process.exit(0); | ||||||
|  |       } | ||||||
|  |       if ('EACCES' === err.code) { | ||||||
|  |         console.error("Could not bind on port '" + cli.port + "': EACCESS (you probably need root permissions)"); | ||||||
|  |         tcpServer.close(); | ||||||
|  |         process.exit(0); | ||||||
|  |       } | ||||||
|  |       console.error("TCP Server Error:"); | ||||||
|  |       console.error(err); | ||||||
|  |       tcpServer.close(function () { | ||||||
|  |         setTimeout(runTcp, 1000); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     tcpServer.listen(cli.port, function () { | ||||||
|  |       console.log('TCP Server bound'); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return tcpServer; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return runTcp(); | ||||||
|  | }; | ||||||
							
								
								
									
										58
									
								
								lib/udpd.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								lib/udpd.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,58 @@ | |||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | module.exports.create = function (cli, dnsd) { | ||||||
|  |   var server = require('dgram').createSocket({ | ||||||
|  |     type: cli.udp6 ? 'udp6' : 'udp4' | ||||||
|  |   , reuseAddr: true | ||||||
|  |   }); | ||||||
|  |   server.bind({ | ||||||
|  |     port: cli.port | ||||||
|  |   , address: cli.address | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   var handlers = {}; | ||||||
|  |   handlers.onError = function (err) { | ||||||
|  |     if ('EACCES' === err.code) { | ||||||
|  |       console.error(""); | ||||||
|  |       console.error("EACCES: Couldn't bind to port. You probably need to use sudo, authbind, or setcap."); | ||||||
|  |       console.error(""); | ||||||
|  |       process.exit(123); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     console.error("error:", err.stack); | ||||||
|  |     server.close(); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   handlers.onMessage = function (nb, rinfo) { | ||||||
|  |     //console.log('[DEBUG] got a UDP message', nb.length);
 | ||||||
|  |     //console.log(nb.toString('hex'));
 | ||||||
|  | 
 | ||||||
|  |     dnsd.onMessage(nb, function (err, newAb, dbgmsg) { | ||||||
|  |       // TODO send legit error message
 | ||||||
|  |       if (err) { server.send(Buffer.from([0x00])); return; } | ||||||
|  |       server.send(newAb, rinfo.port, rinfo.address, function () { | ||||||
|  |         console.log(dbgmsg, rinfo.port, rinfo.address); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   handlers.onListening = function () { | ||||||
|  |     /*jshint validthis:true*/ | ||||||
|  |     var server = this; | ||||||
|  | 
 | ||||||
|  |     if (cli.mdns || '224.0.0.251' === cli.nameserver) { | ||||||
|  |       server.setBroadcast(true); | ||||||
|  |       server.addMembership(cli.nameserver); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     console.log(''); | ||||||
|  |     console.log('Bound and Listening:'); | ||||||
|  |     console.log(server.address().address + '#' + server.address().port + ' (' + server.type + ')'); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   server.on('error', handlers.onError); | ||||||
|  |   server.on('message', handlers.onMessage); | ||||||
|  |   server.on('listening', handlers.onListening); | ||||||
|  | 
 | ||||||
|  |   return server; | ||||||
|  | }; | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user