socket-function 1.2.37 → 1.2.38
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/src/forwardPort.ts +30 -20
package/package.json
CHANGED
package/src/forwardPort.ts
CHANGED
|
@@ -89,10 +89,11 @@ export async function listPortMappings(): Promise<PortMapping[]> {
|
|
|
89
89
|
// live IGD; see test.ts.)
|
|
90
90
|
const PERMANENT_LEASE = 0;
|
|
91
91
|
|
|
92
|
-
// A permanent mapping never expires, so we don't renew. We only watch, on this interval, for
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
|
|
92
|
+
// A permanent mapping never expires, so we don't renew. We only watch, on this interval, for our
|
|
93
|
+
// forward having disappeared (routers do drop them, e.g. on reboot) or another application having
|
|
94
|
+
// taken it. A mapping that is simply gone is free, so we re-take it; one that someone else holds we
|
|
95
|
+
// leave alone and only log — fighting over it would just be a takeover war neither side wins.
|
|
96
|
+
const MAPPING_CHECK_INTERVAL = timeInMinute * 30;
|
|
96
97
|
|
|
97
98
|
/** Outcome of forwardPort. `owned` is true once we hold the router mapping for the port. When
|
|
98
99
|
* false, `reason` says why: "declined" = noPortStealing and another host holds the port (the
|
|
@@ -137,7 +138,9 @@ export async function forwardPort(config: {
|
|
|
137
138
|
return (await listPortMappings()).find(m => m.externalPort === externalPort && m.protocol.toUpperCase() === "TCP");
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
|
|
141
|
+
// allowSteal is false for the periodic re-take, so a mapping that changed hands between our check
|
|
142
|
+
// and this call is still left alone.
|
|
143
|
+
async function takeOwnership(options: { allowSteal: boolean }): Promise<ForwardPortResult> {
|
|
141
144
|
try {
|
|
142
145
|
const { internalIP, gatewayIP, controlPort, controlURLs } = await resolveGateway();
|
|
143
146
|
|
|
@@ -145,8 +148,8 @@ export async function forwardPort(config: {
|
|
|
145
148
|
// whether a mapping exists at all (so we only delete when there's something to delete).
|
|
146
149
|
let existing = await readMapping();
|
|
147
150
|
|
|
148
|
-
if (existing && existing.internalClient !== internalIP &&
|
|
149
|
-
console.log(`Port ${externalPort} is already forwarded to ${existing.internalClient} (not us); not stealing it
|
|
151
|
+
if (existing && existing.internalClient !== internalIP && !options.allowSteal) {
|
|
152
|
+
console.log(`Port ${externalPort} is already forwarded to ${existing.internalClient} (not us); not stealing it.`);
|
|
150
153
|
return { owned: false, reason: "declined" };
|
|
151
154
|
}
|
|
152
155
|
|
|
@@ -195,32 +198,39 @@ export async function forwardPort(config: {
|
|
|
195
198
|
return { owned: false, reason: "error" };
|
|
196
199
|
}
|
|
197
200
|
|
|
198
|
-
// Re-read the mapping and
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
+
// Re-read the mapping and act on having lost it. Compares against our CURRENT LAN IP, so a mapping
|
|
202
|
+
// left over from an IP change of ours reads as someone else's — which is the safe way around, as
|
|
203
|
+
// we can't tell it apart from a genuine takeover and would rather not steal.
|
|
204
|
+
async function checkMapping() {
|
|
201
205
|
try {
|
|
202
206
|
let currentIP = await getLocalInternalIP();
|
|
203
|
-
let
|
|
204
|
-
if (!
|
|
205
|
-
console.error(`Port forward ${externalPort} is gone — the mapping was removed (router reboot, or another host deleted it).`);
|
|
206
|
-
|
|
207
|
-
|
|
207
|
+
let mapping = await readMapping();
|
|
208
|
+
if (!mapping) {
|
|
209
|
+
console.error(`Port forward ${externalPort} is gone — the mapping was removed (router reboot, or another host deleted it). Nothing holds the port, so re-taking it.`);
|
|
210
|
+
let retaken = await takeOwnership({ allowSteal: false });
|
|
211
|
+
if (!retaken.owned) {
|
|
212
|
+
console.error(`Failed to re-take port forward ${externalPort} (${retaken.reason}).`);
|
|
213
|
+
}
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (currentIP && mapping.internalClient !== currentIP) {
|
|
217
|
+
console.error(`Port forward ${externalPort} was taken — it now forwards to ${mapping.internalClient} instead of us (${currentIP}). Leaving it alone rather than fighting for it.`);
|
|
208
218
|
}
|
|
209
219
|
} catch (e) {
|
|
210
|
-
console.error(`Failed to check port forward ${externalPort}
|
|
220
|
+
console.error(`Failed to check port forward ${externalPort}`, (e as Error).stack ?? e);
|
|
211
221
|
}
|
|
212
222
|
}
|
|
213
223
|
|
|
214
|
-
let result = await takeOwnership();
|
|
224
|
+
let result = await takeOwnership({ allowSteal: !config.noPortStealing });
|
|
215
225
|
|
|
216
226
|
// Only monitor permanent mappings we actually own. A finite lease is expected to expire, so its
|
|
217
|
-
// disappearance isn't a
|
|
227
|
+
// disappearance isn't a loss worth reporting or reclaiming; a declined/failed claim owns nothing.
|
|
218
228
|
if (result.owned && permanent) {
|
|
219
229
|
// unref so a script that only wants a one-off forward (and then exits) isn't held open by
|
|
220
230
|
// the monitor timer; a running server stays alive on its own and the checks keep firing.
|
|
221
231
|
let timer = setInterval(() => {
|
|
222
|
-
void
|
|
223
|
-
},
|
|
232
|
+
void checkMapping();
|
|
233
|
+
}, MAPPING_CHECK_INTERVAL);
|
|
224
234
|
timer.unref?.();
|
|
225
235
|
}
|
|
226
236
|
|