Conversation
Five new optional subsystems with the MacIP-style build-tag gating pattern (`-tags ipx`, `-tags netbeui`, `-tags netbios`, `-tags smb`, `-tags shortname`, all rolled up under `-tags all`): - protocol/ipx, port/ipx, router/ipx, service/ipx: real IPX header encode/decode, port subscribing to all three Ethernet framings (Ethernet II 0x8137, raw 802.3 0x00FF, 802.2 LLC SAP 0xE0), socket router, RIP/SAP services registering sockets 0x0453/0x0452. - protocol/netbeui, port/netbeui: real NBF frame encode/decode (0xEFFF delimiter, 16/16 byte name pair), port building full Ethernet+LLC+NBF outbound frames and stripping link headers inbound. - protocol/netbios, service/netbios + over_ipx/over_netbeui/over_tcp: multi-transport NetBIOS service with per-transport CommandHandler fan-out and ordered Start/Stop with rollback on transport failure. - service/smb: SMB 1.0 server stub with NetBIOS CommandHandler implementation, VFS event-bus subscription for cross-protocol cache invalidation, anonymous-only authenticator, share config type matching `[SMB.Volumes.<name>]` TOML layout. - pkg/shortname: shared 8.3 mapping primitive (Mapper + Store + in-memory backing) used by SMB and AFP. cnid.Store now embeds shortname.Store so per-volume CNID databases also carry the shortname bindings without a circular import. - pkg/vfs: process-wide event bus (Create/Rename/Modify/Delete/Attr) with per-subscriber bounded channels and origin-tag loop avoidance, enabling future fsnotify integration and the cross-protocol service bus for file mutations. - port/rawlink/mux.go: FrameFilter / FrameConsumer types and the optional MultiplexableLink capability that IPX and NetBEUI ports consume to demux by EtherType / LLC SAP. cmd/classicstack: hook trios for each subsystem mirroring MacIP/AFP patterns; main.go now drives Start/Stop on each independently of the AppleTalk router (IPX → NetBEUI → NetBIOS → SMB), and parses 24 new CLI flags plus the matching TOML tables. server.toml.example gets commented-out templates for every new section. All five subsystems are stubs: send paths return ErrNotImplemented, inbound dispatch is logged-and-dropped, and no rawlink wiring is plumbed through main.go yet. Default builds remain identical in feature surface; tagged builds compile cleanly and unit tests cover encode/decode round-trips, lifecycle rollback, and bus loop avoidance. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Decouples AFP's local-FS implementation so SMB and any future service
can build a local-backed share by calling vfs.New("local_fs", ...)
without dragging in a single line of AFP. The host-filesystem
operations (ReadDir/Stat/CreateFile/OpenFile/Remove/Rename/DiskUsage)
now live in pkg/vfs.LocalFileSystem registered as the canonical
"local_fs" backend; AFP keeps the AFP-specific extensions
(CatSearch / ChildCount / DirAttributes / IsReadOnly /
SupportsCatSearch) on a thin wrapper that delegates the universal
methods to a vfs.FileSystem held through the interface, never the
concrete type.
Disk-usage syscall shims (statfs / GetDiskFreeSpaceEx) move with the
backend into pkg/vfs/disk_usage_{windows,other}.go so future backends
inherit them.
afp.File becomes a type alias for vfs.File (structurally identical
contracts) so handles flow across the boundary without an adapter.
The AFP wrapper's zero value remains usable: the embedded backend is
created lazily on first call so the ~50 existing test sites that
construct &LocalFileSystem{} directly continue to work.
All documented build combinations (default, ipx, netbeui, netbios,
smb, shortname, afp, "afp macgarden", "macip ipx netbeui netbios smb
shortname afp macgarden", all) compile clean. Full test suite
green at default and -tags all.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The stub commit had IPX and NetBEUI subscribing to a shared rawlink mux via rawlink.RegisterConsumer. That model assumes one read loop per interface fanning frames out to multiple consumers, with a generic FrameFilter struct synthesizing a union BPF expression. For ClassicStack's actual filter set — EtherType 0x809B for EtherTalk, 0x8137 / raw 802.3 / LLC SAP 0xE0 for IPX, LLC SAP 0xF0 for NetBEUI — the filters are non-overlapping, so the kernel never duplicates a frame across handles. The native model used by EtherTalk and MacIP today (one pcap handle per protocol, each with its own kernel BPF filter, each with its own read goroutine) is strictly cheaper and lets each protocol express its filter natively instead of through a generic struct that struggles with clauses like AARP's `(ether proto 0x806 and arp[2:2] == 0x80F3)`. Changes: - port/rawlink: remove FrameFilter, FrameConsumer, MultiplexableLink, and the RegisterConsumer helper entirely. The rawlink surface is now just RawLink (read/write/close), FilterableLink (BPF push), and MediumReporter (medium hint). port/rawlink/mux.go is deleted. - port/ipx/port.go: rewritten to own its read loop, push the IPX BPF filter via FilterableLink.SetFilter, and demux Ethernet II / raw 802.3 / LLC framings in software (one handle covers all three via the kernel filter; the second-level discriminator is a couple of bytes of arithmetic). Adds Start()/Stop() to the Port interface for explicit lifecycle. - port/netbeui/port.go: same shape as IPX — own read loop, push the NetBEUI BPF filter for LLC DSAP/SSAP=0xF0 with UI control byte, strip the 14+3 byte link header inbound, prepend it outbound. - port/rawlink/pcap.go: adds DefaultIPXConfig and DefaultNetBEUIConfig helpers shaped like DefaultEtherTalkConfig (promiscuous, immediate mode, 250ms timeout). - cmd/classicstack/ipx_enabled.go and netbeui_enabled.go: open their own pcap rawlinks via OpenPcap when an interface is configured, call port.Start in the hook's Start, port.Stop in the hook's Stop. IPX framing is parsed from the operator-facing string into the Framing constant. - main.go drops the dead Rawlink: nil arguments and lets each hook open its own handle. Tests: port-level read-loop tests with a synthetic channel-backed RawLink confirm IPX decodes Ethernet II / raw 802.3 / LLC framings, NetBEUI strips the LLC header and decodes the NBF body, and Send on both ports produces correctly framed Ethernet bytes. All build combinations (default, ipx, netbeui, ipx+netbeui+netbios+ smb, all) compile clean. Full test suite green at default and -tags all. Zero references to the removed mux surface remain in the tree. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Foundation for IPX live traffic. The router now holds the network number and node ID for the process, fills outbound source addresses on the way through Send, and discards inbound datagrams that aren't addressed to us (or to a broadcast address). None of this is visible to clients yet — it's the substrate that RIP and SAP need before either can do anything useful. router/ipx changes: - Router gains SetIdentity / Network / Node, plus a default network number routeripx.DefaultNetwork = 00000001 used when the operator hasn't configured one. Single-segment deployments work out of the box; multi-segment must configure explicitly. - Send now fills SrcNet and SrcNode (when zero) before forwarding to the attached port. Pre-set source fields are preserved so a future forwarding path can still override them. - Inbound enforces addressed-to-us: DstNet must match ours (or be zero, the "local segment, unknown" sentinel that name-claim broadcasts use), and DstNode must be ours or the all-ones broadcast. Frames that survive the kernel pcap filter but aren't for us are dropped silently. - BroadcastNode = FF:FF:FF:FF:FF:FF exported for SAP/RIP/NB-IPX to use when constructing broadcasts. cmd/classicstack/ipx_enabled changes: - parseIPXNetwork accepts an 8-hex-digit network number with an optional 0x prefix; empty input falls back to DefaultNetwork. - resolveIPXNodeFromInterface reads the host interface MAC via rawlink.DetectHostMACForPcapInterface and uses it as the IPX node ID — the standard Ethernet-IPX convention. Failure is a warning, not fatal; the router stays usable with a zero node for tests and loopback. - wireIPX calls router.SetIdentity once it knows both, and logs the resolved network/node at info level so operators can confirm what the router will announce. Tests: - router_test.go covers the address filter (us, broadcast, zero network, foreign network, foreign node, unregistered socket), the source-fill (zero fields filled, pre-set fields preserved, no-port error), and the duplicate-socket guard. - ipx_enabled_test.go covers parseIPXNetwork happy paths and rejection of invalid input. All builds (default, ipx, ipx+netbeui+netbios+smb, all) clean. Full test suite green at default and -tags all. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
We are not a router — we don't forward traffic and we maintain no table beyond our own single network. But to be a well-behaved IPX node we need to: 1. Respond to RIP requests so other nodes learn our network number. 2. Periodically broadcast our network so other nodes don't time us out of their tables. This commit lands both, with sensible defaults and a clock injection point so the broadcaster can be tested without sleeping in real time. service/ipx/rip_packet.go (new): - RIPPacket / RIPEntry types and EncodeRIP / DecodeRIP for the wire format documented in RFC 1551 / Novell IPX RIP. - RIPRequest / RIPResponse operation constants, RIPNetworkAny (0xFFFFFFFF) wildcard, RIPHopUnreachable (16) sentinel. - Decoder ignores trailing pad bytes that don't form a complete 8-byte entry, since IPX frames are commonly padded to the 60-byte Ethernet minimum. service/ipx/rip.go (rewrite): - HandleDatagram parses the RIP body. Requests get a unicast response to the requester; responses from other nodes are ignored (we don't maintain a routing table to update). - Wildcard requests (no entries, or any entry with network=0xFFFFFFFF) match: we respond with our own network as hops=1, ticks=1. - Request-with-specific-network only matches when the network is ours; otherwise we say nothing. - Periodic broadcaster goroutine emits an unsolicited RIP response for our network every Period (default 60s) to the broadcast node on socket 0x0453. First broadcast goes out immediately on Start so the segment learns about us without waiting a full period. - Outbound goes through router.Send so SrcNet/SrcNode are filled from the router's identity automatically. - Stop cancels the broadcaster context and waits for the loop to exit. Tests: - rip_packet_test: round-trip, minimal request, short-input rejection, trailing-pad tolerance. - rip_test: wildcard request gets answered, unknown-network request doesn't, RIP responses from other nodes are silently ignored, periodic broadcast cadence verified via a synthetic clock that closes a channel on demand instead of waiting 60s. All builds (default, ipx, ipx+netbeui+netbios+smb, all) clean. Full test suite green at default and -tags all. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…t 0x0452 SAP is the announcement layer that makes a server visible in NetWare-aware clients' browse lists. Without it nothing finds us; with it we appear as soon as a client running IPX/SPX is on the segment. Higher layers (NetBIOS-over-IPX, when it claims a name) call Register to publish an advertisement. The SAP service holds the local registry, replies to inbound queries with matching entries, and broadcasts the registry every 60 seconds so clients pick us up without having to ask. service/ipx/sap_packet.go (new): - SAPPacket / SAPEntry types, EncodeSAP / DecodeSAP for the wire format documented in the NetWare protocol spec. - Operation codes: SAPGeneralQuery (1), SAPGeneralResponse (2), SAPNearestQuery (3), SAPNearestResponse (4). - Service-type constants: 0x0640 NetBIOS, 0x0004 file server, 0xFFFF wildcard. - 64-byte fixed-width entry (2-byte type + 48-byte zero-padded name + 4-byte network + 6-byte node + 2-byte socket + 2-byte hops). Encoder truncates names longer than 47 bytes; decoder trims trailing nulls. - SAPMaxEntriesPerPacket = 7 keeps a response under the 576-byte IPX MTU after the 30-byte IPX header and 2-byte op. service/ipx/sap.go (rewrite): - SAPService.Register fills Network/Node from the router's identity (and Hops=1) when the caller leaves them zero, so most local advertisements just say "this server, on this socket of mine". Returns a cancel func that removes the entry. - HandleDatagram dispatches: queries get a unicast response carrying matching local entries; responses from other agents are ignored. Wildcard (0xFFFF) service-type matches every registered entry; specific types match exact-equal. - SAPNearestQuery returns at most one entry (the first match) — semantics expected by Win9x's "Find Server" dialog. - Periodic broadcaster emits the registry to socket 0x0452 at the broadcast node every 60s (DefaultSAPPeriod). Splits across multiple packets when the registry exceeds 7 entries. First broadcast goes out immediately on Start. - Empty registry means no broadcast. Tests: - sap_packet_test: query and response round-trips; rejects oversized response; truncates long names; rejects truncated query bodies. - sap_test: Register fills identity from router (and respects pre-set fields); Cancel removes entries; specific-type and wildcard query handling; nearest-query returns one entry; empty-match query produces no response; periodic broadcast cadence verified via the synthetic-clock pattern from RIP. The hook already starts both RIP and SAP from Phase 1, so this commit is wire-visible the moment cfg.IPX.enabled is true on a configured interface — though no service is registered yet (that lands in Phase 5 when NetBIOS-over-IPX claims its name). All builds clean. Full test suite green at default and -tags all. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lays the wire-format foundation for NetBIOS-over-IPX (NWLink). The
NB-IPX protocol uses two distinct IPX packet types depending on
purpose:
- IPX type 20 ("NetBIOS broadcast / forwarding") for name service
operations (claim, query, in-conflict). Travels broadcast and
may traverse up to 8 routers.
- IPX type 4 ("Packet Exchange Protocol") on socket 0x0455 for
session-layer traffic. Carries the 16-byte NB-IPX session
header below.
protocol/netbios/netbios.go:
- Name (16-byte) gains structured construction. NewName uppercases,
truncates to 15 bytes, space-pads, and sets the type byte. Type
constants: 0x00 workstation, 0x20 file-server (SMB), 0x1E group.
- Name.String trims trailing spaces; Name.Type returns byte 15.
protocol/netbios/nbipx.go (new):
- NBIPXSessionHeader: the 16-byte session-family header carrying
ConnCtrlFlag, DataStreamType, SourceConnID, DestConnID, SendSeq,
TotalDataLen, Offset, DataLen, ConnCtrlByte. Encode/Decode round-
trip in big-endian.
- DataStreamType constants: NBIPXFindName, NBIPXNameRecognized,
NBIPXSessionInit/Confirm/End/EndAck, NBIPXStatusQuery/Response,
NBIPXDataAck/OnlyLast/FirstMiddle.
- ConnCtrlFlag bits: SYS, ACK, ATT, EOM.
- IPXTypeNetBIOS (0x14) and IPXTypePEP (0x04) constants name the
two IPX packet types NB-IPX uses.
- NBIPXNameServicePacket is the 16-byte body for type-20 broadcasts
carrying just a NetBIOS name. The function (claim vs query vs
conflict) is inferred from context, not from a wire opcode —
matches what NWLink clients actually emit.
Tests:
- NewName padding, uppercasing, truncation behaviour.
- Session header round-trip and short-input rejection.
- Name service body round-trip and short-input rejection.
The session machine (Phase 5C-E) and the name-claim broadcaster
with retry (Phase 5B) build on this codec.
All builds clean. Full test suite green at default and -tags all.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Implements the NetBIOS-over-IPX name-claim handshake. On Start the transport broadcasts a type-20 IPX FindName carrying our chosen NetBIOS name (16-byte padded form, type 0x20 file-server) at 500ms × 6 cadence — the same retry shape NWLink and Win9x clients use. If silence reigns, we own the name and register it with SAP under SAPServiceTypeNetBIOS (0x0640) so other nodes browsing SAP discover us. If any node objects (sends back a type-20 packet carrying our name), we abort the claim before all retries lapse and leave SAP unregistered. This is the moment IPX-side discovery starts working on the wire. A Win9x client running IPX/SPX with NetBIOS-over-IPX enabled should see CLASSICSTACK in its browse list once we land here. SMB won't *talk* to that name until the session machine arrives in Phase 5C, but the segment-level visibility is real. service/netbios/over_ipx changes: - NewTransport(r, sap, name) replaces NewTransport(r). The SAP argument is a SAPRegistrar interface (one method, Register), so unit tests can substitute a fake without dragging the full SAP agent in. - claimAndAdvertise goroutine runs the retry loop, sleeping via the injected sleep func (synthetic clock in tests). On uncontested completion it calls SAP.Register and stores the cancel func for Stop to invoke. - HandleDatagram now dispatches by the IPX packet-type field: type 20 routes to handleNameService (which checks for objections during a pending claim); type 4 (PEP) is reserved for the session machine in Phase 5C and currently dropped. - handleNameService filters out our own loopback broadcasts (matching source net+node against the router's identity) before treating an inbound name-match as an objection — pcap on some drivers delivers our own broadcasts back to us. - Stop unregisters the SAP entry via the stored cancel func. - Empty (zero) name skips the claim entirely, useful for tests that want only the socket-level transport. cmd/classicstack changes: - IPXHook gains SAP() returning the SAP service so wireNetBIOS can pass it through to over_ipx.NewTransport. - ipx_disabled.go's stub satisfies the new interface with a nil SAPService. - wireNetBIOS builds the 16-byte NetBIOS name from cfg.NetBIOSServerName via netbiosproto.NewName(... , NameTypeFileServer) and threads it into NewTransport. Skips the IPX transport when SAP is unavailable (e.g. binary built without -tags ipx). Tests (service/netbios/over_ipx/transport_test.go): - Uncontested claim: synthetic clock drives the retry loop; three FindName broadcasts go out, then SAP receives one Register call carrying our name, type 0x0640, socket 0x0455. Every broadcast is type-20 to the broadcast node on socket 0x0455. - Contested claim: an inbound type-20 packet from a foreign source carrying our name triggers an objection; the goroutine exits before SAP.Register fires. - Self-loopback: an inbound type-20 packet from our own net+node carrying our name is ignored, the claim still succeeds. - Stop after register: the SAP cancel func is invoked exactly once. - Empty name skips the claim entirely (no broadcasts, no SAP). All builds (default, ipx, netbios, ipx+netbios, all) clean. Full test suite green at default and -tags all. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
A live run of -tags all with [IPX] enabled but no interface set produced "ipx: no ports attached" errors from RIP and the NB-IPX name-claim broadcaster: the IPX router's identity was set but no port had been attached. The intended deployment runs every protocol on the same physical NIC, so blank IPX/NetBEUI interface should reuse [EtherTalk]'s. cmd/classicstack/main.go now resolves the IPX and NetBEUI interfaces before calling wireIPX/wireNetBEUI: if blank and EtherTalk has a device, fall back to EtherTalk's, log it at info level so it's visible in the startup banner. Operators who want a different NIC still set [IPX] interface = "..." explicitly. cmd/classicstack/smb_shares.go: the loader was already reading [SMB.Volumes.<key>] (the documented spelling) but the example file and most live configs in the wild used [SMB.Shares.<key>]. Accept both: prefer Volumes when present, fall through to Shares with a deprecation warning. Future commits may drop the alias. cmd/classicstack/ipx_enabled.go: the startup banner said "RIP+SAP registered (stub)" — left over from before Phases 1-3 landed real RIP and SAP. Renamed to "RIP+SAP active". server.toml.example: clarify that blank [IPX]/[NetBEUI] interface reuses [EtherTalk] device, and document the 8-hex-digit format and 00000001 default for internal_network. All builds clean. cmd/classicstack tests green at -tags all. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Project shipped under the ClassicStack brand; matching the icon filenames keeps the asset set consistent with the binary name. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wire the shortname.Mapper into AFP path encoding so 8.3 names are exposed when UseShortnames is enabled, surface Get/LookupShort/Put on the CNID store interface (stub backend for the no-sqlite build), and add a CNID subscriber that listens on vfs.DefaultBus for rename/delete events from non-AFP origins. Refresh the bundled Sample Volume CNID DB to match. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add an IPX capture path (Capture.ipx in TOML, plumbed via IPXConfig.CapturePath)
that writes raw IPX frames to a pcap with DLT_EN10MB so wireshark can
decode them alongside the existing LocalTalk and EtherTalk captures. The
router gains tx/rx Debug lines and explains drops by reason ("network"
vs "node"), and DefaultNetwork is changed to 0.0.0.0 so a fresh
ClassicStack appears on the same "unknown" segment Win98/NWLink uses
before a NetWare assignment.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Build out the NetBEUI command set (protocol/netbeui/commands.go) and grow the over_netbeui transport with a real name table and session layer so NB-frame name claims and session establishment are handled end-to-end. Round out NB-IPX transport coverage and protocol-level decode/encode tests, and have the NetBEUI port carry a configurable source MAC so the NetBEUI transport can announce its own identity. NetBIOS service.go gains the SetCommandHandler/datagram glue SMB needs as a tenant. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Expand the SMB service into a working SMB1 server with shared session state (state.go), wire it into NetBIOS for NBT-bound transports, and add a direct IPX transport (service/smb/over_ipx_direct) bound to socket 0x0550 for DOS clients that speak SMB straight over IPX. On the config side, make SMB the source of truth for the computer/workgroup identity and have NetBIOS inherit those values via normalizeSMBIdentity so operators do not have to set them in two places. main.go threads the IPX hook into wireSMB and the new IPX capture path through wireIPX. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Turn on the new SMB-over-IPX path in the operator's server.toml (IPX interface, NetBIOS over ipx transport, an SMB share, and an IPX pcap), trim the redundant netbios server_name/workgroup hints from the example template now that SMB is the identity source, and check in the SMB1 VFS MVP planning prompt under .github/prompts/. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Drop the Microsoft Open Specification PDFs/Word originals for MS-SMB, MS-CIFS, and MS-BRWS into spec/ alongside markdown extracts, plus an IEEE 802 framing reference. These are the authoritative sources the SMB1 server is being implemented against. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add .gitattributes so text files (Go, markdown, TOML, JSON, YAML, shell) are stored and checked out as LF on every platform, with .bat/.cmd/.ps1 keeping CRLF and binary asset extensions explicitly marked. Run git add --renormalize across the tree to refresh the few files whose indexed copy still had CRLF or stray gofmt drift, so subsequent diffs no longer light up with "LF will be replaced by CRLF" warnings. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Drop inbound frames that originate from the server's own MAC address (Ethernet) or node address (LocalTalk). This prevents OS-level pcap loopback echoes from being reprocessed as inbound requests, which was causing duplicate SMB responses. - ethernetBridgeAdapter: filter frames where source MAC matches hostMAC or virtualMAC (server's own address) - wifiBridgeAdapter: apply same filtering for WiFi bridge mode - localtalk/Port: filter LLAP frames where SourceNode matches the port's own node address Fixes duplicate response issue identified in ipx.pcap where each SMB request was generating 2-20+ identical responses.
Always send exactly one response per ECHO request with SequenceNumber=1, regardless of the EchoCount field in the request. This is because our architecture only supports one response per handler call. Clients that want multiple echoes should send multiple ECHO requests. This matches common SMB server behavior and avoids the complexity of supporting the multi-response case through the current architecture.
Re-run gofmt across a handful of files where struct field/const alignment had drifted and two over_ipx_direct files carried a trailing blank line. No behavior change. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Set Capabilities = CAP_NT_SMBS | CAP_STATUS32 so clients know the server supports NT-style SMB commands and 32-bit NT status codes. Previously the field was zero which could cause some clients to fall back to older, less compatible code paths. Also populate the SystemTime fields in the response with the current UTC time expressed as a Windows FILETIME (100-ns intervals since 1 Jan 1601) instead of the previous all-zeros value. Adds constants: capNTSMBs, capStatus32, windowsFiletimeOffset, smbStatusBadNetworkName, ipcShareName, ipcShareIdx (the last three are used by the following step). Refs MS-CIFS §2.2.4.52.2
…own shares Previously handleTreeConnectAndX silently fell back to returning TID=1 with service IPC for any request that could not be resolved (no share name, or share not in config). Classic clients depend on accurate error responses to detect mounts that did not succeed. Changes: - IPC$ is now recognised as a virtual share that is always present. A dynamic TID is allocated (same pool as disk shares) and the response carries service type IPC. - Any share name that is neither IPC$ nor present in the server's share list now returns STATUS_BAD_NETWORK_NAME (0xC00000CC), per MS-CIFS ss3.3.5.45. - A malformed request with no parseable share path also returns STATUS_BAD_NETWORK_NAME. - Adds buildTreeConnectResponseForIPC; the old silent fallback is removed. Tests: - Updated makeTreeConnectPayload / TestHandleSessionContextTreeConnectReturnsIPC to send a valid UNC IPC$ path and verify service = IPC. - Added TestHandleSessionContextTreeConnectUnknownShareReturnsError. Refs MS-CIFS ss3.3.5.45
Add a NetBEUI capture path (Capture.netbeui in TOML, plumbed via NetBEUIConfig.CapturePath) symmetric with the existing IPX/EtherTalk capture wiring, drop the "(stub)" tag now that frames flow end-to-end, and turn the NetBEUI transport on in the operator's server.toml so NetBIOS-over-NetBEUI sits alongside NetBIOS-over-IPX. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Hash and stash every frame we send through the dedupe ring before calling WriteFrame, so any kernel-level loopback copy that comes back through readLoop is filtered by isDuplicateFrame. Without this our own frames were being captured (and decoded) twice on Windows hosts where the loopback echo always surfaces. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Per [MS-CIFS] 2.2.3.1 and 2.2.1.6.4, connectionless SMB transports carry a per-client CID and a per-request SequenceNumber inside the SMB header SecurityFeatures field. Allocate a CID on NEGOTIATE keyed on the IPX (network, node) endpoint, reuse it on every later command from that client, and mirror the client's SequenceNumber back on each response so the redirector can pair requests to replies. Reserved CID values 0x0000 and 0xFFFF are skipped. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Land the work that took us from a NEGOTIATE-only stub to working share enumeration, directory enumeration, and read-only file access from real Win9x/NT clients. Path resolution: every share-relative SMB path is now joined to the share's host root and case-folded against the underlying directory via resolveExistingPath, so client paths like "\Public\AUTOEXEC.BAT" resolve correctly even when the on-disk name differs in case or trailing-dot handling. Trans2: handle SMB_COM_TRANSACTION2 with FIND_FIRST2/FIND_NEXT2 at info level FILE_BOTH_DIR (0x0104), backed by a per-connection search table; handle FIND_CLOSE2 to release entries; handle the legacy SMB_COM_QUERY_INFORMATION (0x08) so Win9x browsing keeps working pre-Trans2. File I/O: implement SMB_COM_READ (0x0A), SMB_COM_SEEK (0x12), and fold a closeFID helper out of handleClose so AndX chains can reuse it. OpenAndX now honors openFunction's open-only bit, picks grantedAccess/action from the actual outcome, and pads ReadAndX data to a 2-byte boundary the way Samba does. Reject SMB_COM_READ_MPX with ERRSRV/ERRuseSTD so clients that ignore our cleared CAP_MPX_MODE flag fall back to plain SMB_COM_READ (mirrors Samba's reply_readbmpx in source3/smbd/reply.c). LockingAndX: walk AndX chains, supporting SMB_COM_CLOSE as the next command — clients commonly send LOCKING_ANDX,CLOSE in one PDU. Wire-error mapping: when the client did not set FLAGS2_NT_STATUS, translate our internal NTSTATUS-shaped codes back into the matching ERRDOS/ERRSRV pair via toWireErrorStatus, so DOS-era redirectors get the errors they expect (ERRbadfile, ERRnoaccess, ERRuseSTD, etc.). state.go gains fileHandle.offset to back SEEK; constants.go adds CommandSeek and CommandReadMPX; session_dispatch routes the new opcodes; server_test covers the new behaviors end-to-end. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR aims to add protocol support for IPX, NetBeui + NetBIOS + SMB.
It's a work in progress.
Current state:
Some other changes: