Anatomia de um e-mail preso em fila: logs de servidores, erros de digitação e a mecânica do Exim com smarthost
Voltar para blog

Anatomia de um e-mail preso em fila: logs de servidores, erros de digitação e a mecânica do Exim com smarthost

09/10/2026 · 7 min · E-mail

Quem gerencia servidores de e-mail com frequência se depara com mensagens retidas na fila de saída sem um motivo óbvio à primeira vista. Muitas vezes, o que parece ser uma instabilidade no servidor de relay ou uma falha de rede é apenas um caractere a mais digitado pelo remetente ao preencher o endereço do destinatário.

No entanto, a forma como o Mail Transfer Agent (MTA) local e o servidor de entrega intermediário (smarthost) reagem a esse detalhe revela uma cadeia interessante de eventos: forks de processos, checagens de DNS em código C, filtros de spam intermediários e bancos de dados locais de retentativas.

Neste artigo, vamos analisar um trecho real extraído dos logs do Exim para entender o ciclo de vida completo de uma mensagem com domínio incorreto, por que ela não retorna imediatamente como erro permanente e como limpar esse acúmulo pelo terminal.


1. O log de eventos no Exim#

Ao investigar mensagens represadas em servidores Linux (como cPanel, DirectAdmin ou instalações dedicadas do Exim), o primeiro passo é buscar referências no arquivo /var/log/exim_mainlog.

O trecho abaixo mostra o rastro de uma mensagem enviada com múltiplos destinatários legítimos, mas com um endereço digitado incorretamente ([email protected] em vez de .com.br):

2026-04-23 10:30:51 1wFu8p-000000062Hd-1Bob <= [email protected] H=(ADMCON037) [186.237.144.42]:58230 P=esmtpsa X=TLS1.2:ECDHE-RSA-AES256-GCM-SHA384:256 A=dovecot_login:[email protected] S=49523 [email protected] T="RES: Listagem de cobranca" for [email protected] [email protected] [email protected] [email protected] [email protected] [email protected]
2026-04-23 10:30:51 cwd=/ 13 args: exim -bm -oem -oi -oMr spam-scanner -oMm 1wFu8p-000000062Hd-1Bob -oMai [email protected] -f [email protected] <[email protected]>
2026-04-23 10:30:51 1wFu8p-000000062NQ-2GiD <= [email protected] R=1wFu8p-000000062Hd-1Bob U=mailnull P=spam-scanner S=50322 [email protected] T="RES: Listagem de cobranca" for [email protected]
2026-04-23 10:30:51 1wFu8p-000000062Hd-1Bob <[email protected]>: imunifyemail_spamfilter_transport transport output: action=no action score=-4.359000/6.000000
2026-04-23 10:30:51 1wFu8p-000000062Hd-1Bob => [email protected] R=imunifyemail_spamfilter_router T=imunifyemail_spamfilter_transport
2026-04-23 10:30:52 1wFu8p-000000062NQ-2GiD == [email protected] R=smarthost_auth T=dkim_remote_smtp defer (-44) H=smarthost.domain.com [216.55.99.57]: SMTP error from remote mail server after RCPT TO:<[email protected]>: 450 4.1.2 <[email protected]>: Recipient address rejected: Domain not found
2026-04-23 10:43:06 1wFu8p-000000062NQ-2GiD == [email protected] routing defer (-52): retry time not reached
2026-04-23 10:58:06 1wFu8p-000000062NQ-2GiD == [email protected] R=smarthost_auth T=dkim_remote_smtp defer (-44) H=smarthost.domain.com [216.55.99.57]: SMTP error from remote mail server after RCPT TO:<[email protected]>: 450 4.1.2 <[email protected]>: Recipient address rejected: Domain not found

2. Decodificando o fluxo linha por linha#

Para entender o motivo pelo qual a mensagem permaneceu no spool, vamos examinar os quatro passos fundamentais desse processo.

Passo 1: Ingress e autenticação do remetente (<=)#

2026-04-23 10:30:51 1wFu8p-000000062Hd-1Bob <= [email protected] H=(ADMCON037) [186.237.144.42]:58230 P=esmtpsa ... A=dovecot_login:[email protected]

Nessa primeira linha, o Exim aceita a conexão de entrada:

Passo 2: O filtro de segurança e o fork com novo ID#

2026-04-23 10:30:51 cwd=/ 13 args: exim -bm -oem -oi -oMr spam-scanner ...
2026-04-23 10:30:51 1wFu8p-000000062NQ-2GiD <= [email protected] R=1wFu8p-000000062Hd-1Bob U=mailnull P=spam-scanner ...

Antes de entregar a mensagem à rede externa, o servidor a submete a um verificador antispam local (como ImunifyEmail ou SpamAssassin):

  1. O Exim executa um novo processo via execve() com o parâmetro -oMr spam-scanner.
  2. A mensagem original é copiada e re-injetada no spool sob o usuário de sistema mailnull.
  3. Essa nova instância recebe um novo ID: 1wFu8p-000000062NQ-2GiD, mantendo o vínculo R=1wFu8p-000000062Hd-1Bob para fins de auditoria.
  4. O filtro atribui uma pontuação de -4.359000 (abaixo do limiar de descarte de 6.0), liberando o envio para o transporte de saída.

Passo 3: O adiamento no smarthost (defer -44)#

2026-04-23 10:30:52 1wFu8p-000000062NQ-2GiD == [email protected] R=smarthost_auth T=dkim_remote_smtp defer (-44) H=smarthost.domain.com [216.55.99.57]: SMTP error from remote mail server after RCPT TO:<[email protected]>: 450 4.1.2 <[email protected]>: Recipient address rejected: Domain not found

Aqui ocorre a primeira retenção:

  450 4.1.2 Recipient address rejected: Domain not found

Passo 4: O algoritmo de recuo e o banco de retentativas (defer -52)#

2026-04-23 10:43:06 1wFu8p-000000062NQ-2GiD == [email protected] routing defer (-52): retry time not reached

Cerca de 12 minutos depois, uma varredura periódica de fila (exim -q) tenta reprocessar as mensagens pendentes. O Exim consulta seu banco de dados de retentativas (retry.db) e identifica a marca routing defer (-52): retry time not reached.

Isso significa que o tempo mínimo de espera calculado pelo algoritmo de recuo exponencial (exponential backoff) ainda não expirou, poupando ciclos de CPU e conexões de rede desnecessárias com o smarthost.


3. Por que o smarthost responde com 450 em vez de 550#

Uma dúvida muito frequente em auditorias de e-mail é: se o domínio não existe, por que o servidor não rejeitou de imediato com um código 550 (erro permanente)?

A resposta está na convenção do protocolo SMTP (RFC 5321) e na implementação interna dos servidores de relay.

O comportamento interno da checagem em c#

Muitos smarthosts utilizam Postfix com a diretiva reject_unknown_recipient_domain ativada nas restrições de destinatário (smtpd_recipient_restrictions).

No código-fonte em C do Postfix (módulo smtpd_check.c), a verificação de domínio chama rotas de biblioteca que realizam consultas DNS:

/* Lógica simplificada de verificação DNS em smtpd_check.c */
int check_domain_dns(const char *domain) {
    unsigned char reply[1024];
    int len;

    /* 1. Consulta MX (Mail Exchanger) */
    len = res_search(domain, C_IN, T_MX, reply, sizeof(reply));
    if (len >= 0) return DNS_FOUND;

    /* 2. Fallback para registro A (IPv4) */
    len = res_search(domain, C_IN, T_A, reply, sizeof(reply));
    if (len >= 0) return DNS_FOUND;

    /* 3. Avaliação do código de erro da biblioteca */
    if (h_errno == HOST_NOT_FOUND) {
        return DNS_NXDOMAIN;
    }
    return DNS_RETRY;
}

Quando a consulta retorna sem sucesso, o servidor precisa decidir entre:

  1. Erro permanente (550): o domínio definitivamente não existe no DNS raiz e não há dúvidas sobre isso.
  2. Erro temporário (450): a consulta falhou, mas pode ter ocorrido uma perda temporária de pacotes UDP na porta 53, timeout dos resolvers locais do smarthost ou uma zona recém-propagada.

Muitos provedores de relay corporativo configuram deliberadamente a recusa como código 450 para evitar descartes incorretos em caso de instabilidades pontuais no DNS. O efeito colateral é que erros de digitação óbvios (como .copm.br ou .gmial.com) não geram um bounce instantâneo para o usuário; em vez disso, a mensagem fica presa na fila tentando ser enviada por horas ou dias.


4. Descartando outras hipóteses antes de agir#

Mesmo quando um erro de digitação parece evidente, vale a pena descartar rapidamente dois cenários antes de limpar a fila:

Cenário 1: Falha na resolução de DNS do servidor local ou do relay#

Para confirmar se o domínio realmente não existe ou se o problema é uma falha pontual de DNS:

# 1. Consulta direcionada a servidores públicos globais
dig MX domain.copm.br @8.8.8.8 +trace +nodnssec

# 2. Verificar se há alguma entrada residual em /etc/hosts
grep -i "domain.copm.br" /etc/hosts

Se o retorno for status: NXDOMAIN, fica confirmado que o domínio não está registrado.

Cenário 2: Bloqueio ou greylisting disfarçado de erro de rota#

Alguns filtros corporativos retornam mensagens genéricas de recusa de endereço quando o remetente atinge limites de frequência ou quando o IP cai em regras de greylisting. É possível simular a conversa SMTP manualmente com o utilitário netcat:

nc -vv smarthost.domain.com 25 << 'EOF'
EHLO meu-servidor.local
MAIL FROM:<[email protected]>
RCPT TO:<[email protected]>
QUIT
EOF

Se a resposta permanecer consistente com 450 Recipient address rejected, trata-se da validação estrita de destinatário do relay.


5. Comandos práticos para inspecionar e limpar a fila#

Abaixo estão os comandos necessários para investigar mensagens retidas no Exim e removê-las com segurança.

Inspecionando metadados, corpo e logs de uma mensagem#

Para inspecionar os cabeçalhos salvos no spool (/var/spool/exim/input/<ID>-H):

exim -Mvh 1wFu8p-000000062NQ-2GiD

Para ler o corpo textual armazenado em disco (/var/spool/exim/input/<ID>-D):

exim -Mvb 1wFu8p-000000062NQ-2GiD

Para listar todos os eventos de log associados exclusivamente a esse identificador:

exim -Mvl 1wFu8p-000000062NQ-2GiD

Consultando o banco de retentativas (retry database)#

O Exim armazena o histórico de tentativas com falha em um banco de dados binário em /var/spool/exim/db/retry. Para consultar esses registros:

exim_dumpdb /var/spool/exim retry

Para forçar o envio imediato da mensagem ignorando o tempo de espera do banco de retentativas:

exim -M -v 1wFu8p-000000062NQ-2GiD

A flag -v exibe a negociação SMTP linha por linha no terminal em tempo real.

Removendo mensagens malformadas da fila#

Depois de confirmar que o endereço contém um erro de digitação e que os outros destinatários da mensagem já receberam suas cópias, remova o item da fila:

exim -Mrm 1wFu8p-000000062NQ-2GiD

Esse comando aciona internamente as chamadas unlink() para os arquivos de cabeçalho (-H) e dados (-D), liberando os inodes e o espaço em disco do servidor.

Se houver muitas mensagens presas para o mesmo domínio inexistente, você pode combinar o utilitário exiqgrep com xargs:

exiqgrep -i -r 'domain.copm.br' | xargs -r exim -Mrm

6. Depuração avançada de conexões com strace#

Se você precisar acompanhar a interação exata de rede entre o Exim e o smarthost (incluindo o envio do comando RCPT TO e o recebimento do código 450), anexe o strace ao processo forçando uma tentativa de entrega:

strace -f -s 512 -e trace=network,write,read exim -M 1wFu8p-000000062NQ-2GiD

Os parâmetros utilizados funcionam da seguinte forma:


Rotina prática para evitar acúmulo de mensagens por erros de digitação#

Para manter a fila do Exim limpa sem depender de intervenções manuais diárias:

  1. Ajuste os tempos de expiração de fila: revise as diretivas ignore_bounce_errors_after e timeout_frozen_after no arquivo de configuração do Exim para que mensagens com erros irreparáveis sejam descartadas automaticamente após 24 ou 48 horas.
  2. Monitore destinatários com falhas recorrentes: crie um script simples em cron para alertar quando um mesmo domínio acumular dezenas de mensagens em estado de adiamento.
  3. Oriente os remetentes: quando notar mensagens retidas por erros tipográficos comuns (.copm.br, .con.br, .gmai.com), avise o usuário da conta para que ele corrija o endereço no cliente de e-mail e evite reenvios sucessivos que alimentam o banco de retentativas.

Este artigo foi útil?

Deixe uma reação rápida para apoiar o conteúdo:

CC BY-NC

Este post está licenciado sob CC BY-NC.

Comentários

Participe da discussão abaixo.

0 comentários