Last active
October 29, 2025 23:57
-
-
Save gunk1n/4e3f7574452cc1ccf0b0f724300d7f60 to your computer and use it in GitHub Desktop.
🔘 This script checks the availability of a range of IP addresses based on a specified pattern. It sends ICMP ping requests to each address in sequence and displays the results with color‑coded output.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Цвета | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| NC='\033[0m' | |
| # Порты для проверки (можно изменить) | |
| TCP_PORTS="22 80 443 53 21 25 110 143 993 995 3389" | |
| UDP_PORTS="53 67 68 123 161 500 4500" | |
| # Настройки многопоточности | |
| THREADS=10 | |
| PING_TIMEOUT=2 | |
| TCP_TIMEOUT=2 | |
| UDP_TIMEOUT=3 | |
| # Переменные для сохранения в файл | |
| OUTPUT_FILE="scan_results_$(date +%Y%m%d_%H%M%S).txt" | |
| SAVE_TO_FILE=true | |
| show_help() { | |
| local width=120 | |
| local inner_width=$((width - 4)) | |
| local indent=" " | |
| frame_line() { | |
| local text="$1" | |
| if [ -z "$text" ]; then | |
| printf "%${inner_width}s\n" "" | |
| else | |
| local padded="${indent}${text}" | |
| printf "%-${inner_width}s\n" "${padded:0:$inner_width}" | |
| fi | |
| } | |
| line=$(printf '%*s' "$width" | tr ' ' '-') | |
| printf '+%s+\n' "$line" | |
| frame_line "" | |
| frame_line "Usage: $0 <subnet> <range> [threads]" | |
| frame_line " $0 --subnets <subnet1> <subnet2> ... --range <range> [threads]" | |
| frame_line " $0 --file <filename> --range <range> [threads]" | |
| frame_line "" | |
| frame_line "Description:" | |
| frame_line " Fast parallel scanning of IP ranges with ICMP, TCP/UDP ports and PTR checks" | |
| frame_line "" | |
| frame_line "Arguments:" | |
| frame_line " Single subnet mode:" | |
| frame_line " <subnet> Base subnet in format NNN.NNN.NNN. (each NNN: 1–255, no leading zeros)" | |
| frame_line " <range> Number of IPs to scan (0–255)" | |
| frame_line " [threads] Number of parallel threads (default: $THREADS, max: 50)" | |
| frame_line "" | |
| frame_line " Multiple subnets mode:" | |
| frame_line " --subnets List of subnets to scan" | |
| frame_line " --file File containing list of subnets (one per line)" | |
| frame_line " --range Number of IPs to scan for each subnet (0–255)" | |
| frame_line " [threads] Number of parallel threads" | |
| frame_line "" | |
| frame_line "Options:" | |
| frame_line " -h, --help Display this help message and exit" | |
| frame_line "" | |
| frame_line "Examples:" | |
| frame_line " Single subnet:" | |
| frame_line " $0 192.168.1. 5" | |
| frame_line " $0 10.0.0. 10 20" | |
| frame_line "" | |
| frame_line " Multiple subnets:" | |
| frame_line " $0 --subnets 192.168.1. 10.0.0. 172.16.1. --range 10" | |
| frame_line " $0 --file subnets.txt --range 50 25" | |
| frame_line " $0 --help" | |
| frame_line "" | |
| frame_line "File format for --file:" | |
| frame_line " 192.168.1." | |
| frame_line " 10.0.0." | |
| frame_line " 172.16.1." | |
| frame_line "" | |
| frame_line "Performance features:" | |
| frame_line " - Parallel scanning with configurable threads" | |
| frame_line " - Bulk ICMP ping for initial host discovery" | |
| frame_line " - Sequential port scanning only for alive hosts" | |
| frame_line " - Optimized timeouts for each check type" | |
| frame_line "" | |
| frame_line "Output format:" | |
| frame_line " PING: {✅ is alive (value_ms), ❌ is not reachable}" | |
| frame_line " TCP: {✅ open, ❌ close} port0,port1,portX" | |
| frame_line " UDP: {✅ open, ❌ close} port0,port1,portX" | |
| frame_line " PTR: {✅ value_domain, ❌ the record was not found}" | |
| frame_line "" | |
| frame_line "File output:" | |
| frame_line " - Results are saved to: scan_results_TIMESTAMP.txt" | |
| frame_line " - Only alive hosts are saved to file" | |
| frame_line " - Full output shown in console" | |
| frame_line "" | |
| frame_line "Colors:" | |
| frame_line " ${GREEN}Green${NC} → Host is reachable / Port is open" | |
| frame_line " ${RED}Red${NC} → Host is unreachable / Port is closed" | |
| frame_line " ${YELLOW}Yellow${NC} → Warning / Timeout" | |
| frame_line " ${BLUE}Blue${NC} → Information / Progress" | |
| frame_line "" | |
| frame_line "Exit codes:" | |
| frame_line " 0 — Success" | |
| frame_line " 1 — Validation error" | |
| frame_line " 2 — Required tools missing" | |
| frame_line "" | |
| frame_line "" | |
| line=$(printf '%*s' "$width" | tr ' ' '-') | |
| printf '+%s+\n' "$line" | |
| } | |
| # Функция для сохранения информации о доступном хосте в файл | |
| save_to_file() { | |
| local ip="$1" | |
| local ping_time="$2" | |
| local tcp_ports="$3" | |
| local udp_ports="$4" | |
| local ptr_record="$5" | |
| if [ "$SAVE_TO_FILE" = true ]; then | |
| { | |
| echo "==========================================" | |
| echo "Host: $ip" | |
| echo "==========================================" | |
| echo "PING: ✅ is alive (${ping_time}ms)" | |
| echo "TCP: $tcp_ports" | |
| echo "UDP: $udp_ports" | |
| echo "PTR: $ptr_record" | |
| echo "" | |
| } >> "$OUTPUT_FILE" | |
| fi | |
| } | |
| # Функция проверки TCP порта (оптимизированная) | |
| check_tcp_port() { | |
| local host=$1 | |
| local port=$2 | |
| if timeout $TCP_TIMEOUT bash -c "echo >/dev/tcp/$host/$port" 2>/dev/null; then | |
| echo -n "✅" | |
| else | |
| echo -n "❌" | |
| fi | |
| } | |
| # Функция проверки UDP порта (оптимизированная) | |
| check_udp_port() { | |
| local host=$1 | |
| local port=$2 | |
| if command -v nmap &> /dev/null; then | |
| # Параллельный nmap сканер | |
| if timeout $UDP_TIMEOUT nmap -sU -p $port --host-timeout ${UDP_TIMEOUT}s $host 2>/dev/null | grep -q "$port/udp.*open"; then | |
| echo -n "✅" | |
| else | |
| echo -n "❌" | |
| fi | |
| else | |
| # Базовая UDP проверка | |
| if timeout $UDP_TIMEOUT bash -c "echo >/dev/udp/$host/$port" 2>/dev/null; then | |
| echo -n "✅" | |
| else | |
| echo -n "❌" | |
| fi | |
| fi | |
| } | |
| # Функция проверки PTR записи | |
| check_ptr_record() { | |
| local ip=$1 | |
| local ptr_result | |
| if command -v dig &> /dev/null; then | |
| ptr_result=$(dig +short +time=2 +tries=1 -x "$ip" 2>/dev/null | head -1) | |
| elif command -v nslookup &> /dev/null; then | |
| ptr_result=$(timeout 2 nslookup "$ip" 2>/dev/null | grep "name =" | awk '{print $4}' | sed 's/\.$//') | |
| else | |
| ptr_result="" | |
| fi | |
| if [ -n "$ptr_result" ]; then | |
| echo -e "${GREEN}✅ $ptr_result${NC}" | |
| else | |
| echo -e "${RED}❌ the record was not found${NC}" | |
| fi | |
| } | |
| # Функция для получения PTR записи без цветов (для файла) | |
| get_ptr_record_plain() { | |
| local ip=$1 | |
| local ptr_result | |
| if command -v dig &> /dev/null; then | |
| ptr_result=$(dig +short +time=2 +tries=1 -x "$ip" 2>/dev/null | head -1) | |
| elif command -v nslookup &> /dev/null; then | |
| ptr_result=$(timeout 2 nslookup "$ip" 2>/dev/null | grep "name =" | awk '{print $4}' | sed 's/\.$//') | |
| else | |
| ptr_result="" | |
| fi | |
| if [ -n "$ptr_result" ]; then | |
| echo "✅ $ptr_result" | |
| else | |
| echo "❌ the record was not found" | |
| fi | |
| } | |
| # Функция сканирования одного хоста | |
| scan_host() { | |
| local ip="$1" | |
| local ping_time="$2" | |
| local output="==========================================\n" | |
| output+="Scanning $ip...\n" | |
| output+="==========================================\n" | |
| # Переменные для сохранения в файл | |
| local tcp_results="" | |
| local udp_results="" | |
| local ptr_result_plain="" | |
| if [ -n "$ping_time" ] && [ "$ping_time" != "null" ]; then | |
| output+="PING: ${GREEN}✅ is alive (${ping_time}ms)${NC}\n" | |
| # TCP проверки | |
| output+="TCP: " | |
| tcp_results="" | |
| first_tcp=true | |
| for port in $TCP_PORTS; do | |
| if [ "$first_tcp" = true ]; then | |
| first_tcp=false | |
| else | |
| output+="," | |
| tcp_results+="," | |
| fi | |
| local tcp_result | |
| tcp_result=$(check_tcp_port "$ip" "$port") | |
| output+="${tcp_result}$port" | |
| tcp_results+="${tcp_result}$port" | |
| done | |
| output+="\n" | |
| # UDP проверки | |
| output+="UDP: " | |
| udp_results="" | |
| first_udp=true | |
| for port in $UDP_PORTS; do | |
| if [ "$first_udp" = true ]; then | |
| first_udp=false | |
| else | |
| output+="," | |
| udp_results+="," | |
| fi | |
| local udp_result | |
| udp_result=$(check_udp_port "$ip" "$port") | |
| output+="${udp_result}$port" | |
| udp_results+="${udp_result}$port" | |
| done | |
| output+="\n" | |
| # PTR запись | |
| output+="PTR: " | |
| local ptr_result | |
| ptr_result=$(check_ptr_record "$ip") | |
| output+="$ptr_result\n" | |
| # Получаем PTR запись без цветов для файла | |
| ptr_result_plain=$(get_ptr_record_plain "$ip") | |
| # Сохраняем в файл | |
| save_to_file "$ip" "$ping_time" "$tcp_results" "$udp_results" "$ptr_result_plain" | |
| else | |
| output+="PING: ${RED}❌ is not reachable${NC}\n" | |
| output+="TCP: ${YELLOW}⏭️ skipped (host unreachable)${NC}\n" | |
| output+="UDP: ${YELLOW}⏭️ skipped (host unreachable)${NC}\n" | |
| output+="PTR: ${YELLOW}⏭️ skipped (host unreachable)${NC}\n" | |
| fi | |
| output+="\n" | |
| echo -e "$output" | |
| } | |
| # Функция для параллельного выполнения | |
| run_parallel() { | |
| local function_name="$1" | |
| local max_jobs="$2" | |
| local -a items=("${@:3}") | |
| local -a pids=() | |
| local running_jobs=0 | |
| local index=0 | |
| while [ $index -lt ${#items[@]} ] || [ ${#pids[@]} -gt 0 ]; do | |
| # Запускаем новые задачи если есть свободные слоты | |
| while [ $running_jobs -lt $max_jobs ] && [ $index -lt ${#items[@]} ]; do | |
| $function_name "${items[$index]}" & | |
| pids+=($!) | |
| running_jobs=$((running_jobs + 1)) | |
| index=$((index + 1)) | |
| done | |
| # Проверяем завершенные процессы | |
| local new_pids=() | |
| for pid in "${pids[@]}"; do | |
| if kill -0 "$pid" 2>/dev/null; then | |
| new_pids+=($pid) | |
| else | |
| running_jobs=$((running_jobs - 1)) | |
| wait "$pid" 2>/dev/null | |
| fi | |
| done | |
| pids=("${new_pids[@]}") | |
| sleep 0.1 | |
| done | |
| } | |
| # Функция массового ping | |
| bulk_ping() { | |
| local subnet="$1" | |
| local limit="$2" | |
| local -n result_ref="$3" | |
| local count=0 | |
| local total=$limit | |
| echo -e "${BLUE}Pinging $total hosts in $subnet*...${NC}" >&2 | |
| # Параллельный ping | |
| for ((i=0; i<limit; i++)); do | |
| local ip="${subnet}${i}" | |
| { | |
| # Пробуем ping с таймаутом | |
| if ping -c 1 -W $PING_TIMEOUT "$ip" &>/dev/null; then | |
| # Если пинг успешен, получаем время | |
| local ping_result | |
| ping_result=$(ping -c 1 -W $PING_TIMEOUT "$ip" 2>/dev/null | grep "time=" | head -1) | |
| if [ -n "$ping_result" ]; then | |
| local ping_time | |
| ping_time=$(echo "$ping_result" | grep -o "time=[0-9.]*" | cut -d= -f2) | |
| result_ref["$ip"]="$ping_time" | |
| echo -e "${GREEN}✓ Alive: $ip (${ping_time}ms)${NC}" >&2 | |
| else | |
| result_ref["$ip"]="1" | |
| echo -e "${GREEN}✓ Alive: $ip (1ms)${NC}" >&2 | |
| fi | |
| fi | |
| } & | |
| count=$((count + 1)) | |
| # Ограничиваем количество параллельных процессов | |
| if [[ $(jobs -r -p | wc -l) -ge $THREADS ]]; then | |
| wait -n | |
| fi | |
| done | |
| # Ждем завершения всех процессов | |
| wait | |
| # Альтернативный метод с fping (если нужен) | |
| if command -v fping &> /dev/null && [ ${#result_ref[@]} -eq 0 ]; then | |
| echo -e "${YELLOW}Trying fping method...${NC}" >&2 | |
| local ping_list=() | |
| for ((i=0; i<limit; i++)); do | |
| ping_list+=("${subnet}${i}") | |
| done | |
| # Используем fping в простом режиме | |
| for ip in "${ping_list[@]}"; do | |
| if fping -c 1 -t 1000 "$ip" &>/dev/null; then | |
| result_ref["$ip"]="1" | |
| echo -e "${GREEN}✓ Alive (fping): $ip${NC}" >&2 | |
| fi | |
| done | |
| fi | |
| } | |
| # Вспомогательная функция для параллельного сканирования | |
| scan_single_host() { | |
| local task="$1" | |
| local ip="${task%:*}" | |
| local ping_time="${task#*:}" | |
| scan_host "$ip" "$ping_time" | |
| } | |
| # Функция для чтения подсетей из файла | |
| read_subnets_from_file() { | |
| local filename="$1" | |
| local -n subnets_ref="$2" | |
| if [ ! -f "$filename" ]; then | |
| echo "Error: File '$filename' not found." | |
| exit 1 | |
| fi | |
| while IFS= read -r line || [ -n "$line" ]; do | |
| # Пропускаем пустые строки и комментарии | |
| line=$(echo "$line" | sed 's/#.*//') | |
| if [ -n "$line" ]; then | |
| # Проверяем формат подсети | |
| if [[ "$line" =~ ^([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.$ ]]; then | |
| subnets_ref+=("$line") | |
| else | |
| echo "Warning: Invalid subnet format '$line' in file, skipping." | |
| fi | |
| fi | |
| done < "$filename" | |
| } | |
| # Функция валидации подсети | |
| validate_subnet() { | |
| local subnet="$1" | |
| if ! [[ "$subnet" =~ ^([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.$ ]]; then | |
| echo "Error: Invalid subnet '$subnet'. Must be in format NNN.NNN.NNN. where each NNN is 1–255 (no leading zeros)." | |
| return 1 | |
| fi | |
| return 0 | |
| } | |
| # Основная функция сканирования | |
| main_scan() { | |
| local subnets=("$@") | |
| local total_hosts=0 | |
| # Подсчитываем общее количество хостов | |
| for subnet in "${subnets[@]}"; do | |
| total_hosts=$((total_hosts + range)) | |
| done | |
| echo -e "\n${BLUE}Starting parallel scan of ${#subnets[@]} subnets, $total_hosts total hosts using $THREADS threads...${NC}" | |
| echo -e "${BLUE}Subnets: ${subnets[*]}${NC}" | |
| echo -e "${BLUE}Range per subnet: 0-$((range-1))${NC}" | |
| echo -e "${BLUE}Ports - TCP: $TCP_PORTS${NC}" | |
| echo -e "${BLUE}Ports - UDP: $UDP_PORTS${NC}" | |
| # Инициализация файла результатов | |
| if [ "$SAVE_TO_FILE" = true ]; then | |
| echo -e "${BLUE}Results will be saved to: $OUTPUT_FILE${NC}" | |
| { | |
| echo "Network Scan Results" | |
| echo "====================" | |
| echo "Date: $(date)" | |
| echo "Subnets: ${subnets[*]}" | |
| echo "Range per subnet: 0-$((range-1))" | |
| echo "Total subnets: ${#subnets[@]}" | |
| echo "Total hosts scanned: $total_hosts" | |
| echo "" | |
| echo "ALIVE HOSTS:" | |
| echo "============" | |
| echo "" | |
| } > "$OUTPUT_FILE" | |
| fi | |
| echo "" | |
| start_time=$(date +%s) | |
| local total_alive_hosts=0 | |
| # Сканируем каждую подсеть | |
| for subnet in "${subnets[@]}"; do | |
| echo -e "${BLUE}=========================================${NC}" | |
| echo -e "${BLUE}Scanning subnet: $subnet${NC}" | |
| echo -e "${BLUE}=========================================${NC}" | |
| # Этап 1: Массовое обнаружение хостов | |
| echo -e "${BLUE}Stage 1: Host discovery...${NC}" | |
| declare -A alive_hosts | |
| bulk_ping "$subnet" "$range" alive_hosts | |
| echo -e "${GREEN}Found ${#alive_hosts[@]} alive hosts in $subnet*${NC}" | |
| total_alive_hosts=$((total_alive_hosts + ${#alive_hosts[@]})) | |
| # Этап 2: Параллельное сканирование доступных хостов | |
| if [ ${#alive_hosts[@]} -gt 0 ]; then | |
| echo -e "${BLUE}Stage 2: Parallel port scanning...${NC}" | |
| # Создаем массив для задач сканирования | |
| scan_tasks=() | |
| for ((i=0; i<range; i++)); do | |
| ip="${subnet}${i}" | |
| if [ -n "${alive_hosts[$ip]}" ]; then | |
| scan_tasks+=("$ip:${alive_hosts[$ip]}") | |
| else | |
| scan_tasks+=("$ip:null") | |
| fi | |
| done | |
| # Запускаем параллельное сканирование | |
| run_parallel scan_single_host "$THREADS" "${scan_tasks[@]}" | |
| else | |
| echo -e "${YELLOW}No alive hosts found in $subnet*, skipping port scanning.${NC}" | |
| fi | |
| echo "" | |
| done | |
| end_time=$(date +%s) | |
| duration=$((end_time - start_time)) | |
| # Добавляем итоги в файл | |
| if [ "$SAVE_TO_FILE" = true ]; then | |
| { | |
| echo "" | |
| echo "SCAN SUMMARY:" | |
| echo "=============" | |
| echo "Scan completed: $(date)" | |
| echo "Duration: ${duration} seconds" | |
| echo "Total subnets: ${#subnets[@]}" | |
| echo "Total hosts scanned: $total_hosts" | |
| echo "Total alive hosts found: $total_alive_hosts" | |
| } >> "$OUTPUT_FILE" | |
| echo -e "${GREEN}Results saved to: $OUTPUT_FILE${NC}" | |
| fi | |
| echo -e "${GREEN}Scan completed in ${duration} seconds${NC}" | |
| echo -e "${GREEN}Scanned ${#subnets[@]} subnets, $total_hosts hosts, found $total_alive_hosts alive hosts${NC}" | |
| } | |
| # Парсинг аргументов | |
| parse_arguments() { | |
| local subnets=() | |
| local range_set=false | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| -h|--help) | |
| show_help | |
| exit 0 | |
| ;; | |
| --subnets) | |
| shift | |
| while [[ $# -gt 0 ]] && [[ $1 != --* ]]; do | |
| if validate_subnet "$1"; then | |
| subnets+=("$1") | |
| else | |
| exit 1 | |
| fi | |
| shift | |
| done | |
| ;; | |
| --file) | |
| if [ $# -lt 2 ]; then | |
| echo "Error: --file requires a filename argument." | |
| exit 1 | |
| fi | |
| shift | |
| read_subnets_from_file "$1" subnets | |
| shift | |
| ;; | |
| --range) | |
| if [ $# -lt 2 ]; then | |
| echo "Error: --range requires a numeric argument." | |
| exit 1 | |
| fi | |
| shift | |
| if [[ "$1" =~ ^[0-9]+$ ]] && [ "$1" -ge 0 ] && [ "$1" -le 255 ]; then | |
| range="$1" | |
| range_set=true | |
| else | |
| echo "Error: <range> must be between 0 and 255 (inclusive)." | |
| exit 1 | |
| fi | |
| shift | |
| ;; | |
| *) | |
| # Одиночный режим (backward compatibility) | |
| if [ ${#subnets[@]} -eq 0 ] && ! $range_set; then | |
| if validate_subnet "$1"; then | |
| subnets+=("$1") | |
| else | |
| exit 1 | |
| fi | |
| shift | |
| if [[ $# -gt 0 ]] && [[ "$1" =~ ^[0-9]+$ ]]; then | |
| if [ "$1" -ge 0 ] && [ "$1" -le 255 ]; then | |
| range="$1" | |
| range_set=true | |
| else | |
| echo "Error: <range> must be between 0 and 255 (inclusive)." | |
| exit 1 | |
| fi | |
| shift | |
| fi | |
| else | |
| # Последний аргумент - threads | |
| if [[ "$1" =~ ^[0-9]+$ ]] && [ "$1" -ge 1 ] && [ "$1" -le 50 ]; then | |
| THREADS="$1" | |
| else | |
| echo "Error: <threads> must be between 1 and 50." | |
| exit 1 | |
| fi | |
| shift | |
| fi | |
| ;; | |
| esac | |
| done | |
| # Проверка обязательных параметров | |
| if [ ${#subnets[@]} -eq 0 ]; then | |
| echo "Error: No subnets specified." | |
| echo "Use '$0 --help' for usage information." | |
| exit 1 | |
| fi | |
| if ! $range_set; then | |
| echo "Error: Range not specified." | |
| echo "Use --range to specify the number of IPs to scan per subnet." | |
| exit 1 | |
| fi | |
| # Запускаем сканирование | |
| main_scan "${subnets[@]}" | |
| } | |
| # 1. Проверка утилит | |
| echo -e "${BLUE}Checking required utilities...${NC}" | |
| if ! command -v ping &> /dev/null; then | |
| echo "Error: 'ping' command not found. Please install it." | |
| exit 2 | |
| fi | |
| if command -v fping &> /dev/null; then | |
| echo -e "${GREEN}✓ fping available (fallback method)${NC}" | |
| else | |
| echo -e "${YELLOW}⚠ fping not found, using standard ping${NC}" | |
| fi | |
| if ! command -v nmap &> /dev/null; then | |
| echo -e "${YELLOW}⚠ 'nmap' not found. UDP port checking will be limited.${NC}" | |
| fi | |
| if ! command -v dig &> /dev/null && ! command -v nslookup &> /dev/null; then | |
| echo -e "${YELLOW}⚠ Neither 'dig' nor 'nslookup' found. PTR records cannot be checked.${NC}" | |
| fi | |
| # 2. Парсинг аргументов и запуск | |
| parse_arguments "$@" | |
| exit 0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Цвета | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| PURPLE='\033[0;35m' | |
| CYAN='\033[0;36m' | |
| NC='\033[0m' | |
| # Порты для проверки (можно изменить) | |
| TCP_PORTS="22 80 443 53 21 25 110 143 993 995 3389" | |
| UDP_PORTS="53 67 68 123 161 500 4500" | |
| # Настройки многопоточности | |
| THREADS=10 | |
| PING_TIMEOUT=2 | |
| TCP_TIMEOUT=2 | |
| UDP_TIMEOUT=3 | |
| # Переменные для сохранения в файл | |
| OUTPUT_FILE="scan_results_$(date +%Y%m%d_%H%M%S).txt" | |
| SAVE_TO_FILE=true | |
| # Режим вывода: detailed (подробный) или progress (прогресс-бар) | |
| OUTPUT_MODE="detailed" | |
| show_help() { | |
| local width=120 | |
| local inner_width=$((width - 4)) | |
| local indent=" " | |
| frame_line() { | |
| local text="$1" | |
| if [ -z "$text" ]; then | |
| printf "%${inner_width}s\n" "" | |
| else | |
| local padded="${indent}${text}" | |
| printf "%-${inner_width}s\n" "${padded:0:$inner_width}" | |
| fi | |
| } | |
| line=$(printf '%*s' "$width" | tr ' ' '-') | |
| printf '+%s+\n' "$line" | |
| frame_line "" | |
| frame_line "Usage: $0 <subnet> <range> [threads]" | |
| frame_line " $0 --subnets <subnet1> <subnet2> ... --range <range> [threads]" | |
| frame_line " $0 --file <filename> --range <range> [threads]" | |
| frame_line "" | |
| frame_line "Description:" | |
| frame_line " Fast parallel scanning of IP ranges with ICMP, TCP/UDP ports and PTR checks" | |
| frame_line "" | |
| frame_line "Arguments:" | |
| frame_line " Single subnet mode:" | |
| frame_line " <subnet> Base subnet in format NNN.NNN.NNN. (each NNN: 1–255, no leading zeros)" | |
| frame_line " <range> Number of IPs to scan (0–255)" | |
| frame_line " [threads] Number of parallel threads (default: $THREADS, max: 50)" | |
| frame_line "" | |
| frame_line " Multiple subnets mode:" | |
| frame_line " --subnets List of subnets to scan" | |
| frame_line " --file File containing list of subnets (one per line)" | |
| frame_line " --range Number of IPs to scan for each subnet (0–255)" | |
| frame_line " [threads] Number of parallel threads" | |
| frame_line "" | |
| frame_line "Output modes:" | |
| frame_line " --mode detailed Detailed output (default)" | |
| frame_line " --mode progress Progress bar with compact results" | |
| frame_line "" | |
| frame_line "Options:" | |
| frame_line " -h, --help Display this help message and exit" | |
| frame_line "" | |
| frame_line "Examples:" | |
| frame_line " Single subnet:" | |
| frame_line " $0 192.168.1. 5" | |
| frame_line " $0 10.0.0. 10 20" | |
| frame_line "" | |
| frame_line " Multiple subnets:" | |
| frame_line " $0 --subnets 192.168.1. 10.0.0. 172.16.1. --range 10" | |
| frame_line " $0 --file subnets.txt --range 50 25 --mode progress" | |
| frame_line " $0 --help" | |
| frame_line "" | |
| frame_line "File format for --file:" | |
| frame_line " 192.168.1." | |
| frame_line " 10.0.0." | |
| frame_line " 172.16.1." | |
| frame_line "" | |
| frame_line "Performance features:" | |
| frame_line " - Parallel scanning with configurable threads" | |
| frame_line " - Bulk ICMP ping for initial host discovery" | |
| frame_line " - Sequential port scanning only for alive hosts" | |
| frame_line " - Optimized timeouts for each check type" | |
| frame_line "" | |
| frame_line "Output format:" | |
| frame_line " PING: {✅ is alive (value_ms), ❌ is not reachable}" | |
| frame_line " TCP: {✅ open, ❌ close} port0,port1,portX" | |
| frame_line " UDP: {✅ open, ❌ close} port0,port1,portX" | |
| frame_line " PTR: {✅ value_domain, ❌ the record was not found}" | |
| frame_line "" | |
| frame_line "File output:" | |
| frame_line " - Results are saved to: scan_results_TIMESTAMP.txt" | |
| frame_line " - Only alive hosts are saved to file" | |
| frame_line " - Full output shown in console" | |
| frame_line "" | |
| frame_line "Colors:" | |
| frame_line " ${GREEN}Green${NC} → Host is reachable / Port is open" | |
| frame_line " ${RED}Red${NC} → Host is unreachable / Port is closed" | |
| frame_line " ${YELLOW}Yellow${NC} → Warning / Timeout" | |
| frame_line " ${BLUE}Blue${NC} → Information / Progress" | |
| frame_line "" | |
| frame_line "Exit codes:" | |
| frame_line " 0 — Success" | |
| frame_line " 1 — Validation error" | |
| frame_line " 2 — Required tools missing" | |
| frame_line "" | |
| frame_line "" | |
| line=$(printf '%*s' "$width" | tr ' ' '-') | |
| printf '+%s+\n' "$line" | |
| } | |
| # Функция для отображения прогресс-бара | |
| show_progress() { | |
| local current="$1" | |
| local total="$2" | |
| local width=50 | |
| local percentage=$((current * 100 / total)) | |
| local completed=$((current * width / total)) | |
| local remaining=$((width - completed)) | |
| printf "\r${CYAN}Progress: [${NC}" | |
| printf "%*s" $completed | tr ' ' '█' | |
| printf "%*s" $remaining | tr ' ' '░' | |
| printf "${CYAN}] ${percentage}%% (%d/%d)${NC}" $current $total | |
| } | |
| # Функция для компактного вывода хоста | |
| show_compact_host() { | |
| local ip="$1" | |
| local ping_time="$2" | |
| local tcp_ports="$3" | |
| local udp_ports="$4" | |
| local ptr_record="$5" | |
| if [ -n "$ping_time" ] && [ "$ping_time" != "null" ]; then | |
| # Считаем открытые TCP порты | |
| local open_tcp=$(echo "$tcp_ports" | grep -o '✅' | wc -l) | |
| local total_tcp=$(echo "$tcp_ports" | grep -o '❌\|✅' | wc -l) | |
| # Считаем открытые UDP порты | |
| local open_udp=$(echo "$udp_ports" | grep -o '✅' | wc -l) | |
| local total_udp=$(echo "$udp_ports" | grep -o '❌\|✅' | wc -l) | |
| # Извлекаем PTR запись | |
| local ptr_domain="" | |
| if [[ "$ptr_record" =~ ✅\ (.*) ]]; then | |
| ptr_domain="${BASH_REMATCH[1]}" | |
| fi | |
| printf "${GREEN}✓ ${ip}${NC} (${ping_time}ms) | " | |
| printf "TCP: ${open_tcp}/${total_tcp} | " | |
| printf "UDP: ${open_udp}/${total_udp}" | |
| if [ -n "$ptr_domain" ]; then | |
| printf " | PTR: ${ptr_domain}" | |
| fi | |
| printf "\n" | |
| fi | |
| } | |
| # Функция для сохранения информации о доступном хосте в файл | |
| save_to_file() { | |
| local ip="$1" | |
| local ping_time="$2" | |
| local tcp_ports="$3" | |
| local udp_ports="$4" | |
| local ptr_record="$5" | |
| if [ "$SAVE_TO_FILE" = true ]; then | |
| { | |
| echo "==========================================" | |
| echo "Host: $ip" | |
| echo "==========================================" | |
| echo "PING: ✅ is alive (${ping_time}ms)" | |
| echo "TCP: $tcp_ports" | |
| echo "UDP: $udp_ports" | |
| echo "PTR: $ptr_record" | |
| echo "" | |
| } >> "$OUTPUT_FILE" | |
| fi | |
| } | |
| # Функция проверки TCP порта (оптимизированная) | |
| check_tcp_port() { | |
| local host=$1 | |
| local port=$2 | |
| if timeout $TCP_TIMEOUT bash -c "echo >/dev/tcp/$host/$port" 2>/dev/null; then | |
| echo -n "✅" | |
| else | |
| echo -n "❌" | |
| fi | |
| } | |
| # Функция проверки UDP порта (оптимизированная) | |
| check_udp_port() { | |
| local host=$1 | |
| local port=$2 | |
| if command -v nmap &> /dev/null; then | |
| # Параллельный nmap сканер | |
| if timeout $UDP_TIMEOUT nmap -sU -p $port --host-timeout ${UDP_TIMEOUT}s $host 2>/dev/null | grep -q "$port/udp.*open"; then | |
| echo -n "✅" | |
| else | |
| echo -n "❌" | |
| fi | |
| else | |
| # Базовая UDP проверка | |
| if timeout $UDP_TIMEOUT bash -c "echo >/dev/udp/$host/$port" 2>/dev/null; then | |
| echo -n "✅" | |
| else | |
| echo -n "❌" | |
| fi | |
| fi | |
| } | |
| # Функция проверки PTR записи | |
| check_ptr_record() { | |
| local ip=$1 | |
| local ptr_result | |
| if command -v dig &> /dev/null; then | |
| ptr_result=$(dig +short +time=2 +tries=1 -x "$ip" 2>/dev/null | head -1) | |
| elif command -v nslookup &> /dev/null; then | |
| ptr_result=$(timeout 2 nslookup "$ip" 2>/dev/null | grep "name =" | awk '{print $4}' | sed 's/\.$//') | |
| else | |
| ptr_result="" | |
| fi | |
| if [ -n "$ptr_result" ]; then | |
| echo -e "${GREEN}✅ $ptr_result${NC}" | |
| else | |
| echo -e "${RED}❌ the record was not found${NC}" | |
| fi | |
| } | |
| # Функция для получения PTR записи без цветов (для файла) | |
| get_ptr_record_plain() { | |
| local ip=$1 | |
| local ptr_result | |
| if command -v dig &> /dev/null; then | |
| ptr_result=$(dig +short +time=2 +tries=1 -x "$ip" 2>/dev/null | head -1) | |
| elif command -v nslookup &> /dev/null; then | |
| ptr_result=$(timeout 2 nslookup "$ip" 2>/dev/null | grep "name =" | awk '{print $4}' | sed 's/\.$//') | |
| else | |
| ptr_result="" | |
| fi | |
| if [ -n "$ptr_result" ]; then | |
| echo "✅ $ptr_result" | |
| else | |
| echo "❌ the record was not found" | |
| fi | |
| } | |
| # Функция сканирования одного хоста | |
| scan_host() { | |
| local ip="$1" | |
| local ping_time="$2" | |
| # Переменные для сохранения в файл | |
| local tcp_results="" | |
| local udp_results="" | |
| local ptr_result_plain="" | |
| if [ -n "$ping_time" ] && [ "$ping_time" != "null" ]; then | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| local output="==========================================\n" | |
| output+="Scanning $ip...\n" | |
| output+="==========================================\n" | |
| output+="PING: ${GREEN}✅ is alive (${ping_time}ms)${NC}\n" | |
| fi | |
| # TCP проверки | |
| tcp_results="" | |
| first_tcp=true | |
| for port in $TCP_PORTS; do | |
| if [ "$first_tcp" = true ]; then | |
| first_tcp=false | |
| else | |
| tcp_results+="," | |
| fi | |
| local tcp_result | |
| tcp_result=$(check_tcp_port "$ip" "$port") | |
| tcp_results+="${tcp_result}$port" | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| if [ "$first_tcp" = false ]; then | |
| output+="," | |
| fi | |
| output+="${tcp_result}$port" | |
| fi | |
| done | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| output+="\n" | |
| # UDP проверки | |
| output+="UDP: " | |
| fi | |
| udp_results="" | |
| first_udp=true | |
| for port in $UDP_PORTS; do | |
| if [ "$first_udp" = true ]; then | |
| first_udp=false | |
| else | |
| udp_results+="," | |
| fi | |
| local udp_result | |
| udp_result=$(check_udp_port "$ip" "$port") | |
| udp_results+="${udp_result}$port" | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| if [ "$first_udp" = false ]; then | |
| output+="," | |
| fi | |
| output+="${udp_result}$port" | |
| fi | |
| done | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| output+="\n" | |
| # PTR запись | |
| output+="PTR: " | |
| local ptr_result | |
| ptr_result=$(check_ptr_record "$ip") | |
| output+="$ptr_result\n" | |
| else | |
| # Компактный режим - просто получаем PTR | |
| ptr_result_plain=$(get_ptr_record_plain "$ip") | |
| fi | |
| # Получаем PTR запись без цветов для файла | |
| if [ -z "$ptr_result_plain" ]; then | |
| ptr_result_plain=$(get_ptr_record_plain "$ip") | |
| fi | |
| # Сохраняем в файл | |
| save_to_file "$ip" "$ping_time" "$tcp_results" "$udp_results" "$ptr_result_plain" | |
| # Вывод в зависимости от режима | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| output+="\n" | |
| echo -e "$output" | |
| else | |
| show_compact_host "$ip" "$ping_time" "$tcp_results" "$udp_results" "$ptr_result_plain" | |
| fi | |
| else | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| local output="==========================================\n" | |
| output+="Scanning $ip...\n" | |
| output+="==========================================\n" | |
| output+="PING: ${RED}❌ is not reachable${NC}\n" | |
| output+="TCP: ${YELLOW}⏭️ skipped (host unreachable)${NC}\n" | |
| output+="UDP: ${YELLOW}⏭️ skipped (host unreachable)${NC}\n" | |
| output+="PTR: ${YELLOW}⏭️ skipped (host unreachable)${NC}\n" | |
| output+="\n" | |
| echo -e "$output" | |
| fi | |
| # В компактном режиме недоступные хосты не показываем | |
| fi | |
| } | |
| # Функция для параллельного выполнения | |
| run_parallel() { | |
| local function_name="$1" | |
| local max_jobs="$2" | |
| local -a items=("${@:3}") | |
| local -a pids=() | |
| local running_jobs=0 | |
| local index=0 | |
| local completed=0 | |
| local total=${#items[@]} | |
| # Инициализация прогресс-бара | |
| if [ "$OUTPUT_MODE" = "progress" ]; then | |
| echo -e "\n${CYAN}Scanning hosts...${NC}" | |
| show_progress 0 $total | |
| fi | |
| while [ $index -lt ${#items[@]} ] || [ ${#pids[@]} -gt 0 ]; do | |
| # Запускаем новые задачи если есть свободные слоты | |
| while [ $running_jobs -lt $max_jobs ] && [ $index -lt ${#items[@]} ]; do | |
| $function_name "${items[$index]}" & | |
| pids+=($!) | |
| running_jobs=$((running_jobs + 1)) | |
| index=$((index + 1)) | |
| done | |
| # Проверяем завершенные процессы | |
| local new_pids=() | |
| for pid in "${pids[@]}"; do | |
| if kill -0 "$pid" 2>/dev/null; then | |
| new_pids+=($pid) | |
| else | |
| running_jobs=$((running_jobs - 1)) | |
| wait "$pid" 2>/dev/null | |
| completed=$((completed + 1)) | |
| # Обновляем прогресс-бар | |
| if [ "$OUTPUT_MODE" = "progress" ]; then | |
| show_progress $completed $total | |
| fi | |
| fi | |
| done | |
| pids=("${new_pids[@]}") | |
| sleep 0.1 | |
| done | |
| # Завершаем прогресс-бар | |
| if [ "$OUTPUT_MODE" = "progress" ]; then | |
| show_progress $total $total | |
| echo -e "\n" | |
| fi | |
| } | |
| # Функция массового ping | |
| bulk_ping() { | |
| local subnet="$1" | |
| local limit="$2" | |
| local -n result_ref="$3" | |
| local count=0 | |
| local total=$limit | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| echo -e "${BLUE}Pinging $total hosts in $subnet*...${NC}" >&2 | |
| else | |
| echo -e "${CYAN}Discovering hosts in $subnet*...${NC}" >&2 | |
| fi | |
| # Параллельный ping | |
| for ((i=0; i<limit; i++)); do | |
| local ip="${subnet}${i}" | |
| { | |
| # Пробуем ping с таймаутом | |
| if ping -c 1 -W $PING_TIMEOUT "$ip" &>/dev/null; then | |
| # Если пинг успешен, получаем время | |
| local ping_result | |
| ping_result=$(ping -c 1 -W $PING_TIMEOUT "$ip" 2>/dev/null | grep "time=" | head -1) | |
| if [ -n "$ping_result" ]; then | |
| local ping_time | |
| ping_time=$(echo "$ping_result" | grep -o "time=[0-9.]*" | cut -d= -f2) | |
| result_ref["$ip"]="$ping_time" | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| echo -e "${GREEN}✓ Alive: $ip (${ping_time}ms)${NC}" >&2 | |
| fi | |
| else | |
| result_ref["$ip"]="1" | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| echo -e "${GREEN}✓ Alive: $ip (1ms)${NC}" >&2 | |
| fi | |
| fi | |
| fi | |
| } & | |
| count=$((count + 1)) | |
| # Ограничиваем количество параллельных процессов | |
| if [[ $(jobs -r -p | wc -l) -ge $THREADS ]]; then | |
| wait -n | |
| fi | |
| done | |
| # Ждем завершения всех процессов | |
| wait | |
| # Альтернативный метод с fping (если нужен) | |
| if command -v fping &> /dev/null && [ ${#result_ref[@]} -eq 0 ]; then | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| echo -e "${YELLOW}Trying fping method...${NC}" >&2 | |
| fi | |
| local ping_list=() | |
| for ((i=0; i<limit; i++)); do | |
| ping_list+=("${subnet}${i}") | |
| done | |
| # Используем fping в простом режиме | |
| for ip in "${ping_list[@]}"; do | |
| if fping -c 1 -t 1000 "$ip" &>/dev/null; then | |
| result_ref["$ip"]="1" | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| echo -e "${GREEN}✓ Alive (fping): $ip${NC}" >&2 | |
| fi | |
| fi | |
| done | |
| fi | |
| } | |
| # Вспомогательная функция для параллельного сканирования | |
| scan_single_host() { | |
| local task="$1" | |
| local ip="${task%:*}" | |
| local ping_time="${task#*:}" | |
| scan_host "$ip" "$ping_time" | |
| } | |
| # Функция для чтения подсетей из файла | |
| read_subnets_from_file() { | |
| local filename="$1" | |
| local -n subnets_ref="$2" | |
| if [ ! -f "$filename" ]; then | |
| echo "Error: File '$filename' not found." | |
| exit 1 | |
| fi | |
| while IFS= read -r line || [ -n "$line" ]; do | |
| # Пропускаем пустые строки и комментарии | |
| line=$(echo "$line" | sed 's/#.*//') | |
| if [ -n "$line" ]; then | |
| # Проверяем формат подсети | |
| if [[ "$line" =~ ^([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.$ ]]; then | |
| subnets_ref+=("$line") | |
| else | |
| echo "Warning: Invalid subnet format '$line' in file, skipping." | |
| fi | |
| fi | |
| done < "$filename" | |
| } | |
| # Функция валидации подсети | |
| validate_subnet() { | |
| local subnet="$1" | |
| if ! [[ "$subnet" =~ ^([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.$ ]]; then | |
| echo "Error: Invalid subnet '$subnet'. Must be in format NNN.NNN.NNN. where each NNN is 1–255 (no leading zeros)." | |
| return 1 | |
| fi | |
| return 0 | |
| } | |
| # Основная функция сканирования | |
| main_scan() { | |
| local subnets=("$@") | |
| local total_hosts=0 | |
| # Подсчитываем общее количество хостов | |
| for subnet in "${subnets[@]}"; do | |
| total_hosts=$((total_hosts + range)) | |
| done | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| echo -e "\n${BLUE}Starting parallel scan of ${#subnets[@]} subnets, $total_hosts total hosts using $THREADS threads...${NC}" | |
| echo -e "${BLUE}Subnets: ${subnets[*]}${NC}" | |
| echo -e "${BLUE}Range per subnet: 0-$((range-1))${NC}" | |
| echo -e "${BLUE}Ports - TCP: $TCP_PORTS${NC}" | |
| echo -e "${BLUE}Ports - UDP: $UDP_PORTS${NC}" | |
| else | |
| echo -e "\n${CYAN}Scanning ${#subnets[@]} subnets, $total_hosts hosts, $THREADS threads, mode: $OUTPUT_MODE${NC}" | |
| fi | |
| # Инициализация файла результатов | |
| if [ "$SAVE_TO_FILE" = true ]; then | |
| echo -e "${BLUE}Results will be saved to: $OUTPUT_FILE${NC}" | |
| { | |
| echo "Network Scan Results" | |
| echo "====================" | |
| echo "Date: $(date)" | |
| echo "Subnets: ${subnets[*]}" | |
| echo "Range per subnet: 0-$((range-1))" | |
| echo "Total subnets: ${#subnets[@]}" | |
| echo "Total hosts scanned: $total_hosts" | |
| echo "Output mode: $OUTPUT_MODE" | |
| echo "" | |
| echo "ALIVE HOSTS:" | |
| echo "============" | |
| echo "" | |
| } > "$OUTPUT_FILE" | |
| fi | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| echo "" | |
| fi | |
| start_time=$(date +%s) | |
| local total_alive_hosts=0 | |
| # Сканируем каждую подсеть | |
| for subnet in "${subnets[@]}"; do | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| echo -e "${BLUE}=========================================${NC}" | |
| echo -e "${BLUE}Scanning subnet: $subnet${NC}" | |
| echo -e "${BLUE}=========================================${NC}" | |
| else | |
| echo -e "${CYAN}Subnet: $subnet*${NC}" | |
| fi | |
| # Этап 1: Массовое обнаружение хостов | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| echo -e "${BLUE}Stage 1: Host discovery...${NC}" | |
| fi | |
| declare -A alive_hosts | |
| bulk_ping "$subnet" "$range" alive_hosts | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| echo -e "${GREEN}Found ${#alive_hosts[@]} alive hosts in $subnet*${NC}" | |
| fi | |
| total_alive_hosts=$((total_alive_hosts + ${#alive_hosts[@]})) | |
| # Этап 2: Параллельное сканирование доступных хостов | |
| if [ ${#alive_hosts[@]} -gt 0 ]; then | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| echo -e "${BLUE}Stage 2: Parallel port scanning...${NC}" | |
| fi | |
| # Создаем массив для задач сканирования | |
| scan_tasks=() | |
| for ((i=0; i<range; i++)); do | |
| ip="${subnet}${i}" | |
| if [ -n "${alive_hosts[$ip]}" ]; then | |
| scan_tasks+=("$ip:${alive_hosts[$ip]}") | |
| else | |
| scan_tasks+=("$ip:null") | |
| fi | |
| done | |
| # Запускаем параллельное сканирование | |
| run_parallel scan_single_host "$THREADS" "${scan_tasks[@]}" | |
| else | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| echo -e "${YELLOW}No alive hosts found in $subnet*, skipping port scanning.${NC}" | |
| fi | |
| fi | |
| if [ "$OUTPUT_MODE" = "detailed" ]; then | |
| echo "" | |
| fi | |
| done | |
| end_time=$(date +%s) | |
| duration=$((end_time - start_time)) | |
| # Добавляем итоги в файл | |
| if [ "$SAVE_TO_FILE" = true ]; then | |
| { | |
| echo "" | |
| echo "SCAN SUMMARY:" | |
| echo "=============" | |
| echo "Scan completed: $(date)" | |
| echo "Duration: ${duration} seconds" | |
| echo "Total subnets: ${#subnets[@]}" | |
| echo "Total hosts scanned: $total_hosts" | |
| echo "Total alive hosts found: $total_alive_hosts" | |
| } >> "$OUTPUT_FILE" | |
| echo -e "${GREEN}Results saved to: $OUTPUT_FILE${NC}" | |
| fi | |
| echo -e "${GREEN}Scan completed in ${duration} seconds${NC}" | |
| echo -e "${GREEN}Scanned ${#subnets[@]} subnets, $total_hosts hosts, found $total_alive_hosts alive hosts${NC}" | |
| } | |
| # Парсинг аргументов | |
| parse_arguments() { | |
| local subnets=() | |
| local range_set=false | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| -h|--help) | |
| show_help | |
| exit 0 | |
| ;; | |
| --subnets) | |
| shift | |
| while [[ $# -gt 0 ]] && [[ $1 != --* ]]; do | |
| if validate_subnet "$1"; then | |
| subnets+=("$1") | |
| else | |
| exit 1 | |
| fi | |
| shift | |
| done | |
| ;; | |
| --file) | |
| if [ $# -lt 2 ]; then | |
| echo "Error: --file requires a filename argument." | |
| exit 1 | |
| fi | |
| shift | |
| read_subnets_from_file "$1" subnets | |
| shift | |
| ;; | |
| --range) | |
| if [ $# -lt 2 ]; then | |
| echo "Error: --range requires a numeric argument." | |
| exit 1 | |
| fi | |
| shift | |
| if [[ "$1" =~ ^[0-9]+$ ]] && [ "$1" -ge 0 ] && [ "$1" -le 255 ]; then | |
| range="$1" | |
| range_set=true | |
| else | |
| echo "Error: <range> must be between 0 and 255 (inclusive)." | |
| exit 1 | |
| fi | |
| shift | |
| ;; | |
| --mode) | |
| if [ $# -lt 2 ]; then | |
| echo "Error: --mode requires an argument (detailed or progress)." | |
| exit 1 | |
| fi | |
| shift | |
| case $1 in | |
| detailed|progress) | |
| OUTPUT_MODE="$1" | |
| ;; | |
| *) | |
| echo "Error: --mode must be 'detailed' or 'progress'." | |
| exit 1 | |
| ;; | |
| esac | |
| shift | |
| ;; | |
| *) | |
| # Одиночный режим (backward compatibility) | |
| if [ ${#subnets[@]} -eq 0 ] && ! $range_set; then | |
| if validate_subnet "$1"; then | |
| subnets+=("$1") | |
| else | |
| exit 1 | |
| fi | |
| shift | |
| if [[ $# -gt 0 ]] && [[ "$1" =~ ^[0-9]+$ ]]; then | |
| if [ "$1" -ge 0 ] && [ "$1" -le 255 ]; then | |
| range="$1" | |
| range_set=true | |
| else | |
| echo "Error: <range> must be between 0 and 255 (inclusive)." | |
| exit 1 | |
| fi | |
| shift | |
| fi | |
| else | |
| # Последний аргумент - threads | |
| if [[ "$1" =~ ^[0-9]+$ ]] && [ "$1" -ge 1 ] && [ "$1" -le 50 ]; then | |
| THREADS="$1" | |
| else | |
| echo "Error: <threads> must be between 1 and 50." | |
| exit 1 | |
| fi | |
| shift | |
| fi | |
| ;; | |
| esac | |
| done | |
| # Проверка обязательных параметров | |
| if [ ${#subnets[@]} -eq 0 ]; then | |
| echo "Error: No subnets specified." | |
| echo "Use '$0 --help' for usage information." | |
| exit 1 | |
| fi | |
| if ! $range_set; then | |
| echo "Error: Range not specified." | |
| echo "Use --range to specify the number of IPs to scan per subnet." | |
| exit 1 | |
| fi | |
| # Запускаем сканирование | |
| main_scan "${subnets[@]}" | |
| } | |
| # 1. Проверка утилит | |
| echo -e "${BLUE}Checking required utilities...${NC}" | |
| if ! command -v ping &> /dev/null; then | |
| echo "Error: 'ping' command not found. Please install it." | |
| exit 2 | |
| fi | |
| if command -v fping &> /dev/null; then | |
| echo -e "${GREEN}✓ fping available (fallback method)${NC}" | |
| else | |
| echo -e "${YELLOW}⚠ fping not found, using standard ping${NC}" | |
| fi | |
| if ! command -v nmap &> /dev/null; then | |
| echo -e "${YELLOW}⚠ 'nmap' not found. UDP port checking will be limited.${NC}" | |
| fi | |
| if ! command -v dig &> /dev/null && ! command -v nslookup &> /dev/null; then | |
| echo -e "${YELLOW}⚠ Neither 'dig' nor 'nslookup' found. PTR records cannot be checked.${NC}" | |
| fi | |
| # 2. Парсинг аргументов и запуск | |
| parse_arguments "$@" | |
| exit 0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Цвета | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| NC='\033[0m' | |
| # Порты для проверки (можно изменить) | |
| TCP_PORTS="22 80 443 53 21 25 110 143 993 995 3389" | |
| UDP_PORTS="53 67 68 123 161 500 4500" | |
| # Настройки многопоточности | |
| THREADS=10 | |
| PING_TIMEOUT=2 | |
| TCP_TIMEOUT=2 | |
| UDP_TIMEOUT=3 | |
| # Переменные для сохранения в файл | |
| OUTPUT_FILE="scan_results_$(date +%Y%m%d_%H%M%S).txt" | |
| SAVE_TO_FILE=true | |
| show_help() { | |
| local width=120 | |
| local inner_width=$((width - 4)) | |
| local indent=" " | |
| frame_line() { | |
| local text="$1" | |
| if [ -z "$text" ]; then | |
| printf "%${inner_width}s\n" "" | |
| else | |
| local padded="${indent}${text}" | |
| printf "%-${inner_width}s\n" "${padded:0:$inner_width}" | |
| fi | |
| } | |
| line=$(printf '%*s' "$width" | tr ' ' '-') | |
| printf '+%s+\n' "$line" | |
| frame_line "" | |
| frame_line "Usage: $0 <subnet> <range> [threads]" | |
| frame_line "" | |
| frame_line "Description:" | |
| frame_line " Fast parallel scanning of IP range with ICMP, TCP/UDP ports and PTR checks" | |
| frame_line "" | |
| frame_line "Arguments:" | |
| frame_line " <subnet> Base subnet in format NNN.NNN.NNN. (each NNN: 1–255, no leading zeros)" | |
| frame_line " <range> Number of IPs to scan (0–255)" | |
| frame_line " [threads] Number of parallel threads (default: $THREADS, max: 50)" | |
| frame_line "" | |
| frame_line "Options:" | |
| frame_line " -h, --help Display this help message and exit" | |
| frame_line "" | |
| frame_line "Examples:" | |
| frame_line " $0 192.168.1. 5" | |
| frame_line " $0 10.0.0. 10 20" | |
| frame_line " $0 --help" | |
| frame_line "" | |
| frame_line "Performance features:" | |
| frame_line " - Parallel scanning with configurable threads" | |
| frame_line " - Bulk ICMP ping for initial host discovery" | |
| frame_line " - Sequential port scanning only for alive hosts" | |
| frame_line " - Optimized timeouts for each check type" | |
| frame_line "" | |
| frame_line "Output format:" | |
| frame_line " PING: {✅ is alive (value_ms), ❌ is not reachable}" | |
| frame_line " TCP: {✅ open, ❌ close} port0,port1,portX" | |
| frame_line " UDP: {✅ open, ❌ close} port0,port1,portX" | |
| frame_line " PTR: {✅ value_domain, ❌ the record was not found}" | |
| frame_line "" | |
| frame_line "File output:" | |
| frame_line " - Results are saved to: scan_results_TIMESTAMP.txt" | |
| frame_line " - Only alive hosts are saved to file" | |
| frame_line " - Full output shown in console" | |
| frame_line "" | |
| frame_line "Colors:" | |
| frame_line " ${GREEN}Green${NC} → Host is reachable / Port is open" | |
| frame_line " ${RED}Red${NC} → Host is unreachable / Port is closed" | |
| frame_line " ${YELLOW}Yellow${NC} → Warning / Timeout" | |
| frame_line " ${BLUE}Blue${NC} → Information / Progress" | |
| frame_line "" | |
| frame_line "Exit codes:" | |
| frame_line " 0 — Success" | |
| frame_line " 1 — Validation error" | |
| frame_line " 2 — Required tools missing" | |
| frame_line "" | |
| frame_line "" | |
| line=$(printf '%*s' "$width" | tr ' ' '-') | |
| printf '+%s+\n' "$line" | |
| } | |
| # Функция для сохранения информации о доступном хосте в файл | |
| save_to_file() { | |
| local ip="$1" | |
| local ping_time="$2" | |
| local tcp_ports="$3" | |
| local udp_ports="$4" | |
| local ptr_record="$5" | |
| if [ "$SAVE_TO_FILE" = true ]; then | |
| { | |
| echo "==========================================" | |
| echo "Host: $ip" | |
| echo "==========================================" | |
| echo "PING: ✅ is alive (${ping_time}ms)" | |
| echo "TCP: $tcp_ports" | |
| echo "UDP: $udp_ports" | |
| echo "PTR: $ptr_record" | |
| echo "" | |
| } >> "$OUTPUT_FILE" | |
| fi | |
| } | |
| # Функция проверки TCP порта (оптимизированная) | |
| check_tcp_port() { | |
| local host=$1 | |
| local port=$2 | |
| if timeout $TCP_TIMEOUT bash -c "echo >/dev/tcp/$host/$port" 2>/dev/null; then | |
| echo -n "✅" | |
| else | |
| echo -n "❌" | |
| fi | |
| } | |
| # Функция проверки UDP порта (оптимизированная) | |
| check_udp_port() { | |
| local host=$1 | |
| local port=$2 | |
| if command -v nmap &> /dev/null; then | |
| # Параллельный nmap сканер | |
| if timeout $UDP_TIMEOUT nmap -sU -p $port --host-timeout ${UDP_TIMEOUT}s $host 2>/dev/null | grep -q "$port/udp.*open"; then | |
| echo -n "✅" | |
| else | |
| echo -n "❌" | |
| fi | |
| else | |
| # Базовая UDP проверка | |
| if timeout $UDP_TIMEOUT bash -c "echo >/dev/udp/$host/$port" 2>/dev/null; then | |
| echo -n "✅" | |
| else | |
| echo -n "❌" | |
| fi | |
| fi | |
| } | |
| # Функция проверки PTR записи | |
| check_ptr_record() { | |
| local ip=$1 | |
| local ptr_result | |
| if command -v dig &> /dev/null; then | |
| ptr_result=$(dig +short +time=2 +tries=1 -x "$ip" 2>/dev/null | head -1) | |
| elif command -v nslookup &> /dev/null; then | |
| ptr_result=$(timeout 2 nslookup "$ip" 2>/dev/null | grep "name =" | awk '{print $4}' | sed 's/\.$//') | |
| else | |
| ptr_result="" | |
| fi | |
| if [ -n "$ptr_result" ]; then | |
| echo -e "${GREEN}✅ $ptr_result${NC}" | |
| else | |
| echo -e "${RED}❌ the record was not found${NC}" | |
| fi | |
| } | |
| # Функция для получения PTR записи без цветов (для файла) | |
| get_ptr_record_plain() { | |
| local ip=$1 | |
| local ptr_result | |
| if command -v dig &> /dev/null; then | |
| ptr_result=$(dig +short +time=2 +tries=1 -x "$ip" 2>/dev/null | head -1) | |
| elif command -v nslookup &> /dev/null; then | |
| ptr_result=$(timeout 2 nslookup "$ip" 2>/dev/null | grep "name =" | awk '{print $4}' | sed 's/\.$//') | |
| else | |
| ptr_result="" | |
| fi | |
| if [ -n "$ptr_result" ]; then | |
| echo "✅ $ptr_result" | |
| else | |
| echo "❌ the record was not found" | |
| fi | |
| } | |
| # Функция сканирования одного хоста | |
| scan_host() { | |
| local ip="$1" | |
| local ping_time="$2" | |
| local output="==========================================\n" | |
| output+="Scanning $ip...\n" | |
| output+="==========================================\n" | |
| # Переменные для сохранения в файл | |
| local tcp_results="" | |
| local udp_results="" | |
| local ptr_result_plain="" | |
| if [ -n "$ping_time" ] && [ "$ping_time" != "null" ]; then | |
| output+="PING: ${GREEN}✅ is alive (${ping_time}ms)${NC}\n" | |
| # TCP проверки | |
| output+="TCP: " | |
| tcp_results="" | |
| first_tcp=true | |
| for port in $TCP_PORTS; do | |
| if [ "$first_tcp" = true ]; then | |
| first_tcp=false | |
| else | |
| output+="," | |
| tcp_results+="," | |
| fi | |
| local tcp_result | |
| tcp_result=$(check_tcp_port "$ip" "$port") | |
| output+="${tcp_result}$port" | |
| tcp_results+="${tcp_result}$port" | |
| done | |
| output+="\n" | |
| # UDP проверки | |
| output+="UDP: " | |
| udp_results="" | |
| first_udp=true | |
| for port in $UDP_PORTS; do | |
| if [ "$first_udp" = true ]; then | |
| first_udp=false | |
| else | |
| output+="," | |
| udp_results+="," | |
| fi | |
| local udp_result | |
| udp_result=$(check_udp_port "$ip" "$port") | |
| output+="${udp_result}$port" | |
| udp_results+="${udp_result}$port" | |
| done | |
| output+="\n" | |
| # PTR запись | |
| output+="PTR: " | |
| local ptr_result | |
| ptr_result=$(check_ptr_record "$ip") | |
| output+="$ptr_result\n" | |
| # Получаем PTR запись без цветов для файла | |
| ptr_result_plain=$(get_ptr_record_plain "$ip") | |
| # Сохраняем в файл | |
| save_to_file "$ip" "$ping_time" "$tcp_results" "$udp_results" "$ptr_result_plain" | |
| else | |
| output+="PING: ${RED}❌ is not reachable${NC}\n" | |
| output+="TCP: ${YELLOW}⏭️ skipped (host unreachable)${NC}\n" | |
| output+="UDP: ${YELLOW}⏭️ skipped (host unreachable)${NC}\n" | |
| output+="PTR: ${YELLOW}⏭️ skipped (host unreachable)${NC}\n" | |
| fi | |
| output+="\n" | |
| echo -e "$output" | |
| } | |
| # Функция для параллельного выполнения | |
| run_parallel() { | |
| local function_name="$1" | |
| local max_jobs="$2" | |
| local -a items=("${@:3}") | |
| local -a pids=() | |
| local running_jobs=0 | |
| local index=0 | |
| while [ $index -lt ${#items[@]} ] || [ ${#pids[@]} -gt 0 ]; do | |
| # Запускаем новые задачи если есть свободные слоты | |
| while [ $running_jobs -lt $max_jobs ] && [ $index -lt ${#items[@]} ]; do | |
| $function_name "${items[$index]}" & | |
| pids+=($!) | |
| running_jobs=$((running_jobs + 1)) | |
| index=$((index + 1)) | |
| done | |
| # Проверяем завершенные процессы | |
| local new_pids=() | |
| for pid in "${pids[@]}"; do | |
| if kill -0 "$pid" 2>/dev/null; then | |
| new_pids+=($pid) | |
| else | |
| running_jobs=$((running_jobs - 1)) | |
| wait "$pid" 2>/dev/null | |
| fi | |
| done | |
| pids=("${new_pids[@]}") | |
| sleep 0.1 | |
| done | |
| } | |
| # Функция массового ping | |
| bulk_ping() { | |
| local subnet="$1" | |
| local limit="$2" | |
| local -n result_ref="$3" | |
| local count=0 | |
| local total=$limit | |
| echo -e "${BLUE}Pinging $total hosts...${NC}" >&2 | |
| # Параллельный ping | |
| for ((i=0; i<limit; i++)); do | |
| local ip="${subnet}${i}" | |
| { | |
| # Пробуем ping с таймаутом | |
| if ping -c 1 -W $PING_TIMEOUT "$ip" &>/dev/null; then | |
| # Если пинг успешен, получаем время | |
| local ping_result | |
| ping_result=$(ping -c 1 -W $PING_TIMEOUT "$ip" 2>/dev/null | grep "time=" | head -1) | |
| if [ -n "$ping_result" ]; then | |
| local ping_time | |
| ping_time=$(echo "$ping_result" | grep -o "time=[0-9.]*" | cut -d= -f2) | |
| result_ref["$ip"]="$ping_time" | |
| echo -e "${GREEN}✓ Alive: $ip (${ping_time}ms)${NC}" >&2 | |
| else | |
| result_ref["$ip"]="1" | |
| echo -e "${GREEN}✓ Alive: $ip (1ms)${NC}" >&2 | |
| fi | |
| fi | |
| } & | |
| count=$((count + 1)) | |
| # Ограничиваем количество параллельных процессов | |
| if [[ $(jobs -r -p | wc -l) -ge $THREADS ]]; then | |
| wait -n | |
| fi | |
| done | |
| # Ждем завершения всех процессов | |
| wait | |
| # Альтернативный метод с fping (если нужен) | |
| if command -v fping &> /dev/null && [ ${#result_ref[@]} -eq 0 ]; then | |
| echo -e "${YELLOW}Trying fping method...${NC}" >&2 | |
| local ping_list=() | |
| for ((i=0; i<limit; i++)); do | |
| ping_list+=("${subnet}${i}") | |
| done | |
| # Используем fping в простом режиме | |
| for ip in "${ping_list[@]}"; do | |
| if fping -c 1 -t 1000 "$ip" &>/dev/null; then | |
| result_ref["$ip"]="1" | |
| echo -e "${GREEN}✓ Alive (fping): $ip${NC}" >&2 | |
| fi | |
| done | |
| fi | |
| } | |
| # Вспомогательная функция для параллельного сканирования | |
| scan_single_host() { | |
| local task="$1" | |
| local ip="${task%:*}" | |
| local ping_time="${task#*:}" | |
| scan_host "$ip" "$ping_time" | |
| } | |
| # 1. Проверка на --help / -h | |
| if [[ "$1" == "--help" ]] || [[ "$1" == "-h" ]]; then | |
| show_help | |
| exit 0 | |
| fi | |
| # 2. Проверка количества аргументов | |
| if [ $# -lt 2 ]; then | |
| echo "Error: At least 2 arguments required." | |
| echo "Use '$0 --help' for usage information." | |
| exit 1 | |
| fi | |
| # 3. Валидация subnet | |
| subnet="$1" | |
| if ! [[ "$subnet" =~ ^([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.$ ]]; then | |
| echo "Error: <subnet> must be in format NNN.NNN.NNN. where each NNN is 1–255 (no leading zeros)." | |
| echo "Example: 192.168.1." | |
| exit 1 | |
| fi | |
| # 4. Валидация range | |
| range="$2" | |
| if ! [[ "$range" =~ ^[0-9]+$ ]]; then | |
| echo "Error: <range> must be a non-negative integer." | |
| exit 1 | |
| fi | |
| if [ "$range" -lt 0 ] || [ "$range" -gt 255 ]; then | |
| echo "Error: <range> must be between 0 and 255 (inclusive)." | |
| exit 1 | |
| fi | |
| # 5. Валидация threads (опциональный параметр) | |
| if [ $# -ge 3 ]; then | |
| if [[ "$3" =~ ^[0-9]+$ ]] && [ "$3" -ge 1 ] && [ "$3" -le 50 ]; then | |
| THREADS="$3" | |
| else | |
| echo "Error: <threads> must be between 1 and 50." | |
| exit 1 | |
| fi | |
| fi | |
| # 6. Проверка утилит | |
| echo -e "${BLUE}Checking required utilities...${NC}" | |
| if ! command -v ping &> /dev/null; then | |
| echo "Error: 'ping' command not found. Please install it." | |
| exit 2 | |
| fi | |
| if command -v fping &> /dev/null; then | |
| echo -e "${GREEN}✓ fping available (fallback method)${NC}" | |
| else | |
| echo -e "${YELLOW}⚠ fping not found, using standard ping${NC}" | |
| fi | |
| if ! command -v nmap &> /dev/null; then | |
| echo -e "${YELLOW}⚠ 'nmap' not found. UDP port checking will be limited.${NC}" | |
| fi | |
| if ! command -v dig &> /dev/null && ! command -v nslookup &> /dev/null; then | |
| echo -e "${YELLOW}⚠ Neither 'dig' nor 'nslookup' found. PTR records cannot be checked.${NC}" | |
| fi | |
| # 7. Основное сканирование | |
| echo -e "\n${BLUE}Starting parallel scan of $range hosts using $THREADS threads...${NC}" | |
| echo -e "${BLUE}Subnet: $subnet${NC}" | |
| echo -e "${BLUE}Ports - TCP: $TCP_PORTS${NC}" | |
| echo -e "${BLUE}Ports - UDP: $UDP_PORTS${NC}" | |
| # Инициализация файла результатов | |
| if [ "$SAVE_TO_FILE" = true ]; then | |
| echo -e "${BLUE}Results will be saved to: $OUTPUT_FILE${NC}" | |
| { | |
| echo "Network Scan Results" | |
| echo "====================" | |
| echo "Date: $(date)" | |
| echo "Subnet: $subnet" | |
| echo "Range: 0-$((range-1))" | |
| echo "Total hosts scanned: $range" | |
| echo "" | |
| echo "ALIVE HOSTS:" | |
| echo "============" | |
| echo "" | |
| } > "$OUTPUT_FILE" | |
| fi | |
| echo "" | |
| start_time=$(date +%s) | |
| # Этап 1: Массовое обнаружение хостов | |
| echo -e "${BLUE}Stage 1: Host discovery...${NC}" | |
| declare -A alive_hosts | |
| bulk_ping "$subnet" "$range" alive_hosts | |
| echo -e "${GREEN}Found ${#alive_hosts[@]} alive hosts out of $range${NC}" | |
| # Этап 2: Параллельное сканирование доступных хостов | |
| if [ ${#alive_hosts[@]} -gt 0 ]; then | |
| echo -e "${BLUE}Stage 2: Parallel port scanning...${NC}" | |
| # Создаем массив для задач сканирования | |
| scan_tasks=() | |
| for ((i=0; i<range; i++)); do | |
| ip="${subnet}${i}" | |
| if [ -n "${alive_hosts[$ip]}" ]; then | |
| scan_tasks+=("$ip:${alive_hosts[$ip]}") | |
| else | |
| scan_tasks+=("$ip:null") | |
| fi | |
| done | |
| # Запускаем параллельное сканирование | |
| run_parallel scan_single_host "$THREADS" "${scan_tasks[@]}" | |
| else | |
| echo -e "${RED}No alive hosts found!${NC}" | |
| echo -e "${YELLOW}Debug info:${NC}" | |
| echo -e "${YELLOW}- Subnet: $subnet${NC}" | |
| echo -e "${YELLOW}- Range: 0 to $((range-1))${NC}" | |
| echo -e "${YELLOW}- Threads: $THREADS${NC}" | |
| echo -e "${YELLOW}- Ping timeout: ${PING_TIMEOUT}s${NC}" | |
| # Тестируем конкретные адреса которые должны быть доступны | |
| echo -e "\n${YELLOW}Testing specific addresses manually:${NC}" | |
| test_addresses=("${subnet}1" "${subnet}4" "${subnet}10" "${subnet}100") | |
| for test_ip in "${test_addresses[@]}"; do | |
| echo -n "Testing $test_ip: " | |
| if ping -c 1 -W 3 "$test_ip" &>/dev/null; then | |
| echo -e "${GREEN}REACHABLE${NC}" | |
| else | |
| echo -e "${RED}UNREACHABLE${NC}" | |
| fi | |
| done | |
| fi | |
| end_time=$(date +%s) | |
| duration=$((end_time - start_time)) | |
| # Добавляем итоги в файл | |
| if [ "$SAVE_TO_FILE" = true ]; then | |
| { | |
| echo "" | |
| echo "SCAN SUMMARY:" | |
| echo "=============" | |
| echo "Scan completed: $(date)" | |
| echo "Duration: ${duration} seconds" | |
| echo "Total hosts scanned: $range" | |
| echo "Alive hosts found: ${#alive_hosts[@]}" | |
| } >> "$OUTPUT_FILE" | |
| echo -e "${GREEN}Results saved to: $OUTPUT_FILE${NC}" | |
| fi | |
| echo -e "${GREEN}Scan completed in ${duration} seconds${NC}" | |
| echo -e "${GREEN}Scanned $range hosts, found ${#alive_hosts[@]} alive hosts${NC}" | |
| exit 0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Цвета | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| NC='\033[0m' | |
| show_help() { | |
| local width=120 # Общая ширина рамки (можно менять) | |
| local inner_width=$((width - 4)) # Ширина текста внутри (минус: |, пробел, пробел, |) | |
| local indent=" " # Отступ внутри рамки (слева) | |
| # Функция для форматирования строки в рамке | |
| frame_line_0() { | |
| local text="$1" | |
| if [ -z "$text" ]; then | |
| # Пустая строка — просто отступы и границы | |
| printf "| %${inner_width}s |\n" "" | |
| else | |
| # Добавляем отступ, обрезаем/дополняем до ширины | |
| local padded="${indent}${text}" | |
| printf "| %-${inner_width}s |\n" "${padded:0:$inner_width}" | |
| fi | |
| } | |
| frame_line() { | |
| local text="$1" | |
| if [ -z "$text" ]; then | |
| # Пустая строка — просто отступы и границы | |
| printf "%${inner_width}s\n" "" | |
| else | |
| # Добавляем отступ, обрезаем/дополняем до ширины | |
| local padded="${indent}${text}" | |
| printf "%-${inner_width}s\n" "${padded:0:$inner_width}" | |
| fi | |
| } | |
| # Верхняя граница: +----------+ | |
| line=$(printf '%*s' "$width" | tr ' ' '-') | |
| printf '+%s+\n' "$line" | |
| # Пустая строка после верхней границы | |
| frame_line "" | |
| # Основной контент | |
| frame_line "Usage: $0 <subnet> <range>" | |
| frame_line "" | |
| frame_line "Description:" | |
| frame_line " Pings a range of IP addresses based on the provided subnet and range." | |
| frame_line "" | |
| frame_line "Arguments:" | |
| frame_line " <subnet> Base subnet in format NNN.NNN.NNN. (each NNN: 1–255, no leading zeros)" | |
| frame_line " Example: 192.168.1." | |
| frame_line " <range> Number of IPs to ping (0–255). IPs will be: subnet0, subnet1, ..., subnet<range-1>" | |
| frame_line "" | |
| frame_line "Options:" | |
| frame_line " -h, --help Display this help message and exit" | |
| frame_line "" | |
| frame_line "Examples:" | |
| frame_line " $0 192.168.1. 5" | |
| frame_line " → Pings: 192.168.1.0, 192.168.1.1, ..., 192.168.1.4" | |
| frame_line " $0 10.0.0. 3" | |
| frame_line " → Pings: 10.0.0.0, 10.0.0.1, 10.0.0.2" | |
| frame_line " $0 --help" | |
| frame_line " → Shows this help" | |
| frame_line "" | |
| frame_line "Validation rules:" | |
| frame_line " - <subnet> must end with a dot (.)" | |
| frame_line " - Each octet in <subnet> must be 1–255 (no leading zeros like '01' or '007')" | |
| frame_line " - <range> must be an integer from 0 to 255" | |
| frame_line "" | |
| frame_line "Exit codes:" | |
| frame_line " 0 — Success (all checks passed, pinging completed)" | |
| frame_line " 1 — Validation error or invalid arguments" | |
| frame_line "" | |
| frame_line "Colors:" | |
| frame_line " ${GREEN}Green${NC} → Host is reachable" | |
| frame_line " ${RED}Red${NC} → Host is unreachable" | |
| # Пустая строка перед нижней границей | |
| frame_line "" | |
| # Нижняя граница: +----------+ | |
| line=$(printf '%*s' "$width" | tr ' ' '-') | |
| printf '+%s+\n' "$line" | |
| } | |
| # 1. Проверка на --help / -h | |
| if [[ "$1" == "--help" ]] || [[ "$1" == "-h" ]]; then | |
| show_help | |
| exit 0 | |
| fi | |
| # 2. Проверка количества аргументов | |
| if [ $# -ne 2 ]; then | |
| echo "Error: Exactly 2 arguments required." | |
| echo "Use '$0 --help' for usage information." | |
| exit 1 | |
| fi | |
| # 3. Валидация subnet (первый аргумент) | |
| subnet="$1" | |
| if ! [[ "$subnet" =~ ^([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.([1-9][0-9]{0,2}|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.$ ]]; then | |
| echo "Error: <subnet> must be in format NNN.NNN.NNN. where each NNN is 1–255 (no leading zeros)." | |
| echo "Example: 192.168.1." | |
| echo "Use '$0 --help' for more details." | |
| exit 1 | |
| fi | |
| # 4. Валидация range (второй аргумент) | |
| range="$2" | |
| if ! [[ "$range" =~ ^[0-9]+$ ]]; then | |
| echo "Error: <range> must be a non-negative integer." | |
| echo "Use '$0 --help' for valid range (0–255)." | |
| exit 1 | |
| fi | |
| if [ "$range" -lt 0 ] || [ "$range" -gt 255 ]; then | |
| echo "Error: <range> must be between 0 and 255 (inclusive)." | |
| echo "Use '$0 --help' for details." | |
| exit 1 | |
| fi | |
| # 5. Основные переменные | |
| NET="$subnet" | |
| LIMIT="$range" | |
| # 6. Цикл пинг-запросов | |
| for ((i=0; i<LIMIT; i++)); do | |
| ADR="${NET}${i}" | |
| echo "Pinging $ADR..." | |
| if ping -c 1 -W 1 "$ADR" &> /dev/null; then | |
| echo -e "${GREEN}$ADR is alive${NC}" | |
| else | |
| echo -e "${RED}$ADR is not reachable${NC}" | |
| fi | |
| done | |
| exit 0 |
Author
Author
Без скачивания
-
bash <(curl -Ls https://gist.githubusercontent.com/...) --help -
bash <(curl -Ls https://gist.githubusercontent.com/...) -h -
bash <(curl -Ls https://gist.githubusercontent.com/...) 192.168.1. 10
Author
Новые возможности (oh-my-hostvds-extended-v2-sh):
1. Три режима работы:
Одиночная подсеть (совместимость):
./oh-my-hostvds-extended-v2.sh 192.168.1. 50Несколько подсетей через аргументы:
./oh-my-hostvds-extended-v2.sh --subnets 192.168.1. 10.0.0. 172.16.1. --range 20Подсети из файла:
./oh-my-hostvds-extended-v2.sh --file subnets.txt --range 50 252. Формат файла subnets.txt:
192.168.1.
10.0.0.
172.16.1.
# Это комментарий
172.17.1.
3. Примеры использования:
# Сканирование трех подсетей по 10 адресов в каждой
./oh-my-hostvds-extended-v2.sh --subnets 192.168.1. 10.0.0. 172.16.1. --range 10
# Сканирование подсетей из файла с 50 потоками
./oh-my-hostvds-extended-v2.sh --file my_subnets.txt --range 100 50
# Совместимость со старым форматом
./oh-my-hostvds-extended-v2.sh 192.168.1. 50 254. Улучшенный вывод:
Starting parallel scan of 3 subnets, 30 total hosts using 10 threads...
Subnets: 192.168.1. 10.0.0. 172.16.1.
Range per subnet: 0-9
=========================================
Scanning subnet: 192.168.1.
=========================================
Stage 1: Host discovery...
Pinging 10 hosts in 192.168.1.*...
✓ Alive: 192.168.1.1 (12.5ms)
Found 3 alive hosts in 192.168.1.*
Stage 2: Parallel port scanning...
5. Сводка по всем подсетям:
- Общее количество подсетей
- Общее количество хостов
- Общее количество найденных активных хостов
- Время выполнения
Теперь вы можете сканировать несколько подсетей одновременно с сохранением всех результатов в один файл!
Author
Добавленные возможности (oh-my-hostvds-extended.sh):
1. Автоматическое сохранение в файл:
- Файл создается с timestamp:
scan_results_20231201_143022.txt - Сохраняется только информация о доступных хостах
- Полный вывод по-прежнему показывается в консоли
2. Формат файла результатов:
Network Scan Results
====================
Date: Fri Dec 1 14:30:22 UTC 2023
Subnet: 109.248.162.
Range: 0-199
Total hosts scanned: 200
ALIVE HOSTS:
============
==========================================
Host: 109.248.162.1
==========================================
PING: ✅ is alive (12.5ms)
TCP: ✅22,✅80,❌443,❌53,❌21,❌25,❌110,❌143,❌993,❌995,❌3389
UDP: ❌53,❌67,❌68,✅123,❌161,❌500,❌4500
PTR: ✅ host1.example.com
==========================================
Host: 109.248.162.4
==========================================
PING: ✅ is alive (8.2ms)
TCP: ❌22,✅80,✅443,❌53,❌21,❌25,❌110,❌143,❌993,❌995,❌3389
UDP: ❌53,❌67,❌68,✅123,❌161,❌500,❌4500
PTR: ❌ the record was not found
SCAN SUMMARY:
=============
Scan completed: Fri Dec 1 14:30:25 UTC 2023
Duration: 3 seconds
Total hosts scanned: 200
Alive hosts found: 15
3. Особенности сохранения:
- ✅ Только доступные хосты
- ✅ Без цветового форматирования (для удобства чтения)
- ✅ Включает сводку сканирования
- ✅ Автоматическое имя файла с датой и временем
4. Пример использования:
./oh-my-hostvds-extended.sh 109.248.162. 200 50После выполнения сканирования вы увидите:
Results saved to: scan_results_20231201_143022.txt
Теперь у вас есть полный лог всех доступных хостов в удобном для анализа формате!
Author
Новые возможности (oh-my-hostvds-extended-v3.sh):
1. Два режима вывода:
Подробный режим (по умолчанию):
./oh-my-hostvds-extended-v3.sh --subnets 192.168.1. 10.0.0. --range 10 --mode detailedВывод как раньше - полная информация по каждому хосту.
Режим с прогресс-баром:
./oh-my-hostvds-extended-v3.sh --subnets 192.168.1. 10.0.0. --range 10 --mode progress2. Прогресс-бар:
Progress: [██████████████████████████████████████████████████] 100% (100/100)
3. Компактный вывод результатов:
В режиме progress показывается только краткая информация о доступных хостах:
✓ 192.168.1.1 (12.5ms) | TCP: 2/11 | UDP: 1/7 | PTR: router.local
✓ 192.168.1.4 (8.2ms) | TCP: 3/11 | UDP: 1/7
✓ 10.0.0.1 (15.3ms) | TCP: 1/11 | UDP: 0/7 | PTR: server.example.com
4. Примеры использования:
# Подробный режим (по умолчанию)
./oh-my-hostvds-extended-v3.sh --subnets 192.168.1. 10.0.0. --range 20
# Режим прогресс-бара
./oh-my-hostvds-extended-v3.sh --file subnets.txt --range 50 --mode progress 25
# Смешанный режим - разные подсети в разных режимах
./oh-my-hostvds-extended-v3.sh 192.168.1. 50 --mode progress5. Особенности режима progress:
- ✅ Прогресс-бар показывает общий прогресс сканирования
- ✅ Недоступные хосты не показываются (только доступные)
- ✅ Компактная информация: IP, время ping, открытые порты, PTR
- ✅ Сохранение в файл работает в обоих режимах одинаково
6. Визуальное отличие:
- Detailed: Много текста, подходит для отладки
- Progress: Минималистичный вывод, подходит для мониторинга больших сетей
Теперь вы можете выбирать режим вывода в зависимости от задачи!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
1. Скачиваем
wget https://gist.githubusercontent.com/... -O ~/oh-my-hostvds-extended.shwget https://gist.githubusercontent.com/... -O ~/oh-my-hostvds-extended-v2.sh2. Даём права
chmod +x ~/oh-my-hostvds*.sh3. Смотрим справку
~/oh-my-hostvds*.sh --help