tcpdumpでパケットキャプチャした結果を読みたいが、Wiresharkで調べるのではなく、なんらかのプログラムで読み込ませたい。pktsというJavaライブラリを使って読み込むことができたので、実施した内容をメモする。
キャプチャを行なったコマンド
TCPハンドシェイクの最初のSYNパケットとDNS応答から、通信先のドメインの一覧を取得したいので以下のようにフィルターを設定した
# 次の条件のいずれか満たすパケットを抽出する
# - SYNフラグが立っている、かつ、ACKフラグが立っていない
# - 送信元ポートが53(DNS)
sudo tcpdump -s 0 -i en0 -nn -w tcpdump.pcap \('(tcp[tcpflags] & tcp-syn)' != 0 and '(tcp[tcpflags] & tcp-ack) ==0'\) or src port 53
Javaで読み込む方法
pktsというJavaライブラリを使った。簡単な実装例の一部は以下の通り。
final Pcap pcap = Pcap.openStream("tcpdump.pcap");
pcap.loop(new PacketHandler() {
@Override
public boolean nextPacket(Packet packet) throws IOException {
if (packet.hasProtocol(Protocol.UDP)) {
System.out.println(packet.getPacket(Protocol.UDP).getPayload());
}
}
}
IPv4やUDPの場合、パケットを取得することでそのヘッダー部分の情報を取得できる。
if (packet.hasProtocol(Protocol.IPv4)) {
IPv4Packet ipv4Packet = (IPv4Packet) packet.getPacket(Protocol.IPv4);
String srcIP = ipv4Packet.getSourceIP();
String dstIP = ipv4Packet.getDestinationIP();
}
Payloadの内容を読み込みたい場合は、以下のような方法でバイト配列として取得できる。
UDPPacket udpPacket = (UDPPacket) packet.getPacket(Protocol.UDP);
Buffer buffer = udpPacket.getPayload();
byte[] bytes = buffer.getArray();
DNSのパケットとしてパースする部分は、DNSのパケットフォーマットを参考にして自前で書く必要がある。DNSの場合はドメイン部分や応答の数が可変なのでやや面倒。フォーマットを気にせずにとりあえず文字で読むことだけを目指して非常に雑に実装したものはこちら。