ひきつづき『サイバーセキュリティプログラミング第2版』の読書メモです。
3.4 ICMP のパース より
P62 UDP データグラムをサブネット全体に送信する Python コードについてのメモ
udp_sender 関数を抜粋
import ipaddress
# スキャン対象のサブネット
SUBNET = '192.168.1.0/24'
# ICMP レスポンスのチェック用マジック文字列
MESSAGE = 'PYTHONRULES'
# マジック文字列を含んだ UDP ダイアグラムをサブネット全体に送信
def udp_sender ():
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sender:
for ip in ipaddress.ip_network(SUBNET).hosts():
sender.sendto(bytes(MESSAGE, 'utf8')), (str(ip), 65212))
この関数は ipaddress.ip_network(SUBNET).hosts() で得られるサブネット内の各IPアドレスに対して UDP ダイアグラムを送信します。
254 回 sendto 関数が呼ばれます。
ここでちょっとネットワークを勉強したことのある方なら 「ユニキャスト」ではなく「ブロードキャスト」(ディレクテッド・ブロードキャスト)であれば一回の送信で済むのではないか という疑問をいだくことでしょう。
なぜ著者はユニキャストで実装しているのか?
本ではその理由を確認できませんでした。(もしも私が見落としていて、ちゃんと書いてあったら謝ります)
おそらく、送信元ホスト(つまりこのコードを実行するホスト)と異なるネットワークセグメントをスキャンのターゲットとする場合には、途中のルータでディレクテッド・ブロードキャストのパケットをフィルタされる可能性があることを考慮したのかもしれません。
例えば Cisco ルータではディレクテッド・ブロードキャストの転送はデフォルトでは行われません。(専用のコマンドを投入すれば転送可能ですが)
その理由としてはディレクテッド・ブロードキャストの転送ができるルータは 「DoS 攻撃のリフレクタ」 として使われる恐れがあるためです。
まぁ、それでもディレクテッド・ブロードキャストが転送される環境を前提とした場合に、ブロードキャストで送信する udp_sender の実装も考えてみました。使わないでしょうけど IT系オタクなので一応技術的なところは押さえておきます。
# マジック文字列を含んだ UDP ダイアグラムをサブネット全体に送信(ブロードキャスト版)
def udp_sender_broadcast ():
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sender:
sender.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
broadcast_address = ipaddress.ip_network(SUBNET).broadcast_address
sender.sendto(bytes(MESSAGE, 'utf8')), (str(ip), 65212))
ポイントは以下の2点:
- setsockopt 関数でブロードキャストの設定を行う。
- 送信先アドレスはブロードキャストアドレス(ディレクテッド・ブロードキャストアドレス)を指定する。この例では ipaddress.ip_network(SUBNET).broadcast_address により '192.168.1.255' となる。
この他、コーディングの流儀ではありますが、ポート番号が関数に埋め込みなのは少し気持ち悪い。
いまどきは Final 使ってこんな感じで定数化するのかな。
from typing import Final
PORT_NUMBER: Final[int] = 65212
(省略)
sender.sendto(bytes(MESSAGE, 'utf8')), (str(ip), PORT_NUMBER))