First basic implementation of protocol.
This commit is contained in:
commit
524f246398
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
yarn.lock
|
||||||
|
*.pcapng
|
||||||
|
*.bin
|
7
lib/listener.d.ts
vendored
Normal file
7
lib/listener.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { Request } from "./request";
|
||||||
|
export default class Listener {
|
||||||
|
private udp;
|
||||||
|
private tcp;
|
||||||
|
constructor(type: "udp" | "tcp", onRequest: (request: Request) => any, port: number, host?: string);
|
||||||
|
close(): void;
|
||||||
|
}
|
69
lib/listener.js
Normal file
69
lib/listener.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const net = require("net");
|
||||||
|
const dgram = require("dgram");
|
||||||
|
const request_1 = require("./request");
|
||||||
|
class Listener {
|
||||||
|
constructor(type, onRequest, port, host = "0.0.0.0") {
|
||||||
|
switch (type) {
|
||||||
|
case "udp":
|
||||||
|
this.udp = dgram.createSocket("udp4");
|
||||||
|
this.udp.on("listening", () => {
|
||||||
|
console.log(`UDP Server Listening on ${port}`);
|
||||||
|
});
|
||||||
|
this.udp.on("message", (message, remote) => {
|
||||||
|
let request = new request_1.Request(message, (data) => {
|
||||||
|
// console.log("sending:", new Request(data, (a) => 0));
|
||||||
|
this.udp.send(data, remote.port, remote.address);
|
||||||
|
});
|
||||||
|
onRequest(request);
|
||||||
|
});
|
||||||
|
this.udp.bind(port, host);
|
||||||
|
break;
|
||||||
|
case "tcp":
|
||||||
|
console.log("Not correct implemented");
|
||||||
|
this.tcp = net.createServer((socket) => {
|
||||||
|
let length;
|
||||||
|
let got = 0;
|
||||||
|
let message = undefined;
|
||||||
|
socket.on("data", (data) => {
|
||||||
|
let offset = 0;
|
||||||
|
if (!message) {
|
||||||
|
length = data.readUInt16BE(0);
|
||||||
|
if (length > 2048)
|
||||||
|
return socket.destroy(); //Requests with more that 2k are ignored
|
||||||
|
message = Buffer.alloc(length);
|
||||||
|
offset = 2;
|
||||||
|
}
|
||||||
|
let read = (data.length - offset) > (length - got) ? (length - got) : (data.length - offset);
|
||||||
|
data.copy(message, got, offset, read + offset);
|
||||||
|
got += read;
|
||||||
|
//ToDo don't ignore probably following requests
|
||||||
|
if (got >= length) {
|
||||||
|
let request = new request_1.Request(message, (data) => {
|
||||||
|
socket.write(data);
|
||||||
|
});
|
||||||
|
got = 0;
|
||||||
|
message = undefined;
|
||||||
|
length = 0;
|
||||||
|
onRequest(request);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.tcp.listen(port, host);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Unknown socket type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close() {
|
||||||
|
if (this.udp) {
|
||||||
|
this.udp.close();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.tcp.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.default = Listener;
|
||||||
|
//# sourceMappingURL=listener.js.map
|
1
lib/listener.js.map
Normal file
1
lib/listener.js.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"listener.js","sourceRoot":"","sources":["../src/listener.ts"],"names":[],"mappings":"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"}
|
120
lib/request.d.ts
vendored
Normal file
120
lib/request.d.ts
vendored
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/// <reference types="node" />
|
||||||
|
export declare enum queryTypes {
|
||||||
|
/**
|
||||||
|
* IPv4 address
|
||||||
|
*/
|
||||||
|
A = 1,
|
||||||
|
/**
|
||||||
|
* Nameserver
|
||||||
|
*/
|
||||||
|
NS = 2,
|
||||||
|
/**
|
||||||
|
* Obsolete
|
||||||
|
*/
|
||||||
|
MD = 3,
|
||||||
|
/**
|
||||||
|
* Obsolete
|
||||||
|
*/
|
||||||
|
MF = 4,
|
||||||
|
/**
|
||||||
|
* Alias
|
||||||
|
*/
|
||||||
|
CNAME = 5,
|
||||||
|
/**
|
||||||
|
* Start of authority
|
||||||
|
*/
|
||||||
|
SOA = 6,
|
||||||
|
/**
|
||||||
|
* Experimental
|
||||||
|
*/
|
||||||
|
MB = 7,
|
||||||
|
/**
|
||||||
|
* Experimental
|
||||||
|
*/
|
||||||
|
MG = 8,
|
||||||
|
/**
|
||||||
|
* Experimental
|
||||||
|
*/
|
||||||
|
MR = 9,
|
||||||
|
/**
|
||||||
|
* Experimental
|
||||||
|
*/
|
||||||
|
NULL = 10,
|
||||||
|
/**
|
||||||
|
* Service description
|
||||||
|
*/
|
||||||
|
WKS = 11,
|
||||||
|
/**
|
||||||
|
* Reverse entry (inaddr.arpa)
|
||||||
|
*/
|
||||||
|
PTR = 12,
|
||||||
|
/**
|
||||||
|
* Host information
|
||||||
|
*/
|
||||||
|
HINFO = 13,
|
||||||
|
/**
|
||||||
|
* Mailbox / Mail-list information
|
||||||
|
*/
|
||||||
|
MINFO = 14,
|
||||||
|
/**
|
||||||
|
* Mail exchange
|
||||||
|
*/
|
||||||
|
MX = 15,
|
||||||
|
/**
|
||||||
|
* Text strings
|
||||||
|
*/
|
||||||
|
TXT = 16,
|
||||||
|
/**
|
||||||
|
* IPv6 address
|
||||||
|
*/
|
||||||
|
AAAA = 28,
|
||||||
|
/**
|
||||||
|
* SRV records
|
||||||
|
*/
|
||||||
|
SRV = 33,
|
||||||
|
/**
|
||||||
|
* Request to transfer entire zone
|
||||||
|
*/
|
||||||
|
AXFR = 252,
|
||||||
|
/**
|
||||||
|
* Request for mailbox related records
|
||||||
|
*/
|
||||||
|
MAILA = 254,
|
||||||
|
/**
|
||||||
|
* Request for mail agend RRs
|
||||||
|
*/
|
||||||
|
MAILB = 253,
|
||||||
|
/**
|
||||||
|
* Any class
|
||||||
|
*/
|
||||||
|
ANY = 255,
|
||||||
|
}
|
||||||
|
export declare class Request implements Message {
|
||||||
|
private sendCallback;
|
||||||
|
_header: MessageHeader;
|
||||||
|
readonly header: MessageHeader;
|
||||||
|
_questions: MessageQuestion[];
|
||||||
|
readonly questions: MessageQuestion[];
|
||||||
|
answers: RecourceRecord[];
|
||||||
|
authorities: RecourceRecord[];
|
||||||
|
additionals: RecourceRecord[];
|
||||||
|
_packet: Buffer;
|
||||||
|
constructor(packet: Buffer, sendCallback: (packet: Buffer) => any);
|
||||||
|
send(): void;
|
||||||
|
private serialize(truncate?, rcode?);
|
||||||
|
private serializeHeader();
|
||||||
|
private serializeQuestion(question);
|
||||||
|
private serializeResourceRecord(record);
|
||||||
|
private serializeName(name);
|
||||||
|
}
|
||||||
|
export declare class RecourceRecord implements MessageRecourceRecord {
|
||||||
|
NAME: string;
|
||||||
|
private _TYPE;
|
||||||
|
TYPE: number;
|
||||||
|
private _CLASS;
|
||||||
|
CLASS: number;
|
||||||
|
_TTL: number;
|
||||||
|
TTL: number;
|
||||||
|
RDATA: Buffer;
|
||||||
|
readonly RDLENGTH: number;
|
||||||
|
}
|
309
lib/request.js
Normal file
309
lib/request.js
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const binary_parser_1 = require("binary-parser");
|
||||||
|
const MAX_LABEL_SIZE = 63;
|
||||||
|
var queryTypes;
|
||||||
|
(function (queryTypes) {
|
||||||
|
/**
|
||||||
|
* IPv4 address
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["A"] = 1] = "A";
|
||||||
|
/**
|
||||||
|
* Nameserver
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["NS"] = 2] = "NS";
|
||||||
|
/**
|
||||||
|
* Obsolete
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["MD"] = 3] = "MD";
|
||||||
|
/**
|
||||||
|
* Obsolete
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["MF"] = 4] = "MF";
|
||||||
|
/**
|
||||||
|
* Alias
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["CNAME"] = 5] = "CNAME";
|
||||||
|
/**
|
||||||
|
* Start of authority
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["SOA"] = 6] = "SOA";
|
||||||
|
/**
|
||||||
|
* Experimental
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["MB"] = 7] = "MB";
|
||||||
|
/**
|
||||||
|
* Experimental
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["MG"] = 8] = "MG";
|
||||||
|
/**
|
||||||
|
* Experimental
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["MR"] = 9] = "MR";
|
||||||
|
/**
|
||||||
|
* Experimental
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["NULL"] = 10] = "NULL";
|
||||||
|
/**
|
||||||
|
* Service description
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["WKS"] = 11] = "WKS";
|
||||||
|
/**
|
||||||
|
* Reverse entry (inaddr.arpa)
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["PTR"] = 12] = "PTR";
|
||||||
|
/**
|
||||||
|
* Host information
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["HINFO"] = 13] = "HINFO";
|
||||||
|
/**
|
||||||
|
* Mailbox / Mail-list information
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["MINFO"] = 14] = "MINFO";
|
||||||
|
/**
|
||||||
|
* Mail exchange
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["MX"] = 15] = "MX";
|
||||||
|
/**
|
||||||
|
* Text strings
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["TXT"] = 16] = "TXT";
|
||||||
|
/**
|
||||||
|
* IPv6 address
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["AAAA"] = 28] = "AAAA";
|
||||||
|
/**
|
||||||
|
* SRV records
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["SRV"] = 33] = "SRV";
|
||||||
|
/**
|
||||||
|
* Request to transfer entire zone
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["AXFR"] = 252] = "AXFR";
|
||||||
|
/**
|
||||||
|
* Request for mailbox related records
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["MAILA"] = 254] = "MAILA";
|
||||||
|
/**
|
||||||
|
* Request for mail agend RRs
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["MAILB"] = 253] = "MAILB";
|
||||||
|
/**
|
||||||
|
* Any class
|
||||||
|
*/
|
||||||
|
queryTypes[queryTypes["ANY"] = 255] = "ANY";
|
||||||
|
})(queryTypes = exports.queryTypes || (exports.queryTypes = {}));
|
||||||
|
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");
|
||||||
|
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) {
|
||||||
|
return new binary_parser_1.Parser()
|
||||||
|
.endianess("big")
|
||||||
|
.array("questions", {
|
||||||
|
type: questionParser,
|
||||||
|
length: count
|
||||||
|
}).parse(packet).questions;
|
||||||
|
}
|
||||||
|
class Request {
|
||||||
|
constructor(packet, sendCallback) {
|
||||||
|
this.sendCallback = sendCallback;
|
||||||
|
this.answers = [];
|
||||||
|
this.authorities = [];
|
||||||
|
this.additionals = [];
|
||||||
|
this._packet = packet;
|
||||||
|
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);
|
||||||
|
this._header = headerParser.parse(headerData);
|
||||||
|
this._header.AD = 0;
|
||||||
|
this._questions = parseQuestions(this._header.QDCOUNT, bodyData);
|
||||||
|
}
|
||||||
|
get header() {
|
||||||
|
return Object.assign({}, this._header);
|
||||||
|
}
|
||||||
|
get questions() {
|
||||||
|
return this._questions.map(e => e);
|
||||||
|
}
|
||||||
|
send() {
|
||||||
|
this.sendCallback(this.serialize());
|
||||||
|
}
|
||||||
|
serialize(truncate = false, rcode = 0) {
|
||||||
|
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;
|
||||||
|
this._header.RCODE = rcode;
|
||||||
|
this._header.RA = 0;
|
||||||
|
let questions = this.questions.map(this.serializeQuestion, this);
|
||||||
|
let answers = this.answers.map(this.serializeResourceRecord, this);
|
||||||
|
let authority = this.authorities.map(this.serializeResourceRecord, this);
|
||||||
|
let additional = this.additionals.map(this.serializeResourceRecord, this);
|
||||||
|
let questionsByteLength = 0;
|
||||||
|
questions.forEach(e => questionsByteLength += e.length);
|
||||||
|
let answersByteLength = 0;
|
||||||
|
answers.forEach(e => answersByteLength += e.length);
|
||||||
|
let authorityByteLength = 0;
|
||||||
|
authority.forEach(e => authorityByteLength += e.length);
|
||||||
|
let additionalByteLength = 0;
|
||||||
|
additional.forEach(e => additionalByteLength += e.length);
|
||||||
|
let length = 12 + questionsByteLength + answersByteLength + authorityByteLength + additionalByteLength; //Header is always 12 byte large
|
||||||
|
if (truncate && length > 512) {
|
||||||
|
this._header.TC = 1;
|
||||||
|
//Buffer will ignore data that exeeds the max buffer length
|
||||||
|
length = 512;
|
||||||
|
}
|
||||||
|
let header = this.serializeHeader();
|
||||||
|
let data = Buffer.alloc(length);
|
||||||
|
let offset = 0;
|
||||||
|
let append = (buffer) => {
|
||||||
|
buffer.copy(data, offset, 0, buffer.length);
|
||||||
|
offset += buffer.length;
|
||||||
|
};
|
||||||
|
append(header);
|
||||||
|
questions.forEach(append);
|
||||||
|
answers.forEach(append);
|
||||||
|
authority.forEach(append);
|
||||||
|
additional.forEach(append);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
serializeHeader() {
|
||||||
|
let header = this.header;
|
||||||
|
let data = Buffer.alloc(12);
|
||||||
|
data.writeUInt16BE(header.ID, 0);
|
||||||
|
var f = 0x0000;
|
||||||
|
f = f | (header.QR << 15);
|
||||||
|
f = f | (header.OPCODE << 11);
|
||||||
|
f = f | (header.AA << 10);
|
||||||
|
f = f | (header.TC << 9);
|
||||||
|
f = f | (header.RD << 8);
|
||||||
|
f = f | (header.RA << 7);
|
||||||
|
f = f | (header.Z << 6);
|
||||||
|
f = f | (header.AD << 5);
|
||||||
|
f = f | (header.CD << 4);
|
||||||
|
f = f | header.RCODE;
|
||||||
|
data.writeUInt16BE(f, 2);
|
||||||
|
data.writeUInt16BE(header.QDCOUNT, 4);
|
||||||
|
data.writeUInt16BE(header.ANCOUNT, 6);
|
||||||
|
data.writeUInt16BE(header.NSCOUNT, 8);
|
||||||
|
data.writeUInt16BE(header.ARCOUNT, 10);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
serializeQuestion(question) {
|
||||||
|
let qname = this.serializeName(question.QNAME);
|
||||||
|
let data = Buffer.alloc(qname.length + 4);
|
||||||
|
qname.copy(data, 0, 0, qname.length);
|
||||||
|
let offset = qname.length;
|
||||||
|
data.writeUInt16BE(question.QTYPE, offset);
|
||||||
|
offset += 2;
|
||||||
|
data.writeUInt16BE(question.QCLASS, offset);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
serializeResourceRecord(record) {
|
||||||
|
// TODO: Implement compression
|
||||||
|
let name = this.serializeName(record.NAME);
|
||||||
|
let data = Buffer.alloc(name.length + 10 + record.RDLENGTH); // For TYPE, CLASS, TTL, RLENGTH
|
||||||
|
name.copy(data, 0, 0, name.length);
|
||||||
|
let offset = name.length;
|
||||||
|
data.writeUInt16BE(record.TYPE, offset);
|
||||||
|
offset += 2;
|
||||||
|
data.writeUInt16BE(record.CLASS, offset);
|
||||||
|
offset += 2;
|
||||||
|
data.writeUInt32BE(record.TTL, offset);
|
||||||
|
offset += 4;
|
||||||
|
data.writeUInt16BE(record.RDLENGTH, offset);
|
||||||
|
offset += 2;
|
||||||
|
record.RDATA.copy(data, offset, 0, record.RDLENGTH);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
serializeName(name) {
|
||||||
|
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 => {
|
||||||
|
console.log(e.length);
|
||||||
|
data.writeUInt8(e.length, offset);
|
||||||
|
offset++;
|
||||||
|
data.write(e, offset, e.length);
|
||||||
|
offset += e.length;
|
||||||
|
});
|
||||||
|
data.writeUInt8(0, offset);
|
||||||
|
console.log("name |", data.toString("hex"), length);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.Request = Request;
|
||||||
|
class RecourceRecord {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
get TTL() {
|
||||||
|
return this._TTL;
|
||||||
|
}
|
||||||
|
get RDLENGTH() {
|
||||||
|
return this.RDATA.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.RecourceRecord = RecourceRecord;
|
||||||
|
//# sourceMappingURL=request.js.map
|
1
lib/request.js.map
Normal file
1
lib/request.js.map
Normal file
File diff suppressed because one or more lines are too long
0
lib/test.d.ts
vendored
Normal file
0
lib/test.d.ts
vendored
Normal file
6
lib/test.js
Normal file
6
lib/test.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// const t = `71 15 01 00 00 01 00 00 00 00 00 00 02 64 63 08 73 65 72 76 69 63 65 73 0c 76 69 73 75 61 6c 73 74 75 64 69 6f 03 63 6f 6d 00 00 01 00 01`.split(" ").join("");
|
||||||
|
// // const tp = Buffer.from(t, "hex")
|
||||||
|
// // // import Parse from "./index";
|
||||||
|
// // import * as util from "util";
|
||||||
|
// // console.log(util.inspect(Parse(tp), false, 20, true));
|
||||||
|
//# sourceMappingURL=test.js.map
|
1
lib/test.js.map
Normal file
1
lib/test.js.map
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"AAAA,6KAA6K;AAC7K,sCAAsC;AACtC,qCAAqC;AACrC,mCAAmC;AACnC,4DAA4D"}
|
22
package.json
Normal file
22
package.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "nodename-core",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"main": "lib/listener.js",
|
||||||
|
"types": "lib/listener.d.ts",
|
||||||
|
"author": "Fabian Stamm <dev@fabianstamm.de>",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"watch": "tsc --watch",
|
||||||
|
"test": "nodemon lib/test.js"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/binary-parser": "^1.3.0",
|
||||||
|
"@types/node": "^10.0.8",
|
||||||
|
"nodemon": "^1.17.4",
|
||||||
|
"typescript": "^2.8.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"binary-parser": "^1.3.2"
|
||||||
|
}
|
||||||
|
}
|
70
src/listener.ts
Normal file
70
src/listener.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import * as net from "net"
|
||||||
|
import * as dgram from "dgram";
|
||||||
|
import { Request } from "./request";
|
||||||
|
import { PassThrough } from "stream";
|
||||||
|
export default class Listener {
|
||||||
|
private udp: dgram.Socket
|
||||||
|
private tcp: net.Server
|
||||||
|
constructor(type: "udp" | "tcp", onRequest: (request: Request) => any, port: number, host: string = "0.0.0.0") {
|
||||||
|
switch (type) {
|
||||||
|
case "udp":
|
||||||
|
this.udp = dgram.createSocket("udp4")
|
||||||
|
this.udp.on("listening", () => {
|
||||||
|
console.log(`UDP Server Listening on ${port}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.udp.on("message", (message, remote) => {
|
||||||
|
let request = new Request(message, (data) => {
|
||||||
|
// console.log("sending:", new Request(data, (a) => 0));
|
||||||
|
this.udp.send(data, remote.port, remote.address)
|
||||||
|
})
|
||||||
|
onRequest(request);
|
||||||
|
})
|
||||||
|
|
||||||
|
this.udp.bind(port, host)
|
||||||
|
break;
|
||||||
|
case "tcp":
|
||||||
|
console.log("Not correct implemented")
|
||||||
|
this.tcp = net.createServer((socket) => {
|
||||||
|
let length: number;
|
||||||
|
let got: number = 0;
|
||||||
|
let message = undefined;
|
||||||
|
socket.on("data", (data) => {
|
||||||
|
let offset = 0;
|
||||||
|
if (!message) {
|
||||||
|
length = data.readUInt16BE(0);
|
||||||
|
if (length > 2048) return socket.destroy(); //Requests with more that 2k are ignored
|
||||||
|
message = Buffer.alloc(length);
|
||||||
|
offset = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
let read = (data.length - offset) > (length - got) ? (length - got) : (data.length - offset);
|
||||||
|
data.copy(message, got, offset, read + offset)
|
||||||
|
got += read;
|
||||||
|
//ToDo don't ignore probably following requests
|
||||||
|
if (got >= length) {
|
||||||
|
let request = new Request(message, (data) => {
|
||||||
|
socket.write(data);
|
||||||
|
})
|
||||||
|
got = 0;
|
||||||
|
message = undefined;
|
||||||
|
length = 0;
|
||||||
|
onRequest(request);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.tcp.listen(port, host);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Unknown socket type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
if (this.udp) {
|
||||||
|
this.udp.close();
|
||||||
|
} else {
|
||||||
|
this.tcp.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
365
src/request.ts
Normal file
365
src/request.ts
Normal file
@ -0,0 +1,365 @@
|
|||||||
|
import { Parser } from "binary-parser"
|
||||||
|
|
||||||
|
const MAX_LABEL_SIZE = 63;
|
||||||
|
|
||||||
|
export enum queryTypes {
|
||||||
|
/**
|
||||||
|
* IPv4 address
|
||||||
|
*/
|
||||||
|
A = 0x01,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nameserver
|
||||||
|
*/
|
||||||
|
NS = 0x02, // nameserver
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obsolete
|
||||||
|
*/
|
||||||
|
MD = 0x03, // obsolete
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obsolete
|
||||||
|
*/
|
||||||
|
MF = 0x04, // obsolete
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias
|
||||||
|
*/
|
||||||
|
CNAME = 0x05, // alias
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start of authority
|
||||||
|
*/
|
||||||
|
SOA = 0x06, // start of authority
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Experimental
|
||||||
|
*/
|
||||||
|
MB = 0x07, // experimental
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Experimental
|
||||||
|
*/
|
||||||
|
MG = 0x08, // experimental
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Experimental
|
||||||
|
*/
|
||||||
|
MR = 0x09, // experimental
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Experimental
|
||||||
|
*/
|
||||||
|
NULL = 0x0A, // experimental null RR
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service description
|
||||||
|
*/
|
||||||
|
WKS = 0x0B, // service description
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse entry (inaddr.arpa)
|
||||||
|
*/
|
||||||
|
PTR = 0x0C, // reverse entry (inaddr.arpa)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Host information
|
||||||
|
*/
|
||||||
|
HINFO = 0x0D, // host information
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mailbox / Mail-list information
|
||||||
|
*/
|
||||||
|
MINFO = 0x0E, // mailbox or mail list information
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mail exchange
|
||||||
|
*/
|
||||||
|
MX = 0x0F, // mail exchange
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Text strings
|
||||||
|
*/
|
||||||
|
TXT = 0x10, // text strings
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IPv6 address
|
||||||
|
*/
|
||||||
|
AAAA = 0x1C, // ipv6 address
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SRV records
|
||||||
|
*/
|
||||||
|
SRV = 0x21, // srv records
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request to transfer entire zone
|
||||||
|
*/
|
||||||
|
AXFR = 0xFC, // request to transfer entire zone
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request for mailbox related records
|
||||||
|
*/
|
||||||
|
MAILA = 0xFE, // request for mailbox related records
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request for mail agend RRs
|
||||||
|
*/
|
||||||
|
MAILB = 0xFD, // request for mail agent RRs
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any class
|
||||||
|
*/
|
||||||
|
ANY = 0xFF, // any class
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerParser = new 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")
|
||||||
|
|
||||||
|
const labelParser = new Parser()
|
||||||
|
.endianess("big")
|
||||||
|
.uint8("dataLength")
|
||||||
|
.string("name", {
|
||||||
|
length: "dataLength",
|
||||||
|
encoding: "ascii"
|
||||||
|
})
|
||||||
|
|
||||||
|
const questionParser = new Parser()
|
||||||
|
.endianess("big")
|
||||||
|
.array("QNAME", {
|
||||||
|
type: labelParser,
|
||||||
|
readUntil: (item: any, buffer) => {
|
||||||
|
if (item.dataLength <= 0)
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
formatter: (value: { dataLength: number, name: string }[]) => {
|
||||||
|
return value.map(e => e.name).join(".").slice(0, -1);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.uint16("QTYPE")
|
||||||
|
.uint16("QCLASS")
|
||||||
|
|
||||||
|
function parseQuestions(count: number, packet: Buffer): MessageQuestion[] {
|
||||||
|
return <any>new Parser()
|
||||||
|
.endianess("big")
|
||||||
|
.array("questions", {
|
||||||
|
type: questionParser,
|
||||||
|
length: count
|
||||||
|
}).parse(packet).questions;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Request implements Message {
|
||||||
|
_header: MessageHeader;
|
||||||
|
get header() {
|
||||||
|
return Object.assign({}, this._header);
|
||||||
|
}
|
||||||
|
|
||||||
|
_questions: MessageQuestion[];
|
||||||
|
get questions() {
|
||||||
|
return this._questions.map(e => e);
|
||||||
|
}
|
||||||
|
|
||||||
|
answers: RecourceRecord[] = [];
|
||||||
|
authorities: RecourceRecord[] = [];
|
||||||
|
additionals: RecourceRecord[] = [];
|
||||||
|
|
||||||
|
_packet: Buffer;
|
||||||
|
constructor(packet: Buffer, private sendCallback: (packet: Buffer) => any) {
|
||||||
|
this._packet = packet;
|
||||||
|
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);
|
||||||
|
|
||||||
|
this._header = <any>headerParser.parse(headerData);
|
||||||
|
this._header.AD = 0;
|
||||||
|
this._questions = parseQuestions(this._header.QDCOUNT, bodyData);
|
||||||
|
}
|
||||||
|
|
||||||
|
send() {
|
||||||
|
this.sendCallback(this.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
private serialize(truncate: boolean = false, rcode: 0 | 1 | 2 | 3 | 4 | 5 = 0) {
|
||||||
|
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;
|
||||||
|
this._header.RCODE = rcode;
|
||||||
|
this._header.RA = 0;
|
||||||
|
let questions = this.questions.map(this.serializeQuestion, this)
|
||||||
|
let answers = this.answers.map(this.serializeResourceRecord, this)
|
||||||
|
let authority = this.authorities.map(this.serializeResourceRecord, this)
|
||||||
|
let additional = this.additionals.map(this.serializeResourceRecord, this)
|
||||||
|
|
||||||
|
let questionsByteLength = 0;
|
||||||
|
questions.forEach(e => questionsByteLength += e.length);
|
||||||
|
|
||||||
|
let answersByteLength = 0;
|
||||||
|
answers.forEach(e => answersByteLength += e.length)
|
||||||
|
|
||||||
|
let authorityByteLength = 0;
|
||||||
|
authority.forEach(e => authorityByteLength += e.length)
|
||||||
|
|
||||||
|
let additionalByteLength = 0;
|
||||||
|
additional.forEach(e => additionalByteLength += e.length)
|
||||||
|
|
||||||
|
let length = 12 + questionsByteLength + answersByteLength + authorityByteLength + additionalByteLength; //Header is always 12 byte large
|
||||||
|
|
||||||
|
if (truncate && length > 512) {
|
||||||
|
this._header.TC = 1;
|
||||||
|
|
||||||
|
//Buffer will ignore data that exeeds the max buffer length
|
||||||
|
length = 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
let header = this.serializeHeader()
|
||||||
|
|
||||||
|
let data = Buffer.alloc(length)
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
let append = (buffer: Buffer) => {
|
||||||
|
buffer.copy(data, offset, 0, buffer.length)
|
||||||
|
offset += buffer.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
append(header)
|
||||||
|
questions.forEach(append)
|
||||||
|
answers.forEach(append)
|
||||||
|
authority.forEach(append)
|
||||||
|
additional.forEach(append)
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private serializeHeader() {
|
||||||
|
let header = this.header;
|
||||||
|
let data = Buffer.alloc(12);
|
||||||
|
data.writeUInt16BE(header.ID, 0);
|
||||||
|
var f = 0x0000;
|
||||||
|
f = f | (header.QR << 15);
|
||||||
|
f = f | (header.OPCODE << 11);
|
||||||
|
f = f | (header.AA << 10);
|
||||||
|
f = f | (header.TC << 9);
|
||||||
|
f = f | (header.RD << 8);
|
||||||
|
f = f | (header.RA << 7);
|
||||||
|
f = f | (header.Z << 6);
|
||||||
|
f = f | (header.AD << 5);
|
||||||
|
f = f | (header.CD << 4);
|
||||||
|
f = f | header.RCODE;
|
||||||
|
data.writeUInt16BE(f, 2);
|
||||||
|
|
||||||
|
data.writeUInt16BE(header.QDCOUNT, 4)
|
||||||
|
data.writeUInt16BE(header.ANCOUNT, 6)
|
||||||
|
data.writeUInt16BE(header.NSCOUNT, 8)
|
||||||
|
data.writeUInt16BE(header.ARCOUNT, 10)
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private serializeQuestion(question: MessageQuestion) {
|
||||||
|
let qname = this.serializeName(question.QNAME);
|
||||||
|
let data = Buffer.alloc(qname.length + 4);
|
||||||
|
qname.copy(data, 0, 0, qname.length);
|
||||||
|
let offset = qname.length;
|
||||||
|
data.writeUInt16BE(question.QTYPE, offset);
|
||||||
|
offset += 2;
|
||||||
|
data.writeUInt16BE(question.QCLASS, offset);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private serializeResourceRecord(record: MessageRecourceRecord) {
|
||||||
|
// TODO: Implement compression
|
||||||
|
let name = this.serializeName(record.NAME);
|
||||||
|
let data = Buffer.alloc(name.length + 10 + record.RDLENGTH) // For TYPE, CLASS, TTL, RLENGTH
|
||||||
|
name.copy(data, 0, 0, name.length);
|
||||||
|
let offset = name.length;
|
||||||
|
data.writeUInt16BE(record.TYPE, offset)
|
||||||
|
offset += 2
|
||||||
|
data.writeUInt16BE(record.CLASS, offset)
|
||||||
|
offset += 2
|
||||||
|
data.writeUInt32BE(record.TTL, offset)
|
||||||
|
offset += 4
|
||||||
|
data.writeUInt16BE(record.RDLENGTH, offset)
|
||||||
|
offset += 2
|
||||||
|
record.RDATA.copy(data, offset, 0, record.RDLENGTH)
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private serializeName(name: string) {
|
||||||
|
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 => {
|
||||||
|
console.log(e.length);
|
||||||
|
data.writeUInt8(e.length, offset)
|
||||||
|
offset++
|
||||||
|
data.write(e, offset, e.length)
|
||||||
|
offset += e.length
|
||||||
|
})
|
||||||
|
data.writeUInt8(0, offset);
|
||||||
|
console.log("name |", data.toString("hex"), length)
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RecourceRecord implements MessageRecourceRecord {
|
||||||
|
NAME: string
|
||||||
|
private _TYPE: number;
|
||||||
|
set TYPE(value) {
|
||||||
|
if (value < 0 || value > 65535) throw new TypeError("TYPE Range: 0 - 65.535")
|
||||||
|
this._TYPE = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get TYPE() {
|
||||||
|
return this._TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _CLASS: number;
|
||||||
|
set CLASS(value) {
|
||||||
|
if (value < 0 || value > 65535) throw new TypeError("CLASS Range: 0 - 65.535")
|
||||||
|
this._CLASS = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get CLASS() {
|
||||||
|
return this._CLASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
_TTL: number;
|
||||||
|
set TTL(value) {
|
||||||
|
if (value < 0 || value > 4294967295) throw new TypeError("TTL Range: 0 - 4.294.967.295")
|
||||||
|
}
|
||||||
|
|
||||||
|
get TTL() {
|
||||||
|
return this._TTL;
|
||||||
|
}
|
||||||
|
|
||||||
|
RDATA: Buffer;
|
||||||
|
|
||||||
|
get RDLENGTH() {
|
||||||
|
return this.RDATA.length;
|
||||||
|
}
|
||||||
|
}
|
5
src/test.ts
Normal file
5
src/test.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// const t = `71 15 01 00 00 01 00 00 00 00 00 00 02 64 63 08 73 65 72 76 69 63 65 73 0c 76 69 73 75 61 6c 73 74 75 64 69 6f 03 63 6f 6d 00 00 01 00 01`.split(" ").join("");
|
||||||
|
// // const tp = Buffer.from(t, "hex")
|
||||||
|
// // // import Parse from "./index";
|
||||||
|
// // import * as util from "util";
|
||||||
|
// // console.log(util.inspect(Parse(tp), false, 20, true));
|
177
src/types.d.ts
vendored
Normal file
177
src/types.d.ts
vendored
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
interface MessageHeader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A 16 bit identifier assigned by the program that
|
||||||
|
* generates any kind of query. This identifier is copied
|
||||||
|
* the corresponding reply and can be used by the requester
|
||||||
|
* to match up replies to outstanding queries.
|
||||||
|
*/
|
||||||
|
ID: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines if query or response
|
||||||
|
*/
|
||||||
|
QR: 0 | 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 4 Bit code, that defines type of query.
|
||||||
|
* 0 Standard query
|
||||||
|
* 1 Inverse query
|
||||||
|
* 2 Server status request
|
||||||
|
* 3-15 reserved for future use
|
||||||
|
*/
|
||||||
|
OPCODE: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorative Answer - only valid in responses and
|
||||||
|
* specifies that the responding name server is an
|
||||||
|
* authority for the domain name in question section
|
||||||
|
*/
|
||||||
|
AA: 0 | 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truncation - specifies that his message was truncated doe to
|
||||||
|
* length grater than permitted on the transaction channel
|
||||||
|
*
|
||||||
|
* WARNING: NOT IMPLEMENTED IN THIS APPLICATION
|
||||||
|
*/
|
||||||
|
TC: 0 | 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursion Desired - set in query and copied to response
|
||||||
|
* if is set, directs name server to pursue the query recursively
|
||||||
|
*
|
||||||
|
* WARNING: NOT IMPLEMENTED IN THIS APPLICATION
|
||||||
|
*/
|
||||||
|
RD: 0 | 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursion Available - will be cleared in response to
|
||||||
|
* show the client that recursion is not available
|
||||||
|
*/
|
||||||
|
RA: 0 | 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserved for future usage, must be 0 in all queries
|
||||||
|
*/
|
||||||
|
Z: 0
|
||||||
|
|
||||||
|
AD: 0 | 1
|
||||||
|
|
||||||
|
CD: 0 | 1
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response Code - 4 bit field is part of response
|
||||||
|
*
|
||||||
|
* 0 No error condition
|
||||||
|
* 1 Format error - unable to interpret query
|
||||||
|
* 2 Server failure - internal problem
|
||||||
|
* 3 Name error - Only for authorative name server, domain name of query does not exist
|
||||||
|
* 4 Not implemented - Request not supported
|
||||||
|
* 5 Refused - Nameserver refuses request
|
||||||
|
* 6-15 Reserved for future usage
|
||||||
|
*/
|
||||||
|
RCODE: 0 | 1 | 2 | 3 | 4 | 5
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of entries in question section
|
||||||
|
* uint16
|
||||||
|
*/
|
||||||
|
QDCOUNT: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of entries in answer section
|
||||||
|
* uint16
|
||||||
|
*/
|
||||||
|
ANCOUNT: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of resource records in authority records section
|
||||||
|
* uint16
|
||||||
|
*/
|
||||||
|
NSCOUNT: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of resource records in additional records section
|
||||||
|
* uint16
|
||||||
|
*/
|
||||||
|
ARCOUNT: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MessageQuestion {
|
||||||
|
/**
|
||||||
|
* Domain name represented as sequence of labels
|
||||||
|
* Each label consists of a length octed followed
|
||||||
|
* by that number of octeds
|
||||||
|
*/
|
||||||
|
QNAME: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Two octed code wich specifies the type of the query.
|
||||||
|
*/
|
||||||
|
QTYPE: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Two octed code that specifies the class of the Query
|
||||||
|
* IS for internet
|
||||||
|
* WARNING: ONLY IN IS SUPPORTED BY THIS APPLICATION
|
||||||
|
*/
|
||||||
|
QCLASS: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MessageRecourceRecord {
|
||||||
|
/**
|
||||||
|
* Domain name to wich resource record pertains
|
||||||
|
*/
|
||||||
|
NAME: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Two octets containing TT type code.
|
||||||
|
* Specifies meaning of data in RDATA field
|
||||||
|
*
|
||||||
|
* uint16
|
||||||
|
*/
|
||||||
|
TYPE: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Two octets specifying class of RDATA field
|
||||||
|
*
|
||||||
|
* uint16
|
||||||
|
*/
|
||||||
|
CLASS: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies Time Interval (in seconds) that the record
|
||||||
|
* may be cached before discarded.
|
||||||
|
* Zero values are not cached
|
||||||
|
*
|
||||||
|
* uint32
|
||||||
|
*/
|
||||||
|
TTL: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of RDATA field
|
||||||
|
*
|
||||||
|
* uint16
|
||||||
|
*/
|
||||||
|
RDLENGTH: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a variable length string of ectets taht describes
|
||||||
|
* the resource. The format is defined by TYPE and CLASS
|
||||||
|
* field.
|
||||||
|
*
|
||||||
|
* If TYPE is A and CLASS is IN, RDATA is a 4 octet
|
||||||
|
* ARPA internet address.
|
||||||
|
*/
|
||||||
|
RDATA: Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Message {
|
||||||
|
header: MessageHeader
|
||||||
|
questions: MessageQuestion[]
|
||||||
|
answers: MessageRecourceRecord[];
|
||||||
|
authorities: MessageRecourceRecord[];
|
||||||
|
additionals: MessageRecourceRecord[];
|
||||||
|
}
|
55
tsconfig.json
Normal file
55
tsconfig.json
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Basic Options */
|
||||||
|
"target": "es2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
|
||||||
|
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||||
|
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||||
|
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||||
|
// "checkJs": true, /* Report errors in .js files. */
|
||||||
|
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||||
|
"declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||||
|
"sourceMap": true, /* Generates corresponding '.map' file. */
|
||||||
|
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||||
|
"outDir": "./lib", /* Redirect output structure to the directory. */
|
||||||
|
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||||
|
// "removeComments": true, /* Do not emit comments to output. */
|
||||||
|
// "noEmit": true, /* Do not emit outputs. */
|
||||||
|
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||||
|
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||||
|
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||||
|
/* Strict Type-Checking Options */
|
||||||
|
"strict": false, /* Enable all strict type-checking options. */
|
||||||
|
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||||
|
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||||
|
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||||
|
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||||
|
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||||
|
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||||
|
/* Additional Checks */
|
||||||
|
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||||
|
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||||
|
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||||
|
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||||
|
/* Module Resolution Options */
|
||||||
|
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||||
|
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||||
|
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||||
|
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||||
|
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||||
|
// "types": [], /* Type declaration files to be included in compilation. */
|
||||||
|
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||||
|
//"esModuleInterop": false /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||||
|
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||||
|
/* Source Map Options */
|
||||||
|
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||||
|
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||||
|
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||||
|
/* Experimental Options */
|
||||||
|
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./src"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user