rcmd

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 23, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package rcmd serves the BSD rcmd protocol, the wire format behind rsh: the client connects to TCP port 514 from a reserved port, sends an ASCII stderr port number and three NUL-terminated strings (remote user, local user, command), and the server answers with a single zero byte before streaming the command's stdout on the connection. When the client named a stderr port, the server dials back to it for the stderr stream.

The package carries no command semantics: the caller's Handler decides what a command means. IRIX inst is the intended client, so instigator wires a constrained command table - never a shell.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Handler

type Handler func(req *Request) error

Handler executes one command. A returned error is reported to the client through the protocol's error byte and message.

type Request

type Request struct {
	RemoteUser string
	LocalUser  string
	Command    string
	Addr       netip.Addr

	// Stdout is the primary connection. Stderr is the dial-back
	// connection when the client asked for one, io.Discard otherwise.
	Stdout io.Writer
	Stderr io.Writer

	// Stdin is the rest of the primary connection after the command,
	// which a shell command (rsh exec /bin/sh) reads its command stream
	// from. It carries only what the client sends next, unbounded.
	Stdin io.Reader
}

Request is one accepted rcmd exchange.

type Server

type Server struct {
	Handler Handler

	// AllowIP filters clients; nil allows everyone.
	AllowIP func(netip.Addr) bool

	// AllowHighPorts disables the reserved-port check on the client's
	// source port. The check stays on in production - the miniroot's
	// rcmd client uses reserved ports - and off in unprivileged tests.
	AllowHighPorts bool

	// Logger, when set, receives leveled log output: DEBUG for every
	// connection and the parsed request/stderr dial-back detail, INFO
	// for each command run, WARN for a connection refused or malformed,
	// ERROR for a command the Handler refused or failed - a command
	// this side couldn't or wouldn't serve must show up here, not just
	// on the client's own stderr. A nil Logger is silent.
	Logger *logging.Logger

	// IdleTimeout bounds the time between reads on a connection: every
	// read - the request-head fields and, for a shell command, every
	// line a Handler pulls from Request.Stdin - resets it. Zero means
	// no timeout, unbounded wait, the historical behavior. Without this,
	// a shell session (rsh exec /bin/sh) blocks its handler goroutine
	// reading Stdin for as long as the client holds the connection open
	// and stays silent - stuck, hostile, or simply gone.
	IdleTimeout time.Duration

	// RejectHook, when set, is called when a connection is refused before
	// any command runs - a non-reserved source port or a client outside the
	// filter - with the client address and a short reason. It lets a caller
	// record a refusal the Handler never sees.
	RejectHook func(addr netip.Addr, reason string)
	// contains filtered or unexported fields
}

Server accepts rcmd connections.

func (*Server) Serve

func (s *Server) Serve(l net.Listener) error

Serve accepts connections on l (conventionally bound to :514) until l is closed.

func (*Server) Shutdown

func (s *Server) Shutdown(timeout time.Duration) bool

Shutdown closes every active connection - unblocking any session sitting on a Stdin read - and waits up to timeout for the handler goroutines to finish, so their final events are emitted before the caller finalizes. It returns false if the timeout elapsed with handlers still running.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL