Email MIME
2018-05-19
说明
多用途互联网邮件扩展
Multipurpose Internet Mail Extensions
最初的电子邮件标准 RFC 822 只支持发送 ASCII 字符文本内容,通过 MIME 这个拓展(RFC 2822),可以发送所有类型的内容。
后面的 HTTP 协议也是在 MIME 这个框架内构建的,Web 开发者比较熟悉的那几个 Content-xxx 头就是从 MIME 里面来的。
规范文件
| RFC No. |
Type |
Title |
| RFC 2045 |
Standards Track |
Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies |
| RFC 2046 |
Standards Track |
Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types |
| RFC 2047 |
Standards Track |
MIME (Multipurpose Internet Mail Extensions) Part Three: Message Header Extensions for Non-ASCII Text |
| RFC 2048 |
Best Current Practice |
Multipurpose Internet Mail Extensions (MIME) Part Four: Registration Procedures |
| RFC 2049 |
Standards Track |
Multipurpose Internet Mail Extensions (MIME) Part Five: Conformance Criteria and Examples |
语法
MIME-Version: 1.0 // MIME 版本
Content-Type: [type]/[subtype]; parameter // 内容类型
Content-Transfer-Encoding: [encoding] // 内容传输编码
Content-Disposition: [disposition] // 内容配置
内容类型 Content-Type
又叫互联网媒体类型(Internet media type)或者 MIME 类型(MIME type)。
类型信息的注册事宜,由 IANA(Internet Assigned Numbers Authority)统一管理。
按照注册来源分成几种类型,只用关心标准数,这个了解一下就行了:
- 标准树
类型名 / 子类型名 [ + 后缀 ] [ ; 可选参数 ]
- 厂商树
类型名 / vnd.子类型名 [ + 后缀 ] [ ; 可选参数 ],例如:application/vnd.debian.binary-package
- 个人树
类型名 / prs.子类型名 [ + 后缀 ] [ ; 可选参数 ]
- 未注册的 x.树
类型名 / x.子类型名 [ + 后缀 ] [ ; 可选参数 ]
常见的类型
详细类型在 IANA 官网有,链接在下面参考资料部分我贴了一个。
| Type |
Subtype |
Description |
| text |
plain |
文本 |
| text |
html |
HTML |
| text |
xml |
XML |
| text |
javascript |
JavaScript |
| text |
css |
CSS |
| text |
csv |
CSV |
| text |
vcard |
vCard 电子名片 |
| image |
bmp |
|
| image |
jpeg |
|
| image |
png |
|
| image |
gif |
|
| image |
webp |
|
| image |
svg+xml |
|
| image |
icon |
|
| audio |
mpeg |
|
| audio |
mp4 |
|
| audio |
ogg |
|
| audio |
webm |
|
| audio |
flac |
|
| video |
mpeg |
|
| video |
mp4 |
|
| video |
ogg |
|
| video |
webm |
|
| application |
xml |
XML |
| application |
json |
JSON |
| application |
ecmascript |
|
| application |
javascript |
|
| application |
zip |
|
| application |
gzip |
|
| application |
pdf |
|
| application |
rss+xml |
|
| application |
atom+xml |
|
| application |
octet-stream |
|
内容传输编码
编码类型主要是这几种:“7bit”,“8bit”,“binary”,“quoted-printable”,“base64”。
SMTP Email
2018-05-18
最常见的三种 SMTP 认证方法:
Python SMTP PythonSimpleServer Email
2018-05-07
Python2
python2 -m smtpd -h
# An RFC 2821 smtp proxy.
# Usage: /usr/lib/python2.7/smtpd.py [options] [localhost:localport [remotehost:remoteport]]
# --nosetuid, -n
# --version, -V
# --class classname, -c classname
# --debug, -d
# --help, -h
Python3
python -m smtpd -h
# An RFC 5321 smtp proxy with optional RFC 1870 and RFC 6531 extensions.
# Usage: /usr/lib/python3.9/smtpd.py [options] [localhost:localport [remotehost:remoteport]]
# --nosetuid, -n 默认会设置用户为 nobody,如果不是 root 会因权限不足失败
# --version, -V
# --class classname, -c classname 默认: PureProxy
# --size limit, -s limit 消息大小限制(RFC 1870 SIZE extension),默认是 33554432 字节,即 32MB
# --smtputf8, -u 启用 SMTPUTF8 扩展(RFC 6531)
# --debug, -d
# --help, -h
# 如果不指定主机,就使用 localhost
# 如果主机是 localhost,端口使用 8025
# 如果是其他主机,端口使用 25
python3 -m smtpd -n
# 默认的 PureProxy 会给转信出去,正常情况会被服务器拒绝
python3 -m smtpd -n -c smtpd.DebuggingServer
Python 3.9 的 PureProxy 有 BUG,会报 process_message() got an unexpected keyword argument 'mail_options'。
自定义黑洞服务器
blackhole.py
import smtpd
import time
class BlackHoleServer(smtpd.SMTPServer):
def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
print('%s %s %s -> %s' % (time.strftime('%Y-%m-%d %H:%M:%S'), peer, mailfrom, rcpttos))
setup.py
import setuptools
setuptools.setup(name="blackhole", py_modules=["blackhole"])
附件下载:blackhole.zip
python setup.py install --user
python -m smtpd -n -c blackhole.BlackHoleServer
测试
import smtplib
smtp = smtplib.SMTP('localhost', 8025)
from_addr = 'admin@markjour.com'
to_addr = 'you@markjour.com'
smtp.sendmail(from_addr, to_addr,
f"""From: {from_addr}\nTo: {to_addr}\nSubject: just4fun\n\nhello, world!""")
Python FTP PythonSimpleServer
2018-05-07
FTP 需要 pyftpdlib 包的支持。
Python HTTP PythonSimpleServer
2018-05-07
Python2
在当前目录起 HTTP 服务,可以用于测试和临时性的文件下载服务。
# Default bind to 0.0.0.0:8000
python -m SimpleHTTPServer
# Maybe you want to use port 8080
python -m SimpleHTTPServer 8080
Python3
除了可以指定端口,还可以指定绑定地址、工作目录。
# Also bind to 0.0.0.0:8000
python -m http.server
python -m http.server -h
# usage: server.py [-h] [--cgi] [--bind ADDRESS] [--directory DIRECTORY] [port]
#
# positional arguments:
# port Specify alternate port [default: 8000]
#
# optional arguments:
# -h, --help show this help message and exit
# --cgi Run as CGI Server
# --bind ADDRESS, -b ADDRESS
# Specify alternate bind address [default: all interfaces]
# --directory DIRECTORY, -d DIRECTORY
# Specify alternative directory [default:current directory]
python -m http.server 9999
python -m http.server --bind=127.0.0.1
python -m http.server --bind=127.0.0.1 9999
python -m http.server -d ~/Pictures
Python Redis
2018-05-06
如果可以的话,使用 redis-cli monitor 命令来输出所有 Redis 命令也很方便。
有时,条件不允许,或者 Redis 需要处理其他的连接,我希望将自己代码调用的 Redis 命令输出到日志中,方便调试。
Email
2018-05-05
相关的文章:
邮件是由纯文本组成,其详细的格式有很多 RFC 规范需要遵守。我这里只能对我所了解的,也是基础的 —— 或者说最核心的 —— 格式做一个说明。
最核心的部分是 1982 年的 RFC 822 (STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES),之后又做过一些更新, 比如 RFC 2822 和 RFC 5322 (Internet Message Format) 和一堆补丁更新。本文要讲的基本格式,从开始到现在并没有什么明显变化。
-
邮件是一种纯文本格式,最开始只包含 ASCII 字符,后来引入了 MIME 之后,可以制定别的编码,比如 UTF-8 等。
-
换行符是 \r\n,也就是 CR + LF。
-
整体来说,一封邮件由邮件头(Headers)和邮件体(Payload)组成。
-
邮件头包含若干个头字段
-
邮件头和邮件体之间用一个空行隔开
-
RFC2882 和 RFC5322 都规定了电子邮件每一行的长度,排除行末 CRLF,不可以超过 998 个字符,建议不超过 78 个字符。
There are two limits that this specification places on the number of
characters in a line. Each line of characters MUST be no more than
998 characters, and SHOULD be no more than 78 characters, excluding
the CRLF.
如果太长,应该拆分成多行,下一行行首加上至少一个空格或者制表符,表示是上一行的延续。
邮件示例
From: Bob <bob@markjour.com>
To: Mark <mark@markjour.com>
Subject: Hello
Hello, Bob,
Would you like to join me for dinner?
--
Mark
RFC 822 中关于字符的定义
; ( Octal, Decimal.)
# 字符
CHAR = <any ASCII character> ; ( 0-177, 0.-127.)
# 字母
ALPHA = <any ASCII alphabetic character>
; (101-132, 65.- 90.)
; (141-172, 97.-122.)
# 数字
DIGIT = <any ASCII decimal digit> ; ( 60- 71, 48.- 57.)
# 控制字符
CTL = <any ASCII control ; ( 0- 37, 0.- 31.)
character and DEL> ; ( 177, 127.)
# 回车
CR = <ASCII CR, carriage return> ; ( 15, 13.)
# 换行
LF = <ASCII LF, linefeed> ; ( 12, 10.)
# 空格
SPACE = <ASCII SP, space> ; ( 40, 32.)
# 制表符
HTAB = <ASCII HT, horizontal-tab> ; ( 11, 9.)
# 引号
<"> = <ASCII quote mark> ; ( 42, 34.)
# 回车换行
CRLF = CR LF
# 空白
LWSP-char = SPACE / HTAB ; semantics = SPACE
# 连贯空白, 折行空白
linear-white-space = 1*([CRLF] LWSP-char) ; semantics = SPACE
; CRLF => folding
# 特殊字符
specials = "(" / ")" / "<" / ">" / "@" ; Must be in quoted-
/ "," / ";" / ":" / "\" / <"> ; string, to use
/ "." / "[" / "]" ; within a word.
# 分隔符
delimiters = specials / linear-white-space / comment
# 文本
text = <any CHAR, including bare ; => atoms, specials,
CR & bare LF, but NOT ; comments and
including CRLF> ; quoted-strings are
; NOT recognized.
# 原子字符
atom = 1*<any CHAR except specials, SPACE and CTLs>
quoted-string = <"> *(qtext/quoted-pair) <">; Regular qtext or
; quoted chars.
qtext = <any CHAR excepting <">, ; => may be folded
"\" & CR, and including
linear-white-space>
domain-literal = "[" *(dtext / quoted-pair) "]"
dtext = <any CHAR excluding "[", ; => may be folded
"]", "\" & CR, & including
linear-white-space>
# 注释
comment = "(" *(ctext / quoted-pair / comment) ")"
ctext = <any CHAR excluding "(", ; => may be folded
")", "\" & CR, & including
linear-white-space>
quoted-pair = "\" CHAR ; may quote any char
phrase = 1*word ; Sequence of words
word = atom / quoted-string
对应上 ASCII:
0 - 31 控制字符, 其中包括常用的:
- HT ( 9) 水平制表符
- LF (10) 换行
- CR (13) 回车
32 空格
33 - 47 符号 !"#$%&'()*+,-./
48 - 57 数字
58 - 64 符号 :;<=>?@
65 - 90 大写字母
91 - 96 符号 [\]^_`
97 - 122 小写字母
123 - 126 符号 {|}~
127 控制字符(DEL)
CHAR 0-127
CTL 0-37 + 127
符号中:
()<>[]@,;:\".
13 个被视作特殊字符,需要转义
!#$%&'*+-/=?^_`
15 个就是普通符号
atom = 数字 + 字母 + 普通符号
邮件头格式
field = field-name ":" [ field-body ] CRLF
field-name = 1*<any CHAR, excluding CTLs, SPACE, and ":">
field-body = field-body-contents
[CRLF LWSP-char field-body]
field-body-contents =
<the ASCII characters making up the field-body, as
defined in the following sections, and consisting
of combinations of atom, quoted-string, and
specials tokens, or else consisting of texts>
字段名称允许使用的字符范围非常宽泛,而且大小写不敏感,
但是一般实践中:
常用字段
Received MTA 轨迹(传输过程中的相关信息)
Date 发信时间,格式:Fri, 21 Nov 1997 09:55:06 -0600
Sender Mail From 地址
From 发件人
Subject 邮件标题
To 收件人
Cc 抄送
Bcc 密送
Reply-To 回复地址
Message-ID 邮件标识
References 回复邮件标识,逗号隔开
In-Reply-To 回复邮件标识(会话发起的第一封)
Return-Path 发信任地址(2020/07/31,邮件的 Return-Path 头是什么)
Comments 说明
Keywords 关键字
一般采用 X- 开头的字段名称表示自定义字段,或者叫拓展字段:
常见的拓展字段:
日志 Linux
2018-05-03
配置
# cat /etc/logrotate.conf
weekly
su root adm
rotate 4
create
#dateext
#compress
include /etc/logrotate.d
以 Nginx 配置为例:
# cat /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily # 按日切割
missingok # 如果文件不存在,则不创建
rotate 14 # 最多保留 14 个日志文件
compress # 压缩
delaycompress # 延迟压缩
notifempty # 如果文件为空,则不创建
create 0640 www-data adm
# 可能一次切割多个日志,
# 但是后面遇到的每个脚本都只执行一次,
# 在所有日志切割之前或之后
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi \
endscript
postrotate
invoke-rc.d nginx rotate >/dev/null 2>&1
endscript
}
其他常用选项:
dateext 部分日志需要添加日期后缀
lastaction/endscript 最后执行的指令,很有用,比如最后将日志备份到某些地方
比如:
rsync -au *.gz 192.168.64.234:/backup/nginx-logs/`hostname -s`/
参考资料与拓展阅读
架构 DNS
2018-05-02
历史
从阿帕网 (ARPANET) 时代一直到互联网的早期,网络节点比较少,都是通过本地 hosts 文件来实现主机名到 IP 地址的映射。
根据维基百科的信息,斯坦福研究所负责维护了一个公共 hosts 文件,大家会找他同步 (rfc606, rfc608)。
PS: 这个时候如果有主机名重复了谁来管?打电话过去让他们改名?
这套机制一直运行了十几年,公共 hosts 文件已经变的很大了,变化也很频繁(IP 可能已经不再那么固定了),需要经常同步。这个时候,斯坦福研究所的网络压力也越来越大了。
后来人们开始设计域名和域名相关的公共设施 (rfc805, rfc830)。最后,在 1983 年,形成了下面两个 RFC 文档:
- RFC 882, DOMAIN NAMES - CONCEPTS and FACILITIES
- RFC 883, DOMAIN NAMES - IMPLEMENTATION and SPECIFICATION
几年后(1987),正式的 DNS 标准 RFC 1034 和 RFC 1035 推出。
这套标准一直运行到现在,可能有对其进行拓展(比如 DNS 记录类型不断添加,Unicode 字符引入),但是基本技术设计没有改变。
DNS 的管理权问题
https://zhidao.baidu.com/question/1386069665602139980.html
基本流程
比如本站域名 www.markjour.com, 其完整形式应该是 www.markjour.com. (后面多一个小数点)
DNS 软件
- BIND
- PowerDNS
- dnsmasq
- Unbound
- CoreDNS
- SmartDNS
Cache-Only DNS Server
新的发展
- 标准的 DNS 是运行在 UDP 53 端口上的。后来的 RFC 1123 增加了 TCP 的支持, 这个方案叫做 DNS over TCP, 还是在 53 端口。
- DNSCrypt, 2011 年设计的, 实现 DNS 的加密和验证,运行于 443 端口。注意:存在于 IETF 框架之外,但是好像有很多服务器支持。
- DNS over TLS (DoT), 2016 年 5 月成为规范。
RFC 7858 Specification for DNS over Transport Layer Security (TLS)
主要作用是加密传输,防止窃听。
- DNS over HTTPS (DoH), 2018 年 10 月成为规范。
RFC 8484 DNS Queries over HTTPS (DoH)
作用和 DoT 一样。
- DNS over TOR, 2019 年。
- Oblivious DNS-over-HTTPS (ODoH), 透过代理的方式,让 DoH 服务器无法获取客户端的真实 IP。同时代理无法获取 DNS 请求的内容。
参考资料与拓展阅读
NodeJS
2018-04-27
sudo apt install -y nodejs npm
# 已经不需要这句了:
# sudo ln -s `which nodejs` /usr/bin/node
# node -v
# npm -v
npm config set registry=https://registry.npm.taobao.org
sudo npm upgrade -g npm
# sudo npm install -g yarn --registry=https://registry.npm.taobao.org
curl --compressed -o- -L https://yarnpkg.com/install.sh | bash
yarn config set registry https://registry.npm.taobao.org
配置
echo registry=https://registry.npm.taobao.org > ~/.npmrc
npm config get registry
https://registry.npmjs.org/
npm config set registry=https://registry.npm.taobao.org
yarn config get registry
https://registry.yarnpkg.com
yarn config set registry https://registry.npm.taobao.org