「アクセス解析」や「掲示板の投稿ログ」など、アクセスしてきた回線つまりIPアドレスを保存するという処理があります。


単に環境変数「REMOTE_HOST」等を保存するだけではプロクシ経由の場合意味がありません。(多分)


掲示板が特定の人物に荒らされた場合には保存してあるログを参考にすることで、うまくいけばアクセス制限できることもあります。


インターネット経由でアクセスしてくる機器には必ずグローバルなIPアドレスが必要になります。
そのIPアドレスは固定のものもあれば、ネット接続時に動的に割り振られるもの、アクセスする度に変わるものなどいろいろあります。
ホスト名は人間が覚えやすいようにIPアドレスに対応づけしてるだけのものなのでホスト名がなくてもIPアドレスだけでアクセスはできます。


つまりホスト名とIPアドレスではIPアドレスのほうがより重要な情報という風になりそうですが、人間ですから認識しやすいホスト名の方に注目する場合が多いように思います。



ということでここからはリアルなIPアドレス(生IP)をできるだけ取得する方法を書いていきたいと思います。



■IPアドレスを取得する
<?php

function realaddr()
{
    $ip= array();
    if (isset($_SERVER['HTTP_SP_HOST']) && preg_match('/^\d+(?:\.\d+){3}$/D', $_SERVER['HTTP_SP_HOST']))
        $ip[]= $_SERVER['HTTP_SP_HOST'];
    if (isset($_SERVER['HTTP_VIA']) && preg_match('/.*\s(\d+(?:\.\d+){3})/', $_SERVER['HTTP_VIA'], $match))
        $ip[]= $match[1];
    if (isset($_SERVER['HTTP_CLIENT_IP']) && preg_match('/^\d+(?:\.\d+){3}/', $_SERVER['HTTP_CLIENT_IP'], $match))
        $ip[]= $match[0];
    if (isset($_SERVER['HTTP_CLIENT_IP']) && preg_match('/^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})/i', $_SERVER['HTTP_CLIENT_IP'], $match))
        $ip[]= implode('.', array(hexdec($match[1]), hexdec($match[2]), hexdec($match[3]), hexdec($match[4])));
    if (isset($_SERVER['HTTP_FORWARDED']) && preg_match('/.*\s(\d+(?:\.\d+){3})/', $_SERVER['HTTP_FORWARDED'], $match))
        $ip[]= $match[1];
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match('/^\d+(?:\.\d+){3}/', $_SERVER['HTTP_X_FORWARDED_FOR'], $match))
        $ip[]= $match[0];
    if (isset($_SERVER['HTTP_FROM']) && preg_match('/^\d+(?:\.\d+){3}$/D', $_SERVER['HTTP_FROM']))
        $ip[]= $_SERVER['HTTP_FROM'];


    $addr= '';
    foreach ($ip as $value)
        if (!preg_match('/^(?:10|172\.16|192\.168|127\.0|0\.|169\.254)\./', $value) and $addr=$value) break;


    return($addr ? $addr : $_SERVER['REMOTE_ADDR']);
}
?>


プロクシ経由特有の環境変数を見ていき、生IPの形跡があればそれを取得していきます。
できれば信頼度の高いものから順にセットしていき、条件に見合っているかどうかチェックするようにします。
プロクシの残す環境変数から取得できない場合にはREMOTE_ADDR値をIPアドレスとしています。


IPアドレスを取得したら次はこれをホスト名に変換(いわゆる逆引き)します。
PHPではgethostbyaddr()関数にIPアドレスをそのまま渡すだけでこれができます。



■逆引き関数
<?php

function realhost($addr="")
{
    if ($addr === "") return '';
    $host= gethostbyaddr($addr);
    if (empty($host)) $host= $_SERVER['REMOTE_HOST'];


    return($host ? $host : $addr);
}
?>


先に書いたrealaddr()を引数として渡せばホスト名を返してくれることになります。
echo realhost(realaddr());



こうやって取得した、IPアドレスも絶対ではありません。あくまでもできるだけ取得できるように頑張ってみたまでです。


では簡易的なプロクシ判定関数も書いておきます。



■プロクシ判定関数
<?php

function is_proxy()
{
    return(
    isset($_SERVER['HTTP_SP_HOST']) or
    isset($_SERVER['HTTP_VIA']) or
    isset($_SERVER['HTTP_CLIENT_IP']) or
    isset($_SERVER['HTTP_FORWARDED']) or
    isset($_SERVER['HTTP_X_FORWARDED_FOR']) or
    isset($_SERVER['HTTP_FROM']) or
    preg_match('/via|squid|gate|httpd|proxy|cache|gateway|www|anonymous|keeper/i', $_SERVER['HTTP_USER_AGENT']) or
    preg_match('/prox|squid|gate|cache|news|web|www|secure|cgi|ftp|pop|dummy|keep|mail|mx\d*\b|smtp|dns|ns\d*\b|^gw|^fw|firewall/i', realhost())
    );
}
?>


realhost()関数を内部で使用してホスト名を簡易チェックしています。
とまぁ、、合法的に取得するのはこんなところかと・・・



いやはやペットボトルが山のように溜まっていまして、これらを捨てるにはつぶして小さくして捨てるルールに家の近所ではなっているので今からつぶしてこようと思っているんですが、これがなかなか「バリバリ」という音がすごくて面倒なんですよね。



あまりにもうるさくて夜中にやったら近所に響く恐れがあるので今からやろうと思いながらもなかなかどうして・・



思ったのはペットボトルつぶすときに音の出ないのを開発してほしいということ。。


最近なにかと目にする「いろはす」だっけ?? あれって音しないのかな? まだ実物を見たことないけど

メールを送信する際にはSMTPというプロトコル(やり取り)を使います。
このSMTPで定められたやり取りをしないと、いくら気合いがあっても恐らく送ることはできないでしょう。。。


そこで一般によく使われるのがメールソフトいわゆるメーラー(Outlook ExpressやEudoraなど)と呼ばれるものです。
その他だとスクリプト内でsendmailというプログラムに渡してやり取りを代行してもらう方法もよく使われています。


つまりSMTPを知れば、これらメーラーやsendmailを使わないでスクリプトから直でメールサーバに接続して送信できるわけです。
SMTPの詳細はRFC821で定められています。


ざっくり言うとSMTPは、
メールサーバに接続して「今からメール内容を送信するよん!」と言えば、送っていいかどうかを3桁数字で答えてくれます。
「コマンド」を送って「ステータスコード(レスポンスコード)」で答えるといった感じになります。
このようなやり取りの応酬です。



■実際の手順

プロバイダのメールサーバ25番ポートに接続します。
⇒220が返されたら接続OK、これ以降は「コマンド」と「ステータスコード」でやり取り


HELO 自分のホスト名[CRLF]
⇒250が返されたら挨拶OK


MAIL FROM:<mailfrom@example.com>[CRLF]
⇒250が返されたらFROM指定OK


RCPT TO:<mailto@example.com>[CRLF]
⇒250または251が返されたらTO指定OK


DATA[CRLF]
⇒354が返されたらこれ以降「メールヘッダ」と「本文」送信が可能


From: hi! <mailfrom@example.com>[CRLF]
Subject: This is test mail![CRLF]
To: mailto@example.com[CRLF]
MIME-Version: 1.0[CRLF]
[CRLF]
ここから本文開始
    :
ここで本文終了


[CRLF].[CRLF]
⇒250が返されたらメール内容送信終了


QUIT[CRLF]
⇒221が返されたらやり取り終了



改行の[CRLF]は必ず文字コード「0x0D 0x0A」の2バイトになります。


DATAコマンドを送信後、「メールヘッダ」「改行のみの行」「メール本文」を送信します。
「改行+ドット+改行」を送ることでメール本文の終端を知らせます。



こうして見てみるとそれほど難しい処理ではありません。
ですが、日本語を使ったメール送信ではよく文字化けするトラブルが起こっています。
正しく処理すれば文字化けは起こらないはずですからここからは文字化けしない正しい日本語メール送信を書いていきます。



まず押さえておくことは、「メールで日本語を使える部分はどの部分か」ということです。
RFC1522によると「From」「To」「CC」「Subject」の4つのヘッダが使えるようです。もちろん「メール本文」は言うまでもありません。


日本語の部分はまずJISコードに変換する必要があります。
日本語で書く部分は普通はせいぜい「From」「Subject」「本文」ぐらいだと思いますのでこれらについて書いていきます。



まずは日本語を使っている部分はすべてJISコードに変換します。
JISコードとはどういう文字コードなのか以前書いておきましたのでコチラを参照していただければと思います。


加えて「From」「Subject」を書くことになるメールヘッダ領域には制御コードが使えませんのでJISコードを更にBASE64エンコード変換して「a-zA-Z0-9+/」の64アスキー文字で表現するようにしないといけません。



具体的な記述は以下のような感じになります。

From: =?ISO-2022-JP?B?GyRCQXckayRHJDckZxsoQg==?= <mailfrom@example.com>
Subject: =?ISO-2022-JP?B?GyRCJWEhPCVrRkkkYCRHJDckZxsoQg==?=


JISコードをBASE64エンコードした場合には
エンコード文字の両端を「=?ISO-2022-JP?B?」と「?=」でサンドイッチする必要があります。
「ISO-2022-JP」がJISコードであることを表し、「B」がBASE64エンコードが施されてるという意味になります。
BASE64エンコードでは変換後に4の倍数に満たなかった分だけ末尾を「=」で埋めることになっています。
また、76文字超える場合はこのサンドイッチを複数行に分けて書きます。
エンコードした後の文字を76文字ごとに分割すると文字化けするので必ずJISコードの時点でBASE64エンコードした場合に76文字超えないように予測して分割してからBASE64エンコードします。
この76文字内に分割がされていないと、メールサーバに送信したときに勝手に分割されて文字化けになってしまいます。



複数行に分けた記述例

Subject: =?ISO-2022-JP?B?GyRCJWEhPCVrRkkkYCRHJDckZxsoQg==?=[CRLF]
[TAB]=?ISO-2022-JP?B?GyRCJWEhPCVrRkkkYCRHJDckZxsoQg==?=[CRLF]
[TAB]=?ISO-2022-JP?B?GyRCJWEhPCVrRkkkYCRHJDckZxsoQg==?=[CRLF]


[TAB]は文字コード0x09の水平タブのことです。


ヘッダー領域に書いた日本語文字の文字コード明示はこれで充分ですが、
本文がJISコードであることを明示する必要があります。
したがいましてメールヘッダの記述には「Content-Type」と「Content-Transfer-Encoding」を付加して以下のように書くことができます。


From: =?ISO-2022-JP?B?GyRCQXckayRHJDckZxsoQg==?= <mailfrom@example.com>[CRLF]
Subject: =?ISO-2022-JP?B?GyRCJWEhPCVrRkkkYCRHJDckZxsoQg==?=[CRLF]
To: mailto@example.com[CRLF]
MIME-Version: 1.0[CRLF]
Content-Type: text/plain; charset=iso-2022-jp[CRLF]
Content-Transfer-Encoding: 7bit[CRLF]



以上のことに留意すると文字化けしない日本語メールを送信することができます。