mirror of
https://github.com/kc1awv/rrcd.git
synced 2026-06-19 10:59:45 -07:00
update nickname and room name limits to use byte size instead of character count
This commit is contained in:
+17
-9
@@ -146,10 +146,6 @@ room_invite_timeout_s = 900.0
|
||||
# Optional behaviors.
|
||||
include_joined_member_list = false
|
||||
|
||||
# Nickname policy.
|
||||
# Maximum accepted nickname length (Unicode characters). 0 disables length limiting.
|
||||
nick_max_chars = 32
|
||||
|
||||
# Limits.
|
||||
# These limits help mitigate abuse and resource exhaustion, but can be adjusted
|
||||
# based on your use case.
|
||||
@@ -157,9 +153,10 @@ nick_max_chars = 32
|
||||
# N.B. max_msg_body_bytes should not allow messages so large that they cannot
|
||||
# fit within the link MTU after UTF-8 encoding and envelope overhead. The
|
||||
# default of 350 bytes is a safe choice for the default Reticulum MTU of 500.
|
||||
max_rooms_per_session = 32
|
||||
max_room_name_len = 64
|
||||
max_nick_bytes = 32
|
||||
max_room_name_bytes = 64
|
||||
max_msg_body_bytes = 350
|
||||
max_rooms_per_session = 32
|
||||
rate_limit_msgs_per_minute = 240
|
||||
|
||||
# Hub-initiated liveness checks (0 disables).
|
||||
@@ -330,7 +327,16 @@ def _build_arg_parser() -> argparse.ArgumentParser:
|
||||
|
||||
p.add_argument("--max-rooms", type=int, default=None, help="Max rooms per session")
|
||||
p.add_argument(
|
||||
"--max-room-name-len", type=int, default=None, help="Max room name length"
|
||||
"--max-nick-bytes",
|
||||
type=int,
|
||||
default=None,
|
||||
help="Max nickname size in UTF-8 bytes",
|
||||
)
|
||||
p.add_argument(
|
||||
"--max-room-name-bytes",
|
||||
type=int,
|
||||
default=None,
|
||||
help="Max room name size in UTF-8 bytes",
|
||||
)
|
||||
|
||||
p.add_argument(
|
||||
@@ -426,8 +432,10 @@ def main(argv: list[str] | None = None) -> None:
|
||||
|
||||
if args.max_rooms is not None:
|
||||
cfg = replace(cfg, max_rooms_per_session=int(args.max_rooms))
|
||||
if args.max_room_name_len is not None:
|
||||
cfg = replace(cfg, max_room_name_len=int(args.max_room_name_len))
|
||||
if args.max_nick_bytes is not None:
|
||||
cfg = replace(cfg, max_nick_bytes=int(args.max_nick_bytes))
|
||||
if args.max_room_name_bytes is not None:
|
||||
cfg = replace(cfg, max_room_name_bytes=int(args.max_room_name_bytes))
|
||||
|
||||
if args.rate_limit_msgs_per_minute is not None:
|
||||
cfg = replace(
|
||||
|
||||
+2
-2
@@ -25,9 +25,9 @@ class HubRuntimeConfig:
|
||||
room_registry_prune_interval_s: float = 3600.0
|
||||
room_invite_timeout_s: float = 900.0
|
||||
include_joined_member_list: bool = False
|
||||
nick_max_chars: int = 32
|
||||
max_nick_bytes: int = 32
|
||||
max_rooms_per_session: int = 32
|
||||
max_room_name_len: int = 64
|
||||
max_room_name_bytes: int = 64
|
||||
max_msg_body_bytes: int = 350
|
||||
rate_limit_msgs_per_minute: int = 240
|
||||
ping_interval_s: float = 0.0
|
||||
|
||||
+8
-8
@@ -300,7 +300,7 @@ class MessageRouter:
|
||||
new_nick = None
|
||||
|
||||
if isinstance(nick, str):
|
||||
n = normalize_nick(nick, max_chars=self.hub.config.nick_max_chars)
|
||||
n = normalize_nick(nick, max_bytes=self.hub.config.max_nick_bytes)
|
||||
if n is not None:
|
||||
new_nick = n
|
||||
sess["nick"] = n
|
||||
@@ -311,7 +311,7 @@ class MessageRouter:
|
||||
if new_nick is None:
|
||||
legacy_nick = body.get(B_HELLO_NICK_LEGACY)
|
||||
n2 = normalize_nick(
|
||||
legacy_nick, max_chars=self.hub.config.nick_max_chars
|
||||
legacy_nick, max_bytes=self.hub.config.max_nick_bytes
|
||||
)
|
||||
if n2 is not None:
|
||||
new_nick = n2
|
||||
@@ -360,7 +360,7 @@ class MessageRouter:
|
||||
new_nick = None
|
||||
|
||||
if isinstance(nick, str):
|
||||
n = normalize_nick(nick, max_chars=self.hub.config.nick_max_chars)
|
||||
n = normalize_nick(nick, max_bytes=self.hub.config.max_nick_bytes)
|
||||
if n is not None:
|
||||
new_nick = n
|
||||
sess["nick"] = n
|
||||
@@ -370,7 +370,7 @@ class MessageRouter:
|
||||
if new_nick is None:
|
||||
legacy_nick = body.get(B_HELLO_NICK_LEGACY)
|
||||
n2 = normalize_nick(
|
||||
legacy_nick, max_chars=self.hub.config.nick_max_chars
|
||||
legacy_nick, max_bytes=self.hub.config.max_nick_bytes
|
||||
)
|
||||
if n2 is not None:
|
||||
new_nick = n2
|
||||
@@ -686,10 +686,10 @@ class MessageRouter:
|
||||
text="message requires room name",
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
# Validate message body size (UTF-8 bytes)
|
||||
if isinstance(body, str):
|
||||
body_bytes = len(body.encode('utf-8', errors='replace'))
|
||||
body_bytes = len(body.encode("utf-8", errors="replace"))
|
||||
if body_bytes > self.hub.config.max_msg_body_bytes:
|
||||
if self.hub.identity is not None:
|
||||
self.hub.message_helper.emit_error(
|
||||
@@ -781,7 +781,7 @@ class MessageRouter:
|
||||
|
||||
incoming_nick = env.get(K_NICK)
|
||||
if incoming_nick is not None:
|
||||
n = normalize_nick(incoming_nick, max_chars=self.hub.config.nick_max_chars)
|
||||
n = normalize_nick(incoming_nick, max_bytes=self.hub.config.max_nick_bytes)
|
||||
if n is not None:
|
||||
old_session_nick = sess.get("nick")
|
||||
if old_session_nick != n:
|
||||
@@ -794,7 +794,7 @@ class MessageRouter:
|
||||
env.pop(K_NICK, None)
|
||||
else:
|
||||
nick = sess.get("nick")
|
||||
n = normalize_nick(nick, max_chars=self.hub.config.nick_max_chars)
|
||||
n = normalize_nick(nick, max_bytes=self.hub.config.max_nick_bytes)
|
||||
if n is not None:
|
||||
env[K_NICK] = n
|
||||
|
||||
|
||||
+10
-6
@@ -97,10 +97,10 @@ class HubService:
|
||||
self.destination.hash.hex() if self.destination else "-",
|
||||
)
|
||||
self.log.info(
|
||||
"Policy nick_max_chars=%s max_rooms=%s max_room_name_len=%s rate_limit_msgs_per_minute=%s",
|
||||
self.config.nick_max_chars,
|
||||
"Policy max_nick_bytes=%s max_rooms=%s max_room_name_bytes=%s rate_limit_msgs_per_minute=%s",
|
||||
self.config.max_nick_bytes,
|
||||
self.config.max_rooms_per_session,
|
||||
self.config.max_room_name_len,
|
||||
self.config.max_room_name_bytes,
|
||||
self.config.rate_limit_msgs_per_minute,
|
||||
)
|
||||
|
||||
@@ -330,7 +330,7 @@ class HubService:
|
||||
f"banned={len(old_banned)}->{len(new_banned)} "
|
||||
f"registered_rooms={len(old_registry)}->{len(new_registry)}"
|
||||
)
|
||||
lines.append(f"policy: nick_max_chars={new_cfg.nick_max_chars}")
|
||||
lines.append(f"policy: max_nick_bytes={new_cfg.max_nick_bytes}")
|
||||
|
||||
if cfg_changes:
|
||||
lines.append("config_changes:")
|
||||
@@ -503,8 +503,12 @@ class HubService:
|
||||
r = room.strip().lower()
|
||||
if not r:
|
||||
raise ValueError("room name must not be empty")
|
||||
if len(r) > int(self.config.max_room_name_len):
|
||||
raise ValueError("room name too long")
|
||||
# Check UTF-8 byte length
|
||||
room_bytes = len(r.encode("utf-8", errors="replace"))
|
||||
if room_bytes > int(self.config.max_room_name_bytes):
|
||||
raise ValueError(
|
||||
f"room name too long: {room_bytes} bytes > {self.config.max_room_name_bytes} bytes"
|
||||
)
|
||||
return r
|
||||
|
||||
def _on_packet(self, link: RNS.Link, data: bytes) -> None:
|
||||
|
||||
+11
-3
@@ -135,16 +135,24 @@ class SessionManager:
|
||||
self.hub.room_manager.remove_member(room, link)
|
||||
|
||||
if remaining_members and peer_hash and self.hub.identity:
|
||||
notification_body = [peer_hash] if self.hub.config.include_joined_member_list else None
|
||||
notification_body = (
|
||||
[peer_hash] if self.hub.config.include_joined_member_list else None
|
||||
)
|
||||
member_notification = make_envelope(
|
||||
T_PARTED, src=self.hub.identity.hash, room=room, body=notification_body
|
||||
T_PARTED,
|
||||
src=self.hub.identity.hash,
|
||||
room=room,
|
||||
body=notification_body,
|
||||
)
|
||||
member_notification_payload = encode(member_notification)
|
||||
for member_link in remaining_members:
|
||||
try:
|
||||
import RNS
|
||||
|
||||
RNS.Packet(member_link, member_notification_payload).send()
|
||||
self.hub.stats_manager.inc("bytes_out", len(member_notification_payload))
|
||||
self.hub.stats_manager.inc(
|
||||
"bytes_out", len(member_notification_payload)
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
+2
-2
@@ -109,8 +109,8 @@ class StatsManager:
|
||||
lines.append(
|
||||
f"limits: rate_limit_msgs_per_minute={self.hub.config.rate_limit_msgs_per_minute} "
|
||||
f"max_rooms_per_session={self.hub.config.max_rooms_per_session} "
|
||||
f"max_room_name_len={self.hub.config.max_room_name_len} "
|
||||
f"nick_max_chars={self.hub.config.nick_max_chars}"
|
||||
f"max_room_name_bytes={self.hub.config.max_room_name_bytes} "
|
||||
f"max_nick_bytes={self.hub.config.max_nick_bytes}"
|
||||
)
|
||||
lines.append(
|
||||
f"features: ping_interval_s={self.hub.config.ping_interval_s} "
|
||||
|
||||
+11
-10
@@ -2,14 +2,14 @@ from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
_DEFAULT_NICK_MAX_CHARS = 32
|
||||
_DEFAULT_NICK_MAX_BYTES = 32
|
||||
|
||||
|
||||
def expand_path(p: str) -> str:
|
||||
return os.path.expanduser(os.path.expandvars(p))
|
||||
|
||||
|
||||
def normalize_nick(value, *, max_chars: int = _DEFAULT_NICK_MAX_CHARS) -> str | None:
|
||||
def normalize_nick(value, *, max_bytes: int = _DEFAULT_NICK_MAX_BYTES) -> str | None:
|
||||
if not isinstance(value, str):
|
||||
return None
|
||||
|
||||
@@ -18,19 +18,20 @@ def normalize_nick(value, *, max_chars: int = _DEFAULT_NICK_MAX_CHARS) -> str |
|
||||
return None
|
||||
|
||||
try:
|
||||
limit = int(max_chars)
|
||||
limit = int(max_bytes)
|
||||
except Exception:
|
||||
limit = int(_DEFAULT_NICK_MAX_CHARS)
|
||||
limit = int(_DEFAULT_NICK_MAX_BYTES)
|
||||
|
||||
if limit > 0 and len(s) > limit:
|
||||
# Check UTF-8 byte length
|
||||
try:
|
||||
encoded = s.encode("utf-8", "strict")
|
||||
except UnicodeError:
|
||||
return None
|
||||
|
||||
if limit > 0 and len(encoded) > limit:
|
||||
return None
|
||||
|
||||
if "\n" in s or "\r" in s or "\x00" in s:
|
||||
return None
|
||||
|
||||
try:
|
||||
s.encode("utf-8", "strict")
|
||||
except UnicodeError:
|
||||
return None
|
||||
|
||||
return s
|
||||
|
||||
Reference in New Issue
Block a user