@blockcast/mmt-container 1.0.1 → 2.0.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/README.md +9 -7
- package/dist/browser-runtime.js +6931 -6685
- package/dist/browser-runtime.js.map +4 -4
- package/dist/carp-catalog.d.ts +7 -7
- package/dist/carp-catalog.d.ts.map +1 -1
- package/dist/carp-catalog.js +65 -230
- package/dist/carp-catalog.js.map +3 -3
- package/dist/catalog-fec-params.js.map +1 -1
- package/dist/catalog-network-source.js.map +1 -1
- package/dist/delivery-catalog.js.map +1 -1
- package/dist/delta-fallback-config.js.map +1 -1
- package/dist/fec-manager.d.ts +25 -1
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +208 -15
- package/dist/fec-manager.js.map +2 -2
- package/dist/fec-state.js.map +1 -1
- package/dist/fec-worker-setup.js.map +1 -1
- package/dist/held-layer-admission.d.ts +8 -0
- package/dist/held-layer-admission.d.ts.map +1 -1
- package/dist/held-layer-admission.js +22 -1
- package/dist/held-layer-admission.js.map +2 -2
- package/dist/iso-block-locator.js.map +1 -1
- package/dist/mfu-reassembler.js.map +1 -1
- package/dist/mmtp-container-parser.js.map +1 -1
- package/dist/mmtp-container-parser.no-modules.js +127 -322
- package/dist/mmtp-container-parser.no-modules.js.map +4 -4
- package/dist/mmtp-decode-pipeline.js.map +1 -1
- package/dist/mmtp-fec-session.d.ts.map +1 -1
- package/dist/mmtp-fec-session.js +61 -15
- package/dist/mmtp-fec-session.js.map +3 -3
- package/dist/mmtp-router.d.ts +11 -6
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +47 -10
- package/dist/mmtp-router.js.map +3 -3
- package/dist/multicast-auth-catalog.d.ts +8 -8
- package/dist/multicast-auth-catalog.d.ts.map +1 -1
- package/dist/multicast-auth-catalog.js +171 -75
- package/dist/multicast-auth-catalog.js.map +4 -4
- package/dist/multicast-input.d.ts +5 -5
- package/dist/multicast-input.d.ts.map +1 -1
- package/dist/multicast-input.js +126 -49
- package/dist/multicast-input.js.map +3 -3
- package/dist/multicast-manager.d.ts +11 -4
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +7922 -7700
- package/dist/multicast-manager.js.map +4 -4
- package/dist/multicast-transport.d.ts +8 -0
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +27 -3
- package/dist/multicast-transport.js.map +3 -3
- package/dist/multicast-unwrap.js +2 -2
- package/dist/multicast-unwrap.js.map +2 -2
- package/dist/no-modules.js +81 -278
- package/dist/no-modules.js.map +4 -4
- package/dist/repair-fetch.js.map +1 -1
- package/dist/signaling.js.map +1 -1
- package/dist/timestamp-codec.js.map +1 -1
- package/dist/wasm-mfu-reassembler.js.map +1 -1
- package/dist/wasm-mmtp-container-parser.js.map +1 -1
- package/package.json +5 -5
- package/schemas/catalog.schema.json +15 -7
- package/src/carp-catalog.ts +14 -13
- package/src/fec-manager.ts +89 -14
- package/src/held-layer-admission.ts +38 -1
- package/src/mmtp-fec-session.ts +20 -12
- package/src/mmtp-router.ts +65 -16
- package/src/multicast-auth-catalog.ts +43 -36
- package/src/multicast-input.ts +28 -8
- package/src/multicast-manager.ts +38 -34
- package/src/multicast-transport.ts +31 -3
- package/test-vectors/bc-provenance-v2.json +614 -175
- package/test-vectors/catalog/README.md +24 -8
- package/test-vectors/catalog/negative/multicast-auth-asm-missing-sender-address.json +0 -2
- package/test-vectors/catalog/negative/{multicast-auth-manifest-port-equals-port.json → multicast-auth-draft01-manifest-port.json} +1 -1
- package/test-vectors/catalog/negative/multicast-auth-duplicate-first-epoch.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-duplicate-kid.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-endpoint-auth-without-object-auth.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-endpoint-missing-bandwidth.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-manifest-timer-out-of-range.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-sender-address-on-ssm.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-shared-sg-port.json +73 -0
- package/test-vectors/catalog/negative/multicast-auth-version-1.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-version-3.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-version-missing.json +0 -1
- package/test-vectors/catalog/negative/multicast-endpoint-address-family-mismatch.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-host-name.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-leading-zero.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-ls32-leading-zero.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-mapped-family-mismatch.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-zone-id.json +55 -0
- package/test-vectors/catalog/positive/multicast-auth-asm-sender.json +0 -2
- package/test-vectors/catalog/{negative/multicast-auth-max-auth-delay-above-max.json → positive/multicast-auth-max-auth-delay-above-max-clamped.json} +0 -1
- package/test-vectors/catalog/{negative/multicast-auth-max-auth-delay-below-min.json → positive/multicast-auth-max-auth-delay-below-min-clamped.json} +0 -1
- package/test-vectors/catalog/positive/multicast-auth-max-auth-delay-string.json +55 -0
- package/test-vectors/catalog/positive/multicast-auth-rotation.json +0 -1
- package/test-vectors/catalog/positive/multicast-draft01-7-2-6-example.json +0 -1
- package/test-vectors/catalog/section-codes.json +8 -3
- package/test-vectors/catalog-multicast-auth-v1.json +0 -1
- package/test-vectors/catalog-validation.json +3 -3
- package/test-vectors/generate-bc-provenance-v2.mjs +94 -14
|
@@ -32,7 +32,7 @@ export interface MulticastEndpointInput {
|
|
|
32
32
|
readonly relayAddress?: string;
|
|
33
33
|
/** Aggregate bandwidth in bits per second. RECOMMENDED per draft §4.1; REQUIRED with `auth`. */
|
|
34
34
|
readonly bandwidth?: number;
|
|
35
|
-
/** Protected-flow parameters (draft -
|
|
35
|
+
/** Protected-flow parameters (draft -02 7.2.6); REQUIRED exactly when `MulticastInput.auth` is set. */
|
|
36
36
|
readonly auth?: EndpointAuthConfig;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
@@ -74,13 +74,13 @@ export type MulticastInputErrorKind = "empty_endpoints"
|
|
|
74
74
|
| "invalid_endpoints" | "ssm_requires_source"
|
|
75
75
|
/** Input `protocol` "asm" with a `sourceAddress`, which selects SSM. */
|
|
76
76
|
| "protocol_source_mismatch"
|
|
77
|
-
/** `MulticastInput.auth` fails draft -
|
|
77
|
+
/** `MulticastInput.auth` fails draft -02 7.2.6 (version, keys, a repeated kid or firstEpoch). */
|
|
78
78
|
| "invalid_multicast_auth"
|
|
79
79
|
/**
|
|
80
|
-
* An endpoint's `auth` fails draft -
|
|
80
|
+
* An endpoint's `auth` fails draft -02 7.2.6: present without
|
|
81
81
|
* `MulticastInput.auth` or absent with it, `senderAddress` on SSM or
|
|
82
|
-
* missing on ASM, a timer out of range
|
|
83
|
-
* `
|
|
82
|
+
* missing on ASM, a timer out of range or not an integer, a non-string
|
|
83
|
+
* `manifestTrack`, or no `bandwidth`.
|
|
84
84
|
*/
|
|
85
85
|
| "invalid_endpoint_auth"
|
|
86
86
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multicast-input.d.ts","sourceRoot":"","sources":["../src/multicast-input.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"multicast-input.d.ts","sourceRoot":"","sources":["../src/multicast-input.ts"],"names":[],"mappings":"AAoEA,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,sBAAsB,EAE3B,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAE3F;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,OAAO,CAAC;AAErE;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IACrD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,gGAAgG;IAChG,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,uGAAuG;IACvG,QAAQ,CAAC,IAAI,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,sBAAsB,CAAC,CAAC;IAC1D;;;;;;;OAOG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IACtD,kEAAkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE;QAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,YAAY,CAAC;QAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzF,kEAAkE;IAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,mBAAmB,CAAC;IACpC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,uBAAuB,GAChC,iBAAiB;AACnB;;;;;GAKG;GACD,mBAAmB,GACnB,qBAAqB;AACvB,wEAAwE;GACtE,0BAA0B;AAC5B,iGAAiG;GAC/F,wBAAwB;AAC1B;;;;;GAKG;GACD,uBAAuB;AACzB;;;;;;;GAOG;GACD,oBAAoB;AACtB;;;;;;GAMG;GACD,uBAAuB;AACzB;;;;;GAKG;GACD,uBAAuB;AACzB;;;GAGG;GACD,uBAAuB;AACzB;;;GAGG;GACD,wBAAwB,GACxB,uBAAuB,GACvB,wBAAwB,GACxB,cAAc,GACd,cAAc,GACd,oBAAoB,GACpB,mBAAmB;AACrB;;;;GAIG;GACD,qBAAqB,GACrB,qBAAqB,GACrB,0BAA0B,GAC1B,mBAAmB,GACnB,qBAAqB,GACrB,iBAAiB,GACjB,mBAAmB,CAAC;AAEvB,qBAAa,mBAAoB,SAAQ,KAAK;aAE5B,IAAI,EAAE,uBAAuB;aAC7B,aAAa,EAAE,MAAM,GAAG,IAAI;gBAD5B,IAAI,EAAE,uBAAuB,EAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5C,OAAO,EAAE,MAAM;CAKhB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,sBAAsB,CAuLjF;AA6PD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAC/B,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CACqB,GACzD,sBAAsB,GAAG,SAAS,CAgDpC"}
|
package/dist/multicast-input.js
CHANGED
|
@@ -15,13 +15,74 @@ var FEC_BLOCK_PROFILES = Object.freeze([
|
|
|
15
15
|
"iso-ssbg1-v1"
|
|
16
16
|
]);
|
|
17
17
|
|
|
18
|
+
// ../mmt-base/dist/ip-address.js
|
|
19
|
+
var DEC_OCTET = /^(?:0|[1-9]\d{0,2})$/;
|
|
20
|
+
var H16 = /^[0-9A-Fa-f]{1,4}$/;
|
|
21
|
+
function parseIpv4(text) {
|
|
22
|
+
const fields = text.split(".");
|
|
23
|
+
if (fields.length !== 4)
|
|
24
|
+
return void 0;
|
|
25
|
+
const octets = [];
|
|
26
|
+
for (const field of fields) {
|
|
27
|
+
if (!DEC_OCTET.test(field))
|
|
28
|
+
return void 0;
|
|
29
|
+
const value = Number(field);
|
|
30
|
+
if (value > 255)
|
|
31
|
+
return void 0;
|
|
32
|
+
octets.push(value);
|
|
33
|
+
}
|
|
34
|
+
return octets;
|
|
35
|
+
}
|
|
36
|
+
function parseIpv6(text) {
|
|
37
|
+
const halves = text.split("::");
|
|
38
|
+
if (halves.length > 2)
|
|
39
|
+
return void 0;
|
|
40
|
+
const groupsOf = (part) => part === "" ? [] : part.split(":");
|
|
41
|
+
const left = groupsOf(halves[0]);
|
|
42
|
+
const right = halves.length === 2 ? groupsOf(halves[1]) : [];
|
|
43
|
+
const words = [];
|
|
44
|
+
const all = [...left, ...right];
|
|
45
|
+
const last = halves.length === 2 && right.length === 0 ? -1 : all.length - 1;
|
|
46
|
+
for (let index = 0; index < all.length; index++) {
|
|
47
|
+
const group = all[index];
|
|
48
|
+
if (index === last && group.includes(".")) {
|
|
49
|
+
const v4 = parseIpv4(group);
|
|
50
|
+
if (!v4)
|
|
51
|
+
return void 0;
|
|
52
|
+
words.push([v4[0] << 8 | v4[1], v4[2] << 8 | v4[3]]);
|
|
53
|
+
} else if (H16.test(group)) {
|
|
54
|
+
words.push([Number.parseInt(group, 16)]);
|
|
55
|
+
} else {
|
|
56
|
+
return void 0;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const leftWords = words.slice(0, left.length).flat();
|
|
60
|
+
const rightWords = words.slice(left.length).flat();
|
|
61
|
+
const missing = 8 - leftWords.length - rightWords.length;
|
|
62
|
+
if (halves.length === 2 ? missing < 1 : missing !== 0)
|
|
63
|
+
return void 0;
|
|
64
|
+
const out = new Uint8Array(16);
|
|
65
|
+
[...leftWords, ...new Array(missing).fill(0), ...rightWords].forEach((word, index) => {
|
|
66
|
+
out[index * 2] = word >> 8;
|
|
67
|
+
out[index * 2 + 1] = word & 255;
|
|
68
|
+
});
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
function parseIpAddressLiteral(text) {
|
|
72
|
+
if (typeof text !== "string")
|
|
73
|
+
return void 0;
|
|
74
|
+
if (text.includes(":"))
|
|
75
|
+
return parseIpv6(text);
|
|
76
|
+
const v4 = parseIpv4(text);
|
|
77
|
+
return v4 ? Uint8Array.from(v4) : void 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
18
80
|
// ../mmt-base/dist/multicast-catalog.js
|
|
19
81
|
var ED25519_PUBLIC_KEY_BASE64URL = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
|
|
20
82
|
var UINT32_MAX = 4294967295;
|
|
21
83
|
var AUTH_MEMBERS = /* @__PURE__ */ new Set(["scheme", "version", "sig", "keys", "minEpoch"]);
|
|
22
84
|
var KEY_MEMBERS = /* @__PURE__ */ new Set(["kid", "publicKey", "firstEpoch"]);
|
|
23
85
|
var ENDPOINT_AUTH_MEMBERS = /* @__PURE__ */ new Set([
|
|
24
|
-
"manifestPort",
|
|
25
86
|
"streamId",
|
|
26
87
|
"senderAddress",
|
|
27
88
|
"maxAuthDelayMs",
|
|
@@ -31,17 +92,32 @@ var ENDPOINT_AUTH_MEMBERS = /* @__PURE__ */ new Set([
|
|
|
31
92
|
function isObject(value) {
|
|
32
93
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33
94
|
}
|
|
34
|
-
function isNonEmptyString(value) {
|
|
35
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
36
|
-
}
|
|
37
95
|
function isIntegerIn(value, min, max) {
|
|
38
96
|
return Number.isSafeInteger(value) && value >= min && value <= max;
|
|
39
97
|
}
|
|
98
|
+
var BCP_DEFAULT_MAX_AUTH_DELAY_MS = 200;
|
|
99
|
+
var BCP_DEFAULT_MANIFEST_TIMER_MS = 20;
|
|
100
|
+
function isFiniteNumber(value) {
|
|
101
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
102
|
+
}
|
|
103
|
+
function endpointAuthTiming(auth) {
|
|
104
|
+
const record = isObject(auth) ? auth : {};
|
|
105
|
+
const delay = record["maxAuthDelayMs"];
|
|
106
|
+
const timer = record["manifestTimerMs"];
|
|
107
|
+
const track = record["manifestTrack"];
|
|
108
|
+
const maxAuthDelayMs = !isFiniteNumber(delay) ? BCP_DEFAULT_MAX_AUTH_DELAY_MS : delay < 50 ? 50 : delay > 1e3 ? 1e3 : Number.isInteger(delay) ? delay : BCP_DEFAULT_MAX_AUTH_DELAY_MS;
|
|
109
|
+
const manifestTimerMs = !isFiniteNumber(timer) ? BCP_DEFAULT_MANIFEST_TIMER_MS : timer < 1 || Number.isSafeInteger(timer) ? timer : BCP_DEFAULT_MANIFEST_TIMER_MS;
|
|
110
|
+
return {
|
|
111
|
+
maxAuthDelayMs,
|
|
112
|
+
manifestTimerMs,
|
|
113
|
+
...typeof track === "string" && track.length > 0 ? { manifestTrack: track } : {}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
40
116
|
function multicastEndpointSource(endpoint) {
|
|
41
117
|
if (!isObject(endpoint))
|
|
42
118
|
return void 0;
|
|
43
119
|
const sourceAddress = endpoint["sourceAddress"];
|
|
44
|
-
if (sourceAddress !== void 0 && !
|
|
120
|
+
if (sourceAddress !== void 0 && !parseIpAddressLiteral(sourceAddress))
|
|
45
121
|
return void 0;
|
|
46
122
|
const mode = sourceAddress === void 0 ? "asm" : "ssm";
|
|
47
123
|
const protocol = endpoint["protocol"];
|
|
@@ -53,7 +129,7 @@ function multicastEndpointSource(endpoint) {
|
|
|
53
129
|
return senderAddress === void 0 ? { mode, source: sourceAddress } : void 0;
|
|
54
130
|
if (auth === void 0)
|
|
55
131
|
return { mode };
|
|
56
|
-
return
|
|
132
|
+
return parseIpAddressLiteral(senderAddress) ? { mode, source: senderAddress } : void 0;
|
|
57
133
|
}
|
|
58
134
|
function authIssues(auth) {
|
|
59
135
|
const base = "$.multicast.auth";
|
|
@@ -64,12 +140,12 @@ function authIssues(auth) {
|
|
|
64
140
|
return issue("", "auth must be an object");
|
|
65
141
|
const unknown = Object.keys(auth).filter((key) => !AUTH_MEMBERS.has(key));
|
|
66
142
|
if (unknown.length > 0) {
|
|
67
|
-
return issue("", `auth has unknown member(s) ${unknown.join(", ")}; draft -
|
|
143
|
+
return issue("", `auth has unknown member(s) ${unknown.join(", ")}; draft -02 7.2.6 defines scheme, version, sig, keys and minEpoch`);
|
|
68
144
|
}
|
|
69
145
|
if (auth["scheme"] !== "bc-provenance")
|
|
70
146
|
return issue(".scheme", 'scheme must be "bc-provenance"');
|
|
71
147
|
if (auth["version"] !== 2)
|
|
72
|
-
return issue(".version", "version must be 2 (bc-provenance version 2, draft -
|
|
148
|
+
return issue(".version", "version must be 2 (bc-provenance version 2, draft -02 7.2)");
|
|
73
149
|
if (auth["sig"] !== "ed25519")
|
|
74
150
|
return issue(".sig", 'sig must be "ed25519"');
|
|
75
151
|
const keys = auth["keys"];
|
|
@@ -112,26 +188,17 @@ function endpointAuthIssue(endpoint, mode, at) {
|
|
|
112
188
|
const unknown = Object.keys(auth).filter((key) => !ENDPOINT_AUTH_MEMBERS.has(key));
|
|
113
189
|
if (unknown.length > 0)
|
|
114
190
|
return { path: `${at}.auth`, message: `auth has unknown member(s) ${unknown.join(", ")}` };
|
|
115
|
-
if (!isIntegerIn(auth["manifestPort"], 1, 65535)) {
|
|
116
|
-
return { path: `${at}.auth.manifestPort`, message: "manifestPort must be an integer from 1 to 65535" };
|
|
117
|
-
}
|
|
118
191
|
if (!isIntegerIn(auth["streamId"], 0, UINT32_MAX)) {
|
|
119
192
|
return { path: `${at}.auth.streamId`, message: "streamId must be an unsigned 32-bit integer" };
|
|
120
193
|
}
|
|
121
194
|
if (mode === "ssm" && auth["senderAddress"] !== void 0) {
|
|
122
195
|
return { path: `${at}.auth.senderAddress`, message: "senderAddress must not appear on an SSM endpoint; S is sourceAddress" };
|
|
123
196
|
}
|
|
124
|
-
if (mode === "asm" && !
|
|
125
|
-
return { path: `${at}.auth.senderAddress`, message: "a protected ASM endpoint requires senderAddress" };
|
|
197
|
+
if (mode === "asm" && !parseIpAddressLiteral(auth["senderAddress"])) {
|
|
198
|
+
return { path: `${at}.auth.senderAddress`, message: "a protected ASM endpoint requires senderAddress, an RFC 3986 IP address literal" };
|
|
126
199
|
}
|
|
127
|
-
if (
|
|
128
|
-
return { path: `${at}.auth.
|
|
129
|
-
}
|
|
130
|
-
if (auth["manifestTimerMs"] !== void 0 && !isIntegerIn(auth["manifestTimerMs"], 1, Number.MAX_SAFE_INTEGER)) {
|
|
131
|
-
return { path: `${at}.auth.manifestTimerMs`, message: "manifestTimerMs must be an integer of at least 1" };
|
|
132
|
-
}
|
|
133
|
-
if (auth["manifestTrack"] !== void 0 && !isNonEmptyString(auth["manifestTrack"])) {
|
|
134
|
-
return { path: `${at}.auth.manifestTrack`, message: "manifestTrack must be a non-empty string" };
|
|
200
|
+
if (endpointAuthTiming(auth).manifestTimerMs < 1) {
|
|
201
|
+
return { path: `${at}.auth.manifestTimerMs`, message: "manifestTimerMs must be at least 1" };
|
|
135
202
|
}
|
|
136
203
|
if (!isIntegerIn(endpoint["bandwidth"], 1, Number.MAX_SAFE_INTEGER)) {
|
|
137
204
|
return { path: `${at}.bandwidth`, message: "a protected endpoint requires bandwidth, which bounds the receiver's hold queues" };
|
|
@@ -147,7 +214,7 @@ function multicastCatalogIssues(catalog, options = {}) {
|
|
|
147
214
|
issues.push({
|
|
148
215
|
code: "multicast_auth",
|
|
149
216
|
path: '$["multicast.auth"]',
|
|
150
|
-
message: 'the root "multicast.auth" key is retired; declare auth as multicast.auth (draft -
|
|
217
|
+
message: 'the root "multicast.auth" key is retired; declare auth as multicast.auth (draft -02 7.2.6)'
|
|
151
218
|
});
|
|
152
219
|
}
|
|
153
220
|
const multicast = catalog["multicast"];
|
|
@@ -181,7 +248,7 @@ function multicastCatalogIssues(catalog, options = {}) {
|
|
|
181
248
|
}
|
|
182
249
|
const protocol = endpoint["protocol"];
|
|
183
250
|
if (protocol !== void 0 && protocol !== "ssm" && protocol !== "asm") {
|
|
184
|
-
fail("multicast_endpoint", `${at}.protocol`, `protocol must be "ssm" or "asm"; a bearer such as ${JSON.stringify(protocol)} is a networkSource entry (draft -
|
|
251
|
+
fail("multicast_endpoint", `${at}.protocol`, `protocol must be "ssm" or "asm"; a bearer such as ${JSON.stringify(protocol)} is a networkSource entry (draft -02 4.2)`);
|
|
185
252
|
continue;
|
|
186
253
|
}
|
|
187
254
|
const hasAuth = endpoint["auth"] !== void 0;
|
|
@@ -190,8 +257,18 @@ function multicastCatalogIssues(catalog, options = {}) {
|
|
|
190
257
|
continue;
|
|
191
258
|
}
|
|
192
259
|
const sourceAddress = endpoint["sourceAddress"];
|
|
193
|
-
|
|
194
|
-
|
|
260
|
+
const sourceBytes = sourceAddress === void 0 ? void 0 : parseIpAddressLiteral(sourceAddress);
|
|
261
|
+
if (sourceAddress !== void 0 && !sourceBytes) {
|
|
262
|
+
fail("multicast_endpoint", `${at}.sourceAddress`, "sourceAddress must be an RFC 3986 IPv4address or IPv6address literal");
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
const groupBytes = parseIpAddressLiteral(endpoint["groupAddress"]);
|
|
266
|
+
if (!groupBytes) {
|
|
267
|
+
fail("multicast_endpoint", `${at}.groupAddress`, "groupAddress must be an RFC 3986 IPv4address or IPv6address literal");
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (sourceBytes && sourceBytes.length !== groupBytes.length) {
|
|
271
|
+
fail("multicast_endpoint", `${at}.sourceAddress`, "sourceAddress and groupAddress are of different address families");
|
|
195
272
|
continue;
|
|
196
273
|
}
|
|
197
274
|
const mode = sourceAddress === void 0 ? "asm" : "ssm";
|
|
@@ -210,7 +287,13 @@ function multicastCatalogIssues(catalog, options = {}) {
|
|
|
210
287
|
continue;
|
|
211
288
|
}
|
|
212
289
|
}
|
|
213
|
-
|
|
290
|
+
const source = multicastEndpointSource(endpoint);
|
|
291
|
+
const senderBytes = source?.mode === "asm" && source.source !== void 0 ? parseIpAddressLiteral(source.source) : void 0;
|
|
292
|
+
if (senderBytes && senderBytes.length !== groupBytes.length) {
|
|
293
|
+
fail("multicast_endpoint_auth", `${at}.auth.senderAddress`, "senderAddress and groupAddress are of different address families");
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
sources[index] = source;
|
|
214
297
|
const refs = Array.isArray(endpoint["tracks"]) ? endpoint["tracks"] : [];
|
|
215
298
|
for (let refIndex = 0; refIndex < refs.length; refIndex++) {
|
|
216
299
|
const ref = refs[refIndex];
|
|
@@ -226,38 +309,23 @@ function multicastCatalogIssues(catalog, options = {}) {
|
|
|
226
309
|
}
|
|
227
310
|
}
|
|
228
311
|
}
|
|
312
|
+
const addressKey = (text) => {
|
|
313
|
+
const bytes = text === void 0 ? void 0 : parseIpAddressLiteral(text);
|
|
314
|
+
return bytes ? `${bytes.length}:${bytes.join(".")}` : "-";
|
|
315
|
+
};
|
|
229
316
|
const tupleOf = (index) => {
|
|
230
317
|
const endpoint = endpoints[index];
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
sg: JSON.stringify([sources[index].source ?? null, endpoint["groupAddress"]]),
|
|
234
|
-
port: endpoint["port"],
|
|
235
|
-
manifestPort: isObject(auth) ? auth["manifestPort"] : void 0
|
|
236
|
-
};
|
|
318
|
+
return `${addressKey(sources[index].source)}|${addressKey(endpoint["groupAddress"])}|${endpoint["port"]}`;
|
|
237
319
|
};
|
|
238
320
|
for (let index = 0; index < endpoints.length; index++) {
|
|
239
321
|
if (!sources[index])
|
|
240
322
|
continue;
|
|
241
323
|
const own = tupleOf(index);
|
|
242
|
-
const at = `$.multicast.endpoints[${index}]`;
|
|
243
|
-
if (own.manifestPort !== void 0 && own.manifestPort === own.port) {
|
|
244
|
-
issues.push({ code: "multicast_endpoint_auth", path: `${at}.auth.manifestPort`, message: "manifestPort must differ from the endpoint's own port", endpointIndex: index });
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
324
|
for (let other = 0; other < endpoints.length; other++) {
|
|
248
|
-
if (other === index || !sources[other])
|
|
325
|
+
if (other === index || !sources[other] || tupleOf(other) !== own)
|
|
249
326
|
continue;
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
continue;
|
|
253
|
-
if (peer.port === own.port) {
|
|
254
|
-
issues.push({ code: "multicast_endpoint", path: `${at}.port`, message: `(S, G, port) is shared with endpoint ${other}; one endpoint object per multicast tuple`, endpointIndex: index });
|
|
255
|
-
break;
|
|
256
|
-
}
|
|
257
|
-
if (own.manifestPort !== void 0 && (own.manifestPort === peer.port || own.manifestPort === peer.manifestPort)) {
|
|
258
|
-
issues.push({ code: "multicast_endpoint_auth", path: `${at}.auth.manifestPort`, message: `manifestPort is a port or manifestPort of endpoint ${other} on the same (S, G)`, endpointIndex: index });
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
327
|
+
issues.push({ code: "multicast_endpoint", path: `$.multicast.endpoints[${index}].port`, message: `(S, G, port) is shared with endpoint ${other}; one endpoint object per multicast tuple`, endpointIndex: index });
|
|
328
|
+
break;
|
|
261
329
|
}
|
|
262
330
|
}
|
|
263
331
|
return issues;
|
|
@@ -357,6 +425,15 @@ function multicastFromConfig(input) {
|
|
|
357
425
|
const where = issue.endpointIndex === void 0 ? "" : `endpoint[${issue.endpointIndex}] `;
|
|
358
426
|
throw new MulticastInputError(kind, issue.endpointIndex ?? null, `${where}${issue.path.replace("$.multicast.", "")}: ${issue.message}`);
|
|
359
427
|
}
|
|
428
|
+
endpoints.forEach((ep, idx) => {
|
|
429
|
+
const auth = ep.auth;
|
|
430
|
+
if (auth === void 0) return;
|
|
431
|
+
const delay = auth["maxAuthDelayMs"];
|
|
432
|
+
const timer = auth["manifestTimerMs"];
|
|
433
|
+
const track = auth["manifestTrack"];
|
|
434
|
+
const bad = delay !== void 0 && !(Number.isSafeInteger(delay) && delay >= 50 && delay <= 1e3) ? "maxAuthDelayMs must be an integer from 50 to 1000" : timer !== void 0 && !(Number.isSafeInteger(timer) && timer >= 1) ? "manifestTimerMs must be an integer of at least 1" : track !== void 0 && !(typeof track === "string" && track.length > 0) ? "manifestTrack must be a non-empty string" : void 0;
|
|
435
|
+
if (bad) throw new MulticastInputError("invalid_endpoint_auth", idx, `endpoint[${idx}] auth: ${bad}`);
|
|
436
|
+
});
|
|
360
437
|
if (input.enabled !== void 0) out.enabled = input.enabled;
|
|
361
438
|
if (input.version !== void 0) out.version = input.version;
|
|
362
439
|
if (input.validUntil !== void 0) out.validUntil = input.validUntil;
|