]> jfr.im git - irc/rqf/shadowircd.git/blame - modules/m_dline.c
[svn] - the new plan:
[irc/rqf/shadowircd.git] / modules / m_dline.c
CommitLineData
212380e3 1/*
2 * ircd-ratbox: A slightly useful ircd.
3 * m_dline.c: Bans/unbans a user.
4 *
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA
23 *
24 * $Id: m_dline.c 3051 2006-12-27 00:02:32Z jilles $
25 */
26
27#include "stdinc.h"
28#include "tools.h"
29#include "channel.h"
30#include "class.h"
31#include "client.h"
32#include "common.h"
33#include "irc_string.h"
34#include "sprintf_irc.h"
35#include "ircd.h"
36#include "hostmask.h"
37#include "numeric.h"
38#include "commio.h"
39#include "s_conf.h"
40#include "s_newconf.h"
41#include "s_log.h"
42#include "send.h"
43#include "hash.h"
44#include "s_serv.h"
45#include "msg.h"
46#include "parse.h"
47#include "modules.h"
48
49static int mo_dline(struct Client *, struct Client *, int, const char **);
50static int mo_undline(struct Client *, struct Client *, int, const char **);
51
52struct Message dline_msgtab = {
53 "DLINE", 0, 0, 0, MFLG_SLOW,
54 {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, mg_ignore, {mo_dline, 2}}
55};
56struct Message undline_msgtab = {
57 "UNDLINE", 0, 0, 0, MFLG_SLOW,
58 {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, mg_ignore, {mo_undline, 2}}
59};
60
61mapi_clist_av1 dline_clist[] = { &dline_msgtab, &undline_msgtab, NULL };
62DECLARE_MODULE_AV1(dline, NULL, NULL, dline_clist, NULL, NULL, "$Revision: 3051 $");
63
64static int valid_comment(char *comment);
65static int flush_write(struct Client *, FILE *, char *, char *);
66static int remove_temp_dline(const char *);
67
68/* mo_dline()
69 *
70 * parv[1] - dline to add
71 * parv[2] - reason
72 */
73static int
74mo_dline(struct Client *client_p, struct Client *source_p,
75 int parc, const char *parv[])
76{
77 char def[] = "No Reason";
78 const char *dlhost;
79 char *oper_reason;
80 char *reason = def;
81 struct irc_sockaddr_storage daddr;
82 char cidr_form_host[HOSTLEN + 1];
83 struct ConfItem *aconf;
84 int bits;
85 char dlbuffer[IRCD_BUFSIZE];
86 const char *current_date;
87 int tdline_time = 0;
88 int loc = 1;
89
90 if(!IsOperK(source_p))
91 {
92 sendto_one(source_p, form_str(ERR_NOPRIVS),
93 me.name, source_p->name, "kline");
94 return 0;
95 }
96
97 if((tdline_time = valid_temp_time(parv[loc])) >= 0)
98 loc++;
99
100 if(parc < loc + 1)
101 {
102 sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS),
103 me.name, source_p->name, "DLINE");
104 return 0;
105 }
106
107 dlhost = parv[loc];
108 strlcpy(cidr_form_host, dlhost, sizeof(cidr_form_host));
109
110 if(!parse_netmask(dlhost, NULL, &bits))
111 {
112 sendto_one(source_p, ":%s NOTICE %s :Invalid D-Line",
113 me.name, source_p->name);
114 return 0;
115 }
116
117 loc++;
118
119 if(parc >= loc + 1) /* host :reason */
120 {
121 if(!EmptyString(parv[loc]))
122 reason = LOCAL_COPY(parv[loc]);
123
124 if(!valid_comment(reason))
125 {
126 sendto_one(source_p,
127 ":%s NOTICE %s :Invalid character '\"' in comment",
128 me.name, source_p->name);
129 return 0;
130 }
131 }
132
133 if(IsOperAdmin(source_p))
134 {
135 if(bits < 8)
136 {
137 sendto_one(source_p,
138 ":%s NOTICE %s :For safety, bitmasks less than 8 require conf access.",
139 me.name, parv[0]);
140 return 0;
141 }
142 }
143 else
144 {
145 if(bits < 16)
146 {
147 sendto_one(source_p,
148 ":%s NOTICE %s :Dline bitmasks less than 16 are for admins only.",
149 me.name, parv[0]);
150 return 0;
151 }
152 }
153
154 if(ConfigFileEntry.non_redundant_klines)
155 {
156 const char *creason;
157 int t = AF_INET, ty, b;
158 ty = parse_netmask(dlhost, (struct sockaddr *)&daddr, &b);
159#ifdef IPV6
160 if(ty == HM_IPV6)
161 t = AF_INET6;
162 else
163#endif
164 t = AF_INET;
165
166 if((aconf = find_dline((struct sockaddr *)&daddr, t)) != NULL)
167 {
168 int bx;
169 parse_netmask(aconf->host, NULL, &bx);
170 if(b >= bx)
171 {
172 creason = aconf->passwd ? aconf->passwd : "<No Reason>";
173 if(IsConfExemptKline(aconf))
174 sendto_one(source_p,
175 ":%s NOTICE %s :[%s] is (E)d-lined by [%s] - %s",
176 me.name, parv[0], dlhost, aconf->host, creason);
177 else
178 sendto_one(source_p,
179 ":%s NOTICE %s :[%s] already D-lined by [%s] - %s",
180 me.name, parv[0], dlhost, aconf->host, creason);
181 return 0;
182 }
183 }
184 }
185
186 set_time();
187 current_date = smalldate();
188
189 aconf = make_conf();
190 aconf->status = CONF_DLINE;
191 DupString(aconf->host, dlhost);
192
193 /* Look for an oper reason */
194 if((oper_reason = strchr(reason, '|')) != NULL)
195 {
196 *oper_reason = '\0';
197 oper_reason++;
198
199 if(!EmptyString(oper_reason))
200 DupString(aconf->spasswd, oper_reason);
201 }
202
203 if(tdline_time > 0)
204 {
205 ircsnprintf(dlbuffer, sizeof(dlbuffer),
206 "Temporary D-line %d min. - %s (%s)",
207 (int) (tdline_time / 60), reason, current_date);
208 DupString(aconf->passwd, dlbuffer);
209 aconf->hold = CurrentTime + tdline_time;
210 add_temp_dline(aconf);
211
212 if(EmptyString(oper_reason))
213 {
214 sendto_realops_snomask(SNO_GENERAL, L_ALL,
215 "%s added temporary %d min. D-Line for [%s] [%s]",
216 get_oper_name(source_p), tdline_time / 60,
217 aconf->host, reason);
218 ilog(L_KLINE, "D %s %d %s %s",
219 get_oper_name(source_p), tdline_time / 60,
220 aconf->host, reason);
221 }
222 else
223 {
224 sendto_realops_snomask(SNO_GENERAL, L_ALL,
225 "%s added temporary %d min. D-Line for [%s] [%s|%s]",
226 get_oper_name(source_p), tdline_time / 60,
227 aconf->host, reason, oper_reason);
228 ilog(L_KLINE, "D %s %d %s %s|%s",
229 get_oper_name(source_p), tdline_time / 60,
230 aconf->host, reason, oper_reason);
231 }
232
233 sendto_one(source_p, ":%s NOTICE %s :Added temporary %d min. D-Line for [%s]",
234 me.name, source_p->name, tdline_time / 60, aconf->host);
235 }
236 else
237 {
238 ircsnprintf(dlbuffer, sizeof(dlbuffer), "%s (%s)", reason, current_date);
239 DupString(aconf->passwd, dlbuffer);
240 add_conf_by_address(aconf->host, CONF_DLINE, NULL, aconf);
241 write_confitem(DLINE_TYPE, source_p, NULL, aconf->host, reason,
242 oper_reason, current_date, 0);
243 }
244
245 check_dlines();
246 return 0;
247}
248
249/* mo_undline()
250 *
251 * parv[1] = dline to remove
252 */
253static int
254mo_undline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
255{
256 FILE *in;
257 FILE *out;
258 char buf[BUFSIZE], buff[BUFSIZE], temppath[BUFSIZE], *p;
259 const char *filename, *found_cidr;
260 const char *cidr;
261 int pairme = NO, error_on_write = NO;
262 mode_t oldumask;
263
264 ircsnprintf(temppath, sizeof(temppath), "%s.tmp", ConfigFileEntry.dlinefile);
265
266 if(!IsOperUnkline(source_p))
267 {
268 sendto_one(source_p, form_str(ERR_NOPRIVS),
269 me.name, source_p->name, "unkline");
270 return 0;
271 }
272
273 cidr = parv[1];
274
275 if(parse_netmask(cidr, NULL, NULL) == HM_HOST)
276 {
277 sendto_one(source_p, ":%s NOTICE %s :Invalid D-Line",
278 me.name, source_p->name);
279 return 0;
280 }
281
282 if(remove_temp_dline(cidr))
283 {
284 sendto_one(source_p,
285 ":%s NOTICE %s :Un-dlined [%s] from temporary D-lines",
286 me.name, parv[0], cidr);
287 sendto_realops_snomask(SNO_GENERAL, L_ALL,
288 "%s has removed the temporary D-Line for: [%s]",
289 get_oper_name(source_p), cidr);
290 ilog(L_KLINE, "UD %s %s", get_oper_name(source_p), cidr);
291 return 0;
292 }
293
294 filename = get_conf_name(DLINE_TYPE);
295
296 if((in = fopen(filename, "r")) == 0)
297 {
298 sendto_one(source_p, ":%s NOTICE %s :Cannot open %s", me.name, parv[0], filename);
299 return 0;
300 }
301
302 oldumask = umask(0);
303 if((out = fopen(temppath, "w")) == 0)
304 {
305 sendto_one(source_p, ":%s NOTICE %s :Cannot open %s", me.name, parv[0], temppath);
306 fclose(in);
307 umask(oldumask);
308 return 0;
309 }
310
311 umask(oldumask);
312
313 while (fgets(buf, sizeof(buf), in))
314 {
315 strlcpy(buff, buf, sizeof(buff));
316
317 if((p = strchr(buff, '\n')) != NULL)
318 *p = '\0';
319
320 if((*buff == '\0') || (*buff == '#'))
321 {
322 if(!error_on_write)
323 flush_write(source_p, out, buf, temppath);
324 continue;
325 }
326
327 if((found_cidr = getfield(buff)) == NULL)
328 {
329 if(!error_on_write)
330 flush_write(source_p, out, buf, temppath);
331 continue;
332 }
333
334 if(irccmp(found_cidr, cidr) == 0)
335 {
336 pairme++;
337 }
338 else
339 {
340 if(!error_on_write)
341 flush_write(source_p, out, buf, temppath);
342 continue;
343 }
344 }
345
346 fclose(in);
347 if (fclose(out))
348 error_on_write = YES;
349
350 if(error_on_write)
351 {
352 sendto_one(source_p,
353 ":%s NOTICE %s :Couldn't write D-line file, aborted",
354 me.name, parv[0]);
355 return 0;
356 }
357 else if(!pairme)
358 {
359 sendto_one(source_p, ":%s NOTICE %s :No D-Line for %s",
360 me.name, parv[0], cidr);
361
362 if(temppath != NULL)
363 (void) unlink(temppath);
364
365 return 0;
366 }
367
368 if (rename(temppath, filename))
369 {
370 sendto_one_notice(source_p, ":Couldn't rename temp file, aborted");
371 return 0;
372 }
373 rehash_bans(0);
374
375
376 sendto_one(source_p, ":%s NOTICE %s :D-Line for [%s] is removed", me.name, parv[0], cidr);
377 sendto_realops_snomask(SNO_GENERAL, L_ALL,
378 "%s has removed the D-Line for: [%s]", get_oper_name(source_p), cidr);
379 ilog(L_KLINE, "UD %s %s", get_oper_name(source_p), cidr);
380
381 return 0;
382}
383
384/*
385 * valid_comment
386 * inputs - pointer to client
387 * - pointer to comment
388 * output - 0 if no valid comment, 1 if valid
389 * side effects - NONE
390 */
391static int
392valid_comment(char *comment)
393{
394 if(strchr(comment, '"'))
395 return 0;
396
397 if(strlen(comment) > REASONLEN)
398 comment[REASONLEN] = '\0';
399
400 return 1;
401}
402
403/*
404 * flush_write()
405 *
406 * inputs - pointer to client structure of oper requesting unkline
407 * - out is the file descriptor
408 * - buf is the buffer to write
409 * - ntowrite is the expected number of character to be written
410 * - temppath is the temporary file name to be written
411 * output - YES for error on write
412 * - NO for success
413 * side effects - if successful, the buf is written to output file
414 * if a write failure happesn, and the file pointed to
415 * by temppath, if its non NULL, is removed.
416 *
417 * The idea here is, to be as robust as possible when writing to the
418 * kline file.
419 *
420 * -Dianora
421 */
422static int
423flush_write(struct Client *source_p, FILE * out, char *buf, char *temppath)
424{
425 int error_on_write = (fputs(buf, out) < 0) ? YES : NO;
426
427 if(error_on_write)
428 {
429 sendto_one(source_p, ":%s NOTICE %s :Unable to write to %s",
430 me.name, source_p->name, temppath);
431 fclose(out);
432 if(temppath != NULL)
433 (void) unlink(temppath);
434 }
435 return (error_on_write);
436}
437
438/* remove_temp_dline()
439 *
440 * inputs - hostname to undline
441 * outputs -
442 * side effects - tries to undline anything that matches
443 */
444static int
445remove_temp_dline(const char *host)
446{
447 struct ConfItem *aconf;
448 dlink_node *ptr;
449 struct irc_sockaddr_storage addr, caddr;
450 int bits, cbits;
451 int i;
452
453 parse_netmask(host, (struct sockaddr *)&addr, &bits);
454
455 for (i = 0; i < LAST_TEMP_TYPE; i++)
456 {
457 DLINK_FOREACH(ptr, temp_dlines[i].head)
458 {
459 aconf = ptr->data;
460
461 parse_netmask(aconf->host, (struct sockaddr *)&caddr, &cbits);
462
463 if(comp_with_mask_sock((struct sockaddr *)&addr, (struct sockaddr *)&caddr, bits) && bits == cbits)
464 {
465 dlinkDestroy(ptr, &temp_dlines[i]);
466 delete_one_address_conf(aconf->host, aconf);
467 return YES;
468 }
469 }
470 }
471
472 return NO;
473}