@swf2es/desktop 0.1.15 → 0.1.17
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/README.md +85 -12
- package/dist/launch/launch.js +4 -1
- package/dist/launch/launch.js.map +1 -1
- package/dist/main/air.d.ts +3 -3
- package/dist/main/air.d.ts.map +1 -1
- package/dist/main/air.js +17 -8
- package/dist/main/air.js.map +1 -1
- package/dist/main/case-paths.d.ts +12 -0
- package/dist/main/case-paths.d.ts.map +1 -0
- package/dist/main/case-paths.js +84 -0
- package/dist/main/case-paths.js.map +1 -0
- package/dist/main/connections.d.ts +20 -1
- package/dist/main/connections.d.ts.map +1 -1
- package/dist/main/connections.js +71 -8
- package/dist/main/connections.js.map +1 -1
- package/dist/main/main.js +133 -9
- package/dist/main/main.js.map +1 -1
- package/dist/main/netlog.d.ts +21 -0
- package/dist/main/netlog.d.ts.map +1 -0
- package/dist/main/netlog.js +90 -0
- package/dist/main/netlog.js.map +1 -0
- package/dist/main/network.d.ts +10 -0
- package/dist/main/network.d.ts.map +1 -1
- package/dist/main/network.js +111 -35
- package/dist/main/network.js.map +1 -1
- package/dist/main/proxy.d.ts +48 -0
- package/dist/main/proxy.d.ts.map +1 -0
- package/dist/main/proxy.js +402 -0
- package/dist/main/proxy.js.map +1 -0
- package/dist/main/sandbox.d.ts.map +1 -1
- package/dist/main/sandbox.js +2 -1
- package/dist/main/sandbox.js.map +1 -1
- package/dist/main/settings.d.ts +2 -0
- package/dist/main/settings.d.ts.map +1 -1
- package/dist/main/settings.js +4 -0
- package/dist/main/settings.js.map +1 -1
- package/dist/main/sockets.d.ts +6 -2
- package/dist/main/sockets.d.ts.map +1 -1
- package/dist/main/sockets.js +18 -3
- package/dist/main/sockets.js.map +1 -1
- package/dist/main/websocket-connections.d.ts +73 -0
- package/dist/main/websocket-connections.d.ts.map +1 -0
- package/dist/main/websocket-connections.js +266 -0
- package/dist/main/websocket-connections.js.map +1 -0
- package/dist/main/websockets.d.ts +12 -0
- package/dist/main/websockets.d.ts.map +1 -0
- package/dist/main/websockets.js +94 -0
- package/dist/main/websockets.js.map +1 -0
- package/dist/preload/preload.cjs +20 -0
- package/dist/preload/preload.cjs.map +1 -1
- package/dist/renderer/air.d.ts.map +1 -1
- package/dist/renderer/air.js +1 -0
- package/dist/renderer/air.js.map +1 -1
- package/dist/renderer/full-screen.d.ts +4 -0
- package/dist/renderer/full-screen.d.ts.map +1 -0
- package/dist/renderer/full-screen.js +32 -0
- package/dist/renderer/full-screen.js.map +1 -0
- package/dist/renderer/index.d.ts.map +1 -1
- package/dist/renderer/index.js +4 -0
- package/dist/renderer/index.js.map +1 -1
- package/dist/renderer/websockets.d.ts +4 -0
- package/dist/renderer/websockets.d.ts.map +1 -0
- package/dist/renderer/websockets.js +36 -0
- package/dist/renderer/websockets.js.map +1 -0
- package/dist/shared/api.d.ts +53 -0
- package/dist/shared/api.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/launch/launch.ts +5 -2
- package/src/main/air.ts +18 -8
- package/src/main/case-paths.ts +97 -0
- package/src/main/connections.ts +90 -8
- package/src/main/main.ts +174 -10
- package/src/main/netlog.ts +108 -0
- package/src/main/network.ts +140 -37
- package/src/main/proxy.ts +497 -0
- package/src/main/sandbox.ts +2 -1
- package/src/main/settings.ts +5 -0
- package/src/main/sockets.ts +22 -3
- package/src/main/websocket-connections.ts +344 -0
- package/src/main/websockets.ts +117 -0
- package/src/preload/preload.cts +23 -0
- package/src/renderer/air.ts +1 -0
- package/src/renderer/full-screen.ts +44 -0
- package/src/renderer/index.ts +4 -0
- package/src/renderer/websockets.ts +43 -0
- package/src/shared/api.ts +40 -0
package/src/main/network.ts
CHANGED
|
@@ -21,12 +21,17 @@
|
|
|
21
21
|
// that changes its answer (rebinding) changes nothing.
|
|
22
22
|
// - Requests are bounded: methods, headers, sizes, redirects, time, and
|
|
23
23
|
// how many run at once; and each movie's are aborted as the next opens.
|
|
24
|
+
// - Through a proxy (--proxy, proxy.ts), each connection is a tunnel to
|
|
25
|
+
// the address its host was pinned to, judged as without one: the proxy
|
|
26
|
+
// is asked for that address, not the name, so a proxy's DNS that answers
|
|
27
|
+
// otherwise than this machine's reaches nothing the judgement did not.
|
|
24
28
|
//
|
|
25
29
|
// Without Electron, so that node tests it (tests/desktop/network.test.ts).
|
|
26
30
|
import { lookup } from "node:dns/promises";
|
|
27
31
|
import { type ClientRequest, request as httpRequest, type IncomingMessage } from "node:http";
|
|
28
32
|
import { request as httpsRequest } from "node:https";
|
|
29
|
-
import { connect, isIP, isIPv4 } from "node:net";
|
|
33
|
+
import { connect, isIP, isIPv4, type Socket } from "node:net";
|
|
34
|
+
import type { Duplex } from "node:stream";
|
|
30
35
|
import * as tls from "node:tls";
|
|
31
36
|
import { type AddressClass, addressClass, badPort, RANK } from "./addresses.ts";
|
|
32
37
|
import { DECODED, Decompressor } from "./decompress.ts";
|
|
@@ -44,6 +49,7 @@ import {
|
|
|
44
49
|
urlMetaPolicy,
|
|
45
50
|
usablePolicies,
|
|
46
51
|
} from "./policy.ts";
|
|
52
|
+
import { connectThrough, type ProxyConfig, ProxyError } from "./proxy.ts";
|
|
47
53
|
|
|
48
54
|
export type Purpose = "movie" | "content" | "data" | "send";
|
|
49
55
|
|
|
@@ -145,6 +151,8 @@ export interface NetworkOptions {
|
|
|
145
151
|
limits?: Partial<Limits>;
|
|
146
152
|
/** Where a socket server keeps its master policy: 843. */
|
|
147
153
|
socketPolicyPort?: number;
|
|
154
|
+
/** What every connection goes through (--proxy); none by default. */
|
|
155
|
+
proxy?: ProxyConfig | null;
|
|
148
156
|
}
|
|
149
157
|
|
|
150
158
|
/** What Flash Player 32 reported, sent as it sent it. */
|
|
@@ -239,6 +247,7 @@ export class Network {
|
|
|
239
247
|
private readonly ca: string | string[] | undefined;
|
|
240
248
|
private readonly unverified: (host: string) => boolean;
|
|
241
249
|
private readonly socketPolicyPort: number;
|
|
250
|
+
private readonly proxy: ProxyConfig | null;
|
|
242
251
|
private session: Session | null = null;
|
|
243
252
|
private running = 0;
|
|
244
253
|
private readonly waiting: (() => void)[] = [];
|
|
@@ -253,6 +262,7 @@ export class Network {
|
|
|
253
262
|
this.ca = options.ca ?? defaultCa();
|
|
254
263
|
this.unverified = options.unverified ?? (() => false);
|
|
255
264
|
this.socketPolicyPort = options.socketPolicyPort ?? 843;
|
|
265
|
+
this.proxy = options.proxy ?? null;
|
|
256
266
|
}
|
|
257
267
|
|
|
258
268
|
/**
|
|
@@ -753,7 +763,13 @@ export class Network {
|
|
|
753
763
|
timeout: this.limits.policyTimeout,
|
|
754
764
|
deadline: this.limits.policyTimeout,
|
|
755
765
|
});
|
|
756
|
-
} catch {
|
|
766
|
+
} catch (error) {
|
|
767
|
+
// A proxy that failed is said, as the request that needed the policy fails;
|
|
768
|
+
// a server the proxy did not reach has no policy, as one that is not there.
|
|
769
|
+
if (proxyFault(error)) {
|
|
770
|
+
throw error;
|
|
771
|
+
}
|
|
772
|
+
|
|
757
773
|
return null;
|
|
758
774
|
}
|
|
759
775
|
|
|
@@ -790,15 +806,27 @@ export class Network {
|
|
|
790
806
|
timeout: number,
|
|
791
807
|
signal: AbortSignal,
|
|
792
808
|
): Promise<SocketPolicy | null> {
|
|
793
|
-
return new Promise((answer) => {
|
|
809
|
+
return new Promise((answer, fail) => {
|
|
794
810
|
const chunks: Buffer[] = [];
|
|
795
811
|
let length = 0;
|
|
796
|
-
|
|
797
|
-
|
|
812
|
+
let socket: Socket | null = null;
|
|
813
|
+
let finished = false;
|
|
814
|
+
const opening = new AbortController();
|
|
815
|
+
const finish = (policy: SocketPolicy | null, error?: Error) => {
|
|
816
|
+
if (finished) {
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
finished = true;
|
|
798
821
|
clearTimeout(timer);
|
|
799
822
|
signal.removeEventListener("abort", abort);
|
|
800
|
-
|
|
801
|
-
|
|
823
|
+
opening.abort();
|
|
824
|
+
socket?.destroy();
|
|
825
|
+
if (error) {
|
|
826
|
+
fail(error);
|
|
827
|
+
} else {
|
|
828
|
+
answer(policy);
|
|
829
|
+
}
|
|
802
830
|
};
|
|
803
831
|
const read = () => {
|
|
804
832
|
const text = Buffer.concat(chunks).toString("utf8");
|
|
@@ -808,24 +836,70 @@ export class Network {
|
|
|
808
836
|
const abort = () => finish(null);
|
|
809
837
|
const timer = setTimeout(abort, timeout);
|
|
810
838
|
signal.addEventListener("abort", abort, { once: true });
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
839
|
+
const talk = (connected: Socket) => {
|
|
840
|
+
socket = connected;
|
|
841
|
+
connected.on("data", (chunk: Buffer) => {
|
|
842
|
+
length += chunk.length;
|
|
843
|
+
if (length > this.limits.policy) {
|
|
844
|
+
finish(null);
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
818
847
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
848
|
+
chunks.push(chunk);
|
|
849
|
+
if (chunk.includes(0)) {
|
|
850
|
+
read();
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
connected.on("end", read);
|
|
854
|
+
connected.on("error", () => finish(null));
|
|
855
|
+
};
|
|
856
|
+
|
|
857
|
+
if (!this.proxy) {
|
|
858
|
+
const direct = connect({ host: address, port });
|
|
859
|
+
talk(direct);
|
|
860
|
+
direct.on("connect", () => direct.write("<policy-file-request/>\0"));
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
connectThrough(this.proxy, address, port, { signal: opening.signal }).then(
|
|
865
|
+
(tunnel) => {
|
|
866
|
+
if (finished) {
|
|
867
|
+
tunnel.destroy();
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
talk(tunnel);
|
|
872
|
+
tunnel.write("<policy-file-request/>\0");
|
|
873
|
+
},
|
|
874
|
+
(error: ProxyError) =>
|
|
875
|
+
// A server the proxy did not reach has no policy; a proxy that failed is said.
|
|
876
|
+
finish(null, error.fault ? new Refused(error.message, { cause: error }) : undefined),
|
|
877
|
+
);
|
|
826
878
|
});
|
|
827
879
|
}
|
|
828
880
|
|
|
881
|
+
/**
|
|
882
|
+
* A request's createConnection: a tunnel through `proxy` to `address`,
|
|
883
|
+
* with TLS over it where `secure` says how, as Node's callback form takes
|
|
884
|
+
* it. A proxy's failure is a refusal saying why, the ProxyError its cause.
|
|
885
|
+
*/
|
|
886
|
+
private tunnel(
|
|
887
|
+
proxy: ProxyConfig,
|
|
888
|
+
address: string,
|
|
889
|
+
port: number,
|
|
890
|
+
secure: tls.ConnectionOptions | false,
|
|
891
|
+
signal: AbortSignal,
|
|
892
|
+
) {
|
|
893
|
+
return (_options: unknown, done: (error: Error | null, socket: Duplex) => void) => {
|
|
894
|
+
connectThrough(proxy, address, port, { signal }).then(
|
|
895
|
+
(tunnel) => done(null, secure ? tls.connect({ ...secure, socket: tunnel }) : tunnel),
|
|
896
|
+
(error: ProxyError) =>
|
|
897
|
+
done(new Refused(error.message, { cause: error }), undefined as unknown as Duplex),
|
|
898
|
+
);
|
|
899
|
+
return undefined;
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
|
|
829
903
|
/**
|
|
830
904
|
* One HTTP exchange with `address`, the URL's host pinned to it: its
|
|
831
905
|
* status and headers, and its body if `read` and the status is a
|
|
@@ -877,28 +951,41 @@ export class Network {
|
|
|
877
951
|
|
|
878
952
|
const https = url.protocol === "https:";
|
|
879
953
|
const family = isIP(address);
|
|
954
|
+
const port = Number(url.port || (https ? 443 : 80));
|
|
955
|
+
const secure: tls.ConnectionOptions = {
|
|
956
|
+
servername: isIP(host) ? undefined : host,
|
|
957
|
+
ca: this.ca,
|
|
958
|
+
// Only where the user asked for this host: its certificate goes unchecked.
|
|
959
|
+
...(this.unverified(host) ? { rejectUnauthorized: false } : {}),
|
|
960
|
+
};
|
|
880
961
|
let request: ClientRequest;
|
|
881
962
|
try {
|
|
882
963
|
request = (https ? httpsRequest : httpRequest)({
|
|
883
964
|
host,
|
|
884
|
-
port
|
|
965
|
+
port,
|
|
885
966
|
path: `${url.pathname}${url.search}`,
|
|
886
967
|
method,
|
|
887
968
|
headers: outgoing,
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
969
|
+
...(this.proxy
|
|
970
|
+
? // Through the proxy to the pinned address, TLS over the tunnel for https; the
|
|
971
|
+
// request's own timers and signal bound the proxy's part too. No agent: the
|
|
972
|
+
// connection is this one's.
|
|
973
|
+
{ createConnection: this.tunnel(this.proxy, address, port, https && secure, signal) }
|
|
974
|
+
: {
|
|
975
|
+
agent: false,
|
|
976
|
+
// The address pinned for this request, whatever the resolver would say now.
|
|
977
|
+
lookup: (_name, opts, callback) => {
|
|
978
|
+
if ((opts as { all?: boolean }).all) {
|
|
979
|
+
(callback as (e: null, a: { address: string; family: number }[]) => void)(
|
|
980
|
+
null,
|
|
981
|
+
[{ address, family }],
|
|
982
|
+
);
|
|
983
|
+
} else {
|
|
984
|
+
(callback as (e: null, a: string, f: number) => void)(null, address, family);
|
|
985
|
+
}
|
|
986
|
+
},
|
|
987
|
+
...(https ? secure : {}),
|
|
988
|
+
}),
|
|
902
989
|
});
|
|
903
990
|
} catch (error) {
|
|
904
991
|
// What check() let through and Node still refuses: a refusal like any other.
|
|
@@ -931,7 +1018,10 @@ export class Network {
|
|
|
931
1018
|
const abort = () => stop(new Refused("aborted"));
|
|
932
1019
|
signal.addEventListener("abort", abort, { once: true });
|
|
933
1020
|
arm();
|
|
934
|
-
|
|
1021
|
+
// A proxy's refusal comes as it was made, with its cause.
|
|
1022
|
+
request.on("error", (error) =>
|
|
1023
|
+
stop(error instanceof Refused ? error : new Refused(error.message)),
|
|
1024
|
+
);
|
|
935
1025
|
request.on("response", (response: IncomingMessage) => {
|
|
936
1026
|
const status = response.statusCode ?? 0;
|
|
937
1027
|
const received: [string, string][] = [];
|
|
@@ -1195,11 +1285,24 @@ function cached<T>(
|
|
|
1195
1285
|
|
|
1196
1286
|
value = make();
|
|
1197
1287
|
cache.set(key, value);
|
|
1288
|
+
// A failure, a proxy's, is not kept: the next request asks again.
|
|
1289
|
+
const made = value;
|
|
1290
|
+
made.catch(() => {
|
|
1291
|
+
if (cache.get(key) === made) {
|
|
1292
|
+
cache.delete(key);
|
|
1293
|
+
}
|
|
1294
|
+
});
|
|
1198
1295
|
}
|
|
1199
1296
|
|
|
1200
1297
|
return value;
|
|
1201
1298
|
}
|
|
1202
1299
|
|
|
1300
|
+
/** Whether `error` is a refusal for a proxy that failed, not a server it did not reach. */
|
|
1301
|
+
function proxyFault(error: unknown): boolean {
|
|
1302
|
+
const cause = (error as Error | null)?.cause;
|
|
1303
|
+
return cause instanceof ProxyError && cause.fault;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1203
1306
|
function describe(requester: Requester): string {
|
|
1204
1307
|
return requester.host === null ? 'a local SWF (domain="*")' : requester.host;
|
|
1205
1308
|
}
|