1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | var dgram = require('dgram'),
|
22 | EventEmitter = require('events').EventEmitter,
|
23 | ipaddr = require('ipaddr.js'),
|
24 | net = require('net'),
|
25 | util = require('util');
|
26 |
|
27 | var 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 | };
|
36 | util.inherits(UDPSocket, EventEmitter);
|
37 |
|
38 | UDPSocket.prototype.buffer = function(size) {
|
39 | this._buff = new Buffer(size);
|
40 | return this._buff;
|
41 | };
|
42 |
|
43 | UDPSocket.prototype.send = function(len) {
|
44 | this._socket.send(this._buff, 0, len, this._remote.port,
|
45 | this._remote.address);
|
46 | };
|
47 |
|
48 | UDPSocket.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 |
|
79 | UDPSocket.prototype.close = function() {
|
80 | this._socket.close();
|
81 | };
|
82 |
|
83 | UDPSocket.prototype.remote = function(remote) {
|
84 | return new UDPSocket(this._socket, remote);
|
85 | };
|
86 |
|
87 | var TCPSocket = exports.TCPSocket = function(socket) {
|
88 | UDPSocket.call(this, socket);
|
89 | this.base_size = 4096;
|
90 | this._rest = undefined;
|
91 | };
|
92 | util.inherits(TCPSocket, UDPSocket);
|
93 |
|
94 | TCPSocket.prototype.buffer = function(size) {
|
95 | this._buff = new Buffer(size + 2);
|
96 | return this._buff.slice(2);
|
97 | };
|
98 |
|
99 | TCPSocket.prototype.send = function(len) {
|
100 | this._buff.writeUInt16BE(len, 0);
|
101 | this._socket.write(this._buff.slice(0, len + 2));
|
102 | };
|
103 |
|
104 | TCPSocket.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 |
|
139 | TCPSocket.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 |
|
163 | TCPSocket.prototype.close = function() {
|
164 | this._socket.end();
|
165 | };
|
166 |
|
167 | TCPSocket.prototype.remote = function() {
|
168 | return this;
|
169 | };
|
170 |
|
171 | exports.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 |
|
201 | var is_absolute = exports.is_absolute = function (f) {
|
202 | return f && /\.$/.test(f);
|
203 | };
|
204 |
|
205 | var ensure_absolute = exports.ensure_absolute = function (f) {
|
206 | if (!is_absolute(f))
|
207 | return f += '.';
|
208 | return f;
|
209 | };
|
210 |
|
211 | var CNAME = require('./consts').NAME_TO_QTYPE.CNAME;
|
212 |
|
213 | var 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 |
|
229 | Lookup.prototype.send = function (err) {
|
230 | this.cb(err, this.results);
|
231 | };
|
232 |
|
233 | Lookup.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 |
|
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 | };
|