Ken Johnson 4736062182 v0.4.1: don't fail session on legitimate end-of-batch peer close
ReadOneFrame's unconditional FailSession(bpErrIO, 'transport
closed by peer') on Recv<0 was firing after the mutual EOB
exchange (the spec-correct moment for a peer to hang up),
overriding the success path: every file delivered + ACKed
cleanly, but consumers saw Result.Success = False.

Reported with full bppShutdown -> Pump -> ReadOneFrame trace
by the fpc-fastway / fidonet-tc maintainer.

Fixed by guarding the FailSession with the same predicate
fpc-comet's cm.driver.pas already used: skip the failure
when FPhase is already shutdown/done/failed, or when we've
finished the batch with a full mutual-EOB exchange (MBT-aware).
The dead-socket spin guard the original FailSession was added
for is preserved.
2026-04-27 07:21:21 -07:00
2026-04-21 12:45:41 -07:00
2026-04-21 12:45:41 -07:00
2026-04-21 12:45:41 -07:00
2026-04-21 12:45:41 -07:00

fpc-binkp

A Free Pascal library for the BinkP/1.1 mailer protocol (FTS-1026, FTS-1027) with support for the widely-deployed vendor extensions (NR, ND, MBT, PLZ, CRYPT, DES-CBC, ED25519).

Sibling library to fpc-msgbase (message storage), fpc-ftn-transport (PKT, ArcMail, queues), and comet (the mailer daemon that consumes all three). Same vendoring model: each library is its own repo with its own tests; consumers cp / rsync units from units/ into their build; bug fix → commit here → consumers pull at their own cadence → rebuild → deploy.

Scope

Concern Owned by
BinkP/1.1 wire protocol (frames, commands, state machine) fpc-binkp
CRAM-MD5, CRYPT, DES-CBC, ED25519 auth fpc-binkp
PLZ (zlib per-frame) compression fpc-binkp
NR (resume), ND (non-destructive), MBT (multi-batch) fpc-binkp
PKT / ArcMail / BSO queue fpc-ftn-transport
Message storage (JAM, Squish, ...) fpc-msgbase
Session dispatch, config, UI, logging backends consumer
Dialup / EMSI / modem framing out of scope — separate library

The library handles the wire. Consumers own everything above and below: the socket, the filesystem, the logging sink, the decision of which file to send next, what "secure" means for an inbound directory.

Status

Current: 0.1.0 — initial release. Core library compiles on all five targets (Linux, FreeBSD, Windows, OS/2, DOS go32v2). Reference TCP transport is UNIX-only; DOS has a dedicated Watt-32 transport; Windows / OS/2 consumers plug in their own IBPTransport.

Seven test programs green including a full two-session round-trip self-test. End-to-end smoke-tested on localhost (outbound ↔ inbound, 10 KB binary transferred with identical SHA-256, full CRAM-MD5 + CRYPT + MBT).

Naming

Unit prefix: bp.<category>.<name>.pasbp = "binkp". All units use {$mode objfpc}{$H+}{$modeswitch advancedrecords}.

Architecture

Step-based engine (modeled on Argus, not binkd):

Session := TBPSession.Create(bpsOutbound, Transport, Provider, Events);
Session.Config.LocalAddress := FTNAddr('1:218/720');
Session.Config.Advertise    := [bpOptNR, bpOptND, bpOptPLZ, bpOptMBT];
Session.Config.OnPostAuth   := @MyPostAuthHook;

while Session.NextStep do
begin
  // embedder is in control between every tick.
  // can cancel, inspect state, swap provider, etc.
end;

writeln('Result: ', Session.Result.Description);
Session.Free;

The embedder drives the loop. Each call to NextStep does a bounded unit of I/O + state-machine work and returns. This gives:

  • Testability. Replace Transport with a TMemoryStream- backed mock; feed in canned bytes; assert on emitted frames. No sockets required.
  • Cancellation. Stop calling NextStep; free the session.
  • Multiplexing. An event loop can drive N sessions on a single thread if the Transport implementation supports non-blocking I/O.
  • Observability. Inspect Session.Phase, Session.RemoteInfo between ticks. Hooks fire at every policy decision.

See docs/architecture.md for the full design, docs/api.md for the consumer-facing API reference, and docs/spec.md for the FTSC / FSP spec cheat sheet and wire-format notes.

Building

Once units land:

./build.sh                # all targets
./build.sh x86_64-linux   # just one
./run_tests.sh            # run test suite

Supported targets (matches fpc-ftn-transport): x86_64-linux, i386-go32v2, i386-os2, i386-win32, i386-linux, i386-freebsd.

License

GPL-2.0 — same as comet.

Layout

fpc-binkp/
├── build.sh              — multi-target build driver
├── CHANGELOG.md          — release notes, tagged vX.Y.Z
├── README.md             — this file
├── run_tests.sh          — test runner
├── fpc.cfg               — FPC search paths
├── docs/
│   ├── architecture.md   — engine design, hook taxonomy
│   ├── api.md            — public API reference
│   └── spec.md           — FTS-1026/1027 + FSP cheat sheet
├── examples/
│   ├── example_outbound.pas    — embed as originator
│   ├── example_inbound.pas     — embed as answerer
│   ├── example_mock.pas        — replay a captured session
│   └── example_multiplex.pas   — N sessions one thread
├── src/
│   ├── bp.version.pas         — BP_VERSION constant
│   ├── bp.types.pas           — enums, records, forward decls
│   ├── bp.frame.pas           — TBPFrame, read/write helpers
│   ├── bp.opt.pas             — OPT keyword parser / builder
│   ├── bp.cram.pas            — CRAM-MD5 (HMAC-MD5 RFC 2104)
│   ├── bp.crypt.pas           — FSP-1037 password-derived cipher
│   ├── bp.des.pas             — Argus DES-CBC stream
│   ├── bp.plz.pas             — zlib per-frame compression
│   ├── bp.transport.pas       — IBPTransport interface
│   ├── bp.provider.pas        — IBPFileProvider interface
│   ├── bp.events.pas          — IBPEventSink, event records
│   ├── bp.config.pas          — TBPSessionConfig record
│   └── bp.session.pas         — TBPSession engine (NextStep)
├── tests/
│   ├── test_frame.pas
│   ├── test_cram.pas
│   ├── test_crypt.pas
│   ├── test_des.pas
│   ├── test_plz.pas
│   ├── test_opt.pas
│   ├── test_session_outbound.pas
│   ├── test_session_inbound.pas
│   ├── vectors/               — captured traffic, one file per case
│   └── testutil.pas           — test harness helpers
└── units/                    — compiled output (.ppu/.o), per target
Description
FPC/FreePascal BinkP (FTS-1026/1027) library — Argus/binkd-compatible mailer protocol
Readme 780 KiB
Languages
Pascal 98.4%
Shell 1.6%