Files
smpp-js/src/pdu-refusal.ts
T
lilleman 45d2f5548e Read a delivery receipt's body as the octets that arrived (#81)
* Regression tests for a receipt body read under a data_coding it is not written in

* Read a delivery receipt's body as the octets that arrived, not by its data_coding

* Assert SMPPSim's UCS2 receipt body parses like every other segment's

* Record the receipt-body defect as fixed

* Retain a multipart segment's octets once, not twice

* Sort the new type import into its file's order
2026-09-05 22:37:05 +02:00

58 lines
2.0 KiB
TypeScript

import type { CommandName } from './defs/commands.ts';
import type { ErrorName } from './defs/errors.ts';
import { respNameFor } from './defs/commands.ts';
/** A hostile peer must not be able to make us allocate arbitrarily. */
export const maxPduLength = 1024 * 1024;
/** The 16 octets a framed PDU always has, whatever its body turns out to be. */
export type PduHeader = {
cmdId: number;
cmdLength: number;
cmdName: CommandName | undefined;
cmdStatusId: number;
seqNr: number;
};
/** Which part of a PDU the codec could not read. */
export type PduRefusalReason = 'body' | 'command' | 'tlvs';
/** A PDU refused with the stream still in sync, so only this one PDU is lost. */
export class PduRefusedError extends Error {
readonly header: PduHeader;
readonly reason: PduRefusalReason;
constructor(header: PduHeader, reason: PduRefusalReason, cause: Error) {
const named = header.cmdName ?? `command id ${String(header.cmdId)}`;
super(`Refused ${named} with seqNr ${String(header.seqNr)}: ${cause.message}`, { cause });
this.header = header;
this.name = 'PduRefusedError';
this.reason = reason;
}
}
// ESME_RINVTLVSTREAM is SMPP 5.0's name for 0xC0, which SMPP 3.4 spells ESME_RINVOPTPARSTREAM.
const refusalStatus = {
body: 'ESME_RINVCMDLEN',
command: 'ESME_RINVCMDID',
tlvs: 'ESME_RINVTLVSTREAM',
} as const satisfies Record<PduRefusalReason, ErrorName>;
/** Why a command_length cannot frame a stream: past it nothing can say where the next PDU starts. */
export function framingRefusal(cmdLength: number): Error | undefined {
if (cmdLength < 16 || cmdLength > maxPduLength) {
return new Error(`Refusing a cmd_length of ${String(cmdLength)}`);
}
return undefined;
}
/** SMPP 3.4 4.3: a PDU whose command has no response of its own is refused with generic_nack. */
export function refusalAnswer(refused: PduRefusedError): { cmdName: CommandName; cmdStatus: ErrorName } {
return {
cmdName: respNameFor(refused.header.cmdName) ?? 'generic_nack',
cmdStatus: refusalStatus[refused.reason],
};
}