#29 使用 curl 命令发送邮件

2026-04-05

用 perl 开发的命令行工具 swaks 用来发送邮件挺方便的,但是有些环境不便安装,我问 AI,结果推荐了 curl 命令。

-> % curl --help smtp
smtp: SMTP protocol
     --crlf                       Convert LF to CRLF in upload
 -F, --form <name=content>        Specify multipart MIME data
     --form-string <name=string>  Specify multipart MIME data
 -H, --header <header/@file>      Pass custom header(s) to server
     --login-options <options>    Server login options
     --mail-auth <address>        Originator address of the original email
     --mail-from <address>        Mail from this address
     --mail-rcpt <address>        Mail to this address
     --mail-rcpt-allowfails       Allow RCPT TO command to fail
     --oauth2-bearer <token>      OAuth 2 Bearer Token
 -X, --request <method>           Specify request method to use
     --ssl                        Try enabling TLS
     --ssl-reqd                   Require SSL/TLS

我用 curl 发送了几封邮件,还真的挺好的,满足我的要求。

  • 生成邮件内容:

    export FROM=noreply@example.com
    export RCPT=ninedoors@126.com
    export USER=shanda
    export PASS=6404290f99f75bf182673bcbd012c121
    
    # AI 生成验证码邮件模板
    cat > /tmp/mail.html <<EOF
    From: $FROM
    To: $RCPT
    Subject: Your Verification Code: CODE
    Content-Type: text/html; charset=UTF-8
    
    <!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, initial-scale=1.0"><title>Verification Code</title><style>@media only screen and(max-width:600px){.container{width:100%!important}.mobile-padding{padding:10px!important}}</style></head><body style="margin: 0; padding: 10px; background-color: #f4f4f4; font-family: Arial, sans-serif;"><table role="presentation"align="center"border="0"cellpadding="0"cellspacing="0"width="100%"style="max-width: 600px; width: 100%; margin: 0 auto; background-color: #ffffff;"><tr><td class="mobile-padding"style="padding: 40px 20px;"><table role="presentation"width="100%"border="0"cellpadding="0"cellspacing="0"><tr><td style="padding-bottom: 30px;"><h1 style="color: #333333; margin: 0;">Verification Code</h1></td></tr></table><table role="presentation"width="100%"border="0"cellpadding="0"cellspacing="0"><tr><td style="padding: 20px 0;"><p style="color: #666666; line-height: 1.6; font-size: 16px;">Your verification code is:<h2 style="letter-spacing: 2px;">CODE</h2></p></td></tr></table></td></tr></table></body></html>
    EOF
    
    # 替换验证码内容,我不想每次发送相同内容
    # sed "s/CODE/$(printf "%06d" $((RANDOM % 1000000)))/g" /tmp/mail.html > /tmp/mail2.html
    sed s/CODE/$(python -c "import secrets; print('%06d' % secrets.randbelow(10**6))")/g /tmp/mail.html > /tmp/mail2.html
    
  • 邮件发送:

    curl smtp://smtp.engagelab.cc:2525 --mail-from nobody@whatever.com --mail-rcpt $RCPT --user "$USER:$PASS" --upload-file /tmp/mail2.html -vv -s 2>&1 | grep -Fv '] * '
    

遇到一个奇怪的现象:zsh 中 RANDOM 这样使用结果不会变更,没有时间研究,暂时忽略。

-> % echo $(printf "%06d" $((RANDOM % 1000000)))
018840
-> % echo $(printf "%06d" $((RANDOM % 1000000)))
018840
-> % echo $(printf "%06d" $((RANDOM % 1000000)))
018840

如果直接使用却会,不知道为什么:

for i in {1..5}; do echo $RANDOM done

#28 进箱:电子邮件自动归类

2025-08-17

谷歌(Gmail)和苹果(Apple iCloud)

Gmail: Primary(主要) Social(社交) Updates(动态) “Forums(论坛) Promotions(推广)
Apple: Primary(主要) Transactions(交易) Updates(更新) Promotions(推广)

Gmail 和 Apple 分类比较类似:

  • 主要:个人邮件
  • 更新/动态:资讯、订阅
  • 推广:营销邮件

不同的是,谷歌搞了一个社交与论坛,因为他们有这方面的业务。
苹果则是单独列了一个交易,将订单、物流等邮件单独管理。

微软(Outlook)

  • 重点
  • 其他

这个归类方式就比较简单,可能和微软的在线业务不够丰富、市场占有率不够高(和谷歌、苹果相比)有关。

总结

这个分类就收益于 AI 的出现,体现了 ISP 对电子邮件内容的控制程度的升高。

#27 邮件域名认证机制

2024-03-03

说明:SMTP AUTH (PLAIN / LOGIN) 属于邮件客户端身份认证机制,与上面说的邮件域名认证机制(也叫做邮件认证机制,Email Authentication)是两码事。

邮件域名认证机制包括 SPF、DKIM、DMARC 三种:

  • SPF 校验(RFC 7208):当前 SMTP 会话的客户端 IP 是否被 MAIL FROM 域名授权发送邮件。
    PS:优先使用 MAIL FROM(Envelope-From)域名,若 MAIL FROM 为空(<>),则使用 HELO/EHLO 域名。

  • DKIM 校验:DKIM 通过 DKIM-Signature 头中的 d=(Signing Domain,署域)和 s=(Selector)从 DNS 获取公钥,对指定的 header 和 body 进行签名校验。

    dig txt selector._domainkey.signing-domain
    
  • DMARC 校验(RFC 7489 Domain-based Message Authentication, Reporting, and Conformance (DMARC)):

    DMARC 在 SPF、DKIM 基础上引入了域名 “对齐” (Alignment) 概念,要求邮件 FROM 头域名必须与 SPF 校验所使用的域名 或 DKIM 署域 中的至少一个保持对齐(一致),实践中通常是两者同时保持对齐。
    PS:严格对齐就是要求域名部分完全一致,宽松对齐就是仅要求主域名 (eTLD + 1) 一致,对应 DMARC 记录的 aspf/adkim TAG 的值 r(relaxed mode)与 s(strict mode),如果没有指定,默认就是宽松对齐,实际上主流 MP 都没有指定。

    此外,DMARC 还提供了如果校验失败的处理策略框架 (none 监控 / quarantine 隔离 / reject 拒绝) 与反馈机制 (rua 汇总报告 / ruf 失败报告)。

#25 邮件地址黑名单

2023-12-25
  1. 退订
  2. FBL 举报
  3. 运营商反馈
  4. 收件人反馈(投诉)
  5. 地址不存在
  6. 邮箱容量已满
  7. 平台或客户手动指定的域名或地址
  8. 第三方提供的黑名单地址

有些是永久禁止,有些是临时禁止(比如一个月、三个月、半年)。

#24 SpamHaus

2023-12-05

关于 SpamHaus

Wikipedia:The SpamHaus Project

Spamhaus Project 是一个位于安道尔公国(法国和西班牙中间,498 平方公里)的国际组织,由 Steve Linford 于 1998 年创立,旨在跟踪垃圾邮件发送者和垃圾邮件相关活动。
SpamHaus 这个名字是一个伪德语表达,由林福德创造,指的是向垃圾邮件发送者发送垃圾邮件或故意向垃圾邮件发送者提供服务的互联网服务提供商或其他公司。

该组织维护了几个反垃圾邮件列表,被很多邮件服务采用。
这个列表通过基于 DNS 的黑名单(DNSBL)和白名单(DNSWL)提供,对个人和中小企业免费,大量查询使用应该是有付费服务(采用 rsync 订阅)。
SpamHaus 提供了文档《Understanding DNSBL Filtering》讲解其 DNSBL 的工作原理。

主要列表

  • SBL:Spamhaus Block List,发送垃圾邮件 IP
    • BCL:Spamhaus Botnet Controller List,僵尸网络
    • CSS:Combined Spam Sources,属于 SBL
    • DROP:Don't Route Or Peer,直接丢弃所有流量 IP,提供给防火墙使用,属于 SBL
  • XBL:Exploits Block List,被劫持机器 IP
  • PBL:Policy Block List,未通过身份验证滥发邮件 IP,可以自助移除
  • ZEN:SBL + XBL + PBL
  • DBL:Domain Block List,域名级别拉黑

#23 关于自托管邮件服务

2023-06-10

看到科技爱好者周刊推荐的一篇文章,介绍了自托管邮件服务的一些现状,主要是 Gmail 这样的主流邮箱服务提供商(MSP)拒收来自自托管邮件服务的邮件(或标记成垃圾邮件),导致自托管邮件服务的运营遇到很大的困难。

电子邮件在因特网没有出现之前就已经诞生,简单、开放,易于开发和使用,人人都能成为 Email 网络中的一个节点。实际上,大部分人都是使用的一些大 MSP 的服务,但也有部分人(或者组织)使用的是自己部署的邮件服务。他们会发现哪怕所有应该做的都做了,比如 SPF,DKIM,DMARC,他们的邮件还是经常无法正常投递出去(被拒、限流等),或者在收件人的垃圾文件夹中。

在一定程度上,MSP 的做法也是可以理解的,垃圾邮件泛滥成灾,确实防不胜防。因为邮件服务本身是毫无门槛。除非上一个手机实名制这样的严格管控,或许能解决这个问题。

文章提出的主要价值点是,什么情况下我们有必要自建邮箱服务?

  1. 准备好投入很多时间和精力来维护这套系统
  2. 搭建系统
  3. 留意 SPF 和 DMARC 报告
  4. 有服务器管理能力(Linux,Docker)
  5. ISP 支持开放 25、143、465、587、993 端口
  6. 静态 IP + rDNS 配置权限
  7. 一个合适的域名

#22 SMTP 校验主机域名

2023-02-15

Python

import smtplib
import ssl

host = 'smtp.126.com'
s = smtplib.SMTP(host)
context = ssl.create_default_context()
context.check_hostname = True
s.starttls(context=context)
s.quit()

Golang

如果服务器支持 STARTTLS,标准库 net/smtp 的 SendMail 方法就会校验主机名。

package main

import (
    "crypto/tls"
    "fmt"
    "net/smtp"
)

func main() {
    host := "smtp.126.com"
    port := 25
    c, err := smtp.Dial(fmt.Sprintf("%s:%d", host, port))
    if err != nil {
        panic(err)
    }
    tlsConfig := &tls.Config{ServerName: host}
    if err := c.StartTLS(tlsConfig); err != nil {
        panic(err) // panic: x509: certificate is valid for xxx, not yyy
    }
    if err = c.Quit(); err != nil {
        panic(err)
    }
}

#21 ARC(Authenticated Received Chain)

2023-02-08

谷歌的邮件中看到 ARC-xxx 头,研究了一下,叫做 Authenticated Received Chain
好像中文资料还非常少,直译过来,可能应该叫做 可信邮件转发链
解决的问题是邮件经过一些服务进行中转过程中,原有的安全措施 —— SPF,DKIM,DMARC —— 会失效的问题。

#20 转载:是谁拉黑了你的 IP

2021-11-24

邮件无法送达的原因有很多,例如 取消订阅、服务器不可达、地址格式错误或不存在、被判定为垃圾邮件、发信人/收信人被拒等等等等…今天,我们来聊一下 IP、域名被拒。