From 1964c78f6ea43482cddd2a262b4538ca0e7cc397 Mon Sep 17 00:00:00 2001 From: Egor Date: Tue, 20 Jan 2026 19:28:36 +0200 Subject: [PATCH] job: use lambdas --- src/common.hpp | 4 ++ src/db.hpp | 2 - src/dbtool.cpp | 3 -- src/irc.cpp | 116 ++++++++++++++++++++++++------------------------- src/job.cpp | 94 +++++++++++++++++++-------------------- src/job.hpp | 10 ++--- src/main.cpp | 2 +- 7 files changed, 113 insertions(+), 118 deletions(-) diff --git a/src/common.hpp b/src/common.hpp index 90d73c4..196552c 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -29,7 +29,11 @@ /* C++ standard */ #include #include +#include +#include #include +#include +#include #include #include #include diff --git a/src/db.hpp b/src/db.hpp index 0bf2d58..3850a48 100644 --- a/src/db.hpp +++ b/src/db.hpp @@ -4,8 +4,6 @@ #define DB_H_ typedef struct sqlite3 sqlite3; typedef struct sqlite3_stmt sqlite3_stmt; -#include -#include namespace db { struct Statement; struct Database { diff --git a/src/dbtool.cpp b/src/dbtool.cpp index f4e44b5..9aaa78b 100644 --- a/src/dbtool.cpp +++ b/src/dbtool.cpp @@ -4,9 +4,6 @@ #include "db.hpp" #include "passwd.hpp" #include "util.hpp" -#include -#include -#include static const char *argv0 = nullptr; static void usage() { printf("Usage: %s import\n", argv0); diff --git a/src/irc.cpp b/src/irc.cpp index 082b0c1..12a85fa 100644 --- a/src/irc.cpp +++ b/src/irc.cpp @@ -180,62 +180,6 @@ int irc_parse_line(Client *client, char *p, char *end) { fprintf(stderr, "\n"); return irc_process_line(client, source, commandsv, argc, args); } -static void cont_webirc(Client *client, int res) { - if (!(client->umode & UMODE_SECURE) || res != 0) { - free(client->webircpeername); - client->webircpeername = 0; - err_passwdmismatch(client), sendbuf(client); - } else { - free(client->peername); - client->peername = client->webircpeername; - if (client->webirchostname) { - client_chghost(client, 0, client->webirchostname); - free(client->webirchostname); - client->webirchostname = 0; - } - client->webircpeername = 0; - if (client->webircwait == 2) { - // NOTE: this relies on the job system being FIFO - client->umode &= ~UMODE_SECURE; - } - client->webircwait = 0; - newUser(client); - } -} -static void cont_oper(Client *client, int res) { - if (!(client->umode & UMODE_SECURE) || res != 0) { - err_passwdmismatch(client), sendbuf(client); - } else { - client->umode |= UMODE_OPER; - rpl_youreoper(client), sendbuf(client); - copy_strf(&databuf, ":%s MODE %s +o\r\n", hostname, client->nick); - sendbuf(client); - } -} -static void cont_sasl(Client *client, int res) { - if (client->saslusername) { - if (res == 0) { - rpl_saslsuccess(), sendbuf(client); - client_chghost(client, client->saslusername, 0); - } else { - err_saslfail(), sendbuf(client); - } - free(client->saslusername); - client->saslusername = 0; - newUser(client); - } -} -static void cont_pass(Client *client, int res) { - if (client->passwait) { - client->passwait = 0; - if (!(client->umode & UMODE_SECURE) || res != 0) { - err_passwdmismatch(client), sendbuf(client); - } else { - client_chghost(client, client->username+1, 0); - } - newUser(client); - } -} #define USE_COMMAND_HASH #ifdef USE_COMMAND_HASH constexpr size_t command_hash(std::string_view sv) { @@ -446,7 +390,19 @@ int irc_process_line(Client *client, char *source, std::string_view command, int char *password = strchr(p, '\0')+1; if (password > client->saslbuf + client->saslbufLen) goto saslerr; - job_password_check(client, username, password, "users", cont_sasl); + job_password_check(client, username, password, "users", [client](int res) { + if (client->saslusername) { + if (res == 0) { + rpl_saslsuccess(), sendbuf(client); + client_chghost(client, client->saslusername, 0); + } else { + err_saslfail(), sendbuf(client); + } + free(client->saslusername); + client->saslusername = 0; + newUser(client); + } + }); // we'll clear the whole buffer later, this is just in case username alloc fails memset(password, 0, strlen(password)); client->saslusername = mystrdup(username); @@ -540,7 +496,28 @@ saslcleanup: memcpy(client->webirchostname+len, ".xmpp", sizeof(".xmpp")); } } - job_password_check(client, args[1], args[0], "webirc", cont_webirc); + job_password_check(client, args[1], args[0], "webirc", [client](int res) { + if (!(client->umode & UMODE_SECURE) || res != 0) { + free(client->webircpeername); + client->webircpeername = 0; + err_passwdmismatch(client), sendbuf(client); + } else { + free(client->peername); + client->peername = client->webircpeername; + if (client->webirchostname) { + client_chghost(client, 0, client->webirchostname); + free(client->webirchostname); + client->webirchostname = 0; + } + client->webircpeername = 0; + if (client->webircwait == 2) { + // NOTE: this relies on the job system being FIFO + client->umode &= ~UMODE_SECURE; + } + client->webircwait = 0; + newUser(client); + } + }); memset(args[0], 0, strlen(args[0])); } } MATCH_COMMAND("OPER") { @@ -549,7 +526,16 @@ saslcleanup: if (argc < 2) return err_needmoreparams(client, command), sendbuf(client), 0; stringlower(args[0]); - job_password_check(client, args[0], args[1], "ops", cont_oper); + job_password_check(client, args[0], args[1], "ops", [client](int res) { + if (!(client->umode & UMODE_SECURE) || res != 0) { + err_passwdmismatch(client), sendbuf(client); + } else { + client->umode |= UMODE_OPER; + rpl_youreoper(client), sendbuf(client); + copy_strf(&databuf, ":%s MODE %s +o\r\n", hostname, client->nick); + sendbuf(client); + } + }); memset(args[1], 0, strlen(args[1])); } MATCH_COMMAND("NEWACC") { if (!is_registered(client)) @@ -1259,7 +1245,17 @@ void newUser(Client *client) { } } if (client->username[0] == '~' && client->pass_reg) { - job_password_check(client, client->username+1, client->pass_reg, "users", cont_pass); + job_password_check(client, client->username+1, client->pass_reg, "users", [client](int res) { + if (client->passwait) { + client->passwait = 0; + if (!(client->umode & UMODE_SECURE) || res != 0) { + err_passwdmismatch(client), sendbuf(client); + } else { + client_chghost(client, client->username+1, 0); + } + newUser(client); + } + }); client->passwait = 1; memset(client->pass_reg, 0, strlen(client->pass_reg)); free(client->pass_reg); diff --git a/src/job.cpp b/src/job.cpp index 597b9f6..763ad41 100644 --- a/src/job.cpp +++ b/src/job.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2024-2025 Egor +// Copyright (c) 2024-2026 Egor // SPDX-License-Identifier: GPL-2.0-or-later #include "common.hpp" #include "job.hpp" @@ -8,8 +8,6 @@ struct jobent { Client *client; jobproc_t job; contproc_t then; - char *data; - size_t len; }; static std::deque jobq; @@ -52,7 +50,7 @@ void job_process(void) if (!jobq.empty()) { jobent &job = jobq.front(); if (!job.job && current_child == -1) { - job.then(job.client, current_result); + job.then(current_result); jobq.pop_front(); } } @@ -63,27 +61,19 @@ void job_process(void) if (pid == -1) { abortf("job_process: fork failed\n"); } else if (pid == 0) { - int rv = job.job(job.client, job.data, job.len); - memset(job.data, 0, job.len); + int rv = job.job(); _exit(rv); } else { current_child = pid; - memset(job.data, 0, job.len); - free(job.data); - job.data = 0; - job.len = 0; - job.job = 0; + job.job = nullptr; } } } } -void job_add(Client *cl, jobproc_t job, contproc_t then, char *data, size_t len) +void job_add(Client *cl, jobproc_t&& job, contproc_t&& then) { - if (!job) - return; - struct jobent ent = { cl, job, then, data, len }; - jobq.push_back(ent); + jobq.push_back(jobent{cl, std::move(job), std::move(then)}); } void job_kill(Client *cl) { @@ -93,43 +83,53 @@ void job_kill(Client *cl) auto q = p; while (p != jobq.end()) { if (p->client != cl) { - *q++ = *p++; + *q++ = std::move(*p++); } else { - memset(p->data, 0, p->len); - free(p->data); p++; } } jobq.erase(q, jobq.end()); } - -static int job_password_check_job(Client *cl, const char *data, size_t len) -{ - (void)cl; - if (!len) - return 1; - const char *pw = data; - const char *salt = strchr(pw, '\0')+1; - char *hash = password_crypt(pw, salt); - int rv = !hash || strcmp(hash, salt) != 0; - if (hash) - memset(hash, 0, strlen(hash)); - return rv; -} -void job_password_check(Client *client, const char *key, const char *pw, const char *fn, contproc_t then) +// FIXME: make a better version of this and use it throughout the codebase +struct SecureStringWrapper { + char *data; + operator char*() { return data; } + SecureStringWrapper(const char *s) :data(nullptr) { + if (s) { + size_t len = strlen(s); + data = new char[len+1]; + memcpy(data, s, len); + data[len] = '\0'; + } + } + SecureStringWrapper(const SecureStringWrapper &) = delete; + SecureStringWrapper& operator=(const SecureStringWrapper &) = delete; + SecureStringWrapper(SecureStringWrapper &&other) { + data = std::exchange(other.data, nullptr); + } + SecureStringWrapper& operator=(SecureStringWrapper &&other) { + std::swap(data, other.data); + return *this; + } + ~SecureStringWrapper() { + if (data) { + memset(data, 0, strlen(data)); + delete[] data; + } + } +}; +void job_password_check(Client *client, const char *key, const char *pw, const char *fn, contproc_t&& then) { char *salt = password_get(key, fn); - if (!salt) { - job_add(client, job_password_check_job, then, 0, 0); - } else { - size_t pwlen = strlen(pw)+1; - size_t len = pwlen+strlen(salt)+1; - char *buf = (char *)malloc(len); - if (!buf) - abortf("job_password_check: allocation failure\n"); - memcpy(buf, pw, pwlen); - memcpy(buf+pwlen, salt, len-pwlen); - job_add(client, job_password_check_job, then, buf, len); - memset(salt, 0, len-pwlen); - } + // FIXME: make_shared only really needed here cuz std::function wants a copyable object + // C++23 adds a std::move_only_function + job_add(client, [pw=std::make_shared(pw), salt=std::make_shared(salt)]() -> int { + if (!*salt) + return 1; + char *hash = password_crypt(*pw, *salt); + int rv = !hash || strcmp(hash, *salt) != 0; + if (hash) + memset(hash, 0, strlen(hash)); + return rv; + }, std::move(then)); } diff --git a/src/job.hpp b/src/job.hpp index a44448c..490e018 100644 --- a/src/job.hpp +++ b/src/job.hpp @@ -1,14 +1,14 @@ -// Copyright (c) 2024-2025 Egor +// Copyright (c) 2024-2026 Egor // SPDX-License-Identifier: GPL-2.0-or-later #ifndef JOB_H_ #define JOB_H_ #include "ircdata.hpp" -typedef int (*jobproc_t)(Client *cl, const char *data, size_t len); -typedef void (*contproc_t)(Client *cl, int res); +using jobproc_t = std::function; +using contproc_t = std::function; void job_init(void); void job_fini(void); void job_process(void); -void job_add(Client *client, jobproc_t job, contproc_t then, char *data, size_t len); +void job_add(Client *client, jobproc_t&& job, contproc_t&& then); void job_kill(Client *client); -void job_password_check(Client *client, const char *key, const char *pw, const char *fn, contproc_t then); +void job_password_check(Client *client, const char *key, const char *pw, const char *fn, contproc_t&& then); #endif diff --git a/src/main.cpp b/src/main.cpp index b9171bd..d2848fe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -187,7 +187,7 @@ static void show_license(void) fprintf(stderr, PACKAGE_STRING "\n" "Copyright (c) 2022, 2024 Sam Smith\n" -"Copyright (c) 2022-2025 Egor\n" +"Copyright (c) 2022-2026 Egor\n" "\n" "This program is free software; you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" -- 2.56.0