1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | 'use strict';
|
22 |
|
23 | var consts = require('./consts'),
|
24 | BufferCursor = require('buffercursor'),
|
25 | BufferCursorOverflow = BufferCursor.BufferCursorOverflow,
|
26 | ipaddr = require('ipaddr.js'),
|
27 | util = require('util');
|
28 |
|
29 | var Packet = module.exports = function(socket) {
|
30 | this.header = {
|
31 | id: 0,
|
32 | qr: 0,
|
33 | opcode: 0,
|
34 | aa: 0,
|
35 | tc: 0,
|
36 | rd: 1,
|
37 | ra: 0,
|
38 | res1: 0,
|
39 | res2: 0,
|
40 | res3: 0,
|
41 | rcode: 0
|
42 | };
|
43 | this.question = [];
|
44 | this.answer = [];
|
45 | this.authority = [];
|
46 | this.additional = [];
|
47 | this.edns_options = [];
|
48 | this.payload = undefined;
|
49 | this.address = undefined;
|
50 |
|
51 | this._socket = socket;
|
52 | };
|
53 |
|
54 | Packet.prototype.send = function() {
|
55 | var buff, len, size;
|
56 |
|
57 | if (typeof(this.edns_version) !== 'undefined') {
|
58 | size = 4096;
|
59 | }
|
60 |
|
61 | this.payload = size = size || this._socket.base_size;
|
62 |
|
63 | buff = this._socket.buffer(size);
|
64 | len = Packet.write(buff, this);
|
65 | this._socket.send(len);
|
66 | };
|
67 |
|
68 | var LABEL_POINTER = 0xC0;
|
69 |
|
70 | var isPointer = function(len) {
|
71 | return (len & LABEL_POINTER) === LABEL_POINTER;
|
72 | };
|
73 |
|
74 | var name_unpack = function(buff, index) {
|
75 | var parts, len, start, pos, i, part, combine = [];
|
76 |
|
77 | start = buff.tell();
|
78 |
|
79 | parts = [];
|
80 | len = buff.readUInt8();
|
81 |
|
82 | while (len !== 0) {
|
83 | if (isPointer(len)) {
|
84 | len -= LABEL_POINTER;
|
85 | len = len << 8;
|
86 | pos = len + buff.readUInt8();
|
87 | parts.push({
|
88 | pos: pos,
|
89 | value: index[pos]
|
90 | });
|
91 | len = 0;
|
92 | } else {
|
93 | parts.push({
|
94 | pos: buff.tell() - 1,
|
95 | value: buff.toString('ascii', len)
|
96 | });
|
97 | len = buff.readUInt8();
|
98 | }
|
99 | }
|
100 |
|
101 | for (i = parts.length - 1; i >= 0; i--) {
|
102 | part = parts[i];
|
103 | combine.splice(0, 0, part.value);
|
104 | index[part.pos] = combine.join('.');
|
105 | }
|
106 |
|
107 | return combine.join('.');
|
108 | };
|
109 |
|
110 | var name_pack = function(str, buff, index) {
|
111 | var offset, dot, part;
|
112 |
|
113 | while (str) {
|
114 | if (index[str]) {
|
115 | offset = (LABEL_POINTER << 8) + index[str];
|
116 | buff.writeUInt16BE(offset);
|
117 | break;
|
118 | } else {
|
119 | index[str] = buff.tell();
|
120 | dot = str.indexOf('.');
|
121 | if (dot > -1) {
|
122 | part = str.slice(0, dot);
|
123 | str = str.slice(dot + 1);
|
124 | } else {
|
125 | part = str;
|
126 | str = undefined;
|
127 | }
|
128 | buff.writeUInt8(part.length);
|
129 | buff.write(part, part.length, 'ascii');
|
130 | }
|
131 | }
|
132 |
|
133 | if (!str) {
|
134 | buff.writeUInt8(0);
|
135 | }
|
136 | };
|
137 |
|
138 | Packet.write = function(buff, packet) {
|
139 | var state,
|
140 | next,
|
141 | name,
|
142 | val,
|
143 | section,
|
144 | count,
|
145 | pos,
|
146 | rdata_pos,
|
147 | last_resource,
|
148 | label_index = {};
|
149 |
|
150 | buff = BufferCursor(buff);
|
151 |
|
152 | if (typeof(packet.edns_version) !== 'undefined') {
|
153 | state = 'EDNS';
|
154 | } else {
|
155 | state = 'HEADER';
|
156 | }
|
157 |
|
158 | while (true) {
|
159 | try {
|
160 | switch (state) {
|
161 | case 'EDNS':
|
162 | val = {
|
163 | name: '',
|
164 | type: consts.NAME_TO_QTYPE.OPT,
|
165 | class: packet.payload
|
166 | };
|
167 | pos = packet.header.rcode;
|
168 | val.ttl = packet.header.rcode >> 4;
|
169 | packet.header.rcode = pos - (val.ttl << 4);
|
170 | val.ttl = (val.ttl << 8) + packet.edns_version;
|
171 | val.ttl = (val.ttl << 16) + (packet.do << 15) & 0x8000;
|
172 | packet.additional.splice(0, 0, val);
|
173 | state = 'HEADER';
|
174 | break;
|
175 | case 'HEADER':
|
176 | buff.writeUInt16BE(packet.header.id);
|
177 | val = 0;
|
178 | val += (packet.header.qr << 15) & 0x8000;
|
179 | val += (packet.header.opcode << 11) & 0x7800;
|
180 | val += (packet.header.aa << 10) & 0x400;
|
181 | val += (packet.header.tc << 9) & 0x200;
|
182 | val += (packet.header.rd << 8) & 0x100;
|
183 | val += (packet.header.ra << 7) & 0x80;
|
184 | val += (packet.header.res1 << 6) & 0x40;
|
185 | val += (packet.header.res1 << 5) & 0x20;
|
186 | val += (packet.header.res1 << 4) & 0x10;
|
187 | val += packet.header.rcode & 0xF;
|
188 | buff.writeUInt16BE(val);
|
189 |
|
190 |
|
191 | buff.writeUInt16BE(1);
|
192 |
|
193 | buff.writeUInt16BE(packet.answer.length);
|
194 |
|
195 | buff.writeUInt16BE(packet.authority.length);
|
196 |
|
197 | buff.writeUInt16BE(packet.additional.length);
|
198 | state = 'QUESTION';
|
199 | break;
|
200 | case 'TRUNCATE':
|
201 | buff.seek(2);
|
202 | val = buff.readUInt16BE();
|
203 | val |= (1 << 9) & 0x200;
|
204 | buff.seek(2);
|
205 | buff.writeUInt16BE(val);
|
206 | switch (section) {
|
207 | case 'answer':
|
208 | pos = 6;
|
209 |
|
210 | buff.seek(8);
|
211 | buff.writeUInt16BE(0);
|
212 | buff.writeUInt16BE(0);
|
213 | break;
|
214 | case 'authority':
|
215 | pos = 8;
|
216 |
|
217 | buff.seek(10);
|
218 | buff.writeUInt16BE(0);
|
219 | break;
|
220 | case 'additional':
|
221 | pos = 10;
|
222 | break;
|
223 | }
|
224 | buff.seek(pos);
|
225 | buff.writeUInt16BE(count - 1);
|
226 | buff.seek(last_resource);
|
227 | state = 'END';
|
228 | break;
|
229 | case 'NAME_PACK':
|
230 | name_pack(name, buff, label_index);
|
231 | state = next;
|
232 | break;
|
233 | case 'QUESTION':
|
234 | val = packet.question[0];
|
235 | name = val.name;
|
236 | state = 'NAME_PACK';
|
237 | next = 'QUESTION_NEXT';
|
238 | break;
|
239 | case 'QUESTION_NEXT':
|
240 | buff.writeUInt16BE(val.type);
|
241 | buff.writeUInt16BE(val.class);
|
242 | state = 'RESOURCE_RECORD';
|
243 | section = 'answer';
|
244 | count = 0;
|
245 | break;
|
246 | case 'RESOURCE_RECORD':
|
247 | last_resource = buff.tell();
|
248 | if (packet[section].length == count) {
|
249 | switch (section) {
|
250 | case 'answer':
|
251 | section = 'authority';
|
252 | state = 'RESOURCE_RECORD';
|
253 | break;
|
254 | case 'authority':
|
255 | section = 'additional';
|
256 | state = 'RESOURCE_RECORD';
|
257 | break;
|
258 | case 'additional':
|
259 | state = 'END';
|
260 | break;
|
261 | }
|
262 | count = 0;
|
263 | } else {
|
264 | state = 'RESOURCE_WRITE';
|
265 | }
|
266 | break;
|
267 | case 'RESOURCE_WRITE':
|
268 | val = packet[section][count];
|
269 | name = val.name;
|
270 | state = 'NAME_PACK';
|
271 | next = 'RESOURCE_WRITE_NEXT';
|
272 | break;
|
273 | case 'RESOURCE_WRITE_NEXT':
|
274 | buff.writeUInt16BE(val.type);
|
275 | buff.writeUInt16BE(val.class);
|
276 | buff.writeUInt32BE(val.ttl);
|
277 |
|
278 |
|
279 | rdata_pos = buff.tell();
|
280 | buff.writeUInt16BE(0);
|
281 |
|
282 | state = consts.QTYPE_TO_NAME[val.type];
|
283 | break;
|
284 | case 'RESOURCE_DONE':
|
285 | pos = buff.tell();
|
286 | buff.seek(rdata_pos);
|
287 | buff.writeUInt16BE(pos - rdata_pos - 2);
|
288 | buff.seek(pos);
|
289 | count += 1;
|
290 | state = 'RESOURCE_RECORD';
|
291 | break;
|
292 | case 'A':
|
293 | case 'AAAA':
|
294 |
|
295 | val = ipaddr.parse(val.address).toByteArray();
|
296 | val.forEach(function(b) {
|
297 | buff.writeUInt8(b);
|
298 | });
|
299 | state = 'RESOURCE_DONE';
|
300 | break;
|
301 | case 'NS':
|
302 | case 'CNAME':
|
303 | case 'PTR':
|
304 | name = val.data;
|
305 | state = 'NAME_PACK';
|
306 | next = 'RESOURCE_DONE';
|
307 | break;
|
308 | case 'TXT':
|
309 |
|
310 | buff.writeUInt8(val.data.length);
|
311 | buff.write(val.data, val.data.length, 'ascii');
|
312 | state = 'RESOURCE_DONE';
|
313 | break;
|
314 | case 'MX':
|
315 | buff.writeUInt16BE(val.priority);
|
316 | name = val.exchange;
|
317 | state = 'NAME_PACK';
|
318 | next = 'RESOURCE_DONE';
|
319 | break;
|
320 | case 'SRV':
|
321 | buff.writeUInt16BE(val.priority);
|
322 | buff.writeUInt16BE(val.weight);
|
323 | buff.writeUInt16BE(val.port);
|
324 | name = val.target;
|
325 | state = 'NAME_PACK';
|
326 | next = 'RESOURCE_DONE';
|
327 | break;
|
328 | case 'SOA':
|
329 | name = val.primary;
|
330 | state = 'NAME_PACK';
|
331 | next = 'SOA_ADMIN';
|
332 | break;
|
333 | case 'SOA_ADMIN':
|
334 | name = val.admin;
|
335 | state = 'NAME_PACK';
|
336 | next = 'SOA_NEXT';
|
337 | break;
|
338 | case 'SOA_NEXT':
|
339 | buff.writeUInt32BE(val.serial);
|
340 | buff.writeInt32BE(val.refresh);
|
341 | buff.writeInt32BE(val.retry);
|
342 | buff.writeInt32BE(val.expiration);
|
343 | buff.writeInt32BE(val.minimum);
|
344 | state = 'RESOURCE_DONE';
|
345 | break;
|
346 | case 'OPT':
|
347 | while (packet.edns_options.length) {
|
348 | val = packet.edns_options.pop();
|
349 | buff.writeUInt16BE(val.code);
|
350 | buff.writeUInt16BE(val.data.length);
|
351 | for (pos = 0; pos < val.data.length; pos++) {
|
352 | buff.writeUInt8(val.data.readUInt8(pos));
|
353 | }
|
354 | }
|
355 | state = 'RESOURCE_DONE';
|
356 | break;
|
357 | case 'NAPTR':
|
358 | buff.writeUInt16BE(val.order);
|
359 | buff.writeUInt16BE(val.preference);
|
360 | buff.writeUInt8(val.flags.length);
|
361 | buff.write(val.flags, val.flags.length, 'ascii');
|
362 | buff.writeUInt8(val.service.length);
|
363 | buff.write(val.service, val.service.length, 'ascii');
|
364 | buff.writeUInt8(val.regexp.length);
|
365 | buff.write(val.regexp, val.regexp.length, 'ascii');
|
366 | buff.writeUInt8(val.replacement.length);
|
367 | buff.write(val.replacement, val.replacement.length, 'ascii');
|
368 | state = 'RESOURCE_DONE';
|
369 | break;
|
370 | case 'END':
|
371 | return buff.tell();
|
372 | break;
|
373 | default:
|
374 | throw new Error('WTF No State While Writing');
|
375 | break;
|
376 | }
|
377 | } catch (e) {
|
378 | if (e instanceof BufferCursorOverflow) {
|
379 | state = 'TRUNCATE';
|
380 | } else {
|
381 | throw e;
|
382 | }
|
383 | }
|
384 | }
|
385 | };
|
386 |
|
387 | Packet.parse = function(msg, socket) {
|
388 | var state,
|
389 | len,
|
390 | pos,
|
391 | val,
|
392 | rdata_len,
|
393 | rdata,
|
394 | label_index = {},
|
395 | counts = {},
|
396 | section,
|
397 | count;
|
398 |
|
399 | var packet = new Packet(socket);
|
400 |
|
401 | pos = 0;
|
402 | state = 'HEADER';
|
403 |
|
404 | msg = BufferCursor(msg);
|
405 | len = msg.length;
|
406 |
|
407 | while (true) {
|
408 | switch (state) {
|
409 | case 'HEADER':
|
410 | packet.header.id = msg.readUInt16BE();
|
411 | val = msg.readUInt16BE();
|
412 | packet.header.qr = (val & 0x8000) >> 15;
|
413 | packet.header.opcode = (val & 0x7800) >> 11;
|
414 | packet.header.aa = (val & 0x400) >> 10;
|
415 | packet.header.tc = (val & 0x200) >> 9;
|
416 | packet.header.rd = (val & 0x100) >> 8;
|
417 | packet.header.ra = (val & 0x80) >> 7;
|
418 | packet.header.res1 = (val & 0x40) >> 6;
|
419 | packet.header.res2 = (val & 0x20) >> 5;
|
420 | packet.header.res3 = (val & 0x10) >> 4;
|
421 | packet.header.rcode = (val & 0xF);
|
422 | counts.qdcount = msg.readUInt16BE();
|
423 | counts.ancount = msg.readUInt16BE();
|
424 | counts.nscount = msg.readUInt16BE();
|
425 | counts.arcount = msg.readUInt16BE();
|
426 | state = 'QUESTION';
|
427 | break;
|
428 | case 'QUESTION':
|
429 | val = {};
|
430 | val.name = name_unpack(msg, label_index);
|
431 | val.type = msg.readUInt16BE();
|
432 | val.class = msg.readUInt16BE();
|
433 | packet.question.push(val);
|
434 |
|
435 | state = 'RESOURCE_RECORD';
|
436 | section = 'answer';
|
437 | count = 'ancount';
|
438 | break;
|
439 | case 'RESOURCE_RECORD':
|
440 | if (counts[count] === packet[section].length) {
|
441 | switch (section) {
|
442 | case 'answer':
|
443 | section = 'authority';
|
444 | count = 'nscount';
|
445 | break;
|
446 | case 'authority':
|
447 | section = 'additional';
|
448 | count = 'arcount';
|
449 | break;
|
450 | case 'additional':
|
451 | state = 'END';
|
452 | break;
|
453 | }
|
454 | } else {
|
455 | state = 'RR_UNPACK';
|
456 | }
|
457 | break;
|
458 | case 'RR_UNPACK':
|
459 | val = {};
|
460 | val.name = name_unpack(msg, label_index);
|
461 | val.type = msg.readUInt16BE();
|
462 | val.class = msg.readUInt16BE();
|
463 | val.ttl = msg.readUInt32BE();
|
464 | rdata_len = msg.readUInt16BE();
|
465 | rdata = msg.slice(rdata_len);
|
466 | state = consts.QTYPE_TO_NAME[val.type];
|
467 | break;
|
468 | case 'RESOURCE_DONE':
|
469 | packet[section].push(val);
|
470 | state = 'RESOURCE_RECORD';
|
471 | break;
|
472 | case 'A':
|
473 | val.address = new ipaddr.IPv4(rdata.toByteArray());
|
474 | val.address = val.address.toString();
|
475 | state = 'RESOURCE_DONE';
|
476 | break;
|
477 | case 'AAAA':
|
478 | val.address = new ipaddr.IPv6(rdata.toByteArray('readUInt16BE'));
|
479 | val.address = val.address.toString();
|
480 | state = 'RESOURCE_DONE';
|
481 | break;
|
482 | case 'NS':
|
483 | case 'CNAME':
|
484 | case 'PTR':
|
485 | pos = msg.tell();
|
486 | msg.seek(pos - rdata_len);
|
487 | val.data = name_unpack(msg, label_index);
|
488 | msg.seek(pos);
|
489 | state = 'RESOURCE_DONE';
|
490 | break;
|
491 | case 'TXT':
|
492 | val.data = '';
|
493 | while (!rdata.eof()) {
|
494 | val.data += rdata.toString('ascii', rdata.readUInt8());
|
495 | }
|
496 | state = 'RESOURCE_DONE';
|
497 | break;
|
498 | case 'MX':
|
499 | val.priority = rdata.readUInt16BE();
|
500 | pos = msg.tell();
|
501 | msg.seek(pos - rdata_len + rdata.tell());
|
502 | val.exchange = name_unpack(msg, label_index);
|
503 | msg.seek(pos);
|
504 | state = 'RESOURCE_DONE';
|
505 | break;
|
506 | case 'SRV':
|
507 | val.priority = rdata.readUInt16BE();
|
508 | val.weight = rdata.readUInt16BE();
|
509 | val.port = rdata.readUInt16BE();
|
510 | pos = msg.tell();
|
511 | msg.seek(pos - rdata_len + rdata.tell());
|
512 | val.target = name_unpack(msg, label_index);
|
513 | msg.seek(pos);
|
514 | state = 'RESOURCE_DONE';
|
515 | break;
|
516 | case 'SOA':
|
517 | pos = msg.tell();
|
518 | msg.seek(pos - rdata_len + rdata.tell());
|
519 | val.primary = name_unpack(msg, label_index);
|
520 | val.admin = name_unpack(msg, label_index);
|
521 | rdata.seek(msg.tell() - (pos - rdata_len + rdata.tell()));
|
522 | msg.seek(pos);
|
523 | val.serial = rdata.readUInt32BE();
|
524 | val.refresh = rdata.readInt32BE();
|
525 | val.retry = rdata.readInt32BE();
|
526 | val.expiration = rdata.readInt32BE();
|
527 | val.minimum = rdata.readInt32BE();
|
528 | state = 'RESOURCE_DONE';
|
529 | break;
|
530 | case 'OPT':
|
531 |
|
532 | counts[count] -= 1;
|
533 | packet.payload = val.class;
|
534 | pos = msg.tell();
|
535 | msg.seek(pos - 6);
|
536 | packet.header.rcode = (msg.readUInt8() << 4) + packet.header.rcode;
|
537 | packet.edns_version = msg.readUInt8();
|
538 | val = msg.readUInt16BE();
|
539 | msg.seek(pos);
|
540 | packet.do = (val & 0x8000) << 15;
|
541 | while (!rdata.eof()) {
|
542 | packet.edns_options.push({
|
543 | code: rdata.readUInt16BE(),
|
544 | data: rdata.slice(rdata.readUInt16BE()).buffer
|
545 | });
|
546 | }
|
547 | state = 'RESOURCE_RECORD';
|
548 | break;
|
549 | case 'NAPTR':
|
550 | val.order = rdata.readUInt16BE();
|
551 | val.preference = rdata.readUInt16BE();
|
552 | pos = rdata.readUInt8();
|
553 | val.flags = rdata.toString('ascii', pos);
|
554 | pos = rdata.readUInt8();
|
555 | val.service = rdata.toString('ascii', pos);
|
556 | pos = rdata.readUInt8();
|
557 | val.regexp = rdata.toString('ascii', pos);
|
558 | pos = rdata.readUInt8();
|
559 | val.replacement = rdata.toString('ascii', pos);
|
560 | state = 'RESOURCE_DONE';
|
561 | break;
|
562 | case 'END':
|
563 | return packet;
|
564 | break;
|
565 | default:
|
566 |
|
567 | state = 'RESOURCE_DONE';
|
568 | break;
|
569 | }
|
570 | }
|
571 | };
|