ssn/src/cdn/funcs/resolveDns.ts

105 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* --- DNS resolver for the CDN storing SWTORs patch files ---
|
| The SWTOR launcher first connects to cdn-patch.swtor.com,
| there the traffic is split onto Akamai and Level3, and
| distributed to local points of presence.
| The master files appear to be stored on 159.153.92.51, and
| all CDNs are synchronized with that source.
|
| > cdn-patch.swtor.com
| -> gslb-patch.swtor.biowareonline.net
| -> cdn-patch.swtor.com.edgesuite.net (Akamai)
| | -> a56.d.akamai.net
| | -> 2.21.74.90
| | -> 2.21.74.98
| -> cdn-patch.swtor.com.c.footprint.net (Level3)
| -> eu.lvlt.cdn.ea.com.c.footprint.net (Europe)
| | -> 8.12.207.125
| | -> 8.253.37.126
| -> na.lvlt.cdn.ea.com.c.footprint.net (US)
| -> 205.128.74.252
| -> 4.23.36.253
| -> 192.221.105.254
|
| Examples:
| - http://cdn-patch.swtor.com/patch/assets_swtor_main/assets_swtor_main_-1to0.solidpkg
| - http://159.153.92.51/patch/assets_swtor_main/assets_swtor_main_-1to0.solidpkg
*/
import { exec} from 'child_process';
import * as dns from 'dns';
import { IDnsResult } from '../../interfaces/IDnsResult';
//TODO: send e-mail with the error
const assert = (condition: boolean) => { if (!condition) { console.warn('Assert failed'); } };
/** Looks up the given domain and returns a list of IP addresses, along with their time-to-live */
async function resolveDns(domain: string): Promise<IDnsResult[]> {
return new Promise((resolve) => {
//check given string for correctness to prevent injection attacks
if (!domain.match(/^[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,3}$/)) { return resolve([]); }
//Check Level3/North_America separately
if (domain !== 'cdn-patch.swtor.com') {
dns.resolve4(domain, { ttl: true }, (err, result) => {
return resolve(result.map(({ address, ttl }) => ({ address, ttl, type: 'level3-us' as IDnsResult['type'] })));
});
} else {
//Use bash so we get more information.
//Also do plenty of asserts to ensure that overall CDN structure has stayed unchanged, and TODO send e-mail if it's different (max. once per hour)
exec('dig +noall +answer "cdn-patch.swtor.com"', { timeout: 10000 }, (error, stdout) => {
//check for error
assert(!error);
if (error) {
return resolve([]);
}
const data = stdout.trim().split('\n').map((line) => line.split(/\t| /));
//Verify output
assert(data.length > 3);
data.forEach((dataLine) => assert(dataLine.length === 5));
assert(data[0][0] === 'cdn-patch.swtor.com.');
assert(data[0][1].match(/^[0-9]{1,3}$/) !== null); //at least up to 598
assert(data[0][2] === 'IN');
assert(data[0][3] === 'CNAME');
assert(data[0][4] === 'gslb-patch.swtor.biowareonline.net.');
assert(data[1][0] === 'gslb-patch.swtor.biowareonline.net.');
assert(data[1][1].match(/^[0-9]{1,2}$/) !== null); //up to 60 seconds
assert(data[1][2] === 'IN');
assert(data[1][3] === 'CNAME');
assert(data[1][4] === 'cdn-patch.swtor.com.edgesuite.net.' || data[1][4] === 'cdn-patch.swtor.com.c.footprint.net.');
assert(data[2][0] === data[1][4]);
assert(data[2][1].match(/^[0-9]{1,5}$/) !== null); //at least up to 15092 if Akamai, at least up to 627 if Level3
assert(data[2][2] === 'IN');
assert(data[2][3] === 'CNAME');
assert(
(data[2][4] === 'a56.d.akamai.net.' && data[1][4] === 'cdn-patch.swtor.com.edgesuite.net.') ||
(data[2][4] === 'eu.lvlt.cdn.ea.com.c.footprint.net.' && data[1][4] === 'cdn-patch.swtor.com.c.footprint.net.'),
);
for (let i = 3, il = data.length; i < il; i += 1) {
assert(data[i][0] === data[2][4]);
assert(data[i][1].match(/^[0-9]{1,3}$/) !== null); //up to 60 seconds if Akamai, at least up to 218 if Level3
assert(data[i][2] === 'IN');
assert(data[i][3] === 'A');
assert(data[i][4].match(/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) !== null);
}
//Prepare return values
let type: IDnsResult['type'];
switch (data[1][4]) {
case 'cdn-patch.swtor.com.edgesuite.net.': type = 'akamai'; break;
case 'cdn-patch.swtor.com.c.footprint.net.': type = 'level3-eu'; break;
default: type = 'unknown';
}
resolve(data.filter((e, index) => index >= 3).map(([, ttl, , , address]) => ({ ttl: Math.min(Number(ttl), Number(data[0][1]), Number(data[1][1]), Number(data[2][1])), address, type })));
});
}
}) as Promise<IDnsResult[]>;
}
export default resolveDns;