@fuman/net 0.0.1
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/LICENSE +8 -0
- package/errors.cjs +14 -0
- package/errors.d.ts +6 -0
- package/errors.js +14 -0
- package/fake.cjs +42 -0
- package/fake.d.ts +16 -0
- package/fake.js +42 -0
- package/index.cjs +30 -0
- package/index.d.ts +7 -0
- package/index.js +30 -0
- package/ip/bundle.cjs +19 -0
- package/ip/bundle.d.ts +3 -0
- package/ip/bundle.js +19 -0
- package/ip/index.d.ts +8 -0
- package/ip/parse.cjs +34 -0
- package/ip/parse.d.ts +5 -0
- package/ip/parse.js +34 -0
- package/ip/types.d.ts +10 -0
- package/ip/v4.bench.d.ts +1 -0
- package/ip/v4.cjs +55 -0
- package/ip/v4.d.ts +4 -0
- package/ip/v4.js +55 -0
- package/ip/v6.bench.d.ts +1 -0
- package/ip/v6.cjs +217 -0
- package/ip/v6.d.ts +18 -0
- package/ip/v6.js +217 -0
- package/package.json +30 -0
- package/proxy/http/_protocol.cjs +31 -0
- package/proxy/http/_protocol.d.ts +3 -0
- package/proxy/http/_protocol.js +31 -0
- package/proxy/http/connect.cjs +34 -0
- package/proxy/http/connect.d.ts +4 -0
- package/proxy/http/connect.js +34 -0
- package/proxy/http/index.cjs +15 -0
- package/proxy/http/index.d.ts +5 -0
- package/proxy/http/index.js +15 -0
- package/proxy/http/types.cjs +10 -0
- package/proxy/http/types.d.ts +32 -0
- package/proxy/http/types.js +10 -0
- package/proxy/index.d.ts +2 -0
- package/proxy/socks/_protocol.cjs +111 -0
- package/proxy/socks/_protocol.d.ts +7 -0
- package/proxy/socks/_protocol.js +111 -0
- package/proxy/socks/connect.cjs +78 -0
- package/proxy/socks/connect.d.ts +4 -0
- package/proxy/socks/connect.js +78 -0
- package/proxy/socks/index.cjs +15 -0
- package/proxy/socks/index.d.ts +5 -0
- package/proxy/socks/index.js +15 -0
- package/proxy/socks/types.cjs +10 -0
- package/proxy/socks/types.d.ts +34 -0
- package/proxy/socks/types.js +10 -0
- package/reconnection.cjs +151 -0
- package/reconnection.d.ts +82 -0
- package/reconnection.js +151 -0
- package/types.d.ts +41 -0
- package/websocket.cjs +157 -0
- package/websocket.d.ts +44 -0
- package/websocket.js +157 -0
package/ip/v6.cjs
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const io = require("@fuman/io");
|
|
4
|
+
const utils = require("@fuman/utils");
|
|
5
|
+
const IPV6_PARTS = 8;
|
|
6
|
+
function parseV6(string) {
|
|
7
|
+
const parts = new Uint16Array(8);
|
|
8
|
+
let parsedParts = 0;
|
|
9
|
+
let afterDoubleColon;
|
|
10
|
+
let zoneId;
|
|
11
|
+
let currentPartStart = 0;
|
|
12
|
+
let ipv4Part = false;
|
|
13
|
+
let hadSquareBracket = false;
|
|
14
|
+
let pos = 0;
|
|
15
|
+
function addPart(str, part) {
|
|
16
|
+
if (str.length > 4 || Number.isNaN(part) || part < 0 || part > 65535) {
|
|
17
|
+
throw new Error(`Invalid IPv6 part: ${str} (in ${string})`);
|
|
18
|
+
}
|
|
19
|
+
if (afterDoubleColon) {
|
|
20
|
+
afterDoubleColon.push(part);
|
|
21
|
+
} else {
|
|
22
|
+
parts[parsedParts] = part;
|
|
23
|
+
parsedParts += 1;
|
|
24
|
+
if (parsedParts > IPV6_PARTS) {
|
|
25
|
+
throw new Error(`Invalid IPv6 address (too many parts): ${string}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function handleCurrentPart() {
|
|
30
|
+
if (currentPartStart === pos) return;
|
|
31
|
+
const str = string.slice(currentPartStart, pos);
|
|
32
|
+
if (ipv4Part) {
|
|
33
|
+
const ipv4 = str.split(".");
|
|
34
|
+
if (ipv4.length !== 4) {
|
|
35
|
+
throw new Error(`Invalid IPv4 part: ${str} (in ${string})`);
|
|
36
|
+
}
|
|
37
|
+
addPart("", Number.parseInt(ipv4[0], 10) << 8 | Number.parseInt(ipv4[1], 10));
|
|
38
|
+
addPart("", Number.parseInt(ipv4[2], 10) << 8 | Number.parseInt(ipv4[3], 10));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const part = Number.parseInt(str, 16);
|
|
42
|
+
addPart(str, part);
|
|
43
|
+
}
|
|
44
|
+
while (pos < string.length) {
|
|
45
|
+
const c = string[pos];
|
|
46
|
+
if (c === "%") {
|
|
47
|
+
handleCurrentPart();
|
|
48
|
+
pos += 1;
|
|
49
|
+
zoneId = string.slice(pos);
|
|
50
|
+
if (zoneId[zoneId.length - 1] === "]") {
|
|
51
|
+
zoneId = zoneId.slice(0, -1);
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
} else if (c === "[") {
|
|
55
|
+
hadSquareBracket = true;
|
|
56
|
+
currentPartStart = pos + 1;
|
|
57
|
+
} else if (c === "]") {
|
|
58
|
+
if (!hadSquareBracket || pos !== string.length - 1 && string[pos + 1] !== "%") {
|
|
59
|
+
throw new Error(`Invalid IPv6 address (unexpected closing bracket): ${string}`);
|
|
60
|
+
}
|
|
61
|
+
handleCurrentPart();
|
|
62
|
+
currentPartStart = pos + 1;
|
|
63
|
+
} else if (c === ":") {
|
|
64
|
+
if (ipv4Part) {
|
|
65
|
+
throw new Error(`Invalid IPv6 address (colon after IPv4 part): ${string}`);
|
|
66
|
+
}
|
|
67
|
+
if (pos !== 0) {
|
|
68
|
+
handleCurrentPart();
|
|
69
|
+
}
|
|
70
|
+
if (string[pos + 1] === ":") {
|
|
71
|
+
if (afterDoubleColon) {
|
|
72
|
+
throw new Error(`Invalid IPv6 address (multiple double-colons): ${string}`);
|
|
73
|
+
}
|
|
74
|
+
afterDoubleColon = [];
|
|
75
|
+
pos += 1;
|
|
76
|
+
} else if (pos === 0) {
|
|
77
|
+
throw new Error(`Invalid IPv6 address (colon at the beginning): ${string}`);
|
|
78
|
+
}
|
|
79
|
+
currentPartStart = pos + 1;
|
|
80
|
+
} else if (c === ".") {
|
|
81
|
+
ipv4Part = true;
|
|
82
|
+
}
|
|
83
|
+
pos += 1;
|
|
84
|
+
}
|
|
85
|
+
if (!hadSquareBracket && zoneId === void 0) {
|
|
86
|
+
handleCurrentPart();
|
|
87
|
+
}
|
|
88
|
+
if (afterDoubleColon) {
|
|
89
|
+
const missing = IPV6_PARTS - parsedParts - afterDoubleColon.length;
|
|
90
|
+
if (missing < 0) {
|
|
91
|
+
throw new Error(`Invalid IPv6 address (too many parts): ${string}`);
|
|
92
|
+
}
|
|
93
|
+
for (let i = 0; i < missing; i++) {
|
|
94
|
+
parts[parsedParts + i] = 0;
|
|
95
|
+
}
|
|
96
|
+
for (let i = 0; i < afterDoubleColon.length; i++) {
|
|
97
|
+
parts[parsedParts + missing + i] = afterDoubleColon[i];
|
|
98
|
+
}
|
|
99
|
+
} else if (parsedParts < IPV6_PARTS) {
|
|
100
|
+
throw new Error(`Invalid IPv6 address (too few parts): ${string}`);
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
type: "ipv6",
|
|
104
|
+
parts,
|
|
105
|
+
zoneId
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function stringifyV6(parsed, options) {
|
|
109
|
+
const {
|
|
110
|
+
zeroCompression = true,
|
|
111
|
+
fixedLength = false
|
|
112
|
+
} = options || {};
|
|
113
|
+
let result = "";
|
|
114
|
+
let compressStart = -1;
|
|
115
|
+
let compressEnd = 0;
|
|
116
|
+
if (zeroCompression) {
|
|
117
|
+
let maxLength = 0;
|
|
118
|
+
let maxStart = -1;
|
|
119
|
+
let start = -1;
|
|
120
|
+
let length = 0;
|
|
121
|
+
for (let i2 = 0; i2 < parsed.parts.length; i2++) {
|
|
122
|
+
if (parsed.parts[i2] === 0) {
|
|
123
|
+
if (start === -1) {
|
|
124
|
+
start = i2;
|
|
125
|
+
}
|
|
126
|
+
length++;
|
|
127
|
+
} else {
|
|
128
|
+
if (length > maxLength) {
|
|
129
|
+
maxLength = length;
|
|
130
|
+
maxStart = start;
|
|
131
|
+
}
|
|
132
|
+
start = -1;
|
|
133
|
+
length = 0;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (length > maxLength) {
|
|
137
|
+
maxLength = length;
|
|
138
|
+
maxStart = start;
|
|
139
|
+
}
|
|
140
|
+
if (maxLength > 1) {
|
|
141
|
+
compressStart = maxStart;
|
|
142
|
+
compressEnd = maxStart + maxLength;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
let i = 0;
|
|
146
|
+
let writeColon = false;
|
|
147
|
+
while (i < parsed.parts.length) {
|
|
148
|
+
const part = parsed.parts[i];
|
|
149
|
+
if (part < 0 || part > 65535) {
|
|
150
|
+
throw new Error(`Invalid IPv6 part: ${part.toString(16)}`);
|
|
151
|
+
}
|
|
152
|
+
if (i === compressStart) {
|
|
153
|
+
result += "::";
|
|
154
|
+
i = compressEnd;
|
|
155
|
+
writeColon = false;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (writeColon) result += ":";
|
|
159
|
+
else writeColon = true;
|
|
160
|
+
if (fixedLength) {
|
|
161
|
+
if (part < 16) result += "000";
|
|
162
|
+
else if (part < 256) result += "00";
|
|
163
|
+
else if (part < 4096) result += "0";
|
|
164
|
+
}
|
|
165
|
+
result += part.toString(16);
|
|
166
|
+
i += 1;
|
|
167
|
+
}
|
|
168
|
+
if (parsed.zoneId != null) {
|
|
169
|
+
result += `%${parsed.zoneId}`;
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
function expandV6(string) {
|
|
174
|
+
return stringifyV6(parseV6(string), { zeroCompression: false });
|
|
175
|
+
}
|
|
176
|
+
function _fromBytesInternal(bytes) {
|
|
177
|
+
const parts = new Uint16Array(bytes.buffer, bytes.byteOffset, 8);
|
|
178
|
+
if (utils.typed.getPlatformByteOrder() === "little") {
|
|
179
|
+
utils.u8.swap16(bytes);
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
type: "ipv6",
|
|
183
|
+
parts
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function fromBytesV6(bytes) {
|
|
187
|
+
if (bytes.length !== 16) {
|
|
188
|
+
throw new Error(`Invalid IPv6 address buffer: ${bytes.length} ≠ 16`);
|
|
189
|
+
}
|
|
190
|
+
return _fromBytesInternal(utils.u8.clone(bytes));
|
|
191
|
+
}
|
|
192
|
+
function readV6(reader) {
|
|
193
|
+
return _fromBytesInternal(io.read.exactly(reader, 16));
|
|
194
|
+
}
|
|
195
|
+
function toBytesV6(parsed) {
|
|
196
|
+
const copy = new Uint16Array(parsed.parts);
|
|
197
|
+
const bytes = new Uint8Array(copy.buffer);
|
|
198
|
+
if (utils.typed.getPlatformByteOrder() === "little") {
|
|
199
|
+
utils.u8.swap16(bytes);
|
|
200
|
+
}
|
|
201
|
+
return bytes;
|
|
202
|
+
}
|
|
203
|
+
function writeV6(parsed, writer) {
|
|
204
|
+
const buf = writer.writeSync(16);
|
|
205
|
+
buf.set(new Uint8Array(parsed.parts.buffer, parsed.parts.byteOffset, 16));
|
|
206
|
+
if (utils.typed.getPlatformByteOrder() === "little") {
|
|
207
|
+
utils.u8.swap16(buf);
|
|
208
|
+
}
|
|
209
|
+
writer.disposeWriteSync();
|
|
210
|
+
}
|
|
211
|
+
exports.expandV6 = expandV6;
|
|
212
|
+
exports.fromBytesV6 = fromBytesV6;
|
|
213
|
+
exports.parseV6 = parseV6;
|
|
214
|
+
exports.readV6 = readV6;
|
|
215
|
+
exports.stringifyV6 = stringifyV6;
|
|
216
|
+
exports.toBytesV6 = toBytesV6;
|
|
217
|
+
exports.writeV6 = writeV6;
|
package/ip/v6.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ISyncReadable, ISyncWritable } from '@fuman/io';
|
|
2
|
+
import { Ipv6Address } from './types.js';
|
|
3
|
+
export declare function parseV6(string: string): Ipv6Address;
|
|
4
|
+
export interface StringifyV6Options {
|
|
5
|
+
/**
|
|
6
|
+
* Whether to compress consecutive zeroes
|
|
7
|
+
* (according to [RFC 5952](https://datatracker.ietf.org/doc/html/rfc5952#section-4))
|
|
8
|
+
*/
|
|
9
|
+
zeroCompression?: boolean;
|
|
10
|
+
/** Whether to pad each part to 4 characters */
|
|
11
|
+
fixedLength?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function stringifyV6(parsed: Ipv6Address, options?: StringifyV6Options): string;
|
|
14
|
+
export declare function expandV6(string: string): string;
|
|
15
|
+
export declare function fromBytesV6(bytes: Uint8Array): Ipv6Address;
|
|
16
|
+
export declare function readV6(reader: ISyncReadable): Ipv6Address;
|
|
17
|
+
export declare function toBytesV6(parsed: Ipv6Address): Uint8Array;
|
|
18
|
+
export declare function writeV6(parsed: Ipv6Address, writer: ISyncWritable): void;
|
package/ip/v6.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { read } from "@fuman/io";
|
|
2
|
+
import { u8, typed } from "@fuman/utils";
|
|
3
|
+
const IPV6_PARTS = 8;
|
|
4
|
+
function parseV6(string) {
|
|
5
|
+
const parts = new Uint16Array(8);
|
|
6
|
+
let parsedParts = 0;
|
|
7
|
+
let afterDoubleColon;
|
|
8
|
+
let zoneId;
|
|
9
|
+
let currentPartStart = 0;
|
|
10
|
+
let ipv4Part = false;
|
|
11
|
+
let hadSquareBracket = false;
|
|
12
|
+
let pos = 0;
|
|
13
|
+
function addPart(str, part) {
|
|
14
|
+
if (str.length > 4 || Number.isNaN(part) || part < 0 || part > 65535) {
|
|
15
|
+
throw new Error(`Invalid IPv6 part: ${str} (in ${string})`);
|
|
16
|
+
}
|
|
17
|
+
if (afterDoubleColon) {
|
|
18
|
+
afterDoubleColon.push(part);
|
|
19
|
+
} else {
|
|
20
|
+
parts[parsedParts] = part;
|
|
21
|
+
parsedParts += 1;
|
|
22
|
+
if (parsedParts > IPV6_PARTS) {
|
|
23
|
+
throw new Error(`Invalid IPv6 address (too many parts): ${string}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function handleCurrentPart() {
|
|
28
|
+
if (currentPartStart === pos) return;
|
|
29
|
+
const str = string.slice(currentPartStart, pos);
|
|
30
|
+
if (ipv4Part) {
|
|
31
|
+
const ipv4 = str.split(".");
|
|
32
|
+
if (ipv4.length !== 4) {
|
|
33
|
+
throw new Error(`Invalid IPv4 part: ${str} (in ${string})`);
|
|
34
|
+
}
|
|
35
|
+
addPart("", Number.parseInt(ipv4[0], 10) << 8 | Number.parseInt(ipv4[1], 10));
|
|
36
|
+
addPart("", Number.parseInt(ipv4[2], 10) << 8 | Number.parseInt(ipv4[3], 10));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const part = Number.parseInt(str, 16);
|
|
40
|
+
addPart(str, part);
|
|
41
|
+
}
|
|
42
|
+
while (pos < string.length) {
|
|
43
|
+
const c = string[pos];
|
|
44
|
+
if (c === "%") {
|
|
45
|
+
handleCurrentPart();
|
|
46
|
+
pos += 1;
|
|
47
|
+
zoneId = string.slice(pos);
|
|
48
|
+
if (zoneId[zoneId.length - 1] === "]") {
|
|
49
|
+
zoneId = zoneId.slice(0, -1);
|
|
50
|
+
}
|
|
51
|
+
break;
|
|
52
|
+
} else if (c === "[") {
|
|
53
|
+
hadSquareBracket = true;
|
|
54
|
+
currentPartStart = pos + 1;
|
|
55
|
+
} else if (c === "]") {
|
|
56
|
+
if (!hadSquareBracket || pos !== string.length - 1 && string[pos + 1] !== "%") {
|
|
57
|
+
throw new Error(`Invalid IPv6 address (unexpected closing bracket): ${string}`);
|
|
58
|
+
}
|
|
59
|
+
handleCurrentPart();
|
|
60
|
+
currentPartStart = pos + 1;
|
|
61
|
+
} else if (c === ":") {
|
|
62
|
+
if (ipv4Part) {
|
|
63
|
+
throw new Error(`Invalid IPv6 address (colon after IPv4 part): ${string}`);
|
|
64
|
+
}
|
|
65
|
+
if (pos !== 0) {
|
|
66
|
+
handleCurrentPart();
|
|
67
|
+
}
|
|
68
|
+
if (string[pos + 1] === ":") {
|
|
69
|
+
if (afterDoubleColon) {
|
|
70
|
+
throw new Error(`Invalid IPv6 address (multiple double-colons): ${string}`);
|
|
71
|
+
}
|
|
72
|
+
afterDoubleColon = [];
|
|
73
|
+
pos += 1;
|
|
74
|
+
} else if (pos === 0) {
|
|
75
|
+
throw new Error(`Invalid IPv6 address (colon at the beginning): ${string}`);
|
|
76
|
+
}
|
|
77
|
+
currentPartStart = pos + 1;
|
|
78
|
+
} else if (c === ".") {
|
|
79
|
+
ipv4Part = true;
|
|
80
|
+
}
|
|
81
|
+
pos += 1;
|
|
82
|
+
}
|
|
83
|
+
if (!hadSquareBracket && zoneId === void 0) {
|
|
84
|
+
handleCurrentPart();
|
|
85
|
+
}
|
|
86
|
+
if (afterDoubleColon) {
|
|
87
|
+
const missing = IPV6_PARTS - parsedParts - afterDoubleColon.length;
|
|
88
|
+
if (missing < 0) {
|
|
89
|
+
throw new Error(`Invalid IPv6 address (too many parts): ${string}`);
|
|
90
|
+
}
|
|
91
|
+
for (let i = 0; i < missing; i++) {
|
|
92
|
+
parts[parsedParts + i] = 0;
|
|
93
|
+
}
|
|
94
|
+
for (let i = 0; i < afterDoubleColon.length; i++) {
|
|
95
|
+
parts[parsedParts + missing + i] = afterDoubleColon[i];
|
|
96
|
+
}
|
|
97
|
+
} else if (parsedParts < IPV6_PARTS) {
|
|
98
|
+
throw new Error(`Invalid IPv6 address (too few parts): ${string}`);
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
type: "ipv6",
|
|
102
|
+
parts,
|
|
103
|
+
zoneId
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function stringifyV6(parsed, options) {
|
|
107
|
+
const {
|
|
108
|
+
zeroCompression = true,
|
|
109
|
+
fixedLength = false
|
|
110
|
+
} = options || {};
|
|
111
|
+
let result = "";
|
|
112
|
+
let compressStart = -1;
|
|
113
|
+
let compressEnd = 0;
|
|
114
|
+
if (zeroCompression) {
|
|
115
|
+
let maxLength = 0;
|
|
116
|
+
let maxStart = -1;
|
|
117
|
+
let start = -1;
|
|
118
|
+
let length = 0;
|
|
119
|
+
for (let i2 = 0; i2 < parsed.parts.length; i2++) {
|
|
120
|
+
if (parsed.parts[i2] === 0) {
|
|
121
|
+
if (start === -1) {
|
|
122
|
+
start = i2;
|
|
123
|
+
}
|
|
124
|
+
length++;
|
|
125
|
+
} else {
|
|
126
|
+
if (length > maxLength) {
|
|
127
|
+
maxLength = length;
|
|
128
|
+
maxStart = start;
|
|
129
|
+
}
|
|
130
|
+
start = -1;
|
|
131
|
+
length = 0;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (length > maxLength) {
|
|
135
|
+
maxLength = length;
|
|
136
|
+
maxStart = start;
|
|
137
|
+
}
|
|
138
|
+
if (maxLength > 1) {
|
|
139
|
+
compressStart = maxStart;
|
|
140
|
+
compressEnd = maxStart + maxLength;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
let i = 0;
|
|
144
|
+
let writeColon = false;
|
|
145
|
+
while (i < parsed.parts.length) {
|
|
146
|
+
const part = parsed.parts[i];
|
|
147
|
+
if (part < 0 || part > 65535) {
|
|
148
|
+
throw new Error(`Invalid IPv6 part: ${part.toString(16)}`);
|
|
149
|
+
}
|
|
150
|
+
if (i === compressStart) {
|
|
151
|
+
result += "::";
|
|
152
|
+
i = compressEnd;
|
|
153
|
+
writeColon = false;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (writeColon) result += ":";
|
|
157
|
+
else writeColon = true;
|
|
158
|
+
if (fixedLength) {
|
|
159
|
+
if (part < 16) result += "000";
|
|
160
|
+
else if (part < 256) result += "00";
|
|
161
|
+
else if (part < 4096) result += "0";
|
|
162
|
+
}
|
|
163
|
+
result += part.toString(16);
|
|
164
|
+
i += 1;
|
|
165
|
+
}
|
|
166
|
+
if (parsed.zoneId != null) {
|
|
167
|
+
result += `%${parsed.zoneId}`;
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
function expandV6(string) {
|
|
172
|
+
return stringifyV6(parseV6(string), { zeroCompression: false });
|
|
173
|
+
}
|
|
174
|
+
function _fromBytesInternal(bytes) {
|
|
175
|
+
const parts = new Uint16Array(bytes.buffer, bytes.byteOffset, 8);
|
|
176
|
+
if (typed.getPlatformByteOrder() === "little") {
|
|
177
|
+
u8.swap16(bytes);
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
type: "ipv6",
|
|
181
|
+
parts
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function fromBytesV6(bytes) {
|
|
185
|
+
if (bytes.length !== 16) {
|
|
186
|
+
throw new Error(`Invalid IPv6 address buffer: ${bytes.length} ≠ 16`);
|
|
187
|
+
}
|
|
188
|
+
return _fromBytesInternal(u8.clone(bytes));
|
|
189
|
+
}
|
|
190
|
+
function readV6(reader) {
|
|
191
|
+
return _fromBytesInternal(read.exactly(reader, 16));
|
|
192
|
+
}
|
|
193
|
+
function toBytesV6(parsed) {
|
|
194
|
+
const copy = new Uint16Array(parsed.parts);
|
|
195
|
+
const bytes = new Uint8Array(copy.buffer);
|
|
196
|
+
if (typed.getPlatformByteOrder() === "little") {
|
|
197
|
+
u8.swap16(bytes);
|
|
198
|
+
}
|
|
199
|
+
return bytes;
|
|
200
|
+
}
|
|
201
|
+
function writeV6(parsed, writer) {
|
|
202
|
+
const buf = writer.writeSync(16);
|
|
203
|
+
buf.set(new Uint8Array(parsed.parts.buffer, parsed.parts.byteOffset, 16));
|
|
204
|
+
if (typed.getPlatformByteOrder() === "little") {
|
|
205
|
+
u8.swap16(buf);
|
|
206
|
+
}
|
|
207
|
+
writer.disposeWriteSync();
|
|
208
|
+
}
|
|
209
|
+
export {
|
|
210
|
+
expandV6,
|
|
211
|
+
fromBytesV6,
|
|
212
|
+
parseV6,
|
|
213
|
+
readV6,
|
|
214
|
+
stringifyV6,
|
|
215
|
+
toBytesV6,
|
|
216
|
+
writeV6
|
|
217
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fuman/net",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"description": "experimental network abstractions",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"scripts": {},
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"@fuman/io": "^0.0.1",
|
|
10
|
+
"@fuman/utils": "^0.0.1"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": {
|
|
15
|
+
"types": "./index.d.ts",
|
|
16
|
+
"default": "./index.js"
|
|
17
|
+
},
|
|
18
|
+
"require": {
|
|
19
|
+
"types": "./index.d.cts",
|
|
20
|
+
"default": "./index.cjs"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"author": "",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/teidesu/fuman.git"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const utils = require("@fuman/utils");
|
|
4
|
+
function buildConnectRequest(options, dest) {
|
|
5
|
+
let addr = dest.address;
|
|
6
|
+
if (addr.includes(":")) {
|
|
7
|
+
addr = `[${addr}]`;
|
|
8
|
+
}
|
|
9
|
+
const host = `${addr}:${dest.port}`;
|
|
10
|
+
const lines = [
|
|
11
|
+
`CONNECT ${host} HTTP/1.1`,
|
|
12
|
+
`Host: ${host}`,
|
|
13
|
+
"User-Agent: @fuman/net",
|
|
14
|
+
"Proxy-Connection: Keep-Alive"
|
|
15
|
+
];
|
|
16
|
+
if (options.user != null) {
|
|
17
|
+
let auth = options.user;
|
|
18
|
+
if (options.password != null) {
|
|
19
|
+
auth += `:${options.password}`;
|
|
20
|
+
}
|
|
21
|
+
lines.push(`Proxy-Authorization: Basic ${utils.base64.encode(utils.utf8.encoder.encode(auth))}`);
|
|
22
|
+
}
|
|
23
|
+
if (options.headers) {
|
|
24
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
25
|
+
lines.push(`${key}: ${value}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
lines.push("", "");
|
|
29
|
+
return utils.utf8.encoder.encode(lines.join("\r\n"));
|
|
30
|
+
}
|
|
31
|
+
exports.buildConnectRequest = buildConnectRequest;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { base64, utf8 } from "@fuman/utils";
|
|
2
|
+
function buildConnectRequest(options, dest) {
|
|
3
|
+
let addr = dest.address;
|
|
4
|
+
if (addr.includes(":")) {
|
|
5
|
+
addr = `[${addr}]`;
|
|
6
|
+
}
|
|
7
|
+
const host = `${addr}:${dest.port}`;
|
|
8
|
+
const lines = [
|
|
9
|
+
`CONNECT ${host} HTTP/1.1`,
|
|
10
|
+
`Host: ${host}`,
|
|
11
|
+
"User-Agent: @fuman/net",
|
|
12
|
+
"Proxy-Connection: Keep-Alive"
|
|
13
|
+
];
|
|
14
|
+
if (options.user != null) {
|
|
15
|
+
let auth = options.user;
|
|
16
|
+
if (options.password != null) {
|
|
17
|
+
auth += `:${options.password}`;
|
|
18
|
+
}
|
|
19
|
+
lines.push(`Proxy-Authorization: Basic ${base64.encode(utf8.encoder.encode(auth))}`);
|
|
20
|
+
}
|
|
21
|
+
if (options.headers) {
|
|
22
|
+
for (const [key, value] of Object.entries(options.headers)) {
|
|
23
|
+
lines.push(`${key}: ${value}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
lines.push("", "");
|
|
27
|
+
return utf8.encoder.encode(lines.join("\r\n"));
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
buildConnectRequest
|
|
31
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const io = require("@fuman/io");
|
|
4
|
+
const utils = require("@fuman/utils");
|
|
5
|
+
const _protocol = require("./_protocol.cjs");
|
|
6
|
+
const types = require("./types.cjs");
|
|
7
|
+
const HTTP1_1_OK = /* @__PURE__ */ utils.utf8.encoder.encode("HTTP/1.1 200");
|
|
8
|
+
const CR = /* @__PURE__ */ "\r".charCodeAt(0);
|
|
9
|
+
const LF = /* @__PURE__ */ "\n".charCodeAt(0);
|
|
10
|
+
async function performHttpProxyHandshake(reader, writer, proxy, destination) {
|
|
11
|
+
await writer.write(_protocol.buildConnectRequest(proxy, destination));
|
|
12
|
+
const res1 = await io.read.async.exactly(reader, 12);
|
|
13
|
+
if (!utils.typed.equal(res1, HTTP1_1_OK)) {
|
|
14
|
+
throw new types.HttpProxyConnectionError(
|
|
15
|
+
proxy,
|
|
16
|
+
`Invalid HTTP response: ${utils.utf8.decoder.decode(res1)}`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
const buf = utils.u8.alloc(4);
|
|
20
|
+
while (true) {
|
|
21
|
+
await io.read.async.exactly(reader, buf);
|
|
22
|
+
const idx = utils.typed.indexOf(buf, CR);
|
|
23
|
+
if (idx !== -1) {
|
|
24
|
+
if (idx > 0) {
|
|
25
|
+
buf.copyWithin(0, idx);
|
|
26
|
+
await io.read.async.exactly(reader, buf.subarray(4 - idx));
|
|
27
|
+
}
|
|
28
|
+
if (buf[0] === CR && buf[1] === LF && buf[2] === CR && buf[3] === LF) {
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.performHttpProxyHandshake = performHttpProxyHandshake;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { TcpEndpoint } from '../../types.js';
|
|
2
|
+
import { IReadable, IWritable } from '@fuman/io';
|
|
3
|
+
import { HttpProxySettings } from './types.js';
|
|
4
|
+
export declare function performHttpProxyHandshake(reader: IReadable, writer: IWritable, proxy: HttpProxySettings, destination: TcpEndpoint): Promise<void>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { read } from "@fuman/io";
|
|
2
|
+
import { typed, utf8, u8 } from "@fuman/utils";
|
|
3
|
+
import { buildConnectRequest } from "./_protocol.js";
|
|
4
|
+
import { HttpProxyConnectionError } from "./types.js";
|
|
5
|
+
const HTTP1_1_OK = /* @__PURE__ */ utf8.encoder.encode("HTTP/1.1 200");
|
|
6
|
+
const CR = /* @__PURE__ */ "\r".charCodeAt(0);
|
|
7
|
+
const LF = /* @__PURE__ */ "\n".charCodeAt(0);
|
|
8
|
+
async function performHttpProxyHandshake(reader, writer, proxy, destination) {
|
|
9
|
+
await writer.write(buildConnectRequest(proxy, destination));
|
|
10
|
+
const res1 = await read.async.exactly(reader, 12);
|
|
11
|
+
if (!typed.equal(res1, HTTP1_1_OK)) {
|
|
12
|
+
throw new HttpProxyConnectionError(
|
|
13
|
+
proxy,
|
|
14
|
+
`Invalid HTTP response: ${utf8.decoder.decode(res1)}`
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
const buf = u8.alloc(4);
|
|
18
|
+
while (true) {
|
|
19
|
+
await read.async.exactly(reader, buf);
|
|
20
|
+
const idx = typed.indexOf(buf, CR);
|
|
21
|
+
if (idx !== -1) {
|
|
22
|
+
if (idx > 0) {
|
|
23
|
+
buf.copyWithin(0, idx);
|
|
24
|
+
await read.async.exactly(reader, buf.subarray(4 - idx));
|
|
25
|
+
}
|
|
26
|
+
if (buf[0] === CR && buf[1] === LF && buf[2] === CR && buf[3] === LF) {
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
performHttpProxyHandshake
|
|
34
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const connect = require("./connect.cjs");
|
|
4
|
+
function withHttpProxy(connect$1, proxy) {
|
|
5
|
+
return async (endpoint) => {
|
|
6
|
+
const conn = await connect$1({
|
|
7
|
+
address: proxy.host,
|
|
8
|
+
port: proxy.port
|
|
9
|
+
});
|
|
10
|
+
await connect.performHttpProxyHandshake(conn, conn, proxy, endpoint);
|
|
11
|
+
return conn;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
exports.performHttpProxyHandshake = connect.performHttpProxyHandshake;
|
|
15
|
+
exports.withHttpProxy = withHttpProxy;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ConnectFunction, ITcpConnection, TcpEndpoint } from '../../types.js';
|
|
2
|
+
import { HttpProxySettings } from './types.js';
|
|
3
|
+
export * from './connect.js';
|
|
4
|
+
export * from './types.js';
|
|
5
|
+
export declare function withHttpProxy<Connection extends ITcpConnection, Connect extends ConnectFunction<TcpEndpoint, Connection>>(connect: Connect, proxy: HttpProxySettings): Connect;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { performHttpProxyHandshake } from "./connect.js";
|
|
2
|
+
function withHttpProxy(connect, proxy) {
|
|
3
|
+
return async (endpoint) => {
|
|
4
|
+
const conn = await connect({
|
|
5
|
+
address: proxy.host,
|
|
6
|
+
port: proxy.port
|
|
7
|
+
});
|
|
8
|
+
await performHttpProxyHandshake(conn, conn, proxy, endpoint);
|
|
9
|
+
return conn;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
performHttpProxyHandshake,
|
|
14
|
+
withHttpProxy
|
|
15
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
class HttpProxyConnectionError extends Error {
|
|
4
|
+
proxy;
|
|
5
|
+
constructor(proxy, message) {
|
|
6
|
+
super(`Error while connecting to ${proxy.host}:${proxy.port}: ${message}`);
|
|
7
|
+
this.proxy = proxy;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.HttpProxyConnectionError = HttpProxyConnectionError;
|