91 lines
13 KiB
JavaScript
91 lines
13 KiB
JavaScript
|
|
"use strict";
|
||
|
|
|
||
|
|
Object.defineProperty(exports, "__esModule", {
|
||
|
|
value: true
|
||
|
|
});
|
||
|
|
exports.instanceLookup = instanceLookup;
|
||
|
|
exports.parseBrowserResponse = parseBrowserResponse;
|
||
|
|
var _dns = _interopRequireDefault(require("dns"));
|
||
|
|
var _sender = require("./sender");
|
||
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
|
const SQL_SERVER_BROWSER_PORT = 1434;
|
||
|
|
const TIMEOUT = 2 * 1000;
|
||
|
|
const RETRIES = 3;
|
||
|
|
// There are three bytes at the start of the response, whose purpose is unknown.
|
||
|
|
const MYSTERY_HEADER_LENGTH = 3;
|
||
|
|
// Most of the functionality has been determined from from jTDS's MSSqlServerInfo class.
|
||
|
|
async function instanceLookup(options) {
|
||
|
|
const server = options.server;
|
||
|
|
if (typeof server !== 'string') {
|
||
|
|
throw new TypeError('Invalid arguments: "server" must be a string');
|
||
|
|
}
|
||
|
|
const instanceName = options.instanceName;
|
||
|
|
if (typeof instanceName !== 'string') {
|
||
|
|
throw new TypeError('Invalid arguments: "instanceName" must be a string');
|
||
|
|
}
|
||
|
|
const timeout = options.timeout === undefined ? TIMEOUT : options.timeout;
|
||
|
|
if (typeof timeout !== 'number') {
|
||
|
|
throw new TypeError('Invalid arguments: "timeout" must be a number');
|
||
|
|
}
|
||
|
|
const retries = options.retries === undefined ? RETRIES : options.retries;
|
||
|
|
if (typeof retries !== 'number') {
|
||
|
|
throw new TypeError('Invalid arguments: "retries" must be a number');
|
||
|
|
}
|
||
|
|
if (options.lookup !== undefined && typeof options.lookup !== 'function') {
|
||
|
|
throw new TypeError('Invalid arguments: "lookup" must be a function');
|
||
|
|
}
|
||
|
|
const lookup = options.lookup ?? _dns.default.lookup;
|
||
|
|
if (options.port !== undefined && typeof options.port !== 'number') {
|
||
|
|
throw new TypeError('Invalid arguments: "port" must be a number');
|
||
|
|
}
|
||
|
|
const port = options.port ?? SQL_SERVER_BROWSER_PORT;
|
||
|
|
const signal = options.signal;
|
||
|
|
signal.throwIfAborted();
|
||
|
|
let response;
|
||
|
|
const request = Buffer.from([0x02]);
|
||
|
|
for (let i = 0; i <= retries; i++) {
|
||
|
|
const timeoutSignal = AbortSignal.timeout(timeout);
|
||
|
|
try {
|
||
|
|
response = await (0, _sender.sendMessage)(options.server, port, lookup, AbortSignal.any([signal, timeoutSignal]), request);
|
||
|
|
} catch (err) {
|
||
|
|
// If the current attempt timed out, continue with the next
|
||
|
|
if (timeoutSignal.aborted) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
throw err;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (!response) {
|
||
|
|
throw new Error('Failed to get response from SQL Server Browser on ' + server);
|
||
|
|
}
|
||
|
|
const message = response.toString('ascii', MYSTERY_HEADER_LENGTH);
|
||
|
|
const foundPort = parseBrowserResponse(message, instanceName);
|
||
|
|
if (!foundPort) {
|
||
|
|
throw new Error('Port for ' + instanceName + ' not found in ' + options.server);
|
||
|
|
}
|
||
|
|
return foundPort;
|
||
|
|
}
|
||
|
|
function parseBrowserResponse(response, instanceName) {
|
||
|
|
let getPort;
|
||
|
|
const instances = response.split(';;');
|
||
|
|
for (let i = 0, len = instances.length; i < len; i++) {
|
||
|
|
const instance = instances[i];
|
||
|
|
const parts = instance.split(';');
|
||
|
|
for (let p = 0, partsLen = parts.length; p < partsLen; p += 2) {
|
||
|
|
const name = parts[p];
|
||
|
|
const value = parts[p + 1];
|
||
|
|
if (name === 'tcp' && getPort) {
|
||
|
|
const port = parseInt(value, 10);
|
||
|
|
return port;
|
||
|
|
}
|
||
|
|
if (name === 'InstanceName') {
|
||
|
|
if (value.toUpperCase() === instanceName.toUpperCase()) {
|
||
|
|
getPort = true;
|
||
|
|
} else {
|
||
|
|
getPort = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfZG5zIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfc2VuZGVyIiwib2JqIiwiX19lc01vZHVsZSIsImRlZmF1bHQiLCJTUUxfU0VSVkVSX0JST1dTRVJfUE9SVCIsIlRJTUVPVVQiLCJSRVRSSUVTIiwiTVlTVEVSWV9IRUFERVJfTEVOR1RIIiwiaW5zdGFuY2VMb29rdXAiLCJvcHRpb25zIiwic2VydmVyIiwiVHlwZUVycm9yIiwiaW5zdGFuY2VOYW1lIiwidGltZW91dCIsInVuZGVmaW5lZCIsInJldHJpZXMiLCJsb29rdXAiLCJkbnMiLCJwb3J0Iiwic2lnbmFsIiwidGhyb3dJZkFib3J0ZWQiLCJyZXNwb25zZSIsInJlcXVlc3QiLCJCdWZmZXIiLCJmcm9tIiwiaSIsInRpbWVvdXRTaWduYWwiLCJBYm9ydFNpZ25hbCIsInNlbmRNZXNzYWdlIiwiYW55IiwiZXJyIiwiYWJvcnRlZCIsIkVycm9yIiwibWVzc2FnZSIsInRvU3RyaW5nIiwiZm91bmRQb3J0IiwicGFyc2VCcm93c2VyUmVzcG9uc2UiLCJnZXRQb3J0IiwiaW5zdGFuY2VzIiwic3BsaXQiLCJsZW4iLCJsZW5ndGgiLCJpbnN0YW5jZSIsInBhcnRzIiwicCIsInBhcnRzTGVuIiwibmFtZSIsInZhbHVlIiwicGFyc2VJbnQiLCJ0b1VwcGVyQ2FzZSJdLCJzb3VyY2VzIjpbIi4uL3NyYy9pbnN0YW5jZS1sb29rdXAudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGRucyBmcm9tICdkbnMnO1xuXG5pbXBvcnQgeyBzZW5kTWVzc2FnZSB9IGZyb20gJy4vc2VuZGVyJztcblxuY29uc3QgU1FMX1NFUlZFUl9CUk9XU0VSX1BPUlQgPSAxNDM0O1xuY29uc3QgVElNRU9VVCA9IDIgKiAxMDAwO1xuY29uc3QgUkVUUklFUyA9IDM7XG4vLyBUaGVyZSBhcmUgdGhyZWUgYnl0ZXMgYXQgdGhlIHN0YXJ0IG9mIHRoZSByZXNwb25zZSwgd2hvc2UgcHVycG9zZSBpcyB1bmtub3duLlxuY29uc3QgTVlTVEVSWV9IRUFERVJfTEVOR1RIID0gMztcblxudHlwZSBMb29rdXBGdW5jdGlvbiA9IChob3N0bmFtZTogc3RyaW5nLCBvcHRpb25zOiBkbnMuTG9va3VwQWxsT3B0aW9ucywgY2FsbGJhY2s6IChlcnI6IE5vZGVKUy5FcnJub0V4Y2VwdGlvbiB8IG51bGwsIGFkZHJlc3NlczogZG5zLkxvb2t1cEFkZHJlc3NbXSkgPT4gdm9pZCkgPT4gdm9pZDtcblxuLy8gTW9zdCBvZiB0aGUgZnVuY3Rpb25hbGl0eSBoYXMgYmVlbiBkZXRlcm1pbmVkIGZyb20gZnJvbSBqVERTJ3MgTVNTcWxTZXJ2ZXJJbmZvIGNsYXNzLlxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGluc3RhbmNlTG9va3VwKG9wdGlvbnM6IHsgc2VydmVyOiBzdHJpbmcsIGluc3RhbmNlTmFtZTogc3RyaW5nLCB0aW1lb3V0PzogbnVtYmVyLCByZXRyaWVzPzogbnVtYmVyLCBwb3J0PzogbnVtYmVyLCBsb29rdXA/OiBMb29rdXBGdW5jdGlvbiwgc2lnbmFsOiBBYm9ydFNpZ25hbCB9KSB7XG4gIGNvbnN0IHNlcnZlciA9IG9wdGlvbnMuc2VydmVyO1xuICBpZiAodHlwZW9mIHNlcnZlciAhPT0gJ3N0cmluZycpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdJbnZhbGlkIGFyZ3VtZW50czogXCJzZXJ2ZXJcIiBtdXN0IGJlIGEgc3RyaW5nJyk7XG4gIH1cblxuICBjb25zdCBpbnN0YW5jZU5hbWUgPSBvcHRpb25zLmluc3RhbmNlTmFtZTtcbiAgaWYgKHR5cGVvZiBpbnN0YW5jZU5hbWUgIT09ICdzdHJpbmcnKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcignSW52YWxpZCBhcmd1bWVudHM6IFwiaW5zdGFuY2VOYW1lXCIgbXVzdCBiZSBhIHN0cmluZycpO1xuICB9XG5cbiAgY29uc3QgdGltZW91dCA9IG9wdGlvbnMudGltZW91dCA9PT0gdW5kZWZpbmVkID8gVElNRU9VVCA6IG9wdGlvbnMudGltZW91dDtcbiAgaWYgKHR5cGVvZiB0aW1lb3V0ICE9PSAnbnVtYmVyJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0ludmFsaWQgYXJndW1lbnRzOiBcInRpbWVvdXRcIiBtdXN0IGJlIGEgbnVtYmVyJyk7XG4gIH1cblxuICBjb25zdCByZXRyaWVzID0gb3B0aW9ucy5yZXRyaWVzID09PSB1bmRlZmluZWQgPyBSRVRSSUVTIDogb3B0aW9ucy5yZXRyaWVzO1xuICBpZiAodHlwZW9mIHJldHJpZXMgIT09ICdudW1iZXInKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcignSW52YWxpZCBhcmd1bWVudHM6IFwicmV0cmllc1wiIG11c3QgYmUgYSBudW1iZXInKTtcbiAgfVxuXG4gIGlmIChvcHRpb25zLmxvb2t1cCAhPT0gdW5kZWZpbmVkICYmIHR5cGVvZiBvcHRpb25zLmxvb2t1cCAhPT0gJ2Z1bmN0aW9uJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0ludmFsaWQgYXJndW1lbnRzOiBcImxvb2t1cFwiIG11c3QgYmUgYSBmdW5jdGlvbicpO1xuICB9XG4gIGNvbnN0IGxvb2t1cCA9IG9wdGlvbnMubG9va3VwID8/IGRucy5sb29rdXA7XG5cbiAgaWYgKG9wdGlvbnMucG9ydCAhPT0gdW5kZWZpbmVkICYmIHR5cGVvZiBvcHRpb25zLnBvcnQgIT09ICdudW1iZXInKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcignSW52YWxpZCBhcmd1bWVudHM6IFwicG9ydFwiIG11c3QgYmUgYSBudW1iZXInKTtcbiAgfVxuICBjb25zdCBwb3J0ID0gb3B0aW9ucy5wb3J0ID8/IFNRTF9TRVJWRVJfQlJPV1NFUl9QT1JUO1xuXG4gIGNvbnN0IHNpZ25hbCA9IG9wdGlvbnMuc2lnbmFsO1xuXG4gIHNpZ25hbC50aHJvd0lmQWJvcnRlZCgpO1xuXG4gIGxldCByZXNwb25zZTtcblxuICBjb25zdCByZXF1ZXN0ID0gQnVmZmVyLmZyb20oWzB4MDJdKTtcblxuICBmb3IgKGxldCBpID0gMDsgaSA8PSByZXRyaWVzOyBpKyspIHtcbiAgICBjb25zdCB0aW1lb3V0U2lnbmFsID0gQWJvcnRTaWduYWwudGltZW91dCh0aW1lb3V0KTtcblxuICAgIHRyeSB7XG4gICAgICByZXNwb25zZSA9IGF3YWl0IHNlbmRNZXNzYWdlKG9wdGlvbnMuc2VydmVyLCBwb3J0LCBsb29rdXAsIEFib3J0U2lnbmFsLmFueShbIHNpZ25hbCwgdGltZW91dFNpZ25hbCBdKSwgcmVxdWVzdCk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAvLyBJZiB0aGUgY3VycmVudCBhdHRlbXB0IHRpbWVkIG91dCwgY29udGludWUgd2l0aCB0aGUgbmV4dFxuICAgICAgaWYgKHRpbWVvdXRTaWduYWwuYWJvcnRlZCkge1x
|