socket-function 0.23.0 → 0.25.0
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/SocketFunctionTypes.ts +1 -0
- package/package.json +1 -1
- package/src/callManager.ts +7 -7
- package/src/forwardPort.ts +65 -50
- package/src/networking.ts +1 -1
- package/src/webSocketServer.ts +5 -3
- package/time/trueTimeShim.ts +8 -3
package/SocketFunctionTypes.ts
CHANGED
|
@@ -38,6 +38,7 @@ export type SocketExposedShape<ExposedType extends SocketExposedInterface = Sock
|
|
|
38
38
|
hooks?: SocketFunctionHook<ExposedType>[];
|
|
39
39
|
clientHooks?: SocketFunctionClientHook<ExposedType>[];
|
|
40
40
|
noDefaultHooks?: boolean;
|
|
41
|
+
/** BUG: I think this is broken if it is on the default hooks function? */
|
|
41
42
|
noClientHooks?: boolean;
|
|
42
43
|
};
|
|
43
44
|
};
|
package/package.json
CHANGED
package/src/callManager.ts
CHANGED
|
@@ -122,13 +122,13 @@ export const runClientHooks = measureWrap(async function runClientHooks(
|
|
|
122
122
|
): Promise<ClientHookContext> {
|
|
123
123
|
let context: ClientHookContext = { call: callType, connectionId };
|
|
124
124
|
|
|
125
|
-
let clientHooks = (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
125
|
+
let clientHooks = hooks.clientHooks?.slice() || [];
|
|
126
|
+
if (!hooks.noClientHooks) {
|
|
127
|
+
clientHooks = globalClientHooks.concat(clientHooks);
|
|
128
|
+
for (let otherClientHook of globalHooks.concat(hooks.hooks || []).map(x => x.clientHook)) {
|
|
129
|
+
if (otherClientHook) {
|
|
130
|
+
clientHooks.push(otherClientHook);
|
|
131
|
+
}
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
for (let hook of clientHooks) {
|
package/src/forwardPort.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import debugbreak from "debugbreak";
|
|
2
2
|
import * as dgram from "dgram";
|
|
3
3
|
import os from "os";
|
|
4
|
+
import { timeInHour } from "./misc";
|
|
4
5
|
|
|
5
6
|
const SSDP_DISCOVER_MX = 2;
|
|
6
7
|
const SSDP_DISCOVER_MSG = `M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: "ssdp:discover"\r\nMX: ${SSDP_DISCOVER_MX}\r\nST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n\r\n`;
|
|
@@ -8,34 +9,41 @@ const SSDP_DISCOVER_MSG = `M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\n
|
|
|
8
9
|
export async function forwardPort(config: {
|
|
9
10
|
externalPort: number;
|
|
10
11
|
internalPort: number;
|
|
12
|
+
duration?: number;
|
|
11
13
|
}) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
14
|
+
try {
|
|
15
|
+
const { externalPort, internalPort } = config;
|
|
16
|
+
let duration = config.duration ?? timeInHour;
|
|
17
|
+
|
|
18
|
+
const localObj = getLocalInterfaceAddress();
|
|
19
|
+
if (!localObj) throw new Error("Could not find the local address / gateway");
|
|
20
|
+
|
|
21
|
+
const { internalIP, gatewayIP } = localObj;
|
|
22
|
+
console.log(`Local IP: ${internalIP}, Gateway IP: ${gatewayIP}`);
|
|
23
|
+
let gateway = await discoverGateway(internalIP);
|
|
24
|
+
let controlURLs = await getControlPaths(gateway);
|
|
25
|
+
let controlPort = Number(new URL(gateway).port);
|
|
26
|
+
|
|
27
|
+
for (let controlURL of controlURLs) {
|
|
28
|
+
try {
|
|
29
|
+
await createPortMapping({
|
|
30
|
+
externalPort, internalPort,
|
|
31
|
+
gatewayIP,
|
|
32
|
+
controlPort,
|
|
33
|
+
controlPath: controlURL,
|
|
34
|
+
internalIP,
|
|
35
|
+
duration,
|
|
36
|
+
});
|
|
37
|
+
console.log(`Port mapping created on ${gatewayIP}:${externalPort} -> ${internalIP}:${internalPort}`);
|
|
38
|
+
return;
|
|
39
|
+
} catch (e) {
|
|
40
|
+
console.error(`Failed to create port mapping using controlURL ${controlURL}`, e);
|
|
41
|
+
}
|
|
36
42
|
}
|
|
43
|
+
console.error("Failed to create port mapping, could not find controlURL");
|
|
44
|
+
} catch (e) {
|
|
45
|
+
console.error("Error in forwardPort", e);
|
|
37
46
|
}
|
|
38
|
-
console.error("Failed to create port mapping, could not find controlURL");
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
function getLocalInterfaceAddress(): { internalIP: string; gatewayIP: string; } | undefined {
|
|
@@ -61,34 +69,41 @@ function getLocalInterfaceAddress(): { internalIP: string; gatewayIP: string; }
|
|
|
61
69
|
}
|
|
62
70
|
}
|
|
63
71
|
} else {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
let gatewayMatch: RegExpMatchArray | undefined;
|
|
73
|
+
try {
|
|
74
|
+
// Attempt to get the gateway using "ip route" command (more universal)
|
|
75
|
+
const routeOutput = require("child_process")("ip route show default").toString();
|
|
76
|
+
gatewayMatch = routeOutput.match(/default via (\d+\.\d+\.\d+\.\d+)/);
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.error("Failed to execute 'ip route show default', trying fallback", err);
|
|
79
|
+
}
|
|
68
80
|
|
|
69
81
|
if (!gatewayMatch) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
82
|
+
try {
|
|
83
|
+
// Fallback to "netstat -rn" for older systems
|
|
84
|
+
const netstatOutput = require("child_process")("netstat -rn").toString();
|
|
85
|
+
gatewayMatch = netstatOutput.match(/default\s+(\d+\.\d+\.\d+\.\d+)/);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.error("Failed to execute 'netstat -rn', unable to find gateway", err);
|
|
88
|
+
}
|
|
73
89
|
}
|
|
74
90
|
|
|
75
|
-
if (gatewayMatch
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
let ipMatch;
|
|
81
|
-
if (os.platform() === "darwin") {
|
|
82
|
-
ipMatch = ipOutput.match(/inet ((?!127\.0\.0\.1)\d+\.\d+\.\d+\.\d+)/);
|
|
83
|
-
} else {
|
|
84
|
-
ipMatch = ipOutput.match(/inet ((?!127\.0\.0\.1)\d+\.\d+\.\d+\.\d+)\/\d+/);
|
|
85
|
-
}
|
|
91
|
+
if (gatewayMatch) {
|
|
92
|
+
try {
|
|
93
|
+
// Use "ip addr" to get internal IP (more universal)
|
|
94
|
+
const ipOutput = require("child_process")("ip addr").toString();
|
|
95
|
+
const ipMatch = ipOutput.match(/inet (?!127\.0\.0\.1)(\d+\.\d+\.\d+\.\d+)\//);
|
|
86
96
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
if (ipMatch) {
|
|
98
|
+
return {
|
|
99
|
+
internalIP: ipMatch[1],
|
|
100
|
+
gatewayIP: gatewayMatch[1]
|
|
101
|
+
};
|
|
102
|
+
} else {
|
|
103
|
+
console.error("Failed to match internal IP");
|
|
104
|
+
}
|
|
105
|
+
} catch (err) {
|
|
106
|
+
console.error("Failed to execute 'ip addr'", err);
|
|
92
107
|
}
|
|
93
108
|
}
|
|
94
109
|
}
|
|
@@ -160,7 +175,7 @@ async function createPortMapping(config: {
|
|
|
160
175
|
controlPort: number;
|
|
161
176
|
controlPath: string;
|
|
162
177
|
internalIP: string;
|
|
163
|
-
|
|
178
|
+
duration: number;
|
|
164
179
|
}): Promise<void> {
|
|
165
180
|
const { externalPort, internalPort, internalIP, controlPath, controlPort, gatewayIP } = config;
|
|
166
181
|
const action = "\"urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping\"";
|
|
@@ -177,7 +192,7 @@ async function createPortMapping(config: {
|
|
|
177
192
|
<NewInternalClient>${internalIP}</NewInternalClient>
|
|
178
193
|
<NewEnabled>1</NewEnabled>
|
|
179
194
|
<NewPortMappingDescription>My Port Mapping</NewPortMappingDescription>
|
|
180
|
-
<NewLeaseDuration
|
|
195
|
+
<NewLeaseDuration>${Math.ceil(config.duration / 1000)}</NewLeaseDuration>
|
|
181
196
|
</u:AddPortMapping>
|
|
182
197
|
</s:Body>
|
|
183
198
|
</s:Envelope>
|
package/src/networking.ts
CHANGED
|
@@ -33,7 +33,7 @@ const ipServers = [
|
|
|
33
33
|
export const getExternalIP = lazy(measureWrap(async function getExternalIP(): Promise<string> {
|
|
34
34
|
for (let server of ipServers) {
|
|
35
35
|
try {
|
|
36
|
-
return (await httpsRequest(server)).toString();
|
|
36
|
+
return (await httpsRequest(server, undefined, undefined, false)).toString();
|
|
37
37
|
} catch (e) {
|
|
38
38
|
console.warn(`Failed to get external ip from ${server}: ${e}`);
|
|
39
39
|
}
|
package/src/webSocketServer.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { parseSNIExtension, parseTLSHello, SNIType } from "./tlsParsing";
|
|
|
11
11
|
import debugbreak from "debugbreak";
|
|
12
12
|
import { getNodeId } from "./nodeCache";
|
|
13
13
|
import crypto from "crypto";
|
|
14
|
-
import { Watchable, timeInHour } from "./misc";
|
|
14
|
+
import { Watchable, timeInHour, timeInMinute } from "./misc";
|
|
15
15
|
import { delay, runInfinitePoll, runInfinitePollCallAtStart } from "./batching";
|
|
16
16
|
import { magenta, red } from "./formatting/logColors";
|
|
17
17
|
import { yellow } from "./formatting/logColors";
|
|
@@ -35,7 +35,8 @@ export type SocketServerConfig = (
|
|
|
35
35
|
/** Tries forwarding ports (using UPnP), if we detect they aren't externally reachable.
|
|
36
36
|
* - This causes an extra request and delay during startup, so should only be used
|
|
37
37
|
* during development.
|
|
38
|
-
* - Ignored if public is false
|
|
38
|
+
* - Ignored if public is false (in which case we mount on 127.0.0.1, so port forwarding
|
|
39
|
+
* wouldn't matter anyways).
|
|
39
40
|
*/
|
|
40
41
|
autoForwardPort?: boolean;
|
|
41
42
|
ip?: string;
|
|
@@ -180,6 +181,7 @@ export async function startSocketServer(
|
|
|
180
181
|
});
|
|
181
182
|
|
|
182
183
|
let realServer = net.createServer(socket => {
|
|
184
|
+
//console.log("Received TCP connection from " + socket.remoteAddress);
|
|
183
185
|
const remoteAddress = socket.remoteAddress;
|
|
184
186
|
function handleTLSHello(buffer: Buffer, packetCount: number): void | "more" {
|
|
185
187
|
// All HTTPS requests start with 22, and no HTTP requests start with 22,
|
|
@@ -296,7 +298,7 @@ export async function startSocketServer(
|
|
|
296
298
|
console.log(magenta(`Forwarded port ${port} to our machine`));
|
|
297
299
|
}
|
|
298
300
|
// Every hour, in case our network configuration changes
|
|
299
|
-
runInfinitePollCallAtStart(
|
|
301
|
+
runInfinitePollCallAtStart(timeInMinute * 30, forward).catch(e => console.error(red(`Error in port forwarding ${e.stack}`)));
|
|
300
302
|
}
|
|
301
303
|
|
|
302
304
|
let nodeId = getNodeId(getCommonName(config.cert), port);
|
package/time/trueTimeShim.ts
CHANGED
|
@@ -200,10 +200,15 @@ const TimeController = SocketFunction.register(
|
|
|
200
200
|
"TimeController-ddf4753e-fc8a-413f-8cc2-b927dd449976",
|
|
201
201
|
new TimeControllerBase(),
|
|
202
202
|
() => ({
|
|
203
|
-
getTrueTime: {
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
getTrueTime: {
|
|
204
|
+
// No hooks, as this needs to run very early on. Also, it is basically just a ping,
|
|
205
|
+
// so it should be safe for anyone to use (we might even make it just a regular HTTPS endpoint,
|
|
206
|
+
// or even just set up a dedicated domain for this).
|
|
207
|
+
noDefaultHooks: true,
|
|
208
|
+
noClientHooks: true,
|
|
209
|
+
},
|
|
206
210
|
}),
|
|
211
|
+
() => ({}),
|
|
207
212
|
{
|
|
208
213
|
// NOTE: Autoexpose, because our exposed endpoints are incredibly lightweight
|
|
209
214
|
// (just a ping), and don't expose really expose any data.
|