| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183 |
4x
4x
4x
4x
4x
4x
4x
52x
52x
52x
17x
17x
4x
3x
4x
3x
1x
1x
3x
3x
3x
4x
4x
1x
3x
3x
3x
3x
1x
3x
4x
3x
3x
1x
2x
1x
1x
1x
1x
1x
1x
4x
1x
4x
5x
6x
5x
5x
1x
1x
4x
1x
1x
3x
3x
1x
1x
2x
2x
2x
2x
2x
1x
1x
1x
1x
1x
1x
50x
50x
50x
50x
| 'use strict';
const dns = require('dns');
const async = require('async');
const fs = require('fs');
const path = require('path');
const EOL = /\r?\n/g;
const dictionariesPath = path.join(__dirname, '../dictionary');
/**
* Resolve the domain ip address and nameservers
* @param {String} subdomain The subdomain to test
* @param {String} tld The hostname tld
* @param {Function} cb The callback to run once has done
*/
exports.probeDNS = (subdomain, tld, cb) => {
// Build the domain name
const domain = `${subdomain}.${tld}`;
// Run the resolve request
async.series({
address: next => {
dns.resolve(domain, 'A', next);
},
nameserver: next => {
dns.resolve(domain, 'NS', (err, res) => {
next(null, err ? null : res);
});
}
}, cb);
};
/**
* Return the default DNS servers
* @return {Array} An array of DNS used by the module
*/
exports.getDefaultResolvers = function () {
return fs.readFileSync(
path.join(__dirname, 'resolvers.txt')
).toString().trim().split(EOL);
};
// Check whether a dns server is valid.
exports.isValidDnsServer = function (dnsServer, timeout, cb) {
// Ensure arguments are good
if (typeof timeout === 'function') {
cb = timeout;
timeout = 4000;
}
// Set custom callback handler
let called = false;
let timeoutPromise = null;
let dnsCallback = err => {
clearTimeout(timeoutPromise);
if (called) {
return;
}
called = true;
cb(err);
};
// Force to use this dns server
dns.setServers([dnsServer]);
// Set a custom timeout for DNS request
timeoutPromise = setTimeout(() => {
dnsCallback(new Error('Request timeout exceeded!'));
}, timeout);
// Try to resolve google.com
dns.resolve4('www.google.com', dnsCallback);
};
/**
* Get the best resolver in the following order:
* 1. User supplied.
* 2. From our list.
* @param {String} server The DNS server address as string
* @param {function} callback The callback to run once has done
*/
exports.getResolvers = function (server, callback) {
// Results array
let dnsServers = exports.getDefaultResolvers();
// Return default dns servers
if (typeof server === 'undefined') {
callback(dnsServers);
// Return default dns servers
} else if (typeof server === 'function') {
callback = server;
callback(dnsServers);
// Validate custom DNS server than add to resolvers list
} else {
exports.isValidDnsServer(server, 4000, err => {
Eif (err === null && dnsServers.indexOf(server) === -1) {
dnsServers.unshift(server);
}
callback(dnsServers);
});
}
};
/**
* Get the dictionary files names
* @return {Array} Array of file names
*/
exports.getDictionaryNames = function () {
return fs.readdirSync(dictionariesPath);
};
// Send requests to a DNS resolver and find valid sub-domains
exports.getSubDomains = function (options, callback = () => {}) {
// Default subdomain scan options
let defaults = {
dictionary: 'top_50'
};
// Clean undefined options
Object.keys(options).forEach(key => options[key] === undefined && delete options[key]);
// Extend default options with user defined ones
options = Object.assign({}, defaults, options);
// Exit if no host option
if (!options.host) {
callback(new Error('The host property is missing.'));
return;
}
// Optionally run a bing search
if (options.bingSearch === true) {
var bingSearch = require('./bingSearch.js');
return bingSearch.find(options.host, callback);
}
// Build dictionary file path and test for existence
let dictionaryPath = path.join(dictionariesPath, `${options.dictionary}.txt`);
if (!fs.existsSync(dictionaryPath)) {
callback(new Error(`The dictionary file ${options.dictionary} does not exist.`));
return;
}
async.waterfall([
cb => {
let wildcard = Math.floor(Math.random() * 1e14) + 1e15;
exports.probeDNS(wildcard, options.host, err => {
cb(null, err ? null : true);
});
},
(wildcard, cb) => {
// If wildcard is enabled return it and stop
if (wildcard) {
cb(null, ['*']);
return;
}
// Get the resolvers list
exports.getResolvers(options.dnsServer, servers => {
// Set new servers list for the requests
dns.setServers(servers);
// Get dictionary content and split lines in array rows
let dictionary = fs.readFileSync(dictionaryPath).toString().trim().split(EOL);
// Probe each subdomain
async.mapSeries(dictionary, (subdomain, next) => {
let hostname = `${subdomain}.${options.host}`;
exports.probeDNS(subdomain, options.host, (err, res) => {
next(null, err ? null : Object.assign(res, {hostname}));
});
}, (err, results) => {
// Clear from empty results
cb(err, results.filter(v => v));
});
});
}
], callback);
};
|