Skip to main content

Python wrapper for netscope-scan port scanner

Project description

netscope-py

RU | EN


Русский

Тонкая Python-обертка над бинарником netscope-scan — форком RustScan без nmap и скриптинга. Бинарник принимает цели, сканирует порты и возвращает чистый JSON; эта библиотека превращает его в удобный Python-API с полной типизацией.

Требования

  • Python ≥ 3.12
  • Бинарник netscope-scan в PATH или переменная окружения NETSCOPE_BIN

Установка

# из исходников (editable)
uv pip install -e .

# сборка wheel
uv build

Быстрый старт

from netscope import scan, scan_async, ScanResult, ScanError

# --- Синхронный вызов ---
results: list[ScanResult] = scan(
    ["192.168.1.0/24", "10.0.0.1"],
    ports=[22, 80, 443],
)

for r in results:
    print(r.ip, r.ports)
# 192.168.1.5   [22, 80]
# 10.0.0.1      [22, 443]

# --- Асинхронный вызов ---
import asyncio

async def main() -> None:
    results = await scan_async(["10.0.0.0/24"], range=(1, 1000))
    for r in results:
        print(r.ip, r.ports)

asyncio.run(main())

Справочник API

scan(addresses, *, ...)list[ScanResult]

Синхронное сканирование. Блокирует поток до завершения бинарника.

Параметр Тип По умолчанию Описание
addresses list[str] Цели: IP, CIDR, хосты
ports list[int] | None None Конкретные порты
range tuple[int, int] | None None Диапазон портов (start, end) включительно
top bool False Топ-1000 популярных портов
batch_size int 4500 Одновременных сокетов
timeout int 1500 Таймаут на порт, мс
tries int 1 Попыток на порт
ulimit int | None None Лимит файловых дескрипторов (Unix)
scan_order str "serial" "serial" или "random"
exclude_ports list[int] | None None Исключить порты
exclude_addresses list[str] | None None Исключить адреса/CIDR
udp bool False UDP-режим
resolver str | None None Кастомные DNS-резолверы

Приоритет выбора портов: ports > range > top. Если задано несколько — используется первое ненулевое.

scan_async(addresses, *, ...)Awaitable[list[ScanResult]]

Асинхронная версия. Принимает те же аргументы, что и scan. Использует asyncio.create_subprocess_exec — event loop не блокируется.

ScanResult

@dataclass
class ScanResult:
    ip: str        # IP-адрес хоста
    ports: list[int]  # Открытые порты

ScanError

Наследует RuntimeError. Выбрасывается, если netscope-scan завершился с ненулевым кодом. Сообщение содержит вывод stderr.

try:
    results = scan(["10.0.0.1"], ports=[80])
except ScanError as e:
    print(e)  # netscope-scan error output

Расположение бинарника

Порядок поиска:

  1. Переменная окружения NETSCOPE_BIN
  2. Файл bin/netscope-scan внутри установленного пакета
  3. netscope-scan в PATH
# Явно указать путь
NETSCOPE_BIN=/opt/tools/netscope-scan python my_script.py

Примеры

# Сканирование подсети, случайный порядок
results = scan(
    ["10.10.0.0/16"],
    range=(1, 65535),
    batch_size=8000,
    timeout=800,
    scan_order="random",
    ulimit=65536,
)

# Исключить служебные адреса
results = scan(
    ["192.168.0.0/24"],
    top=True,
    exclude_addresses=["192.168.0.1", "192.168.0.254"],
    exclude_ports=[135, 139, 445],
)

# UDP
results = scan(["10.0.0.1"], ports=[53, 161, 500], udp=True)

# Несколько целей параллельно (asyncio)
import asyncio

async def multi_scan() -> None:
    tasks = [
        scan_async(["10.0.0.0/24"], ports=[22]),
        scan_async(["172.16.0.0/24"], ports=[80, 443]),
    ]
    all_results = await asyncio.gather(*tasks)
    for batch in all_results:
        for r in batch:
            print(r.ip, r.ports)

asyncio.run(multi_scan())

English

A thin Python wrapper around netscope-scan — a RustScan fork with nmap and scripting removed. The binary accepts targets, scans ports, and emits clean JSON; this library turns it into a typed Python API.

Requirements

  • Python ≥ 3.12
  • netscope-scan binary on PATH or NETSCOPE_BIN environment variable

Installation

# editable install from source
uv pip install -e .

# build a wheel
uv build

Quick start

from netscope import scan, scan_async, ScanResult, ScanError

# --- Synchronous ---
results: list[ScanResult] = scan(
    ["192.168.1.0/24", "10.0.0.1"],
    ports=[22, 80, 443],
)

for r in results:
    print(r.ip, r.ports)
# 192.168.1.5   [22, 80]
# 10.0.0.1      [22, 443]

# --- Asynchronous ---
import asyncio

async def main() -> None:
    results = await scan_async(["10.0.0.0/24"], range=(1, 1000))
    for r in results:
        print(r.ip, r.ports)

asyncio.run(main())

API reference

scan(addresses, *, ...)list[ScanResult]

Synchronous port scan. Blocks the calling thread until the binary exits.

Parameter Type Default Description
addresses list[str] Targets: IPs, CIDRs, hostnames
ports list[int] | None None Explicit port list
range tuple[int, int] | None None Port range (start, end) inclusive
top bool False Scan top-1000 common ports
batch_size int 4500 Concurrent socket count
timeout int 1500 Per-port timeout in ms
tries int 1 Probe attempts per port
ulimit int | None None File-descriptor limit (Unix)
scan_order str "serial" "serial" or "random"
exclude_ports list[int] | None None Ports to skip
exclude_addresses list[str] | None None Addresses/CIDRs to skip
udp bool False UDP mode
resolver str | None None Custom DNS resolvers

Port selection priority: ports > range > top. The first non-None / truthy value wins.

scan_async(addresses, *, ...)Awaitable[list[ScanResult]]

Async variant with identical parameters. Uses asyncio.create_subprocess_exec — the event loop is never blocked.

ScanResult

@dataclass
class ScanResult:
    ip: str           # Host IP address
    ports: list[int]  # Open port numbers

ScanError

Subclass of RuntimeError. Raised when netscope-scan exits with a non-zero code. The error message includes the binary's stderr output.

try:
    results = scan(["10.0.0.1"], ports=[80])
except ScanError as e:
    print(e)  # netscope-scan error output

Binary resolution

The binary is located in this order:

  1. NETSCOPE_BIN environment variable
  2. bin/netscope-scan bundled inside the installed package
  3. netscope-scan anywhere on PATH
# Point to a custom binary
NETSCOPE_BIN=/opt/tools/netscope-scan python my_script.py

Examples

# Large subnet, random order, raised ulimit
results = scan(
    ["10.10.0.0/16"],
    range=(1, 65535),
    batch_size=8000,
    timeout=800,
    scan_order="random",
    ulimit=65536,
)

# Exclude gateway and broadcast, skip Windows noise ports
results = scan(
    ["192.168.0.0/24"],
    top=True,
    exclude_addresses=["192.168.0.1", "192.168.0.254"],
    exclude_ports=[135, 139, 445],
)

# UDP scan
results = scan(["10.0.0.1"], ports=[53, 161, 500], udp=True)

# Run multiple subnets in parallel with asyncio
import asyncio

async def multi_scan() -> None:
    tasks = [
        scan_async(["10.0.0.0/24"], ports=[22]),
        scan_async(["172.16.0.0/24"], ports=[80, 443]),
    ]
    all_results = await asyncio.gather(*tasks)
    for batch in all_results:
        for r in batch:
            print(r.ip, r.ports)

asyncio.run(multi_scan())

Project structure

netscope-py/
├── pyproject.toml
└── src/
    └── netscope/
        ├── __init__.py   # Public re-exports: scan, scan_async, ScanResult, ScanError
        ├── _models.py    # ScanResult dataclass, ScanError exception
        ├── _runner.py    # subprocess / asyncio implementation
        └── py.typed      # PEP 561 marker — full type information shipped

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

netscope_py-0.1.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (2.0 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

netscope_py-0.1.4-py3-none-macosx_11_0_arm64.whl (1.6 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

File details

Details for the file netscope_py-0.1.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.

File metadata

File hashes

Hashes for netscope_py-0.1.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 8a707117e7bc7232baaba320f222003b853fcf2a89e5c6626a9499dad097022e
MD5 b25bbc632f4c6ead49dae8edbace1dc4
BLAKE2b-256 97b8403ba832157e07954148c52c9ac01b9d68d9f9993bc635f42dc0256fef67

See more details on using hashes here.

Provenance

The following attestation bundles were made for netscope_py-0.1.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl:

Publisher: publish.yml on NetSc0pe/netscope-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file netscope_py-0.1.4-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for netscope_py-0.1.4-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b85fc0c5a083395c234c355a0dca6b9b6333f082b37d9c0f65d017cfb9998311
MD5 3e67ace7b617d3d3594e1976bf247b3d
BLAKE2b-256 5e29a8bc1f80b67b5cc1c2e03a58047bd7a320a07cf8edda9c45d6443e4d1f6e

See more details on using hashes here.

Provenance

The following attestation bundles were made for netscope_py-0.1.4-py3-none-macosx_11_0_arm64.whl:

Publisher: publish.yml on NetSc0pe/netscope-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page