nat-upnp-rejetto
Version:
Port mapping via UPnP APIs
165 lines (164 loc) • 7.06 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Client = void 0;
const device_1 = __importDefault(require("./device"));
const ssdp_1 = __importDefault(require("./ssdp"));
class Client {
constructor(options = {}) {
this.ssdp = new ssdp_1.default();
this.timeout = options.timeout || 1800;
}
createMapping(options) {
return __awaiter(this, void 0, void 0, function* () {
return this.getGateway().then(({ gateway, address }) => {
var _a;
const ports = normalizeOptions(options);
return gateway.run("AddPortMapping", [
["NewRemoteHost", ports.remote.host + ""],
["NewExternalPort", ports.remote.port + ""],
[
"NewProtocol",
options.protocol ? options.protocol.toUpperCase() : "TCP",
],
["NewInternalPort", ports.internal.port + ""],
["NewInternalClient", ports.internal.host || address],
["NewEnabled", 1],
["NewPortMappingDescription", options.description || "node:nat:upnp"],
["NewLeaseDuration", (_a = options.ttl) !== null && _a !== void 0 ? _a : 60 * 30],
]);
});
});
}
removeMapping(options) {
return __awaiter(this, void 0, void 0, function* () {
return this.getGateway().then(({ gateway }) => {
const ports = normalizeOptions(options);
return gateway.run("DeletePortMapping", [
["NewRemoteHost", ports.remote.host + ""],
["NewExternalPort", ports.remote.port + ""],
[
"NewProtocol",
options.protocol ? options.protocol.toUpperCase() : "TCP",
],
]);
});
});
}
getMappings(options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const { gateway, address } = yield this.getGateway();
let i = 0;
const results = [];
while (true) {
const data = yield gateway.run("GetGenericPortMappingEntry", [["NewPortMappingIndex", i++]])
.catch(() => { });
if (!data)
break; // finished
const key = Object.keys(data).find((k) => k.startsWith('GetGenericPortMappingEntryResponse'));
if (!key) {
throw new Error("Incorrect response");
}
const res = data[key];
const result = {
public: {
host: res.NewRemoteHost || "",
port: Number(res.NewExternalPort),
},
private: {
host: res.NewInternalClient,
port: Number(res.NewInternalPort),
},
protocol: res.NewProtocol.toLowerCase(),
enabled: res.NewEnabled == 1,
description: res.NewPortMappingDescription,
ttl: Number(res.NewLeaseDuration),
// temporary, so typescript will compile
local: false,
};
result.local = result.private.host === address;
if (options.local && !result.local) {
continue;
}
if (options.description) {
if (typeof result.description !== "string")
continue;
if (options.description instanceof RegExp) {
if (!options.description.test(result.description))
continue;
}
else {
if (result.description.indexOf(options.description) === -1)
continue;
}
}
results.push(result);
}
return results;
});
}
getPublicIp() {
return __awaiter(this, void 0, void 0, function* () {
return this.getGateway().then(({ gateway, address }) => __awaiter(this, void 0, void 0, function* () {
var _a;
const data = yield gateway.run("GetExternalIPAddress", []);
const key = Object.keys(data || {}).find((k) => /^GetExternalIPAddressResponse$/.test(k));
if (!key)
throw new Error("Incorrect response");
return ((_a = data[key]) === null || _a === void 0 ? void 0 : _a.NewExternalIPAddress) + "";
}));
});
}
getGateway() {
return __awaiter(this, void 0, void 0, function* () {
let timeouted = false;
const p = this.ssdp.search("urn:schemas-upnp-org:device:InternetGatewayDevice:1");
return new Promise((s, r) => {
const timeout = setTimeout(() => {
timeouted = true;
p.emit("end");
r(new Error("Connection timed out while searching for the gateway."));
}, this.timeout);
p.on("device", (info, address) => {
if (timeouted)
return;
p.emit("end");
clearTimeout(timeout);
// Create gateway
s({ gateway: new device_1.default(info.location), address });
});
});
});
}
close() {
this.ssdp.close();
}
}
exports.Client = Client;
function normalizeOptions(options) {
function toObject(addr) {
if (typeof addr === "number")
return { port: addr };
if (typeof addr === "string" && !isNaN(addr))
return { port: Number(addr) };
if (typeof addr === "object")
return addr;
return {};
}
return {
remote: toObject(options.public),
internal: toObject(options.private),
};
}
exports.default = Client;
;