From 2dd3a5b7dd46e1a5f0d262d77540d0996d4aaafd Mon Sep 17 00:00:00 2001 From: Masahiko Ito Date: Sun, 8 May 2016 23:42:08 +0900 Subject: [PATCH] 2016/05/08 23:42:08 --- CHANGES | 7 + MISC | 35 ++++ README | 289 ++++++++++++++++++++++----------- README.en | 21 +++ sample/black_hostname_db | 4 + sample/black_ip_db | 4 + sample/relay_allow_address_db | 4 + sample/relay_allow_host_db | 4 + sample/show_retry.sh | 16 -- sample/show_retry1.sh | 74 +++++++++ sample/show_retry2.sh | 22 +++ sample/smtp_filter.sh | 29 ++-- sample/smtp_filter1.sh | 200 +++++++++++++++++++++++ sample/smtp_filter2.sh | 182 +++++++++++++++++++++ sample/smtp_null_filter.sh | 12 +- sample/white_hostname_db | 3 + sample/white_ip_db | 7 +- smtp_wrapper.c | 298 ++++++++++++++++++++++------------ 18 files changed, 972 insertions(+), 239 deletions(-) create mode 100644 MISC create mode 100644 README.en create mode 100644 sample/relay_allow_address_db create mode 100644 sample/relay_allow_host_db delete mode 100755 sample/show_retry.sh create mode 100755 sample/show_retry1.sh create mode 100755 sample/show_retry2.sh create mode 100755 sample/smtp_filter1.sh create mode 100755 sample/smtp_filter2.sh diff --git a/CHANGES b/CHANGES index 006ddf2..685de85 100644 --- a/CHANGES +++ b/CHANGES @@ -11,3 +11,10 @@ o 制御端末の切り離しをまじめにしてみた o ソケットの読み込みブロックを、1回につき16byteに制限した o 同一IPからの連続したアクセスに対して許容する最小間隔を指定できるようにした(-i minimum_interval_sec) +2006/05/15 Ver. 0.2.1 + o READMEのGPL宣言の住所部分修正 +2006/05/24 Ver. 0.3 + o フィルタを2種類指定出来るように変更した。 + 外部MTAからの接続要求時点でIPアドレスを元にチェックを行うフィルタと、コンテンツ受信完了後にコンテン + ツ内容を元にチェックを行うフィルタの2種類を指定できる。従来通り(Ver. 0.2.x以前と同様に)全てのフィル + タリングを1つのフィルタで行うことも可能。 diff --git a/MISC b/MISC new file mode 100644 index 0000000..2621f33 --- /dev/null +++ b/MISC @@ -0,0 +1,35 @@ +Command sequence +================ + + stat|command/response|in |out |stat|comment +-----+----------------+---------+---------+----+------------------------- + 1 |QUIT |(1) | [2] |8 | + | | | | | + 1 |HELO |(1) | [2][3]|1 | + 1 |RES(HELO) | (2) |[1] |1 | + 1 |MAIL FROM: |(1) | [2][3]|1 | + 1 |RES(MAIL FROM:) | (2) |[1] |1 | + 1 |RCPT TO: |(1) | [2][3]|1 | + 1 |RES(RCPT TO:) | (2) |[1] |1 | + | | | | | + 1 |DATA |(1) | [3]|2 | + 2 |FAKERES(DATA) | |[1] |3 |354 fake ack + | | | | | + 3 |MESSAGES |(1) | [3]|3 | +[3 |ANYDATA | (3)|[1] |1 |450 temporary error] + | | | | | + 3 |. |(1) | [3]|4 | + | | | | | + 4 |DATA | (3)| [2] |5 | +[4 |!DATA | (3)|[1] |1 |450 temporary error] + | | | | | + 5 |RES(DATA) | (2) |---------|6 |no response + | | | | |(already responsed 354) +[5 |RES(DATA) | (2) |[1] |1 |!354 then 450 temporary + | | | | |error for .] + | | | | | + 6 |MESSAGES | (3)| [2] |6 | + 6 |. | (3)| [2] |7 | + 7 |RES(.) | (2) |[1] |1 | + | | | | | + 8 |RES(QUIT) | (2) |[1] | |exit diff --git a/README b/README index 670b986..fd88ee6 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -smtp_wrapper-0.2 -- SMTP filtering support daemon +smtp_wrapper-0.3 -- SMTP filtering support daemon Copyright (C) 2006 Masahiko Ito These programs is free software; you can redistribute it and/or modify @@ -13,17 +13,18 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with these programs; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 0.2-1307 USA +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Mail suggestions and bug reports for these programs to -"Masahiko Ito" +"Masahiko Ito" History ======= 2005/11/25 Develop started -2006/01/23 First release(0.1) +2006/01/23 Ver. 0.1 release (1st) 2006/05/15 Ver. 0.2 release +2006/05/24 Ver. 0.3 release What's this ? ============= @@ -92,17 +93,20 @@ Install ======= o cd /usr/local/ -o tar xvzf smtp_wrapper-0.2.tar.gz -o cd smtp_wrapper-0.2 +o tar xvzf smtp_wrapper-0.3.tar.gz +o ln -sf smtp_wrapper-0.3 smtp_wrapper +o cd smtp_wrapper o cc -o smtp_wrapper smtp_wrapper.c -o vi filter_script -o chmod +x filter_script +o vi smtp_filter1.sh # フィルタは各自で用意して下さい(for ip_filter) +o vi smtp_filter2.sh # フィルタは各自で用意して下さい(*必須*)(for contents_filter) +o chmod +x smtp_filter[12].sh o smtp_wrapper -h Usage : smtp_wrapper [-mh hostname] [-mp port] [-q backlog] [-sh smtpserver_hostname] [-sp smtpserver_port] - [-t timeout_sec] [-d delay_sec] [-f filter] - [-cm child_max] [-i minimum_interval_sec] [-F] + [-t timeout_sec] [-d delay_sec] [-if ip_filter] + [-f contents_filter] [-cm child_max] [-i minimum_interval_sec] + [-F] -mh hostname : my hostname [ANY] -mp port : my port [25] -q backlog : socket queue number [5] @@ -110,12 +114,10 @@ o smtp_wrapper -h -sp smtpserver_port : real smtp port [8025] -t timeout_sec : timeout second [no timeout] -d delay_sec : delay second for initial connection [0] - -f filter : filter program - [/usr/local/smtp_wrapper/smtp_filter] - -cm child_max : max number of connection to real smtp daemon - [10] - -i minimum_interval_sec : minimum interval second of connection from - same ip address [0] + -if ip_filter : filter program for IP check [/usr/local/smtp_wrapper/smtp_ip_filter] + -f contents_filter : filter program for contents check [/usr/local/smtp_wrapper/smtp_contents_filter] + -cm child_max : max number of connection to real smtp daemon [10] + -i minimum_interval_sec : minimum interval second of connection from same ip address [0] -F : run in foreground sendmailの設定 @@ -129,12 +131,12 @@ o vi submit.mc DAEMON_OPTIONS(`Port=8025, Name=NoMTA, Addr=127.0.0.1, M=E')dnl o sendmail.cf, submit.cfを更新 -sendmail.cf ------------ +sendmail.cfに以下の行が追加されている事を確認 +--------------------------------------------- O DaemonPortOptions=Port=8025, Name=MTA -submit.cf ---------- +submit.cfに以下の行が追加されている事を確認 +------------------------------------------- O DaemonPortOptions=Port=8025, Name=NoMTA, Addr=127.0.0.1, M=E 起動 @@ -142,12 +144,14 @@ O DaemonPortOptions=Port=8025, Name=NoMTA, Addr=127.0.0.1, M=E o vi /etc/rc.d/rc.local - if [ -x /usr/local/smtp_wrapper-0.2/smtp_wrapper ] + if [ -x /usr/local/smtp_wrapper/smtp_wrapper ] then - /usr/local/smtp_wrapper-0.2/smtp_wrapper \ + /usr/local/smtp_wrapper/smtp_wrapper \ -t 60 \ -d 10 \ - -f /usr/local/smtp_wrapper-0.2/filter_script \ + -if /usr/local/smtp_wrapper/smtp_filter1.sh \ + -f /usr/local/smtp_wrapper/smtp_filter2.sh \ + -cm 10 \ -i 5 echo ' smtp_wrapper' fi @@ -173,42 +177,60 @@ Figure | | E | | | (stdout) | V (stdin) | | +--------+ | - | | | | - | | FILTER | | - | | | | - | +--------+ | - | | + | | IP |--------+ | + | | FILTER |CONTENTS| | + | | | FILTER | | + | +--------+ | | + | +---------+ | | | +-----------------------------------------------------------+ o smtp_wrapper(親)が本来のMTAに代ってsmtp(25)ポートへの接続を監視します。 - o 接続を受けたsmtp_wrapper(親)は、`-d'オプションで指定した秒数sleepした - 後に子プロセス(smtp_wrapper(子))を生成し、以降の接続元との通信を子プロ - セスにまかせ、再度smtp(25)ポートの監視を繰り返します。また、接続を受け - た時点で、今現在稼働しているsmtp_wrapper(子)の数が`-mc'オプションで指 - 定した数を越えている場合、その数が`-mc'オプションの指定値の2分の1以下 - になるまで`-d'オプションで指定した秒数のsleepを繰り返します。 + o 接続を受けたsmtp_wrapper(親)は、今現在稼働しているsmtp_wrapper(子)の + 数が`-mc'オプションで指定した数を越えている場合、その数が`-mc'オプショ + ンの指定値の2分の1以下になるまで`-d'オプションで指定した秒数のsleepを + 繰り返します。 o 同一IPからの連続した接続に対して、その接続間隔が`-i'オプションで指定 した秒数よりも短い場合は、その接続に対して一時エラー(421)を返し接続を 閉じます。 - o smtp_wrapper(子)は接続元のIPアドレスを環境変数`SW_FROM_IP'に設定したの - ちフィルタプログラムをfork&execします。 + o smtp_wrapper(親)は子プロセス(smtp_wrapper(子))を生成し、以降の接続元 + との通信を子プロセスにまかせ、再度smtp(25)ポートの監視を繰り返します。 + + o smtp_wrapper(子)は`-d'オプションで指定した秒数sleepした後に接続元と + の通信を開始(greeting message送出)します。 + + o smtp_wrapper(子)は接続元のIPアドレスを環境変数`SW_FROM_IP'に設定した + のち`-if'オプションで指定したフィルタプログラムをfork&execします。こ + のフィルタプログラムには環境変数`SW_FROM_IP'の情報を元にしたチェック + を行わせます。その際、このフィルタプログラムの標準入力に対しては一切 + データは渡されないので、コンテンツの内容を利用したチェックは出来ませ + ん。 + + フィルタプログラムには、正当な接続に対しては`OK'または`ok'または`'(空 + 文字列)を標準出力に出力させ、不正な接続に対しては`OK',`ok'以外のメッセー + ジを標準出力に出力させ、終了させます。 - o smtp_wrapper(子)は接続元からのメッセージのうち`HELO|EHLO'、`MAIL FROM:'、 + o smtp_wrapper(子)はフィルタプログラムからのメッセージが`OK',`ok'以外の + 文字列で始まっていた場合、それを不正な接続とみなし、接続元に`450'で始 + まる一時エラー応答を返します。フィルタプログラムのチェックを通過した後、 + 本来のMTAに対して接続を開始し、`-f'オプションで指定したフィルタプログ + ラムをfork&execします。 + + o smtp_wrapper(子)は接続元からのメッセージのうち`HELO'、`MAIL FROM:'、 `RCPT TO:'等の`DATA'コマンドまでのメッセージを本来のMTA(上図では、待ち 受けポートを8025に変更)とフィルタプログラムに渡し、MTAからの応答メッセー - ジを接続元に渡します。その後`DATA'から`.'をフィルタプログラムのみに渡 - した後、フィルタプログラムからの応答(チェック結果)を待ちます。 + ジを接続元に渡します。その後`DATA'から`.'までをフィルタプログラムのみ + に渡した後、フィルタプログラムからの応答(チェック結果)を待ちます。 - o フィルタプログラムは環境変数`SW_FROM_IP'と(必要であれば)受け渡されたメッ - セージ本文から正当なメッセージか不当なメッセージかを判断し、正当なメッ - セージと判断した場合は受け渡されたメッセージのうち`DATA'から - `.'までを標準出力に出力させます。また不正メッセージと判断した - 場合は直ちに終了するか、`DATA'以外で始まるメッセージを標準出力 - に出力した後、終了させます。 + o フィルタプログラムには標準入力から受け渡されたメッセージ本文から正当な + メッセージか不当なメッセージかを判断し、正当なメッセージと判断した場合 + は受け渡されたメッセージのうち`DATA'から`.'までを標準出 + 力に出力させます。また不正メッセージと判断した場合は直ちに終了するか、 + `DATA'以外で始まるメッセージを標準出力に出力した後、終了させま + す。 o smtp_wrapper(子)はフィルタプログラムからのメッセージが`DATA'で 始まっていた場合、それを正当なメッセージとみなし、本来のMTAに受け渡しま @@ -220,59 +242,78 @@ Figure smtp_wrapper(子)<->本来のMTA間のコネクションが切断されるまで上記の動作 を繰り返します。 -Command sequence -================ - - stat|command/response|in |out |stat|comment ------+----------------+---------+---------+----+------------------------- - 1 |QUIT |(1) | [2] |8 | - | | | | | - 1 |HELO |(1) | [2][3]|1 | - 1 |RES(HELO) | (2) |[1] |1 | - 1 |MAIL FROM: |(1) | [2][3]|1 | - 1 |RES(MAIL FROM:) | (2) |[1] |1 | - 1 |RCPT TO: |(1) | [2][3]|1 | - 1 |RES(RCPT TO:) | (2) |[1] |1 | - | | | | | - 1 |DATA |(1) | [3]|2 | - 2 |FAKERES(DATA) | |[1] |3 |354 fake ack - | | | | | - 3 |MESSAGES |(1) | [3]|3 | -[3 |ANYDATA | (3)|[1] |1 |450 temporary error] - | | | | | - 3 |. |(1) | [3]|4 | - | | | | | - 4 |DATA | (3)| [2] |5 | -[4 |!DATA | (3)|[1] |1 |450 temporary error] - | | | | | - 5 |RES(DATA) | (2) |---------|6 |no response - | | | | |(already responsed 354) -[5 |RES(DATA) | (2) |[1] |1 |!354 then 450 temporary - | | | | |error for .] - | | | | | - 6 |MESSAGES | (3)| [2] |6 | - 6 |. | (3)| [2] |7 | - 7 |RES(.) | (2) |[1] |1 | - | | | | | - 8 |RES(QUIT) | (2) |[1] | |exit +spam対策機能(のまとめ) +====================== + +o `-i'オプションで指定した秒数未満の間隔での、同一IPからのアクセスを拒否する。 + 接続元には以下のメッセージを返す。 + + 421 Service not available, closing transmission channel(rejected by rapidly access. IP=%s)(SW01)\r\n + +o `-if'オプションで指定したフィルタにより不正と判断された場合、アクセスを拒否 + する。 + 接続元には以下のメッセージを返す。 + + 450 This message was rejected according to site policy(rejected by ip_filter. IP=%s)(SW02)\r\n + +o SMTPコマンドの送受信順序を無視したアクセスを拒否する。`-d'オプション指定に + より遅延させたgreeting messageを待ち切れずに、おそらく多くのspammerがメッ + セージを送り付けて来る。これを撃退する。 + 接続元には以下のメッセージを返す。 + + 450 This message was rejected according to site policy(rejected by seq error. IP=%s)(SW05)\r\n + +o `-f'オプションで指定したフィルタにより不正と判断された場合、受信を拒否する。 + 接続元には以下のメッセージを返す。 + + 450 This message was accepted partly, but it was rejected according to site policy(rejected by contents_filter. IP=%s)(SW09)\r\n + 450 This message was accepted all, but it was rejected according to site policy(rejected by contents_filter. IP=%s)(SW10)\r\n Filter ====== -フィルタの基本的な構造は以下のように作成してください。 +`-if'で指定するフィルタの基本的な構造は以下のように作成してください。 +--------------------------------------------------------------------- + +o 主に接続元のIPアドレスに対するチェックを行います(S25R)。 + +o 環境変数`SW_FROM_IP'に設定されている接続元のIPおよびIPから + 逆引したFQDNをチェックし、正当な接続と判断したならstdoutに + 何も出力せずにexitして下さい(明示的に正当である事を示したい + 場合は`OK'または`ok'を出力しexitする事も可能) + + 不正な接続と判断したなら、stdoutに`OK', `ok'以外で始まるメッ + セージを出力しexitして下さい。このstdoutに出力したメッセージ + はsyslog経由(mail.info)で記録されます。 + +o 要約 + + 正当な接続時 : stdoutには何も出力せずに終了する(`OK', `ok'を + 出力する事も可能)。 + 不正な接続時 : `OK', `ok'以外で始まるメッセージをstdoutに出 + 力して終了する。 + +`-f'で指定するフィルタの基本的な構造は以下のように作成してください。 +-------------------------------------------------------------------- -o 必要に応じて、環境変数`SW_FROM_IP'に設定されている接続元の - IPおよびIPから逆引したFQDNをチェックし、不正接続と判断した - ならexitしてください。また、exit直前にstdoutに出力した内容 - の最初の1行がsyslog経由(mail.info)で記録されます。その際の - 出力には`DATA'を含めないで下さい(smtp_wrapperが正当 - なメールと誤判断してしまうので)。 +o 主にコンテンツ内容に対するチェックを行います(第三者リレーチェック)。 o *必ず*、一旦stdinからの入力を全てテンポラリファイルに書き出 - して下さい。通常stdinからは`HELO ...'または`EHLO ...' - または`MAIL FROM: ...' -> いくつかのSMTPコマンド - -> `DATA' -> メール本文 -> `.'の順でデータ - が入って来るでしょう...。 + して下さい。通常stdinからは + + `HELO ...'または`MAIL FROM: ...' + -> いくつかのSMTPコマンド + -> `DATA' + -> メール本文 + -> `.' + + の順でデータが入って来るでしょう...。 + +o テンポラリファイルに書き出したデータをチェックし、不正接続 + と判断したならexitしてください。また、exit直前にstdoutに出 + 力した内容の最初の1行がsyslog経由(mail.info)で記録されます。 + その際の出力には`DATA'を含めないで下さい(smtp_wrapper + が正当なメールと誤判断してしまうので)。 o *必ず*、上述のテンポラリファイル中の`RCPT TO:'の内容と環境 変数`SW_FROM_IP'の内容から不正リレー(第三者リレー)をチェッ @@ -283,14 +324,70 @@ o *必 から見ると接続して来るサーバは全て自分自身(smtp_wrapper)な ので判断のしようが無いためです :-P -o 必要に応じて、上述のテンポラリファイルの内容によりチェック - (spam,virus etc)を行い、不正接続と判断したならexitしてくだ - さい。 +o 必要に応じて、その他のチェック(spam,virus etc)を行い、不正 + 接続と判断したならexitしてください。 o 必要に応じて、その他の必要な処理(ヘッダー挿入、内容改竄、 極秘保存等)を行って下さい B-) o 最終的に、正当な接続と判断されたなら、上述のテンポラリファ - イルから`DATA' -> メール本文 -> `.'をstdout - に出力しexitしてください。 + イルから + + `DATA' + -> メール本文 + -> `.' + + をstdoutに出力しexitしてください。 + +o 要約 + + 正当な接続時 : `DATA' -> メール本文 -> `.' + を標準出力に出力し終了する。 + 不正な接続時 : `DATA'以外で始まるメッセージを標準出力に出力 + し終了する。 + +※ 本フィルタ(`-f')は*必ず*設置しなければなりません。最低限、 + 標準入力から`DATA' -> メール本文 -> `.'を + 標準出力に出力する機能を実装して下さい(see sample/smtp_null_filter.sh)。 + +※ 従来(Ver. 0.2.x以前)と同様に`-f'オプションで指定するフィル + タプログラムに全てのチェックを行わせることも可能です。その + 場合は`-if'オプションを指定する必要はありません。 + + 非力なサーバで運用される場合は`-if', `-f'を使い分けた方が + 負荷が軽減されるでしょう。ちなみに、うちのサーバは + MMX166MHz + Memory 64MB ;) + +BUGS +==== + +o とりあえず、フィルタスクリプトを自分で書かないと、あまり意味 + がありません。一応、smtp_wrapperだけでも`-i'オプションによる + 速射spam対策と`-d'オプションによる`greet pause'効果 + (sendmail-8.13.x系でも同様の機能を導入)は見込めるのですが...。 + +o fetchmailで取り込むメールを重複して受信することがあるようで + す(-_-;。 + + まず、fetchmailで取り込むメールはフィルタの検査対象にしては + なりません。なぜなら、複数のメッセージを取り込んでいる途中 + でspamと判定されるメッセージが有った場合、fetchmailの動作が + その時点で終了してしまう(ようだ)からです。この場合、手動で + POPサーバ側からspamメールを削除しない限り、永久に先に進めな + いのではないかと思われます。 + + また、仮にフィルタ側で常にspamでないと判断させ、smtp_wrapper + をスルーさせても、システムのロードアベレージが10を越えて + sendmail自身がメッセージの受付を拒否し始めた場合に重複して受 + 信することがあるようです。 + + ですので結局のところfetchmailに関しては、 + + fetchmail -S localhost/8025 ほにゃらら + + みたいにして、smtp_wrapperをすっ飛ばして受信したほうが良さそ + うです。 + fetchmailを使って(個人で)取り込むメールのspam対策は、procmail + と上述のフィルタの応用版、もしくはベイズ系フィルタとの組合せ + でもって、クライアント側で対応するのが吉かと...。 diff --git a/README.en b/README.en new file mode 100644 index 0000000..577f3a0 --- /dev/null +++ b/README.en @@ -0,0 +1,21 @@ +smtp_wrapper-0.3 -- SMTP filtering support daemon +Copyright (C) 2006 Masahiko Ito + +These programs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +These programs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with these programs; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Mail suggestions and bug reports for these programs to +"Masahiko Ito" + +See http://myh.no-ip.org/~m-ito/lib100/smtp_wrapper-e.html for detail ;-) diff --git a/sample/black_hostname_db b/sample/black_hostname_db index e69de29..5ecd2c1 100644 --- a/sample/black_hostname_db +++ b/sample/black_hostname_db @@ -0,0 +1,4 @@ +# sample +# spammerのホスト名 +^mail\.spammer\.net$ +^warumono\.org$ diff --git a/sample/black_ip_db b/sample/black_ip_db index e69de29..145fa54 100644 --- a/sample/black_ip_db +++ b/sample/black_ip_db @@ -0,0 +1,4 @@ +# sample +# spammerのIP +^111\.222\.111\.222$ +^222\.111\.222\.[0-9]*$ diff --git a/sample/relay_allow_address_db b/sample/relay_allow_address_db new file mode 100644 index 0000000..30ab0c9 --- /dev/null +++ b/sample/relay_allow_address_db @@ -0,0 +1,4 @@ +# sample +# 外部のサーバに許可する配送先アドレス(for 第三者リレーチェック) +# 検査の対象が`RCPT TO:'行そのものなので、正規表現(^$)で囲んではダメ +myh.no-ip.org diff --git a/sample/relay_allow_host_db b/sample/relay_allow_host_db new file mode 100644 index 0000000..6dd151d --- /dev/null +++ b/sample/relay_allow_host_db @@ -0,0 +1,4 @@ +# sample +# 全てのリレーを許可するホストのIP(for 第三者リレーチェック) +^127\.0\.0\.1$ +^192\.168\.0\.[0-9]*$ diff --git a/sample/show_retry.sh b/sample/show_retry.sh deleted file mode 100755 index fc1a6de..0000000 --- a/sample/show_retry.sh +++ /dev/null @@ -1,16 +0,0 @@ -#! /bin/sh -# -# This part of smtp_wrapper-0.2 is distributed under GNU General Public License. -# -# show today's retry mail -# -# ex. cat /var/log/maillog | show_retry.sh -# -date=`date +'%b %d' | sed -e 's/ *0/ /'` -# -cat $* |\ -sed -e 's/ */ /g' |\ -grep -i "^${date} .*smtp_filter.*:MAIL FROM:" |\ -cut -d: -f5- |\ -sort |\ -uniq -d diff --git a/sample/show_retry1.sh b/sample/show_retry1.sh new file mode 100755 index 0000000..687a9a7 --- /dev/null +++ b/sample/show_retry1.sh @@ -0,0 +1,74 @@ +#! /bin/sh +# +# This part of smtp_wrapper is distributed under GNU General Public License. +# +# show today's retry mail +# +# ex. cat /var/log/maillog | show_retry1.sh +# +PATH="/usr/local/smtp_wrapper:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"; export PATH +#----------------------------------------------------------------- +# +# smtp_filter1.shに拒否されているIPの内、INTERVALに設定した秒数以 +# 上間隔を空けてアクセスしている物を表示する。 +# +# RFC2821(Simple Mail Transfer Protocol) +# +# 4.5.4.1 送信戦略 +# +# ある特定の宛先への送信が失敗した後、クライアントは再送を遅延させ +# なければならない(MUST)。一般にこの再送の間隔は少なくとも 30 分で +# あるべき(SHOULD)だが、配送不能の原因を SMTP クライアントが特定で +# きる場合には、より繊細で柔軟な戦略が有益だろう。 +# +INTERVAL=`expr 30 \* 60`; export INTERVAL +# +date=`date +'%b %d' | sed -e 's/ *0/ /'` +# +# In : May 23 10:59:35 lib100 smtp_wrapper: [25403] 450 This message was rejected according to site policy(rejected by ip_filter. IP=60.236.0.5)(SL18) +# +cat $* |\ +sed -e 's/ */ /g' |\ +grep -i "^${date} .*smtp_wrapper.*reject" |\ +egrep -v 'rejected by rapidly access' |\ +sed -e 's/smtp_wrapper:.*IP=//;s/).*$//;s/:/ /g' |\ +sort -t ' ' +6 -7 +2 -5 |\ +# +# In : May 23 11 10 04 lib100 61.211.239.162 +# +awk 'BEGIN{ + FS = " "; + ip = ""; + interval = ENVIRON["INTERVAL"] +} +{ + if (ip == ""){ + time = ($3 * 3600) + ($4 * 60) + $5 + ip = $7; + print ip; + }else{ + if (ip == $7){ + time_next = ($3 * 3600) + ($4 * 60) + $5 + if ((time_next - time) > interval){ + print $7; + } + time = time_next; + }else{ + time = ($3 * 3600) + ($4 * 60) + $5 + ip = $7; + print ip; + } + } +} +END{ +}' |\ +sort |\ +uniq -d >/tmp/show_retry1.1.$$.tmp +# +for i in `cat /tmp/show_retry1.1.$$.tmp` +do + echo -n $i " : " + host $i +done +# +rm -f /tmp/show_retry1.*.$$.tmp diff --git a/sample/show_retry2.sh b/sample/show_retry2.sh new file mode 100755 index 0000000..cc31c5f --- /dev/null +++ b/sample/show_retry2.sh @@ -0,0 +1,22 @@ +#! /bin/sh +# +# This part of smtp_wrapper is distributed under GNU General Public License. +# +# show today's retry mail +# +# ex. cat /var/log/maillog | show_retry2.sh +# +PATH="/usr/local/smtp_wrapper:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin"; export PATH +#----------------------------------------------------------------- +# +# smtp_filter2.shに拒否されているIPの内、リトライしている物を表示 +# する。 +# +date=`date +'%b %d' | sed -e 's/ *0/ /'` +# +cat $* |\ +sed -e 's/ */ /g' |\ +grep -i "^${date} .*smtp_filter.*:MAIL FROM:" |\ +cut -d: -f5- |\ +sort |\ +uniq -d diff --git a/sample/smtp_filter.sh b/sample/smtp_filter.sh index 08de426..6da9810 100755 --- a/sample/smtp_filter.sh +++ b/sample/smtp_filter.sh @@ -1,12 +1,15 @@ #! /bin/ash # -# This part of smtp_wrapper-0.2 is distributed under GNU General Public License. +# This part of smtp_wrapper is distributed under GNU General Public License. # # Sample spam filter script -# by "Masahiko Ito" +# by "Masahiko Ito" # # Thanks to http://www.gabacho-net.jp/anti-spam/anti-spam-system.html # +# IPに対するチェック(S25R)とコンテンツに対するチェック(第三者リレー)を同時に +# 行う場合のスクリプト。`-f'オプションに指定する。`-if'オプションは指定しない。 +# #============================================================ # # 割り込み時の処理設定 @@ -16,13 +19,13 @@ trap "spam_exit 1 \"kill by signal\"" INT HUP TERM QUIT # # 実行パス設定 # -PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" +PATH="/usr/local/smtp_wrapper:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" export PATH #============================================================ # # smtp_wrapperディレクトリ指定 # -smtp_wrapper_dir="/usr/local/smtp_wrapper-0.2" +smtp_wrapper_dir="/usr/local/smtp_wrapper" # # テンポラリスプールディレクトリ tmp="/var/tmp" @@ -39,16 +42,16 @@ black_ip_db="${smtp_wrapper_dir}/black_ip_db" # 不正ホストのFQDNデータベース black_hostname_db="${smtp_wrapper_dir}/black_hostname_db" # -# 全てのリレーを許可するホストのIPデータベース(sendmailと共用) -relay_allow_host="/etc/mail/access" -# 外部のサーバに許可する配送先アドレスデータベース(sendmailと共用) -relay_allow_address="/etc/mail/local-host-names" +# 全てのリレーを許可するホストのIPデータベース +relay_allow_host="${smtp_wrapper_dir}/relay_allow_host_db" +# 外部のサーバに許可する配送先アドレスデータベース +relay_allow_address="${smtp_wrapper_dir}/relay_allow_address_db" #============================================================ # # ある種の定数 # -cr=`awk 'BEGIN{printf "\r"}'`; export cr -tab=`awk 'BEGIN{printf "\t"}'`; export tab +cr=`echo -n -e '\r'`; export cr +tab=`echo -n -e '\t'`; export tab pid="$$"; export pid #============================================================ # @@ -187,7 +190,8 @@ spam_check () relay_check () { allow_ip="" - for i in `cat ${relay_allow_host}|\ + for i in `cat ${relay_allow_host} |\ + grep -v '^#' |\ sed -e "s/${tab}/ /g;s/ */ /g" |\ cut -d ' ' -f 1` do @@ -215,7 +219,8 @@ relay_check () egrep -i '^RCPT *TO:' |\ sed -e 's/ *//g` do - for j in `cat ${relay_allow_address}` + for j in `cat ${relay_allow_address} |\ + grep -v '^#'` do allow_rcpt="" allow_rcpt=`echo $i |\ diff --git a/sample/smtp_filter1.sh b/sample/smtp_filter1.sh new file mode 100755 index 0000000..ba229c5 --- /dev/null +++ b/sample/smtp_filter1.sh @@ -0,0 +1,200 @@ +#! /bin/ash +# +# This part of smtp_wrapper is distributed under GNU General Public License. +# +# Sample spam filter script +# by "Masahiko Ito" +# +# Thanks to http://www.gabacho-net.jp/anti-spam/anti-spam-system.html +# +# IPに対するチェック(S25R)を行う場合のスクリプト。`-if'オプションに指定する。 +# +#============================================================ +# +# 割り込み時の処理設定 +# +trap "spam_exit 1 \"kill by signal\"" INT HUP TERM QUIT +#============================================================ +# +# 実行パス設定 +# +PATH="/usr/local/smtp_wrapper:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" +export PATH +#============================================================ +# +# smtp_wrapperディレクトリ指定 +# +smtp_wrapper_dir="/usr/local/smtp_wrapper" +# +# テンポラリスプールディレクトリ +tmp="/var/tmp" +# +# syslog出力時のID +sl_ident="smtp_filter1" +# +# 正当ホストのIPデータベース +white_ip_db="${smtp_wrapper_dir}/white_ip_db" +# 正当ホストのFQDNデータベース +white_hostname_db="${smtp_wrapper_dir}/white_hostname_db" +# 不正ホストのIPデータベース +black_ip_db="${smtp_wrapper_dir}/black_ip_db" +# 不正ホストのFQDNデータベース +black_hostname_db="${smtp_wrapper_dir}/black_hostname_db" +# +#============================================================ +# +# ある種の定数 +# +cr=`echo -n -e '\r'`; export cr +tab=`echo -n -e '\t'`; export tab +pid="$$"; export pid +#============================================================ +# +# spamと判断時に終了させる処理 +# +spam_exit () +{ + logger -p mail.info -t ${sl_ident} "[$$]:IP=${from_ip}" + logger -p mail.info -t ${sl_ident} "[$$]:HOST=${from_hostname}" + logger -p mail.info -t ${sl_ident} "[$$]:EXIT=$1" + logger -p mail.info -t ${sl_ident} "[$$]:REASON=$2" + echo "[$$]:IP=${from_ip} HOST=${from_hostname} EXIT=$1 REASON=$2" + rm -f ${tmp}/smtp_filter1.*.$$.tmp + exit $1 +} +#============================================================ +# +# スパムチェック処理 +# +spam_check () +{ + if [ "X${from_ip}" = "X" ] + then + spam_exit 2 "unknown ip" # *** 送信ホストのIPが判らない *** + fi +# + for i in `cat ${black_ip_db} |\ + egrep -v '^#'` + do + black_ip=`echo "${from_ip}" |\ + egrep -i "$i"` + if [ "X${black_ip}" != "X" ] + then + spam_exit 3 "black IP" # *** 送信ホストIPが黒IPデータベースに登録されている *** + fi + done +# + for i in `cat ${white_ip_db} |\ + egrep -v '^#'` + do + white_ip=`echo "${from_ip}" |\ + egrep -i "$i"` + if [ "X${white_ip}" != "X" ] + then + break; + fi + done +# + from_hostname=`host ${from_ip} 2>/dev/null |\ + egrep -i 'domain name pointer' |\ + head -1 |\ + sed -e 's/^.* domain name pointer *//;s/\.$//'` + if [ "X${white_ip}" = "X" -a "X${from_hostname}" = "X" ] + then + spam_exit 4 "no dns" # *** 送信ホストのIPが逆引きできない *** + fi +# + for i in `cat ${black_hostname_db} |\ + egrep -v '^#'` + do + black_hostname=`echo "${from_hostname}" |\ + egrep -i "$i"` + if [ "X${black_hostname}" != "X" ] + then + spam_exit 5 "black hostname" # *** 送信ホスト名が黒ホスト名データベースに登録されている *** + fi + done +# + for i in `cat ${white_hostname_db} |\ + egrep -v '^#'` + do + white_hostname=`echo "${from_hostname}" |\ + egrep -i "$i"` + if [ "X${white_hostname}" != "X" ] + then + break; + fi + done +# + if [ "X${white_ip}" = "X" -a "X${white_hostname}" = "X" ] + then +# + spam_host=`echo ${from_hostname} |\ + egrep -i '^[^\.]*[0-9][^0-9\.]+[0-9]'` + if [ "X${spam_host}" != "X" ] + then + spam_exit 6 "rule 1" # *** [ルール1] 逆引きFQDNの最下位(左端)の名前が、数字以外の文字列で分断された二つ以上の数 *** + fi +# + spam_host=`echo ${from_hostname} |\ + egrep -i '^[^\.]*[0-9]{5}'` + if [ "X${spam_host}" != "X" ] + then + spam_exit 7 "rule 2" # *** [ルール2] 逆引きFQDNの最下位の名前が、5個以上連続する数字を含む *** + fi +# + spam_host=`echo ${from_hostname} |\ + egrep -i '^([^\.]+\.)?[0-9][^\.]*\.[^\.]+\..+\.[a-z]'` + if [ "X${spam_host}" != "X" ] + then + spam_exit 8 "rule 3" # *** [ルール3] 逆引きFQDNの上位3階層を除き、最下位または下位から2番目の名前が数字で始まる *** + fi +# + spam_host=`echo ${from_hostname} |\ + egrep -i '^[^\.]*[0-9]\.[^\.]*[0-9]-[0-9]'` + if [ "X${spam_host}" != "X" ] + then + spam_exit 9 "rule 4" # *** [ルール4] 逆引きFQDNの最下位の名前が数字で終わり、かつ下位から2番目の名前が、1個のハイフンで分断された二つ以上の数字列を含む *** + fi +# + spam_host=`echo ${from_hostname} |\ + egrep -i '^[^\.]*[0-9]\.[^\.]*[0-9]\.[^\.]+\..+\.'` + if [ "X${spam_host}" != "X" ] + then + spam_exit 10 "rule 5" # *** [ルール5] 逆引きFQDNが5階層以上で、下位2階層の名前がともに数字で終わる *** + fi +# + spam_host=`echo ${from_hostname} |\ + egrep -i '^(dhcp|dialup|ppp|adsl)[^\.]*[0-9]'` + if [ "X${spam_host}" != "X" ] + then + spam_exit 11 "rule 6" # *** [ルール6] 逆引きFQDNの最下位の名前が「dhcp」、「dialup」、「ppp」、または「adsl」で始まり、かつ数字を含む *** + fi + fi +} +#============================================================ +# +# 主処理開始 +# +from_ip="" +from_hostname="" +white_ip="" +white_hostname="" +black_ip="" +black_hostname="" +# +#============================================================ +# +# 直接SMTP接続分のチェック +# +# (smtp_wrapper から環境変数 SW_FROM_IP に接続元IPが渡される) +# +from_ip=${SW_FROM_IP} +# +spam_check +#============================================================ +# +# 後始末して終了 +# +rm -f ${tmp}/smtp_filter1.*.$$.tmp +exit 0 diff --git a/sample/smtp_filter2.sh b/sample/smtp_filter2.sh new file mode 100755 index 0000000..2e1c079 --- /dev/null +++ b/sample/smtp_filter2.sh @@ -0,0 +1,182 @@ +#! /bin/ash +# +# This part of smtp_wrapper is distributed under GNU General Public License. +# +# Sample spam filter script +# by "Masahiko Ito" +# +# Thanks to http://www.gabacho-net.jp/anti-spam/anti-spam-system.html +# +# コンテンツに対するチェック(第三者リレー)を行う場合のスクリプト。`-f'オプションに指定する。 +# +#============================================================ +# +# 割り込み時の処理設定 +# +trap "spam_exit 1 \"kill by signal\"" INT HUP TERM QUIT +#============================================================ +# +# 実行パス設定 +# +PATH="/usr/local/smtp_wrapper:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" +export PATH +#============================================================ +# +# smtp_wrapperディレクトリ指定 +# +smtp_wrapper_dir="/usr/local/smtp_wrapper" +# +# テンポラリスプールディレクトリ +tmp="/var/tmp" +# +# syslog出力時のID +sl_ident="smtp_filter2" +# +# 全てのリレーを許可するホストのIPデータベース +relay_allow_host="${smtp_wrapper_dir}/relay_allow_host_db" +# 外部のサーバに許可する配送先アドレスデータベース +relay_allow_address="${smtp_wrapper_dir}/relay_allow_address_db" +#============================================================ +# +# ある種の定数 +# +cr=`echo -n -e '\r'`; export cr +tab=`echo -n -e '\t'`; export tab +pid="$$"; export pid +#============================================================ +# +# spamと判断時に終了させる処理 +# +spam_exit () +{ + if [ -r ${tmp}/smtp_filter2.*.$$.tmp ] + then + logger -p mail.info -t ${sl_ident} "[$$]:`cat ${tmp}/smtp_filter2.*.$$.tmp | head -50 | egrep -i '^MAIL *FROM:|^RCPT *TO:|^From:|^To:|^Subject:|^Date:'`" + else + logger -p mail.info -t ${sl_ident} "[$$]:`cat | head -50 | egrep -i '^MAIL *FROM:|^RCPT *TO:|^From:|^To:|^Subject:|^Date:'`" + fi + logger -p mail.info -t ${sl_ident} "[$$]:IP=${from_ip}" + logger -p mail.info -t ${sl_ident} "[$$]:HOST=${from_hostname}" + logger -p mail.info -t ${sl_ident} "[$$]:EXIT=$1" + logger -p mail.info -t ${sl_ident} "[$$]:REASON=$2" + rm -f ${tmp}/smtp_filter2.*.$$.tmp + echo "[$$]:IP=${from_ip} HOST=${from_hostname} EXIT=$1 REASON=$2" + exit $1 +} +#============================================================ +# +# 第三者リレーチェック処理 +# +relay_check () +{ + allow_ip="" + for i in `cat ${relay_allow_host} |\ + grep -v '^#' |\ + sed -e "s/${tab}/ /g;s/ */ /g" |\ + cut -d ' ' -f 1` + do + allow_ip=`echo ${from_ip} |\ + egrep "$i"` + if [ "X${allow_ip}" != "X" ] + then + break + fi + done +# + if [ "X${allow_ip}" = "X" ] + then + for i in `cat ${tmp}/smtp_filter2.1.$$.tmp |\ + awk 'BEGIN{ + cr = ENVIRON["cr"] + } + { + if (length($0) == 1 && index($0, cr) > 0){ + exit; + }else{ + print; + } + }' |\ + egrep -i '^RCPT *TO:' |\ + sed -e 's/ *//g` + do + for j in `cat ${relay_allow_address} |\ + grep -v '^#'` + do + allow_rcpt="" + allow_rcpt=`echo $i |\ + egrep "$j"` + if [ "X${allow_rcpt}" != "X" ] + then + break; + fi + done + if [ "X${allow_rcpt}" = "X" ] + then + spam_exit 12 "no relay" # *** 第三者中継拒否 *** + fi + done + fi +} +#============================================================ +# +# 主処理開始 +# +from_ip="" +from_hostname="" +# +#============================================================ +# +# メール本文を全文受信(スプール)する +# +cat >${tmp}/smtp_filter2.1.$$.tmp +#============================================================ +# +# 第三者中継拒否 +# +from_ip=${SW_FROM_IP} +from_hostname=`host ${from_ip} 2>/dev/null |\ + egrep -i 'domain name pointer' |\ + head -1 |\ + sed -e 's/^.* domain name pointer *//;s/\.$//'` +# +relay_check +# +#============================================================ +# +# 正当なメールと判断されたものを出力する +# +export from_ip +export from_hostname +# +cat ${tmp}/smtp_filter2.1.$$.tmp |\ +awk 'BEGIN{ + from_ip = ENVIRON["from_ip"] + from_hostname = ENVIRON["from_hostname"] + pid = ENVIRON["pid"] + out_sw = 0; +} +{ + if (out_sw == 0){ + head = toupper($0); + if (head ~ /^DATA\r$/ || + head ~ /^DATA$/){ + out_sw = 1; + } + } + + if (out_sw == 1){ + print $0; + } +} +END{ + if (out_sw == 0){ + printf("[%d]:IP=%-s HOST=%-s REASON=NOT spam, but NO DATA\n", pid, from_ip, from_hostname) + } +}' +# +#============================================================ +# +# 後始末して終了 +# +rm -f ${tmp}/smtp_filter2.*.$$.tmp +exit 0 diff --git a/sample/smtp_null_filter.sh b/sample/smtp_null_filter.sh index c29e8cb..d8dbdca 100755 --- a/sample/smtp_null_filter.sh +++ b/sample/smtp_null_filter.sh @@ -1,22 +1,22 @@ #! /bin/ash # -# This part of smtp_wrapper-0.2 is distributed under GNU General Public License. +# This part of smtp_wrapper is distributed under GNU General Public License. # # Sample null filter script -# by "Masahiko Ito" +# by "Masahiko Ito" # # smtp_wrapperデバッグ用 # 第三者中継チェックすらしてないので注意 # #------------------------------------------------------------ -PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" +PATH="/usr/local/smtp_wrapper:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" export PATH #------------------------------------------------------------ tmp="/var/tmp" #------------------------------------------------------------ -cat >${tmp}/smtp_filter.1.$$.tmp +cat >${tmp}/smtp_null_filter.1.$$.tmp # -cat ${tmp}/smtp_filter.*.$$.tmp |\ +cat ${tmp}/smtp_null_filter.*.$$.tmp |\ awk 'BEGIN{ out_sw = 0; } @@ -36,5 +36,5 @@ awk 'BEGIN{ END{ }' # -rm -f ${tmp}/smtp_filter.*.$$.tmp +rm -f ${tmp}/smtp_null_filter.*.$$.tmp exit 0 diff --git a/sample/white_hostname_db b/sample/white_hostname_db index cfbf88e..43e354f 100644 --- a/sample/white_hostname_db +++ b/sample/white_hostname_db @@ -1,3 +1,6 @@ # sample +# 小細工用(普通はいらない) ^_OK_$ +# +# 許可するドメイン ^foo.*\.bar\.ne\.jp$ diff --git a/sample/white_ip_db b/sample/white_ip_db index dd6287a..b992513 100644 --- a/sample/white_ip_db +++ b/sample/white_ip_db @@ -1,4 +1,9 @@ # sample +# 小細工用(普通はいらない) ^_OK_$ +# +# localhost ^127\.0\.0\.1$ -^192\.168\.0\.[0-9]$ +# +# 許可するIPアドレス(自ネットワークも忘れず指定しましょう) +^192\.168\.0\.[0-9]*$ diff --git a/smtp_wrapper.c b/smtp_wrapper.c index 748d8aa..ccaec35 100644 --- a/smtp_wrapper.c +++ b/smtp_wrapper.c @@ -1,12 +1,14 @@ /* - * This part of smtp_wrapper-0.2 is distributed under GNU General Public License. + * This part of smtp_wrapper-0.3 is distributed under GNU General Public License. */ /* * smtp_wrapper (spam filtering support daemon) - * by "Masahiko Ito" + * by "Masahiko Ito" * + * Ver. 0.1 2006/01/23 release (1st) * Ver. 0.2 2006/05/15 release + * Ver. 0.3 2006/05/24 release */ #include #include @@ -28,7 +30,7 @@ #include #include -#define SW_VERSION "0.2" +#define SW_VERSION "0.3" #define SS_INITMSG (0) #define SS_RECVCOM (1) @@ -53,7 +55,7 @@ int ChildCount = 0; /* * main routine * - * Usage : smtp_wrapper -mh hostname -mp port -q backlog -sh smtpserver_hostname -sp smtpserver_port -t timeout_sec -d delay_sec -f filter -cm child_max -i minimum_interval_sec -F + * Usage : smtp_wrapper -mh hostname -mp port -q backlog -sh smtpserver_hostname -sp smtpserver_port -t timeout_sec -d delay_sec -if ip_filter -f contents_filter -cm child_max -i minimum_interval_sec -F */ int main(argc, argv, arge) int argc; @@ -63,7 +65,7 @@ int main(argc, argv, arge) int i; int socket_accept_client, socket_rw_client; char hostname[HOST_LEN], smtp_hostname[HOST_LEN]; - char filter[PATH_LEN]; + char ip_filter[PATH_LEN], filter[PATH_LEN]; int port, smtp_port, backlog; struct timeval tv, *ptr_tv; int delay_sec, minimum_interval_sec; @@ -79,7 +81,7 @@ int main(argc, argv, arge) /* * start message */ - syslog(LOG_MAIL|LOG_INFO, "[%d] smtp_wrapper ver. %-s started\n", getpid(), SW_VERSION); + syslog(LOG_MAIL|LOG_INFO, "[%d] smtp_wrapper ver. %-s started(SL01)\n", getpid(), SW_VERSION); /* * initialize struct @@ -97,8 +99,10 @@ int main(argc, argv, arge) bzero(smtp_hostname, sizeof(smtp_hostname)); strncpy(smtp_hostname, "localhost", strlen("localhost")); smtp_port = 8025; + bzero(ip_filter, sizeof(ip_filter)); + strncpy(ip_filter, "/usr/local/smtp_wrapper/smtp_ip_filter", strlen("/usr/local/smtp_wrapper/smtp_ip_filter")); bzero(filter, sizeof(filter)); - strncpy(filter, "/usr/local/smtp_wrapper/smtp_filter", strlen("/usr/local/smtp_wrapper/smtp_filter")); + strncpy(filter, "/usr/local/smtp_wrapper/smtp_contents_filter", strlen("/usr/local/smtp_wrapper/smtp_contents_filter")); ptr_tv = (struct timeval *)NULL; delay_sec = 0; forground_sw = 0; @@ -131,6 +135,10 @@ int main(argc, argv, arge) }else if (strncmp(argv[i], "-d", strlen("-d")) == 0){ i++; delay_sec = atoi(argv[i]); + }else if (strncmp(argv[i], "-if", strlen("-if")) == 0){ + i++; + bzero(ip_filter, sizeof(ip_filter)); + strncpy(ip_filter, argv[i], sizeof(ip_filter) - 1); }else if (strncmp(argv[i], "-f", strlen("-f")) == 0){ i++; bzero(filter, sizeof(filter)); @@ -218,7 +226,7 @@ int main(argc, argv, arge) * check child max count */ if (ChildCount > child_max){ - syslog(LOG_MAIL|LOG_INFO, "[%d] ChildCount exceeds child_max(%d), so sleep into %d sec\n", getpid(), child_max, delay_sec); + syslog(LOG_MAIL|LOG_INFO, "[%d] ChildCount exceeds child_max(%d), so sleep into %d sec(SL02)\n", getpid(), child_max, delay_sec); left_sleep = sleep(delay_sec); while (left_sleep > 0){ @@ -227,7 +235,7 @@ int main(argc, argv, arge) sweep_zombi(); while (ChildCount > (child_max / 2)){ - syslog(LOG_MAIL|LOG_INFO, "[%d] ChildCount exceeds half of child_max(%d), so sleep into %d sec\n", getpid(), child_max, delay_sec); + syslog(LOG_MAIL|LOG_INFO, "[%d] ChildCount exceeds half of child_max(%d), so sleep into %d sec(SL03)\n", getpid(), child_max, delay_sec); left_sleep = sleep(delay_sec); while (left_sleep > 0){ @@ -236,7 +244,7 @@ int main(argc, argv, arge) sweep_zombi(); } - syslog(LOG_MAIL|LOG_INFO, "[%d] ChildCount become under half of child_max(%d), so wakeup\n", getpid(), child_max); + syslog(LOG_MAIL|LOG_INFO, "[%d] ChildCount become under half of child_max(%d), so wakeup(SL04)\n", getpid(), child_max); } /* @@ -251,15 +259,15 @@ int main(argc, argv, arge) bzero(ip_old, sizeof(ip_old)); strncpy(ip_old, inet_ntoa(client_sockaddr_in_old.sin_addr), sizeof(ip_old) - 1); if (strncmp(ip_new, ip_old, sizeof(ip_new)) == 0){ - syslog(LOG_MAIL|LOG_INFO, "[%d] This message was sent from %-s cyclic & rapidly, so rejected\n", getpid(), inet_ntoa(client_sockaddr_in.sin_addr)); - bzero(client_buf, BUF_LEN); - strncpy(client_buf, "421 Service not available, closing transmission channel\r\n", sizeof(client_buf) - 1); + snprintf(client_buf, sizeof(client_buf) - 1, "421 Service not available, closing transmission channel(rejected by rapidly access. IP=%s)(SW01)\r\n", inet_ntoa(client_sockaddr_in.sin_addr)); if (sock_write(socket_rw_client, client_buf, strlen(client_buf)) < 0){ - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error in cyclic check\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL05)\n", getpid()); } close(socket_rw_client); socket_rw_client = -1; + + syslog(LOG_MAIL|LOG_INFO, "[%d] 421 Service not available, closing transmission channel(rejected by rapidly access. IP=%-s)(SL06)\n", getpid(), inet_ntoa(client_sockaddr_in.sin_addr)); } } } @@ -270,7 +278,7 @@ int main(argc, argv, arge) * call child routine */ if (socket_rw_client == -1){ - /* do nothing */ + /* do nothing */; }else{ if (fork() == 0){ @@ -280,7 +288,7 @@ int main(argc, argv, arge) } close(socket_accept_client); - communicate_smtpdaemon(smtp_hostname, smtp_port, socket_rw_client, ptr_tv, filter, &client_sockaddr_in); + communicate_smtpdaemon(smtp_hostname, smtp_port, socket_rw_client, ptr_tv, ip_filter, filter, &client_sockaddr_in); exit(0); }else{ ChildCount++; @@ -331,13 +339,13 @@ void SigTrap(sig) switch (sig){ case SIGTERM: - syslog(LOG_MAIL|LOG_INFO, "[%d] SIGTERM catched and normal shutdown\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] SIGTERM catched and normal shutdown(SL07)\n", getpid()); exit(0); break; case SIGCHLD: break; default: - syslog(LOG_MAIL|LOG_INFO, "[%d] signal(%d) catched and exit\n", getpid(), sig); + syslog(LOG_MAIL|LOG_INFO, "[%d] signal(%d) catched and exit(SL08)\n", getpid(), sig); exit(0); break; } @@ -368,7 +376,7 @@ int open_accept_socket_for_client(hostname, port, backlog) my_sockaddr_in.sin_addr.s_addr = htonl(INADDR_ANY); }else{ if ((my_hostent = gethostbyname(hostname)) == (struct hostent *)NULL){ - syslog(LOG_MAIL|LOG_INFO, "[%d] open_accept_socket_for_client : gethostbyname(%-s) : %-s\n", getpid(), hostname, strerror(errno)); + syslog(LOG_MAIL|LOG_INFO, "[%d] open_accept_socket_for_client : gethostbyname(%-s) error : %-s(SL09)\n", getpid(), hostname, strerror(errno)); return -1; } bcopy(my_hostent->h_addr, (char *)&my_sockaddr_in.sin_addr, my_hostent->h_length); @@ -378,16 +386,16 @@ int open_accept_socket_for_client(hostname, port, backlog) * ready socket */ if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1){ - syslog(LOG_MAIL|LOG_INFO, "[%d] open_accept_socket_for_client : socket : %-s\n", getpid(), strerror(errno)); + syslog(LOG_MAIL|LOG_INFO, "[%d] open_accept_socket_for_client : socket error : %-s(SL10)\n", getpid(), strerror(errno)); return -1; } if (bind(s, (struct sockaddr *)&my_sockaddr_in, sizeof my_sockaddr_in) == -1){ - syslog(LOG_MAIL|LOG_INFO, "[%d] open_accept_socket_for_client : bind : %-s\n", getpid(), strerror(errno)); + syslog(LOG_MAIL|LOG_INFO, "[%d] open_accept_socket_for_client : bind error : %-s(SL11)\n", getpid(), strerror(errno)); return -1; } if (listen(s, backlog) == -1){ - syslog(LOG_MAIL|LOG_INFO, "[%d] open_accept_socket_for_client : listen : %-s\n", getpid(), strerror(errno)); + syslog(LOG_MAIL|LOG_INFO, "[%d] open_accept_socket_for_client : listen error : %-s(SL12)\n", getpid(), strerror(errno)); return -1; } @@ -432,11 +440,11 @@ int open_rw_socket_for_server(hostname, port) errno = 0; if ((server_hostent = gethostbyname(hostname)) == (struct hostent *)NULL){ - syslog(LOG_MAIL|LOG_INFO, "[%d] open_rw_socket_for_server : gethostbyname(%-s) : %-s\n", getpid(), hostname, strerror(errno)); + syslog(LOG_MAIL|LOG_INFO, "[%d] open_rw_socket_for_server : gethostbyname(%-s) error : %-s(SL13)\n", getpid(), hostname, strerror(errno)); return -1; } if ((s = socket(AF_INET,SOCK_STREAM, 0)) < 0){ - syslog(LOG_MAIL|LOG_INFO, "[%d] open_rw_socket_for_server : socket : %-s\n", getpid(), strerror(errno)); + syslog(LOG_MAIL|LOG_INFO, "[%d] open_rw_socket_for_server : socket error : %-s(SL14)\n", getpid(), strerror(errno)); return -1; } memset((char *)&server_sockaddr_in, 0, sizeof(server_sockaddr_in)); @@ -444,7 +452,7 @@ int open_rw_socket_for_server(hostname, port) server_sockaddr_in.sin_port = htons(port); memcpy((char *)&server_sockaddr_in.sin_addr, server_hostent->h_addr, server_hostent->h_length); if (connect(s, (struct sockaddr *)&server_sockaddr_in, sizeof(server_sockaddr_in)) == -1){ - syslog(LOG_MAIL|LOG_INFO, "[%d] open_rw_socket_for_server : connect : %-s\n", getpid(), strerror(errno)); + syslog(LOG_MAIL|LOG_INFO, "[%d] open_rw_socket_for_server : connect error : %-s(SL15)\n", getpid(), strerror(errno)); return -1; } @@ -473,11 +481,12 @@ int sweep_zombi() /* * communicate smtpdaemon */ -int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa) +int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, ip_filter, filter, csa) char *hostname; int port; int socket_rw_client; struct timeval *ptr_tv; + char *ip_filter; char *filter; struct sockaddr_in *csa; { @@ -495,6 +504,9 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa int socket_rw_smtpdaemon; int ret_client, ret_smtpdaemon, ret_filter; + FILE *fp_ip_filter; + struct stat st_ip_filter; + int pipe_p2c[2],pipe_c2p[2]; int fd_r_filter, fd_w_filter; @@ -503,17 +515,75 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa struct timeval tv; +/* + * set client IP to SW_FROM_IP + */ + setenv("SW_FROM_IP", inet_ntoa(csa->sin_addr), 1); + /* * child logging start */ - syslog(LOG_MAIL|LOG_INFO, "[%d] smtp_wrapper child start : SW_FROM_IP=%-s\n", getpid(), inet_ntoa(csa->sin_addr)); + syslog(LOG_MAIL|LOG_INFO, "[%d] smtp_wrapper child start : IP=%-s(SL16)\n", getpid(), inet_ntoa(csa->sin_addr)); + +/* + * execute ip_filter + */ + if (stat(ip_filter, &st_ip_filter) == 0){ + if ((fp_ip_filter = popen(ip_filter, "r")) == (FILE *)NULL){ + smtp_status = SS_INITMSG; + }else{ + bzero(filter_buf, BUF_LEN); + fgets(filter_buf, sizeof filter_buf, fp_ip_filter); + pclose(fp_ip_filter); + if (strncmp(filter_buf, "OK", strlen("OK")) == 0 || + strncmp(filter_buf, "ok", strlen("ok")) == 0 || + filter_buf[0] == '\0'){ + smtp_status = SS_INITMSG; + }else{ + bzero(client_buf, BUF_LEN); + snprintf(client_buf, sizeof(client_buf) - 1, "450 This message was rejected according to site policy(rejected by ip_filter. IP=%s)(SW02)\r\n", inet_ntoa(csa->sin_addr)); + if ((ret_client = sock_write(socket_rw_client, client_buf, strlen(client_buf))) < 0){ + close(socket_rw_client); + socket_rw_client = -1; + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL17)\n", getpid()); + } + + syslog(LOG_MAIL|LOG_INFO, "[%d] 450 This message was rejected according to site policy(rejected by ip_filter. IP=%s)(SL18)\n", getpid(), inet_ntoa(csa->sin_addr)); + + close(socket_rw_client); + socket_rw_client = -1; + close(socket_rw_smtpdaemon); + socket_rw_smtpdaemon = -1; + smtp_status = SS_EXIT; + } + } + }else{ + smtp_status = SS_INITMSG; + } + bzero(filter_buf, BUF_LEN); + bzero(client_buf, BUF_LEN); /* * open socket to smtp daemon */ - if ((socket_rw_smtpdaemon = open_rw_socket_for_server(hostname, port)) == -1){ - syslog(LOG_MAIL|LOG_INFO, "[%d] communicate_smtpdaemon : open_rw_socket_for_server(%-s, %d) : %-s\n", getpid(), hostname, port, strerror(errno)); - return -1; + if (smtp_status == SS_INITMSG){ + if ((socket_rw_smtpdaemon = open_rw_socket_for_server(hostname, port)) == -1){ + bzero(client_buf, BUF_LEN); + snprintf(client_buf, sizeof(client_buf) - 1, "451 server error, rejected(rejected by MTA error for connect. IP=%s)(SW03)\r\n", inet_ntoa(csa->sin_addr)); + if ((ret_client = sock_write(socket_rw_client, client_buf, strlen(client_buf))) < 0){ + close(socket_rw_client); + socket_rw_client = -1; + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL19)\n", getpid()); + } + + syslog(LOG_MAIL|LOG_INFO, "[%d] 451 server error, rejected(rejected by MTA error for connect. IP=%s)(SL20)\n", getpid(), inet_ntoa(csa->sin_addr)); + + close(socket_rw_client); + socket_rw_client = -1; + close(socket_rw_smtpdaemon); + socket_rw_smtpdaemon = -1; + smtp_status = SS_EXIT; + } } bzero(read_client_buf, BUF_LEN); @@ -521,7 +591,6 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa fd_r_filter = -1; fd_w_filter = -1; - smtp_status = SS_INITMSG; while (smtp_status != SS_EXIT && socket_rw_client > 0 && socket_rw_smtpdaemon > 0){ /* @@ -537,11 +606,7 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa sw_socket_rw_smtpdaemon = 1; sw_socket_rw_client = 1; }else if (smtp_status == SS_RECVCOM){ -#if 0 /* funny wait happen. why ? */ - sw_fd_r_filter = 1; -#else sw_fd_r_filter = 0; -#endif sw_socket_rw_smtpdaemon = 0; sw_socket_rw_client = 1; }else if (smtp_status == SS_RESNORM){ @@ -642,39 +707,44 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa } if (select_num == 0){ - syslog(LOG_MAIL|LOG_INFO, "[%d] communicate_smtpdaemon : select timeout : %-s\n", getpid(), strerror(errno)); bzero(client_buf, BUF_LEN); - strncpy(client_buf, "451 Requested action aborted: local error in processing(1 select timeout)\r\n", sizeof(client_buf) - 1); + snprintf(client_buf, (sizeof client_buf) - 1, "451 Requested action aborted: local error in processing(rejected by select timeout. IP=%s)(SW04)\r\n", inet_ntoa(csa->sin_addr)); if ((ret_client = sock_write(socket_rw_client, client_buf, strlen(client_buf))) < 0){ close(socket_rw_client); socket_rw_client = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error in processing(1 select timeout)\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL21)\n", getpid()); } - syslog(LOG_MAIL|LOG_INFO, "[%d] 451 Requested action aborted: local error in processing(1 select timeout)\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] 451 Requested action aborted: local error in processing(rejected by select timeout. IP=%s)(SL22)\n", getpid(), inet_ntoa(csa->sin_addr)); + close(socket_rw_client); + socket_rw_client = -1; + close(socket_rw_smtpdaemon); + socket_rw_smtpdaemon = -1; smtp_status = SS_EXIT; }else if (select_num < 0){ /* * select return negative value when SIGPIPE occured(?), So IGNORE */ - syslog(LOG_MAIL|LOG_INFO, "[%d] communicate_smtpdaemon : select error : %-s\n", getpid(), strerror(errno)); + syslog(LOG_MAIL|LOG_INFO, "[%d] communicate_smtpdaemon : select SIGPIPE ignore : %-s(SL23)\n", getpid(), strerror(errno)); } if (smtp_status == SS_INITMSG){ if (FD_ISSET(socket_rw_client, &fdset) || read_client_buf[0] != '\0'){ bzero(client_buf, BUF_LEN); - strncpy(client_buf, "450 This message was rejected according to site policy(1)\r\n", sizeof(client_buf) - 1); + snprintf(client_buf, sizeof(client_buf) - 1, "450 This message was rejected according to site policy(rejected by seq error. IP=%s)(SW05)\r\n", inet_ntoa(csa->sin_addr)); if ((ret_client = sock_write(socket_rw_client, client_buf, strlen(client_buf))) < 0){ - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error in SS_INITMSG\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL24)\n", getpid()); } - close(socket_rw_client); - socket_rw_client = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] 450 This message was rejected according to site policy(1)\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] 450 This message was rejected according to site policy(rejected by seq error. IP=%s)(SL25)\n", getpid(), inet_ntoa(csa->sin_addr)); + close(socket_rw_client); + socket_rw_client = -1; + close(socket_rw_smtpdaemon); + socket_rw_smtpdaemon = -1; smtp_status = SS_EXIT; } @@ -687,7 +757,7 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa if ((ret_client = sock_write(socket_rw_client, smtpdaemon_buf, strlen(smtpdaemon_buf))) < 0){ close(socket_rw_client); socket_rw_client = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error in SS_INITMSG\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL26)\n", getpid()); } smtp_status = SS_RECVCOM; @@ -720,12 +790,36 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa * create pipe for filter */ if (pipe(pipe_c2p) < 0){ - syslog(LOG_MAIL|LOG_INFO, "[%d] communicate_smtpdaemon : pipe_c2p : %-s\n", getpid(), strerror(errno)); - return -1; + bzero(client_buf, BUF_LEN); + snprintf(client_buf, sizeof(client_buf) - 1, "451 server error, rejected(rejected by pipe_c2p. IP=%s)(SW06)\r\n", inet_ntoa(csa->sin_addr)); + if ((ret_client = sock_write(socket_rw_client, client_buf, strlen(client_buf))) < 0){ + close(socket_rw_client); + socket_rw_client = -1; + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL27)\n", getpid()); + } + + syslog(LOG_MAIL|LOG_INFO, "[%d] 451 server error, rejected(rejected by pipe_c2p. IP=%s)(SL28)\n", getpid(), inet_ntoa(csa->sin_addr)); + close(socket_rw_client); + socket_rw_client = -1; + close(socket_rw_smtpdaemon); + socket_rw_smtpdaemon = -1; + smtp_status = SS_EXIT; } if (pipe(pipe_p2c) < 0){ - syslog(LOG_MAIL|LOG_INFO, "[%d] communicate_smtpdaemon : pipe_p2c : %-s\n", getpid(), strerror(errno)); - return -1; + bzero(client_buf, BUF_LEN); + snprintf(client_buf, sizeof(client_buf) - 1, "451 server error, rejected(rejected by pipe_p2c. IP=%s)(SW07)\r\n", inet_ntoa(csa->sin_addr)); + if ((ret_client = sock_write(socket_rw_client, client_buf, strlen(client_buf))) < 0){ + close(socket_rw_client); + socket_rw_client = -1; + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL29)\n", getpid()); + } + + syslog(LOG_MAIL|LOG_INFO, "[%d] 451 server error, rejected(rejected by pipe_p2c. IP=%s)(SL30)\n", getpid(), inet_ntoa(csa->sin_addr)); + close(socket_rw_client); + socket_rw_client = -1; + close(socket_rw_smtpdaemon); + socket_rw_smtpdaemon = -1; + smtp_status = SS_EXIT; } /* * execute filter @@ -733,7 +827,7 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa if (fork() == 0){ close(socket_rw_client); close(socket_rw_smtpdaemon); - execute_filter(filter, pipe_p2c, pipe_c2p, csa); + execute_filter(filter, pipe_p2c, pipe_c2p); exit(0); }else{ close(pipe_p2c[0]); @@ -752,7 +846,7 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa if ((ret_filter = sock_write(fd_w_filter, client_buf, strlen(client_buf))) < 0){ close(fd_w_filter); fd_w_filter = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write fd_w_filter error in SS_RECVCOM\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write fd_w_filter error(SL31)\n", getpid()); } } } @@ -765,7 +859,7 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa if ((ret_client = sock_write(socket_rw_client, client_buf, strlen(client_buf))) < 0){ close(socket_rw_client); socket_rw_client = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error in SS_RECVCOM(1)\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL32)\n", getpid()); } smtp_status = SS_RECVMSG; @@ -776,18 +870,18 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa if ((ret_smtpdaemon = sock_write(socket_rw_smtpdaemon, client_buf, strlen(client_buf))) < 0){ close(socket_rw_smtpdaemon); socket_rw_smtpdaemon = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_smtpdaemon error in SS_RECVCOM(1)\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_smtpdaemon error(SL33)\n", getpid()); } smtp_status = SS_RESQUIT; }else if (string_compare(client_buf, "EHLO ", strlen("EHLO ")) == 0){ bzero(client_buf, BUF_LEN); - strncpy(client_buf, "504 Command parameter not implemented.\r\n", sizeof(client_buf) - 1); + snprintf(client_buf, sizeof(client_buf) - 1, "504 Command parameter not implemented(rejected by EHLO error. IP=%s)(SW08)\r\n", inet_ntoa(csa->sin_addr)); if ((ret_client = sock_write(socket_rw_client, client_buf, strlen(client_buf))) < 0){ close(socket_rw_client); socket_rw_client = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error in SS_RECVCOM(2)\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL34)\n", getpid()); } smtp_status = SS_RECVCOM; @@ -797,35 +891,13 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa if ((ret_smtpdaemon = sock_write(socket_rw_smtpdaemon, client_buf, strlen(client_buf))) < 0){ close(socket_rw_smtpdaemon); socket_rw_smtpdaemon = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_smtpdaemon error in SS_RECVCOM(2)\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_smtpdaemon error(SL35)\n", getpid()); } smtp_status = SS_RESNORM; } } -#if 0 /* funny wait happen. why ? */ - if (FD_ISSET(fd_r_filter, &fdset) || read_filter_buf[0] != '\0'){ - bzero(filter_buf, BUF_LEN); - if ((ret_filter = sock_read(fd_r_filter, filter_buf, BUF_LEN - 1, read_filter_buf)) < 0){ - close(fd_r_filter); - fd_r_filter = -1; - } - - bzero(client_buf, BUF_LEN); - strncpy(client_buf, "450 This message was accepted partly, but it was rejected according to site policy(2)\r\n", sizeof(client_buf) - 1); - if ((ret_client = sock_write(socket_rw_client, client_buf, strlen(client_buf))) < 0){ - close(socket_rw_client); - socket_rw_client = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error in SS_RECVMSG\n", getpid()); - } - - syslog(LOG_MAIL|LOG_INFO, "[%d] 450 This message was accepted partly, but it was rejected according to site policy(2) filter return=(%-s)\n", getpid(), filter_buf); - - smtp_status = SS_EXIT; - } -#endif - }else if (smtp_status == SS_RESNORM){ if (FD_ISSET(socket_rw_smtpdaemon, &fdset) || read_smtpdaemon_buf[0] != '\0'){ @@ -837,7 +909,7 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa if ((ret_client = sock_write(socket_rw_client, smtpdaemon_buf, strlen(smtpdaemon_buf))) < 0){ close(socket_rw_client); socket_rw_client = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error in SS_RESNORM\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL36)\n", getpid()); } if (smtpdaemon_buf[3] == '-'){ @@ -858,7 +930,7 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa if ((ret_client = sock_write(socket_rw_client, smtpdaemon_buf, strlen(smtpdaemon_buf))) < 0){ close(socket_rw_client); socket_rw_client = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error in SS_RESQUIT\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL37)\n", getpid()); } smtp_status = SS_EXIT; @@ -876,7 +948,7 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa if ((ret_filter = sock_write(fd_w_filter, client_buf, strlen(client_buf))) < 0){ close(fd_w_filter); fd_w_filter = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write fd_w_filter error in SS_RECVMSG\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write fd_w_filter error(SL38)\n", getpid()); } } @@ -899,15 +971,19 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa } bzero(client_buf, BUF_LEN); - strncpy(client_buf, "450 This message was accepted partly, but it was rejected according to site policy(3)\r\n", sizeof(client_buf) - 1); + snprintf(client_buf, sizeof(client_buf) - 1, "450 This message was accepted partly, but it was rejected according to site policy(rejected by contents_filter. IP=%s)(SW09)\r\n", inet_ntoa(csa->sin_addr)); if ((ret_client = sock_write(socket_rw_client, client_buf, strlen(client_buf))) < 0){ close(socket_rw_client); socket_rw_client = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error in SS_RECVMSG\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL39)\n", getpid()); } - syslog(LOG_MAIL|LOG_INFO, "[%d] 450 This message was accepted partly, but it was rejected according to site policy(3) filter return=(%-s)\n", getpid(), filter_buf); + syslog(LOG_MAIL|LOG_INFO, "[%d] 450 This message was accepted partly, but it was rejected according to site policy(rejected by contents_filter. IP=%s). filter return=(%-s)(SL40)\n", getpid(), inet_ntoa(csa->sin_addr), filter_buf); + close(socket_rw_client); + socket_rw_client = -1; + close(socket_rw_smtpdaemon); + socket_rw_smtpdaemon = -1; smtp_status = SS_EXIT; } @@ -924,21 +1000,25 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa if ((ret_smtpdaemon = sock_write(socket_rw_smtpdaemon, filter_buf, strlen(filter_buf))) < 0){ close(socket_rw_smtpdaemon); socket_rw_smtpdaemon = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_smtpdaemon error in SS_CHKMSG\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_smtpdaemon error(SL41)\n", getpid()); } smtp_status = SS_RESDATA; }else{ bzero(client_buf, BUF_LEN); - strncpy(client_buf, "450 This message was accepted all, but it was rejected according to site policy(4)\r\n", sizeof(client_buf) - 1); + snprintf(client_buf, sizeof(client_buf) - 1, "450 This message was accepted all, but it was rejected according to site policy(rejected by contents_filter. IP=%s)(SW10)\r\n", inet_ntoa(csa->sin_addr)); if ((ret_client = sock_write(socket_rw_client, client_buf, strlen(client_buf))) < 0){ close(socket_rw_client); socket_rw_client = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error in SS_CHKMSG\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL42)\n", getpid()); } - syslog(LOG_MAIL|LOG_INFO, "[%d] 450 This message was accepted all, but it was rejected according to site policy(4) filter return=(%-s)\n", getpid(), filter_buf); + syslog(LOG_MAIL|LOG_INFO, "[%d] 450 This message was accepted all, but it was rejected according to site policy(rejected by contents_filter. IP=%s). filter return=(%-s)(SL43)\n", getpid(), inet_ntoa(csa->sin_addr), filter_buf); + close(socket_rw_client); + socket_rw_client = -1; + close(socket_rw_smtpdaemon); + socket_rw_smtpdaemon = -1; smtp_status = SS_EXIT; } } @@ -955,15 +1035,19 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa smtp_status = SS_OUTMSG; }else{ bzero(client_buf, BUF_LEN); - strncpy(client_buf, "451 server error, rejected(4)\r\n", sizeof(client_buf) - 1); + snprintf(client_buf, sizeof(client_buf) - 1, "451 server error, rejected(rejected by MTA error for DATA. IP=%s)(SW11)\r\n", inet_ntoa(csa->sin_addr)); if ((ret_client = sock_write(socket_rw_client, client_buf, strlen(client_buf))) < 0){ close(socket_rw_client); socket_rw_client = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error in SS_RESDATA\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL44)\n", getpid()); } - syslog(LOG_MAIL|LOG_INFO, "[%d] 451 server error, rejected(4)[%-s]\n", getpid(), smtpdaemon_buf); + syslog(LOG_MAIL|LOG_INFO, "[%d] 451 server error, rejected(rejected by MTA error for DATA. IP=%s)(SL45)\n", getpid(), inet_ntoa(csa->sin_addr)); + close(socket_rw_client); + socket_rw_client = -1; + close(socket_rw_smtpdaemon); + socket_rw_smtpdaemon = -1; smtp_status = SS_EXIT; } } @@ -979,7 +1063,7 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa if ((ret_smtpdaemon = sock_write(socket_rw_smtpdaemon, filter_buf, strlen(filter_buf))) < 0){ close(socket_rw_smtpdaemon); socket_rw_smtpdaemon = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_smtpdaemon error in SS_OUTMSG\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_smtpdaemon error(SL46)\n", getpid()); } if (string_compare(filter_buf, ".\r\n", strlen(".\r\n")) == 0 || @@ -1004,7 +1088,7 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa if ((ret_client = sock_write(socket_rw_client, smtpdaemon_buf, strlen(smtpdaemon_buf))) < 0){ close(socket_rw_client); socket_rw_client = -1; - syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error in SS_RESPERIOD\n", getpid()); + syslog(LOG_MAIL|LOG_INFO, "[%d] sock_write socket_rw_client error(SL47)\n", getpid()); } smtp_status = SS_RECVCOM; @@ -1014,14 +1098,17 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa close(socket_rw_client); socket_rw_client = -1; - close(socket_rw_smtpdaemon); socket_rw_smtpdaemon = -1; }else{ - syslog(LOG_MAIL|LOG_INFO, "[%d] communicate_smtpdaemon : smtp_status error(%d) : %-s\n", smtp_status, getpid(), strerror(errno)); /* never reached */ - return -1; + syslog(LOG_MAIL|LOG_INFO, "[%d] communicate_smtpdaemon : smtp_status error(%d) : %-s(SL48)\n", smtp_status, getpid(), strerror(errno)); /* never reached */ + close(socket_rw_client); + socket_rw_client = -1; + close(socket_rw_smtpdaemon); + socket_rw_smtpdaemon = -1; + smtp_status = SS_EXIT; } } @@ -1034,7 +1121,7 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa /* * child logging end */ - syslog(LOG_MAIL|LOG_INFO, "[%d] smtp_wrapper child end : SW_FROM_IP=%-s\n", getpid(), inet_ntoa(csa->sin_addr)); + syslog(LOG_MAIL|LOG_INFO, "[%d] smtp_wrapper child end : IP=%-s(SL49)\n", getpid(), inet_ntoa(csa->sin_addr)); return 0; } @@ -1042,33 +1129,27 @@ int communicate_smtpdaemon(hostname, port, socket_rw_client, ptr_tv, filter, csa /* * execute filter */ -int execute_filter(filter, pipe_p2c, pipe_c2p, csa) +int execute_filter(filter, pipe_p2c, pipe_c2p) char *filter; int *pipe_p2c; int *pipe_c2p; - struct sockaddr_in *csa; { int fd_null; - setenv("SW_FROM_IP", inet_ntoa(csa->sin_addr), 1); close(*(pipe_p2c + 1)); close(*(pipe_c2p + 0)); dup2(*(pipe_p2c + 0), 0); dup2(*(pipe_c2p + 1), 1); -#if 0 - dup2(*(pipe_c2p + 1), 2); -#else if ((fd_null = open("/dev/null", O_WRONLY)) < 0){ /* do nothing */; }else{ dup2(fd_null, 2); close(fd_null); } -#endif close(*(pipe_p2c + 0)); close(*(pipe_c2p + 1)); if (execl(filter, basename(filter), NULL) < 0){ - syslog(LOG_MAIL|LOG_INFO, "[%d] execute_filter : execl(%-s) : %-s\n", filter, getpid(), strerror(errno)); + syslog(LOG_MAIL|LOG_INFO, "[%d] execute_filter : execl error(%-s) : %-s(SL50)\n", filter, getpid(), strerror(errno)); return -1; } @@ -1186,7 +1267,7 @@ int string_compare(str1, str2, len) */ int show_help() { - fprintf(stderr, "Usage : smtp_wrapper [-mh hostname] [-mp port] [-q backlog] [-sh smtpserver_hostname] [-sp smtpserver_port] [-t timeout_sec] [-d delay_sec] [-f filter] [-cm child_max] [-F]\n"); + fprintf(stderr, "Usage : smtp_wrapper [-mh hostname] [-mp port] [-q backlog] [-sh smtpserver_hostname] [-sp smtpserver_port] [-t timeout_sec] [-d delay_sec] [-if ip_filter] [-f contents_filter] [-cm child_max] [-F]\n"); fprintf(stderr, " -mh hostname : my hostname [ANY]\n"); fprintf(stderr, " -mp port : my port [25]\n"); fprintf(stderr, " -q backlog : socket queue number [5]\n"); @@ -1194,7 +1275,8 @@ int show_help() fprintf(stderr, " -sp smtpserver_port : real smtp port [8025]\n"); fprintf(stderr, " -t timeout_sec : timeout second [no timeout]\n"); fprintf(stderr, " -d delay_sec : delay second for initial connection(greeting message) [0]\n"); - fprintf(stderr, " -f filter : filter program [/usr/local/smtp_wrapper/smtp_filter]\n"); + fprintf(stderr, " -if ip_filter : filter program for IP check [/usr/local/smtp_wrapper/smtp_ip_filter]\n"); + fprintf(stderr, " -f contents_filter : filter program for contents check [/usr/local/smtp_wrapper/smtp_contents_filter]\n"); fprintf(stderr, " -cm child_max : max number of connection to real smtp daemon [10]\n"); fprintf(stderr, " -i minimum_interval_sec : minimum interval second of connection from same ip address [0]\n"); fprintf(stderr, " -F : run in foreground\n");