honeyweb-core 2.0.4 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/utils/dns-verify.js +109 -2
package/package.json
CHANGED
package/utils/dns-verify.js
CHANGED
|
@@ -2,10 +2,102 @@
|
|
|
2
2
|
|
|
3
3
|
const dns = require('dns').promises;
|
|
4
4
|
const LRUCache = require('./cache');
|
|
5
|
+
const https = require('https');
|
|
6
|
+
|
|
7
|
+
// Known bot IP ranges (fallback when DNS fails on Windows)
|
|
8
|
+
const BOT_IP_RANGES = {
|
|
9
|
+
'googlebot.com': null, // Will be fetched dynamically
|
|
10
|
+
'google.com': null,
|
|
11
|
+
'search.msn.com': null,
|
|
12
|
+
'slack.com': null,
|
|
13
|
+
'facebook.com': null,
|
|
14
|
+
'twitter.com': null,
|
|
15
|
+
'linkedin.com': null,
|
|
16
|
+
'apple.com': null,
|
|
17
|
+
'duckduckgo.com': null
|
|
18
|
+
};
|
|
5
19
|
|
|
6
20
|
class DNSVerifier {
|
|
7
21
|
constructor(cacheTTL = 86400000) {
|
|
8
22
|
this.cache = new LRUCache(500, cacheTTL);
|
|
23
|
+
this.ipRangesCache = null;
|
|
24
|
+
this.ipRangesCacheTime = 0;
|
|
25
|
+
this.ipRangesCacheTTL = 86400000; // 24 hours
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Fetch Googlebot IP ranges from Google's official API
|
|
29
|
+
async fetchGooglebotRanges() {
|
|
30
|
+
const now = Date.now();
|
|
31
|
+
if (this.ipRangesCache && (now - this.ipRangesCacheTime) < this.ipRangesCacheTTL) {
|
|
32
|
+
return this.ipRangesCache;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
https.get('https://developers.google.com/search/apis/ipranges/googlebot.json', (res) => {
|
|
37
|
+
let data = '';
|
|
38
|
+
res.on('data', chunk => data += chunk);
|
|
39
|
+
res.on('end', () => {
|
|
40
|
+
try {
|
|
41
|
+
const json = JSON.parse(data);
|
|
42
|
+
const ranges = json.prefixes
|
|
43
|
+
.filter(p => p.ipv4Prefix)
|
|
44
|
+
.map(p => p.ipv4Prefix);
|
|
45
|
+
this.ipRangesCache = ranges;
|
|
46
|
+
this.ipRangesCacheTime = now;
|
|
47
|
+
resolve(ranges);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
reject(err);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}).on('error', reject);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Check if IP is in CIDR range
|
|
57
|
+
ipInRange(ip, cidr) {
|
|
58
|
+
const [range, bits] = cidr.split('/');
|
|
59
|
+
const mask = ~(2 ** (32 - parseInt(bits)) - 1);
|
|
60
|
+
|
|
61
|
+
const ipNum = ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet), 0) >>> 0;
|
|
62
|
+
const rangeNum = range.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet), 0) >>> 0;
|
|
63
|
+
|
|
64
|
+
return (ipNum & mask) === (rangeNum & mask);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Fallback verification using IP ranges
|
|
68
|
+
async verifyByIpRange(ip, expectedDomain) {
|
|
69
|
+
// Only support Googlebot for now
|
|
70
|
+
if (!expectedDomain.includes('google')) {
|
|
71
|
+
return { verified: false, hostname: null, error: 'IP range verification only available for Google' };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const ranges = await this.fetchGooglebotRanges();
|
|
76
|
+
const inRange = ranges.some(cidr => this.ipInRange(ip, cidr));
|
|
77
|
+
|
|
78
|
+
if (inRange) {
|
|
79
|
+
return {
|
|
80
|
+
verified: true,
|
|
81
|
+
hostname: 'googlebot.com (verified by IP range)',
|
|
82
|
+
error: null,
|
|
83
|
+
method: 'ip-range'
|
|
84
|
+
};
|
|
85
|
+
} else {
|
|
86
|
+
return {
|
|
87
|
+
verified: false,
|
|
88
|
+
hostname: null,
|
|
89
|
+
error: 'IP not in Googlebot ranges',
|
|
90
|
+
method: 'ip-range'
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
} catch (err) {
|
|
94
|
+
return {
|
|
95
|
+
verified: false,
|
|
96
|
+
hostname: null,
|
|
97
|
+
error: `IP range check failed: ${err.message}`,
|
|
98
|
+
method: 'ip-range'
|
|
99
|
+
};
|
|
100
|
+
}
|
|
9
101
|
}
|
|
10
102
|
|
|
11
103
|
async verify(ip, expectedDomain) {
|
|
@@ -13,10 +105,17 @@ class DNSVerifier {
|
|
|
13
105
|
if (this.cache.has(cacheKey)) return this.cache.get(cacheKey);
|
|
14
106
|
|
|
15
107
|
try {
|
|
16
|
-
//
|
|
108
|
+
// Try DNS verification first
|
|
17
109
|
const hostnames = await dns.reverse(ip);
|
|
18
110
|
|
|
19
111
|
if (!hostnames || hostnames.length === 0) {
|
|
112
|
+
// DNS failed - try IP range fallback for Google
|
|
113
|
+
if (expectedDomain.includes('google')) {
|
|
114
|
+
const rangeResult = await this.verifyByIpRange(ip, expectedDomain);
|
|
115
|
+
this.cache.set(cacheKey, rangeResult);
|
|
116
|
+
return rangeResult;
|
|
117
|
+
}
|
|
118
|
+
|
|
20
119
|
const result = { verified: false, hostname: null, error: 'No reverse DNS record found' };
|
|
21
120
|
this.cache.set(cacheKey, result);
|
|
22
121
|
return result;
|
|
@@ -39,11 +138,18 @@ class DNSVerifier {
|
|
|
39
138
|
return result;
|
|
40
139
|
}
|
|
41
140
|
|
|
42
|
-
const result = { verified: true, hostname, error: null };
|
|
141
|
+
const result = { verified: true, hostname, error: null, method: 'dns' };
|
|
43
142
|
this.cache.set(cacheKey, result);
|
|
44
143
|
return result;
|
|
45
144
|
|
|
46
145
|
} catch (err) {
|
|
146
|
+
// DNS lookup failed - try IP range fallback for Google
|
|
147
|
+
if (expectedDomain.includes('google')) {
|
|
148
|
+
const rangeResult = await this.verifyByIpRange(ip, expectedDomain);
|
|
149
|
+
this.cache.set(cacheKey, rangeResult, 300000);
|
|
150
|
+
return rangeResult;
|
|
151
|
+
}
|
|
152
|
+
|
|
47
153
|
const result = { verified: false, hostname: null, error: err.message };
|
|
48
154
|
this.cache.set(cacheKey, result, 300000);
|
|
49
155
|
return result;
|
|
@@ -52,6 +158,7 @@ class DNSVerifier {
|
|
|
52
158
|
|
|
53
159
|
clearCache() {
|
|
54
160
|
this.cache.clear();
|
|
161
|
+
this.ipRangesCache = null;
|
|
55
162
|
}
|
|
56
163
|
}
|
|
57
164
|
|