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)
}
Output:
Index ¶
Examples ¶
Constants ¶
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.
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 ¶
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.
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") )
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") )
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 ¶
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)
}
Output:
func Open ¶
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)
}
Output:
func (*Conn) Close ¶
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 ¶
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)
}
}
Output:
func (*Conn) RemoteAddr ¶
RemoteAddr returns the server's network address.
type Option ¶
type Option func(*settings)
Option configures a Conn created by Dial or Open.
func WithDeadline ¶
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 ¶
WithDialTimeout sets the timeout for establishing the TCP connection. It has no effect on Open, which receives an already-connected socket.
func WithMaxCommandLen ¶
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"
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).