Skip to main content

tcpsweep

A TCP connect sweep built for proxychains, in a single dependency-free Python file.

proxychains4 tcpsweep 10.0.0.0/24 -p 22,80,443

A connect scan is the only kind of scan that survives a SOCKS proxy — no raw sockets, no SYN, no ICMP — so it is the right primitive. But a connect scan through a proxy behaves nothing like a direct one, and most scanners report confidently wrong results because of it. This tool is designed around the difference.

Authorized use only. Only scan hosts and networks you own or have explicit permission to test.

What running through a chain actually does

These are measured against a fault-injecting SOCKS5 server, not assumed:

SOCKS outcome what Python sees elapsed
success connected 0.01s
refused (0x05) ECONNREFUSED 0.00s
host unreachable (0x04) ECONNREFUSED 0.00s
network unreachable (0x03) ECONNREFUSED 0.00s
denied by ruleset (0x02) ECONNREFUSED 0.00s
general failure (0x01) ECONNREFUSED 0.00s
proxy hung ECONNREFUSED = tcp_read_time_out
proxy dead ECONNREFUSED 0.00s

Three consequences drive the whole design:

errno carries no information. Everything is ECONNREFUSED. Only elapsed time separates a definitive answer from a black hole, so every classification here is time-based. A negative that came back instantly is the chain's real answer; one that consumed half the read timeout or more is a stall, and is reported filtered rather than closed.

Your timeout is inert. The SOCKS handshake happens inside proxychains' hooked connect(), so settimeout() does not bound a probe — the config's tcp_read_time_out does. tcpsweep reads the live config and shows the real budget instead of pretending -w applies. Raise -c to go faster, not lower -w.

A dead proxy is identical to "everything closed." Both are an instant ECONNREFUSED. Without a control target a sweep cannot tell a clean negative result from a broken chain, so tcpsweep keeps a canary — the first open port it finds, or one you name with --canary — and re-probes it after a long run of negatives. If it has stopped answering, every unverified result is withdrawn and re-run, and if the chain never returns the tool exits 3 rather than reporting a tidy, wrong, empty result.

Efficiency

Through a chain the cost model is lopsided: an open port and a fast negative are nearly free, while a stalled probe costs the entire read timeout. Sweep time is therefore dominated by stalls.

So a multi-host sweep runs a short discovery pass first. Hosts that answer anything — open or a fast negative — are kept, because sweeping them is cheap. Hosts where every discovery probe stalled are skipped, because those are exactly the ones that would burn the full timeout on every port. Discovery results are reused, never re-probed.

Measured against 8 hosts where 4 black-hole everything: 4.1s with discovery, 8.1s without, identical findings. The gap widens with more ports per host.

Concurrency is the one lever that matters, and it scales linearly (16 stalling probes: 64.1s serial → 4.0s at -c 16).

Install

pipx install tcpsweep      # isolated, recommended
pip install tcpsweep

No third-party dependencies, so you can also just copy tcpsweep.py onto a host. It identifies itself by whatever name you invoke it under.

Usage

proxychains4 tcpsweep 10.0.0.0/24                  # top 20 ports, discovery on
proxychains4 tcpsweep 10.0.0.0/16 -p 445 -c 64     # one port, wide, fast
proxychains4 tcpsweep 10.0.0.5 -p 1-1024 --no-discover
proxychains4 tcpsweep -iL targets.txt -p 22,80 --canary 10.0.0.1:22
tcpsweep 10.0.0.0/24 -p 80 --json out.json         # direct, no proxy

Targets accept 10.0.0.1, 10.0.0.0/24, 10.0.0.1-20, 10.0.0.{1,5-9}, hostnames, -iL FILE (- for stdin) and --exclude. Run --help for the full option list.

Open ports stream to stdout as host port, flushed, so the tool pipes:

proxychains4 tcpsweep 10.0.0.0/24 -p 445 | tee found.txt | while read ip port; do
  echo "hit $ip:$port"
done

Progress, warnings and the summary go to stderr, so they never contaminate the pipeline. An open port is never withdrawn, so anything that reaches stdout is final even if the run is interrupted.

Exit codes

Code Meaning
0 completed, at least one open port
1 completed, nothing open
2 bad arguments
3 the chain failed and never came back — results are not trustworthy
130 interrupted

0/1 are grep-style, so if tcpsweep ...; then branches on "found something". 3 is the one that matters for automation: it means don't believe this run.

Notes

  • IPv4 only, deliberately: proxychains handles IPv4 TCP, and silently scanning something else would be worse than refusing.
  • With proxy_dns, a hostname resolves to a synthetic 224.0.0.x placeholder. The sweep reaches the right host but results are labelled with that address; tcpsweep warns when it sees one. Scan literal IPs if the output matters.
  • Through a chain, closed only means the proxy answered fast — it cannot be told apart from host-unreachable or a ruleset denial. The summary says so.
  • --json output is written atomically and 0600, since scan results are sensitive.
  • Banners (-b) are reduced to printable ASCII before they touch your terminal or the JSON.

Development

python3 -m pytest test_scan.py -q

Metadata

Release files for tcpsweep 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tcpsweep 0.3.0
File Size Uploaded
tcpsweep-0.3.0.tar.gz 26.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tcpsweep 0.3.0
File Interpreter ABI Platform
tcpsweep-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 45.6 kB

Release files / tcpsweep-0.3.0.tar.gz

Download URL tcpsweep-0.3.0.tar.gz
Size 26.0 kB
Tags Source
SHA-256 checksum
How to use checksums
e02a89097431b3f9b757108dab8e7e3fd1d2918914f2a424b3aa8ae130ba2968
BLAKE2b-256 checksum
How to use checksums
987935c7603940c73d1c6e175d3551c3a303c2be4179b945612ebb92b0d469c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 24, 2026.

Transparency log

Release files / tcpsweep-0.3.0-py3-none-any.whl

Download URL tcpsweep-0.3.0-py3-none-any.whl
Size 19.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
748b34fd73296a372483524c1dfea0e092f4d7ff7914879aa26c2055b6702071
BLAKE2b-256 checksum
How to use checksums
bd3234b63c5ce5c67f977e12bb9e412417a8f1b2f8e2dd9a15f6f788f879bb8c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 24, 2026.

Transparency log

Release history Release notifications | RSS feed

0.5.0

2 release files

0.4.0

2 release files

0.3.1

2 release files

This release

0.3.0 This release

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page