2018-05-13 10:07:43 +00:00
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
|
const binary_parser_1 = require("binary-parser");
|
2018-05-13 19:59:02 +00:00
|
|
|
const types_1 = require("./types");
|
2018-05-13 10:07:43 +00:00
|
|
|
const headerParser = new binary_parser_1.Parser()
|
|
|
|
.endianess("big")
|
|
|
|
.uint16("ID")
|
|
|
|
.bit1("QR")
|
|
|
|
.bit4("OPCODE")
|
|
|
|
.bit1("AA")
|
|
|
|
.bit1("TC")
|
|
|
|
.bit1("RD")
|
|
|
|
.bit1("RA")
|
|
|
|
.bit1("Z")
|
|
|
|
.bit1("AD")
|
|
|
|
.bit1("CD")
|
|
|
|
.bit4("RCODE")
|
|
|
|
.uint16("QDCOUNT")
|
|
|
|
.uint16("ANCOUNT")
|
|
|
|
.uint16("NSCOUNT")
|
|
|
|
.uint16("ARCOUNT");
|
2018-05-17 08:01:56 +00:00
|
|
|
function parseHeader(data) {
|
|
|
|
try {
|
|
|
|
return headerParser.parse(data);
|
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
throw new Error("Header parsing failed" + e.message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exports.parseHeader = parseHeader;
|
2018-05-13 10:07:43 +00:00
|
|
|
const labelParser = new binary_parser_1.Parser()
|
|
|
|
.endianess("big")
|
|
|
|
.uint8("dataLength")
|
|
|
|
.string("name", {
|
|
|
|
length: "dataLength",
|
|
|
|
encoding: "ascii"
|
|
|
|
});
|
|
|
|
const questionParser = new binary_parser_1.Parser()
|
|
|
|
.endianess("big")
|
|
|
|
.array("QNAME", {
|
|
|
|
type: labelParser,
|
|
|
|
readUntil: (item, buffer) => {
|
|
|
|
if (item.dataLength <= 0)
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
formatter: (value) => {
|
|
|
|
return value.map(e => e.name).join(".").slice(0, -1);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.uint16("QTYPE")
|
|
|
|
.uint16("QCLASS");
|
|
|
|
function parseQuestions(count, packet) {
|
2018-05-17 08:01:56 +00:00
|
|
|
try {
|
|
|
|
return new binary_parser_1.Parser()
|
|
|
|
.endianess("big")
|
|
|
|
.array("questions", {
|
|
|
|
type: questionParser,
|
|
|
|
length: count
|
|
|
|
}).parse(packet).questions;
|
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
throw new Error("Question parsing failed" + e.message);
|
|
|
|
}
|
2018-05-13 10:07:43 +00:00
|
|
|
}
|
2018-05-17 08:01:56 +00:00
|
|
|
exports.parseQuestions = parseQuestions;
|
|
|
|
const MAX_LABEL_SIZE = 63;
|
|
|
|
function serializeName(name) {
|
2018-05-13 20:27:16 +00:00
|
|
|
let length = 0;
|
|
|
|
let parts = name.split(".");
|
|
|
|
parts.forEach(e => {
|
|
|
|
// Length of part and byte that holds the length information
|
|
|
|
if (e.length > MAX_LABEL_SIZE)
|
|
|
|
throw new Error("Label to large");
|
|
|
|
length += e.length + 1;
|
|
|
|
});
|
|
|
|
length += 1; //Adding last 0 length octet
|
|
|
|
let data = Buffer.alloc(length);
|
|
|
|
let offset = 0;
|
|
|
|
parts.forEach(e => {
|
|
|
|
data.writeUInt8(e.length, offset);
|
|
|
|
offset++;
|
|
|
|
data.write(e, offset, e.length);
|
|
|
|
offset += e.length;
|
|
|
|
});
|
|
|
|
data.writeUInt8(0, offset);
|
|
|
|
return data;
|
|
|
|
}
|
2018-05-17 08:01:56 +00:00
|
|
|
exports.serializeName = serializeName;
|
2018-05-13 10:07:43 +00:00
|
|
|
class Request {
|
2018-05-17 08:01:56 +00:00
|
|
|
constructor(packet, sendCallback, max_size = 512) {
|
2018-05-13 10:07:43 +00:00
|
|
|
this.sendCallback = sendCallback;
|
2018-05-17 08:01:56 +00:00
|
|
|
this.max_size = max_size;
|
2018-05-13 10:07:43 +00:00
|
|
|
this.answers = [];
|
|
|
|
this.authorities = [];
|
|
|
|
this.additionals = [];
|
|
|
|
let headerData = Buffer.alloc(12);
|
|
|
|
packet.copy(headerData, 0, 0, 12);
|
|
|
|
let bodyData = Buffer.alloc(packet.length - 12);
|
|
|
|
packet.copy(bodyData, 0, 12, packet.length);
|
2018-05-17 08:01:56 +00:00
|
|
|
this._header = new Header(parseHeader(headerData));
|
2018-05-13 10:07:43 +00:00
|
|
|
this._header.AD = 0;
|
2018-05-13 19:59:02 +00:00
|
|
|
this._header.RCODE = types_1.ErrorCodes.NoError;
|
2018-05-13 20:34:20 +00:00
|
|
|
this._header.RA = this._header.RD;
|
2018-05-17 08:01:56 +00:00
|
|
|
this._questions = parseQuestions(this._header.QDCOUNT, bodyData).map(e => new Question(e));
|
2018-05-13 10:07:43 +00:00
|
|
|
}
|
|
|
|
get header() {
|
|
|
|
return Object.assign({}, this._header);
|
|
|
|
}
|
|
|
|
get questions() {
|
2018-05-17 08:01:56 +00:00
|
|
|
return this._questions.map(e => Object.assign({}, e));
|
2018-05-13 10:07:43 +00:00
|
|
|
}
|
2018-05-13 18:32:55 +00:00
|
|
|
error(error) {
|
2018-05-13 19:59:02 +00:00
|
|
|
if (this._header.RCODE === types_1.ErrorCodes.NoError)
|
|
|
|
this._header.RCODE = error;
|
2018-05-13 18:32:55 +00:00
|
|
|
}
|
2018-05-13 20:34:20 +00:00
|
|
|
noRecursion() {
|
|
|
|
this._header.RA = 0;
|
|
|
|
}
|
2018-05-13 10:07:43 +00:00
|
|
|
send() {
|
|
|
|
this.sendCallback(this.serialize());
|
|
|
|
}
|
2018-05-17 08:01:56 +00:00
|
|
|
serialize() {
|
2018-05-13 10:07:43 +00:00
|
|
|
this._header.AA = 1;
|
|
|
|
this._header.ANCOUNT = this.answers.length;
|
|
|
|
this._header.ARCOUNT = this.additionals.length;
|
|
|
|
this._header.NSCOUNT = this.authorities.length;
|
|
|
|
this._header.QR = 1;
|
2018-05-17 08:01:56 +00:00
|
|
|
let questions = this._questions.map(e => e.serialize());
|
|
|
|
let answers = this.answers.map(e => e.serialize());
|
|
|
|
let authority = this.authorities.map(e => e.serialize());
|
|
|
|
let additional = this.additionals.map(e => e.serialize());
|
|
|
|
let length = 12;
|
|
|
|
questions.forEach(e => length += e.length);
|
|
|
|
answers.forEach(e => length += e.length);
|
|
|
|
authority.forEach(e => length += e.length);
|
|
|
|
additional.forEach(e => length += e.length);
|
|
|
|
if (length > this.max_size) {
|
2018-05-13 10:07:43 +00:00
|
|
|
this._header.TC = 1;
|
2018-05-17 08:01:56 +00:00
|
|
|
//Will ignore data, that exceeds length
|
|
|
|
length = this.max_size;
|
2018-05-13 10:07:43 +00:00
|
|
|
}
|
2018-05-17 08:01:56 +00:00
|
|
|
let header = this._header.serialize();
|
2018-05-13 10:07:43 +00:00
|
|
|
let data = Buffer.alloc(length);
|
|
|
|
let offset = 0;
|
|
|
|
let append = (buffer) => {
|
2018-05-17 08:01:56 +00:00
|
|
|
if (offset <= length) {
|
|
|
|
buffer.copy(data, offset, 0, buffer.length);
|
|
|
|
offset += buffer.length;
|
|
|
|
}
|
2018-05-13 10:07:43 +00:00
|
|
|
};
|
|
|
|
append(header);
|
|
|
|
questions.forEach(append);
|
|
|
|
answers.forEach(append);
|
|
|
|
authority.forEach(append);
|
|
|
|
additional.forEach(append);
|
|
|
|
return data;
|
|
|
|
}
|
2018-05-17 08:01:56 +00:00
|
|
|
}
|
|
|
|
exports.Request = Request;
|
|
|
|
class Header {
|
|
|
|
constructor(header) {
|
|
|
|
for (let k in header) {
|
|
|
|
this[k] = header[k];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
serialize() {
|
2018-05-13 10:07:43 +00:00
|
|
|
let data = Buffer.alloc(12);
|
2018-05-17 08:01:56 +00:00
|
|
|
data.writeUInt16BE(this.ID, 0);
|
2018-05-13 10:07:43 +00:00
|
|
|
var f = 0x0000;
|
2018-05-17 08:01:56 +00:00
|
|
|
f = f | (this.QR << 15);
|
|
|
|
f = f | (this.OPCODE << 11);
|
|
|
|
f = f | (this.AA << 10);
|
|
|
|
f = f | (this.TC << 9);
|
|
|
|
f = f | (this.RD << 8);
|
|
|
|
f = f | (this.RA << 7);
|
|
|
|
f = f | (this.Z << 6);
|
|
|
|
f = f | (this.AD << 5);
|
|
|
|
f = f | (this.CD << 4);
|
|
|
|
f = f | this.RCODE;
|
2018-05-13 10:07:43 +00:00
|
|
|
data.writeUInt16BE(f, 2);
|
2018-05-17 08:01:56 +00:00
|
|
|
data.writeUInt16BE(this.QDCOUNT, 4);
|
|
|
|
data.writeUInt16BE(this.ANCOUNT, 6);
|
|
|
|
data.writeUInt16BE(this.NSCOUNT, 8);
|
|
|
|
data.writeUInt16BE(this.ARCOUNT, 10);
|
2018-05-13 10:07:43 +00:00
|
|
|
return data;
|
|
|
|
}
|
2018-05-17 08:01:56 +00:00
|
|
|
}
|
|
|
|
exports.Header = Header;
|
|
|
|
class Question {
|
|
|
|
constructor(question) {
|
|
|
|
for (let k in question) {
|
|
|
|
this[k] = question[k];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
serialize() {
|
|
|
|
let qname = serializeName(this.QNAME);
|
2018-05-13 10:07:43 +00:00
|
|
|
let data = Buffer.alloc(qname.length + 4);
|
|
|
|
qname.copy(data, 0, 0, qname.length);
|
|
|
|
let offset = qname.length;
|
2018-05-17 08:01:56 +00:00
|
|
|
data.writeUInt16BE(this.QTYPE, offset);
|
2018-05-13 10:07:43 +00:00
|
|
|
offset += 2;
|
2018-05-17 08:01:56 +00:00
|
|
|
data.writeUInt16BE(this.QCLASS, offset);
|
2018-05-13 10:07:43 +00:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
}
|
2018-05-17 08:01:56 +00:00
|
|
|
exports.Question = Question;
|
2018-05-13 10:07:43 +00:00
|
|
|
class RecourceRecord {
|
2018-05-17 16:46:50 +00:00
|
|
|
constructor(data) {
|
|
|
|
if (data) {
|
|
|
|
for (let key in data) {
|
|
|
|
this[key] = data[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-05-13 10:07:43 +00:00
|
|
|
set TYPE(value) {
|
|
|
|
if (value < 0 || value > 65535)
|
|
|
|
throw new TypeError("TYPE Range: 0 - 65.535");
|
|
|
|
this._TYPE = value;
|
|
|
|
}
|
|
|
|
get TYPE() {
|
|
|
|
return this._TYPE;
|
|
|
|
}
|
|
|
|
set CLASS(value) {
|
|
|
|
if (value < 0 || value > 65535)
|
|
|
|
throw new TypeError("CLASS Range: 0 - 65.535");
|
|
|
|
this._CLASS = value;
|
|
|
|
}
|
|
|
|
get CLASS() {
|
|
|
|
return this._CLASS;
|
|
|
|
}
|
|
|
|
set TTL(value) {
|
|
|
|
if (value < 0 || value > 4294967295)
|
|
|
|
throw new TypeError("TTL Range: 0 - 4.294.967.295");
|
2018-05-17 08:01:56 +00:00
|
|
|
this._TTL = value;
|
2018-05-13 10:07:43 +00:00
|
|
|
}
|
|
|
|
get TTL() {
|
|
|
|
return this._TTL;
|
|
|
|
}
|
|
|
|
get RDLENGTH() {
|
|
|
|
return this.RDATA.length;
|
|
|
|
}
|
2018-05-17 08:01:56 +00:00
|
|
|
serialize() {
|
|
|
|
// TODO: Implement compression
|
|
|
|
let name = serializeName(this.NAME);
|
2018-05-17 16:46:50 +00:00
|
|
|
let rdata = this.RDATA;
|
|
|
|
let data = Buffer.alloc(name.length + 10 + rdata.length); // For TYPE, CLASS, TTL, RLENGTH
|
2018-05-17 08:01:56 +00:00
|
|
|
name.copy(data, 0, 0, name.length);
|
|
|
|
let offset = name.length;
|
|
|
|
data.writeUInt16BE(this.TYPE, offset);
|
|
|
|
offset += 2;
|
|
|
|
data.writeUInt16BE(this.CLASS, offset);
|
|
|
|
offset += 2;
|
|
|
|
data.writeUInt32BE(this._TTL, offset);
|
|
|
|
offset += 4;
|
2018-05-17 16:46:50 +00:00
|
|
|
data.writeUInt16BE(rdata.length, offset);
|
2018-05-17 08:01:56 +00:00
|
|
|
offset += 2;
|
2018-05-17 16:46:50 +00:00
|
|
|
rdata.copy(data, offset, 0, rdata.length);
|
2018-05-17 08:01:56 +00:00
|
|
|
return data;
|
|
|
|
}
|
2018-05-13 10:07:43 +00:00
|
|
|
}
|
|
|
|
exports.RecourceRecord = RecourceRecord;
|
|
|
|
//# sourceMappingURL=request.js.map
|