221 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			221 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| ;(function (exports) {
 | |
| 'use strict';
 | |
| 
 | |
| var pdns = exports.DNS_PARSER = {};
 | |
| 
 | |
| var classes = exports.DNS_CLASSES || require('./dns.classes.js').DNS_CLASSES;
 | |
| var types = exports.DNS_TYPES || require('./dns.types.js').DNS_TYPES;
 | |
| 
 | |
| // Order http://www.zytrax.com/books/dns/ch15/
 | |
| var packet = {};
 | |
| pdns.unpackHeader = function (i) {
 | |
|   // i is one element from a Uint16Array (as a 16-bit unsigned integer)
 | |
| 
 | |
|   var header = {
 | |
|     id:     0                   // added here to preserve console.log order
 | |
| 
 | |
|   , 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._unpackLabels = exports.DNS_UNPACK_LABELS || require('./dns.unpack-labels.js').DNS_UNPACK_LABELS;
 | |
| 
 | |
| 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, false);
 | |
|   // 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, false);
 | |
|   packet.header = pdns.unpackHeader(dv.getUint16(2, false));
 | |
|   packet.qdcount = dv.getUint16(4, false);  // query count
 | |
|   packet.ancount = dv.getUint16(6, false);  // answer count
 | |
|   packet.nscount = dv.getUint16(8, false);  // authority count
 | |
|   packet.arcount = dv.getUint16(10, false); // additional count
 | |
|   var total = 12;
 | |
|   var i;
 | |
|   var rec;
 | |
|   var ui8 = new Uint8Array(ab);
 | |
| 
 | |
|   // TODO move to pdns.unpackQuestion to make testable
 | |
|   function unpackQuestion(ab, dv, ui8, total) {
 | |
|     var ototal = total;
 | |
|     var q = pdns._unpackLabels(ui8, total, {
 | |
|       name: ''        // ex: daplie.com
 | |
|     , type: 0         // ex: 1
 | |
|     , typeName: ''    // ex: A
 | |
|     , class: 0        // ex: 1
 | |
|     , className: ''   // ex: IN
 | |
|     , byteLength: 0   // the total byte length (including pointers, but not their labels)
 | |
|     , labels: []      // an array of the labels individually
 | |
|     , cpcount: 0      // the number of compression pointers traversed
 | |
|     });
 | |
|     //console.log('unpackQuestion QNAME:');
 | |
|     //console.log(q);
 | |
|     total += q.byteLength;
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
|     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, false);
 | |
|     total += 2;
 | |
|     q.class = dv.getUint16(total, false);
 | |
|     total += 2;
 | |
|     q.byteLength = total - ototal;
 | |
| 
 | |
|     q.className = classes[q.class];
 | |
|     q.typeName = types[q.type];
 | |
| 
 | |
|     return q;
 | |
|   }
 | |
| 
 | |
|   function unpackAnswer(ab, dv, ui8, total) {
 | |
|     var ototal = total;
 | |
|     var err;
 | |
|     var q = pdns._unpackLabels(ui8, total, {
 | |
|       name: ''
 | |
|     , type: 0
 | |
|     , typeName: ''
 | |
|     , class: 0
 | |
|     , className: ''
 | |
|     , byteLength: 0
 | |
|     , labels: []
 | |
|     , cpcount: 0
 | |
| 
 | |
|     , rdstart: 0
 | |
|     //, rdata: 0
 | |
|     , rdlength: 0
 | |
|     });
 | |
|     //console.log('unpackAnswer [Q]NAME:');
 | |
|     //console.log(q);
 | |
|     total += q.byteLength;
 | |
|     q.className = classes[q.class];
 | |
|     q.typeName = types[q.type];
 | |
| 
 | |
|     if (ab.byteLength - total < 10) {
 | |
|       // console.error(str.join(''));
 | |
|       //console.error(JSON.stringify(packet, null, 1));
 | |
|       //console.error(JSON.stringify(q, null, 1));
 | |
|       err = 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"
 | |
|       );
 | |
|       err.packet = packet;
 | |
|       err.record = q;
 | |
|       throw err;
 | |
|     }
 | |
| 
 | |
|     q.type = dv.getUint16(total, false);
 | |
|     total += 2;
 | |
|     q.class = dv.getUint16(total, false);
 | |
|     total += 2;
 | |
|     q.ttl = dv.getUint32(total, false);
 | |
|     total += 4;
 | |
|     q.rdlength = dv.getUint16(total, false);
 | |
|     total += 2;
 | |
| 
 | |
|     q.className = classes[q.class];
 | |
|     q.typeName = types[q.type];
 | |
| 
 | |
| 
 | |
|     // TODO actually parse RDATA
 | |
|     q.rdstart = total;
 | |
|     // console.log('q.rdata', q.rdlength, 'bytes:');
 | |
|     // console.log(new Uint8Array(ab).slice(total, total + q.rdlength));
 | |
|     //q.rdata = Array.prototype.slice.apply(q.rdata);
 | |
| 
 | |
|     //q.rdend = q.rdstart + q.rdlength;
 | |
| 
 | |
|     total += q.rdlength;
 | |
|     q.byteLength = total - ototal;
 | |
| 
 | |
|     return q;
 | |
|   }
 | |
| 
 | |
|   packet.header.id = id;
 | |
| 
 | |
| 
 | |
|   console.log('qdcount', packet.qdcount);
 | |
|   packet.question = [];
 | |
|   for (i = 0; i < packet.qdcount; i += 1) {
 | |
|     rec = unpackQuestion(ab, dv, ui8, total);
 | |
|     total += rec.byteLength;
 | |
|     packet.question.push(rec);
 | |
|   }
 | |
| 
 | |
|   console.log('ancount', packet.ancount);
 | |
|   packet.answer = [];
 | |
|   for (i = 0; i < packet.ancount; i += 1) {
 | |
|     rec = unpackAnswer(ab, dv, ui8, total);
 | |
|     total += rec.byteLength;
 | |
|     packet.answer.push(rec);
 | |
|   }
 | |
| 
 | |
|   console.log('nscount', packet.nscount);
 | |
|   packet.authority = [];
 | |
|   for (i = 0; i < packet.nscount; i += 1) {
 | |
|     rec = unpackAnswer(ab, dv, ui8, total);
 | |
|     total += rec.byteLength;
 | |
|     packet.authority.push(rec);
 | |
|   }
 | |
| 
 | |
|   console.log('arcount', packet.arcount);
 | |
|   packet.additional = [];
 | |
|   for (i = 0; i < packet.arcount; i += 1) {
 | |
|     rec = unpackAnswer(ab, dv, ui8, total);
 | |
|     total += rec.byteLength;
 | |
|     packet.additional.push(rec);
 | |
|   }
 | |
| 
 | |
|   if (ab.byteLength !== total) {
 | |
|     throw new Error(
 | |
|       "Parsed " + total + " bytes, but packet is " + ab.byteLength + " bytes."
 | |
|     );
 | |
|   }
 | |
|   packet.byteLength = total;
 | |
|   return packet;
 | |
| };
 | |
| pdns.unpackRdata = exports.DNS_RDATA_PARSE || require('./dns.rdata.parse.js').DNS_RDATA_PARSE;
 | |
| 
 | |
| }('undefined' !== typeof window ? window : exports));
 |