Relay a parsed PDU's TLVs as they are, refuse only a tagId that disagrees with its key, and refuse a number for an octet TLV
Mirror / push (push) Has been cancelled
Test / lint (pull_request) Successful in 22s
Test / test (18) (pull_request) Successful in 31s
Test / test (22) (pull_request) Successful in 39s
Test / test (24) (pull_request) Successful in 31s
Test / test (26) (pull_request) Successful in 31s
Test / test (20) (pull_request) Successful in 36s
Mirror / push (push) Has been cancelled
Test / lint (pull_request) Successful in 22s
Test / test (18) (pull_request) Successful in 31s
Test / test (22) (pull_request) Successful in 39s
Test / test (24) (pull_request) Successful in 31s
Test / test (26) (pull_request) Successful in 31s
Test / test (20) (pull_request) Successful in 36s
This commit is contained in:
+5
-3
@@ -63,9 +63,11 @@
|
|||||||
type for its tag fails to compile. Annotate with `Tlvs` or `TlvInputs` where you wrote
|
type for its tag fails to compile. Annotate with `Tlvs` or `TlvInputs` where you wrote
|
||||||
`Record<string, Tlv>` or `Record<string, TlvInput>`; `TlvInput` is gone.
|
`Record<string, Tlv>` or `Record<string, TlvInput>`; `TlvInput` is gone.
|
||||||
|
|
||||||
**A TLV input is keyed by its name, or by its decimal id where the table names none, and `tagId`
|
**A TLV input is keyed by its name, or by its decimal id where the table names none, and a `tagId`
|
||||||
is refused.** Write `{ 5142: { tagValue } }` for a vendor tag, not `{ vendor: { tagId: 5142, … } }`.
|
that disagrees with its key is refused.** Write `{ 5142: { tagValue } }` for a vendor tag, not
|
||||||
A name and a `tagId` could disagree, and `{ message_state: { tagId: 5, … } }` went out as tag 5.
|
`{ vendor: { tagId: 5142, … } }`; `{ message_state: { tagId: 5, … } }` used to go out as tag 5. A
|
||||||
|
parsed PDU's `tlvs` still relay as they are. A number for an octet TLV, vendor tags included, is
|
||||||
|
refused, where it went out as its ASCII digits.
|
||||||
A decimal key naming a tag the table knows, `{ 1063: … }`, is refused in favour of the name, and
|
A decimal key naming a tag the table knows, `{ 1063: … }`, is refused in favour of the name, and
|
||||||
so are `alert_on_msg_delivery` and `failed_broadcast_area_identifier` in favour of
|
so are `alert_on_msg_delivery` and `failed_broadcast_area_identifier` in favour of
|
||||||
`alert_on_message_delivery` and `broadcast_area_identifier`, the names they read back under.
|
`alert_on_message_delivery` and `broadcast_area_identifier`, the names they read back under.
|
||||||
|
|||||||
+3
-2
@@ -30,8 +30,9 @@ shape is the same, connect, send, listen for delivery reports, with callbacks re
|
|||||||
`consts.MESSAGING_MODE`**, which also names `SMSC_DEFAULT`. They are bits 1-0 of `esm_class`, not
|
`consts.MESSAGING_MODE`**, which also names `SMSC_DEFAULT`. They are bits 1-0 of `esm_class`, not
|
||||||
whole values of it. Read them from the new group, or pass `messagingMode` to `sendSms()`. A stale
|
whole values of it. Read them from the new group, or pass `messagingMode` to `sendSms()`. A stale
|
||||||
`consts.ESM_CLASS.STORE_FORWARD` reads `undefined`, which OR-s into an `esm_class` carrying no mode.
|
`consts.ESM_CLASS.STORE_FORWARD` reads `undefined`, which OR-s into an `esm_class` carrying no mode.
|
||||||
- **A TLV is keyed by its name, or by its decimal id where the table names none**, and `tagId` on
|
- **A TLV is keyed by its name, or by its decimal id where the table names none**, and a `tagId`
|
||||||
an input is refused: `{ 5142: { tagValue } }`, not `{ vendor: { tagId: 5142, tagValue } }`.
|
disagreeing with its key is refused: `{ 5142: { tagValue } }`, not
|
||||||
|
`{ vendor: { tagId: 5142, tagValue } }`. A number for an octet TLV is refused; give a Buffer.
|
||||||
Write `alert_on_message_delivery` and `broadcast_area_identifier`, the names they read back
|
Write `alert_on_message_delivery` and `broadcast_area_identifier`, the names they read back
|
||||||
under, for `alert_on_msg_delivery` and `failed_broadcast_area_identifier`.
|
under, for `alert_on_msg_delivery` and `failed_broadcast_area_identifier`.
|
||||||
- **The `error` event is `sessionError`**, and `serverError` on the server handle.
|
- **The `error` event is `sessionError`**, and `serverError` on the server handle.
|
||||||
|
|||||||
@@ -630,9 +630,10 @@ if (isCommand(pduObj, 'submit_sm')) {
|
|||||||
naming the character, its code point and where it is.
|
naming the character, its code point and where it is.
|
||||||
- Every text field is latin1: addresses, `system_id`, `message_id`, `service_type` and the C-Octet
|
- Every text field is latin1: addresses, `system_id`, `message_id`, `service_type` and the C-Octet
|
||||||
String TLVs. A character past `U+00FF` is refused, as is a `U+0000` in a C-Octet String.
|
String TLVs. A character past `U+00FF` is refused, as is a `U+0000` in a C-Octet String.
|
||||||
- `tlvs` is keyed and typed like `pduObj.tlvs`, as `TlvInputs`: `{ message_state: { tagValue: 2 } }`,
|
- `tlvs` is keyed and typed like `pduObj.tlvs`, as `TlvInputs`, so a parsed PDU's `tlvs` relay as
|
||||||
or `{ 5142: { tagValue: octets } }` for a tag the table does not define. Any other key, a decimal
|
they are: `{ message_state: { tagValue: 2 } }`, or `{ 5142: { tagValue: octets } }` for a tag the
|
||||||
id the table names, and a `tagId` are refused.
|
table does not define. Any other key, a decimal id the table names, a `tagId` disagreeing with its
|
||||||
|
key, and a number for an octet TLV are refused.
|
||||||
- The five repeatable TLVs take an array, written as one TLV per element; a lone value or an empty
|
- The five repeatable TLVs take an array, written as one TLV per element; a lone value or an empty
|
||||||
array is refused.
|
array is refused.
|
||||||
- A `Buffer` goes out exactly as given under any `data_coding`: binary payloads, hand-built user
|
- A `Buffer` goes out exactly as given under any `data_coding`: binary payloads, hand-built user
|
||||||
|
|||||||
+27
-16
@@ -106,7 +106,7 @@ type WireValue<K extends TlvName> = Specs[K]['type']['default'];
|
|||||||
|
|
||||||
type ReadValue<K extends TlvName> = Repeated<K, WireValue<K>>;
|
type ReadValue<K extends TlvName> = Repeated<K, WireValue<K>>;
|
||||||
|
|
||||||
/** A lone text field also takes a number, and a lone octet field text, which goes out as latin1. */
|
/** A lone text field also takes a number, and an octet field text, which goes out as latin1. */
|
||||||
type WriteValue<K extends TlvName> = Specs[K] extends { multiple: true } ? ReadValue<K>
|
type WriteValue<K extends TlvName> = Specs[K] extends { multiple: true } ? ReadValue<K>
|
||||||
: WireValue<K> extends number ? number
|
: WireValue<K> extends number ? number
|
||||||
: WireValue<K> extends string ? number | string
|
: WireValue<K> extends string ? number | string
|
||||||
@@ -118,13 +118,13 @@ type KnownTlv<K extends TlvName> = { tagId: number; tagName: K; tagValue: ReadVa
|
|||||||
type UnknownTlv = { tagId: number; tagName: undefined; tagValue: Buffer };
|
type UnknownTlv = { tagId: number; tagName: undefined; tagValue: Buffer };
|
||||||
|
|
||||||
/** Keyed by tag name, or by its decimal id where the table defines no name. */
|
/** Keyed by tag name, or by its decimal id where the table defines no name. */
|
||||||
export type Tlvs = { [K in TlvName]?: KnownTlv<K> } & Record<`${number}`, UnknownTlv>;
|
export type Tlvs = { [K in TlvName]?: KnownTlv<K> } & Partial<Record<`${number}`, UnknownTlv>>;
|
||||||
|
|
||||||
export type Tlv = { [K in TlvName]: KnownTlv<K> }[TlvName] | UnknownTlv;
|
export type Tlv = { [K in TlvName]: KnownTlv<K> }[TlvName] | UnknownTlv;
|
||||||
|
|
||||||
/** Keyed like `Tlvs`, by tag name or by the decimal id of a tag the table does not define. */
|
/** Keyed like `Tlvs`, by tag name or by the decimal id of a tag the table does not define. */
|
||||||
export type TlvInputs = { [K in TlvName]?: { tagValue: WriteValue<K> } }
|
export type TlvInputs = { [K in TlvName]?: { tagValue: WriteValue<K> } }
|
||||||
& Record<`${number}`, { tagValue: Buffer | number | string }>;
|
& Partial<Record<`${number}`, { tagValue: Buffer | string }>>;
|
||||||
|
|
||||||
function isTlvInput(input: unknown): input is { tagValue: TlvValue } {
|
function isTlvInput(input: unknown): input is { tagValue: TlvValue } {
|
||||||
if (typeof input !== 'object' || input === null || !('tagValue' in input)) return false;
|
if (typeof input !== 'object' || input === null || !('tagValue' in input)) return false;
|
||||||
@@ -136,16 +136,8 @@ function isTlvInput(input: unknown): input is { tagValue: TlvValue } {
|
|||||||
return value.every(one => Buffer.isBuffer(one)) || value.every(one => typeof one === 'number');
|
return value.every(one => Buffer.isBuffer(one)) || value.every(one => typeof one === 'number');
|
||||||
}
|
}
|
||||||
|
|
||||||
function entryOf(name: string, input: unknown): Result<{ tagId: number; tagValue: TlvValue }> {
|
function keyedTagId(name: string): Result<{ tagId: number }> {
|
||||||
if (!isTlvInput(input)) {
|
if (isTlvName(name)) return { tagId: specs[name].id };
|
||||||
return { err: new Error(`TLV "${name}": give it as { tagValue }, holding a Buffer, a number, a string, or an array of Buffers or of numbers`) };
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('tagId' in input) {
|
|
||||||
return { err: new Error(`TLV "${name}": key it by its name, or a tag the table does not define by its decimal id, instead of giving a tagId`) };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isTlvName(name)) return { tagId: specs[name].id, tagValue: input.tagValue };
|
|
||||||
|
|
||||||
const alternate = Object.hasOwn(tlvs, name) ? tlvs[name] : undefined;
|
const alternate = Object.hasOwn(tlvs, name) ? tlvs[name] : undefined;
|
||||||
|
|
||||||
@@ -161,9 +153,24 @@ function entryOf(name: string, input: unknown): Result<{ tagId: number; tagValue
|
|||||||
|
|
||||||
const known = tlvsById[tagId];
|
const known = tlvsById[tagId];
|
||||||
|
|
||||||
return known
|
return known ? { err: new Error(`TLV "${name}": the table names this tag ${known.tag}, key it by that`) } : { tagId };
|
||||||
? { err: new Error(`TLV "${name}": the table names this tag ${known.tag}, key it by that`) }
|
}
|
||||||
: { tagId, tagValue: input.tagValue };
|
|
||||||
|
/** The key names the tag; a `tagId` beside it, as a parsed TLV carries, has to agree. */
|
||||||
|
function entryOf(name: string, input: unknown): Result<{ tagId: number; tagValue: TlvValue }> {
|
||||||
|
if (!isTlvInput(input)) {
|
||||||
|
return { err: new Error(`TLV "${name}": give it as { tagValue }, holding a Buffer, a number, a string, or an array of Buffers or of numbers`) };
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyed = keyedTagId(name);
|
||||||
|
|
||||||
|
if (keyed.err) return { err: keyed.err };
|
||||||
|
|
||||||
|
if ('tagId' in input && input.tagId !== keyed.tagId) {
|
||||||
|
return { err: new Error(`TLV "${name}": tagId ${String(input.tagId)} is not the tag its key names, ${String(keyed.tagId)}; drop the tagId`) };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { tagId: keyed.tagId, tagValue: input.tagValue };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Each TLV as its four octet header and the value the tag's own wire type writes. */
|
/** Each TLV as its four octet header and the value the tag's own wire type writes. */
|
||||||
@@ -205,6 +212,10 @@ function occurrences(value: TlvValue, multiple: boolean): Result<{ values: Param
|
|||||||
}
|
}
|
||||||
|
|
||||||
function writeTlv(tagId: number, type: WireType, value: ParamValue): Result<{ chunk: Buffer }> {
|
function writeTlv(tagId: number, type: WireType, value: ParamValue): Result<{ chunk: Buffer }> {
|
||||||
|
if (type === tlvDefault && typeof value === 'number') {
|
||||||
|
return { err: new Error('holds octets, which a number would write as its digits; give a Buffer or a string') };
|
||||||
|
}
|
||||||
|
|
||||||
const sized = type.size(value);
|
const sized = type.size(value);
|
||||||
|
|
||||||
if (sized.err) return { err: sized.err };
|
if (sized.err) return { err: sized.err };
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ export function retainedOctets(pduObj: PduObject): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const tlv of Object.values(pduObj.tlvs)) {
|
for (const tlv of Object.values(pduObj.tlvs)) {
|
||||||
|
if (tlv === undefined) continue;
|
||||||
|
|
||||||
const listed = Array.isArray(tlv.tagValue) ? tlv.tagValue.length : 0;
|
const listed = Array.isArray(tlv.tagValue) ? tlv.tagValue.length : 0;
|
||||||
|
|
||||||
octets += tlvOctets(tlv.tagValue) + (1 + listed) * tlvObjectOverhead;
|
octets += tlvOctets(tlv.tagValue) + (1 + listed) * tlvObjectOverhead;
|
||||||
|
|||||||
+19
-1
@@ -443,7 +443,10 @@ describe('TLVs', () => {
|
|||||||
const refusals = [
|
const refusals = [
|
||||||
// @ts-expect-error nils is no tag name
|
// @ts-expect-error nils is no tag name
|
||||||
{ built: objToPdu({ cmdName: 'deliver_sm', params, tlvs: { nils: { tagValue: 'blajfoo' } } }), reason: /decimal id/ },
|
{ built: objToPdu({ cmdName: 'deliver_sm', params, tlvs: { nils: { tagValue: 'blajfoo' } } }), reason: /decimal id/ },
|
||||||
{ built: objToPdu({ cmdName: 'deliver_sm', params, tlvs: { 5142: { tagId: 5142, tagValue: 'blajfoo' } } }), reason: /instead of giving a tagId/ },
|
{ built: objToPdu({ cmdName: 'deliver_sm', params, tlvs: { 5142: { tagId: 5143, tagValue: 'blajfoo' } } }), reason: /tagId 5143/ },
|
||||||
|
// @ts-expect-error the key names the tag
|
||||||
|
{ built: objToPdu({ cmdName: 'deliver_sm', params, tlvs: { message_state: { tagId: 5, tagValue: 2 } } }), reason: /tagId 5/ },
|
||||||
|
{ built: objToPdu({ cmdName: 'deliver_sm', params, tlvs: { 5142: { tagValue: 300 } } }), reason: /Buffer/ },
|
||||||
{ built: objToPdu({ cmdName: 'deliver_sm', params, tlvs: { 65536: { tagValue: 'blajfoo' } } }), reason: /out of range/ },
|
{ built: objToPdu({ cmdName: 'deliver_sm', params, tlvs: { 65536: { tagValue: 'blajfoo' } } }), reason: /out of range/ },
|
||||||
{ built: objToPdu({ cmdName: 'deliver_sm', params, tlvs: { '05142': { tagValue: 'blajfoo' } } }), reason: /decimal id/ },
|
{ built: objToPdu({ cmdName: 'deliver_sm', params, tlvs: { '05142': { tagValue: 'blajfoo' } } }), reason: /decimal id/ },
|
||||||
{ built: objToPdu({ cmdName: 'deliver_sm', params, tlvs: { 1063: { tagValue: 2 } } }), reason: /message_state/ },
|
{ built: objToPdu({ cmdName: 'deliver_sm', params, tlvs: { 1063: { tagValue: 2 } } }), reason: /message_state/ },
|
||||||
@@ -537,6 +540,21 @@ describe('TLVs', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('relays a parsed PDU\'s TLVs back out as they arrived', () => {
|
||||||
|
const params = { destination_addr: '46709771337', short_message: 'hi', source_addr: '46701113311' };
|
||||||
|
const parsed = decode(encode({
|
||||||
|
cmdName: 'deliver_sm',
|
||||||
|
params,
|
||||||
|
tlvs: {
|
||||||
|
5142: { tagValue: Buffer.from('01', 'hex') },
|
||||||
|
callback_num: { tagValue: [Buffer.from('0146709771337', 'hex')] },
|
||||||
|
receipted_message_id: { tagValue: '0199d8a4-5e2c-7b3f-9a61-c4e07f2d8b15' },
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
assert.deepEqual(decode(encode({ cmdName: 'deliver_sm', params, tlvs: parsed.tlvs })).tlvs, parsed.tlvs);
|
||||||
|
});
|
||||||
|
|
||||||
test('types each known TLV by its tag, and an unknown one as octets', () => {
|
test('types each known TLV by its tag, and an unknown one as octets', () => {
|
||||||
const pduObj = decode(encode({
|
const pduObj = decode(encode({
|
||||||
cmdName: 'deliver_sm',
|
cmdName: 'deliver_sm',
|
||||||
|
|||||||
Reference in New Issue
Block a user