qmail-date-localtime.patch
の問題点Status:
/X-UIDL:
フィールドをサポートさせる一般的には、パッチ (patch) とは日本語で次のような意味を持ちます。
ソフトウェアでいう「パッチ」もほぼこれと同じで、 動作の不具合を応急的に修正あるいは回避するため、 ソフトウェアを書き換える「差分」情報のことを指します。 また、その差分をソフトウェアに組み込むことを「パッチをあてる」、 「パッチを適用する」などと表現します。
ソフトウェアの「欠陥」という名の穴を繕うものだけがパッチではありません。 いわばパッチワークのように、 ソフトウェアに機能を追加したり、動作を変更するためのパッチも存在します。
パッチは薬と同じで、時には副作用が生じることがあります。 構成によってはまったく影響の出ない副作用もありますが、 明かに害となって現れることもあるでしょう。 それが目に見える形で現われることもあれば、 自覚症状がない (より危険) ことがあるかもしれません。 また、複数のパッチの適用によって生じる副作用もあるかもしれません。 麻酔薬のように当面の痛みを取り除くだけで、 根本的な問題を解決できないこともあるでしょう。
あなたが直面している問題をしっかりと分析し、 適切なパッチを選択することが肝心です。 パッチに謳われている効能を理解するのはもちろん、 ソース形式のパッチであれば、 どのような変更がされるのか解析するようにしましょう。
パッチには、 バイナリファイルを書き換えたりファイルごと置き換えるバイナリ形式のパッチと、 ソースファイルやスクリプトを書き換えるためのソース形式のパッチがあります。 この章では、 qmail のソースに変更を加えるためのソース形式のパッチを扱います。 ソースにパッチを適用したあとは、 もちろん再コンパイルして新しいバイナリファイルを生成し、 インストールする必要があります。
ソースファイル用のパッチには、 書き換える対象のソースファイルの名前と位置、そして変更点が記されています。 1つのパッチファイルに複数のファイルのパッチが含まれていることもあります。
以下に、 「Hello, World!!」と表示するコマンドのソース (C言語) を、 「Hello, New World!!」と改行を表示するように変更するためのパッチを示します。
--- helloworld.c.old Thu Oct 11 19:50:51 2001
+++ helloworld.c Thu Oct 11 19:51:53 2001
@@ -1,5 +1,5 @@
int main(void)
{
- printf("Hello, World!!");
+ printf("Hello, New World!!\n");
return 0;
}
この例を見るとわかるように、ソース形式のパッチの正体は、 2 つのファイルを比較して違いを表示する diff コマンドの出力です。 diff コマンドの出力には何種類か異なる形式がありますが、 先の例のパッチは「unified 形式」と呼ばれるものです。 パッチの提供者は、 修正前後のファイルを diff コマンドに通し、 得られた差分情報をパッチファイルとして第3者に配布します。
以下にパッチの適用前後の helloworld.c
を示します。
helloworld.c
:
int main(void)
{
printf("Hello, World!!");
return 0;
}
helloworld.c
:
int main(void)
{
printf("Hello, New World!!\n");
return 0;
}
パッチを適用してソースファイルを変更するには、 patch コマンドを使用します。 patch コマンドの使用方法は、 パッチの形式によって多少異なることがありますが、 概ね、以下のように実行します。
$ ls
helloworld.c newworld.patch
$ patch < newworld.patch
patching file helloworld.c
先にも述べたように、パッチ (diff の出力) の形式にはいくつかの種類がありますが、 patch コマンドは種類を自動で認識し、適切に処理してくれます。 パッチファイルにはパッチの前後にコメントや解説がついていることがありますが、 patch コマンドはそれらを無視してくれます。
通常、パッチファイル内には、 パッチの対象となるファイルの相対パス名が記述されています。 patch コマンドはそのパス名を利用してパッチ対象のファイルにアクセスするため、 適切なディレクトリに移動してから patch コマンドを実行しなければなりません (patch コマンドの -d オプションを利用する方法もある)。
パッチ対象のパス名の先頭に余計なディレクトリ名が含まれているなら、
patch コマンドの -p オプションを利用して削除することができます。
-p オプションには、パス名の先頭から何番目のスラッシュ「/
」
までを削除するかを指定します。
以下の節では、qmail のソースに適用するパッチをいくつか紹介します。 パッチを適用することで、 qmail の動作を変更したり、 追加の機能を実現することができます。
qmail 用のパッチの多くは、 qmail のユーザーサイトの 1つ「The qmail home page」 から入手することができます。 また、qmail のメーリングリストにパッチやその情報が投稿されることもあります。
qmail は様々な場面で日付文字列の生成や表示を行いますが、 それらはすべて世界標準時の日時になります。 そのような日付には、 世界標準時からの時差がないことを意味する「-0000」、 またはグリニッジ標準時 (Greenwich Mean Time) を意味する「GMT」、 あるいはその両方が付加されています (qmail の場合はどちらかのみ)。
一方、qmail 以外の MTA やメールクライアントの多くは、 システムの地域情報の設定 (タイムゾーン) に合わせ、日付は地方時間で生成します。 例えば日本では世界標準時からの時差は 9時間なので、 日付にはそれを意味する「+0900」、 または「JST」(Japan Standard Time)、 あるいはその両方が付加されます。
次に紹介するパッチは、 qmail の日付生成処理に変更を施し、 システムの地域設定に従った地域標準時の日付を生成するようにします。
$ cd source/dir/of/qmail-1.03
$ patch -p1 < /path/to/qmail-date-localtime.patch
このパッチによる動作は、設定ファイルで制御することはできません。
代わりに、コマンドを起動する前に、
環境変数 TZ
を設定してください
(マニュアル tzset(3) などを参照のこと)。
このパッチは、qmail の以下のコマンドに影響を及ぼします。
Date:
(Recent-Date:
) フィールドの日付
(ヘッダーに含まれていない場合に追加する)
Date:
フィールドの日付Received:
フィールドの日付qmail-date-localtime.patch
の問題点
このパッチにより、qmail-qread が出力する日付の表示に問題が発生します。
qmail-qread は、キューにあるメールの情報を表示するコマンドです。
その情報の中にはメールがキューに到着した日時が含まれており、
このパッチによりシステムの地域設定 (または環境変数 TZ
の値)
の標準時で表示されますが、
実際の地域設定に関わらず必ず「GMT」と表示されてしまいます。
これを解消するには、qmail-qread.c に以下のようなパッチを適用するとよいでしょう。
--- qmail-qread.c.dist Mon Jun 15 19:53:16 1998
+++ qmail-qread.c Mon Oct 15 18:50:53 2001
@@ -65,8 +65,8 @@
len = 0;
datetime_tai(&dt,qtime);
- i = date822fmt(s,&dt) - 7/*XXX*/; len += i; if (s) s += i;
- i = fmt_str(s," GMT #"); len += i; if (s) s += i;
+ i = date822fmt(s,&dt) - 1/*XXX*/; len += i; if (s) s += i;
+ i = fmt_str(s," #"); len += i; if (s) s += i;
i = fmt_ulong(s,id); len += i; if (s) s += i;
i = fmt_str(s," "); len += i; if (s) s += i;
i = fmt_ulong(s,size); len += i; if (s) s += i;
UNIX には、ユーザーのディスク使用量を制限するための 「クォータ (quota)」と呼ばれる機能があります。 クォータによりディスク使用を制限されたユーザーは、 作成できるファイルの数と使用できる容量に制限が設けられます。 制限に達っすると、ファイルの新規作成やファイルへのデータ出力は失敗します。
qmail (qmail-local) は、
メールボックスへのメール保存の処理に失敗すると、
一時エラーとし、メールボックスへの配送は後ほど再試行されます。
この動作はクォータの制限によって失敗した場合も変わりません。
その後も問題が解消されなければ、
メールは control/queuelifetime
で指定された時間だけキューに滞留したのち、バウンスします。
メールの送信者は、そのときやっとメールが届かないことがわかりますが、
バウンスメールを見ても送信先のメールボックスが一杯であることはわかりません。
次のパッチを適用することで、 qmail-local はクォータによる書き込み失敗を検知できるようになり、 そのような場合には永続的エラーとするようになります。
$ cd source/dir/of/qmail-1.03
$ patch < /path/to/qmail-1.03-quotas-1.1.patch
このパッチにより、クォータ制限に達っしているユーザーへのメール配送は、 即座にバウンスするようになります。 バウンスメールには、配送に失敗した原因として、 宛先ユーザーのメールボックスが一杯であることが示されます。
qmail (qmail-smtpd) は、
SMTP の MAIL
コマンドで渡される送信者アドレス
(control/badmailfrom
) や、
RCPT
コマンドで渡される宛先アドレスのドメイン部
(control/rcpthosts
, morercpthosts
)、
DATA
コマンドで渡されるメールのサイズ
(control/databytes
) により、
メールの受信を拒否することができます。
ここで紹介するパッチは、 SMTP によるメールの受信と配送に対し、 受信の拒否条件の設定をより柔軟に指定可能にするパッチや、 なにかしらの制限を加えるためのパッチです。 これらは主に、SPAM メールの受信や配送を抑制するために利用されます。
標準の qmail-smtpd は、宛先アドレスのドメイン部により受信の拒否が可能ですが、 宛先アドレスのローカル部を含めた受信拒否の判定をさせることはできません。 次に紹介するパッチを適用することで、ドメイン部だけでなく、 宛先アドレス全体により受信の可否を判定できるようになります。
$ cd source/dir/of/qmail-1.03
$ patch -p1 < /path/to/badrcptto.patch
受信を拒否する宛先アドレスは、
このパッチによる追加の設定ファイル control/badrcptto
に記述します。
このファイルの形式は、
受信を拒否する送信者アドレスを指定するための
control/badmailfrom
ファイルと同じです。
例えば、宛先アドレスが badname@example.com のメールと、
宛先アドレスのドメイン部が baddomain.example.net のメールの受信を拒否するには、
次のように記述します。
control/badrcptto
):
badname@example.com
@baddomain.example.net
qmail には標準で、
特定の送信者アドレスからのメールの受信を拒否する機能があります。
拒否する送信者アドレスの指定は
control/badmailfrom
ファイルで行いますが、
固定的なアドレスかドメインの指定しかできません。
先の節で紹介した宛先アドレスによる受信拒否用のパッチの機能も同様です。
ここで紹介するパッチは、 ニューズサーバーソフトウェア INN で利用されている wildmat と呼ばれるパターン一致処理を qmail-smptd に追加し、 受信を拒否する送信者/宛先アドレスの指定にパターンを記述可能にするパッチです。
$ cd source/dir/of/qmail-1.03
$ gzip -dc /path/to/wildmat-0.3.tar.gz | tar xf -
$ patch < wildmat.patch
このパッチを適用することで、
qmail 標準の
control/badmailfrom
ファイルは機能しなくなり、
代わりに
control/badmailpatterns
ファイルを使うようになります。
しかも、このファイルに記述したアドレスは、
送信者アドレスだけでなく宛先アドレスにも適用されます。
残念ながら、
受信を拒否する送信者アドレスと宛先アドレスを別々に指定することはできません。
この点、十分のご注意ください。
wildmat 形式のパターン表記法は、 シェルのワイルドカードパターンの表記に似ています (glob(3) などを参照のこと)。 残念ながらパッチには詳しい解説が含まれていませんので、 INN の付属のマニュアル wildmat(3) を参照するか、 Web の検索ページで探してみてください。 ここで簡単に解説します。
?
*
[x...y]
-
」で文字の範囲を指定することができる。(例: [0-9a-z]
)[^x...y]
\x
x
に一致する。?
」などの特殊な文字の機能を打ち消すために用いる。
以下に control/badmailpatterns
ファイルの記述例を示します。
control/badmailpatterns
):
*@baddomain.example.net
*%*@*
*@*@*
SPAM メール送信者の多くは、 送信者アドレスを偽って SPAM メールを送信します。 その際に利用されるメールアドレスのドメイン部は、 SPAM 送信者とは関係のない第3者のドメインを勝手に名乗ったり、 実在しないドメインが利用されます。
次に紹介するパッチを qmail に適用することで、
qmail-smtpd は SMTP の
MAIL
コマンドで指定される送信者アドレスからドメイン部を抜き出し、
DNS でそのドメインの MX レコードを検索するようになります。
該当する MX レコードが存在しない場合、
qmail-smtpd は無効な送信者アドレスと判断してメールの受信を拒否します。
$ cd source/dir/of/qmail-1.03
$ patch -p1 < /path/to/qmail-1.03-mfcheck.3.patch
このパッチによる機能は、追加の設定ファイル
control/mfcheck
に 0 か 1 を記述することで、
無効にするか有効にするかを制御することができます。
デフォルトは 0 で、送信者アドレスのドメイン部の検査機能は無効になります。
この設定は環境変数 MFCHECK
の値でも指定可能で、
control/mfcheck
ファイルによる指定よりも優先されます。
qmail (qmail-send) は、
メール配送が失敗した場合に送出するバウンスメールについて、
ヘッダーの From:
フィールドの内容を設定可能にしています
(ただし送信者アドレスは設定に関係なく必ず空)。
また、バウンスメールがバウンスしたときに送出する 2重バウンスメールについては、
送付先の宛先アドレスが設定可能です。
ここで紹介するパッチは qmail-send が生成するバウンスメールに関するもので、 qmail 標準より柔軟な設定変更を可能とします。 パッチやその設定によっては、 qmail が従っている標準のバウンスメッセージ形式 (QSBMF) に適合しなくなることがあります。 ご注意ください。
このパッチを適用することで、
qmail が生成するバウンスメールと
2重バウンスメールに含まれる表題
(ヘッダーの Subject:
フィールドの内容)
と本文のメッセージを設定変更可能にできます。
$ cd source/dir/of/qmail-1.03
$ patch -p1 < /path/to/qmail-bouncecontrol-1.03.patch
このパッチにより、 以下のような 4つの設定ファイルが追加されます。 ファイルが存在しない場合は、qmail 標準の表題/メッセージとなります。
control/bouncesubject
Subject:
フィールドの内容control/bouncemessage
control/doublebouncesubject
Subject:
フィールドの内容control/doublebouncemessage
qmail-send が生成するバウンスメールには
Content-Type:
フィールドは含まれていないため、
control/bouncemessage
, doublebouncemessage
に記述するメッセージにはテキスト形式の ASCII 文字だけを含めることができます。
よって、日本語などを含めることはできません。
また、メッセージに空行を含めてしまうと、
QSBMF
に従わない形式になってしまいます。
control/bouncesubject
,
doublebouncesubject
に記述する表題は、
そのままバウンスメールのヘッダー部に入るため、
ASCII 文字だけを含めることができます。
日本語などを含める場合は、
いわゆる「MIME エンコード」形式にしなければなりません。
(RFC 2047
「Multipurpose Internet Mail Extensions Part Three:
Message Header Extensions for Non-ASCII Text」)
qmail-send が生成するバウンスメールには、 バウンスしたメールがヘッダーと本文とも受信したときのままの形で含まれます。 次のパッチを適用することで、 バウンスメールに含める元のメールのサイズに制限をかけることが可能になります。
$ cd source/dir/of/qmail-1.03
$ patch < /path/to/qmail-bounce.patch
このパッチにより、設定ファイル control/bouncemaxbytes
が追加されます。
このファイルには、
バウンスメールに含める元のメールの最大サイズをバイト数で指定します。
デフォルトは 50000 バイトです。
残念ながら、このパッチによる動作を無効にして制限なしにすることはできません。
バウンスしたメールがこのサイズを越えている場合、
バウンスメールに含まれる元メールはサイズを越える部分が省略され、
代わりに改行と「--- End of message stripped.
」
というメッセージが付加されます。
一般的に POP3 に対応しているメールクライアントは、
POP3 サーバーから取得したメールをサーバーから削除せずに、
POP3 サーバーに残しておくことができます。
POP3 クライアントは、
1度サーバーから取得したメールを再び取得しないようにするため、
POP3 (RFC 1939
「Post Office Protocol - Version 3」)
のオプションとして定義されている
UIDL
コマンドで得られるメールごとの識別子、
あるいはメールのヘッダーに含まれる
Status:
や X-UIDL:
フィールドの値を利用します
(クライアントの実装に依存)。
UIDL
コマンドはその動作である
「Unique-ID Listing」から名付けられたもので、
サーバーのメールボックス内にあるメールから、
各メールを一意に識別するための識別子 (UID) を返します。
UIDL に対応している POP3 クライアントは、
この識別子を利用してメールが取得済みであるかどうかを判断します。
qmail 付属の POP3 サーバーである qmail-pop3d は、
UIDL
コマンドをサポートしています。
Status:
フィールドには、
メールが新着か、既読か、取得済みかどうかの状態が保持されます。
メールボックスを直接参照するメールクライアントである mail コマンドや、
いくつかの POP3 サーバーの実装がサポートしており、
必要に応じて Status: フィールドの値を書き換えます。
しかし、qmail-pop3d は Status: フィールドをサポートしていません。
RFC 2076
「Common Internet Message Headers」によると、
Status:
フィールドは非標準のヘッダーフィールドです。
X-UIDL:
フィールドには、
POP3 の UIDL
コマンドで得られるのと同じ、
一意の識別子が保持されます。
詳細は不明ですが、
何らかの形でこのフィールドの値を利用するクライアントが存在するようです。
qmail-pop3d は X-UIDL:
フィールドをサポートしていません。
Status:
/X-UIDL:
フィールドをサポートさせる
以上のように、
UIDL コマンドをサポートしていなかったり、
UIDL をサポートしていても X-UIDL:
フィールドに依存している
POP3 クライアントは、
qmail-pop3d の下では「サーバーにメールを残す」機能がうまく動作しません。
次に紹介するパッチを適用することで qmail-pop3d がメールに
Status:
/X-UIDL:
フィールドを追加するようになります。
Status:
/X-UIDL:
フィールドをサポートするパッチ$ cd source/dir/of/qmail-1.03
$ patch -p1 < /path/to/qmail-pop3d-1.03.diff
このパッチによる追加の設定ファイルはありません。 また、このパッチによる動作を抑制することはできません。
Netscape 社の Netscape Navigator / Communicator に付属のメールクライアント Netscape Messenger は、 POP3 サーバーからメールを取得している間、 取得の進捗状況をダイアログウィンドウに表示します。 ダイアログは、 サーバー上にあるメールの総数と取得した数、 そして進行の度合を横長のメーターとパーセンテージで表わします。
しかし Messenger は、 POP3 サーバー が qmail-pop3d であると、 横長メーターとパーセンテージが正しく表示されません。 以下のように、メーターは一定のまま変化せず、 パーセンテージの表示はされません。
原因は qmail-pop3d ではなく Netscape Messenger にあります。
POP3 には、
サーバーからメールを取得 (retrieve) するための
RETR
コマンドが用意されています。
POP3 サーバーは RETR
コマンドの応答として、
指定された番号のメールがあるならコマンドを受け付けたことを意味する
「+OK
」行とメールのメッセージ、
そして最後にドット「.
」だけの行をクライアントに返します。
以下に、広く普及している POP3 サーバーの実装の一つである Qpopper に対して
telnet コマンドで接続し、手動でメールを取得する様子を示します。
$ telnet mail.example.net pop3
Trying 192.168.1.1...
Connected to mail.example.net.
Escape character is '^]'.
+OK QPOP (version 3.0.2) at mail.example.net starting.
USER ユーザー名
+OK Password required for ユーザー名.
PASS パスワード
+OK ユーザー名 has 2 visible messages (0 hidden) in 5593 octets.
LIST
+OK 2 visible messages (5593 octets)
1 2881
2 2712
.
RETR 1
+OK 2881 octets
メールメッセージ
…
.
※ USER
, PASS
コマンドにより認証を受け、
LIST
でメールの一覧 (番号とサイズ) を表示、RETR
で取得。
ご覧のように、Qpopper は
RETR
コマンドの応答の「+OK
」行に
「メールのサイズ octets
」を付加します。
一方、qmail-pop3d は「+OK
」とだけ応答します。
RFC 1939 によると、
動作例としてメールのサイズが付加された応答を返すものが記載されていますが、
そのようにすべきと規定しているわけではありません。
Netscape Messenger は RETR
コマンドの応答として
Qpopper が返すような形式を期待しており、
そこから得られるメールのサイズから進捗のパーセンテージを計算しています。
このため、RETR
コマンドに対してメールのサイズを返さない
qmail-pop3d 相手ではうまく機能しません。
そこで、次のパッチを qmail-pop3d に適用することで、
Messenger の不具合を回避することができます。
RETR
コマンドの応答にメールのサイズを追加するパッチ$ cd source/dir/of/qmail-1.03
$ patch < /path/to/netscape-progress.patch
qmail はメール配送処理を、
複数のプロセスを生成し並行して実行します。
ローカルユーザーへの配送の最大並列数は
control/concurrencylocal
、
リモートへの配送の最大並列数は
control/concurrencyremote
の設定により制限され、デフォルトの最大並列数はローカル配送は 10、
リモート配送は 20 に設定されています。
大量のメールを配送する必要があるならば、
配送処理の最大並列数を上げることで
(ほかにボトルネックが生じない限り)、
その分だけメール配送のパフォーマンスは向上します。
しかし、
control/concurrencylocal
, concurrencyremote
で指定できる並列数は、最大で 120 に制限されています。
この制限は、qmail のコンパイル時、
qmail のソースツリーに含まれる
conf-spawn
ファイルに記述されている値で決まります。
このファイルの値を増やして qmail をコンパイルすることで、
121 以上の並列配送を実現できます。
しかしそれでも、
conf-spawn
ファイルに記述できる最大並列数の最大値は
255 に制限されます。
これは qmail の内部実装による制限です。
この制限はコンパイル時に検査され、
conf-spawn
ファイルの値が 255 を越えていると、
次のようにコンパイルが中断されます。
conf-spawn
ファイルに 256 以上の数を記述して qmail をコンパイルすると…:
…ほかのオブジェクトやバイナリのコンパイル状況…
./auto-int auto_spawn `head -1 conf-spawn` > auto_spawn.c
./compile auto_spawn.c
./load chkspawn substdio.a error.a str.a fs.a auto_spawn.o
./chkspawn
Oops. You have set conf-spawn higher than 255.
make: *** [spawn.o] Error 1
次のパッチを適用することで、 qmail は 256 以上のプロセスを生成できるようになります。
$ cd source/dir/of/qmail-1.03
$ patch -p1 < /path/to/big-concurrency.patch
このパッチにより、実装としては最大 65536 の並列実行に対応しますが、
65000 以下に制限するように作られています。
そこまで多くのプロセスを実行するには、
CPU 速度、メモリ、ネットワーク帯域、
そのほか相当のリソースが必要になり現実的ではないため、
問題ないでしょう。
conf-spawn
ファイルは 1000 に書き換わります。
通常 UNIX カーネルは、システム全体あるいはユーザーごとに、 同時に実行できるプロセスや扱えるファイル記述子の数、 消費メモリサイズなどに制限をかけています。 このパッチを適用し qmail の制限を増すだけでなく、 カーネルパラメーターの調整を確認することを忘れないようにしてください。