fix(smtp): skip the reverse DNS lookup of connecting clients (#104)

smtp-server reverse resolves every connecting client before it sends the
220 greeting, unless disableReverseLookup is set. MailDev never reads the
resolved hostname, and without a network the lookup does not answer, so
each connection waited for smtp-server to give up on it.
This commit is contained in:
dualfroz committed 2026-10-03 02:18:43 +02:00
1 parent 53640c6990
commit 3f93b59a2e
3 files changed
+60

No files matched your search

@@ -0,0 +1,49 @@
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import dns from 'node:dns'
import { mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import nodemailer from 'nodemailer'
import { MemoryStorage } from '@maildev/core'
import { SMTPServer } from '../server.js'
describe('client reverse DNS lookup', () => {
let mailDir: string
let storage: MemoryStorage
let server: SMTPServer
beforeEach(async () => {
mailDir = await mkdtemp(join(tmpdir(), 'maildev-reverse-'))
storage = new MemoryStorage()
await storage.initialize()
server = new SMTPServer({ storage, mailDir, port: 0, host: '127.0.0.1' })
await server.start()
})
afterEach(async () => {
vi.restoreAllMocks()
await server.stop()
await rm(mailDir, { recursive: true, force: true })
})
// Without a network the lookup never answers, and smtp-server holds back the
// 220 greeting until it gives up on it (#104).
it('accepts mail without reverse resolving the connecting client', async () => {
const reverse = vi.spyOn(dns, 'reverse').mockImplementation(() => {})
const transport = nodemailer.createTransport({
host: '127.0.0.1',
port: server.getPort(),
secure: false,
})
await transport.sendMail({
from: 'a@example.com',
to: 'b@example.com',
subject: 'offline',
text: 'hello',
})
expect(await storage.getAll()).toHaveLength(1)
expect(reverse).not.toHaveBeenCalled()
})
})
+3
View File
@@ -524,6 +524,9 @@ export class SMTPServer extends EventEmitter {
logger: false,
hideSTARTTLS: true,
disabledCommands: ['AUTH'] as string[],
// The client hostname is never used, and without a network the lookup
// stalls every connection before the greeting (#104).
disableReverseLookup: true,
}
// Advertise and enforce a maximum message size. smtp-server rejects