Skip to content

Add a server-side listener to the SMB transport layer (Refs #1068) - #1069

Open
p0dalirius wants to merge 1 commit into
mainfrom
enhancement-smb1-server-transport
Open

Add a server-side listener to the SMB transport layer (Refs #1068)#1069
p0dalirius wants to merge 1 commit into
mainfrom
enhancement-smb1-server-transport

Conversation

@p0dalirius

Copy link
Copy Markdown
Contributor

Summary

Commit 1 of 12 for #1068. The SMB transport layer had only a client side: Transport exposes Connect, and neither implementation could be built from an accepted connection, so nothing in the tree could listen for SMB. grep -rl 'net.Listen|\.Accept()' matched netbios/nbns, netbios/nbdgm and llmnr/server, and nothing under network/smb/.

This adds the accept side, keeping both transports interchangeable above it so the server layers in later commits do not care which one a connection arrived on.

Changes

  • tcp.NewTCPTransportFromConn and nbt.NewNBTTransportFromConn adopt an already-established connection, so the server side reuses the existing framing, MaxDirectTCPPayloadSize limit and read-deadline handling rather than duplicating them.
  • nbns.DecodeSessionServiceName decodes a second-level-encoded NetBIOS name — the inverse of the existing EncodeSessionServiceName. It returns the number of bytes consumed so a caller can decode the CALLED name and then the CALLING name that follows it, and it walks scope labels rather than assuming the default scope, bounding each by the 63-byte limit.
  • nbt.AcceptSession completes the RFC 1002 §4.3 handshake on an accepted connection, the counterpart of EstablishSession. A CALLED name addressed to a service other than the server service (0x20) and a name the endpoint does not serve are refused separately, and a wildcard CALLED name such as the *SMBSERVER convention is always served — mirroring what EstablishSession expects to receive and retry against.
  • transport.Listener, with ListenTCP and ListenNBT, yields a Transport whose transport-level handshake (where the transport has one) is already complete. A connection that fails the NetBIOS handshake is logged and discarded inside Accept rather than surfaced as an error, so one bad client cannot take the listener down. An address with no port binds the dialect default: 445 for Direct TCP, 139 for NetBIOS.

No existing behaviour changes: the additions are new symbols, and the client-side paths are untouched.

Wire format

Cross-checked against RFC 1002 §4.3:

Packet TYPE FLAGS LENGTH Trailer
SESSION REQUEST (§4.3.2) 0x81 0x00 variable CALLED name, then CALLING name
POSITIVE SESSION RESPONSE (§4.3.3) 0x82 0x00 0x0000 none
NEGATIVE SESSION RESPONSE (§4.3.4) 0x83 0x00 0x0001 1-byte ERROR_CODE

The error codes emitted (0x80 not listening on called name, 0x82 called name not present, 0x8F unspecified) are the existing netbios.NEGATIVE_SESSION_* constants.

Testing

go build ./..., go vet ./... and go test ./... are green across the repository.

New tests:

  • Name codec — round trip across name lengths, suffixes and a scoped name; the RFC 1001 *SMBSERVER known answer; the two-names-in-one-buffer case the SESSION REQUEST needs; and rejection of a bad length byte, a bad encoding character, an unterminated name, a truncated scope label and an oversize scope label.
  • Handshake — completes against the client side of the same implementation, with an SMB-message exchange in both directions afterwards to prove the session is usable; the wildcard and any-name policies; and refusal of an unserved CALLED name, a wrong service suffix, a wrong message type, an empty body, a malformed CALLED name and a missing CALLING name — asserting the exact response bytes where a response is sent.
  • Listeners — a bidirectional SMB-message round trip for each; that a failed NetBIOS handshake leaves the listener still serving the next client; that Close unblocks a blocked Accept; and that a portless address lands on the default port.

Notes

The remaining eleven commits for #1068 build on this: the server skeleton and dispatch loop come next.

The SMB transport layer had only a client side: Transport exposes Connect,
and neither implementation could be built from an accepted connection, so
nothing in the tree could listen for SMB.

Add the accept side, keeping both transports interchangeable above it:

- tcp.NewTCPTransportFromConn and nbt.NewNBTTransportFromConn adopt an
  already-established connection, so the server side of a connection reuses
  the existing framing, length limits and read-deadline handling.
- nbns.DecodeSessionServiceName decodes a second-level-encoded NetBIOS name,
  the inverse of EncodeSessionServiceName. It returns the byte count consumed
  so a caller can decode the CALLED name and then the CALLING name that
  follows it, and it walks scope labels rather than assuming the default
  scope, bounding each by the 63-byte limit.
- nbt.AcceptSession completes the RFC 1002 4.3 handshake on an accepted
  connection: it reads the SESSION REQUEST, decodes both names, and answers a
  POSITIVE SESSION RESPONSE (0x82, LENGTH 0) or a NEGATIVE SESSION RESPONSE
  (0x83, LENGTH 1) carrying the RFC 1002 4.3.4 error code. A CALLED name
  addressed to a service other than the server service (0x20) and a name the
  endpoint does not serve are refused separately, and a wildcard CALLED name
  such as the "*SMBSERVER" convention is always served, mirroring what
  EstablishSession expects to receive and retry against.
- transport.Listener with ListenTCP and ListenNBT yields a Transport whose
  transport-level handshake, where the transport has one, is already complete.
  A connection that fails the NetBIOS handshake is logged and discarded inside
  Accept instead of being surfaced, so one bad client cannot take the listener
  down. An address with no port binds the dialect default: 445 for Direct TCP,
  139 for NetBIOS.

Tests cover the name codec round trip across name lengths, suffixes and a
scoped name, and reject each malformed encoding; the handshake against the
client side of the same implementation, the wildcard and any-name policies,
and refusal of an unserved name, a wrong service suffix and malformed frames,
asserting the exact response bytes; and, for each listener, a bidirectional
SMB-message round trip, that a failed handshake leaves the listener serving
the next client, that Close unblocks Accept, and that a portless address
lands on the default port.
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.

1 participant