Skip to content

server.internal.network

Constants #

const compression_flate = u8(0x00)

A NetherNet message carries the batch on its own: the first byte is the compression marker below, with no game packet header in front of it.

const compression_none = u8(0xff)
const max_compressed_batch = 2 * 1024 * 1024 // wire bytes accepted per read

Inbound frame bounds - a malformed or hostile peer must never drive unbounded work. All values are hard limits; a violation aborts decode and the caller disconnects the peer. Sized generously above vanilla traffic so legitimate clients are unaffected.

const max_decompressed_batch = 8 * 1024 * 1024 // guards against decompression bombs
const max_packets_per_batch = 512
const max_single_packet = 2 * 1024 * 1024
const default_compression_threshold = 256
const max_packets_per_second = 1000

Inbound rate limits, enforced per connection over a 1s sliding window. A peer that exceeds either bound is disconnected. Bedrock clients burst on chunk requests and movement but stay well under these ceilings.

const max_bytes_per_second = 8 * 1024 * 1024
const max_prelogin_packets = 64

A connection that has not finished logging in may only send a handful of packets (network settings, login, resource-pack handshake). This caps the work an unauthenticated peer can force before we know who it is.

fn decode_batch #

fn decode_batch(payload []u8, compression_enabled bool) ![][]u8

fn encode_batch #

fn encode_batch(packets [][]u8, compression_enabled bool, threshold int) ![]u8

fn is_connection_closed #

fn is_connection_closed(err IError) bool

is_connection_closed reports whether err is the peer going away rather than a genuine failure. NetherNet reports it as a plain error, so the message is all there is to match on.

fn load_identity #

fn load_identity(path string, domain string) !nethernet.Identity

load_identity returns the identity the server answers under, creating the key at path the first time.

The key is kept across restarts so a client that remembers the server keeps seeing the same one. The token itself is short lived and reissued per connection by nethernet.

fn new_session #

fn new_session(mut conn nethernet.Conn, log &logger.Logger) &Session

interface Transport #

interface Transport {
	// disable_encryption reports whether the transport already encrypts, in
	// which case the protocol layer must not negotiate its own encryption.
	disable_encryption() bool
mut:
	send(p protocol.Packet) !
	send_batch(packets []protocol.Packet) !
	read() ![]protocol.Packet
	remote_addr() string
	close()
	mark_logged_in()
	enable_compression(threshold int)
	enable_encryption(mut ctx encryption.Context)
}

Transport is everything NetworkSession needs from the wire: framed packet send/receive plus the handshake time hooks (compression, encryption, marking login complete).

struct Session #

@[heap]
struct Session {
mut:
	conn                &nethernet.Conn = unsafe { nil }
	pool                protocol.PacketPool
	compression_enabled bool
	threshold           int                 = default_compression_threshold
	cipher              &encryption.Context = unsafe { nil }
	send_queue          [][]u8
	write_mutex         &sync.Mutex = sync.new_mutex()
	logged_in           bool
	prelogin_packets    int
	window_start        time.Time = time.now()
	window_packets      int
	window_bytes        int
pub mut:
	log &logger.Logger = unsafe { nil }
}

fn (Session) mark_logged_in #

fn (mut s Session) mark_logged_in()

mark_logged_in lifts the pre-login packet cap once authentication completes.

fn (Session) enable_compression #

fn (mut s Session) enable_compression(threshold int)

fn (Session) enable_encryption #

fn (mut s Session) enable_encryption(mut ctx encryption.Context)

enable_encryption installs the per-session cipher once the handshake has completed. From this point every batch body (after the 0xfe header) is encrypted outbound and decrypted inbound. Guarded by write_mutex so it never races an in-flight flush.

fn (Session) encryption_enabled #

fn (s &Session) encryption_enabled() bool

fn (Session) read #

fn (mut s Session) read() ![]protocol.Packet

fn (Session) queue #

fn (mut s Session) queue(p protocol.Packet)

fn (Session) flush #

fn (mut s Session) flush() !

fn (Session) send #

fn (mut s Session) send(p protocol.Packet) !

fn (Session) send_batch #

fn (mut s Session) send_batch(packets []protocol.Packet) !

fn (Session) remote_addr #

fn (mut s Session) remote_addr() string

fn (Session) disable_encryption #

fn (s &Session) disable_encryption() bool

disable_encryption reports that the transport already encrypts every byte, so the protocol layer must not negotiate a second layer on top of it.

fn (Session) close #

fn (mut s Session) close()