UNPKG

7.34 kBJavaScriptView Raw
1// Copyright 2011 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
21'use strict';
22
23var fs = require('fs'),
24 EventEmitter = require('events').EventEmitter,
25 net = require('net'),
26 os = require('os'),
27 util = require('util'),
28 Cache = require('./cache'),
29 consts = require('./consts'),
30 path = require('path'),
31 utils = require('./utils');
32
33var A = consts.NAME_TO_QTYPE.A,
34 AAAA = consts.NAME_TO_QTYPE.AAAA,
35 PTR = consts.NAME_TO_QTYPE.PTR;
36
37var Platform = function() {
38 this._nsReady = false;
39 this._hostsReady = false;
40
41 Object.defineProperty(this, 'ready', {
42 get: function() {
43 return this._nsReady && this._hostsReady;
44 }
45 });
46
47 this._watches = {};
48
49 Object.defineProperty(this, 'watching', {
50 get: function() {
51 return Object.keys(this._watches).length > 0;
52 },
53 set: function(value) {
54 if (value)
55 this._watchFiles();
56 else {
57 for (k in this._watches) {
58 this._watches[k].close();
59 delete this._watches[k];
60 }
61 }
62 }
63 });
64
65 this.hosts = new Cache();
66
67 this._initNameServers();
68 this._initHostsFile();
69 this._populate();
70
71 this.cache = false; //new Cache();
72};
73util.inherits(Platform, EventEmitter);
74
75Platform.prototype.reload = function() {
76 this.emit('unready');
77 this._initNameServers();
78 this._initHostsFile();
79 this._populate();
80};
81
82Platform.prototype._initNameServers = function() {
83 this._nsReady = false;
84 this.name_servers = [];
85 this.search_path = [];
86 this.timeout = 5 * 1000;
87 this.attempts = 5;
88 this.edns = false;
89};
90
91Platform.prototype._initHostsFile = function() {
92 this._hostsReady = false;
93 this.hosts.purge();
94};
95
96Platform.prototype._populate = function() {
97 var hostsfile, self = this;
98
99 switch (os.platform()) {
100 case 'win32':
101 this.name_servers = [
102 {
103 address: '8.8.8.8',
104 port: 53
105 },
106 {
107 address: '8.8.4.4.',
108 port: 53
109 }
110 ];
111 hostsfile = path.join(process.env.SystemRoot,
112 '\\System32\\drivers\\etc\\hosts');
113 break;
114 default:
115 this.parseResolv();
116 hostsfile = '/etc/hosts';
117 break;
118 }
119
120 this._parseHosts(hostsfile);
121};
122
123Platform.prototype._watchFiles = function() {
124 var self = this, watchParams;
125
126 watchParams = {persistent: false};
127
128 switch (os.platform()) {
129 case 'win32':
130 //TODO XXX FIXME: it would be nice if this existed
131 break;
132 default:
133 this._watches.resolve = fs.watch('/etc/resolv.conf', watchParams,
134 function(event, filename) {
135 if (event === 'change') {
136 self.emit('unready');
137 self._initNameServers();
138 self.parseResolv();
139 }
140 });
141 this._watches.hosts = fs.watch('/etc/hosts', watchParams,
142 function(event, filename) {
143 if (event === 'change') {
144 self.emit('unready');
145 self._initHostsFile();
146 self._parseHosts(hostsfile);
147 }
148 });
149 break;
150 }
151};
152
153Platform.prototype._checkReady = function() {
154 if (this.ready) {
155 this.emit('ready');
156 }
157};
158
159Platform.prototype.parseResolv = function() {
160 var self = this;
161
162 fs.readFile('/etc/resolv.conf', 'ascii', function(err, file) {
163 if (err) {
164 throw err;
165 }
166
167 file.split(/\n/).forEach(function(line) {
168 var i, parts, subparts;
169 line = line.replace(/^\s+|\s+$/g, '');
170 if (!line.match(/^#/)) {
171 parts = line.split(/\s+/);
172 switch (parts[0]) {
173 case 'nameserver':
174 self.name_servers.push({
175 address: parts[1],
176 port: 53
177 });
178 break;
179 case 'domain':
180 self.search_path = [parts[1]];
181 break;
182 case 'search':
183 self.search_path = [parts.slice(1)];
184 break;
185 case 'options':
186 for (i = 1; i < parts.length; i++) {
187 subparts = parts[i].split(/:/);
188 switch (subparts[0]) {
189 case 'timeout':
190 self.timeout = parseInt(subparts[1], 10) * 1000;
191 break;
192 case 'attempts':
193 self.attempts = parseInt(subparts[1], 10);
194 break;
195 case 'edns0':
196 self.edns = true;
197 break;
198 }
199 }
200 break;
201 }
202 }
203 });
204
205 self._nsReady = true;
206 self._checkReady();
207 });
208};
209
210Platform.prototype._parseHosts = function(hostsfile) {
211 var self = this;
212
213 fs.readFile(hostsfile, 'ascii', function(err, file) {
214 var toStore = {};
215 if (err) {
216 throw err;
217 }
218
219 file.split(/\n/).forEach(function(line) {
220 var i, parts, ip, revip, kind;
221 line = line.replace(/^\s+|\s+$/g, '');
222 if (!line.match(/^#/)) {
223 parts = line.split(/\s+/);
224 ip = parts[0];
225 parts = parts.slice(1);
226 kind = net.isIP(ip);
227
228 if (parts.length && ip && kind) {
229 /* IP -> Domain */
230 revip = utils.reverseIP(ip);
231 parts.forEach(function(domain) {
232 var r = toStore[revip];
233 if (!r)
234 r = toStore[revip] = {};
235 var t = r[PTR];
236 if (!t)
237 t = r[PTR] = [];
238 t.push({
239 type: PTR,
240 class: 1,
241 name: revip,
242 data: domain,
243 ttl: Infinity,
244 });
245 });
246
247 /* Domain -> IP */
248 parts.forEach(function(domain) {
249 var r = toStore[domain.toLowerCase()];
250 if (!r) {
251 r = toStore[domain.toLowerCase()] = {};
252 }
253 var type = kind === 4 ? A : AAAA;
254 var t = r[type];
255 if (!t)
256 t = r[type] = [];
257 t.push({
258 type: type,
259 name: domain.toLowerCase(),
260 address: ip,
261 ttl: Infinity,
262 });
263 });
264 }
265 }
266 });
267
268 Object.keys(toStore).forEach(function (key) {
269 self.hosts._store.set(self.hosts._zone, utils.ensure_absolute(key), toStore[key]);
270 });
271 self._hostsReady = true;
272 self._checkReady();
273 });
274};
275
276module.exports = new Platform();