OP25Bとサブミッションポート(587番ポート)について

OP25B(Outbound Port 25 Blocking)はISPが実施している迷惑メール対策の1つです。迷惑メールはメール送信プロトコルであるSMTPが、送信者の認証をしていないことにより発生しています。メールサーバーは送られてきたメールをそのまま相手先に中継するので、迷惑メールでも相手に届きます。

メールサーバーがISP契約者以外のメールを中継することを第三者中継と呼んでいます。迷惑メール送信者は自身が契約してるISPのメールサーバーや第三者中継をするメールサーバー向けに、送信元を偽造して迷惑メールを大量に送ります。

迷惑メールが問題になったことでISPは特定の契約者からの大量のメールを制限する、および、メールサーバーの第三者中継を禁止する対策をとりました。

第三者中継を禁止するにはメールサーバーに次の設定をします。

  • 送信元ドメインが自身のドメインの場合は転送する。
  • 宛先ドメインが自身のドメイン場合はメールボックスに格納する。
  • 上記以外はすべて拒否する。

これにより第三者中継をするメールサーバーを踏み台にしての迷惑メールはできなくなりました。このため迷惑メール送信者は相手先のメールサーバーに直接メールを送るようになりました。

このやり方を防ぐためにとられた対策がOP25Bです。OP25Bでは外に出ていく25番ポートの通信をブロックします。ただし正規のメールサーバーが外部の25番ポートへ送信する通信は許可をします。ですので、メールを送るには正規のメールサーバーを利用するしかありません。正規のメールサーバーであればISPがメールの送信状況を監視できます。

ただ正規のISP契約者がフリーWiFiを使って他のISP経由でメールを送るときは問題になります。他のISPがOP25Bで25番ポートへの通信をブロックしてしまうからです。ですのでこれを解消するために受付用のポート(サブミッションポートと呼ぶ)が用意されています。通常は587番ポートを使います。サブミッションポートでは送信者の認証を行うようになっています。

実際にメーラーからメールを送信してみました。ISPはso-netです。so-netの送信用メールの設定です。

※mail.so-net.ne.jp のIPアドレスは 202.238.84.33 です。

まずはOP25Bを確認するため近所のカフェに出かけてフリーWiFiに繋ぎ、so-netのメールサーバー(25番ポート向け)にメールを送ってみました。そのときのWireSharkでのキャプチャです。

TCP Retransmission(再送)が発生していることからso-netのサーバーにはパケットが届いていないようです。

次にフリーWiFiからso-netのメールサーバー(587番ポート向け)にメールを送ってみました。問題なくメールの送信ができました。気になるところとしてメール終了時に Encrypted Alert が出ています。このときメールサーバーはFINパケットを送っていてメーラーはそれを受信、RSTパケットの返信で終わります。このネゴシエーションが正しいのかわからないのですが、メールは送れていました。

自宅からも試してみました。自宅からso-netのメールサーバー(587番ポート向け)にメールを送ってみました。問題なくメールの送信ができました。カフェから送ったときと同じく Encrypted Alert が出ていました。

また自宅からso-netのメールサーバー(25番ポート向け)にもメールを送ってみました。メーラーの設定についてはSMTPのEHLOコマンドの応答から設定値を推測しました。SMTPでは最初にHELOを送りますが機能拡張を使う場合はEHLOとなります。

EHLOを送った後のメールサーバーからの応答です。

587番ポートの場合です。

587番ポート向けには認証があり、STARTTLSを選べます。

25番ポートの場合です。

25番ポート向けには認証はありますが、STARTTLSのオプションはありません。

STARTTLSは暗号化を行わずにTCPセッションを確立して、クライアントがTLS通信に対応していればTLSに切り替えます。クライアントがTLS非対応であればそのままSMTPの通信を行います。

25番ポート向けは認証のみ行っているためメーラーの設定をSSLは使用しない、および、STARTTLSも使用しないとしてメールを送ってみると、メールの送信ができました。

ちなみにSMTPS(465番ポート)向けにもメールを送ってみましたが、こちらはポートが開いていないようでした。

Javaでパッケージにしたクラスのコンパイルと実行方法

任意のパッケージにあるクラスのコンパイルと実行方法についてです。環境はLinuxです。

ソースファイル(Main.java)


package sample01;

class Main {

	public static void main( String[] args ) {
	
		Number num = new Number();
		System.out.println( "Number is " + num.getNum() + "." );

	}
}

ソースファイル(Number.java)


package sample01;

public class Number {

	private int num;

	public Number() {
		num = 10;
	}

	public int getNum() {
		return num;
	}
}

MainクラスとNumberクラスはsample01というパッケージに属しています。

これらのソースを配置するのですが、ディレクトリ構成は以下のようにします。Javaフォルダは任意の場所に配置してください。

Javaフォルダにsrcフォルダとclassesフォルダを用意します。srcフォルダにsample01フォルダを作成し Main.java と Number.java を格納します。

コンパイルするためにコンパイル用のシェルを作成します。シェル名は compile.sh とします。compile.sh をsrcフォルダに格納します。

compile.shの内容


#!/bin/bash
javac -d ../classes -sourcepath ./ $1

compile.sh はカレントディレクトリでの実行とします。javacコマンドの説明をしておくと「-d . ./classes」はクラスファイルの出力先の指定、「-sourcepath ./」はソースファイルの検索場所となります。「$1」は compile.sh の引数です。

srcフォルダで compile.sh を実行します。引数は sample01/Main.java となります。

$ cd (任意の配置場所)/Java/src
$ ./compile.sh sample01/Main.java

compile.sh を実行すると /Java/classes/sample01 フォルダに Main.class と Number.class が生成されます。

Main.class を実行するにはclassesフォルダでjavaコマンドを実行します。引数は sample01.Main となります。

$ cd (任意の配置場所)/Java/classes
$ java sample01.Main

「Number is 10.」と表示されれば実行できています。