Passed
Push — master ( 4f74f9...6dc562 )
by Eduardo
03:10
created

whoiswrapper.js ➔ getDomainParameters   B

Complexity

Conditions 1

Size

Total Lines 44
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 39
dl 0
loc 44
c 0
b 0
f 0
rs 8.9439
cc 1
1
// jshint esversion: 8, -W069
2
3
const util = require('util'),
0 ignored issues
show
Unused Code introduced by
The constant util seems to be never used. Consider removing it.
Loading history...
4
  psl = require('psl'),
5
  puny = require('punycode'),
6
  uts46 = require('idna-uts46'),
7
  whois = require('whois'),
8
  parseRawData = require('./parseRawData'),
9
  debug = require('debug')('common.whoisWrapper'),
10
  {
11
    getDate
12
  } = require('./conversions'),
13
  settings = require('./settings').load();
14
15
16
/*
17
  lookupPromise
18
    Promisified whois lookup
19
 */
20
const lookupPromise = (...args) => {
21
  return new Promise((resolve, reject) => {
22
    whois.lookup(...args, (err, data) => {
23
      if (err) return reject(err);
24
      resolve(data);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
25
    });
26
  });
27
};
28
29
/*
30
  lookup
31
    Do a domain whois lookup
32
  parameters
33
    domain (string) - Domain name
34
    options (object) - Lookup options object, refer to 'defaultoptions' var or 'settings.lookup.general/server'
35
 */
36
async function lookup(domain, options = getWhoisOptions()) {
37
  var {
38
    'lookup.conversion': conversion,
39
    'lookup.general': general
40
  } = settings,
41
  domainResults;
42
43
  try {
44
    domain = conversion.enabled ? convertDomain(domain) : domain;
0 ignored issues
show
Bug introduced by
The variable conversion seems to be never declared. If this is a global, consider adding a /** global: conversion */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
45
    domain = general.psl ? psl.get(domain).replace(/((\*\.)*)/g, '') : domain;
0 ignored issues
show
Bug introduced by
The variable general seems to be never declared. If this is a global, consider adding a /** global: general */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
46
47
    debug("Looking up for {0}".format(domain));
48
    domainResults = await lookupPromise(domain, options);
0 ignored issues
show
Bug introduced by
The call to lookupPromise seems to have too many arguments starting with options.
Loading history...
49
  } catch (e) {
50
    domainResults = "Whois lookup error, {0}".format(e);
51
  }
52
53
  return domainResults;
54
}
55
56
/*
57
  toJSON
58
    Transform a given string to JSON object
59
  parameters
60
    resultsText (string) - whois domain reply string
61
 */
62
function toJSON(resultsText) {
63
  if (typeof resultsText === 'string' && resultsText.includes("lookup: timeout")) return "timeout";
64
65
  if (typeof resultsText === 'object') {
66
    //JSON.stringify(resultsText, null, 2);
67
    resultsText.map(function(data) {
68
      data.data = parseRawData(data.data);
69
      return data;
70
    });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
71
  } else {
72
    return parseRawData(preStringStrip(resultsText));
73
  }
74
}
75
76
/*
77
  isDomainAvailable
78
    Check domain whois reply for its avalability
79
  parameters
80
    resultsText (string) - Pure text whois reply
81
    resultsJSON (JSON Object) - JSON transformed whois reply
82
 */
83
function isDomainAvailable(resultsText, resultsJSON) {
84
  const {
85
    'lookup.assumptions': assumptions
86
  } = settings;
87
88
  resultsJSON = resultsJSON || 0;
89
90
  if (resultsJSON === 0) resultsJSON = toJSON(resultsText);
91
92
  var domainParams = getDomainParameters(null, null, null, resultsJSON, true);
93
  var controlDate = getDate(Date.now());
94
95
  switch (true) {
96
    /*
97
      Special cases
98
     */
99
    case (resultsText.includes('Uniregistry') && resultsText.includes('Query limit exceeded')):
100
      return (assumptions.uniregistry ? 'unavailable' : 'error:ratelimiting');
0 ignored issues
show
Bug introduced by
The variable assumptions seems to be never declared. If this is a global, consider adding a /** global: assumptions */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
101
102
      /*
103
        Available checks
104
       */
105
106
      // Not found cases & variants
107
      //case (resultsText.includes('ERROR:101: no entries found')):
108
109
110
      // No match cases & variants
111
    case (resultsText.includes('No match for domain')):
112
    case (resultsText.includes('- No Match')):
113
    case (resultsText.includes('NO MATCH:')):
114
    case (resultsText.includes('No match for')):
115
    case (resultsText.includes('No match')):
116
    case (resultsText.includes('No matching record.')):
117
    case (resultsText.includes('Nincs talalat')):
118
119
      // Status cases & variants
120
    case (resultsText.includes('Status: AVAILABLE')):
121
    case (resultsText.includes('Status:             AVAILABLE')):
122
    case (resultsText.includes('Status: 	available')):
123
    case (resultsText.includes('Status: free')):
124
    case (resultsText.includes('Status: Not Registered')):
125
    case (resultsText.includes('query_status: 220 Available')):
126
127
      // Unique cases
128
    case (domainParams.expiryDate - controlDate < 0):
129
    case (resultsText.includes('This domain name has not been registered')):
130
    case (resultsText.includes('The domain has not been registered')):
131
    case (resultsText.includes('This query returned 0 objects')):
132
    case (resultsText.includes(' is free') && domainParams.whoisreply.length < 50):
133
    case (resultsText.includes('domain name not known in')):
134
    case (resultsText.includes('registration status: available')):
135
    case (resultsText.includes('whois.nic.bo') && domainParams.whoisreply.length < 55):
136
    case (resultsText.includes('Object does not exist')):
137
    case (resultsText.includes('The queried object does not exist')):
138
    case (resultsText.includes('Not Registered -')):
139
    case (resultsText.includes('is available for registration')):
140
    case (resultsText.includes('is available for purchase')):
141
    case (resultsText.includes('DOMAIN IS NOT A REGISTERD')):
142
    case (resultsText.includes('No such domain')):
143
    case (resultsText.includes('No_Se_Encontro_El_Objeto')):
144
    case (resultsText.includes('Domain unknown')):
145
    case (resultsText.includes('No information available about domain name')):
146
    case (resultsText.includes('Error.') && resultsText.includes('SaudiNIC')):
147
    case (resultsText.includes('is not valid!')): // ???
148
      return 'available';
149
150
      /*
151
        Unavailable checks
152
       */
153
    case (resultsJSON.hasOwnProperty('domainName')): // Has domain name
154
    case (resultsText.includes('Domain Status:ok')): // Domain name is ok
155
    case (resultsText.includes('Expiration Date:')): // Has expiration date (1)
156
    case (resultsText.includes('Expiry Date:')): // Has Expiration date (2)
157
    case (resultsText.includes('Status: connect')): // Has connect status
158
    case (resultsText.includes('Changed:')): // Has a changed date
159
    case (Object.keys(resultsJSON).length > 5): // JSON has more than 5 keys (probably taken?)
160
    case (resultsText.includes('organisation: Internet Assigned Numbers Authority')): // Is controlled by IANA
161
      return 'unavailable';
162
163
      /*
164
        Error checks
165
       */
166
167
      // Error, null or no contents
168
    case (resultsText == null):
0 ignored issues
show
Best Practice introduced by
Comparing resultsText to null using the == operator is not safe. Consider using === instead.
Loading history...
169
    case (resultsText == ''):
170
      return 'error:nocontent';
171
172
      // Error, unauthorized
173
    case (resultsText.includes('You  are  not  authorized  to  access or query our Whois')):
174
      return 'error:unauthorized';
175
176
      // Error, rate limiting
177
    case (resultsText.includes('IP Address Has Reached Rate Limit')):
178
    case (resultsText.includes('Too many connection attempts')):
179
    case (resultsText.includes('Your request is being rate limited')):
180
    case (resultsText.includes('Your query is too often.')):
181
    case (resultsText.includes('Your connection limit exceeded.')):
182
      return (assumptions.ratelimit ? 'unavailable' : 'error:ratelimiting');
183
184
      // Error, unretrivable
185
    case (resultsText.includes('Could not retrieve Whois data')):
186
      return 'error:unretrivable';
187
188
      // Error, forbidden
189
    case (resultsText.includes('si is forbidden')): // .si is forbidden
190
    case (resultsText.includes('Requests of this client are not permitted')): // .ch forbidden
191
      return 'error:forbidden';
192
193
      // Error, reserved by regulator
194
    case (resultsText.includes('reserved by aeDA Regulator')): // Reserved for aeDA regulator
195
      return 'error:reservedbyregulator';
196
197
      // Error, unregistrable.
198
    case (resultsText.includes('third-level domains may not start with')):
199
      return 'error:unregistrable';
200
201
      // Error, reply error
202
    case (resultsJSON.hasOwnProperty('error')):
203
    case (resultsJSON.hasOwnProperty('errno')):
204
    case (resultsText.includes('error ')):
205
    case (resultsText.includes('error')): // includes plain error, may cause false negatives? i.e. error.com lookup
206
    case (resultsText.includes('Error')): // includes plain error, may cause false negatives? i.e. error.com lookup
207
    case (resultsText.includes('ERROR:101:')):
208
    case (resultsText.includes('Whois lookup error')):
209
    case (resultsText.includes('can temporarily not be answered')):
210
    case (resultsText.includes('Invalid input')):
211
      return 'error:replyerror';
212
213
      /*
214
         Error throw
215
           If every check fails throw Error, unparsable
216
        */
217
218
    default:
219
      return (assumptions.unparsable ? 'available' : 'error:unparsable');
220
  }
221
}
222
223
/*
224
  getDomainParameters
225
    Get streamlined domain results object
226
  parameters
227
    domain (string) - Domain name
228
    status (string) - isDomainAvailable result, is domain Available
229
    resultsText (string) - Pure text whois reply
230
    resultsJSON (JSON Object) - JSON transformed whois reply
231
    isAuxiliary (boolean) - Is auxiliary function to domain availability check, if used in "isDomainAvailable" fn
232
 */
233
function getDomainParameters(domain, status, resultsText, resultsJSON, isAuxiliary = false) {
234
  results = {};
0 ignored issues
show
Bug introduced by
The variable results seems to be never declared. Assigning variables without defining them first makes them global. If this was intended, consider making it explicit like using window.results.
Loading history...
235
236
  results.domain = domain;
237
  results.status = status;
238
  results.registrar = resultsJSON.registrar;
239
  results.company =
240
    resultsJSON.registrantOrganization ||
241
    resultsJSON.registrant ||
242
    resultsJSON.registrantOrganization ||
243
    resultsJSON.adminName ||
244
    resultsJSON.ownerName ||
245
    resultsJSON.contact ||
246
    resultsJSON.name;
247
  results.creationDate = getDate(
248
    resultsJSON.creationDate ||
249
    resultsJSON.createdDate ||
250
    resultsJSON.created ||
251
    resultsJSON.creationDate ||
252
    resultsJSON.registered ||
253
    resultsJSON.registeredOn);
254
  results.updateDate = getDate(
255
    resultsJSON.updatedDate ||
256
    resultsJSON.lastUpdated ||
257
    resultsJSON.UpdatedDate ||
258
    resultsJSON.changed ||
259
    resultsJSON.lastModified ||
260
    resultsJSON.lastUpdate);
261
  results.expiryDate = getDate(
262
    resultsJSON.expires ||
263
    resultsJSON.registryExpiryDate ||
264
    resultsJSON.expiryDate ||
265
    resultsJSON.registrarRegistrationExpirationDate ||
266
    resultsJSON.expire ||
267
    resultsJSON.expirationDate ||
268
    resultsJSON.expiresOn ||
269
    resultsJSON.paidTill);
270
  results.whoisReply = resultsText;
271
  results.whoisJson = resultsJSON;
272
273
  //debug(results);
274
275
  return results;
276
}
277
278
/*
279
  convertDomain
280
    Convert a given domain using a defined algorithm in appSettings
281
  parameters
282
    domain (string) - Domain to be converted
283
  modes
284
    punycode - Punycode
285
    uts46 - IDNA2008
286
    uts46-transitional - IDNA2003
287
    ascii - Filter out non-ASCII characters
288
    anything else - No conversion
289
 */
290
function convertDomain(domain, mode) {
291
  var {
292
    'lookup.conversion': conversion
293
  } = settings;
294
295
  mode = mode || conversion.algorithm;
0 ignored issues
show
Bug introduced by
The variable conversion seems to be never declared. If this is a global, consider adding a /** global: conversion */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
296
297
  switch (mode) {
298
    case 'punycode':
299
      return puny.encode(domain);
300
    case 'uts46':
301
      return uts46.toAscii(domain);
302
    case 'uts46-transitional':
303
      return uts46.toAscii(domain, {
304
        transitional: true
305
      });
306
    case 'ascii':
307
      return domain.replace(/[^\x00-\x7F]/g, "");
308
309
    default:
310
      return domain;
311
  }
312
}
313
314
/*
315
  getWhoisOptions
316
    Create whois options based on appSettings
317
 */
318
function getWhoisOptions() {
319
  const {
320
    'lookup.general': general
321
  } = settings;
322
323
  var options = {},
324
    follow = 'follow',
325
    timeout = 'timeout';
326
327
  options.server = general.server;
0 ignored issues
show
Bug introduced by
The variable general seems to be never declared. If this is a global, consider adding a /** global: general */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
328
  options.follow = getWhoisParameters(follow);
329
  options.timeout = getWhoisParameters(timeout);
330
  options.verbose = general.verbose;
331
332
  return options;
333
}
334
335
/*
336
  getWhoisParameters
337
    Get request follow level/depth
338
  parameters
339
    parameter (string) - Whois options parameter
340
      'follow' - Follow depth
341
      'timeout' - Timeout
342
      'timebetween' - Time between requests
343
 */
344
function getWhoisParameters(parameter) {
345
  const {
346
    'lookup.randomize.follow': follow,
347
    'lookup.randomize.timeout': timeout,
348
    'lookup.randomize.timeBetween': timeBetween,
349
    'lookup.general': general
350
  } = settings;
351
352
  console.log(timeout);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
Bug introduced by
The variable timeout seems to be never declared. If this is a global, consider adding a /** global: timeout */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
353
354
  switch (parameter) {
355
    case 'follow':
356
      debug("Follow depth, 'random': {0}, 'maximum': {1}, 'minimum': {2}, 'default': {3}".format(follow.randomize, follow.maximumDepth, follow.minimumDepth, general.follow));
0 ignored issues
show
Bug introduced by
The variable general seems to be never declared. If this is a global, consider adding a /** global: general */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Bug introduced by
The variable follow seems to be never declared. If this is a global, consider adding a /** global: follow */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
357
      return (follow.randomize ? getRandomInt(follow.minimumDepth, follow.maximumDepth) : general.follow);
358
359
    case 'timeout':
360
      debug("Timeout, 'random': {0}, 'maximum': {1}, 'minimum': {2}, 'default': {3}".format(timeout.randomize, timeout.maximum, timeout.minimum, general.timeout));
361
      return (timeout.randomize ? getRandomInt(timeout.minimum, timeout.maximum) : general.timeout);
362
363
    case 'timebetween':
364
      debug("Timebetween, 'random': {0}, 'maximum': {1}, 'minimum': {2}, 'default': {3}".format(randomize.timeBetween, timeBetween.maximum, timeBetween.minimum, general.timeBetween));
0 ignored issues
show
Bug introduced by
The variable timeBetween seems to be never declared. If this is a global, consider adding a /** global: timeBetween */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Bug introduced by
The variable randomize seems to be never declared. If this is a global, consider adding a /** global: randomize */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
365
      return (timeBetween.randomize ? getRandomInt(timeBetween.minimum, timeBetween.maximum) : general.timeBetween);
366
367
    default:
368
      return undefined;
369
370
  }
371
372
}
373
374
/*
375
  getRandomInt
376
    Get a random integer between two values
377
  parameters
378
    min (integer) - Minimum value
379
    max (integer) - Maximum value
380
 */
381
function getRandomInt(min, max) {
382
  return Math.floor((Math.random() * parseInt(max)) + parseInt(min));
383
}
384
385
/*
386
  preStringStrip
387
    Pre strip a given string, space key value pairs
388
  parameters
389
    str (string) - String to be stripped
390
 */
391
function preStringStrip(str) {
392
  return str.toString().replace(/\:\t{1,2}/g, ": "); // Space key value pairs
393
}
394
395
module.exports = {
396
  lookup: lookup,
397
  toJSON: toJSON,
398
  isDomainAvailable: isDomainAvailable,
399
  preStringStrip: preStringStrip,
400
  getDomainParameters: getDomainParameters
401
};
402