任意のパケットを送信する。javaで。UDP編

たとえばFirewallのポリシーの設定確認のときに、任意のパケットを送信したいことがある。 UDP 1234とか、TCP 5678とか。

ICMPならpingを打てばよいし、HTTP、HTTPS、SSH、TELNETなどならブラウザやteratermで実際にアクセスしてみればいい。TCPならtelnetでポート指定すればよいが、UDPの特定のポートの通信を発生させるためにはツールを使っていた。ipsendwinとか、iperfとか。 ixiaのトラフィックジェネレータを使ったこともある。

最近もipsendwinを使ったのだが、もう古いソフトで、Vectorのダウンロード画面ではサポートOSに「Windows XP/Me/2000/NT/98/95」と書いてある。 そしてあるPCでは「winpcap.dllがない」と表示され、コピーするもまた違うdllがない、エントリポイントが見つからない、などどいうことで実行するのをあきらめた。

そして、このご時世、いろんなアプリやスクリプトがあるし、「UDP 1234のパケットを特定のIPアドレスに10個ほど送信する」なんてことは簡単にできるんじゃないのかと思った。

pythonとかjavascriptとかさらっと調べたが、結局javaでやった。

java。 ちょっと、今更java?みたいな感じがした。 javaで簡単なプログラムを書いたことはあり、 簡単に書けて「ネットワークプログラミングならjavaだな」と思った。

しかし、javaは当然インストールされているだろうと思って普段仕事でつかっているPCのコントロールパネルを見たがjavaがインストールされていない。 たしかコントロールパネルにjavaのアイコンがあったはず.... もう最近はjavaが使われなくなりつつあるのか?

というわけでjavaのインストールから始める。 「java ダウンロード」などで検索するとダウンロードページがすぐ出てくるが、 32ビット版をインストールするように導かれる。 今なら、Version 8 Update 241 64ビット版もあるようだがどういうことだろうと少し調べたら、 ブラウザが32ビット版がデフォルトなのでjavaも32ビット版を使え、ということだった。

https://java.com/ja/download/faq/java_win64bit.xml

普段はクロームを使っているが64ビット版である。 が、たしかクロームでjavaが使えなくなったんじゃなかったっけ...?

IEはたしかに32ビット版のようである。使っていないが。 javaのランタイムは32ビット版のインストールページが検索すると上位に表示されるが、 開発環境、jdkだと13.0.2の64ビット版が出てくる。

32ビット版のランタイムがインストールされているところに64ビット版のjdkをインストールすると、実行時にエラーになる。 ランタイムは入れず、入っていたらアンインストールし、64ビット版のjdkをインストールする。 インストールしたら、binフォルダへのpathを登録する。

C:\Program Files\Java\jdk-13.0.2\bin

pathを登録したらコマンドプロンプトを開いてバージョンを確認してみる。

C:\WINDOWS\system32>javac -version
javac 13.0.2

HelloWorld.java を作る。

public class HelloWorld{
   public static void main(String[] args){
     System.out.println("Hello World!!");
   }
}
javac HelloWorld.java

でコンパイル。 HelloWorld.class ができる。

 実行。

C:\WINDOWS\system32>java HelloWorld
Hello World!!

では、UDPの任意のパケットを送信するプログラムを書こう。

 Client.java

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
public class Client {
 public static void main(String[] args) throws IOException {
  String sendData = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
  byte[] data = sendData.getBytes("UTF-8");
  DatagramSocket sock = new DatagramSocket();
   int count = Integer.parseInt(args[2]);
  String Host = args[0];
  Integer DestPort = Integer.parseInt(args[1]);
   for(int i=1; i<=count; i++) {  
   DatagramPacket packet 
   = new DatagramPacket(data, data.length, new InetSocketAddress(Host,DestPort));
   sock.send(packet);
   System.out.println("Sent UDP data  "+Host+":"+DestPort);
    try{
    Thread.sleep(1000);
   }catch(InterruptedException e){}
   }  
   sock.close();//
 }
}

使い方
java Client 宛先 ポート番号 パケット数

たとえば

java Client 192.168.1.1 12345 5

192.168.1.1宛に、UDP の宛先ポート12345のパケットを5個送信する。
間隔は1秒

送るだけならこれで終わり。 もし指定したIPアドレスのホストが存在しないと、arp解決されずパケットが送信されないだろう。 指定したIPアドレスのホストが存在すればパケットが送信されるが、 テキトーなポート宛に送っているので、port unreachableが返ってくるだろう。 送ったパケットを受信しそれを確認したい場合は受信側で下記を実行する。

Server.java

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Arrays;
public class Server {
        public static void main(String[] args) throws IOException {
                Integer Port = Integer.parseInt(args[0]);
                while (true) {
                        DatagramSocket sock = new DatagramSocket(Port);
                        byte[] data = new byte[1024];
                        DatagramPacket packet = new DatagramPacket(data, data.length);
                        sock.receive(packet);
                        System.out.println("Received :"+new String(Arrays.copyOf(packet.getData(),packet.getLength()),"UTF-8"));
                        sock.close();
                }
        }
}

動作確認 Client.javaとServer.javaをコンパイルしたら、 コマンドプロンプトを二つ起動して、 Client.classとServer.classがあるフォルダに移動し、片側でサーバを起動するこのときポート番号を指定する。

C:\Users\sam\udp_data_send>java Server 12345

もう一方でクライアントを起動する。
C:\Users\sam\udp_data_send>java Client localhost 12345 10
Sent UDP data  localhost:12345
Sent UDP data  localhost:12345
Sent UDP data  localhost:12345
Sent UDP data  localhost:12345
Sent UDP data  localhost:12345
Sent UDP data  localhost:12345
Sent UDP data  localhost:12345
Sent UDP data  localhost:12345
Sent UDP data  localhost:12345
Sent UDP data  localhost:12345
C:\Users\sam\udp_data_send>java Server 12345
Received :0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
Received :0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
Received :0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
Received :0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
Received :0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
Received :0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
Received :0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
Received :0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
Received :0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
Received :0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
TCPはまた今度。