feat: improve scan-ip device identification
This commit is contained in:
+156
-8
@@ -11,6 +11,7 @@ usage() {
|
|||||||
|
|
||||||
扫描局域网主机:
|
扫描局域网主机:
|
||||||
无参数 ICMP 扫描本网段 .1-.254,并尽力显示名称、MAC 和厂商
|
无参数 ICMP 扫描本网段 .1-.254,并尽力显示名称、MAC 和厂商
|
||||||
|
--identify 额外通过 mDNS 识别设备(macOS,约增加 3 秒)
|
||||||
10.0.0 扫描指定网段前缀
|
10.0.0 扫描指定网段前缀
|
||||||
22 扫描本网段 TCP 22 端口
|
22 扫描本网段 TCP 22 端口
|
||||||
80,443,8080 扫描多个端口(任一开放即显示)
|
80,443,8080 扫描多个端口(任一开放即显示)
|
||||||
@@ -28,9 +29,19 @@ usage() {
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
dotfile_help_requested "${1:-}" && dotfile_show_help
|
|
||||||
|
|
||||||
TIMEOUT=1
|
TIMEOUT=1
|
||||||
|
IDENTIFY=0
|
||||||
|
ARGS=()
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--identify) IDENTIFY=1 ;;
|
||||||
|
*) ARGS+=("$arg") ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
set -- "${ARGS[@]}"
|
||||||
|
|
||||||
|
dotfile_help_requested "${1:-}" && dotfile_show_help
|
||||||
|
|
||||||
find_mac_prefix_file() {
|
find_mac_prefix_file() {
|
||||||
local nmap_bin prefix candidate
|
local nmap_bin prefix candidate
|
||||||
@@ -213,7 +224,6 @@ mac_vendor_for_mac() {
|
|||||||
first_octet=${mac%%:*}
|
first_octet=${mac%%:*}
|
||||||
# 本地管理位为 1 的 MAC 常见于 iOS/Android/Windows 的 Wi-Fi 随机地址,不能据此推断厂商。
|
# 本地管理位为 1 的 MAC 常见于 iOS/Android/Windows 的 Wi-Fi 随机地址,不能据此推断厂商。
|
||||||
if (( (16#$first_octet) & 2 )); then
|
if (( (16#$first_octet) & 2 )); then
|
||||||
printf '%s' '随机/本地管理 MAC'
|
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -233,6 +243,9 @@ format_host() {
|
|||||||
vendor=$(mac_vendor_for_mac "$mac")
|
vendor=$(mac_vendor_for_mac "$mac")
|
||||||
|
|
||||||
printf '%s' "$ip"
|
printf '%s' "$ip"
|
||||||
|
if [ "$ip" = "$LOCAL_IP" ]; then
|
||||||
|
printf ' (本机)'
|
||||||
|
fi
|
||||||
if [ -n "$hostname" ]; then
|
if [ -n "$hostname" ]; then
|
||||||
printf ' name=%s' "$hostname"
|
printf ' name=%s' "$hostname"
|
||||||
fi
|
fi
|
||||||
@@ -244,26 +257,158 @@ format_host() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RESULT_DIR=$(mktemp -d "${TMPDIR:-/tmp}/scan-ip.XXXXXX")
|
||||||
|
MDNS_RESULT_DIR="$RESULT_DIR/mdns"
|
||||||
|
mkdir -p "$MDNS_RESULT_DIR"
|
||||||
|
trap 'rm -rf "$RESULT_DIR"' EXIT
|
||||||
|
|
||||||
|
show_total() {
|
||||||
|
local i total=0
|
||||||
|
for i in $(seq 1 254); do
|
||||||
|
[ -f "$RESULT_DIR/$i" ] && total=$((total + 1))
|
||||||
|
done
|
||||||
|
echo "共发现 $total 个 IP"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_results() {
|
||||||
|
local i file line mdns labels
|
||||||
|
for i in $(seq 1 254); do
|
||||||
|
[ -f "$RESULT_DIR/$i" ] || continue
|
||||||
|
labels=()
|
||||||
|
for file in "$MDNS_RESULT_DIR/$i."*; do
|
||||||
|
[ -f "$file" ] && labels+=("${file##*.}")
|
||||||
|
done
|
||||||
|
if [ "${#labels[@]}" -gt 0 ]; then
|
||||||
|
mdns=$(IFS=,; printf '%s' "${labels[*]}")
|
||||||
|
line=$(cat "$RESULT_DIR/$i")
|
||||||
|
if [[ "$line" == *" 在线" ]]; then
|
||||||
|
printf '%s mdns=%s 在线\n' "${line% 在线}" "$mdns"
|
||||||
|
else
|
||||||
|
printf '%s mdns=%s\n' "$line" "$mdns"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
cat "$RESULT_DIR/$i"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
capture_dns_sd() {
|
||||||
|
local output="$1"
|
||||||
|
shift
|
||||||
|
local pid
|
||||||
|
|
||||||
|
# dns-sd 持续监听且重定向时会缓冲;script 提供伪终端并立即刷新输出。
|
||||||
|
script -qF "$output" dns-sd "$@" >/dev/null 2>&1 &
|
||||||
|
pid=$!
|
||||||
|
sleep 2
|
||||||
|
pkill -TERM -P "$pid" 2>/dev/null || true
|
||||||
|
kill "$pid" 2>/dev/null || true
|
||||||
|
wait "$pid" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_mdns_instance() {
|
||||||
|
local type="$1"
|
||||||
|
local label="$2"
|
||||||
|
local instance="$3"
|
||||||
|
local index="$4"
|
||||||
|
local output="$RESULT_DIR/mdns-resolve-$index"
|
||||||
|
local pid host ip suffix model
|
||||||
|
|
||||||
|
script -qF "$output" dns-sd -L "$instance" "$type" local. >/dev/null 2>&1 &
|
||||||
|
pid=$!
|
||||||
|
sleep 1
|
||||||
|
pkill -TERM -P "$pid" 2>/dev/null || true
|
||||||
|
kill "$pid" 2>/dev/null || true
|
||||||
|
wait "$pid" 2>/dev/null || true
|
||||||
|
|
||||||
|
host=$(tr -d '\r\b' <"$output" \
|
||||||
|
| awk '/can be reached at/ {for (i = 1; i <= NF; i++) if ($i == "at") {print $(i + 1); exit}}')
|
||||||
|
host=${host%:*}
|
||||||
|
host=${host%.}
|
||||||
|
[ -n "$host" ] || return
|
||||||
|
|
||||||
|
model=$(tr -d '\r\b' <"$output" \
|
||||||
|
| awk '{for (i = 1; i <= NF; i++) if ($i ~ /^model=/) {sub(/^model=/, "", $i); print $i; exit}}')
|
||||||
|
if [ -n "$model" ]; then
|
||||||
|
model=${model//\//_}
|
||||||
|
label="$label($model)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ip=$(dscacheutil -q host -a name "$host" 2>/dev/null \
|
||||||
|
| awk -F': ' '/^ip_address: / && $2 ~ /^[0-9.]+$/ {print $2; exit}')
|
||||||
|
case "$ip" in
|
||||||
|
"$PREFIX".*)
|
||||||
|
suffix=${ip##*.}
|
||||||
|
: >"$MDNS_RESULT_DIR/$suffix.$label"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
identify_mdns() {
|
||||||
|
local type label browse_file instance index=0
|
||||||
|
local types=(_airplay._tcp _device-info._tcp _workstation._tcp)
|
||||||
|
|
||||||
|
[ "$IDENTIFY" -eq 1 ] || return
|
||||||
|
if [ "$(uname -s)" != "Darwin" ] || ! command -v dns-sd &>/dev/null \
|
||||||
|
|| ! command -v script &>/dev/null; then
|
||||||
|
echo "提示: 当前系统缺少受支持的 mDNS 查询工具,跳过设备识别" >&2
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "正在通过 mDNS 识别设备..."
|
||||||
|
for type in "${types[@]}"; do
|
||||||
|
capture_dns_sd "$RESULT_DIR/mdns-${type//_tcp/}" -B "$type" local. &
|
||||||
|
done
|
||||||
|
wait
|
||||||
|
|
||||||
|
for type in "${types[@]}"; do
|
||||||
|
case "$type" in
|
||||||
|
_airplay._tcp)
|
||||||
|
label=airplay
|
||||||
|
;;
|
||||||
|
_device-info._tcp)
|
||||||
|
label=device-info
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
label=workstation
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
browse_file="$RESULT_DIR/mdns-${type//_tcp/}"
|
||||||
|
while IFS= read -r instance; do
|
||||||
|
[ -n "$instance" ] || continue
|
||||||
|
index=$((index + 1))
|
||||||
|
resolve_mdns_instance "$type" "$label" "$instance" "$index" &
|
||||||
|
done < <(tr -d '\r\b' <"$browse_file" \
|
||||||
|
| awk '$2 == "Add" {for (i = 7; i <= NF; i++) printf "%s%s", (i == 7 ? "" : " "), $i; print ""}')
|
||||||
|
done
|
||||||
|
wait
|
||||||
|
}
|
||||||
|
|
||||||
if [ ${#PORTS[@]} -eq 0 ]; then
|
if [ ${#PORTS[@]} -eq 0 ]; then
|
||||||
if [ "$(uname -s)" = "Darwin" ]; then
|
if [ "$(uname -s)" = "Darwin" ]; then
|
||||||
for i in $(seq 1 254); do
|
for i in $(seq 1 254); do
|
||||||
(
|
(
|
||||||
ip="$PREFIX.$i"
|
ip="$PREFIX.$i"
|
||||||
ping -c 1 -W 1000 -S "$LOCAL_IP" "$ip" >/dev/null 2>&1 \
|
if ping -c 1 -W 1000 -S "$LOCAL_IP" "$ip" >/dev/null 2>&1; then
|
||||||
&& echo "$(format_host "$ip") 在线"
|
printf '%s 在线\n' "$(format_host "$ip")" >"$RESULT_DIR/$i"
|
||||||
|
fi
|
||||||
) &
|
) &
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
for i in $(seq 1 254); do
|
for i in $(seq 1 254); do
|
||||||
(
|
(
|
||||||
ip="$PREFIX.$i"
|
ip="$PREFIX.$i"
|
||||||
ping -c 1 -W 1 -I "$LOCAL_IP" "$ip" >/dev/null 2>&1 \
|
if ping -c 1 -W 1 -I "$LOCAL_IP" "$ip" >/dev/null 2>&1; then
|
||||||
&& echo "$(format_host "$ip") 在线"
|
printf '%s 在线\n' "$(format_host "$ip")" >"$RESULT_DIR/$i"
|
||||||
|
fi
|
||||||
) &
|
) &
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
wait
|
wait
|
||||||
|
identify_mdns
|
||||||
|
show_results
|
||||||
echo "------------------------------------------------------"
|
echo "------------------------------------------------------"
|
||||||
|
show_total
|
||||||
echo "扫描完成"
|
echo "扫描完成"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
@@ -287,7 +432,7 @@ check_host() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [ ${#hit[@]} -gt 0 ]; then
|
if [ ${#hit[@]} -gt 0 ]; then
|
||||||
echo "$(format_host "$ip") 开放: ${hit[*]}"
|
printf '%s 开放: %s\n' "$(format_host "$ip")" "${hit[*]}" >"$RESULT_DIR/${ip##*.}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,5 +441,8 @@ for i in $(seq 1 254); do
|
|||||||
done
|
done
|
||||||
wait
|
wait
|
||||||
|
|
||||||
|
identify_mdns
|
||||||
|
show_results
|
||||||
echo "------------------------------------------------------"
|
echo "------------------------------------------------------"
|
||||||
|
show_total
|
||||||
echo "扫描完成"
|
echo "扫描完成"
|
||||||
|
|||||||
Reference in New Issue
Block a user