started dns parser anew
This commit is contained in:
		
							parent
							
								
									4a1ea2d791
								
							
						
					
					
						commit
						d5a36adeec
					
				
							
								
								
									
										235
									
								
								pure-parser.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								pure-parser.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,235 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| // pass a terminal arg
 | ||||
| var filename = process.argv[2]; | ||||
| if (!filename) { | ||||
|   console.error("Usage: node aj-listener.js <type> [count]"); | ||||
|   console.error("Example: node aj-listener.js _service 0"); | ||||
|   process.exit(1); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| var PromiseA = require('bluebird'); | ||||
| var fs = PromiseA.promisifyAll(require('fs')); | ||||
| 
 | ||||
| fs.readFileAsync(filename, null).then(function (nb) { | ||||
|   // nb is a Uint8Array (ArrayBufferView) for nb.buffer
 | ||||
|   // nb.buffer is the actual ArrayBuffer
 | ||||
|   pdns.unpack(nb.buffer); | ||||
| }); | ||||
| 
 | ||||
| var pdns = module.exports; | ||||
| 
 | ||||
| // Order http://www.zytrax.com/books/dns/ch15/
 | ||||
| 
 | ||||
| pdns.unpackHeader = function (i) { | ||||
|   // i is one element from a Uint16Array (as a 16-bit unsigned integer)
 | ||||
| 
 | ||||
|   var header = { | ||||
|     qr:     (i & 0x8000) >> 15  // Query Response (0 is question, 1 is response)
 | ||||
|   , opcode: (i & 0x7800) >> 11  // 0 is question
 | ||||
|   , aa:     (i &  0x400) >> 10  // Authoritative Answer (response-only)
 | ||||
|   , tc:     (i &  0x200) >> 9   // TrunCated - expect another packet with same (?) id
 | ||||
|   , rd:     (i &  0x100) >> 8   // Recursion Desired
 | ||||
| 
 | ||||
|   , ra:     (i &  0x80) >> 7 | ||||
|   , res1:   (i &  0x40) >> 6 // z
 | ||||
|   , res2:   (i &  0x20) >> 5 // ad
 | ||||
|   , res3:   (i &  0x10) >> 4 // cd
 | ||||
|   , rcode:  (i &   0xF)         // Error Code (response-only)
 | ||||
|   }; | ||||
| 
 | ||||
|   return header; | ||||
| }; | ||||
| 
 | ||||
| pdns.packHeader = function(h) { | ||||
|   var val = 0; | ||||
| 
 | ||||
|   val += (h.qr << 15) & 0x8000; | ||||
|   val += (h.opcode << 11) & 0x7800; | ||||
|   val += (h.aa << 10) & 0x400; | ||||
|   val += (h.tc << 9) & 0x200; | ||||
|   val += (h.rd << 8) & 0x100; | ||||
|   val += (h.ra << 7) & 0x80; | ||||
|   val += (h.res1 << 6) & 0x40; | ||||
|   val += (h.res2 << 5) & 0x20; | ||||
|   val += (h.res3 << 4) & 0x10; | ||||
|   val += h.rcode & 0xF; | ||||
| 
 | ||||
|   return val; | ||||
| }; | ||||
| 
 | ||||
| pdns.unpackQname = function (ui8) { | ||||
|   var total = 0; | ||||
|   var i; | ||||
| 
 | ||||
|   var len; | ||||
|   var q = { | ||||
|     byteLength: 0 | ||||
|   , name: '' | ||||
|   , type: 0 | ||||
|   , class: 0 | ||||
|   }; | ||||
|   var str = []; | ||||
| 
 | ||||
|   do { | ||||
|     len = ui8[total]; | ||||
|     //str.length = 0; // fast empty array
 | ||||
|     if (ui8.byteLength - total < len) { | ||||
|       throw new Error( | ||||
|         "Expected a string of length " + len | ||||
|           + " but packet itself has " + (ui8.byteLength - total) + " bytes remaining" | ||||
|       ); | ||||
|     } | ||||
|     for (i = 0; i < len; i += 1) { | ||||
|       total += 1; | ||||
|       // TODO check url-allowable characters
 | ||||
|       str.push(String.fromCharCode(ui8[total])); | ||||
|     } | ||||
|     total += 1; | ||||
|     if (ui8[total]) { | ||||
|       // pushd connecting '.', but not trailing
 | ||||
|       str.push('.'); | ||||
|     } | ||||
|     //console.log('total', total, ui8[total], String.fromCharCode(ui8[total]));
 | ||||
|   } while (len); | ||||
| 
 | ||||
|   //str.pop(); // remove trailing '.'
 | ||||
| 
 | ||||
|   q.name = str.join(''); | ||||
| 
 | ||||
|   return q; | ||||
| }; | ||||
| 
 | ||||
| pdns.unpack = function (ab) { | ||||
|   if (ab.buffer) { | ||||
|     ab = ab.buffer; | ||||
|   } | ||||
| 
 | ||||
|   // SANITY Check
 | ||||
|   if (ab.byteLength < 12) { | ||||
|     throw new Error( | ||||
|       "A DNS header has a minimum length of 12 bytes but this packet has only " + ab.byteLength + "bytes." | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   // DO: new Uint8Array(arrayBuffer);
 | ||||
|   // DO NOT: Uint8Array.from(arrayBuffer); // WILL NOT WORK
 | ||||
| 
 | ||||
|   // DO: new DataView(arrayBuffer).getUint16(7);
 | ||||
|   // DO NOT: arrayBuffer.slice();
 | ||||
|   //
 | ||||
|   // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
 | ||||
|   // https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView
 | ||||
|   // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
 | ||||
|   // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/slice
 | ||||
| 
 | ||||
|   // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView
 | ||||
|   var dv = new DataView(ab); | ||||
|   var id = dv.getUint16(0); | ||||
|   var header = pdns.unpackHeader(dv.getUint16(2)); | ||||
|   var qdcount = dv.getUint16(4);  // query count
 | ||||
|   var ancount = dv.getUint16(6);  // answer count
 | ||||
|   var nscount = dv.getUint16(8);  // authority count
 | ||||
|   var arcount = dv.getUint16(10); // additional count
 | ||||
|   var total = 12; | ||||
|   var data; | ||||
|   var i; | ||||
|   var q; | ||||
| 
 | ||||
|   //console.log('datalen', data.length);
 | ||||
|   console.log(dv, qdcount); | ||||
| 
 | ||||
|   // TODO move to pdns.unpackQuestion to make testable
 | ||||
|   function unpackQuestion() { | ||||
|     data = new Uint8Array(ab).slice(total); | ||||
|     q = pdns.unpackQname(data); | ||||
|     total += q.name.length + 2; // account for leading and trailing string length byte
 | ||||
| 
 | ||||
| 
 | ||||
|     if (ab.byteLength - total < 4) { | ||||
|       // console.error(str.join(''));
 | ||||
|       throw new Error( | ||||
|         "Expected a 2-byte QTYPE and 2-byte QCLASS following " + total + "-byte QNAME" | ||||
|           + " but packet itself has " + (ab.byteLength - total) + " bytes remaining" | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     q.type = dv.getUint16(total); | ||||
|     console.log('type', q.type, total); | ||||
|     total += 2; | ||||
|     q.class = dv.getUint16(total); | ||||
|     console.log('class', q.class, total); | ||||
|     total += 2; | ||||
|     console.log('total', total); | ||||
| 
 | ||||
|     header.questions.push(q); | ||||
|   } | ||||
| 
 | ||||
|   function unpackAnswer(answers) { | ||||
|     data = new Uint8Array(ab).slice(total); | ||||
|     q = pdns.unpackQname(data); | ||||
|     total += q.name.length + 2; // account for leading and trailing string length byte
 | ||||
| 
 | ||||
| 
 | ||||
|     if (ab.byteLength - total < 10) { | ||||
|       // console.error(str.join(''));
 | ||||
|       throw new Error( | ||||
|         "Expected a 2-byte QTYPE, 2-byte QCLASS, 4-byte TTL, and 2-byte RDLENGTH following " + total + "-byte QNAME" | ||||
|           + " but packet itself has " + (ab.byteLength - total) + " bytes remaining" | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     q.type = dv.getUint16(total); | ||||
|     console.log('type', q.type, total); | ||||
| 
 | ||||
|     total += 2; | ||||
|     q.class = dv.getUint16(total); | ||||
|     console.log('class', q.class, total); | ||||
| 
 | ||||
|     total += 2; | ||||
|     q.ttl = dv.getUint32(total); | ||||
|     console.log('ttl', q.ttl, total); | ||||
| 
 | ||||
|     total += 4; | ||||
|     q.rdlength = dv.getUint16(total); | ||||
|     console.log('rdlength', q.rdlength, total); | ||||
| 
 | ||||
|     total += 2; | ||||
|     // TODO actually parse rdata
 | ||||
|     q.rdata = new Uint8Array(ab).slice(ab.byteLength - total, ab.byteLength + -total + q.rdlength); | ||||
|     q.rdata = Array.prototype.slice(q.data); | ||||
|     console.log('total', total); | ||||
| 
 | ||||
|     total += q.rdlength; | ||||
|     console.log('total', total); | ||||
| 
 | ||||
|     answers.push(q); | ||||
|   } | ||||
| 
 | ||||
|   console.log('qdcount', qdcount); | ||||
|   header.questions = []; | ||||
|   for (i = 0; i < qdcount; i += 1) { | ||||
|     unpackQuestion(); | ||||
|   } | ||||
| 
 | ||||
|   console.log('ancount', ancount); | ||||
|   header.answers = []; | ||||
|   for (i = 0; i < ancount; i += 1) { | ||||
|     unpackAnswer(header.answers); | ||||
|   } | ||||
| 
 | ||||
|   console.log('nscount', nscount); | ||||
|   header.authority = []; | ||||
|   for (i = 0; i < nscount; i += 1) { | ||||
|     unpackAnswer(header.authority); | ||||
|   } | ||||
| 
 | ||||
|   console.log('arcount', arcount); | ||||
|   header.additional = []; | ||||
|   for (i = 0; i < arcount; i += 1) { | ||||
|     unpackAnswer(header.additional); | ||||
|   } | ||||
| 
 | ||||
|   console.log('packet', header); | ||||
| }; | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user