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.2-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.2-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.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.

File metadata

File hashes

Hashes for netscope_py-0.1.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 c0aa8187d9205bcd0877dab12f841381472c3eac6ebf5aa4ac3dfb5d1da36021
MD5 bf9447918c35c00665e56f31004c1126
BLAKE2b-256 d2be78cc2452abef10aabd42aa3d7948f198e298f15c5451083a5760bd4a050c

See more details on using hashes here.

Provenance

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

Publisher: publish.yml on qu1nqqy/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.2-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for netscope_py-0.1.2-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 04b62e7896c8fff5080b403e4b9c46ef4c3d024f2111bccfd0aa5c0778617d84
MD5 39fadb4df6b6a5f0e7a678ec46175630
BLAKE2b-256 f547d5678edede7c4048deffd6f8db04f85bb917a85cbf0674fdec85a7a55b9e

See more details on using hashes here.

Provenance

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

Publisher: publish.yml on qu1nqqy/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