echoIRCd/README.md

99 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# echoIRCd
A from-scratch IRC daemon written in Rust. Commands are objects, modes are
handler objects, and modules hook lifecycle events. Design goals:
`#![forbid(unsafe_code)]`, dependency-light (just two small crates — `openssl`
for TLS and `mio` for the epoll socket engine), and lock-free (a single core
thread owns all state).
> Status: early but capable. It boots, registers clients, speaks a large chunk of
> the IRC + IRCv3 protocol (see **What works**), and one reactor thread has served
> 5,000 concurrent connections in testing. Not battle-tested yet.
## Run it
```sh
cp echoircd.conf.example echoircd.conf # then edit: oper pass, cloak_key, TLS paths
cargo run --release # reads ./echoircd.conf
# point a client at it, e.g. /server 127.0.0.1 6667
```
Config is plain `key = value` (see `echoircd.conf.example`). Your real
`echoircd.conf` is gitignored because it holds secrets (oper password, cloak
key, link password) — never commit it. For TLS, generate a cert/key into `tls/`
(the example config has the one-liner).
## Architecture
A single **core thread** owns every `User` and `Channel`, so command and module
code is plain single-threaded logic over `&mut Server` — no `Arc<Mutex<…>>`
anywhere. The I/O edge feeds it events over mpsc channels:
- **Client connections run on one `mio` epoll reactor thread.** The daemon drives
tens of thousands of sockets without a thread per connection — measured at 5,000
concurrent clients on **4 threads total**, and it scales toward ~50k (use a
release build and a high `LimitNOFILE`). It's a bare epoll/kqueue readiness
reactor — no async runtime is pulled in, so the single-threaded core is
untouched.
- **TLS and server links** keep a thread per connection — there are few of them,
and a TLS session can't be split across reader/writer threads.
Both models hand the core the same `OutSink`, so it never knows or cares which one
a connection uses.
Memory-safety by design: `Uid` handles instead of raw pointers (no use-after-free,
no cull list), an `Extensible` typemap instead of `void*` module data (freed
automatically on drop), `&str` slices, and compiled-in trait objects instead of a
fragile `.so` ABI.
### The two extension points
- **Commands** (`src/command.rs`, `src/coremods/`) — a handler declares `name`,
`min_params`, `before_reg` and `handle(&mut Server, uid, params)`, registered
in `command_table()`. Adding a command is one struct + one table line.
- **Modes** (`src/mode.rs`) — channel/user modes are handler objects
(`ChanMode` / `UserMode`) in a table; adding a mode never touches the parser.
- **Modules** (`src/module.rs`, `src/modules/`) — lifecycle hooks. *Pre-hooks*
(`on_user_register`, `on_pre_command`, `on_pre_message`) return a `ModResult`
and can **Deny**; *notify-hooks* fire from a queue after the command.
## What works
- Registration (`CAP`/`NICK`/`USER`), `PING`/`PONG` with idle + registration
timeouts, welcome burst (001005) + ISUPPORT.
- `JOIN`/`PART`/`NAMES`/`TOPIC`/`KICK`/`INVITE`, `PRIVMSG`/`NOTICE`/`TAGMSG`,
`NICK`, `WHO`/`WHOIS`/`WHOWAS`, `LIST`, `AWAY`, `QUIT`, `MOTD`/`LUSERS`.
- **Full mode set** as handler objects: prefixes `+qaohv`, lists `+beI`, and
`+klmntispzONCTcSRMGu` plus flood/rate modes `+f/+j/+F`, redirect `+L`, word
filter `+g`, and acting **extbans** `m:`/`c:`/`n:`.
- **IRC operators**: `OPER`/`KILL`/`WALLOPS`/`GLOBOPS`, `SAJOIN`/`SAPART`/`SANICK`/
`SAMODE`/`SATOPIC`/`SAKICK`, `CHGHOST`/`CHGIDENT`/`SETHOST`/`SETIDENT`,
`KLINE`/`GLINE`/`ZLINE` + `STATS`, snomasks (`+s`), `DIE`/`RESTART`, and a
reload-safe **`REHASH`** (keeps the running config if the file can't be read,
and announces the reload to every connected user).
- **IRCv3**: `CAP` negotiation, `server-time`, `message-tags` + **`msgid`**,
`multi-prefix`, `away-notify`, `account-notify`, `extended-join`, `chghost`,
`userhost-in-names`, `echo-message`, `invite-notify`, `setname`,
`extended-monitor`, `SASL` (PLAIN, relayed to services), `WATCH`/`MONITOR`,
`SILENCE`, and `ACCEPT` + user `+g` **callerid** (only accepted users may PM you).
- **Reverse-DNS on connect**: the classic `*** Looking up your hostname...`
connection notices, backed by a *real* forward-confirmed PTR resolver written
from scratch over UDP (no DNS crate) — resolves clients to hostnames, off the
core thread, fail-safe to the IP. Configurable (`resolve_hosts`,
`use_resolved_host`).
- **TLS** (openssl) with `sslinfo`; keyed-SHA-256 host **cloaking** (`+x`);
**services-ready accounts** (`SVSLOGIN`/`SVSLOGOUT`, account-gated `+r/+R/+M`)
— the ircd is *ready* for an external services package, it is not one itself.
- **Server-to-server linking**: handshake, UID/FJOIN netburst, cross-server
users and channels, nick-collision handling, netsplit.
- An **antimixedutf8** anti-spam module (blocks mixed-script look-alike spam).
## Originality
echoIRCd is original Rust — no code is copied or translated from any other
project. `scripts/native-rust-guard.sh` enforces this (no `unsafe`, no C/FFI, and
dependencies limited to `openssl` + `mio`); it runs on every edit.
## License
See the repository for licensing.