	配送関係

------------------------------------------------------------
1	概要

2	事例
2.1	配送プログラム(e.g. sendmail)との通信のログを残す
2.2	ドメインをソートしてから配送
2.3	$OUTGOING_ADDRESS: perl の生存時間を短くする 
2.4	PIPELININGを使わなくする
2.5	複数のマシンの sendmail を使ってパラレルに送信
2.6	VERPs
2.7	MAIL FROM: を指定したい

3	Internal
3.1	SMTPライブラリについて
3.2	予備のSMTPサーバ
3.3	SMTP の MAIL FROM: を $MAINTAINER 以外にしたい時
3.4	同じmxについて
3.5	MCI Cache
3.6	配送効率についての考察
3.7	配送プロセスの律速段階とは

4	リレーサーバを使う
4.1	activesファイルによるリレー制御
4.2	全てのメールをリレーサーバに投げる
4.3	activesファイルによるリレー (sendmail 5.x)
4.4	CF (by motonori@wide.ad.jp) 形式による relay 設定ファイル

5	IPv6
5.1	4.0-current (2001/05/11 以降)
5.2	必要なモジュール
5.3	IPv6 を無効にする

6	Q&A
6.1	root 権限なしに MTA を交換は？
6.2	smtpfeed を直接呼べませんか？
6.3	MTA みくじ
6.4	受信者リストを分割する
------------------------------------------------------------


1	概要

フィルタリングについては => 

リレーについて => 4

このドキュメントは配送にまつわるその他もろもろについて述べます。

2	事例

2.1	配送プログラム(e.g. sendmail)との通信のログを残す

ログをとるのがデフォールトです。

sendmailとのやりとりの記録(もっとも最近のものだけ)デバッグに便利です。
(デバッグとか終わっちゃって)必要ないなら

	$NOT_TRACE_SMTP = 1; 

と config.ph にかいてください。ログをとらなくなります。

2.2	ドメインをソートしてから配送

bulkmailer の用にドメインをソートしてから配送プログラムに突っ込む。
でも現代ではほとんど意味がないだろう。

	$SMTP_SORT_DOMAIN

を設定するとソートを行なう。

2.3	$OUTGOING_ADDRESS: perl の生存時間を短くする 

貧弱なCPUのマシンに貧弱なメモリしかない場合(486で8M,16Mみたいな…)には、
fmlが直接MTAへSMTP配送する時間すら惜しんで perl の生存時間を短くしたい
という需要があります。そのための機能を提供します。
$USE_OUTGOING_ADDRESS が non zero の時に次のように動きます。

まず $OUTGOING_ADDRESS は:include:するだけの単純なMLです。例えば
/etc/aliasesで

	list-going: :include:/some/where/list

のようにかきます。/some/where/list は actives などから生成されたＭＬの
配送リストです。MTAはこのlistの人に配送を試みます。

   if ($USE_OUTGOING_ADDRESS) {
	1. fml は一つのアドレス $OUTGOING_ADDRESS だけに送り終了
	   perl はこの時点で終了します。
	2. MTAが前述の :include: 先の配送リストを展開しそのメンバーに配送
   }

XXX: minmin 様にささぐっす(謎)

[設定の手順]

0.	elena MLを例にとります。

1.	include の見本を作る(include-outgoing)

   % cd /var/spool/ml/elena
   % make outgoing 

include-outgoing という見本のファイルが生成されます。 

2.	/etc/aliases に書いて newaliases

   elena-outgoing: :include:/var/spool/ml/elena/include-outgoing

3. 	makefml config でこの機能を有効にする。

	-> OPTIONS -> SMTP -> USE_OUTGOING_ADDREESS -> "y"

$USE_OUTGOING_ADDDRESS を1にします。

2.4	PIPELININGを使わなくする

	$NOT_USE_ESMTP_PIPELINING = 1;

PIPELINE が可能な場合でも使わない

2.5	複数のマシンの sendmail を使ってパラレルに送信

複数の(sendmailの走っている)マシン(非常時のセカンダリも含む)

	$MCI_SMTP_HOSTS = N;

で指定されたＮ台のマシンを配列 @HOSTS で指定する

   @HOSTS = (machine-1.domain, machine-2.domain, machine-3.domain, ...);

$HOST と @HOSTS で与えられる N個 のマシンの sendmail をパラレルに使っ
て(理論上での限界は)N倍配送を高速化する。
＃ $HOST も使われてしまうのは、もともと @HOSTS はセカンダリだったから
＃ です(歴史的に)。

もし、指定したサーバ数が N 個に足りない場合は、$HOST が何度も使われま
す。Nは理論上 int と同じだろうが、普通は netmask できまるわな(笑)でも、
他のドメインでも使えるから、int までいく(理屈、理屈)
＃とくに actives の最後の方の人はうれしい

2.6	VERPs

See errormails 2.10. 

2.7	MAIL FROM: を指定したい

MAIL FROM:<アドレス> のアドレス部分は、通常$MAINAINER です。しかし 
SPAM 対策のため $MAINAINER につかっているドメイン名では ISP のメールサー
バに拒否される場合があります。

そういった場合は $SMTP_SENDER で MAIL FROM: の値だけを変更することがで
きます。

例:
/var/spool/ml/etc/fml/site_force.ph を作り、このファイルは
次のようにかきます。

	$HOST        = "転送ホスト名";
	$SMTP_SENDER = 'ml-admin@転送ドメイン';
	1;

もし、ML ごとに $SMTP_SENDER を変更するなら

	$HOST="転送ホスト名";
	$myDOMAIN = '転送ドメイン';

	$SMTP_SENDER = $MAINTAINER;
	$SMTP_SENDER =~ s/$DOMAINNAME/$myDOMAIN/;
	1;

などとして SMTP_SENDER を動的に変更するのが楽でしょう。

3	Internal

3.1	SMTPライブラリについて

SMTP ライブラリは SMTP(プロセス間通信)で sendmail 等のMTA(Mail
Transport Agent)と通信するライブラリです。

$PORT = 25 (25/tcp) で通信しますが、

	$PORT 

を変えることで別のポートで通信することもできます。また通常

	$SMTP_LOG (default "var/log/_smtplog")

というファイルに通信のログを残します。このログはデバッグに非常に役立ち
ます。

	$NOT_TRACE_SMTP 

をセットすると、このログは残りません。

また、どのマシンの sendmail を使うかも自由に設定できます。デフォールト
はそのマシンです。もしMLサーバのマシン(受けるマシン)が貧弱ならそのマシ
ンが何十通もメールを送り出すのは酷です。

	$HOST         = 'localhost'; (default)

そういう場合変数 $HOST を書き換えて別のマシンに配送を任すこともできま
す。その時は

	$HOST         = 'beth.foo.bar.ac.jp'; (another host)

のようにそのマシン名を書いて下さい。もちろんアクセスできないマシンとか
ではできません(あったりする)し勝手にそのマシンのパワーを使うことになる
のでそのマシンを使う許可を得るべきです。

HOSTではポート番号が25でないMTAへのアクセスも可能です。例えば port
10025/tcp を使うこともできます。

	$HOST         = 'beth.foo.bar.ac.jp:10025'; 

3.2	予備のSMTPサーバ

もし SMTP サーバに何かあった時のために @HOSTS に複数台の予備の 
sendmail の走っているマシンを定義することが出来ます。

   @HOSTS = (machine-1.domain, machine-2.domain, machine-3.domain, ...);

この場合、もし $HOST に connect() できない時は、machine-1 に、
もし machine-1 に connect() できない時は machine-2 へ…とい
う風に予備のサーバを使い何とか配送をしようと試みます。
全部だめだった場合は sendmail コマンドを使って配送を試みます。
sendmail がないシステムでは qmail-smtpd, exim なdも試します。

3.3	SMTP の MAIL FROM: を $MAINTAINER 以外にしたい時

$SMTP_SENDER に指定して下さい。	  

例: プロバイダ(例えば fml.org とすると)が不正中継対策のため、MAIL
FROM: のドメイン名を自由にできない場合などに使います。

	$MAINTAINER  = 'elena-admin@nuinui.net';
	$SMTP_SENDER = 'elena-admin@fml.org';

3.4	同じmxについて

sendmail 8.xではCHANGES-R5-R8にあるとおり↓

   For example, if two sites ``foo.com'' and ``bar.com'' are both
   served by UUNET, they will have the same set of MX hosts and will
   be sent in one transaction.  UUNET will then split the message

一通で送られます。ばらばらの順番でも大丈夫。

sendmail 5.xでは@.*の部分が厳密に同じで、さらにちゃんと並んで書いてあ
れば

	fukachan@phys.titech.ac.jp	
	Elena@phys.titech.ac.jp	
	cocoa@phys.titech.ac.jp	

一通にまとめられます。

qmail 1.0 はそうことをせず相手の負荷も回線の太さも考えず、ひたすら一通
ずつ投げつけます(だ〜っ)。

3.5	MCI Cache

TCP Connection Cache の基本的なアイデアは TCP Connection 自体のコスト
の高さです。

もともとの sendmail は

	for (…おくるべきホストのリスト…) {
		ソケット、コネクト、通信、おしまひ
	}

を一つ一つおこないます。これだと 通信が遅いだろうが、なんだろうが、
TIMEOUT か error が起こるまで長い間無駄な配送がありえます。

sendmail 8.7 では標準装備ですが、いまは
#旧もとのりさんのWIDEぱっち ってやつです

	for (…おくるべきホストのリスト…) {
		ソケット1
		ソケット2
		…

		じゅんぐりにまわしていって、
		通信がしやすい connection へ優先的に
		通信、(…おしまひ)

		………
	}
	#ちょっと、簡単化し過ぎかな↑

のように、さっさと送ることが可能なホストへどんどん送って、駄目なところ
は駄目なままです。どうしてもだめなら、最後は queue up されて、次回30分
後とかに retry です。

そういう意味で、MCI CACHE は多いと だめなホストが2、3個ならかなり理想
的な速度で配送できるでしょう。もし、MCI_CACHE が2個で、2個だめなホスト
があると、そこでしばらくつかえてしまうので、遅くなってしまい R5 のよ
うになってしまうと。

そういう意味で MCI CACHE は多ければ多いほどよさそうに見えますが、やり
すぎると 今度はカーネルと折り合いがつかなくなるので、まぁ数個が妥当な
数字なのでしょうね。

○ 1つのメールは常に1か所づつ配送されるのでしょうか?

これは (multi thread kernel でないのなら) kernel がある時点ではかならず 
CurrentProcess を実行しているのだから、YES です。ただ 通信はしていなく
ても Connection 自体は OkN の N 個(MAX) ACTIVE なままです(netstat で
みえるもの)。

これにさらに MX Cache 等が組合わさって、R8はR5にくらべ非常に高速化さ
れましたね。

3.6	配送効率についての考察

配送効率についての考察

3.7	配送プロセスの律速段階とは

律速段階はやっぱり sendmailの配送プロセスそのものでしょう。この部分の 
overhead は R5 にくらべ R8 では非常に改善されましたがそれでもかなりの 
overhead があります。single thread であることが最大の要因の一つです。
並列度をあげるためには、SMTP connection や DNS query を並列化しないと
いけません。高速配送には例えば smtpfeed を利用して下さい:)
qmail vs smtpfeed: 本当に複数のプロセスを立ち上げるという暴挙をやると 
qmail になります;_;理論の上ではマシンの負荷は圧倒的に違うはずですが、
配送するアドレス分布のスペクトルが白色雑音状態なら配送時間はあまり違わ
ないように振舞うでしょうね。

逆にいえば fml 自体を tuning して速度を例えば 0.1 sec 上げても ML の配
送プロセスには何のメリットも生じないということでもあります。基本的には
MTAのチューンや機能の並列化を先に考えるべきです。

１	十分なパワーのマシンで十分な file descriptor や 
	多くの process が走っても大丈夫であれば、
	複数の sendmail を起動して配送を試みる。
	この場合ちゃんと Load Average と sendmail.cf を tune する
	必要があります。人それを qmail と呼ぶという話もあります(苦笑)

２	非力でも複数のマシンが用意できるなら複数のマシンでパラレルに実行

３	リレーサーバを用意する。例えば東京方面、関西方面のように分けて
	一回そのリレーサーバに投げて、そこから再配送をする。

ケース1、2は fml の $MCI_* 変数群がそれを可能にします。

ケース3のリレーサーバは各ユーザごとに”管理者”が設定できます。ここで
考えるべきこととしてリレーサーバは相手のマシンに負荷をかけることに注意
しなくてはなりません。またネットワークトポロジーを考慮の上決めるべきで
す。そして現在ではSPAM不正中継のため普通リレーはできないので特定のホス
トからのリレーだけはあけてもらうなどの設定変更も必要なことにも注意して
下さい。

なお $DEFAULT_RELAY_SERVER が定義されている場合は強制的に全てのメールを

	$DEFAULT_RELAY_SERVER

で定義されたリレーサーバへ投げそのサーバに配送してもらうということもで
きます。
See also: 4.2 

qmail というひたすら配送プロセスを fork() しまくって、何も考えずにばん
ばん投げるMTAがあります。そういう配送の仕方でバックボーンならいいと思
いますが、細い線のサイトにばんばん投げ付けられてしまうようになったら最
低ですよね？世の中どこも線が太いなんていう仮定は誤りですから。
	http://www.qmail.org/

4	リレーサーバを使う

リレーなんか禁止というサイトもあることに注意しましょう。ＭＬの東日本リ
レーサーバ、西日本リレーサーバなんてことをしていたのは随分昔の話です。

最近では SPAM があるのでリレーは基本的に禁止ですからあまり意味がありま
せん。もちろんお互いに合意の上でIPアドレスベースでチェックしてリレーを
するなどは考えられます。

4.1	activesファイルによるリレー制御

actives には

	アドレス			リレーサーバー
	fukachan@phys.titech.ac.jp	r=axion.phys.titech.ac.jp

のような書き方ができます。これをすると sendmail には

	@axion.phys.titech.ac.jp:fukachan@phys.titech.ac.jp

のように渡されます。actives ファイルに

	fukachan@phys.titech.ac.jp	r=axion.phys.titech.ac.jp
	Elena@phys.titech.ac.jp		r=axion.phys.titech.ac.jp
	cocoa@phys.titech.ac.jp		r=axion.phys.titech.ac.jp

なものがあると

	@axion.phys.titech.ac.jp:fukachan@phys.titech.ac.jp
	@axion.phys.titech.ac.jp:Elena@phys.titech.ac.jp
	@axion.phys.titech.ac.jp:cocoa@phys.titech.ac.jp

のように渡されます。sendmail 8.x での配送は axion.phys.titech.ac.jp ま
で一通にまとめられて来てaxion.phys.titech.ac.jp で3通にばらけます。

パケットの数もサーバの負担も軽くなるのでもしリレーしていいという許可が
得られればどこかでリレーサーバを使えるといいですね。

[捕捉]

fml 1.3.2 からは actives 等の表現が拡張性のため違う形になっています。
古いフォーマットで書いた場合は、自動的にNEW FORMAT に自動変換して解釈
しています。
See also: internals 8 

OLD FORMATでは

	fukachan@phys.titech.ac.jp	axion.phys.titech.ac.jp

NEW FORMATでは

	fukachan@phys.titech.ac.jp	r=axion.phys.titech.ac.jp

4.2	全てのメールをリレーサーバに投げる

$DEFAULT_RELAY_SERVER が定義されている場合は強制的に全てのメールを

	$DEFAULT_RELAY_SERVER

で定義されたリレーサーバへ投げそのサーバに配送します。

4.3	activesファイルによるリレー (sendmail 5.x)

	fukachan@phys.titech.ac.jp	r=axion.phys.titech.ac.jp
	Elena@phys.titech.ac.jp		r=axion.phys.titech.ac.jp
	cocoa@phys.titech.ac.jp		r=axion.phys.titech.ac.jp

↑このように順番に並んでいないと一通になってくれませんので注意。
それ以外は8.xの時と一緒です。

4.4	CF (by motonori@wide.ad.jp) 形式による relay 設定ファイル

$RELAY_HACK = 1; がセットされ、$CF_DEF で STATIC_ROUTE_FILE で定義する
static 配送ルールを記したファイルが設定された場合、それに従ってリレー
サーバの設定がされます。

CF 形式の例1:
or.jp ドメイン宛のメールは mlrelay0.domain0 へ全部送る。それ以外は 
$HOST (通常自分のマシン) で定義されるマシンで配送を行なう。

	GW smtp-ignmx:  mlrelay0.domain0
	DOM or.jp

CF 形式の例2:
co.jp にマッチしないドメイン宛のメールはすべて mlrelay.domain というマ
シンへ送る。しかし or.jp ドメイン宛のものは mlrelay0.domain0 へ送る。

	NGW smtp-ignmx: mlrelay.domain
	DOM co.jp
	GW smtp-ignmx:  mlrelay0.domain0
	DOM or.jp

リレー情報はハッシュテーブル %RELAY_NGW %RELAY_GW %RELAY_NGW_DOM に入
れられ、smtp library で利用されます。

5	IPv6

5.1	4.0-current (2001/05/11 以降)

常に IPv6 でメールサーバに接続することに挑戦してみます。
そして、もし、失敗したら IPv4 で挑戦します。
これがデフォルトの動作です。

5.2	必要なモジュール

Socket6 が必要です。適宜インストールして下さい。

5.3	IPv6 を無効にする

$USE_INET6 を 0 にすると IPv4 オンリーに戻ります。

6	Q&A

6.1	root 権限なしに MTA を交換は？

一般にはできません。

理論的には配送くらいはあちこちいじり回せばできるようにおもいますが、
内部構造を非常に良く知らないとできないでしょう。

smtpfeed をよべばいいんじゃん？という発想はあり得ますが、
fml は queue 管理はしていないので
一回失敗したらそのままになります。

この理由により fml は昔(1997/10/30 くらい)から LMTP を喋れますが、
smtpfeed を直接実行するという仕組みは実装されていません。

6.2	smtpfeed を直接呼べませんか？

   smtpfeed は配送部分"だけ"のエージェントです。

   fork() の数が減って嬉しい:) 再送がないなら嬉しい(というのはちょっとま
   ずいだろう)けどqueue管理はしたくない(実装する側の意見)
   という感じす

   queue管理はしたくない、ってのは、仮に fml が smtpfeed と直接にや
   り取りするように実装する場合は、送信先からお断りされたときにその
   メールを手元の queue に保存したりしないといけなくなって、そうな
   るとその queue が時々刻々どうなってるか把握したりする仕事も fml
   にお鉢が回ってくるということでしょうか？

6.3	MTA みくじ

   	1	@HOSTS で複数のほすとのMTAが使えます
   		@HOSTS = (hostA, hostB, hostC, ...)
   	2	document に書いてないけど hostA のところは 
   		hostA:portA なんて書き方もできたりします:)

6.4	受信者リストを分割する

fml 4.0 で自動でやるのはちょっとひと工夫必要です。

MTA の設定と追従していくことも必要です。
postfix  を例にとります。

4.0 は $MCI_SMTP_HOSTS の値で配送リストを分割しています。
受信者数が 4500 で、$MCI_SMTP_HOSTS が 3 なら、

	1500 
	1500 
	1500 	

に分割します(MCI window と呼ばれています)。@HOSTS がないなら $HOSTS
(== "localhost" by default) に対して3回コネクションを張ります。だから

	$MCI_SMTP_HOSTS = 受信者数 / 1000 + 1;

を毎回計算すれば OK です。もっとただしいのは (ざっくりかくと)

 $START_HOOK = q#

	$limit = `postconf smtpd_recipient_limit` して数をもらう;
	$n     = `wc $ACTIVE_LIST` して受信者数;
	$MCI_SMTP_HOSTS = $n / $limit + 1;

 #;

みたいにしておけば postfix の設定と自動追従していくはずです。


		INDEX

_smtplog                                   ...   2.1 
$CF_DEF                                    ...   4.4 
$DEFAULT_RELAY_SERVER                      ...   3.7 4.2 
$HOST                                      ...   3.1 
@HOST(@HOSTSの旧変数名)                    ...   2.5 
@HOSTS                                     ...   2.5 3.2 
$HOSTS                                     ...   2.5 
MCI                                        ...   3.5 
MTA (Mail Transport Agent)                 ...   3.1 
mx                                         ...   3.4 
$NOT_TRACE_SMTP                            ...   3.1 
$NOT_TRACE_SMTP                            ...   2.1 
$PORT                                      ...   3.1 
%RELAY_GW                                  ...   4.4 
$RELAY_HACK                                ...   4.4 
%RELAY_NGW                                 ...   4.4 
%RELAY_NGW_DOM                             ...   4.4 
relay hack based on CF (by motonori@wide.ad.jp)   ...   4.4 
SMTP (Simple Mail Transfer Protocol)       ...   3.1 
$SMTP_LOG                                  ...   2.1 3.1 
$SMTP_SORT_DOMAIN                          ...   2.2 
var/log/_smtplog                           ...   2.1 
%リレー (obsolete)                         ...   4 
配送プロセスの律速段階                     ...   3.7 
予備のSMTPサーバ                           ...   3.2 
