@bumail/smtp 0.4.0 → 0.5.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 +44 -5
- package/dist/chunks/{index-753q702a.js → index-n4xwep5m.js} +3 -3
- package/dist/chunks/{index-753q702a.js.map → index-n4xwep5m.js.map} +3 -3
- package/dist/client/index.js +1 -1
- package/dist/errors.d.ts +3 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +550 -70
- package/dist/index.js.map +15 -8
- package/dist/server/admission.d.ts +29 -0
- package/dist/server/admission.d.ts.map +1 -0
- package/dist/server/listener.d.ts +17 -8
- package/dist/server/listener.d.ts.map +1 -1
- package/dist/server/options.d.ts +21 -1
- package/dist/server/options.d.ts.map +1 -1
- package/dist/server/proxy/address.d.ts +17 -0
- package/dist/server/proxy/address.d.ts.map +1 -0
- package/dist/server/proxy/header.d.ts +26 -0
- package/dist/server/proxy/header.d.ts.map +1 -0
- package/dist/server/proxy/reader.d.ts +15 -0
- package/dist/server/proxy/reader.d.ts.map +1 -0
- package/dist/server/proxy/tls.d.ts +50 -0
- package/dist/server/proxy/tls.d.ts.map +1 -0
- package/dist/server/proxy/trusted.d.ts +9 -0
- package/dist/server/proxy/trusted.d.ts.map +1 -0
- package/dist/server/raw-socket.d.ts +9 -0
- package/dist/server/raw-socket.d.ts.map +1 -0
- package/dist/server/received.d.ts +2 -0
- package/dist/server/received.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/tls-context.d.ts +16 -0
- package/dist/server/tls-context.d.ts.map +1 -0
- package/dist/server/transport.d.ts +2 -2
- package/dist/server/transport.d.ts.map +1 -1
- package/docs/README.md +2 -2
- package/docs/guide.md +345 -7
- package/docs/roadmap.md +13 -0
- package/docs/troubleshooting.md +322 -1
- package/package.json +1 -1
package/docs/troubleshooting.md
CHANGED
|
@@ -24,7 +24,15 @@ A reply carries its enhanced status code (`5.7.1`, RFC 3463) only after
|
|
|
24
24
|
- [`SmtpError: createSmtpServer(): hookTimeout must be at most 2147483 seconds, not …`](#smtperror-createsmtpserver-hooktimeout-must-be-at-most-2147483-seconds-not-)
|
|
25
25
|
- [`SmtpError: createSmtpServer(): greetingDelay must be a number of seconds, 0 or more, not …`](#smtperror-createsmtpserver-greetingdelay-must-be-a-number-of-seconds-0-or-more-not-)
|
|
26
26
|
- [`SmtpError: createSmtpServer(): greetingDelay (… s) must be shorter than timeout (… s), or every client times out before the greeting`](#smtperror-createsmtpserver-greetingdelay--s-must-be-shorter-than-timeout--s-or-every-client-times-out-before-the-greeting)
|
|
27
|
+
- [`SmtpError: createSmtpServer(): proxyProtocol.trusted must list the addresses or CIDRs of the proxies, at least one`](#smtperror-createsmtpserver-proxyprotocoltrusted-must-list-the-addresses-or-cidrs-of-the-proxies-at-least-one)
|
|
28
|
+
- [`SmtpError: createSmtpServer(): proxyProtocol.trusted: "…" is neither an IP address nor a CIDR`](#smtperror-createsmtpserver-proxyprotocoltrusted--is-neither-an-ip-address-nor-a-cidr)
|
|
29
|
+
- [`SmtpError: createSmtpServer(): proxyProtocol.trusted: "…" has a prefix length out of range`](#smtperror-createsmtpserver-proxyprotocoltrusted--has-a-prefix-length-out-of-range)
|
|
30
|
+
- [`SmtpError: createSmtpServer(): proxyProtocol.trusted: "…" has a prefix length of 0, which trusts every peer`](#smtperror-createsmtpserver-proxyprotocoltrusted--has-a-prefix-length-of-0-which-trusts-every-peer)
|
|
31
|
+
- [`SmtpError: createSmtpServer(): proxyProtocol.trusted: … is not a string`](#smtperror-createsmtpserver-proxyprotocoltrusted--is-not-a-string)
|
|
27
32
|
- [`SmtpError: listen(): the server is already listening on …`](#smtperror-listen-the-server-is-already-listening-on-)
|
|
33
|
+
- [`SmtpError: listen(): the server is already starting to listen`](#smtperror-listen-the-server-is-already-starting-to-listen)
|
|
34
|
+
- [`SmtpError: listen(): tls: { key, cert } cannot be used: …`](#smtperror-listen-tls--key-cert--cannot-be-used-)
|
|
35
|
+
- [`SmtpError: listen(): stop() was called before the server bound its port`](#smtperror-listen-stop-was-called-before-the-server-bound-its-port)
|
|
28
36
|
|
|
29
37
|
**Relaying and authentication**
|
|
30
38
|
|
|
@@ -85,6 +93,10 @@ A reply carries its enhanced status code (`5.7.1`, RFC 3463) only after
|
|
|
85
93
|
- [`421 4.3.2 … Too many connections, try later`](#421-432--too-many-connections-try-later)
|
|
86
94
|
- [`421 4.7.0 … Too many connections from your address, try later`](#421-470--too-many-connections-from-your-address-try-later)
|
|
87
95
|
- [On implicit TLS, the connection closes before any greeting](#on-implicit-tls-the-connection-closes-before-any-greeting)
|
|
96
|
+
- [Connections through the proxy close at once, with no greeting](#connections-through-the-proxy-close-at-once-with-no-greeting)
|
|
97
|
+
- [Every client through the proxy is refused with `554 … Talked before the greeting`](#every-client-through-the-proxy-is-refused-with-554--talked-before-the-greeting)
|
|
98
|
+
- [Every client shows the proxy's address](#every-client-shows-the-proxys-address)
|
|
99
|
+
- [Health checks from the proxy show its own address](#health-checks-from-the-proxy-show-its-own-address)
|
|
88
100
|
- [`421 4.4.2 … Idle too long, closing`](#421-442--idle-too-long-closing)
|
|
89
101
|
- [`554 … Talked before the greeting`](#554--talked-before-the-greeting)
|
|
90
102
|
|
|
@@ -409,6 +421,124 @@ createSmtpServer({
|
|
|
409
421
|
});
|
|
410
422
|
```
|
|
411
423
|
|
|
424
|
+
### `SmtpError: createSmtpServer(): proxyProtocol.trusted must list the addresses or CIDRs of the proxies, at least one`
|
|
425
|
+
|
|
426
|
+
**When**: `proxyProtocol` is set without `trusted`, with an empty array, or
|
|
427
|
+
with something that is not an array — a single string included.
|
|
428
|
+
|
|
429
|
+
**Why**: `trusted` names the only peers whose PROXY header is read. Empty,
|
|
430
|
+
it would trust nobody, which is no proxy at all; a server not behind a
|
|
431
|
+
proxy leaves `proxyProtocol` out.
|
|
432
|
+
|
|
433
|
+
**Fix**: list the proxy's own address, in an array:
|
|
434
|
+
|
|
435
|
+
```ts
|
|
436
|
+
import { createSmtpServer } from '@bumail/smtp';
|
|
437
|
+
|
|
438
|
+
createSmtpServer({
|
|
439
|
+
hostname: 'mx.example.com',
|
|
440
|
+
localDomains: ['example.com'],
|
|
441
|
+
proxyProtocol: { trusted: ['10.0.0.5'] }, // not trusted: '10.0.0.5'
|
|
442
|
+
onData: async (message) => {
|
|
443
|
+
await new Response(message.content).bytes();
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### `SmtpError: createSmtpServer(): proxyProtocol.trusted: "…" is neither an IP address nor a CIDR`
|
|
449
|
+
|
|
450
|
+
**When**: an entry of `trusted` is a host name (`traefik`), an address with
|
|
451
|
+
a port (`10.0.0.5:25`), a malformed address, an address with a zone
|
|
452
|
+
(`fe80::1%eth0`: a zone names an interface of this host, never a client),
|
|
453
|
+
or holds more than one `/`.
|
|
454
|
+
`…` is the entry.
|
|
455
|
+
|
|
456
|
+
**Why**: the server matches the TCP address a connection comes from; it
|
|
457
|
+
resolves no name, since a name could resolve to anything later.
|
|
458
|
+
|
|
459
|
+
**Fix**: give the proxy's IP address, or its network as a CIDR. In a
|
|
460
|
+
container network, give the proxy a fixed address and list that:
|
|
461
|
+
|
|
462
|
+
```ts
|
|
463
|
+
import { createSmtpServer } from '@bumail/smtp';
|
|
464
|
+
|
|
465
|
+
createSmtpServer({
|
|
466
|
+
hostname: 'mx.example.com',
|
|
467
|
+
localDomains: ['example.com'],
|
|
468
|
+
proxyProtocol: { trusted: ['172.20.0.2', 'fd00:20::2'] },
|
|
469
|
+
onData: async (message) => {
|
|
470
|
+
await new Response(message.content).bytes();
|
|
471
|
+
},
|
|
472
|
+
});
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### `SmtpError: createSmtpServer(): proxyProtocol.trusted: "…" has a prefix length out of range`
|
|
476
|
+
|
|
477
|
+
**When**: an entry's prefix is past 32 for IPv4 (`10.0.0.0/33`) or 128 for
|
|
478
|
+
IPv6, is empty or not a number (`10.0.0.0/`, `10.0.0.0/8a`), or is below
|
|
479
|
+
96 for an IPv4-mapped address (`::ffff:10.0.0.0/8`).
|
|
480
|
+
|
|
481
|
+
**Why**: an IPv4-mapped entry is taken as its IPv4 address, and its
|
|
482
|
+
prefix counts over the whole IPv6 address, the 96 bits of `::ffff:`
|
|
483
|
+
first: `::ffff:10.0.0.0/104` is `10.0.0.0/8`.
|
|
484
|
+
|
|
485
|
+
**Fix**: write an IPv4 network as IPv4; it matches mapped peers too:
|
|
486
|
+
|
|
487
|
+
```ts
|
|
488
|
+
import { createSmtpServer } from '@bumail/smtp';
|
|
489
|
+
|
|
490
|
+
createSmtpServer({
|
|
491
|
+
hostname: 'mx.example.com',
|
|
492
|
+
localDomains: ['example.com'],
|
|
493
|
+
proxyProtocol: { trusted: ['10.0.0.0/24'] }, // matches ::ffff:10.0.0.5 as well
|
|
494
|
+
onData: async (message) => {
|
|
495
|
+
await new Response(message.content).bytes();
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### `SmtpError: createSmtpServer(): proxyProtocol.trusted: "…" has a prefix length of 0, which trusts every peer`
|
|
501
|
+
|
|
502
|
+
**When**: an entry has a prefix of 0: `0.0.0.0/0`, `::/0`, or
|
|
503
|
+
`::ffff:0.0.0.0/96`, which is the same. `…` is the entry.
|
|
504
|
+
|
|
505
|
+
**Why**: such an entry matches every peer, so any client could send a
|
|
506
|
+
PROXY header and claim any address: the option would defend nothing.
|
|
507
|
+
|
|
508
|
+
**Fix**: list the proxy's own address, or the smallest network it is in:
|
|
509
|
+
|
|
510
|
+
```ts
|
|
511
|
+
proxyProtocol: { trusted: ['172.20.0.2'] }, // not '0.0.0.0/0'
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### `SmtpError: createSmtpServer(): proxyProtocol.trusted: … is not a string`
|
|
515
|
+
|
|
516
|
+
**When**: an entry of `trusted` is not a string: a number, `null`,
|
|
517
|
+
`undefined`, an object. `…` is the value.
|
|
518
|
+
|
|
519
|
+
**Why**: each entry is an address or a CIDR, written as text.
|
|
520
|
+
|
|
521
|
+
**Fix**: write each entry as a string, and filter out what is not set when
|
|
522
|
+
the list comes from the environment:
|
|
523
|
+
|
|
524
|
+
```ts
|
|
525
|
+
import { createSmtpServer } from '@bumail/smtp';
|
|
526
|
+
|
|
527
|
+
const trusted = (Bun.env['SMTP_TRUSTED_PROXIES'] ?? '')
|
|
528
|
+
.split(',')
|
|
529
|
+
.map((entry) => entry.trim())
|
|
530
|
+
.filter((entry) => entry !== '');
|
|
531
|
+
|
|
532
|
+
createSmtpServer({
|
|
533
|
+
hostname: 'mx.example.com',
|
|
534
|
+
localDomains: ['example.com'],
|
|
535
|
+
...(trusted.length > 0 ? { proxyProtocol: { trusted } } : {}),
|
|
536
|
+
onData: async (message) => {
|
|
537
|
+
await new Response(message.content).bytes();
|
|
538
|
+
},
|
|
539
|
+
});
|
|
540
|
+
```
|
|
541
|
+
|
|
412
542
|
### `SmtpError: listen(): the server is already listening on …`
|
|
413
543
|
|
|
414
544
|
**When**: `listen` on a server that is already listening. Its `code` is
|
|
@@ -434,6 +564,67 @@ await createSmtpServer(options).listen({ port: 25 });
|
|
|
434
564
|
await createSmtpServer(options).listen({ port: 2525 });
|
|
435
565
|
```
|
|
436
566
|
|
|
567
|
+
### `SmtpError: listen(): the server is already starting to listen`
|
|
568
|
+
|
|
569
|
+
**When**: a second `listen` on the same server while the first has not
|
|
570
|
+
resolved yet. With `implicitTls`, `listen` reads the key and certificate
|
|
571
|
+
before it binds, so two calls not awaited in turn meet here. Its `code` is
|
|
572
|
+
`ALREADY_LISTENING`; the first call binds, this one binds nothing.
|
|
573
|
+
|
|
574
|
+
**Fix**: await `listen` once per server, and create one server per port:
|
|
575
|
+
|
|
576
|
+
```ts
|
|
577
|
+
const server = createSmtpServer({ ...options, tls, implicitTls: true });
|
|
578
|
+
await server.listen({ port: 465 }); // not Promise.all([server.listen(…), server.listen(…)])
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### `SmtpError: listen(): stop() was called before the server bound its port`
|
|
582
|
+
|
|
583
|
+
**When**: `stop()` ran before `listen` resolved: on an `implicitTls` server
|
|
584
|
+
while it reads the TLS key and certificate, on any other at once, since
|
|
585
|
+
`listen` resolves a tick after it binds. `listen` rejects with
|
|
586
|
+
`code: 'STOPPED'` and leaves nothing listening; a later `listen` binds as
|
|
587
|
+
usual.
|
|
588
|
+
|
|
589
|
+
**Why**: `stop()` means the server is not to serve; binding afterwards would
|
|
590
|
+
leave a listener nothing could stop.
|
|
591
|
+
|
|
592
|
+
**Fix**: nothing is wrong if the stop was meant. To start again, call
|
|
593
|
+
`listen` again:
|
|
594
|
+
|
|
595
|
+
```ts
|
|
596
|
+
await server.listen({ port: 465 }).catch((error) => {
|
|
597
|
+
if (error.code !== 'STOPPED') throw error;
|
|
598
|
+
});
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### `SmtpError: listen(): tls: { key, cert } cannot be used: …`
|
|
602
|
+
|
|
603
|
+
**When**: `listen` on an `implicitTls` server whose `tls.key` or
|
|
604
|
+
`tls.cert` cannot be read — a `Bun.file` that does not exist — or is not
|
|
605
|
+
a PEM key or certificate. The same error, word for word, with
|
|
606
|
+
`proxyProtocol` or without. Its `code` is `INVALID_OPTION`; the rest of
|
|
607
|
+
the message, and its `cause`, are the reason from the file system or
|
|
608
|
+
`node:tls` (`ENOENT: no such file or directory, open '…'`,
|
|
609
|
+
`…PEM routines…NO_START_LINE`). Nothing is bound.
|
|
610
|
+
|
|
611
|
+
**Why**: `listen` reads both and checks them as a TLS context before it
|
|
612
|
+
binds, so a bad key fails at start, not at the first client.
|
|
613
|
+
|
|
614
|
+
**Fix**: pass the PEM text, or a `Bun.file` of a path that exists:
|
|
615
|
+
|
|
616
|
+
```ts
|
|
617
|
+
const tls = {
|
|
618
|
+
key: Bun.file('/etc/bumail/tls/privkey.pem'),
|
|
619
|
+
cert: Bun.file('/etc/bumail/tls/fullchain.pem'),
|
|
620
|
+
};
|
|
621
|
+
try {
|
|
622
|
+
await createSmtpServer({ ...options, tls, implicitTls: true }).listen({ port: 465 });
|
|
623
|
+
} catch (error) {
|
|
624
|
+
console.error(error, (error as Error).cause);
|
|
625
|
+
}
|
|
626
|
+
```
|
|
627
|
+
|
|
437
628
|
## Relaying and authentication
|
|
438
629
|
|
|
439
630
|
### `554 5.7.1 Relay access denied`
|
|
@@ -1443,6 +1634,134 @@ const server = createSmtpServer({
|
|
|
1443
1634
|
});
|
|
1444
1635
|
```
|
|
1445
1636
|
|
|
1637
|
+
### Connections through the proxy close at once, with no greeting
|
|
1638
|
+
|
|
1639
|
+
**When**: a client connecting through a TCP proxy gets no `220`, and the
|
|
1640
|
+
connection closes without a reply — at once, or `handshakeTimeout` seconds
|
|
1641
|
+
(10 by default) after it opened. `onConnect` is not called, `onError` is
|
|
1642
|
+
told nothing, and `connections` does not count the socket.
|
|
1643
|
+
|
|
1644
|
+
**Why**: the server trusts the proxy (`proxyProtocol.trusted` lists it),
|
|
1645
|
+
so it waits for a PROXY header first, and resets a socket whose header is
|
|
1646
|
+
missing, late or not valid, writing nothing. Usually:
|
|
1647
|
+
|
|
1648
|
+
- the proxy is not configured to send the header at all, so the client's
|
|
1649
|
+
bytes, or nothing, come first;
|
|
1650
|
+
- it sends a version 1 line longer than 107 bytes, or a version 2 header
|
|
1651
|
+
with more than 2048 bytes of TLVs;
|
|
1652
|
+
- something other than the proxy connects from a listed address, a health
|
|
1653
|
+
checker that opens a bare TCP connection for instance.
|
|
1654
|
+
|
|
1655
|
+
On an `implicitTls` port, the reverse also closes at once: the proxy sends
|
|
1656
|
+
a header to a server that does not trust it, and the header is not a TLS
|
|
1657
|
+
ClientHello, so the handshake fails.
|
|
1658
|
+
|
|
1659
|
+
**Fix**: send the header from the proxy (with Traefik, a TCP
|
|
1660
|
+
`serversTransport` with `proxyProtocol: { version: 2 }`, named by the
|
|
1661
|
+
service), and list the proxy on the server. Both
|
|
1662
|
+
sides together, or neither:
|
|
1663
|
+
|
|
1664
|
+
```ts
|
|
1665
|
+
import { createSmtpServer } from '@bumail/smtp';
|
|
1666
|
+
|
|
1667
|
+
createSmtpServer({
|
|
1668
|
+
hostname: 'mx.example.com',
|
|
1669
|
+
localDomains: ['example.com'],
|
|
1670
|
+
proxyProtocol: { trusted: ['10.0.0.5'] }, // the address the proxy connects from
|
|
1671
|
+
handshakeTimeout: 10, // the header must arrive within it
|
|
1672
|
+
onData: async (message) => {
|
|
1673
|
+
await new Response(message.content).bytes();
|
|
1674
|
+
},
|
|
1675
|
+
});
|
|
1676
|
+
```
|
|
1677
|
+
|
|
1678
|
+
The [guide](guide.md#with-traefik) has a Traefik configuration.
|
|
1679
|
+
|
|
1680
|
+
### Every client through the proxy is refused with `554 … Talked before the greeting`
|
|
1681
|
+
|
|
1682
|
+
**When**: on a clear port (25, 587), every connection through the proxy
|
|
1683
|
+
gets `554 … Talked before the greeting`, and the server sees every client
|
|
1684
|
+
with the proxy's address. When the header came after the greeting, the
|
|
1685
|
+
client gets replies to input it never sent instead: `500` to a version 1
|
|
1686
|
+
line starting `PROXY`, error replies to the binary of a version 2 header.
|
|
1687
|
+
|
|
1688
|
+
**Why**: the proxy sends a PROXY header, but the server does not trust it:
|
|
1689
|
+
`proxyProtocol` is not set, or `trusted` does not list the address the
|
|
1690
|
+
proxy connects from. To a peer not listed, the header is the client's own
|
|
1691
|
+
input, sent before the greeting, and it never sets an address.
|
|
1692
|
+
|
|
1693
|
+
**Fix**: list the proxy's address in `proxyProtocol.trusted`, as the
|
|
1694
|
+
address the server sees it connect from; see the next entry for how to
|
|
1695
|
+
find it.
|
|
1696
|
+
|
|
1697
|
+
### Every client shows the proxy's address
|
|
1698
|
+
|
|
1699
|
+
**When**: `session.remoteAddress`, the Received field and
|
|
1700
|
+
`maxConnectionsPerClient` see one address, the proxy's, for every client:
|
|
1701
|
+
one more client past `maxConnectionsPerClient` is refused with `421 4.7.0
|
|
1702
|
+
… Too many connections from your address, try later`.
|
|
1703
|
+
|
|
1704
|
+
**Why**: the server is not reading a PROXY header from that peer. Either
|
|
1705
|
+
the proxy sends none — the server then serves its connections as any
|
|
1706
|
+
other's — or the server does not list it, and the header it sends is
|
|
1707
|
+
refused as bad input (the entry above). A common case is a proxy that
|
|
1708
|
+
connects from another address than the one listed: another container
|
|
1709
|
+
network, an IPv6 address, or a new address after a restart. An
|
|
1710
|
+
IPv4-mapped peer (`::ffff:172.20.0.2`) matches an IPv4 entry; it needs no
|
|
1711
|
+
entry of its own.
|
|
1712
|
+
|
|
1713
|
+
**Fix**: log the peer the server sees, then list exactly that address —
|
|
1714
|
+
not the range clients come from:
|
|
1715
|
+
|
|
1716
|
+
```ts
|
|
1717
|
+
import { createSmtpServer } from '@bumail/smtp';
|
|
1718
|
+
|
|
1719
|
+
createSmtpServer({
|
|
1720
|
+
hostname: 'mx.example.com',
|
|
1721
|
+
localDomains: ['example.com'],
|
|
1722
|
+
// Without proxyProtocol, every connection through the proxy shows its address.
|
|
1723
|
+
onConnect: (session) => console.log('peer', session.remoteAddress),
|
|
1724
|
+
onData: async (message) => {
|
|
1725
|
+
await new Response(message.content).bytes();
|
|
1726
|
+
},
|
|
1727
|
+
});
|
|
1728
|
+
```
|
|
1729
|
+
|
|
1730
|
+
Then configure the proxy to send the header, and set
|
|
1731
|
+
`proxyProtocol: { trusted: ['<that address>'] }`.
|
|
1732
|
+
|
|
1733
|
+
### Health checks from the proxy show its own address
|
|
1734
|
+
|
|
1735
|
+
**When**: with `proxyProtocol` set and working, some connections still have
|
|
1736
|
+
the proxy's address in `session.remoteAddress`: they open and close
|
|
1737
|
+
without a transaction, at a steady pace.
|
|
1738
|
+
|
|
1739
|
+
**Why**: by design. The proxy's own connections — health checks — come
|
|
1740
|
+
with a version 2 `LOCAL` header, which names no client, so the server
|
|
1741
|
+
keeps the peer's address, the proxy's. So do a v1 `PROXY UNKNOWN` and a
|
|
1742
|
+
v2 header with an UNSPEC or UNIX address, or over UDP.
|
|
1743
|
+
|
|
1744
|
+
**Fix**: none needed. To keep them out of your logs or your policy, skip
|
|
1745
|
+
the proxy's address in `onConnect`:
|
|
1746
|
+
|
|
1747
|
+
```ts
|
|
1748
|
+
import { createSmtpServer } from '@bumail/smtp';
|
|
1749
|
+
|
|
1750
|
+
const proxies = ['10.0.0.5'];
|
|
1751
|
+
|
|
1752
|
+
createSmtpServer({
|
|
1753
|
+
hostname: 'mx.example.com',
|
|
1754
|
+
localDomains: ['example.com'],
|
|
1755
|
+
proxyProtocol: { trusted: proxies },
|
|
1756
|
+
onConnect: (session) => {
|
|
1757
|
+
if (!proxies.includes(session.remoteAddress)) console.log('client', session.remoteAddress);
|
|
1758
|
+
},
|
|
1759
|
+
onData: async (message) => {
|
|
1760
|
+
await new Response(message.content).bytes();
|
|
1761
|
+
},
|
|
1762
|
+
});
|
|
1763
|
+
```
|
|
1764
|
+
|
|
1446
1765
|
### `421 4.4.2 … Idle too long, closing`
|
|
1447
1766
|
|
|
1448
1767
|
**When**: nothing came from the client for `timeout` seconds (300 by
|
|
@@ -1496,7 +1815,9 @@ client library does; a hand-written client or a test script may not.
|
|
|
1496
1815
|
|
|
1497
1816
|
**Fix**, as the operator: if a legitimate sender is refused, lower
|
|
1498
1817
|
`greetingDelay`, or leave it at `0`; a test client that writes at once
|
|
1499
|
-
needs it at `0`.
|
|
1818
|
+
needs it at `0`. If every client through a TCP proxy gets it, the proxy
|
|
1819
|
+
sends a PROXY header the server does not trust: see
|
|
1820
|
+
[that entry](#every-client-through-the-proxy-is-refused-with-554--talked-before-the-greeting).
|
|
1500
1821
|
|
|
1501
1822
|
## Sending mail: options
|
|
1502
1823
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bumail/smtp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "An SMTP server on Bun.listen — RFC 5321 with PIPELINING, SIZE, 8BITMIME, SMTPUTF8, STARTTLS and AUTH after TLS, policy hooks, and no open relay — and a client that delivers a message to a smarthost or by MX",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|