Metadata-Version: 2.5
Name: tftp
Version: 0.0.0
Summary: Pure-Python TFTP client and server for IPv4 and IPv6: RFC 1350, 2347, 2348, 2349 and 7440
Project-URL: Homepage, https://github.com/jose-pr/pytftp/
Project-URL: Documentation, https://jose-pr.github.io/pytftp/
Project-URL: Issues, https://github.com/jose-pr/pytftp/issues
Author-email: jose-pr <jose-pr@coqui.dev>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet
Classifier: Topic :: System :: Boot
Classifier: Topic :: System :: Networking
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: netimps<0.4,>=0.3.4
Provides-Extra: cli
Requires-Dist: duho<0.7,>=0.6.0; extra == 'cli'
Provides-Extra: dev
Requires-Dist: black; extra == 'dev'
Requires-Dist: build; extra == 'dev'
Requires-Dist: duho<0.7,>=0.6.0; extra == 'dev'
Requires-Dist: pathlib-next[uri]<0.10,>=0.9.0; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs; extra == 'docs'
Requires-Dist: mkdocs-material; extra == 'docs'
Requires-Dist: mkdocstrings[python]; extra == 'docs'
Provides-Extra: path
Requires-Dist: pathlib-next[uri]<0.10,>=0.9.0; extra == 'path'
Description-Content-Type: text/markdown

# tftp

[![Version](https://img.shields.io/pypi/v/tftp.svg)](https://pypi.org/project/tftp/)
[![Python versions](https://img.shields.io/pypi/pyversions/tftp.svg)](https://pypi.org/project/tftp/)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jose-pr/pytftp/blob/main/LICENSE)
[![Docs](https://img.shields.io/badge/docs-latest-blue.svg)](https://jose-pr.github.io/pytftp/)
[![CI](https://img.shields.io/github/actions/workflow/status/jose-pr/pytftp/test.yml)](https://github.com/jose-pr/pytftp/actions/workflows/test.yml)

A **pure-Python TFTP client, server, relay and capture decoder for IPv4 and
IPv6** that implements the whole of modern TFTP: RFC 1350, the option
extension (RFC 2347) with `blksize`, `timeout`, `tsize` and `windowsize`
(RFC 2348, 2349, 7440), tftp-hpa's extensions, block-number rollover and
netascii. Built as infrastructure for network boot: it interoperates with
iPXE, tftp-hpa, BusyBox, dnsmasq and curl, serves files, HTTP or another TFTP
server, and shows you what crossed the wire.

```python
import tftp

tftp.Client("192.0.2.1").download("pxelinux.0", "pxelinux.0")

with tftp.Server("/srv/tftp") as server:      # IPv6 + IPv4, port 69
    server.serve_forever()
```

## Features

- **Complete option support** — `blksize` up to 65464, RFC 7440 windows,
  `tsize` both ways, `timeout`, tftp-hpa's `utimeout`, `rollover`,
  `blksize2`, `cookie`, and Windows bootmgr's `mstfwindow`. Unlimited file size. A registry for your own options,
  and profiles (`pxe`, `hpa`, `legacy`, `strict`) for quirky peers.
- **Fast** — windowed transfers move hundreds of MiB/s over loopback in pure
  Python (see [benchmarks](https://github.com/jose-pr/pytftp/blob/main/benchmarks/README.md)); the hot path reads into
  preallocated buffers and keeps a sent window in memory instead of re-reading.
- **Correct under loss** — Sorcerer's Apprentice fix, exponential backoff,
  gap detection inside a window, repeated OACKs tolerated, ERROR 5 for stray
  packets without disturbing the transfer, dallying on the last ACK. The
  engine is tested over a simulated lossy link and fuzzed.
- **IPv4 and IPv6** — dual-stack listening by default; v4 clients are served
  from plain v4 sockets.
- **Replies from the request's address** — on a multi-homed host or a VIP,
  each transfer answers from the address the request was sent to (pktinfo,
  via [netimps](https://github.com/jose-pr/netimps)); tested on Linux and
  Windows.
- **Safe file serving** — no path escapes `root` (`..`, symlinks, Windows
  device names), uploads are atomic (a failed upload leaves nothing), quotas
  and disk-space checks from `tsize`.
- **Pluggable backends** — a handler is two methods, so boot menus or images
  can be generated per client; built in: a directory, memory, an HTTP(S)
  gateway, and a terminating proxy to another TFTP server. Slow backends apply
  backpressure instead of blocking other transfers.
- **Transparent relay** — forwards requests byte for byte to upstream servers
  (routed by subnet, filename or interface), so unknown extensions still work.
- **Capture and debugging** — trace every datagram from the client, server or
  relay; write Wireshark-readable pcaps; read pcap/pcapng files or a live
  `tcpdump` pipe, reconstruct each transfer and extract its file.
- **Directory listings** — an opt-in extension between pytftp peers: `pytftp ls`,
  `Client.listdir()`, and `iterdir`/`walk`/`glob` on paths; other servers
  simply ignore it.
- **Deployment** — transfer ports pinned to a range for firewalls; per-client
  roots, case-insensitive names and filename remapping in `pytftp serve`.
- **asyncio** — `AsyncClient` and `AsyncServer` with async handlers and streams.
- **pathlib** — `client.path("boot/x").read_bytes()`, and `tftp://` URLs in
  [pathlib-next](https://github.com/jose-pr/pathlib-next), so
  `UriPath("tftp://h/x").copy("s3://bucket/x")` just works (optional extra).
- **Bounded** — limits on requests, sessions per client, window memory and
  transfer time; counters for metrics.
- **CLI** — `pytftp get|put|serve|relay|capture` with `--json`, `--trace` and
  `--pcap` (optional extra).

## Installation

```bash
pip install tftp            # library
pip install "tftp[cli]"     # plus the pytftp command
```

| Extra | Adds | Needed for |
| --- | --- | --- |
| `cli` | `duho` | the `pytftp` command and `python -m tftp` |
| `path` | `pathlib-next[uri]` | `TftpPath`, and `tftp://` URLs in `pathlib_next.uri.UriPath` |

Requires Python 3.9+. The one required dependency, `netimps`, has no
dependencies of its own.

## Quick start

Client:

```python
import tftp

client = tftp.Client("boot.example.net", blksize=1428, windowsize=16)
result = client.download("images/vmlinuz", "vmlinuz")
print(result.bytes, result.duration, result.negotiated)

data = client.get("pxelinux.cfg/default")          # into memory
client.upload("logs/boot.txt", b"ok\n")            # needs a writable server
```

Server with uploads, a bounded window and a completion hook:

```python
import tftp

server = tftp.Server(
    "/srv/tftp",
    "::",
    69,
    writable=True,
    options=tftp.ServerOptions(max_blksize=8192, max_windowsize=32),
    on_complete=lambda r: print(r.operation, r.filename, r.peer[0], r.error or "ok"),
)
server.serve_forever()
```

Generated content:

```python
import io
import tftp

class Menu(tftp.FileSystemHandler):
    def open_read(self, context):
        if context.filename == "menu.cfg":
            return io.BytesIO(b"client %s\n" % context.peer[0].encode())
        return super().open_read(context)

tftp.Server(Menu("/srv/tftp")).serve_forever()
```

Command line:

```bash
pytftp get 192.0.2.1 pxelinux.0 -b 1428 -w 16
pytftp put 192.0.2.1 firmware.bin
pytftp serve /srv/tftp --port 6969 --write --json
```

Relay, capture and asyncio:

```python
import tftp
from tftp.relay import Relay, RouteTable, by_subnet
from tftp.capture import PcapWriter, analyze

# Forward requests to per-subnet boot servers, recording everything.
with PcapWriter("relay.pcap") as pcap:
    route = RouteTable([by_subnet({"10.1.0.0/16": "10.1.0.5"})], default="10.0.0.20")
    Relay(route, trace=pcap).serve_forever()

# What happened in a capture (yours, or tcpdump's)?
for transfer in analyze("boot.pcapng").transfers:
    print(transfer, transfer.missing_blocks)

# asyncio
async def fetch():
    async for chunk in tftp.aio.AsyncClient("192.0.2.1").stream("vmlinuz"):
        ...
```

```bash
pytftp serve --http https://images.example.com/pxe/ --compat pxe
pytftp relay 10.0.0.20 --route-prefix windows/=wds.lan --trace
tcpdump -i eth0 -U -w - udp | pytftp capture - --filter "op=RRQ,ERROR"
```

## API overview

| Module | Purpose |
| --- | --- |
| `tftp.client` | `Client`, `download`, `upload` |
| `tftp.server` | `Server`, `ServerLimits`, `FileSystemHandler`, `AtomicWriter`, `RequestContext` |
| `tftp.backends` | `MemoryHandler`, `HttpHandler`, `UpstreamHandler` (proxy), `Pipe` |
| `tftp.relay` | `Relay` and routing helpers |
| `tftp.aio` | `AsyncClient`, `AsyncServer` |
| `tftp.capture` | trace events, `PcapWriter`, pcap/pcapng reading, `analyze`, filters |
| `tftp.options` | `ServerOptions`, option registry, profiles, `Negotiated` |
| `tftp.packet` | `Opcode`, `ErrorCode`, packet types, `encode_*`/`decode` |
| `tftp.transfer` | I/O-free `Sender`/`Receiver` engine |
| `tftp.uri` | `tftp://` URLs |
| `tftp.path` | `TftpPath`, `TftpUriPath` (`path` extra) |
| `tftp.netascii` | streaming netascii translation |
| `tftp.errors` | `TftpError` and the typed `RemoteError` subclasses |
| `tftp.cli` | the `pytftp` command (`cli` extra) |

Everything is also importable from `tftp` directly. The complete reference,
with every signature and gotcha, is [`src/tftp/AGENTS.md`](https://github.com/jose-pr/pytftp/blob/main/src/tftp/AGENTS.md),
which ships inside the package.

## Development

```bash
py -3.14 -m venv .venv/3.14-nt-arm64          # or python3.14 -m venv .venv/3.14-posix-x86_64
.venv/3.14-nt-arm64/Scripts/pip install -e ".[dev]"
.venv/3.14-nt-arm64/Scripts/python -m pytest -q
.venv/3.14-nt-arm64/Scripts/python -m black --check src tests benchmarks examples
python benchmarks/run.py
```

`pytest -m "not slow"` skips the rollover tests; tests marked `interop` run
against curl, tftp-hpa, BusyBox and dnsmasq where installed (their servers need
passwordless `sudo`). `sudo python tests/firmware_boot.py ipxe` network-boots
iPXE in QEMU against the server (its `uefi` scenario does not pass yet). See
[AGENTS.md](https://github.com/jose-pr/pytftp/blob/main/AGENTS.md) for the layout and conventions.

### Releasing

This project follows [Semantic Versioning](https://semver.org/) and keeps a
[`CHANGELOG.md`](https://github.com/jose-pr/pytftp/blob/main/CHANGELOG.md). Pushing a tag matching `v*` triggers the release
workflow: test gate → build (checking the tag names the version built) → a strict
docs build as a gate → GitHub release → publish. The release workflow never deploys
the docs site itself: for a final release its last job dispatches the docs workflow
at the tag, which owns every Pages deploy.

## License

MIT — see [LICENSE](https://github.com/jose-pr/pytftp/blob/main/LICENSE).
