169 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			169 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| (function (exports) {
 | |
| 'use strict';
 | |
| 
 | |
| var classes = exports.DNS_CLASSES || require('./dns.classes.js').DNS_CLASSES;
 | |
| var types = exports.DNS_TYPES || require('./dns.types.js').DNS_TYPES;
 | |
| 
 | |
| var dnspack = exports.DNS_PACKER = {
 | |
|   pack: function (packet) {
 | |
| 
 | |
|     // TODO update for edns payload size
 | |
|     var ab = new ArrayBuffer(576);                  // http://serverfault.com/a/587626/93930
 | |
|     var dv = new DataView(ab);
 | |
|     var labelsMap = {};
 | |
|     var total = 0;
 | |
|     var id = packet.header.id;                      // 2 bytes
 | |
|     var header = dnspack.packHeader(packet.header); // 2 bytes
 | |
|     total = 12;                                     // 2+2+2+2 bytes ({qd,an,ns,ar}count)
 | |
| 
 | |
|     if (!id) {
 | |
|       throw new Error("'id' should be set to a crypto random id");
 | |
|     }
 | |
| 
 | |
|     dv.setUint16(0, id, false);
 | |
|     dv.setUint16(2, header, false);
 | |
|     dv.setUint16(4, packet.question.length, false);
 | |
|     dv.setUint16(6, (packet.answer||[]).length, false);
 | |
|     dv.setUint16(8, (packet.authority||[]).length, false);
 | |
|     // EDNS is added as an additional with TYPE 41 (OPT, 0x29)
 | |
|     dv.setUint16(10, (packet.additional||[]).length + (packet.payload ? 1 : 0), false);
 | |
| 
 | |
|     function lint(r) {
 | |
|       if (!r.name) {
 | |
|         throw new Error("missing name");
 | |
|       }
 | |
| 
 | |
|       if (!r.class) {
 | |
|         if (!r.className) {
 | |
|           throw new Error("no className");
 | |
|         }
 | |
|         if (!classes[r.className]) {
 | |
|           console.warn("ignoring invalid class '" + r.className + "' for '" + r.name);
 | |
|         } else {
 | |
|           r.class = classes[r.className];
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (!r.type) {
 | |
|         if (!r.typeName) {
 | |
|           throw new Error("no typeName");
 | |
|         }
 | |
|         if (!types[r.typeName]) {
 | |
|           console.warn("ignoring invalid type '" + r.typeName + "' for '" + r.name);
 | |
|         } else {
 | |
|           r.type = types[r.typeName];
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     function packLabelSequence(sequence, terminates) {
 | |
|       if (labelsMap[sequence]) {
 | |
|         // minimal compression pointer 0xc0 (192)
 | |
|         dv.setUint8(total, 0xc0, false);
 | |
|         total += 1;
 | |
|         dv.setUint8(total, labelsMap[sequence].total, false);
 | |
|         total += 1;
 | |
|         return;
 | |
|       }
 | |
|       if (terminates) {
 | |
|         // we don't want non-terminating rdata cached, just terminating names
 | |
|         labelsMap[total] = { total: total, name: sequence };
 | |
|         labelsMap[sequence] = labelsMap[total];
 | |
|       }
 | |
|       sequence.split('.').forEach(function (label) {
 | |
|         dv.setUint8(total, label.length, false);
 | |
|         total += 1;
 | |
| 
 | |
|         label.split('').forEach(function (ch) {
 | |
|           dv.setUint8(total, ch.charCodeAt(0), false);
 | |
|           total += 1;
 | |
|         });
 | |
|       });
 | |
|       if (terminates) {
 | |
|         // trailing 0 (null) as label sequence terminator
 | |
|         dv.setUint8(total, 0, false);
 | |
|         total += 1;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     function packQuestion(q) {
 | |
|       lint(q);
 | |
| 
 | |
|       packLabelSequence(q.name, true);
 | |
| 
 | |
|       dv.setUint16(total, q.type || types[q.typeName], false);
 | |
|       total += 2;
 | |
|       dv.setUint16(total, q.class || classes[q.className], false);
 | |
|       total += 2;
 | |
|     }
 | |
| 
 | |
|     function packAnswer(a) {
 | |
|       packQuestion(a);
 | |
| 
 | |
|       if ('undefined' === typeof a.ttl) {
 | |
|         throw new Error("missing ttl");
 | |
|       }
 | |
| 
 | |
|       // 32-bit ttl
 | |
|       dv.setUint32(total, a.ttl, false);
 | |
|       total += 4;
 | |
| 
 | |
|       if (a.rdata) {
 | |
|         // 16-bit
 | |
|         dv.setUint16(total, a.rdata.byteLength, false);
 | |
|         total += 2;
 | |
|         (new Uint8Array(a.rdata)).forEach(function (b) {
 | |
|           dv.setUint8(total, b, false);
 | |
|           total += 1;
 | |
|         });
 | |
|       } else {
 | |
|         total = dnspack.packRdata(ab, dv, total, a);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     packet.question.forEach(packQuestion);
 | |
|     (packet.answer||[]).forEach(packAnswer);
 | |
|     (packet.authority||[]).forEach(packAnswer);
 | |
|     (packet.additional||[]).forEach(packAnswer);
 | |
| 
 | |
|     // TODO handle compression pointers
 | |
|     // TODO handle edns
 | |
| 
 | |
|     return ab.slice(0, total);
 | |
|   }
 | |
| 
 | |
|   /*
 | |
| , packLabels: function(map, labels) {
 | |
|   }
 | |
|   */
 | |
| 
 | |
| , packHeader: function(h) {
 | |
|     var val = 0;
 | |
|     var req = h.opcode;
 | |
|     var res = h.qr || h.aa || h.ra || h.rcode;
 | |
| 
 | |
|     if (req && res) {
 | |
|       throw new Error("Both request-only and response-only headers are set: " + JSON.stringify(h));
 | |
|     }
 | |
| 
 | |
|     // TODO check if is request or response
 | |
| 
 | |
|     val += ((h.qr || 0)     << 15) & 0x8000;
 | |
|     val += ((h.opcode || 0) << 11) & 0x7800;
 | |
|     val += ((h.aa || 0)     << 10) &  0x400;
 | |
|     val += ((h.tc || 0)     <<  9) &  0x200;
 | |
|     val += ((h.rd || 0)     <<  8) &  0x100;
 | |
|     val += ((h.ra || 0)     <<  7) &   0x80;
 | |
|     val += ((h.res1 || 0)   <<  6) &   0x40;
 | |
|     val += ((h.res2 || 0)   <<  5) &   0x20;
 | |
|     val += ((h.res3 || 0)   <<  4) &   0x10;
 | |
|     val += (h.rcode || 0)          &    0xF;
 | |
| 
 | |
|     return val;
 | |
|   }
 | |
| 
 | |
| };
 | |
| dnspack.packRdata = exports.DNS_RDATA_PACK || require('./dns.rdata.pack.js').DNS_RDATA_PACK;
 | |
| 
 | |
| }('undefined' !== typeof window ? window : exports));
 |