milter 1.0.0 → 3.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/frames.ts","../src/context.ts","../src/decisions.ts","../src/dkim.ts","../src/logger.ts","../src/server.ts","../src/spf.ts","../src/utils.ts"],"sourcesContent":["export {\n ACTION_ALL,\n DISABLE_ALL_CALLBACKS,\n SMFIA,\n SMFIC,\n SMFIF,\n SMFI_VERSION,\n SMFIP,\n SMFIR,\n} from './constants.js';\n\nexport { MilterActionError, MilterContext } from './context.js';\nexport { Decision, normalizeDecision } from './decisions.js';\nexport { signDkim, verifyDkim } from './dkim.js';\nexport { frame, send } from './frames.js';\nexport { ConsoleLogger } from './logger.js';\nexport { Milter, MilterServer } from './server.js';\nexport { checkSpf, spfDecision } from './spf.js';\nexport {\n cloneHeaders, parseConnectInfo, readCString, readCStringList, readCStringPairs,\n} from './utils.js';\n\nexport type {\n ConnectInfo,\n HeaderMap,\n Logger,\n MaybePromise,\n MilterDecision,\n MilterDecisionKeyword,\n MilterEventMap,\n MilterEventName,\n MilterOptions,\n MilterPhase,\n MilterActionFlag,\n MilterReply,\n} from './types.js';\n\nexport type {\n DkimSignOptions,\n DkimSignResult,\n DkimVerifyOptions,\n DkimVerifyResult,\n} from './dkim.js';\n\nexport type {\n SpfOptions,\n SpfPolicy,\n SpfResult,\n SpfResultCode,\n} from './spf.js';\n\nexport { MilterServer as default } from './server.js';\n","export const SMFI_VERSION = 6;\n\nexport const SMFIA = {\n UNKNOWN: 'U',\n UNIX: 'L',\n INET: '4',\n INET6: '6',\n} as const;\n\nexport const SMFIC = {\n CONNECT: 'C',\n OPTNEG: 'O',\n HELO: 'H',\n HEADER: 'L',\n EOH: 'N',\n BODY: 'B',\n BODYEOB: 'E',\n MAIL: 'M',\n RCPT: 'R',\n ABORT: 'A',\n MACRO: 'D',\n QUIT: 'Q',\n DATA: 'T',\n UNKNOWN: 'U',\n} as const;\n\nexport const SMFIR = {\n ACCEPT: 'a',\n CONTINUE: 'c',\n DISCARD: 'd',\n REJECT: 'r',\n TEMPFAIL: 't',\n REPLYCODE: 'y',\n ADDRCPT: '+',\n DELRCPT: '-',\n REPLBODY: 'b',\n ADDHEADER: 'h',\n INSHEADER: 'i',\n CHGHEADER: 'm',\n PROGRESS: 'p',\n QUARANTINE: 'q',\n SETSENDER: 's',\n} as const;\n\nexport const SMFIF = {\n ADDHDRS: 0x01,\n CHGBODY: 0x02,\n ADDRCPT: 0x04,\n DELRCPT: 0x08,\n CHGHDRS: 0x10,\n QUARANTINE: 0x20,\n SETSENDER: 0x40,\n} as const;\n\nexport const ACTION_ALL\n = SMFIF.ADDHDRS\n | SMFIF.CHGBODY\n | SMFIF.ADDRCPT\n | SMFIF.DELRCPT\n | SMFIF.CHGHDRS\n | SMFIF.QUARANTINE\n | SMFIF.SETSENDER;\n\nexport const SMFIP = {\n NOCONNECT: 0x00000001,\n NOHELO: 0x00000002,\n NOMAIL: 0x00000004,\n NORCPT: 0x00000008,\n NOBODY: 0x00000010,\n NOHDRS: 0x00000020,\n NOEOH: 0x00000040,\n NONE: 0x0000007f,\n} as const;\n\nexport const DISABLE_ALL_CALLBACKS\n = SMFIP.NOCONNECT\n | SMFIP.NOHELO\n | SMFIP.NOMAIL\n | SMFIP.NORCPT\n | SMFIP.NOBODY\n | SMFIP.NOHDRS\n | SMFIP.NOEOH;\n","import type net from 'node:net';\n\nexport function frame(command: string, data: Buffer = Buffer.alloc(0)): Buffer {\n\n const len = Buffer.alloc(4);\n\n len.writeUInt32BE(data.length + 1, 0);\n return Buffer.concat([len, Buffer.from(command, 'ascii'), data]);\n}\n\nexport function send(socket: net.Socket | null, command: string, data?: Buffer): void {\n\n if (!socket) {\n return;\n }\n\n socket.write(frame(command, data ?? Buffer.alloc(0)));\n}\n","import type net from 'node:net';\n\nimport { SMFIF, SMFIR } from './constants.js';\nimport type { DkimVerifyResult } from './dkim.js';\nimport { send } from './frames.js';\nimport type { SpfResult } from './spf.js';\nimport type { ConnectInfo, MilterActionFlag, MilterPhase } from './types.js';\n\nexport class MilterActionError extends Error {\n\n readonly code: 'E_MILTER_ACTION_STAGE' | 'E_MILTER_ACTION_CAPABILITY';\n\n constructor(code: 'E_MILTER_ACTION_STAGE' | 'E_MILTER_ACTION_CAPABILITY', message: string) {\n\n super(message);\n this.name = 'MilterActionError';\n this.code = code;\n }\n}\n\nconst EOM_ONLY_PHASES = new Set<MilterPhase>(['bodyEnd']);\n\nexport class MilterContext {\n\n readonly id: number;\n private readonly collectBody: boolean;\n private readonly maxBodyBytes: number;\n private readonly enforceActionStages: boolean;\n private chunks: Buffer[] = [];\n private bodyBytes = 0;\n private phase: MilterPhase = 'connect';\n private negotiatedActions = 0;\n socket: net.Socket | null;\n headers: Record<string, string[]> = {};\n headerLines: Array<[string, string]> = [];\n macros: Record<string, Record<string, string>> = {};\n connection: ConnectInfo | null = null;\n helo: string | null = null;\n dkim: DkimVerifyResult | null = null;\n spf: SpfResult | null = null;\n from: string[] | null = null;\n to: string[] | null = null;\n\n constructor(\n id: number,\n socket: net.Socket,\n collectBody: boolean,\n maxBodyBytes: number,\n enforceActionStages: boolean,\n ) {\n\n this.id = id;\n this.socket = socket;\n this.collectBody = collectBody;\n this.maxBodyBytes = maxBodyBytes;\n this.enforceActionStages = enforceActionStages;\n }\n\n setPhase(phase: MilterPhase): void {\n\n this.phase = phase;\n }\n\n getPhase(): MilterPhase {\n\n return this.phase;\n }\n\n setNegotiatedActions(actions: number): void {\n\n this.negotiatedActions = actions >>> 0;\n }\n\n can(action: MilterActionFlag): boolean {\n\n return (this.negotiatedActions & action) === action;\n }\n\n appendHeader(name: string, value: string): void {\n\n const key = name.toLowerCase();\n\n this.headerLines.push([name, value]);\n if (!this.headers[key]) {\n this.headers[key] = [];\n }\n this.headers[key].push(value);\n }\n\n appendBody(chunk: Buffer): void {\n\n if (!this.collectBody) {\n return;\n }\n\n this.bodyBytes += chunk.length;\n if (this.bodyBytes > this.maxBodyBytes) {\n throw new Error(\n `Body limit exceeded for connection ${this.id}: ${this.bodyBytes} > ${this.maxBodyBytes}`,\n );\n }\n\n this.chunks.push(Buffer.from(chunk));\n }\n\n getBody(): Buffer {\n\n return Buffer.concat(this.chunks);\n }\n\n resetMessage(): void {\n\n this.headers = {};\n this.headerLines = [];\n this.macros = {};\n this.dkim = null;\n this.spf = null;\n this.from = null;\n this.to = null;\n this.chunks = [];\n this.bodyBytes = 0;\n this.phase = 'connect';\n }\n\n private requireAction(action: MilterActionFlag, actionName: string, phases?: ReadonlySet<MilterPhase>): void {\n\n if ((this.negotiatedActions & action) !== action) {\n throw new MilterActionError(\n 'E_MILTER_ACTION_CAPABILITY',\n `${actionName} not permitted by negotiated action flags`,\n );\n }\n\n if (this.enforceActionStages && phases && !phases.has(this.phase)) {\n throw new MilterActionError(\n 'E_MILTER_ACTION_STAGE',\n `${actionName} is not allowed during phase '${this.phase}'`,\n );\n }\n }\n\n send(command: string, data?: Buffer): void {\n\n send(this.socket, command, data);\n }\n\n continue(): void {\n\n this.send(SMFIR.CONTINUE);\n }\n\n accept(): void {\n\n this.send(SMFIR.ACCEPT);\n }\n\n reject(): void {\n\n this.send(SMFIR.REJECT);\n }\n\n discard(): void {\n\n this.send(SMFIR.DISCARD);\n }\n\n tempfail(): void {\n\n this.send(SMFIR.TEMPFAIL);\n }\n\n progress(): void {\n\n this.send(SMFIR.PROGRESS);\n }\n\n addHeader(name: string, value: string): void {\n\n this.requireAction(SMFIF.ADDHDRS, 'addHeader', EOM_ONLY_PHASES);\n this.send(SMFIR.ADDHEADER, Buffer.from(`${name}\\0${value}\\0`, 'utf8'));\n }\n\n insertHeader(index: number, name: string, value: string): void {\n\n this.requireAction(SMFIF.ADDHDRS, 'insertHeader', EOM_ONLY_PHASES);\n\n const suffix = Buffer.from(`${name}\\0${value}\\0`, 'utf8');\n const data = Buffer.alloc(4 + suffix.length);\n\n data.writeUInt32BE(index >>> 0, 0);\n suffix.copy(data, 4);\n this.send(SMFIR.INSHEADER, data);\n }\n\n changeHeader(name: string, index: number, value = ''): void {\n\n this.requireAction(SMFIF.CHGHDRS, 'changeHeader', EOM_ONLY_PHASES);\n\n const suffix = Buffer.from(`${name}\\0${value}\\0`, 'utf8');\n const data = Buffer.alloc(4 + suffix.length);\n\n data.writeUInt32BE(index >>> 0, 0);\n suffix.copy(data, 4);\n this.send(SMFIR.CHGHEADER, data);\n }\n\n addRecipient(address: string): void {\n\n this.requireAction(SMFIF.ADDRCPT, 'addRecipient', EOM_ONLY_PHASES);\n this.send(SMFIR.ADDRCPT, Buffer.from(`${address}\\0`, 'utf8'));\n }\n\n deleteRecipient(address: string): void {\n\n this.requireAction(SMFIF.DELRCPT, 'deleteRecipient', EOM_ONLY_PHASES);\n this.send(SMFIR.DELRCPT, Buffer.from(`${address}\\0`, 'utf8'));\n }\n\n replaceBody(chunk: Buffer | string): void {\n\n this.requireAction(SMFIF.CHGBODY, 'replaceBody', EOM_ONLY_PHASES);\n\n const payload = typeof chunk === 'string' ? Buffer.from(chunk, 'utf8') : chunk;\n\n this.send(SMFIR.REPLBODY, payload);\n }\n\n quarantine(reason = ''): void {\n\n this.requireAction(SMFIF.QUARANTINE, 'quarantine', EOM_ONLY_PHASES);\n this.send(SMFIR.QUARANTINE, Buffer.from(`${reason}\\0`, 'utf8'));\n }\n\n setSender(sender: string): void {\n\n this.requireAction(SMFIF.SETSENDER, 'setSender', EOM_ONLY_PHASES);\n this.send(SMFIR.SETSENDER, Buffer.from(`${sender}\\0`, 'utf8'));\n }\n\n replyCode(code: string, message: string, enhancedCode = '5.7.1'): void {\n\n this.send(SMFIR.REPLYCODE, Buffer.from(`${code} ${enhancedCode} ${message}\\0`, 'utf8'));\n }\n}\n","import { SMFIR } from './constants.js';\nimport type { MilterDecision, MilterDecisionKeyword, MilterReply } from './types.js';\n\nconst DECISION_TO_COMMAND: Record<MilterDecisionKeyword, string> = {\n continue: SMFIR.CONTINUE,\n accept: SMFIR.ACCEPT,\n reject: SMFIR.REJECT,\n discard: SMFIR.DISCARD,\n tempfail: SMFIR.TEMPFAIL,\n};\n\nexport const Decision = {\n continue(): MilterReply {\n\n return { command: SMFIR.CONTINUE };\n },\n accept(): MilterReply {\n\n return { command: SMFIR.ACCEPT };\n },\n reject(): MilterReply {\n\n return { command: SMFIR.REJECT };\n },\n discard(): MilterReply {\n\n return { command: SMFIR.DISCARD };\n },\n tempfail(): MilterReply {\n\n return { command: SMFIR.TEMPFAIL };\n },\n replyCode(code: string, message: string, enhancedCode = '5.7.1'): MilterReply {\n\n const payload = Buffer.from(`${code} ${enhancedCode} ${message}\\0`, 'utf8');\n\n return {\n command: SMFIR.REPLYCODE,\n data: payload,\n };\n },\n};\n\nexport function normalizeDecision(\n decision: MilterDecision,\n fallback: MilterDecisionKeyword,\n): MilterReply | null {\n\n if (decision === null) {\n return null;\n }\n\n if (decision === undefined) {\n return { command: DECISION_TO_COMMAND[fallback] };\n }\n\n if (typeof decision === 'string') {\n return { command: DECISION_TO_COMMAND[decision] };\n }\n\n if (decision.data === undefined) {\n return { command: decision.command };\n }\n\n return {\n command: decision.command,\n data: decision.data,\n };\n}\n","import type {\n DKIMSignOptions,\n DKIMSignResult,\n DKIMVerifyOptions,\n DKIMVerifyResult,\n MessageInput,\n} from 'mailauth';\nimport { dkimSign } from 'mailauth/lib/dkim/sign.js';\nimport { dkimVerify } from 'mailauth/lib/dkim/verify.js';\n\nimport type { MilterContext } from './context.js';\n\nexport type DkimSignOptions = DKIMSignOptions;\nexport type DkimSignResult = DKIMSignResult;\nexport type DkimVerifyOptions = DKIMVerifyOptions;\nexport type DkimVerifyResult = DKIMVerifyResult;\n\nfunction normalizeSender(from: readonly string[] | null): string | undefined {\n\n const value = from?.[0]?.trim();\n\n if (!value || value === '<>') {\n return undefined;\n }\n\n if (value.startsWith('<') && value.endsWith('>')) {\n return value.slice(1, -1).trim() || undefined;\n }\n\n return value;\n}\n\nfunction buildMessage(ctx: MilterContext, body: Buffer): Buffer {\n\n const headers = ctx.headerLines.map(([name, value]) => `${name}: ${value}`).join('\\r\\n');\n\n return Buffer.concat([\n Buffer.from(`${headers}\\r\\n\\r\\n`, 'utf8'),\n body,\n ]);\n}\n\nexport async function verifyDkim(\n body: Buffer,\n ctx: MilterContext,\n options: DkimVerifyOptions = {},\n): Promise<DkimVerifyResult> {\n\n const sender = options.sender ?? normalizeSender(ctx.from);\n const result = await dkimVerify(buildMessage(ctx, body), {\n ...options,\n ...(sender === undefined ? {} : { sender }),\n });\n\n ctx.dkim = result;\n return result;\n}\n\nexport async function signDkim(\n message: MessageInput,\n options: DkimSignOptions,\n): Promise<DkimSignResult> {\n\n const signatureData = options.signatureData?.length\n ? options.signatureData.map((entry) => ({ ...entry }))\n : [{\n signingDomain: options.signingDomain,\n selector: options.selector,\n privateKey: options.privateKey,\n ...(options.canonicalization === undefined ? {} : { canonicalization: options.canonicalization }),\n ...(options.algorithm === undefined ? {} : { algorithm: options.algorithm }),\n ...(options.maxBodyLength === undefined ? {} : { maxBodyLength: options.maxBodyLength }),\n ...(options.identity === undefined ? {} : { identity: options.identity }),\n }];\n\n return dkimSign(message, {\n ...options,\n signatureData,\n });\n}\n","import type { Logger } from './types.js';\n\nexport class ConsoleLogger implements Logger {\n\n private readonly prefix: string;\n\n constructor(prefix = '[MILTER]') {\n\n this.prefix = prefix;\n }\n\n debug(...args: unknown[]): void {\n\n console.debug(this.prefix, ...args);\n }\n\n info(...args: unknown[]): void {\n\n console.info(this.prefix, ...args);\n }\n\n warn(...args: unknown[]): void {\n\n console.warn(this.prefix, ...args);\n }\n\n error(...args: unknown[]): void {\n\n console.error(this.prefix, ...args);\n }\n}\n","import fs from 'node:fs';\nimport net from 'node:net';\n\nimport {\n ACTION_ALL,\n DISABLE_ALL_CALLBACKS,\n SMFIC,\n SMFIF,\n SMFI_VERSION,\n SMFIP,\n} from './constants.js';\nimport { MilterContext } from './context.js';\nimport { normalizeDecision } from './decisions.js';\nimport { verifyDkim } from './dkim.js';\nimport type { DkimVerifyOptions } from './dkim.js';\nimport { ConsoleLogger } from './logger.js';\nimport { send } from './frames.js';\nimport { checkSpf } from './spf.js';\nimport type { SpfOptions } from './spf.js';\nimport type {\n HeaderMap,\n Logger,\n MilterDecision,\n MilterEventMap,\n MilterEventName,\n MilterOptions,\n} from './types.js';\nimport {\n cloneHeaders, parseConnectInfo, readCString, readCStringList, readCStringPairs,\n} from './utils.js';\n\ntype GenericHandler = (...args: unknown[]) => unknown | Promise<unknown>;\ntype HandlerMap = Record<MilterEventName, Set<GenericHandler>>;\n\ninterface ResolvedOptions {\n socketPath: string | undefined;\n host: string;\n port: number | undefined;\n actions: number;\n unlinkOnStart: boolean;\n chmod: number | false;\n collectBody: boolean;\n maxBodyBytes: number;\n enforceActionStages: boolean;\n defaultDecision: 'continue' | 'accept' | 'reject' | 'discard' | 'tempfail';\n logger: Logger;\n}\n\nconst DEFAULT_OPTIONS: ResolvedOptions = {\n socketPath: undefined,\n host: '127.0.0.1',\n port: undefined,\n actions: SMFIF.ADDHDRS | SMFIF.CHGBODY | SMFIF.ADDRCPT,\n unlinkOnStart: true,\n chmod: 0o777,\n collectBody: true,\n maxBodyBytes: 32 * 1024 * 1024,\n enforceActionStages: true,\n defaultDecision: 'continue',\n logger: new ConsoleLogger('[MILTER]'),\n};\n\nfunction createHandlerMap(): HandlerMap {\n\n return {\n connect: new Set(),\n helo: new Set(),\n mail: new Set(),\n rcpt: new Set(),\n headerLine: new Set(),\n headers: new Set(),\n header: new Set(),\n bodyChunk: new Set(),\n bodyEnd: new Set(),\n data: new Set(),\n macro: new Set(),\n abort: new Set(),\n close: new Set(),\n unknown: new Set(),\n error: new Set(),\n };\n}\n\nexport class MilterServer {\n\n private readonly server: net.Server;\n private readonly handlers: HandlerMap;\n private readonly options: ResolvedOptions;\n private nextConnectionId = 1;\n private dkimOptions: DkimVerifyOptions | null = null;\n private spfOptions: SpfOptions | null = null;\n\n constructor(options: MilterOptions = {}) {\n\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n logger: options.logger ?? DEFAULT_OPTIONS.logger,\n };\n\n if (!this.options.socketPath && this.options.port === undefined) {\n throw new Error('Either socketPath or port must be provided.');\n }\n\n this.handlers = createHandlerMap();\n this.server = net.createServer((socket) => {\n\n this.handleConnection(socket);\n });\n\n this.server.on('error', (err) => {\n\n this.options.logger.error('Server error', err);\n this.emitInternalError(undefined, err);\n });\n }\n\n on<K extends MilterEventName>(event: K, handler: MilterEventMap[K]): this {\n\n this.handlers[event].add(handler as GenericHandler);\n return this;\n }\n\n off<K extends MilterEventName>(event: K, handler: MilterEventMap[K]): this {\n\n this.handlers[event].delete(handler as GenericHandler);\n return this;\n }\n\n once<K extends MilterEventName>(event: K, handler: MilterEventMap[K]): this {\n\n const wrap = (async (...args: unknown[]) => {\n\n this.off(event, wrap as MilterEventMap[K]);\n return (handler as GenericHandler)(...args);\n }) as MilterEventMap[K];\n\n this.on(event, wrap);\n return this;\n }\n\n useSpf(options: SpfOptions = {}): this {\n\n this.spfOptions = options;\n return this;\n }\n\n useDkim(options: DkimVerifyOptions = {}): this {\n\n if (!this.options.collectBody) {\n throw new Error('DKIM verification requires collectBody to be enabled.');\n }\n\n this.dkimOptions = options;\n return this;\n }\n\n async listen(socketPathOverride?: string): Promise<void> {\n\n if (socketPathOverride) {\n this.options.socketPath = socketPathOverride;\n }\n\n if (this.options.socketPath) {\n if (this.options.unlinkOnStart && fs.existsSync(this.options.socketPath)) {\n fs.unlinkSync(this.options.socketPath);\n }\n\n await new Promise<void>((resolve, reject) => {\n\n this.server.listen(this.options.socketPath, (err?: Error) => {\n\n if (err) {\n reject(err);\n return;\n }\n\n if (this.options.chmod !== false) {\n fs.chmodSync(this.options.socketPath as string, this.options.chmod);\n }\n\n this.options.logger.info('Listening on', this.options.socketPath);\n resolve();\n });\n });\n return;\n }\n\n await new Promise<void>((resolve, reject) => {\n\n this.server.listen(this.options.port, this.options.host, (err?: Error) => {\n\n if (err) {\n reject(err);\n return;\n }\n\n this.options.logger.info('Listening on', `${this.options.host}:${this.options.port}`);\n resolve();\n });\n });\n }\n\n async close(): Promise<void> {\n\n await new Promise<void>((resolve, reject) => {\n\n this.server.close((err?: Error) => {\n\n if (err) {\n reject(err);\n return;\n }\n resolve();\n });\n });\n }\n\n private handleConnection(socket: net.Socket): void {\n\n const ctx = new MilterContext(\n this.nextConnectionId++,\n socket,\n this.options.collectBody,\n this.options.maxBodyBytes,\n this.options.enforceActionStages,\n );\n\n this.options.logger.info('MTA connected', ctx.id);\n\n let closed = false;\n let buffer = Buffer.alloc(0);\n let pipeline: Promise<void> = Promise.resolve();\n const emitClose = () => {\n\n if (closed) {\n return;\n }\n closed = true;\n void this.emit('close', ctx);\n };\n\n socket.on('error', (err) => {\n\n this.options.logger.error('Socket error', ctx.id, err);\n this.emitInternalError(ctx, err);\n });\n\n socket.on('close', () => {\n\n ctx.socket = null;\n emitClose();\n });\n\n socket.on('end', () => {\n\n this.options.logger.info('MTA disconnected', ctx.id);\n ctx.socket = null;\n emitClose();\n });\n\n socket.on('data', (chunk) => {\n\n buffer = Buffer.concat([buffer, chunk]);\n\n while (buffer.length >= 5) {\n const len = buffer.readUInt32BE(0);\n\n if (len < 1) {\n this.options.logger.warn('Invalid frame length', len, 'connection', ctx.id);\n socket.destroy();\n return;\n }\n\n const total = 4 + len;\n\n if (buffer.length < total) {\n break;\n }\n\n const command = buffer.toString('ascii', 4, 5);\n const data = buffer.subarray(5, total);\n\n buffer = buffer.subarray(total);\n\n pipeline = pipeline\n .then(() => this.parsePacket(ctx, command, data))\n .catch((err) => {\n\n this.options.logger.error('Packet handling error', ctx.id, err);\n this.emitInternalError(ctx, err);\n send(ctx.socket, 't');\n });\n }\n });\n }\n\n private async parsePacket(ctx: MilterContext, command: string, data: Buffer): Promise<void> {\n\n switch (command) {\n case SMFIC.OPTNEG:\n {\n if (data.length < 12) {\n this.options.logger.warn('Invalid OPTNEG payload length', data.length, 'connection', ctx.id);\n return;\n }\n\n const mtaVersion = data.readUInt32BE(0);\n const mtaActions = data.readUInt32BE(4);\n const mtaProtocol = data.readUInt32BE(8);\n const version = Math.min(mtaVersion, SMFI_VERSION);\n const actions = (this.options.actions & mtaActions) >>> 0;\n const protocol = (this.computeProtocolMask() & mtaProtocol) >>> 0;\n\n ctx.setNegotiatedActions(actions);\n\n const payload = Buffer.alloc(12);\n\n payload.writeUInt32BE(version, 0);\n payload.writeUInt32BE(actions, 4);\n payload.writeUInt32BE(protocol, 8);\n send(ctx.socket, SMFIC.OPTNEG, payload);\n return;\n }\n\n case SMFIC.CONNECT:\n {\n ctx.setPhase('connect');\n\n const info = parseConnectInfo(data);\n\n ctx.connection = info;\n\n const decision = (await this.emit('connect', info, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n case SMFIC.HELO:\n {\n ctx.setPhase('helo');\n\n const helo = readCString(data, 0).value;\n\n ctx.helo = helo;\n\n const decision = (await this.emit('helo', helo, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n case SMFIC.MAIL:\n {\n ctx.setPhase('mail');\n\n const from = readCStringList(data);\n\n ctx.from = from;\n\n if (this.spfOptions) {\n await checkSpf(from, ctx, this.spfOptions);\n }\n\n const decision = (await this.emit('mail', from, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n case SMFIC.RCPT:\n {\n ctx.setPhase('rcpt');\n\n const to = readCStringList(data);\n\n ctx.to = to;\n\n const decision = (await this.emit('rcpt', to, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n case SMFIC.HEADER:\n {\n ctx.setPhase('headerLine');\n\n const p = readCString(data, 0);\n const q = readCString(data, p.next);\n\n if (p.value.length > 0) {\n ctx.appendHeader(p.value, q.value);\n\n const decision = (await this.emit('headerLine', p.value, q.value, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n this.respond(ctx, undefined);\n return;\n }\n\n case SMFIC.EOH:\n {\n ctx.setPhase('eoh');\n\n const headers = cloneHeaders(ctx.headers) as HeaderMap;\n const legacy = (await this.emit('header', headers, ctx)) as MilterDecision;\n const modern = (await this.emit('headers', headers, ctx)) as MilterDecision;\n\n this.respond(ctx, modern ?? legacy);\n return;\n }\n\n case SMFIC.BODY:\n {\n ctx.setPhase('bodyChunk');\n try {\n ctx.appendBody(data);\n } catch (err) {\n this.emitInternalError(ctx, err);\n this.respond(ctx, 'tempfail');\n return;\n }\n\n const decision = (await this.emit('bodyChunk', Buffer.from(data), ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n case SMFIC.BODYEOB:\n {\n ctx.setPhase('bodyEnd');\n\n const body = ctx.getBody();\n\n if (this.dkimOptions) {\n await verifyDkim(body, ctx, this.dkimOptions);\n }\n\n const decision = (await this.emit('bodyEnd', body, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n case SMFIC.DATA:\n {\n ctx.setPhase('data');\n\n const decision = (await this.emit('data', data.toString('utf8'), ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n case SMFIC.MACRO:\n {\n ctx.setPhase('macro');\n\n const cmd = data.toString('ascii', 0, 1);\n const map = readCStringPairs(data, 1);\n\n ctx.macros[cmd] = map;\n await this.emit('macro', cmd, map, ctx);\n return;\n }\n\n case SMFIC.ABORT:\n {\n ctx.setPhase('abort');\n ctx.resetMessage();\n await this.emit('abort', ctx);\n return;\n }\n\n case SMFIC.QUIT:\n {\n ctx.setPhase('close');\n await this.emit('close', ctx);\n if (ctx.socket) {\n ctx.socket.end();\n ctx.socket = null;\n }\n return;\n }\n\n case SMFIC.UNKNOWN:\n {\n ctx.setPhase('unknown');\n\n const decision = (await this.emit('unknown', command, data, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n default:\n {\n ctx.setPhase('unknown');\n this.options.logger.warn('Unknown command', command, 'connection', ctx.id);\n\n const decision = (await this.emit('unknown', command, data, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n }\n }\n }\n\n private computeProtocolMask(): number {\n\n let flags = DISABLE_ALL_CALLBACKS;\n\n if (this.handlers.connect.size > 0) {\n flags &= ~SMFIP.NOCONNECT;\n }\n if (this.handlers.helo.size > 0) {\n flags &= ~SMFIP.NOHELO;\n }\n if (this.handlers.mail.size > 0 || this.spfOptions) {\n flags &= ~SMFIP.NOMAIL;\n }\n if (this.handlers.rcpt.size > 0) {\n flags &= ~SMFIP.NORCPT;\n }\n if (this.handlers.bodyChunk.size > 0 || this.handlers.bodyEnd.size > 0 || this.dkimOptions) {\n flags &= ~SMFIP.NOBODY;\n }\n if (\n this.handlers.headerLine.size > 0\n || this.handlers.headers.size > 0\n || this.handlers.header.size > 0\n || this.dkimOptions\n ) {\n flags &= ~SMFIP.NOHDRS;\n }\n if (this.handlers.headers.size > 0 || this.handlers.header.size > 0) {\n flags &= ~SMFIP.NOEOH;\n }\n\n return flags >>> 0;\n }\n\n private respond(ctx: MilterContext, decision: MilterDecision): void {\n\n const normalized = normalizeDecision(decision, this.options.defaultDecision);\n\n if (!normalized) {\n return;\n }\n send(ctx.socket, normalized.command, normalized.data);\n }\n\n private async emit<K extends MilterEventName>(\n event: K,\n ...args: Parameters<MilterEventMap[K]>\n ): Promise<unknown> {\n\n const callbacks = this.handlers[event];\n\n if (!callbacks || callbacks.size === 0) {\n return undefined;\n }\n\n let result: unknown;\n\n for (const fn of callbacks) {\n result = await fn(...(args as unknown[]));\n }\n\n return result;\n }\n\n private emitInternalError(ctx: MilterContext | undefined, err: unknown): void {\n\n const callbacks = this.handlers.error;\n\n if (callbacks.size === 0) {\n return;\n }\n\n for (const fn of callbacks) {\n Promise.resolve(fn(err, ctx)).catch(() => {\n\n this.options.logger.error('Error handler failed', err);\n });\n }\n }\n}\n\nexport class Milter extends MilterServer {\n\n constructor(options: MilterOptions = {}) {\n\n const resolved = {\n actions: ACTION_ALL,\n ...options,\n };\n\n super(resolved);\n }\n}\n","import type { DNSResolver, SPFResult } from 'mailauth';\nimport { spf as verifySpf } from 'mailauth/lib/spf/index.js';\n\nimport type { MilterContext } from './context.js';\nimport type { MilterDecisionKeyword } from './types.js';\n\nexport type SpfResultCode\n = | 'pass'\n | 'fail'\n | 'softfail'\n | 'neutral'\n | 'none'\n | 'temperror'\n | 'permerror';\n\nexport type SpfPolicy = Partial<Record<SpfResultCode, MilterDecisionKeyword>>;\n\nexport interface SpfOptions {\n mta?: string;\n maxResolveCount?: number;\n maxVoidCount?: number;\n resolver?: DNSResolver;\n}\n\nexport type SpfResult = SPFResult;\n\nconst DEFAULT_POLICY: Record<SpfResultCode, MilterDecisionKeyword> = {\n pass: 'continue',\n fail: 'reject',\n softfail: 'continue',\n neutral: 'continue',\n none: 'continue',\n temperror: 'tempfail',\n permerror: 'continue',\n};\n\nfunction normalizeSender(from: string | readonly string[]): string | undefined {\n\n const value = (typeof from === 'string' ? from : from[0])?.trim();\n\n if (!value || value === '<>') {\n return undefined;\n }\n\n if (value.startsWith('<') && value.endsWith('>')) {\n return value.slice(1, -1).trim() || undefined;\n }\n\n return value;\n}\n\nexport async function checkSpf(\n from: string | readonly string[],\n ctx: MilterContext,\n options: SpfOptions = {},\n): Promise<SpfResult> {\n\n const ip = ctx.connection?.address;\n\n if (!ip) {\n throw new Error('SPF requires the client IP from the Milter CONNECT event.');\n }\n\n const sender = normalizeSender(from);\n const helo = ctx.helo ?? ctx.connection?.hostname;\n const result = await verifySpf({\n ip,\n ...(sender === undefined ? {} : { sender }),\n ...(helo === undefined ? {} : { helo }),\n ...(options.mta === undefined ? {} : { mta: options.mta }),\n ...(options.maxResolveCount === undefined ? {} : { maxResolveCount: options.maxResolveCount }),\n ...(options.maxVoidCount === undefined ? {} : { maxVoidCount: options.maxVoidCount }),\n ...(options.resolver === undefined ? {} : { resolver: options.resolver }),\n });\n\n ctx.spf = result;\n return result;\n}\n\nexport function spfDecision(result: SpfResult, policy: SpfPolicy = {}): MilterDecisionKeyword {\n\n const code = result.status.result as SpfResultCode;\n\n return policy[code] ?? DEFAULT_POLICY[code] ?? 'continue';\n}\n","import { SMFIA } from './constants.js';\nimport type { ConnectInfo } from './types.js';\n\nexport function readCString(buf: Buffer, start = 0): { value: string;\n next: number; } {\n\n if (start >= buf.length) {\n return {\n value: '',\n next: buf.length,\n };\n }\n\n const end = buf.indexOf(0x00, start);\n\n if (end === -1) {\n return {\n value: buf.toString('utf8', start),\n next: buf.length,\n };\n }\n\n return {\n value: buf.toString('utf8', start, end),\n next: end + 1,\n };\n}\n\nexport function readCStringList(buf: Buffer, start = 0): string[] {\n\n const out: string[] = [];\n let offset = start;\n\n while (offset < buf.length) {\n const { value, next } = readCString(buf, offset);\n\n if (next === offset) {\n break;\n }\n\n offset = next;\n if (value.length === 0) {\n if (offset >= buf.length) {\n break;\n }\n continue;\n }\n\n out.push(value);\n }\n\n return out;\n}\n\nexport function readCStringPairs(buf: Buffer, start = 0): Record<string, string> {\n\n const list = readCStringList(buf, start);\n const map: Record<string, string> = {};\n\n for (let i = 0; i + 1 < list.length; i += 2) {\n const key = list[i];\n const value = list[i + 1];\n\n if (key && value) {\n map[key] = value;\n }\n }\n\n return map;\n}\n\nexport function parseConnectInfo(payload: Buffer): ConnectInfo {\n\n const host = readCString(payload, 0);\n const familyByte = payload.toString('ascii', host.next, host.next + 1);\n\n if (familyByte === SMFIA.UNKNOWN || host.next >= payload.length) {\n return {\n hostname: host.value,\n family: 'UNKNOWN',\n };\n }\n\n if (host.next + 3 > payload.length) {\n return {\n hostname: host.value,\n family: 'UNKNOWN',\n };\n }\n\n const port = payload.readUInt16BE(host.next + 1);\n const addr = readCString(payload, host.next + 3).value;\n const family\n = familyByte === SMFIA.UNIX\n ? 'UNIX'\n : familyByte === SMFIA.INET\n ? 'INET'\n : familyByte === SMFIA.INET6\n ? 'INET6'\n : 'UNKNOWN';\n\n return {\n hostname: host.value,\n family,\n address: addr,\n port,\n };\n}\n\nexport function cloneHeaders(headers: Record<string, string[]>): Record<string, string[]> {\n\n const out: Record<string, string[]> = {};\n\n for (const [name, values] of Object.entries(headers)) {\n out[name] = [...values];\n }\n return out;\n}\n"],"mappings":"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","names":["normalizeSender","verifySpf","net","fs"]}
@@ -0,0 +1,187 @@
1
+ import { SMFIF } from './constants.cjs';
2
+ export { ACTION_ALL, DISABLE_ALL_CALLBACKS, SMFIA, SMFIC, SMFIP, SMFIR, SMFI_VERSION } from './constants.cjs';
3
+ import net from 'node:net';
4
+ import { DKIMSignOptions, DKIMSignResult, DKIMVerifyOptions, DKIMVerifyResult, MessageInput, SPFResult, DNSResolver } from 'mailauth';
5
+
6
+ type DkimSignOptions = DKIMSignOptions;
7
+ type DkimSignResult = DKIMSignResult;
8
+ type DkimVerifyOptions = DKIMVerifyOptions;
9
+ type DkimVerifyResult = DKIMVerifyResult;
10
+ declare function verifyDkim(body: Buffer, ctx: MilterContext, options?: DkimVerifyOptions): Promise<DkimVerifyResult>;
11
+ declare function signDkim(message: MessageInput, options: DkimSignOptions): Promise<DkimSignResult>;
12
+
13
+ interface Logger {
14
+ debug: (...args: unknown[]) => void;
15
+ info: (...args: unknown[]) => void;
16
+ warn: (...args: unknown[]) => void;
17
+ error: (...args: unknown[]) => void;
18
+ }
19
+ interface MilterOptions {
20
+ socketPath?: string;
21
+ host?: string;
22
+ port?: number;
23
+ actions?: number;
24
+ unlinkOnStart?: boolean;
25
+ chmod?: number | false;
26
+ collectBody?: boolean;
27
+ maxBodyBytes?: number;
28
+ defaultDecision?: MilterDecisionKeyword;
29
+ enforceActionStages?: boolean;
30
+ logger?: Logger;
31
+ }
32
+ type MilterPhase = 'connect' | 'helo' | 'mail' | 'rcpt' | 'headerLine' | 'eoh' | 'bodyChunk' | 'bodyEnd' | 'data' | 'macro' | 'abort' | 'close' | 'unknown';
33
+ type MilterActionFlag = (typeof SMFIF)[keyof typeof SMFIF];
34
+ type MilterDecisionKeyword = 'continue' | 'accept' | 'reject' | 'discard' | 'tempfail';
35
+ interface MilterReply {
36
+ command: string;
37
+ data?: Buffer;
38
+ }
39
+ type MilterDecision = MilterDecisionKeyword | MilterReply | null | undefined;
40
+ type MaybePromise<T> = T | Promise<T>;
41
+ interface ConnectInfo {
42
+ hostname: string;
43
+ family: 'UNKNOWN' | 'UNIX' | 'INET' | 'INET6';
44
+ address?: string;
45
+ port?: number;
46
+ }
47
+ type HeaderMap = Record<string, string[]>;
48
+ interface MilterEventMap {
49
+ connect: (info: ConnectInfo, ctx: MilterContext) => MaybePromise<MilterDecision>;
50
+ helo: (helo: string, ctx: MilterContext) => MaybePromise<MilterDecision>;
51
+ mail: (from: string[], ctx: MilterContext) => MaybePromise<MilterDecision>;
52
+ rcpt: (to: string[], ctx: MilterContext) => MaybePromise<MilterDecision>;
53
+ headerLine: (name: string, value: string, ctx: MilterContext) => MaybePromise<MilterDecision>;
54
+ headers: (headers: HeaderMap, ctx: MilterContext) => MaybePromise<MilterDecision>;
55
+ header: (headers: HeaderMap, ctx: MilterContext) => MaybePromise<MilterDecision>;
56
+ bodyChunk: (chunk: Buffer, ctx: MilterContext) => MaybePromise<MilterDecision>;
57
+ bodyEnd: (body: Buffer, ctx: MilterContext) => MaybePromise<MilterDecision>;
58
+ data: (raw: string, ctx: MilterContext) => MaybePromise<MilterDecision>;
59
+ macro: (command: string, values: Record<string, string>, ctx: MilterContext) => MaybePromise<void>;
60
+ abort: (ctx: MilterContext) => MaybePromise<void>;
61
+ close: (ctx: MilterContext) => MaybePromise<void>;
62
+ unknown: (command: string, data: Buffer, ctx: MilterContext) => MaybePromise<MilterDecision>;
63
+ error: (error: unknown, ctx: MilterContext | undefined) => MaybePromise<void>;
64
+ }
65
+ type MilterEventName = keyof MilterEventMap;
66
+
67
+ type SpfResultCode = 'pass' | 'fail' | 'softfail' | 'neutral' | 'none' | 'temperror' | 'permerror';
68
+ type SpfPolicy = Partial<Record<SpfResultCode, MilterDecisionKeyword>>;
69
+ interface SpfOptions {
70
+ mta?: string;
71
+ maxResolveCount?: number;
72
+ maxVoidCount?: number;
73
+ resolver?: DNSResolver;
74
+ }
75
+ type SpfResult = SPFResult;
76
+ declare function checkSpf(from: string | readonly string[], ctx: MilterContext, options?: SpfOptions): Promise<SpfResult>;
77
+ declare function spfDecision(result: SpfResult, policy?: SpfPolicy): MilterDecisionKeyword;
78
+
79
+ declare class MilterActionError extends Error {
80
+ readonly code: 'E_MILTER_ACTION_STAGE' | 'E_MILTER_ACTION_CAPABILITY';
81
+ constructor(code: 'E_MILTER_ACTION_STAGE' | 'E_MILTER_ACTION_CAPABILITY', message: string);
82
+ }
83
+ declare class MilterContext {
84
+ readonly id: number;
85
+ private readonly collectBody;
86
+ private readonly maxBodyBytes;
87
+ private readonly enforceActionStages;
88
+ private chunks;
89
+ private bodyBytes;
90
+ private phase;
91
+ private negotiatedActions;
92
+ socket: net.Socket | null;
93
+ headers: Record<string, string[]>;
94
+ headerLines: Array<[string, string]>;
95
+ macros: Record<string, Record<string, string>>;
96
+ connection: ConnectInfo | null;
97
+ helo: string | null;
98
+ dkim: DkimVerifyResult | null;
99
+ spf: SpfResult | null;
100
+ from: string[] | null;
101
+ to: string[] | null;
102
+ constructor(id: number, socket: net.Socket, collectBody: boolean, maxBodyBytes: number, enforceActionStages: boolean);
103
+ setPhase(phase: MilterPhase): void;
104
+ getPhase(): MilterPhase;
105
+ setNegotiatedActions(actions: number): void;
106
+ can(action: MilterActionFlag): boolean;
107
+ appendHeader(name: string, value: string): void;
108
+ appendBody(chunk: Buffer): void;
109
+ getBody(): Buffer;
110
+ resetMessage(): void;
111
+ private requireAction;
112
+ send(command: string, data?: Buffer): void;
113
+ continue(): void;
114
+ accept(): void;
115
+ reject(): void;
116
+ discard(): void;
117
+ tempfail(): void;
118
+ progress(): void;
119
+ addHeader(name: string, value: string): void;
120
+ insertHeader(index: number, name: string, value: string): void;
121
+ changeHeader(name: string, index: number, value?: string): void;
122
+ addRecipient(address: string): void;
123
+ deleteRecipient(address: string): void;
124
+ replaceBody(chunk: Buffer | string): void;
125
+ quarantine(reason?: string): void;
126
+ setSender(sender: string): void;
127
+ replyCode(code: string, message: string, enhancedCode?: string): void;
128
+ }
129
+
130
+ declare const Decision: {
131
+ continue(): MilterReply;
132
+ accept(): MilterReply;
133
+ reject(): MilterReply;
134
+ discard(): MilterReply;
135
+ tempfail(): MilterReply;
136
+ replyCode(code: string, message: string, enhancedCode?: string): MilterReply;
137
+ };
138
+ declare function normalizeDecision(decision: MilterDecision, fallback: MilterDecisionKeyword): MilterReply | null;
139
+
140
+ declare function frame(command: string, data?: Buffer): Buffer;
141
+ declare function send(socket: net.Socket | null, command: string, data?: Buffer): void;
142
+
143
+ declare class ConsoleLogger implements Logger {
144
+ private readonly prefix;
145
+ constructor(prefix?: string);
146
+ debug(...args: unknown[]): void;
147
+ info(...args: unknown[]): void;
148
+ warn(...args: unknown[]): void;
149
+ error(...args: unknown[]): void;
150
+ }
151
+
152
+ declare class MilterServer {
153
+ private readonly server;
154
+ private readonly handlers;
155
+ private readonly options;
156
+ private nextConnectionId;
157
+ private dkimOptions;
158
+ private spfOptions;
159
+ constructor(options?: MilterOptions);
160
+ on<K extends MilterEventName>(event: K, handler: MilterEventMap[K]): this;
161
+ off<K extends MilterEventName>(event: K, handler: MilterEventMap[K]): this;
162
+ once<K extends MilterEventName>(event: K, handler: MilterEventMap[K]): this;
163
+ useSpf(options?: SpfOptions): this;
164
+ useDkim(options?: DkimVerifyOptions): this;
165
+ listen(socketPathOverride?: string): Promise<void>;
166
+ close(): Promise<void>;
167
+ private handleConnection;
168
+ private parsePacket;
169
+ private computeProtocolMask;
170
+ private respond;
171
+ private emit;
172
+ private emitInternalError;
173
+ }
174
+ declare class Milter extends MilterServer {
175
+ constructor(options?: MilterOptions);
176
+ }
177
+
178
+ declare function readCString(buf: Buffer, start?: number): {
179
+ value: string;
180
+ next: number;
181
+ };
182
+ declare function readCStringList(buf: Buffer, start?: number): string[];
183
+ declare function readCStringPairs(buf: Buffer, start?: number): Record<string, string>;
184
+ declare function parseConnectInfo(payload: Buffer): ConnectInfo;
185
+ declare function cloneHeaders(headers: Record<string, string[]>): Record<string, string[]>;
186
+
187
+ export { type ConnectInfo, ConsoleLogger, Decision, type DkimSignOptions, type DkimSignResult, type DkimVerifyOptions, type DkimVerifyResult, type HeaderMap, type Logger, type MaybePromise, Milter, MilterActionError, type MilterActionFlag, MilterContext, type MilterDecision, type MilterDecisionKeyword, type MilterEventMap, type MilterEventName, type MilterOptions, type MilterPhase, type MilterReply, MilterServer, SMFIF, type SpfOptions, type SpfPolicy, type SpfResult, type SpfResultCode, checkSpf, cloneHeaders, MilterServer as default, frame, normalizeDecision, parseConnectInfo, readCString, readCStringList, readCStringPairs, send, signDkim, spfDecision, verifyDkim };
@@ -0,0 +1,187 @@
1
+ import { SMFIF } from './constants.js';
2
+ export { ACTION_ALL, DISABLE_ALL_CALLBACKS, SMFIA, SMFIC, SMFIP, SMFIR, SMFI_VERSION } from './constants.js';
3
+ import net from 'node:net';
4
+ import { DKIMSignOptions, DKIMSignResult, DKIMVerifyOptions, DKIMVerifyResult, MessageInput, SPFResult, DNSResolver } from 'mailauth';
5
+
6
+ type DkimSignOptions = DKIMSignOptions;
7
+ type DkimSignResult = DKIMSignResult;
8
+ type DkimVerifyOptions = DKIMVerifyOptions;
9
+ type DkimVerifyResult = DKIMVerifyResult;
10
+ declare function verifyDkim(body: Buffer, ctx: MilterContext, options?: DkimVerifyOptions): Promise<DkimVerifyResult>;
11
+ declare function signDkim(message: MessageInput, options: DkimSignOptions): Promise<DkimSignResult>;
12
+
13
+ interface Logger {
14
+ debug: (...args: unknown[]) => void;
15
+ info: (...args: unknown[]) => void;
16
+ warn: (...args: unknown[]) => void;
17
+ error: (...args: unknown[]) => void;
18
+ }
19
+ interface MilterOptions {
20
+ socketPath?: string;
21
+ host?: string;
22
+ port?: number;
23
+ actions?: number;
24
+ unlinkOnStart?: boolean;
25
+ chmod?: number | false;
26
+ collectBody?: boolean;
27
+ maxBodyBytes?: number;
28
+ defaultDecision?: MilterDecisionKeyword;
29
+ enforceActionStages?: boolean;
30
+ logger?: Logger;
31
+ }
32
+ type MilterPhase = 'connect' | 'helo' | 'mail' | 'rcpt' | 'headerLine' | 'eoh' | 'bodyChunk' | 'bodyEnd' | 'data' | 'macro' | 'abort' | 'close' | 'unknown';
33
+ type MilterActionFlag = (typeof SMFIF)[keyof typeof SMFIF];
34
+ type MilterDecisionKeyword = 'continue' | 'accept' | 'reject' | 'discard' | 'tempfail';
35
+ interface MilterReply {
36
+ command: string;
37
+ data?: Buffer;
38
+ }
39
+ type MilterDecision = MilterDecisionKeyword | MilterReply | null | undefined;
40
+ type MaybePromise<T> = T | Promise<T>;
41
+ interface ConnectInfo {
42
+ hostname: string;
43
+ family: 'UNKNOWN' | 'UNIX' | 'INET' | 'INET6';
44
+ address?: string;
45
+ port?: number;
46
+ }
47
+ type HeaderMap = Record<string, string[]>;
48
+ interface MilterEventMap {
49
+ connect: (info: ConnectInfo, ctx: MilterContext) => MaybePromise<MilterDecision>;
50
+ helo: (helo: string, ctx: MilterContext) => MaybePromise<MilterDecision>;
51
+ mail: (from: string[], ctx: MilterContext) => MaybePromise<MilterDecision>;
52
+ rcpt: (to: string[], ctx: MilterContext) => MaybePromise<MilterDecision>;
53
+ headerLine: (name: string, value: string, ctx: MilterContext) => MaybePromise<MilterDecision>;
54
+ headers: (headers: HeaderMap, ctx: MilterContext) => MaybePromise<MilterDecision>;
55
+ header: (headers: HeaderMap, ctx: MilterContext) => MaybePromise<MilterDecision>;
56
+ bodyChunk: (chunk: Buffer, ctx: MilterContext) => MaybePromise<MilterDecision>;
57
+ bodyEnd: (body: Buffer, ctx: MilterContext) => MaybePromise<MilterDecision>;
58
+ data: (raw: string, ctx: MilterContext) => MaybePromise<MilterDecision>;
59
+ macro: (command: string, values: Record<string, string>, ctx: MilterContext) => MaybePromise<void>;
60
+ abort: (ctx: MilterContext) => MaybePromise<void>;
61
+ close: (ctx: MilterContext) => MaybePromise<void>;
62
+ unknown: (command: string, data: Buffer, ctx: MilterContext) => MaybePromise<MilterDecision>;
63
+ error: (error: unknown, ctx: MilterContext | undefined) => MaybePromise<void>;
64
+ }
65
+ type MilterEventName = keyof MilterEventMap;
66
+
67
+ type SpfResultCode = 'pass' | 'fail' | 'softfail' | 'neutral' | 'none' | 'temperror' | 'permerror';
68
+ type SpfPolicy = Partial<Record<SpfResultCode, MilterDecisionKeyword>>;
69
+ interface SpfOptions {
70
+ mta?: string;
71
+ maxResolveCount?: number;
72
+ maxVoidCount?: number;
73
+ resolver?: DNSResolver;
74
+ }
75
+ type SpfResult = SPFResult;
76
+ declare function checkSpf(from: string | readonly string[], ctx: MilterContext, options?: SpfOptions): Promise<SpfResult>;
77
+ declare function spfDecision(result: SpfResult, policy?: SpfPolicy): MilterDecisionKeyword;
78
+
79
+ declare class MilterActionError extends Error {
80
+ readonly code: 'E_MILTER_ACTION_STAGE' | 'E_MILTER_ACTION_CAPABILITY';
81
+ constructor(code: 'E_MILTER_ACTION_STAGE' | 'E_MILTER_ACTION_CAPABILITY', message: string);
82
+ }
83
+ declare class MilterContext {
84
+ readonly id: number;
85
+ private readonly collectBody;
86
+ private readonly maxBodyBytes;
87
+ private readonly enforceActionStages;
88
+ private chunks;
89
+ private bodyBytes;
90
+ private phase;
91
+ private negotiatedActions;
92
+ socket: net.Socket | null;
93
+ headers: Record<string, string[]>;
94
+ headerLines: Array<[string, string]>;
95
+ macros: Record<string, Record<string, string>>;
96
+ connection: ConnectInfo | null;
97
+ helo: string | null;
98
+ dkim: DkimVerifyResult | null;
99
+ spf: SpfResult | null;
100
+ from: string[] | null;
101
+ to: string[] | null;
102
+ constructor(id: number, socket: net.Socket, collectBody: boolean, maxBodyBytes: number, enforceActionStages: boolean);
103
+ setPhase(phase: MilterPhase): void;
104
+ getPhase(): MilterPhase;
105
+ setNegotiatedActions(actions: number): void;
106
+ can(action: MilterActionFlag): boolean;
107
+ appendHeader(name: string, value: string): void;
108
+ appendBody(chunk: Buffer): void;
109
+ getBody(): Buffer;
110
+ resetMessage(): void;
111
+ private requireAction;
112
+ send(command: string, data?: Buffer): void;
113
+ continue(): void;
114
+ accept(): void;
115
+ reject(): void;
116
+ discard(): void;
117
+ tempfail(): void;
118
+ progress(): void;
119
+ addHeader(name: string, value: string): void;
120
+ insertHeader(index: number, name: string, value: string): void;
121
+ changeHeader(name: string, index: number, value?: string): void;
122
+ addRecipient(address: string): void;
123
+ deleteRecipient(address: string): void;
124
+ replaceBody(chunk: Buffer | string): void;
125
+ quarantine(reason?: string): void;
126
+ setSender(sender: string): void;
127
+ replyCode(code: string, message: string, enhancedCode?: string): void;
128
+ }
129
+
130
+ declare const Decision: {
131
+ continue(): MilterReply;
132
+ accept(): MilterReply;
133
+ reject(): MilterReply;
134
+ discard(): MilterReply;
135
+ tempfail(): MilterReply;
136
+ replyCode(code: string, message: string, enhancedCode?: string): MilterReply;
137
+ };
138
+ declare function normalizeDecision(decision: MilterDecision, fallback: MilterDecisionKeyword): MilterReply | null;
139
+
140
+ declare function frame(command: string, data?: Buffer): Buffer;
141
+ declare function send(socket: net.Socket | null, command: string, data?: Buffer): void;
142
+
143
+ declare class ConsoleLogger implements Logger {
144
+ private readonly prefix;
145
+ constructor(prefix?: string);
146
+ debug(...args: unknown[]): void;
147
+ info(...args: unknown[]): void;
148
+ warn(...args: unknown[]): void;
149
+ error(...args: unknown[]): void;
150
+ }
151
+
152
+ declare class MilterServer {
153
+ private readonly server;
154
+ private readonly handlers;
155
+ private readonly options;
156
+ private nextConnectionId;
157
+ private dkimOptions;
158
+ private spfOptions;
159
+ constructor(options?: MilterOptions);
160
+ on<K extends MilterEventName>(event: K, handler: MilterEventMap[K]): this;
161
+ off<K extends MilterEventName>(event: K, handler: MilterEventMap[K]): this;
162
+ once<K extends MilterEventName>(event: K, handler: MilterEventMap[K]): this;
163
+ useSpf(options?: SpfOptions): this;
164
+ useDkim(options?: DkimVerifyOptions): this;
165
+ listen(socketPathOverride?: string): Promise<void>;
166
+ close(): Promise<void>;
167
+ private handleConnection;
168
+ private parsePacket;
169
+ private computeProtocolMask;
170
+ private respond;
171
+ private emit;
172
+ private emitInternalError;
173
+ }
174
+ declare class Milter extends MilterServer {
175
+ constructor(options?: MilterOptions);
176
+ }
177
+
178
+ declare function readCString(buf: Buffer, start?: number): {
179
+ value: string;
180
+ next: number;
181
+ };
182
+ declare function readCStringList(buf: Buffer, start?: number): string[];
183
+ declare function readCStringPairs(buf: Buffer, start?: number): Record<string, string>;
184
+ declare function parseConnectInfo(payload: Buffer): ConnectInfo;
185
+ declare function cloneHeaders(headers: Record<string, string[]>): Record<string, string[]>;
186
+
187
+ export { type ConnectInfo, ConsoleLogger, Decision, type DkimSignOptions, type DkimSignResult, type DkimVerifyOptions, type DkimVerifyResult, type HeaderMap, type Logger, type MaybePromise, Milter, MilterActionError, type MilterActionFlag, MilterContext, type MilterDecision, type MilterDecisionKeyword, type MilterEventMap, type MilterEventName, type MilterOptions, type MilterPhase, type MilterReply, MilterServer, SMFIF, type SpfOptions, type SpfPolicy, type SpfResult, type SpfResultCode, checkSpf, cloneHeaders, MilterServer as default, frame, normalizeDecision, parseConnectInfo, readCString, readCStringList, readCStringPairs, send, signDkim, spfDecision, verifyDkim };