PHPでCRAM-MD5認証を行ってOutbound Port 25 Blocking(OP25B)を回避してみる

PHPでCRAM-MD5認証を行って、直接プロバイダのメールサーバにメールを送りつけてみる。
プロバイダがCRAM-MD5に対応しているのが前提。
多分この方法なら自分側にSendmail/Postfix/qmailなどのメールサーバが入ってなくても行ける。

<?php
// プロバイダ設定
$host = 'xxx.ne.jp';
$port = '587';
$id = 'xxx';
$pw = 'xxx';


// ソケット作成
$fp = @fsockopen($host,$port);

// ソケット作成失敗
if(!$fp){
	echo "Socket error\n";
	exit();
}

// コネクション確立失敗
$connect_line = fgets($fp);
if(strpos($connect_line,'220 ')!==0){
	echo "Connect error\n";
	exit();
}

// 認証方式送信
fwrite($fp,"AUTH CRAM-MD5\r\n");

// チャレンジコード取得
// チャレンジコード == タイムスタンプ . ホスト名
$cc_line = fgets($fp);
if(strpos($cc_line,'334 ')!==0){
	echo "ChallengeCode error\n";
	exit();
}
list(,$cc_base64) = explode(' ',$cc_line);
$cc = base64_decode($cc_base64);

// 認証文字列送信
$authstr = base64_encode($id.' '.hash_hmac('md5',$cc,$pw));
fwrite($fp,$authstr."\r\n");

// 認証結果
$auth_result = fgets($fp);
if(strpos($auth_result,'235 ')!==0){
	echo "Auth error\n";
	exit();
}

// メール送信
$maildata = <<< MAILDATA
MAIL FROM: xxx@xxx.jp
RCPT TO: xxx@xxx.jp
DATA
From: xxx@xxx.jp
To: xxx@xxx.jp
Subject: phpmail
X-Mailer: phpmailer

testtesttesttesttest
testtesttesttesttest
testtesttesttesttest
.
MAILDATA;
fwrite($fp,$maildata."\r\n");
echo fgets($fp);

 
認証文字列を送って、こんなのが返ってきたらハッシュ辺りが間違ってるような気がする。

535 5.7.0 Error: authentication failed: authentication failure

 
認証文字列を送って、こんなのが返ってきたら繋ぎ目辺りが間違ってるような気がする。
相手側がBase64でデコードした時にID部分とパスワードのハッシュ部分の2つに分けられない(=規格外)、って言ってるんだと思う。
よそ様のエントリーとかから察するに、分け目は半角スペース(0x20)だったりNULL(0x00)だったり、プロバイダによって違うのかもしれない。

535 5.7.0 Error: authentication failed: bad protocol / cancel

 
MXレコードの設定で、メインドメインGoogleAppsサブドメインを全て自ホスト、としてる。
外からなら、ちゃんと引けるしうまく届く。
でも、中から自ドメインに送ると、メインでもサブでも有無を言わさず自分で受け取ってしまう。
多分、DNSに問い合わせるまでもなく、main.cfかhosts辺りで勝手に解決してしまってるんだと思う。
Postfixの設定もいろんなパターン試したけど、多分自分で受け取りたいサブドメインをmain.cfに列挙すれば出来るのかも知れないけど、それはそれで面倒。
って事で、PHPを介して直接プロバイダのSMTPポートを叩こうかと。
ローカルからメインドメイン宛にメールを送った時に、このPHPを介してGoogleAppsに送る。
セキュリティの問題で、ヘッダーに自分と関係ないReceivedとかがあると届かなかった。
まぁローカルからの送信が前提だから問題ない。
 
/etc/postfix/virtual.regexp辺りの最後の行に、メインドメイン全てをこのPHPで受け取るように設定する。

<?php
// メール読み込み
$mail = file_get_contents('php://stdin');

// 初期化
$from = false;
$to = false;

// From
if(preg_match("/\nFrom:([^\n]+)/i",$mail,$m)){
	$from = trim($m[1]);
	if(preg_match("/<([^>]+)>/",$from,$m)){
		$from = trim($m[1]);
	}
}
// To
if(preg_match("/\nTo:([^\n]+)/i",$mail,$m)){
	$to = trim($m[1]);
	if(preg_match("/<([^>]+)>/",$to,$m)){
		$to = trim($m[1]);
	}
}
// ドットだけの行
$mail = preg_replace("/\n\.(\r|\n)/","\n .\\1",$mail);
// FromもToもあるかチェック
if(!$from && !$to){
	echo "Syntax error\n";
	exit();
}

〜〜〜 認証省略 〜〜〜

// メール送信
$maildata = <<< MAILDATA
MAIL FROM: {$from}
RCPT TO: {$to}
DATA
{$mail}
.
MAILDATA;
fwrite($fp,$maildata."\r\n");
echo fgets($fp);

 
これで見事、リレー出来ました∩(・ω・)∩

250 2.1.0 Ok

 

GoogleAppsの為に開けておかなければならないポート

普段、iptablesで日本国内からしか入ってこれないようにしてる。
GoogleAppsのサーバは国外にあるらしく、うっかり拒否してる事が多々。
Appsの為に開けておかなければならないポートは以下。
1台でHTTP/DNS/SMTPなど全てのサービスをまかなってるのが前提。
GoogleのIPリストみたいなのがあればいいんだけど、オフィシャルのドキュメントがなくて断念。
仕方なく全世界に公開する事に。
 
登録時

  • 80(HTTP)
  • 53(DNS)

運用時