-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathindex.js
195 lines (152 loc) · 5.07 KB
/
index.js
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
184
185
186
187
188
189
190
191
192
193
194
195
const { chunkArray } = require("./util");
const whoisServers = require("./servers.json");
const CRLF = "\r\n";
const LINE_BREAK = "\n";
const assert = require("assert");
const net = require("net");
class WhoisLight {
/**
* Gets the whois server associated with a domain name
*/
static _nameToServer(name) {
if (typeof name !== "string") {
return null
}
const majorTldPosition = name.lastIndexOf('.');
if (majorTldPosition === -1) {
return null;
}
const majorTld = name.substring(majorTldPosition + 1);
if (typeof whoisServers[majorTld] === 'undefined') {
return null;
}
const whoisServer = whoisServers[majorTld].find(
(whoisServer) => name.substring(name.length - whoisServer.tld.length) === whoisServer.tld
);
return whoisServer
}
/**
* Handles the whois results returned by a whois server. Parses them. Makes
* them feel pretty.
*/
static _formatResults(res) {
let ret = {};
const lines = res.split(LINE_BREAK);
for (const line of lines) {
if (!line) {
continue;
}
if (line.startsWith(">>>") && line.endsWith("<<<")) {
break;
}
const keyEnd = line.indexOf(":");
if (keyEnd < 0) {
continue;
}
const key = line.substring(0, keyEnd).trim();
const value = line.substring(keyEnd + 1).trim();
if (!key || !value) {
continue;
}
ret[key] = value;
}
// want it to be last thing
ret["_raw"] = res;
return ret;
}
/**
* Lookups whois information on a particular domain name
*/
static lookup(options, name) {
if (typeof options === "string") {
// Options was not passed. So we will assume first parameter is name.
name = options;
}
// Options validation
options = typeof options === "string" ? {} : options;
options.timeout =
typeof options.timeout === "undefined" ? 5000 : options.timeout;
options.port = typeof options.port === "undefined" ? 43 : options.port;
options.format =
typeof options.format === "undefined" ? false : options.format;
// Getting whois server information.
const whoisServer = WhoisLight._nameToServer(name);
if (!whoisServer) {
return Promise.reject(new Error("Failed to lookup"));
}
const domainQuery =
whoisServer.query === null
? name
: whoisServer.query.replace("{name}", name);
// Returning promise that does the whois network query
return new Promise((resolve, reject) => {
let buffer = "";
// Create socket. Send initial query for whois record.
const socket = net.createConnection(
options.port,
whoisServer.server,
() => {
// Send server query when connection is initiated
socket.write(domainQuery + CRLF);
}
);
// Set the socket encoding.
socket.setEncoding("utf8");
socket.setTimeout(options.timeout);
// Received data event
socket.on("data", (data) => {
// As per specification, we send query and receive whois information.
// This should do nothing but receive whois information.
buffer += data;
});
socket.on("end", () => {
// Specification says we have received all whois information when server
// ends connection. In theory, we should have all our whois information
// when this event occures.
// fortmatting results
socket.destroy();
resolve(options.format ? WhoisLight._formatResults(buffer) : buffer);
});
// Error handling.
socket.on("error", (err) => reject(err));
// Server timeout.... rate limit, error, who knows?
socket.on("timeout", () => {
reject(new Error("Server timeout"));
});
});
}
/**
* This will lookup bulk domain names in parallel, and return results once all
* have returned. We RECOMMEND implementing your own parallel execution if
* you are seeking absolute efficiency. But for simply tasks this function will
* suffice.
*/
static async bulkLookup(options, names) {
if (typeof names === "undefined") {
// Options was not passed. So we will assume first parameter is name.
names = options;
}
options = typeof options === "string" ? {} : options;
options.parellel =
typeof options.parellel === "undefined" ? 100 : options.parellel;
if (options.parellel > names.length) {
// trying to start more processors than we need
options.parellel = names.length;
}
// Where we store results. key is domain name, value is _formattedResults
let results = {};
let resultsCount = 0;
const namesChunks = chunkArray(names, options.parellel);
for (const namesChunk of namesChunks) {
const resolvedNamesPromises = namesChunk.map((name) =>
WhoisLight.lookup(options, name)
);
const resolvedNames = await Promise.all(resolvedNamesPromises);
for (const resolvedName of resolvedNames) {
results[names[resultsCount++]] = resolvedName;
}
}
return results;
}
}
module.exports = WhoisLight;