@bumail/smtp 0.3.0 → 0.4.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 +63 -4
- package/dist/chunks/{index-3423jxf9.js → index-753q702a.js} +9 -3
- package/dist/chunks/index-753q702a.js.map +14 -0
- package/dist/client/index.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +226 -108
- package/dist/index.js.map +10 -7
- package/dist/protocol/command.d.ts +4 -2
- package/dist/protocol/command.d.ts.map +1 -1
- package/dist/protocol/path.d.ts +13 -2
- package/dist/protocol/path.d.ts.map +1 -1
- package/dist/server/client-key.d.ts +15 -0
- package/dist/server/client-key.d.ts.map +1 -0
- package/dist/server/listener.d.ts +37 -0
- package/dist/server/listener.d.ts.map +1 -0
- package/dist/server/options.d.ts +25 -1
- package/dist/server/options.d.ts.map +1 -1
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/settings.d.ts +2 -0
- package/dist/server/settings.d.ts.map +1 -1
- package/dist/server/slots.d.ts +14 -0
- package/dist/server/slots.d.ts.map +1 -0
- package/dist/server/transaction.d.ts.map +1 -1
- package/docs/guide.md +130 -8
- package/docs/roadmap.md +44 -0
- package/docs/troubleshooting.md +84 -3
- package/package.json +2 -2
- package/dist/chunks/index-3423jxf9.js.map +0 -14
package/README.md
CHANGED
|
@@ -105,6 +105,11 @@ const server = createSmtpServer({
|
|
|
105
105
|
await server.listen({ port: 465 });
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
+
A client has `handshakeTimeout` seconds (default 10) to complete its TLS
|
|
109
|
+
handshake, and holds a slot of `maxConnections` and
|
|
110
|
+
`maxConnectionsPerClient` meanwhile: a socket that never sends its
|
|
111
|
+
ClientHello is closed, without a word.
|
|
112
|
+
|
|
108
113
|
## Hooks for policy
|
|
109
114
|
|
|
110
115
|
A hook returns nothing to accept, or a `reply(code, status, text)` with a
|
|
@@ -126,7 +131,9 @@ const server = createSmtpServer({
|
|
|
126
131
|
onMailFrom: (path) =>
|
|
127
132
|
path.domain === 'spam.example' ? reply(550, '5.7.1', 'Sender refused') : undefined,
|
|
128
133
|
onRcptTo: (path) =>
|
|
129
|
-
mailboxes.has(path.address.toLowerCase())
|
|
134
|
+
path.postmaster || mailboxes.has(path.address.toLowerCase())
|
|
135
|
+
? undefined
|
|
136
|
+
: reply(550, '5.1.1', 'No such user here'),
|
|
130
137
|
async onData(message) {
|
|
131
138
|
await Bun.write(`spool/${message.id}.eml`, await new Response(message.content).bytes());
|
|
132
139
|
},
|
|
@@ -139,6 +146,33 @@ await server.listen({ port: 25 });
|
|
|
139
146
|
The server's own checks run first: `onRcptTo` is never asked about a
|
|
140
147
|
recipient the server already refused, relaying included.
|
|
141
148
|
|
|
149
|
+
`RCPT TO:<postmaster>`, with no domain and in any case, is taken as the
|
|
150
|
+
server's own postmaster (RFC 5321 §4.5.1): it is not relaying, so it needs
|
|
151
|
+
no AUTH, and `localDomains` is not asked. `onRcptTo` gets a `Path` whose
|
|
152
|
+
`postmaster` is `true`, `address` and `local` `'postmaster'` and `domain`
|
|
153
|
+
`''`, and the envelope lists the recipient as `'postmaster'`, with no `@`.
|
|
154
|
+
Route it to a mailbox of your own:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { createSmtpServer } from '@bumail/smtp';
|
|
158
|
+
|
|
159
|
+
const postmaster = 'postmaster@example.com';
|
|
160
|
+
|
|
161
|
+
const server = createSmtpServer({
|
|
162
|
+
hostname: 'mx.example.com',
|
|
163
|
+
localDomains: ['example.com'],
|
|
164
|
+
async onData(message) {
|
|
165
|
+
const recipients = message.envelope.to.map((to) => (to === 'postmaster' ? postmaster : to));
|
|
166
|
+
const bytes = await new Response(message.content).bytes();
|
|
167
|
+
for (const to of recipients) await Bun.write(`spool/${to}/${message.id}.eml`, bytes);
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`<postmaster@domain>` is an ordinary path, checked as any other. The
|
|
173
|
+
[guide](https://github.com/softistx/bumail/blob/develop/packages/smtp/docs/guide.md#rcpt-topostmaster)
|
|
174
|
+
has the details.
|
|
175
|
+
|
|
142
176
|
## Limits
|
|
143
177
|
|
|
144
178
|
Every limit has a default; each must be a positive integer —
|
|
@@ -154,6 +188,8 @@ const server = createSmtpServer({
|
|
|
154
188
|
maxMessageSize: 10 * 1024 * 1024, // bytes, announced as SIZE; default 25 MiB → 552 5.3.4
|
|
155
189
|
maxRecipients: 50, // per message; default 100 → 452 4.5.3
|
|
156
190
|
maxConnections: 200, // at once; default 1000 → 421 4.3.2
|
|
191
|
+
maxConnectionsPerClient: 5, // at once from one IPv4 address or IPv6 /64; default 10 → 421 4.7.0
|
|
192
|
+
handshakeTimeout: 10, // seconds to complete an implicit TLS handshake; default 10 → closed
|
|
157
193
|
maxErrors: 5, // failed commands before hanging up; default 10 → 421 4.7.0
|
|
158
194
|
timeout: 120, // idle seconds, from the client's last byte or the 220; default 300 → 421 4.4.2
|
|
159
195
|
hookTimeout: 20, // seconds a hook has to settle; default 60 → 451 4.3.0
|
|
@@ -286,6 +322,28 @@ host; a 5xx stops. A null MX (RFC 7505) fails at once with `NULL_MX`.
|
|
|
286
322
|
back for that many seconds once `onConnect` accepted, so a sender that
|
|
287
323
|
does not wait gives itself away, as Postfix's postscreen does. A refusal
|
|
288
324
|
from `onConnect` goes out at once, delay or not.
|
|
325
|
+
- **One client cannot take every slot.** `maxConnectionsPerClient`
|
|
326
|
+
(default 10) bounds the connections one client holds at once, so a
|
|
327
|
+
single host cannot fill `maxConnections`. A client is an IPv4 address,
|
|
328
|
+
or an IPv6 address by its /64; an IPv4-mapped or NAT64 address counts as
|
|
329
|
+
the IPv4 address inside it, so a dual-stack listener counts an IPv4
|
|
330
|
+
client once. One more gets `421 4.7.0 <hostname> Too many connections
|
|
331
|
+
from your address, try later` and is closed, before `onConnect`. Every
|
|
332
|
+
close frees the slot: QUIT, a hang-up, an error, the idle time,
|
|
333
|
+
`stop(true)`. Group addresses the same way in your own policy with
|
|
334
|
+
`clientKey`:
|
|
335
|
+
|
|
336
|
+
```ts
|
|
337
|
+
import { clientKey } from '@bumail/smtp';
|
|
338
|
+
|
|
339
|
+
clientKey('::ffff:192.0.2.1'); // '192.0.2.1'
|
|
340
|
+
clientKey('2001:db8::1'); // '2001:db8:0:0::/64': four groups, zeros written out
|
|
341
|
+
```
|
|
342
|
+
- **A TLS handshake is bounded.** On implicit TLS a socket is counted from
|
|
343
|
+
the TCP connection on, before its handshake, and closed past
|
|
344
|
+
`handshakeTimeout` (default 10 seconds) if the handshake has not
|
|
345
|
+
completed; `onConnect` and the greeting come once it has. A STARTTLS
|
|
346
|
+
handshake is bounded by `timeout`.
|
|
289
347
|
- **Bounded memory.** The server holds 64 KiB of a client's input and of a
|
|
290
348
|
message at most, and stops reading the client past that; replies to a
|
|
291
349
|
client that reads slowly wait in the server, and none is lost while the
|
|
@@ -363,7 +421,7 @@ host; a 5xx stops. A null MX (RFC 7505) fails at once with `NULL_MX`.
|
|
|
363
421
|
| --- | --- |
|
|
364
422
|
| `createSmtpServer(options)` | the server; throws an `SmtpError` (`INVALID_OPTION`) on a bad option |
|
|
365
423
|
| `SmtpServer` | `listen({ port, hostname? })` (once; again throws `ALREADY_LISTENING`), `stop(closeConnections?)` (`true` hangs up on every client, after STARTTLS too), `connections` |
|
|
366
|
-
| `SmtpServerOptions` | `hostname`, `mode`, `localDomains`, `tls`, `implicitTls`, `authenticate`, the limits, `hookTimeout`, `greetingDelay`, `onError`, and the hooks |
|
|
424
|
+
| `SmtpServerOptions` | `hostname`, `mode`, `localDomains`, `tls`, `implicitTls`, `authenticate`, the limits (`maxConnectionsPerClient` among them), `hookTimeout`, `handshakeTimeout`, `greetingDelay`, `onError`, and the hooks |
|
|
367
425
|
| `SmtpError`, `SmtpErrorCode` | `code`: `INVALID_OPTION`, `ALREADY_LISTENING`, and what a content stream or `onError` can get: `MESSAGE_TOO_BIG`, `BARE_LINE_BREAK`, `CONNECTION_LOST`, `HOOK_TIMEOUT`, `INVALID_HOOK_REPLY`, `MESSAGE_NOT_READ`; `sendMail`'s are listed below. `temporary`, `reply` and `rejected` are set for `sendMail`'s errors |
|
|
368
426
|
| `SmtpHooks` | `onConnect`, `onMailFrom`, `onRcptTo`, `onData` |
|
|
369
427
|
| `HookResult` | what a hook returns: `undefined` to accept, a `Reply` to refuse |
|
|
@@ -373,9 +431,10 @@ host; a 5xx stops. A null MX (RFC 7505) fails at once with `NULL_MX`.
|
|
|
373
431
|
| `Credentials` | what `authenticate` receives: `mechanism`, `username`, `password`, `authorizationId?` |
|
|
374
432
|
| `reply(code, status, text)`, `Reply` | a reply, for a hook to refuse with |
|
|
375
433
|
| `formatReply(reply, enhanced?)` | a reply as sent on the wire, CRLF included |
|
|
376
|
-
| `parsePath(text, allowNull, sourceRoute?)`, `Path`, `SourceRoute` | `<local@domain>` (RFC 5321 §4.1.2) to `{ address, local, domain }
|
|
434
|
+
| `parsePath(text, allowNull, sourceRoute?)`, `Path`, `SourceRoute` | `<local@domain>` (RFC 5321 §4.1.2) to `{ address, local, domain }` (and `postmaster: true` for `RCPT TO:<postmaster>`, which `parsePathCommand` gives); a source route `@a,@b:` is dropped (`'discard'`, the default) or refused (`'refuse'`); a control character (C0, DEL or C1), `>`, U+2028 or U+2029, Unicode format character (`\p{Cf}`) or lone surrogate anywhere, or an IPv4 literal octet above 255, refuses the path |
|
|
377
435
|
| `parseCommand(line)`, `Command` | a command line to `{ verb, argument }` |
|
|
378
|
-
| `parsePathCommand(argument, 'FROM' \| 'TO')`, `PathCommand` | the argument of MAIL or RCPT to `{ path, parameters }` |
|
|
436
|
+
| `parsePathCommand(argument, 'FROM' \| 'TO')`, `PathCommand` | the argument of MAIL or RCPT to `{ path, parameters }`; `TO:` also takes `<postmaster>` with no domain, in any case (RFC 5321 §4.1.1.3) |
|
|
437
|
+
| `clientKey(address)` | which client a remote address counts as, for a limit per client: an IPv4 address as it is, an IPv4-mapped or NAT64 address as its IPv4 address, any other IPv6 address as its /64, its first four groups written out, zeros included (`'2001:db8:1:2::/64'`, `'2001:db8:0:0::/64'` for `2001:db8::1`); `undefined` for what is not an IP address |
|
|
379
438
|
| `DataReader`, `DataChunk` | reads DATA: dot-unstuffing, the terminator, bare CR and LF counted |
|
|
380
439
|
| `decodePlain(response)`, `decodeLoginStep(response)` | SASL PLAIN (RFC 4616) and LOGIN responses to `Credentials` or text |
|
|
381
440
|
|
|
@@ -26,6 +26,12 @@ class SmtpError2 extends Error {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// src/protocol/path.ts
|
|
29
|
+
var POSTMASTER = Object.freeze({
|
|
30
|
+
address: "postmaster",
|
|
31
|
+
local: "postmaster",
|
|
32
|
+
domain: "",
|
|
33
|
+
postmaster: true
|
|
34
|
+
});
|
|
29
35
|
var ATOM = /^[A-Za-z0-9!#$%&'*+\-/=?^_`{|}~\u0080-\u{10ffff}]+$/u;
|
|
30
36
|
var LABEL = /^(?:[A-Za-z0-9\u0080-\u{10ffff}](?:[A-Za-z0-9\-\u0080-\u{10ffff}]*[A-Za-z0-9\u0080-\u{10ffff}])?)$/u;
|
|
31
37
|
var INVISIBLE = /[\u0080-\u009f\u2028\u2029\p{Cf}\p{Cs}]/u;
|
|
@@ -243,7 +249,7 @@ function concat(a, b) {
|
|
|
243
249
|
return out;
|
|
244
250
|
}
|
|
245
251
|
|
|
246
|
-
export { SmtpError2, isHelloName, parsePath2, decodePlain2, decodeLoginStep2, encodePlain, encodeLoginStep, MAX_LINE, LineSplitter, Outgoing };
|
|
252
|
+
export { SmtpError2, POSTMASTER, isHelloName, parsePath2, decodePlain2, decodeLoginStep2, encodePlain, encodeLoginStep, MAX_LINE, LineSplitter, Outgoing };
|
|
247
253
|
|
|
248
|
-
//# debugId=
|
|
249
|
-
//# sourceMappingURL=index-
|
|
254
|
+
//# debugId=26FD99243A8649B964756E2164756E21
|
|
255
|
+
//# sourceMappingURL=index-753q702a.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/errors.ts", "../src/protocol/path.ts", "../src/protocol/sasl.ts", "../src/io/outgoing.ts", "../src/protocol/lines.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { Reply } from './protocol/reply';\n\n/** Why the server refused an option, why a message stream ended in error, or why `sendMail` failed. */\nexport type SmtpErrorCode =\n\t| 'INVALID_OPTION'\n\t| 'ALREADY_LISTENING'\n\t| 'MESSAGE_TOO_BIG'\n\t| 'BARE_LINE_BREAK'\n\t| 'CONNECTION_LOST'\n\t| 'HOOK_TIMEOUT'\n\t| 'INVALID_HOOK_REPLY'\n\t| 'MESSAGE_NOT_READ'\n\t| 'CONNECTION_FAILED'\n\t| 'TIMEOUT'\n\t| 'BAD_REPLY'\n\t| 'REFUSED'\n\t| 'RECIPIENTS_REFUSED'\n\t| 'TLS_UNAVAILABLE'\n\t| 'TLS_FAILED'\n\t| 'AUTH_UNAVAILABLE'\n\t| 'EXTENSION_MISSING'\n\t| 'NULL_MX'\n\t| 'DNS_FAILED';\n\n/** One recipient and the server's reply to its `RCPT TO`. */\nexport interface RecipientReply {\n\treadonly recipient: string;\n\treadonly reply: Reply;\n}\n\n/** What `sendMail` adds to an error: whether to try again, and the reply behind it. */\nexport interface SmtpErrorDetails {\n\t/** Trying again later may succeed: a 4xx, the network, a timeout. */\n\treadonly temporary?: boolean;\n\t/** The server's reply that failed the delivery, enhanced status code included. */\n\treadonly reply?: Reply;\n\t/** With `RECIPIENTS_REFUSED`: each recipient, and why it was refused. */\n\treadonly rejected?: readonly RecipientReply[];\n}\n\n/** The codes that are temporary unless a reply or `details` says otherwise. */\nconst TEMPORARY = new Set<SmtpErrorCode>([\n\t'CONNECTION_LOST',\n\t'CONNECTION_FAILED',\n\t'TIMEOUT',\n\t'BAD_REPLY',\n\t'TLS_UNAVAILABLE',\n\t'TLS_FAILED',\n]);\n\n/**\n * Thrown by `createSmtpServer` and `listen` for a wrong option or call; the\n * error a message's `content` stream ends with when the message must not be\n * delivered; what `onError` is given for a hook that broke a rule; and what\n * `sendMail` rejects with. `temporary` tells a failure worth trying again\n * later (a 4xx, the network, a timeout) from a permanent one (a 5xx, a null\n * MX, a message the server can never take).\n */\nexport class SmtpError extends Error {\n\toverride readonly name = 'SmtpError';\n\treadonly code: SmtpErrorCode;\n\t/** Trying again later may succeed. Meaningful for `sendMail`'s errors. */\n\treadonly temporary: boolean;\n\t/** The server reply behind a `sendMail` failure, enhanced status code included. */\n\treadonly reply?: Reply;\n\t/** With `RECIPIENTS_REFUSED`: each recipient, and the reply that refused it. */\n\treadonly rejected?: readonly RecipientReply[];\n\n\tconstructor(\n\t\tcode: SmtpErrorCode,\n\t\tmessage: string,\n\t\tdetails: SmtpErrorDetails = {},\n\t) {\n\t\tsuper(message);\n\t\tthis.code = code;\n\t\tthis.temporary =\n\t\t\tdetails.temporary ??\n\t\t\t(details.reply ? details.reply.code < 500 : TEMPORARY.has(code));\n\t\tif (details.reply) this.reply = details.reply;\n\t\tif (details.rejected) this.rejected = details.rejected;\n\t}\n}\n",
|
|
6
|
+
"/** An envelope address, as `MAIL FROM` and `RCPT TO` give it. */\nexport interface Path {\n\t/**\n\t * `local@domain`; `''` for the null reverse-path `<>`; `'postmaster'`\n\t * for `RCPT TO:<postmaster>`, which has no domain.\n\t */\n\treadonly address: string;\n\treadonly local: string;\n\t/** Lower case; `''` for `<>` and for `<postmaster>`. */\n\treadonly domain: string;\n\t/**\n\t * `true` for `RCPT TO:<postmaster>`, in any case, with no domain (RFC\n\t * 5321 §4.5.1): the postmaster of this server, whatever domains it\n\t * hosts. Absent for every other path, `<postmaster@domain>` included.\n\t */\n\treadonly postmaster?: true;\n}\n\n/** The bare `<postmaster>` path `RCPT TO` takes (RFC 5321 §4.1.1.3, §4.5.1). */\nexport const POSTMASTER: Path = Object.freeze({\n\taddress: 'postmaster',\n\tlocal: 'postmaster',\n\tdomain: '',\n\tpostmaster: true,\n});\n\nconst ATOM = /^[A-Za-z0-9!#$%&'*+\\-/=?^_`{|}~\\u0080-\\u{10ffff}]+$/u;\nconst LABEL =\n\t/^(?:[A-Za-z0-9\\u0080-\\u{10ffff}](?:[A-Za-z0-9\\-\\u0080-\\u{10ffff}]*[A-Za-z0-9\\u0080-\\u{10ffff}])?)$/u;\n\n/**\n * C1 controls, Unicode format characters (zero-width, bidi, BOM), the\n * line and paragraph separators, and lone surrogates: none has a place in\n * an address, and each would land as it is in the Received field and the\n * app's records — a lone surrogate as U+FFFD, once encoded for the wire.\n */\nconst INVISIBLE = /[\\u0080-\\u009f\\u2028\\u2029\\p{Cf}\\p{Cs}]/u;\n\n/**\n * C0 controls (CR, LF and NUL among them), DEL and `>`, anywhere in the\n * path, quoted or not: a CR or an LF would end the command line and start\n * another, and a `>` would end the path where a reader of the line stops.\n */\nfunction hasControl(text: string): boolean {\n\tfor (let i = 0; i < text.length; i++) {\n\t\tconst code = text.charCodeAt(i);\n\t\tif (code < 0x20 || code === 0x7f || code === 0x3e) return true;\n\t}\n\treturn false;\n}\n\n/** One hop of a source route, `@domain`, and what follows it: `,` or `:`. */\nconst HOP = /^@(\\[[^\\]]*\\]|[^,:@[\\]]*)([,:])/;\n\nfunction isDotString(text: string): boolean {\n\treturn text.split('.').every((atom) => ATOM.test(atom));\n}\n\nfunction isQuotedString(text: string): boolean {\n\treturn /^\"(?:[\\x20\\x21\\x23-\\x5b\\x5d-\\x7e\\u0080-\\u{10ffff}]|\\\\[\\x20-\\x7e])*\"$/u.test(\n\t\ttext,\n\t);\n}\n\n/**\n * An address literal (§4.1.3): `[IPv6:…]`, its tag in any case (a quoted\n * string in RFC 5234 is), or `[a.b.c.d]` with each octet at most 255.\n */\nexport function isAddressLiteral(text: string): boolean {\n\tif (/^\\[IPv6:[0-9a-f:.]+\\]$/i.test(text)) return true;\n\tconst ipv4 = /^\\[(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\]$/.exec(text);\n\treturn ipv4?.slice(1).every((octet) => Number(octet) <= 255) === true;\n}\n\n/**\n * The argument of EHLO or HELO (RFC 5321 §4.1.1.1): a host name of\n * letters, digits and hyphens, or an address literal.\n */\nexport function isHelloName(text: string): boolean {\n\treturn (\n\t\t/^[A-Za-z0-9-]+(?:\\.[A-Za-z0-9-]+)*$/.test(text) || isAddressLiteral(text)\n\t);\n}\n\nfunction isDomain(domain: string): boolean {\n\tif (isAddressLiteral(domain)) return true;\n\treturn (\n\t\tdomain.length <= 255 &&\n\t\tdomain.split('.').every((label) => label.length <= 63 && LABEL.test(label))\n\t);\n}\n\n/**\n * What `parsePath` does with a source route (`<@a,@b:local@domain>`, RFC 5321\n * Appendix C): a server accepts and discards it (`'discard'`), a client\n * refuses it, as §4.1.1.3 says clients should not send one (`'refuse'`).\n */\nexport type SourceRoute = 'discard' | 'refuse';\n\n/**\n * The mailbox after a source route, each of whose hops must be\n * `@domain` with a valid domain; `undefined` for any other route.\n */\nfunction afterRoute(inner: string): string | undefined {\n\tlet rest = inner;\n\tfor (;;) {\n\t\tconst hop = HOP.exec(rest);\n\t\tif (!hop || !isDomain(hop[1] as string)) return undefined;\n\t\trest = rest.slice(hop[0].length);\n\t\tif (hop[2] === ':') return rest;\n\t}\n}\n\n/**\n * Parses the `<path>` of RFC 5321 §4.1.2: `<local@domain>`, or `<>` when\n * `allowNull`. A source route (`<@a,@b:local@domain>`, §C) is dropped when\n * `sourceRoute` is `'discard'`, the default, provided each hop is\n * `@domain`; with `'refuse'` the path is not one. A C0 control (CR, LF,\n * NUL…), DEL or `>` anywhere refuses the path, as do C1 controls,\n * U+2028 and U+2029, Unicode format characters and lone surrogates. An IPv4 address literal\n * takes octets up to 255.\n * Non-ASCII is allowed: whether the session may use it is SMTPUTF8's\n * question (RFC 6531), not the grammar's. `undefined` when it is not a path.\n *\n * The local part is kept as written, quotes included: `\"v@x.example\"` and\n * `v%x.example` are local parts of the domain after the last `@`, not\n * addresses elsewhere. Code that delivers must not split on the first `@`.\n */\nexport function parsePath(\n\ttext: string,\n\tallowNull: boolean,\n\tsourceRoute: SourceRoute = 'discard',\n): Path | undefined {\n\tif (!text.startsWith('<') || !text.endsWith('>')) return undefined;\n\tlet inner: string | undefined = text.slice(1, -1);\n\tif (hasControl(inner) || INVISIBLE.test(inner)) return undefined;\n\tif (inner === '')\n\t\treturn allowNull ? { address: '', local: '', domain: '' } : undefined;\n\tif (inner.startsWith('@')) {\n\t\tif (sourceRoute === 'refuse') return undefined;\n\t\tinner = afterRoute(inner);\n\t\tif (inner === undefined) return undefined;\n\t}\n\tconst at = inner.lastIndexOf('@');\n\tif (at <= 0) return undefined;\n\tconst local = inner.slice(0, at);\n\tconst domain = inner.slice(at + 1);\n\tif (local.length > 64 || !(isDotString(local) || isQuotedString(local)))\n\t\treturn undefined;\n\tif (!isDomain(domain)) return undefined;\n\treturn {\n\t\taddress: `${local}@${domain.toLowerCase()}`,\n\t\tlocal,\n\t\tdomain: domain.toLowerCase(),\n\t};\n}\n",
|
|
7
|
+
"/** Credentials a client gave with `AUTH`. */\nexport interface Credentials {\n\treadonly mechanism: 'PLAIN' | 'LOGIN';\n\treadonly username: string;\n\treadonly password: string;\n\t/**\n\t * PLAIN's authorization identity, when it differs from the username.\n\t * The server refuses such credentials before `authenticate` is asked.\n\t */\n\treadonly authorizationId?: string;\n}\n\nfunction base64ToText(text: string): string | undefined {\n\tif (!/^[A-Za-z0-9+/]*={0,2}$/.test(text) || text.length % 4 !== 0)\n\t\treturn undefined;\n\ttry {\n\t\treturn new TextDecoder('utf-8', { fatal: true }).decode(\n\t\t\tUint8Array.fromBase64(text),\n\t\t);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/** PLAIN (RFC 4616): `authzid NUL authcid NUL passwd`, in base64. */\nexport function decodePlain(response: string): Credentials | undefined {\n\tconst text = base64ToText(response);\n\tif (text === undefined) return undefined;\n\tconst parts = text.split('\\0');\n\tif (parts.length !== 3) return undefined;\n\tconst [authorizationId = '', username = '', password = ''] = parts;\n\tif (username === '' || password === '') return undefined;\n\treturn {\n\t\tmechanism: 'PLAIN',\n\t\tusername,\n\t\tpassword,\n\t\t...(authorizationId === '' || authorizationId === username\n\t\t\t? {}\n\t\t\t: { authorizationId }),\n\t};\n}\n\n/** One step of LOGIN: a base64 line to its text. */\nexport function decodeLoginStep(response: string): string | undefined {\n\treturn base64ToText(response);\n}\n\nconst base64 = (text: string) => new TextEncoder().encode(text).toBase64();\n\n/** A client's PLAIN response (RFC 4616): `NUL username NUL password`, in base64, no authorization identity. */\nexport function encodePlain(username: string, password: string): string {\n\treturn base64(`\\0${username}\\0${password}`);\n}\n\n/** One step of LOGIN, the username or the password, in base64. */\nexport function encodeLoginStep(text: string): string {\n\treturn base64(text);\n}\n",
|
|
8
|
+
"/**\n * What waits to leave a Bun socket. Bun's sockets do not buffer: `write`\n * takes what the kernel takes and says how much. The rest waits here for\n * the socket's `drain` event, so nothing written is lost to a peer that\n * reads slowly. The server's transport and the client's socket share it.\n */\nexport class Outgoing {\n\treadonly #write: (bytes: Uint8Array) => number;\n\t#queue: Uint8Array[] = [];\n\t#waiters: (() => void)[] = [];\n\n\tconstructor(write: (bytes: Uint8Array) => number) {\n\t\tthis.#write = write;\n\t}\n\n\t/** Nothing waits to leave. */\n\tget empty(): boolean {\n\t\treturn this.#queue.length === 0;\n\t}\n\n\t/** Writes what the socket takes now, and keeps the rest for `drain`. */\n\twrite(bytes: Uint8Array): void {\n\t\tif (this.#queue.length > 0) {\n\t\t\tthis.#queue.push(bytes);\n\t\t\treturn;\n\t\t}\n\t\tconst written = this.#write(bytes);\n\t\tif (written < bytes.length) {\n\t\t\tthis.#queue.push(bytes.subarray(Math.max(written, 0)));\n\t\t}\n\t}\n\n\t/** The socket's `drain`: it can take more. True once everything has left. */\n\tdrain(): boolean {\n\t\twhile (this.#queue.length > 0) {\n\t\t\tconst bytes = this.#queue[0] as Uint8Array;\n\t\t\tconst written = this.#write(bytes);\n\t\t\tif (written < bytes.length) {\n\t\t\t\tthis.#queue[0] = bytes.subarray(Math.max(written, 0));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tthis.#queue.shift();\n\t\t}\n\t\tfor (const wake of this.#waiters.splice(0)) wake();\n\t\treturn true;\n\t}\n\n\t/** Resolves once everything written has left, or the socket closed. */\n\tdrained(): Promise<void> {\n\t\tif (this.#queue.length === 0) return Promise.resolve();\n\t\treturn new Promise((wake) => this.#waiters.push(wake));\n\t}\n\n\t/** The socket closed: nothing more will leave. */\n\tclear(): void {\n\t\tthis.#queue = [];\n\t\tfor (const wake of this.#waiters.splice(0)) wake();\n\t}\n}\n",
|
|
9
|
+
"/**\n * The longest line taken, CRLF included: a command (RFC 5321 §4.5.3.1.4\n * asks for 512 at least) or a reply line (§4.5.3.1.5, 512 too).\n */\nexport const MAX_LINE = 2048;\n\n/** One line from the peer, or a line that was too long and was skipped. */\nexport type LineEvent = { line: string } | { tooLong: true };\n\n/**\n * Cuts a peer's bytes into lines — a client's commands, a server's\n * replies — ending at LF with or without CR. It holds one partial line at most: past `MAX_LINE`, the line is\n * skipped to its end and reported once.\n */\nexport class LineSplitter {\n\t#held: Uint8Array = new Uint8Array(0);\n\t#skipping = false;\n\t#rest: Uint8Array = new Uint8Array(0);\n\n\t/** Starts on a chunk; read its lines with `next`. */\n\tpush(chunk: Uint8Array): void {\n\t\tthis.#rest = this.#held.length > 0 ? concat(this.#held, chunk) : chunk;\n\t\tthis.#held = new Uint8Array(0);\n\t}\n\n\t/** The next line of the current chunk, or `undefined` when it has no more. */\n\tnext(): LineEvent | undefined {\n\t\tfor (;;) {\n\t\t\tconst data = this.#rest;\n\t\t\tconst lf = data.indexOf(0x0a);\n\t\t\tif (lf < 0) {\n\t\t\t\tthis.#rest = new Uint8Array(0);\n\t\t\t\tif (this.#skipping) return undefined;\n\t\t\t\tif (data.length > MAX_LINE) {\n\t\t\t\t\tthis.#skipping = true;\n\t\t\t\t\treturn { tooLong: true };\n\t\t\t\t}\n\t\t\t\tthis.#held = data.slice();\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tconst end = lf > 0 && data[lf - 1] === 0x0d ? lf - 1 : lf;\n\t\t\tthis.#rest = data.subarray(lf + 1);\n\t\t\tif (this.#skipping) {\n\t\t\t\tthis.#skipping = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (end > MAX_LINE) return { tooLong: true };\n\t\t\treturn { line: new TextDecoder().decode(data.subarray(0, end)) };\n\t\t}\n\t}\n\n\t/** Bytes are held or left: a line begun, or lines not yet read. */\n\tget holding(): boolean {\n\t\treturn this.#held.length > 0 || this.#rest.length > 0;\n\t}\n\n\t/** What is left of the current chunk: message content pipelined after DATA. */\n\ttakeRest(): Uint8Array {\n\t\tconst rest = this.#rest;\n\t\tthis.#rest = new Uint8Array(0);\n\t\treturn rest;\n\t}\n\n\t/** Forgets everything held: after STARTTLS, or after a refused DATA. */\n\tclear(): void {\n\t\tthis.#held = new Uint8Array(0);\n\t\tthis.#rest = new Uint8Array(0);\n\t\tthis.#skipping = false;\n\t}\n}\n\nfunction concat(a: Uint8Array, b: Uint8Array): Uint8Array {\n\tconst out = new Uint8Array(a.length + b.length);\n\tout.set(a, 0);\n\tout.set(b, a.length);\n\treturn out;\n}\n"
|
|
10
|
+
],
|
|
11
|
+
"mappings": "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",
|
|
12
|
+
"debugId": "26FD99243A8649B964756E2164756E21",
|
|
13
|
+
"names": []
|
|
14
|
+
}
|
package/dist/client/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
MAX_LINE,
|
|
8
8
|
LineSplitter,
|
|
9
9
|
Outgoing
|
|
10
|
-
} from "../chunks/index-
|
|
10
|
+
} from "../chunks/index-753q702a.js";
|
|
11
11
|
// src/client/address.ts
|
|
12
12
|
function isMailbox(address) {
|
|
13
13
|
return typeof address === "string" && parsePath2(`<${address}>`, false, "refuse") !== undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { type DataChunk, DataReader } from './protocol/data';
|
|
|
4
4
|
export { type Path, parsePath, type SourceRoute } from './protocol/path';
|
|
5
5
|
export { formatReply, type Reply, reply } from './protocol/reply';
|
|
6
6
|
export { type Credentials, decodeLoginStep, decodePlain, } from './protocol/sasl';
|
|
7
|
+
export { clientKey } from './server/client-key';
|
|
7
8
|
export type { Envelope, HookResult, ReceivedMessage, Session, SmtpHooks, SmtpServerOptions, TlsOptions, } from './server/options';
|
|
8
9
|
export { createSmtpServer, type SmtpServer } from './server/server';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,YAAY,EACZ,gBAAgB,GAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,KAAK,IAAI,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,KAAK,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EACN,KAAK,WAAW,EAChB,eAAe,EACf,WAAW,GACX,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACX,QAAQ,EACR,UAAU,EACV,eAAe,EACf,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,UAAU,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,YAAY,EACZ,gBAAgB,GAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,KAAK,IAAI,EAAE,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,KAAK,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EACN,KAAK,WAAW,EAChB,eAAe,EACf,WAAW,GACX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,YAAY,EACX,QAAQ,EACR,UAAU,EACV,eAAe,EACf,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,UAAU,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
|