Skip to content

Commit 8930fdc

Browse files
authored
Add third/lockserv (PR #73 from ValwareIRC/patch-11)
LockServ; prohibit new connections to a particular server.
2 parents 848ee6e + 9482679 commit 8930fdc

File tree

1 file changed

+395
-0
lines changed

1 file changed

+395
-0
lines changed

files/lockserv.c

Lines changed: 395 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,395 @@
1+
/*
2+
Licence: GPLv3 or later
3+
Copyright Ⓒ 2022 Valerie Pond
4+
LockServ
5+
6+
Locks a server (stops incoming connections)
7+
*/
8+
/*** <<<MODULE MANAGER START>>>
9+
module
10+
{
11+
documentation "https://github.com/ValwareIRC/valware-unrealircd-mods/blob/main/lockserv/README.md";
12+
troubleshooting "In case of problems, documentation or e-mail me at [email protected]";
13+
min-unrealircd-version "6.*";
14+
max-unrealircd-version "6.*";
15+
post-install-text {
16+
"The module is installed. Now all you need to do is add a loadmodule line:";
17+
"loadmodule \"third/lockserv\";";
18+
"And /REHASH the IRCd.";
19+
"Please see the README for operclass requirements";
20+
}
21+
}
22+
*** <<<MODULE MANAGER END>>>
23+
*/
24+
25+
#include "unrealircd.h"
26+
27+
28+
29+
ModuleHeader MOD_HEADER = {
30+
"third/lockserv",
31+
"1.0",
32+
"Adds the /lockserv command which allows privileged server operators to prevent connections to a particular server.",
33+
"Valware",
34+
"unrealircd-6",
35+
};
36+
37+
38+
#define IsServerLocked(x) (moddata_client(x, lockserv_md).i)
39+
#define LockReason(x) (moddata_client(x, lockserv_reason_md).str)
40+
#define LockedBy(x) (moddata_client(x, lockserv_lockedby_md).str)
41+
42+
#define MSG_LOCKSERV "LOCKSERV"
43+
#define MSG_UNLOCKSERV "UNLOCKSERV"
44+
45+
CMD_FUNC(cmd_lockserv);
46+
CMD_FUNC(cmd_unlockserv);
47+
CMD_OVERRIDE_FUNC(lockserv_cap_ovr);
48+
49+
void lockserv_free(ModData *m);
50+
const char *lockserv_serialize(ModData *m);
51+
void lockserv_unserialize(const char *str, ModData *m);
52+
53+
void lockserv_reason_free(ModData *m);
54+
const char *lockserv_reason_serialize(ModData *m);
55+
void lockserv_reason_unserialize(const char *str, ModData *m);
56+
57+
void lockserv_lockedby_free(ModData *m);
58+
const char *lockserv_lockedby_serialize(ModData *m);
59+
void lockserv_lockedby_unserialize(const char *str, ModData *m);
60+
61+
int lockserv_connect(Client *client);
62+
void lockserv_list(Client *client);
63+
64+
static void dumpit(Client *client, char **p);
65+
66+
static char *lockserv_help[] = {
67+
"***** Lockserv *****",
68+
"-",
69+
"Lock a server; prohibit new connections to a particular server.",
70+
"Note: Unlike a G-Line, this will prohibit use of SASL in addition.",
71+
"Requires operclass permission 'lockserv'.",
72+
"-",
73+
"Syntax:",
74+
" /LOCKSERV <server|nick|-list> [<reason>]",
75+
" /UNLOCKSERV <server>",
76+
"-",
77+
"Examples:",
78+
"-",
79+
"List locked servers:",
80+
" /LOCKSERV -list",
81+
"-",
82+
"View this output:",
83+
" /LOCKSERV -help",
84+
"-",
85+
"Lock a server called 'lol.valware.uk':",
86+
" /LOCKSERV lol.valware.uk This server is closed! Please connect to irc.valware.uk instead",
87+
"-",
88+
"Lock the server that user 'Valware' is on:",
89+
" /LOCKSERV Valware Server closed for spam.",
90+
"-",
91+
"Unlock a server to resume allowing connections:",
92+
" /UNLOCKSERV lol.valware.uk",
93+
"-",
94+
NULL
95+
};
96+
97+
ModDataInfo *lockserv_md;
98+
ModDataInfo *lockserv_reason_md;
99+
ModDataInfo *lockserv_lockedby_md;
100+
101+
MOD_INIT() {
102+
ModDataInfo mreq;
103+
104+
MARK_AS_GLOBAL_MODULE(modinfo);
105+
106+
memset(&mreq, 0, sizeof(mreq));
107+
mreq.name = "lockserv";
108+
mreq.free = lockserv_free;
109+
mreq.serialize = lockserv_serialize;
110+
mreq.unserialize = lockserv_unserialize;
111+
mreq.sync = MODDATA_SYNC_EARLY;
112+
mreq.remote_write = 1;
113+
mreq.type = MODDATATYPE_CLIENT;
114+
lockserv_md = ModDataAdd(modinfo->handle, mreq);
115+
if (!lockserv_md)
116+
{
117+
config_error("could not register lockserv moddata");
118+
return MOD_FAILED;
119+
}
120+
121+
memset(&mreq, 0, sizeof(mreq));
122+
mreq.name = "lockserv_reason";
123+
mreq.free = lockserv_reason_free;
124+
mreq.serialize = lockserv_reason_serialize;
125+
mreq.unserialize = lockserv_reason_unserialize;
126+
mreq.sync = MODDATA_SYNC_EARLY;
127+
mreq.remote_write = 1;
128+
mreq.type = MODDATATYPE_CLIENT;
129+
lockserv_reason_md = ModDataAdd(modinfo->handle, mreq);
130+
if (!lockserv_reason_md)
131+
{
132+
config_error("could not register lockserv moddata");
133+
return MOD_FAILED;
134+
}
135+
memset(&mreq, 0, sizeof(mreq));
136+
mreq.name = "lockserv_lockedby";
137+
mreq.free = lockserv_lockedby_free;
138+
mreq.serialize = lockserv_lockedby_serialize;
139+
mreq.unserialize = lockserv_lockedby_unserialize;
140+
mreq.sync = MODDATA_SYNC_EARLY;
141+
mreq.remote_write = 1;
142+
mreq.type = MODDATATYPE_CLIENT;
143+
lockserv_lockedby_md = ModDataAdd(modinfo->handle, mreq);
144+
if (!lockserv_lockedby_md)
145+
{
146+
config_error("could not register lockserv moddata");
147+
return MOD_FAILED;
148+
}
149+
150+
HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_CONNECT, 0, lockserv_connect);
151+
CommandAdd(modinfo->handle, MSG_LOCKSERV, cmd_lockserv, MAXPARA, CMD_OPER | CMD_SERVER);
152+
CommandAdd(modinfo->handle, MSG_UNLOCKSERV, cmd_unlockserv, 1, CMD_OPER | CMD_SERVER);
153+
CommandOverrideAdd(modinfo->handle, "CAP", 0, lockserv_cap_ovr);
154+
155+
156+
return MOD_SUCCESS;
157+
}
158+
/** Called upon module load */
159+
MOD_LOAD()
160+
{
161+
return MOD_SUCCESS;
162+
}
163+
/** Called upon unload */
164+
MOD_UNLOAD()
165+
{
166+
return MOD_SUCCESS;
167+
}
168+
169+
/* Lockserv Status (1/0) */
170+
const char *lockserv_serialize(ModData *m)
171+
{
172+
static char tmp[32];
173+
if (m->i == 0)
174+
return NULL; /* not set */
175+
snprintf(tmp, sizeof(tmp), "%d", m->i);
176+
return tmp;
177+
}
178+
void lockserv_free(ModData *m)
179+
{
180+
m->i = 0;
181+
}
182+
void lockserv_unserialize(const char *str, ModData *m)
183+
{
184+
m->i = atoi(str);
185+
}
186+
187+
/* Lockserv Reason */
188+
const char *lockserv_reason_serialize(ModData *m)
189+
{
190+
static char tmp[32];
191+
if (m->str == NULL)
192+
return NULL; /* not set */
193+
snprintf(tmp, sizeof(tmp), "%s", m->str);
194+
return tmp;
195+
}
196+
void lockserv_reason_free(ModData *m)
197+
{
198+
m->str = 0;
199+
}
200+
void lockserv_reason_unserialize(const char *str, ModData *m)
201+
{
202+
safe_strdup(m->str, str);
203+
}
204+
205+
206+
/* Lockserv set by */
207+
const char *lockserv_lockedby_serialize(ModData *m)
208+
{
209+
static char tmp[32];
210+
if (m->str == NULL)
211+
return NULL; /* not set */
212+
snprintf(tmp, sizeof(tmp), "%s", m->str);
213+
return tmp;
214+
}
215+
void lockserv_lockedby_free(ModData *m)
216+
{
217+
m->str = 0;
218+
}
219+
void lockserv_lockedby_unserialize(const char *str, ModData *m)
220+
{
221+
safe_strdup(m->str, str);
222+
}
223+
224+
static void dumpit(Client *client, char **p) {
225+
if(IsServer(client))
226+
return;
227+
for(; *p != NULL; p++)
228+
sendto_one(client, NULL, ":%s %03d %s :%s", me.name, RPL_TEXT, client->name, *p);
229+
}
230+
231+
CMD_FUNC(cmd_lockserv)
232+
{
233+
Client *target;
234+
235+
if (!IsOper(client) || !ValidatePermissionsForPath("lockserv:can_lock", client, NULL, NULL, NULL))
236+
{
237+
sendnumeric(client, ERR_NOPRIVILEGES);
238+
return;
239+
}
240+
else if (parc < 2) {
241+
sendnumeric(client, ERR_NEEDMOREPARAMS, MSG_LOCKSERV);
242+
return;
243+
}
244+
if (BadPtr(parv[1]) || !strcasecmp(parv[1],"-help"))
245+
{
246+
dumpit(client,lockserv_help);
247+
return;
248+
}
249+
if (!strcasecmp(parv[1],"-list"))
250+
{
251+
lockserv_list(client);
252+
return;
253+
}
254+
target = find_server(parv[1], NULL);
255+
if (!target)
256+
{
257+
/* look it up by user as fallback? */
258+
target = find_user(parv[1], NULL);
259+
if (target && IsUser(target))
260+
target = find_server(target->uplink->name, NULL);
261+
262+
else // still did not find anyone though
263+
{
264+
sendto_one(client, NULL, "FAIL %s NOT_A_SERVER :Server not found.", MSG_LOCKSERV);
265+
return;
266+
}
267+
}
268+
if (!IsServer(target) && !IsMe(target))
269+
{
270+
sendto_one(client, NULL, "FAIL %s NOT_A_SERVER :Not a server: '%s'", MSG_LOCKSERV, target->name);
271+
return;
272+
}
273+
274+
if (IsServerLocked(target))
275+
{
276+
sendto_one(client, NULL, "FAIL %s SERVER_ALREADY_LOCKED :That server '%s' is already locked.", MSG_LOCKSERV, target->name);
277+
return;
278+
}
279+
char p[254] = "\0";
280+
if (!BadPtr(parv[2]))
281+
{
282+
int i;
283+
for (i = 2; i < parc && !BadPtr(parv[i]); i++)
284+
{
285+
strlcat(p, parv[i], sizeof(p));
286+
if (!BadPtr(parv[i + 1]))
287+
strlcat(p, " ", sizeof(p));
288+
}
289+
}
290+
const char *reason = (strlen(p)) ? p : "This server is closed to incoming connections.";
291+
moddata_client_set(target, "lockserv", "1");
292+
moddata_client_set(target, "lockserv_reason", reason);
293+
294+
char s[300] = "\0";
295+
ircsnprintf(s, sizeof(s), "%s!%s@%s [Oper: %s [%s]]", client->name, client->ident, client->ip, moddata_client_get(client, "operlogin"),moddata_client_get(client, "operclass"));
296+
const char *lockedby = s;
297+
moddata_client_set(target, "lockserv_lockedby", lockedby);
298+
299+
sendnotice(client,"Server '%s' is now locked.",target->name);
300+
unreal_log(ULOG_INFO, "lockserv", "SERVER_LOCKED", NULL,
301+
"$serv has been locked from incoming connections [by $setby] [Reason: $reason] ",
302+
log_data_string("serv", target->name),
303+
log_data_string("reason", reason),
304+
log_data_string("setby", lockedby));
305+
return;
306+
}
307+
308+
CMD_FUNC(cmd_unlockserv)
309+
{
310+
Client *target;
311+
if (!IsOper(client) || !ValidatePermissionsForPath("lockserv:can_unlock", client, NULL, NULL, NULL))
312+
{
313+
sendnumeric(client, ERR_NOPRIVILEGES);
314+
return;
315+
}
316+
else if (parc < 2) {
317+
sendnumeric(client, ERR_NEEDMOREPARAMS, MSG_UNLOCKSERV);
318+
return;
319+
}
320+
if (BadPtr(parv[1]) || !strcasecmp(parv[1],"-help"))
321+
{
322+
dumpit(client,lockserv_help);
323+
return;
324+
}
325+
target = find_server(parv[1], NULL);
326+
if (!target)
327+
{
328+
sendto_one(client, NULL, "FAIL %s NOT_A_SERVER :Server not found.", MSG_LOCKSERV);
329+
return;
330+
}
331+
if (!IsServer(target) && !IsMe(target))
332+
{
333+
sendto_one(client, NULL, "FAIL %s NOT_A_SERVER :Not a server: '%s'", MSG_LOCKSERV, target->name);
334+
return;
335+
}
336+
if (!IsServerLocked(target))
337+
{
338+
sendto_one(client, NULL, "FAIL %s SERVER_ALREADY_LOCKED :That server '%s' is already open.", MSG_UNLOCKSERV, target->name);
339+
return;
340+
}
341+
moddata_client_set(target, "lockserv", "0");
342+
moddata_client_set(target, "lockserv_reason", "0");
343+
moddata_client_set(target, "lockserv_lockedby", "0");
344+
sendnotice(client,"Server '%s' is now locked.",target->name);
345+
unreal_log(ULOG_INFO, "lockserv", "SERVER_LOCKED", NULL,
346+
"$serv has been unlocked - now open to incoming connections ($client)",
347+
log_data_string("serv", target->name),
348+
log_data_string("client", client->name));
349+
return;
350+
}
351+
352+
int lockserv_connect(Client *client)
353+
{
354+
if (IsServerLocked(client->uplink) && !find_tkl_exception(TKL_ZAP, client)) // allow servers/rpc/everything else to connect still :D
355+
{
356+
exit_client(client, NULL, LockReason(client->uplink));
357+
return HOOK_DENY;
358+
}
359+
return HOOK_CONTINUE;
360+
}
361+
362+
void lockserv_list(Client *client)
363+
{
364+
Client *server;
365+
int i = 1;
366+
int found = 0;
367+
368+
sendnotice(client,"*** Listing locked servers.");
369+
list_for_each_entry(server, &global_server_list, client_node)
370+
{
371+
if (IsServerLocked(server))
372+
{
373+
char info[200];
374+
sendnotice(client, "%d) %s - Reason: %s - Set by: %s", i, server->name, LockReason(server), LockedBy(server));
375+
i++;
376+
found = 1;
377+
}
378+
}
379+
if (!found)
380+
sendnotice(client,"*** No locked servers were found");
381+
else
382+
sendnotice(client,"*** End of /LOCKSERV -list");
383+
}
384+
385+
/* if for some reason we didn't catch them because they connected without caps (is it even possible?)
386+
* not to worry we'll catch them a little later */
387+
CMD_OVERRIDE_FUNC(lockserv_cap_ovr)
388+
{
389+
Client *server = find_server(me.name, NULL);
390+
391+
if (IsServerLocked(server) && !find_tkl_exception(TKL_ZAP, client) && !IsRegistered(client))
392+
return;
393+
394+
CallCommandOverride(ovr, client, recv_mtags, parc, parv);
395+
}

0 commit comments

Comments
 (0)