Start working on CDN detection

This commit is contained in:
Gaël Métais
2021-03-07 09:41:22 +00:00
parent 0a1b9ce27a
commit f423c5dc4e
4 changed files with 19629 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,150 @@
var debug = require('debug')('ylt:cdnDetector');
var Q = require('q');
var url = require('url');
var dns = require('dns');
var cdnDetector = require('cdn-detector');
// This module checks my multiple ways if a request was served by a CDN
var CDNDetector = function() {
function detectCDN(entry) {
var urlObject = url.parse(entry.url);
debug('Start checking CDN for %s, host is %s', entry.url, urlObject.host);
// 1st, the fastest check: the domain is directly recognized as a CDN
return checkDomainDirectly(entry)
.fail(function() {
// 2nd, check the response headers
return checkHeaders(entry)
.fail(function() {
// 3rd, check the Name Servers
return checkNameServers(entry)
.fail(function(entry) {
// None of the above worked, we didn't find any trace of a CDN
return entry;
});
})
});
}
return {
detectCDN: detectCDN
};
// Compares the URL's domain to a list of know CDN, just in case
function checkDomainDirectly(entry) {
var deferred = Q.defer();
var result = checkHostname(url.parse(entry.url).host);
if (result === null) {
deferred.reject(entry);
} else {
entry.weightCheck.cdn = result;
deferred.resolve(entry);
}
return deferred.promise;
}
// Compares a list of HTTP response headers to a matching list
// For example, if there is an X-Akamai-Request-Id header, we know it is Akamai
function checkHeaders(entry) {
var deferred = Q.defer();
var result = cdnDetector.detectFromHeaders(entry.weightCheck.headers);
debug('Headers of %s were compared to the list. Result: %s', entry.url, JSON.stringify(result));
if (result === null) {
deferred.reject(entry);
} else {
entry.weightCheck.cdn = result.cdn;
deferred.resolve(entry);
}
return deferred.promise;
}
function checkNameServers(entry) {
var deferred = Q.defer();
var hostname = url.parse(entry.url).host;
// Send a request to get the Name Servers list
// https://nodejs.org/api/dns.html#dns_dns_resolvens_hostname_callback
dns.resolveNs(hostname, function(error, nameServers) {
if (error) {
debug(error);
debug('Could not find Name Servers for %s', hostname);
deferred.reject(entry);
} else {
debug('Name Servers for %s are ["%s"]', hostname, nameServers.join('", "'));
var cdn = nameServers.map(checkHostname).find(function(entry) {
return entry !== null;
});
if (cdn) {
entry.weightCheck.cdn = cdn;
deferred.resolve(entry);
} else {
deferred.reject(entry);
}
}
});
return deferred.promise;
}
// Compares the hostname to a matching list of hostnames
function checkHostname(hostname) {
var result = cdnDetector.detectFromHostname(hostname);
debug('Hostname %s was compared to the list. Result: %s', hostname, JSON.stringify(result));
return result ? result.cdn : null;
}
// Find an IP address with a DNS lookup
function getIP(hostname) {
var deferred = Q.defer();
// https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback
dns.lookup(hostname, {family: 4}, function(error, address) {
if (error) {
debug('Could not find IP address for %s', hostname);
} else {
debug('The IP address for %s is %s', hostname, address);
}
deferred.resolve(error ? null : address);
});
return deferred.promise;
}
// Compares the IP address agains a list of IP ranges
function checkIPAddress(address) {
var deferred = Q.defer();
var result = cdnDetector.detectFromHeaders(headers);
debug('Headers were compared to the list. Result: %s', JSON.stringify(result));
if (result === null) {
deferred.reject();
} else {
deferred.resolve(result.cdn);
}
return deferred.promise;
}
function saveCDN(cdnName) {
}
};
module.exports = new CDNDetector();
@@ -0,0 +1,71 @@
var http = require('https');
var zlib = require('zlib');
var fs = require('fs');
var CDN_ASN_LIST = {
13335: 'Cloudflare',
15133: 'Verizon',
16625: 'Akamai',
20446: 'StackPath',
20940: 'Akamai',
22822: 'Limelight',
54113: 'Fastly'
};
// Downloads a file and ungzip ip directly
function getGzipped(url, callback) {
var buffer = [];
http.get(url, function(res) {
var gunzip = zlib.createGunzip();
res.pipe(gunzip);
gunzip.on('data', function(data) {
buffer.push(data.toString())
}).on('end', function() {
callback(null, buffer.join(''));
}).on('error', function(e) {
callback(e);
})
}).on('error', function(e) {
callback(e)
});
}
// File provided here: https://iptoasn.com/
console.log('Start downloading the file...');
getGzipped('https://iptoasn.com/data/ip2asn-v4.tsv.gz', function(err, data) {
if (err) {
console.log(err);
return;
}
console.log('Download complete, let\'s start parsing...');
parseFile(data);
});
// Parse the CSV formated file and grag only the interesting IP ranges
function parseFile(data) {
var results = [];
var allTextLines = data.split(/\r\n|\n/);
for (var i = 0; i < allTextLines.length; i++) {
var lineData = allTextLines[i].split('\t');
var cdnName = CDN_ASN_LIST[lineData[2]];
if (cdnName) {
// We found an ASN number from our list. Let's save it!
results.push({
rangeStart: lineData[0],
rangeEnd: lineData[1],
cdn: cdnName
});
}
}
console.log('%d IP ranges found', results.length);
var outputFile = __dirname + '/cdn-ip-list.json';
fs.writeFileSync(outputFile, JSON.stringify(results, null, 2));
console.log('File saved here: %s', outputFile);
}
+1
View File
@@ -29,6 +29,7 @@
"angular-sanitize": "1.7.7",
"async": "2.6.1",
"body-parser": "1.18.3",
"cdn-detector": "0.1.6",
"chart.js": "2.7.3",
"clean-css": "4.2.1",
"color-diff": "1.1.0",