rcon

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 5, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package rcon implements the Source RCON protocol for administering dedicated game servers over TCP.

It is the low-level foundation of this module: a single authenticated connection (Conn) with explicit, one-command-at-a-time semantics. Higher level ergonomics (a default client, retries, sessions) live in the github.com/cbrgm/rcon/rconclient package, which is built on top of this one.

The zero value of Conn is not usable; obtain one with Dial or Open.

Example

Connect to a server, run a command, and print the response.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/cbrgm/rcon/rcon"
)

func main() {
	ctx := context.Background()

	conn, err := rcon.Dial(ctx, "127.0.0.1:25575", "password")
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	out, err := conn.Execute(ctx, "list")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(out)
}

Index

Examples

Constants

View Source
const (
	// DefaultDialTimeout bounds establishing the TCP connection in Dial.
	DefaultDialTimeout = 5 * time.Second
	// DefaultDeadline bounds each individual read and write on the connection.
	DefaultDeadline = 5 * time.Second
	// DefaultMaxCommandLen is the default upper bound on a command's length, a
	// safe margin under the RCON wire request cap. Zero means unlimited.
	DefaultMaxCommandLen = 1000
)

Default option values used when no override is supplied.

View Source
const (
	// MaxPayloadSize is the maximum RCON body payload per packet.
	MaxPayloadSize = 4096
	// HeaderSize is the size of the ID and Type fields together.
	HeaderSize = 8
	// PaddingSize is the two trailing NUL terminators.
	PaddingSize = 2
	// MinPacketSize is the smallest legal value of the Size field (empty body).
	MinPacketSize = HeaderSize + PaddingSize
)

Wire-format sizes, in bytes.

Variables

View Source
var (
	// ErrResponseTooShort means a frame declared a Size below MinPacketSize.
	ErrResponseTooShort = errors.New("rcon: response smaller than minimum packet")
	// ErrResponseTooLong means a frame declared a Size above the protocol max.
	ErrResponseTooLong = errors.New("rcon: response larger than maximum packet")
)

Sentinel errors returned by this package. Wrap-aware: test with errors.Is.

View Source
var (
	// ErrAuthFailed means the server rejected the password (auth response ID -1).
	ErrAuthFailed = errors.New("rcon: authentication failed")
	// ErrInvalidAuthResponse means the auth reply had an unexpected type.
	ErrInvalidAuthResponse = errors.New("rcon: unexpected auth response type")
)
View Source
var (
	// ErrCommandEmpty means Execute was called with an empty command.
	ErrCommandEmpty = errors.New("rcon: command is empty")
	// ErrCommandTooLong means the command exceeded the configured max length.
	ErrCommandTooLong = errors.New("rcon: command too long")
	// ErrResponseMismatch means a response packet's ID did not match the request.
	ErrResponseMismatch = errors.New("rcon: response id did not match request")
)
View Source
var ErrClosed = errors.New("rcon: connection closed")

ErrClosed is returned by operations on a closed Conn.

Functions

This section is empty.

Types

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn is a single authenticated RCON connection over one TCP socket.

Concurrent calls to Execute are serialized by an internal mutex, so a Conn is safe to share, but only one command is ever in flight (RCON has no request multiplexing). Close may be called concurrently to abort a blocked Execute: it takes no lock and closes the socket directly, unblocking the in-flight read.

func Dial

func Dial(ctx context.Context, address, password string, opts ...Option) (*Conn, error)

Dial connects to address over TCP, authenticates with password, and returns a ready Conn. A deadline carried by ctx bounds the dial and the auth handshake; cancellation of a deadline-less context is not observed once the dial completes.

Example (Timeout)

Dial accepts functional options, for example to tighten the dial and per-command timeouts.

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/cbrgm/rcon/rcon"
)

func main() {
	ctx := context.Background()

	conn, err := rcon.Dial(ctx, "127.0.0.1:25575", "password",
		rcon.WithDialTimeout(2*time.Second),
		rcon.WithDeadline(3*time.Second),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	out, err := conn.Execute(ctx, "status")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(out)
}

func Open

func Open(ctx context.Context, netConn net.Conn, password string, opts ...Option) (*Conn, error)

Open wraps an already-established net.Conn (a custom dialer, a TLS or SSH tunnel, or net.Pipe in tests), authenticates with password, and returns a ready Conn. A deadline carried by ctx bounds the auth handshake; cancellation of a deadline-less context is not observed.

Example

Open wraps a connection you established yourself, so you can reach the server through a proxy, a TLS tunnel, or a custom dialer instead of Dial's plain TCP.

package main

import (
	"context"
	"fmt"
	"log"
	"net"

	"github.com/cbrgm/rcon/rcon"
)

func main() {
	ctx := context.Background()

	netConn, err := net.Dial("tcp", "127.0.0.1:25575")
	if err != nil {
		log.Fatal(err)
	}

	conn, err := rcon.Open(ctx, netConn, "password")
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	out, err := conn.Execute(ctx, "list")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(out)
}

func (*Conn) Close

func (c *Conn) Close() error

Close closes the underlying connection. Subsequent Execute calls return ErrClosed. It is safe to call concurrently with Execute to abort a blocked round-trip: Close takes no lock (Execute holds mu for its entire blocking I/O), it just marks the Conn closed and closes the socket, which unblocks any in-flight read with an error. The socket close error is returned.

func (*Conn) Execute

func (c *Conn) Execute(ctx context.Context, command string) (string, error)

Execute sends command to the server and returns the response body. Concurrent calls are serialized. A deadline carried by ctx bounds the round-trip; cancellation of a deadline-less context is not observed mid-round-trip. Execute returns ErrClosed if the Conn has been closed.

Example

A single Conn runs many commands in sequence. Concurrent calls are safe but serialized, since RCON has no request multiplexing.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/cbrgm/rcon/rcon"
)

func main() {
	ctx := context.Background()

	conn, err := rcon.Dial(ctx, "127.0.0.1:25575", "password")
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	for _, cmd := range []string{"seed", "time query day", "list"} {
		out, err := conn.Execute(ctx, cmd)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(out)
	}
}

func (*Conn) LocalAddr

func (c *Conn) LocalAddr() net.Addr

LocalAddr returns the local network address.

func (*Conn) RemoteAddr

func (c *Conn) RemoteAddr() net.Addr

RemoteAddr returns the server's network address.

type Option

type Option func(*settings)

Option configures a Conn created by Dial or Open.

func WithDeadline

func WithDeadline(d time.Duration) Option

WithDeadline sets the per-operation read/write deadline on the connection. A value of 0 (or negative) disables the per-operation deadline, leaving each operation bounded only by any deadline carried on the caller's context.

func WithDialTimeout

func WithDialTimeout(d time.Duration) Option

WithDialTimeout sets the timeout for establishing the TCP connection. It has no effect on Open, which receives an already-connected socket.

func WithMaxCommandLen

func WithMaxCommandLen(n int) Option

WithMaxCommandLen caps the length of a command passed to Execute. Zero means no limit.

func WithSinglePacket

func WithSinglePacket() Option

WithSinglePacket disables multi-packet response reassembly, reading exactly one response packet per command. Use it only for servers that mishandle the empty-response terminator sentinel.

type Packet

type Packet struct {
	// ID is the request identifier, echoed by the server in the matching reply.
	ID int32
	// Type is the packet type.
	Type PacketType
	// Body is the ASCII payload (a command, a password, or a response chunk).
	Body string
}

Packet is a single RCON protocol frame.

Example

Packet is the wire frame. You only need it to speak the protocol yourself; Conn handles framing for you. Here a command request is encoded to bytes and decoded straight back.

package main

import (
	"bytes"
	"fmt"
	"log"

	"github.com/cbrgm/rcon/rcon"
)

func main() {
	var buf bytes.Buffer

	req := rcon.Packet{ID: 42, Type: rcon.TypeExecCommand, Body: "list"}
	if _, err := req.WriteTo(&buf); err != nil {
		log.Fatal(err)
	}

	var got rcon.Packet
	if _, err := got.ReadFrom(&buf); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("id=%d type=%d body=%q\n", got.ID, got.Type, got.Body)
}
Output:
id=42 type=2 body="list"

func (*Packet) ReadFrom

func (p *Packet) ReadFrom(r io.Reader) (int64, error)

ReadFrom decodes a single RCON frame from r into p. It implements io.ReaderFrom. The returned count includes the 4-byte size field. A frame whose declared size is out of range yields ErrResponseTooShort or ErrResponseTooLong without consuming the (untrusted) body.

func (Packet) WriteTo

func (p Packet) WriteTo(w io.Writer) (int64, error)

WriteTo encodes the packet as a single RCON frame and writes it to w. It implements io.WriterTo. The returned count includes the 4-byte size field.

type PacketType

type PacketType int32

PacketType is the Source RCON packet type field (a little-endian int32 on the wire).

const (
	// TypeResponseValue is a server response to an executed command, and the
	// type of the empty sentinel packet used to terminate multi-packet reads.
	TypeResponseValue PacketType = 0
	// TypeExecCommand is a client request to run a command.
	TypeExecCommand PacketType = 2
	// TypeAuthResponse is the server's reply to an authentication request.
	TypeAuthResponse PacketType = 2
	// TypeAuth is a client authentication request carrying the password.
	TypeAuth PacketType = 3
)

Source RCON packet types. Note that TypeAuthResponse and TypeExecCommand share the wire value 2; they are disambiguated by direction (client sends TypeExecCommand, the server replies with TypeAuthResponse during auth).

Jump to

Keyboard shortcuts

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