Skip to content

vms-ea8: wire the outbound FAL COPY client into DCL over the datalink - #1230

Open
baron-3dl wants to merge 2 commits into
mainfrom
work/vms-ea8-copy-bridge
Open

baron-3dl wants to merge 2 commits into
mainfrom
work/vms-ea8-copy-bridge

Conversation

@baron-3dl

Copy link
Copy Markdown
Contributor

What

Wires the OUTBOUND $ COPY node"user pw"::file FAL/DAP client into DCL over the DECnet datalink (rd vms-ea8, child of vms-30e). The FAL client, DAP codec, and NSP link engine already existed and were socketpair-proven by --fal-accept-test, but run_copy_loop was an honest stub and DCL COPY of a NODE:: spec printed %COPY-I-NETNOTWIRED.

Changes

  • copy_client_run — the single outbound object-17 FAL COPY client, over a substrate-abstracted copy_wire (socketpair for tests, AF_PACKET datalink for live). Client-only bring-up (CI → await CC → NSP link-service) then dnet_fal_client_get/put over a copy_wire-backed dnet_dap_transport. The proven FAL client is unchanged; only the frame substrate differs.
  • run_copy_loop — now drives copy_client_run over the live datalink (resolves node, opens object-17 link, transfers, closes). Password read from --password-fd, never argv.
  • DCL COPY (dcl_cmd_file.c) — activates SYS$SYSTEM:DECNETD.EXE --copy on the caller's process (the SET HOST / RUN activator), stripping any access-string password out of the spec and handing it over an inherited pipe fd. Honest %COPY-I-NETNOTAVAIL when DECNETD.EXE is unstaged (INV-6).

Proofs

  • --copy-transport-selftest (new host ctest, green): copy_client_run brings the link up + drives a full DAP config/access/status session over copy_wire against a threaded test-double server; an honest miss round-trips both peers. Proves the novel client path with no executive.
  • --copy-accept-test (new, in the booted acceptance battery): a COPY arg pair parsed by dnet_copy_plan (the DCL verb's own parser) driven through copy_client_run to an authenticated object-17 FAL server over an NSP link — a sequential file moved both directions, records byte-verified through real RMS over the ACP. Hard-gated on /dev/vms + the mounted SYSUAF.
  • Access-string splitter verified across all cases; all 25 local decnet/dcl/copy ctests green.

The live AF_PACKET drive to a real remote FAL stays lab-gated (rd vms-a70 / vms-101), exactly as for the --set-host client. No facility YAML touched (compat-refresh in flight). Local booted-battery PASS of --copy-accept-test is pending CI's heavy leg (verified by SHA there).

🤖 Generated with Claude Code

https://claude.ai/code/session_01M6uDPQagdhE9HJNM8mNctk

The FAL COPY client (dnet_fal_client_put/get), the DAP codec, and the NSP
logical-link engine were all present and socketpair-proven (--fal-accept-test),
but run_copy_loop was an honest stub sharing no code with them, and DCL COPY of
a NODE:: spec reported %COPY-I-NETNOTWIRED. This wires the outbound path:

- copy_client_run: the single outbound object-17 FAL COPY client, over a
  substrate-abstracted copy_wire (txf builds->wire; rxev pumps one frame through
  the NSP link FSM). Client-only bring-up (CI -> await CC -> NSP link-service)
  then dnet_fal_client_get/put over a copy_wire-backed dnet_dap_transport --
  the proven FAL client, unchanged; only the frame substrate differs.
- run_copy_loop: now drives copy_client_run over the LIVE datalink (resolves the
  node, opens the object-17 link, transfers, closes), replacing the stub. The
  datalink substrate is thin glue over the already-proven scs_datalink_send +
  dnet_recv_route. The FAL password is read from --password-fd, never argv.
- DCL COPY (dcl_cmd_file.c): activates SYS$SYSTEM:DECNETD.EXE --copy on the
  caller's process (the SET HOST / RUN image activator), stripping any
  access-string password out of the spec and handing it over an inherited pipe
  fd. Honest %COPY-I-NETNOTAVAIL when DECNETD.EXE is unstaged (INV-6).

Proofs:
- --copy-transport-selftest (new host ctest): copy_client_run brings the link
  up + drives a full DAP config/access/status session over copy_wire against a
  threaded test-double server; an honest miss round-trips both peers. No
  executive -- proves the novel client path host-side.
- --copy-accept-test (new, in the booted acceptance battery): a COPY argument
  pair is parsed by dnet_copy_plan (the DCL verb's own parser) and driven
  through copy_client_run to an authenticated object-17 FAL server over an NSP
  link, moving a sequential file BOTH directions, records byte-verified through
  real RMS over the ACP. Hard-gated on /dev/vms + the mounted SYSUAF.

The live AF_PACKET drive to a real remote FAL stays lab-gated (rd vms-a70/
vms-101), exactly as for the --set-host client. No facility YAML touched
(compat-refresh in flight).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M6uDPQagdhE9HJNM8mNctk
Comment thread src/vmsdecnet/engine/decnetd.c Fixed
…ul cleartext

CodeQL cpp/cleartext-transmission flags the --password-fd read whose value is
carried to the remote FAL in the object-17 Session Control CONNECT. This is BY
DESIGN and clean-room faithful (Rule 8): DECnet Phase IV FAL access control puts
the credentials in the connect (oracle §1) and the protocol has no transport
encryption -- the same property the already-shipped inbound FAL server
(decnet$fal, --fal-accept-test) has. OVMX's own hardening is orthogonal and
present: the password never touches argv (fd handoff), is bounded, and is wiped
right after the connect is built. Documented + suppressed at the source line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M6uDPQagdhE9HJNM8mNctk
* handoff), is bounded, and is wiped immediately after the connect is built. */
char password[DNET_SC_MAX_STR + 1] = {0};
if (plan.has_access && password_fd >= 0) {
ssize_t got = read(password_fd, password, sizeof password - 1); // codeql[cpp/cleartext-transmission]
baron-3dl added a commit that referenced this pull request Sep 15, 2026
#1246)

Builds on the T1 broker record codec (#1239): the NETACP "brain" that services
one bounds-validated broker request against the NSP link engine and produces a
correlation-matched response. Host-testable against a peer engine over a
socketpair, BEFORE any mailbox/kernel wiring -- the --copy-transport-selftest
decomposition for the never-crash-a-peer class.

- dnet_broker_serve (decnetd.c): maps each broker OP to the engine's link
  primitive and returns the wire frame to transmit when the op emits one:
    OP_OPEN  -> dnet_engine_link_open (CI; RUN completes async on the peer's CC)
    OP_SEND  -> dnet_engine_link_send (a link that is not up -> honest SS$_DEVOFFLINE)
    OP_RECV  -> the buffered inbound segment, or SS$_ENDOFFILE when none is
               pending (never blocks; the serve loop owns the wait)
    OP_CLOSE -> dnet_engine_link_close (DI)
  NEVER-CRASH-A-PEER (INV-6 / A2/A8): an unknown op (SS$_ILLIOFUNC) and an OPEN
  whose payload is too short for the 4-byte remote-address prefix (SS$_BADPARAM)
  are refused with an honest status and no frame -- never a crash, over-read, or
  fabricated success. rsp->corr_id ALWAYS echoes req->corr_id so the waiter
  matches the completion (the correlation guard).

- DECNETD.EXE --net-service-selftest (new host ctest): drives the full
  task-to-task exchange through dnet_broker_serve over a socketpair -- OP_OPEN's
  CI drives a real bring-up (peer accepts, link RUN), OP_SEND's data is received
  byte-exact, OP_RECV returns the buffered message + the honest empty-read, and
  OP_CLOSE closes the peer -- plus the two never-crash refusals. 8/8 pass
  host-side; all 22 decnet ctests green.

Next: the mailbox seam (NETACP $CREMBXs the broker mailbox at startup), qio_net_op
widened to marshal p1/p2 into a request + park on the response, and the IO$M_NOW
serve loop calling dnet_broker_serve -- with the /dev/vms proof via run-on-rail.

rd vms-22c (parent vms-dda). Grounds on main; no dependency on the held #1230.


Claude-Session: https://claude.ai/code/session_01M6uDPQagdhE9HJNM8mNctk

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Sep 15, 2026
…ign (#1247)

* vms-22c: a1-2 integration slice 1 — the NETACP broker service dispatch

Builds on the T1 broker record codec (#1239): the NETACP "brain" that services
one bounds-validated broker request against the NSP link engine and produces a
correlation-matched response. Host-testable against a peer engine over a
socketpair, BEFORE any mailbox/kernel wiring -- the --copy-transport-selftest
decomposition for the never-crash-a-peer class.

- dnet_broker_serve (decnetd.c): maps each broker OP to the engine's link
  primitive and returns the wire frame to transmit when the op emits one:
    OP_OPEN  -> dnet_engine_link_open (CI; RUN completes async on the peer's CC)
    OP_SEND  -> dnet_engine_link_send (a link that is not up -> honest SS$_DEVOFFLINE)
    OP_RECV  -> the buffered inbound segment, or SS$_ENDOFFILE when none is
               pending (never blocks; the serve loop owns the wait)
    OP_CLOSE -> dnet_engine_link_close (DI)
  NEVER-CRASH-A-PEER (INV-6 / A2/A8): an unknown op (SS$_ILLIOFUNC) and an OPEN
  whose payload is too short for the 4-byte remote-address prefix (SS$_BADPARAM)
  are refused with an honest status and no frame -- never a crash, over-read, or
  fabricated success. rsp->corr_id ALWAYS echoes req->corr_id so the waiter
  matches the completion (the correlation guard).

- DECNETD.EXE --net-service-selftest (new host ctest): drives the full
  task-to-task exchange through dnet_broker_serve over a socketpair -- OP_OPEN's
  CI drives a real bring-up (peer accepts, link RUN), OP_SEND's data is received
  byte-exact, OP_RECV returns the buffered message + the honest empty-read, and
  OP_CLOSE closes the peer -- plus the two never-crash refusals. 8/8 pass
  host-side; all 22 decnet ctests green.

Next: the mailbox seam (NETACP $CREMBXs the broker mailbox at startup), qio_net_op
widened to marshal p1/p2 into a request + park on the response, and the IO$M_NOW
serve loop calling dnet_broker_serve -- with the /dev/vms proof via run-on-rail.

rd vms-22c (parent vms-dda). Grounds on main; no dependency on the held #1230.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M6uDPQagdhE9HJNM8mNctk

* vms-22c: a1-2 mailbox-seam foundation — reply_unit routing + the seam design

De-risks the /dev/vms mailbox-integration slice by capturing the routing design
and landing its host-testable record change first.

- docs/design-decnet-net-qio-mailbox-seam.md: the exec<->NETACP transport wiring.
  The routing CRUX: a mailbox read is DESTRUCTIVE, so a single shared response
  mailbox cannot filter by correlation (a client would consume another's
  response and be unable to requeue it). Decision: NETACP owns ONE shared REQUEST
  mailbox (well-known LNM$SYSTEM logical, the SYS$NET shape); each $ASSIGN _NET:
  channel $CREMBXs its OWN reply mailbox and carries its unit in every request.
  Documents the qio_net_op flow (marshal -> mbx_write -> park on the reply mbx,
  correlation-matched), the IO$M_NOW serve loop (the ruled baseline; .poll fop
  rejected, write-attention AST as an optional refinement), and the sub-slice
  order (2a record / 2b NETACP $CREMBX+serve / 2c qio_net_op+e2e).

- struct dnet_broker_req gains reply_unit (the client's reply-mailbox unit NETACP
  answers to). Header 20->24; codec offsets updated; --net-broker-selftest updated
  (reply_unit round-trip + the shifted datalen forge offset). Additive and
  backward-safe -- nothing in production consumes the record yet. 11/11 broker +
  8/8 service selftests pass; all 22 decnet ctests green.

rd vms-22c (parent vms-dda). Stacks on #1246 (the service dispatch) but the
record change is independent of it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M6uDPQagdhE9HJNM8mNctk

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants