UNPKG

7.25 kBJavaScriptView Raw
1// Copyright 2012 Timothy J Fontaine <tjfontaine@gmail.com>
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE
20
21var dgram = require('dgram'),
22 EventEmitter = require('events').EventEmitter,
23 ipaddr = require('ipaddr.js'),
24 net = require('net'),
25 util = require('util');
26
27var UDPSocket = exports.UDPSocket = function(socket, remote) {
28 this._socket = socket;
29 this._remote = remote;
30 this._buff = undefined;
31 this.base_size = 512;
32 this.bound = false;
33 this.unref = undefined;
34 this.ref = undefined;
35};
36util.inherits(UDPSocket, EventEmitter);
37
38UDPSocket.prototype.buffer = function(size) {
39 this._buff = new Buffer(size);
40 return this._buff;
41};
42
43UDPSocket.prototype.send = function(len) {
44 this._socket.send(this._buff, 0, len, this._remote.port,
45 this._remote.address);
46};
47
48UDPSocket.prototype.bind = function(type) {
49 var self = this;
50
51 if (this.bound) {
52 this.emit('ready');
53 } else {
54 this._socket = dgram.createSocket(type);
55 this._socket.on('listening', function() {
56 self.bound = true;
57 if (self._socket.unref) {
58 self.unref = function() {
59 self._socket.unref();
60 }
61 self.ref = function() {
62 self._socket.ref();
63 }
64 }
65 self.emit('ready');
66 });
67
68 this._socket.on('message', this.emit.bind(this, 'message'));
69
70 this._socket.on('close', function() {
71 self.bound = false;
72 self.emit('close');
73 });
74
75 this._socket.bind();
76 }
77};
78
79UDPSocket.prototype.close = function() {
80 this._socket.close();
81};
82
83UDPSocket.prototype.remote = function(remote) {
84 return new UDPSocket(this._socket, remote);
85};
86
87var TCPSocket = exports.TCPSocket = function(socket) {
88 UDPSocket.call(this, socket);
89 this.base_size = 4096;
90 this._rest = undefined;
91};
92util.inherits(TCPSocket, UDPSocket);
93
94TCPSocket.prototype.buffer = function(size) {
95 this._buff = new Buffer(size + 2);
96 return this._buff.slice(2);
97};
98
99TCPSocket.prototype.send = function(len) {
100 this._buff.writeUInt16BE(len, 0);
101 this._socket.write(this._buff.slice(0, len + 2));
102};
103
104TCPSocket.prototype.bind = function(server) {
105 var self = this;
106
107 if (this.bound) {
108 this.emit('ready');
109 } else {
110 this._socket = net.connect(server.port, server.address);
111
112 this._socket.on('connect', function() {
113 self.bound = true;
114 if (self._socket.unref) {
115 self.unref = function() {
116 self._socket.unref();
117 }
118 self.ref = function() {
119 self._socket.ref();
120 }
121 }
122 self.emit('ready');
123 });
124
125 this._socket.on('timeout', function() {
126 self.bound = false;
127 self.emit('close');
128 });
129
130 this._socket.on('close', function() {
131 self.bound = false;
132 self.emit('close');
133 });
134
135 this.catchMessages();
136 }
137};
138
139TCPSocket.prototype.catchMessages = function() {
140 var self = this;
141 this._socket.on('data', function(data) {
142 var len, tmp;
143 if (!self._rest) {
144 self._rest = data;
145 } else {
146 tmp = new Buffer(self._rest.length + data.length);
147 self._rest.copy(tmp, 0);
148 data.copy(tmp, self._rest.length);
149 self._rest = tmp;
150 }
151 while (self._rest && self._rest.length > 2) {
152 len = self._rest.readUInt16BE(0);
153 if (self._rest.length >= len + 2) {
154 self.emit('message', self._rest.slice(2, len + 2), self);
155 self._rest = self._rest.slice(len + 2);
156 } else {
157 break;
158 }
159 }
160 });
161};
162
163TCPSocket.prototype.close = function() {
164 this._socket.end();
165};
166
167TCPSocket.prototype.remote = function() {
168 return this;
169};
170
171exports.reverseIP = function(ip) {
172 var address, kind, reverseip, parts;
173 address = ipaddr.parse(ip.split(/%/)[0]);
174 kind = address.kind();
175
176 switch (kind) {
177 case 'ipv4':
178 address = address.toByteArray();
179 address.reverse();
180 reverseip = address.join('.') + '.IN-ADDR.ARPA';
181 break;
182 case 'ipv6':
183 parts = [];
184 address.toNormalizedString().split(':').forEach(function(part) {
185 var i, pad = 4 - part.length;
186 for (i = 0; i < pad; i++) {
187 part = '0' + part;
188 }
189 part.split('').forEach(function(p) {
190 parts.push(p);
191 });
192 });
193 parts.reverse();
194 reverseip = parts.join('.') + '.IP6.ARPA';
195 break;
196 }
197
198 return reverseip;
199};
200
201var is_absolute = exports.is_absolute = function (f) {
202 return f && /\.$/.test(f);
203};
204
205var ensure_absolute = exports.ensure_absolute = function (f) {
206 if (!is_absolute(f))
207 return f += '.';
208 return f;
209};
210
211var CNAME = require('./consts').NAME_TO_QTYPE.CNAME;
212
213var Lookup = exports.Lookup = function (store, zone, question, cb) {
214 if (!(this instanceof Lookup))
215 return new Lookup(store, zone, question, cb);
216
217 this.store = store;
218 this.zone = zone;
219 this.cb = cb;
220 this.question = question;
221 this.results = [];
222 this.wildcard = undefined;
223
224 this.name = ensure_absolute(question.name);
225
226 this.store.get(this.zone, this.name, this.lookup.bind(this));
227};
228
229Lookup.prototype.send = function (err) {
230 this.cb(err, this.results);
231};
232
233Lookup.prototype.lookup = function (err, results) {
234 var type, ret, name, self = this;
235
236 if (err)
237 return this.send(err);
238
239 if (!results) {
240 if (!this.wildcard)
241 this.wildcard = this.question.name;
242
243 if (this.wildcard.toLowerCase() == this.zone.toLowerCase())
244 return this.send();
245
246 name = this.wildcard = this.wildcard.split('.').splice(1).join('.');
247
248 // 'com.'.split('.').splice(1) will return empty string, we're at the end
249 if (!this.wildcard)
250 return this.send();
251
252 name = '*.' + name;
253 } else if (results[this.question.type]) {
254 type = this.question.type;
255 ret = results;
256 } else if (results[CNAME]) {
257 type = CNAME;
258 ret = results;
259 this.name = name = results[CNAME][0].data
260 }
261
262 if (ret) {
263 ret = ret[type];
264 ret.forEach(function (r) {
265 var rr, k;
266
267 if (self.wildcard && /^\*/.test(r.name)) {
268 rr = {};
269 for (k in r) {
270 rr[k] = r[k];
271 }
272 rr.name = self.name;
273 } else {
274 rr = r;
275 }
276
277 self.results.push(rr);
278 });
279 }
280
281 if (name)
282 this.store.get(this.zone, ensure_absolute(name), this.lookup.bind(this));
283 else
284 this.send();
285};