irctest/irctest/controllers/bahamut.py

177 lines
4.9 KiB
Python

from pathlib import Path
import shutil
import subprocess
from typing import Optional, Set, Type
from irctest.basecontrollers import BaseServerController, DirectoryBasedController
TEMPLATE_CONFIG = """
global {{
name My.Little.Server; # IRC name of the server
info "located on earth"; # A short info line
}};
options {{
network_name unconfigured;
allow_split_ops; # Give ops in empty channels
services_name services.example.org;
// if you need to link more than 1 server, uncomment the following line
servtype hub;
}};
/* where to listen for connections */
port {{
port {port};
bind {hostname};
}};
/* allow clients to connect */
allow {{
host *@*; # Allow anyone
class users; # Place them in the users class
flags T; # No throttling
{password_field}
}};
/* connection class for users */
class {{
name users; # Class name
maxusers 100; # Maximum connections
pingfreq 1000; # Check idle connections every N seconds
maxsendq 100000; # 100KB send buffer limit
}};
/* for services */
super {{
"services.example.org";
}};
/* class for services */
class {{
name services;
pingfreq 60; # Idle check every minute
maxsendq 5000000; # 5MB backlog buffer
}};
/* our services */
connect {{
name services.example.org;
host *@127.0.0.1; # unfortunately, masks aren't allowed here
apasswd password;
cpasswd password;
class services;
}};
oper {{
name operuser;
host *@*;
passwd operpassword;
access *Aa;
class users;
}};
"""
def initialize_entropy(directory: Path) -> None:
# https://github.com/DALnet/bahamut/blob/7fc039d403f66a954225c5dc4ad1fe683aedd794/include/dh.h#L35-L38
nb_rand_bytes = 512 // 8
# https://github.com/DALnet/bahamut/blob/7fc039d403f66a954225c5dc4ad1fe683aedd794/src/dh.c#L186
entropy_file_size = nb_rand_bytes * 4
# Not actually random; but we don't care.
entropy = b"\x00" * entropy_file_size
with (directory / ".ircd.entropy").open("wb") as fd:
fd.write(entropy)
class BahamutController(BaseServerController, DirectoryBasedController):
software_name = "Bahamut"
supported_sasl_mechanisms: Set[str] = set()
supports_sts = False
nickserv = "NickServ@services.example.org"
def create_config(self) -> None:
super().create_config()
with self.open_file("server.conf"):
pass
def run(
self,
hostname: str,
port: int,
*,
password: Optional[str],
ssl: bool,
run_services: bool,
faketime: Optional[str],
) -> None:
assert self.proc is None
self.port = port
self.hostname = hostname
self.create_config()
(unused_hostname, unused_port) = self.get_hostname_and_port()
(services_hostname, services_port) = self.get_hostname_and_port()
password_field = "passwd {};".format(password) if password else ""
self.gen_ssl()
assert self.directory
# Bahamut reads some bytes from /dev/urandom on startup, which causes
# GitHub Actions to sometimes freeze and timeout.
# This initializes the entropy file so Bahamut does not need to do it itself.
initialize_entropy(self.directory)
# they are hardcoded... thankfully Bahamut reads them from the CWD.
shutil.copy(self.pem_path, self.directory / "ircd.crt")
shutil.copy(self.key_path, self.directory / "ircd.key")
with self.open_file("server.conf") as fd:
fd.write(
TEMPLATE_CONFIG.format(
hostname=hostname,
port=port,
services_hostname=services_hostname,
services_port=services_port,
password_field=password_field,
# key_path=self.key_path,
# pem_path=self.pem_path,
)
)
if faketime and shutil.which("faketime"):
faketime_cmd = ["faketime", "-f", faketime]
self.faketime_enabled = True
else:
faketime_cmd = []
self.proc = subprocess.Popen(
[
*faketime_cmd,
"ircd",
"-t", # don't fork
"-f",
self.directory / "server.conf",
],
)
if run_services:
self.wait_for_port()
self.services_controller = self.services_controller_class(
self.test_config, self
)
self.services_controller.run(
protocol="bahamut",
server_hostname=hostname,
server_port=port,
)
def get_irctest_controller_class() -> Type[BahamutController]:
return BahamutController