Anope IRC Services  Version 1.8
sessions.c
Go to the documentation of this file.
1 /* Session Limiting functions.
2  *
3  * (C) 2003-2014 Anope Team
4  * Contact us at team@anope.org
5  *
6  * Please read COPYING and README for further details.
7  *
8  * Based on the original code of Epona by Lara.
9  * Based on the original code of Services by Andy Church.
10  *
11  *
12  */
13 
14 #include "services.h"
15 #include "pseudo.h"
16 
17 /*************************************************************************/
18 
19 /* SESSION LIMITING
20  *
21  * The basic idea of session limiting is to prevent one host from having more
22  * than a specified number of sessions (client connections/clones) on the
23  * network at any one time. To do this we have a list of sessions and
24  * exceptions. Each session structure records information about a single host,
25  * including how many clients (sessions) that host has on the network. When a
26  * host reaches it's session limit, no more clients from that host will be
27  * allowed to connect.
28  *
29  * When a client connects to the network, we check to see if their host has
30  * reached the default session limit per host, and thus whether it is allowed
31  * any more. If it has reached the limit, we kill the connecting client; all
32  * the other clients are left alone. Otherwise we simply increment the counter
33  * within the session structure. When a client disconnects, we decrement the
34  * counter. When the counter reaches 0, we free the session.
35  *
36  * Exceptions allow one to specify custom session limits for a specific host
37  * or a range thereof. The first exception that the host matches is the one
38  * used.
39  *
40  * "Session Limiting" is likely to slow down services when there are frequent
41  * client connects and disconnects. The size of the exception list can also
42  * play a large role in this performance decrease. It is therefore recommened
43  * that you keep the number of exceptions to a minimum. A very simple hashing
44  * method is currently used to store the list of sessions. I'm sure there is
45  * room for improvement and optimisation of this, along with the storage of
46  * exceptions. Comments and suggestions are more than welcome!
47  *
48  * -TheShadow (02 April 1999)
49  */
50 
51 /*************************************************************************/
52 
53 /* I'm sure there is a better way to hash the list of hosts for which we are
54  * storing session information. This should be sufficient for the mean time.
55  * -TheShadow */
56 
57 #define HASH(host) (((host)[0]&31)<<5 | ((host)[1]&31))
58 
61 
64 
65 /*************************************************************************/
66 /****************************** Statistics *******************************/
67 /*************************************************************************/
68 
69 void get_session_stats(long *nrec, long *memuse)
70 {
71  Session *session;
72  long mem;
73  int i;
74 
75  mem = sizeof(Session) * nsessions;
76  for (i = 0; i < 1024; i++) {
77  for (session = sessionlist[i]; session; session = session->next) {
78  mem += strlen(session->host) + 1;
79  }
80  }
81 
82  *nrec = nsessions;
83  *memuse = mem;
84 }
85 
86 void get_exception_stats(long *nrec, long *memuse)
87 {
88  long mem;
89  int i;
90 
91  mem = sizeof(Exception) * nexceptions;
92  for (i = 0; i < nexceptions; i++) {
93  mem += strlen(exceptions[i].mask) + 1;
94  mem += strlen(exceptions[i].reason) + 1;
95  }
96  *nrec = nexceptions;
97  *memuse = mem;
98 }
99 
100 /*************************************************************************/
101 /************************* Session List Display **************************/
102 /*************************************************************************/
103 
104 /* Syntax: SESSION LIST threshold
105  * Lists all sessions with atleast threshold clients.
106  * The threshold value must be greater than 1. This is to prevent
107  * accidental listing of the large number of single client sessions.
108  *
109  * Syntax: SESSION VIEW host
110  * Displays detailed session information about the supplied host.
111  */
112 
113 int do_session(User * u)
114 {
115  Session *session;
116  Exception *exception;
117  char *cmd = strtok(NULL, " ");
118  char *param1 = strtok(NULL, " ");
119  int mincount;
120  int i;
121 
122  if (!LimitSessions) {
123  notice_lang(s_OperServ, u, OPER_SESSION_DISABLED);
124  return MOD_CONT;
125  }
126 
127  if (!cmd)
128  cmd = "";
129 
130  if (stricmp(cmd, "LIST") == 0) {
131  if (!param1) {
132  syntax_error(s_OperServ, u, "SESSION",
133  OPER_SESSION_LIST_SYNTAX);
134 
135  } else if ((mincount = atoi(param1)) <= 1) {
136  notice_lang(s_OperServ, u, OPER_SESSION_INVALID_THRESHOLD);
137 
138  } else {
139  notice_lang(s_OperServ, u, OPER_SESSION_LIST_HEADER, mincount);
140  notice_lang(s_OperServ, u, OPER_SESSION_LIST_COLHEAD);
141  for (i = 0; i < 1024; i++) {
142  for (session = sessionlist[i]; session;
143  session = session->next) {
144  if (session->count >= mincount)
146  OPER_SESSION_LIST_FORMAT,
147  session->count, session->host);
148  }
149  }
150  }
151  } else if (stricmp(cmd, "VIEW") == 0) {
152  if (!param1) {
153  syntax_error(s_OperServ, u, "SESSION",
154  OPER_SESSION_VIEW_SYNTAX);
155 
156  } else {
157  session = findsession(param1);
158  if (!session) {
159  notice_lang(s_OperServ, u, OPER_SESSION_NOT_FOUND, param1);
160  } else {
161  exception = find_host_exception(param1);
162 
163  notice_lang(s_OperServ, u, OPER_SESSION_VIEW_FORMAT,
164  param1, session->count,
165  exception ? exception->
166  limit : DefSessionLimit);
167  }
168  }
169 
170  } else {
171  syntax_error(s_OperServ, u, "SESSION", OPER_SESSION_SYNTAX);
172  }
173  return MOD_CONT;
174 }
175 
176 /*************************************************************************/
177 /********************* Internal Session Functions ************************/
178 /*************************************************************************/
179 
180 Session *findsession(const char *host)
181 {
182  Session *session;
183  int i;
184 
185  if (!host) {
186  return NULL;
187  }
188 
189  for (i = 0; i < 1024; i++) {
190  for (session = sessionlist[i]; session; session = session->next) {
191  if (stricmp(host, session->host) == 0) {
192  return session;
193  }
194  }
195  }
196 
197  return NULL;
198 }
199 
200 /* Attempt to add a host to the session list. If the addition of the new host
201  * causes the the session limit to be exceeded, kill the connecting user.
202  * Returns 1 if the host was added or 0 if the user was killed.
203  */
204 
205 int add_session(char *nick, char *host, char *hostip)
206 {
207  Session *session, **list;
208  Exception *exception;
209  int sessionlimit = 0;
210 
211  session = findsession(host);
212 
213  if (session) {
214  exception = find_hostip_exception(host, hostip);
215 
217  sessionlimit =
218  exception ? exception->limit : DefConSessionLimit;
219  } else {
220  sessionlimit = exception ? exception->limit : DefSessionLimit;
221  }
222 
223  if (sessionlimit != 0 && session->count >= sessionlimit) {
225  notice(s_OperServ, nick, SessionLimitExceeded, host);
228 
229  /* We don't use kill_user() because a user stucture has not yet
230  * been created. Simply kill the user. -TheShadow
231  */
232  kill_user(s_OperServ, nick, "Session limit exceeded");
233 
234  session->hits++;
235  if (MaxSessionKill && session->hits >= MaxSessionKill) {
236  char akillmask[BUFSIZE];
237  snprintf(akillmask, sizeof(akillmask), "*@%s", host);
238  add_akill(NULL, akillmask, s_OperServ,
239  time(NULL) + SessionAutoKillExpiry,
240  "Session limit exceeded");
242  "Added a temporary AKILL for \2%s\2 due to excessive connections",
243  akillmask);
244  }
245  return 0;
246  } else {
247  session->count++;
248  return 1;
249  }
250  }
251 
252  nsessions++;
253  session = scalloc(sizeof(Session), 1);
254  session->host = sstrdup(host);
255  list = &sessionlist[HASH(session->host)];
256  session->next = *list;
257  if (*list)
258  (*list)->prev = session;
259  *list = session;
260  session->count = 1;
261 
262  return 1;
263 }
264 
265 void del_session(const char *host)
266 {
267  Session *session;
268 
269  if (!LimitSessions) {
270  if (debug) {
271  alog("debug: del_session called when LimitSessions is disabled");
272  }
273  return;
274  }
275 
276  if (!host || !*host) {
277  if (debug) {
278  alog("debug: del_session called with NULL values");
279  }
280  return;
281  }
282 
283  if (debug >= 2)
284  alog("debug: del_session() called");
285 
286  session = findsession(host);
287 
288  if (!session) {
289  if (debug) {
291  "WARNING: Tried to delete non-existant session: \2%s",
292  host);
293  alog("session: Tried to delete non-existant session: %s",
294  host);
295  }
296  return;
297  }
298 
299  if (session->count > 1) {
300  session->count--;
301  return;
302  }
303 
304  if (session->prev)
305  session->prev->next = session->next;
306  else
307  sessionlist[HASH(session->host)] = session->next;
308  if (session->next)
309  session->next->prev = session->prev;
310 
311  if (debug >= 2)
312  alog("debug: del_session(): free session structure");
313 
314  free(session->host);
315  free(session);
316 
317  nsessions--;
318 
319  if (debug >= 2)
320  alog("debug: del_session() done");
321 }
322 
323 
324 /*************************************************************************/
325 /********************** Internal Exception Functions *********************/
326 /*************************************************************************/
327 
329 {
330  int i;
331  time_t now = time(NULL);
332 
333  for (i = 0; i < nexceptions; i++) {
334  if (exceptions[i].expires == 0 || exceptions[i].expires > now)
335  continue;
338  "Session limit exception for %s has expired.",
339  exceptions[i].mask);
340  free(exceptions[i].mask);
341  free(exceptions[i].reason);
342  nexceptions--;
343  memmove(exceptions + i, exceptions + i + 1,
344  sizeof(Exception) * (nexceptions - i));
345  exceptions = srealloc(exceptions, sizeof(Exception) * nexceptions);
346  i--;
347  }
348 }
349 
350 /* Find the first exception this host matches and return it. */
351 Exception *find_host_exception(const char *host)
352 {
353  int i;
354 
355  for (i = 0; i < nexceptions; i++) {
356  if ((match_wild_nocase(exceptions[i].mask, host))) {
357  return &exceptions[i];
358  }
359  }
360 
361  return NULL;
362 }
363 
364 /* Same as find_host_exception() except
365  * this tries to find the exception by IP also. */
366 Exception *find_hostip_exception(const char *host, const char *hostip)
367 {
368  int i;
369 
370  for (i = 0; i < nexceptions; i++) {
371  if ((match_wild_nocase(exceptions[i].mask, host))
372  || ((ircd->nickip && hostip)
373  && (match_wild_nocase(exceptions[i].mask, hostip)))) {
374  return &exceptions[i];
375  }
376  }
377 
378  return NULL;
379 }
380 
381 /*************************************************************************/
382 /*********************** Exception Load/Save *****************************/
383 /*************************************************************************/
384 
385 #define SAFE(x) do { \
386  if ((x) < 0) { \
387  if (!forceload) \
388  fatal("Read error on %s", ExceptionDBName); \
389  nexceptions = i; \
390  break; \
391  } \
392 } while (0)
393 
395 {
396  dbFILE *f;
397  int i;
398  uint16 n;
399  uint16 tmp16;
400  uint32 tmp32;
401 
402  if (!
404  return;
405  switch (i = get_file_version(f)) {
406  case 9:
407  case 8:
408  case 7:
409  SAFE(read_int16(&n, f));
410  nexceptions = n;
411  exceptions = scalloc(sizeof(Exception) * nexceptions, 1);
412  if (!nexceptions) {
413  close_db(f);
414  return;
415  }
416  for (i = 0; i < nexceptions; i++) {
417  SAFE(read_string(&exceptions[i].mask, f));
418  SAFE(read_int16(&tmp16, f));
419  exceptions[i].limit = tmp16;
420  SAFE(read_buffer(exceptions[i].who, f));
421  SAFE(read_string(&exceptions[i].reason, f));
422  SAFE(read_int32(&tmp32, f));
423  exceptions[i].time = tmp32;
424  SAFE(read_int32(&tmp32, f));
425  exceptions[i].expires = tmp32;
426  }
427  break;
428 
429  default:
430  fatal("Unsupported version (%d) on %s", i, ExceptionDBName);
431  } /* switch (ver) */
432 
433  close_db(f);
434 }
435 
436 #undef SAFE
437 
438 /*************************************************************************/
439 
440 #define SAFE(x) do { \
441  if ((x) < 0) { \
442  restore_db(f); \
443  log_perror("Write error on %s", ExceptionDBName); \
444  if (time(NULL) - lastwarn > WarningTimeout) { \
445  anope_cmd_global(NULL, "Write error on %s: %s", ExceptionDBName, \
446  strerror(errno)); \
447  lastwarn = time(NULL); \
448  } \
449  return; \
450  } \
451 } while (0)
452 
454 {
455  dbFILE *f;
456  int i;
457  static time_t lastwarn = 0;
458 
459  if (!
461  return;
463  for (i = 0; i < nexceptions; i++) {
464  SAFE(write_string(exceptions[i].mask, f));
465  SAFE(write_int16(exceptions[i].limit, f));
466  SAFE(write_buffer(exceptions[i].who, f));
467  SAFE(write_string(exceptions[i].reason, f));
468  SAFE(write_int32(exceptions[i].time, f));
469  SAFE(write_int32(exceptions[i].expires, f));
470  }
471  close_db(f);
472 }
473 
474 #undef SAFE
475 
476 /*************************************************************************/
477 
479 {
480 #ifdef USE_RDB
481  int i;
482  Exception *e;
483 
484  if (!rdb_open())
485  return;
486  if (rdb_tag_table("anope_os_exceptions") == 0) {
487  alog("Unable to tag table 'anope_os_exceptions' - Exception RDB save failed.");
488  return;
489  }
490  for (i = 0; i < nexceptions; i++) {
491  e = &exceptions[i];
492  if (rdb_save_exceptions(e) == 0) {
493  alog("Unable to save Exception '%s' - Exception RDB save failed.", e->mask);
494  return;
495  }
496  }
497  if (rdb_clean_table("anope_os_exceptions") == 0) {
498  alog("Unable to clean table 'anope_os_exceptions' - Exception RDB save failed.");
499  return;
500  }
501  rdb_close();
502 #endif
503 }
504 
505 /*************************************************************************/
506 /************************ Exception Manipulation *************************/
507 /*************************************************************************/
508 
509 int exception_add(User * u, const char *mask, const int limit,
510  const char *reason, const char *who,
511  const time_t expires)
512 {
513  int i;
514 
515  /* Check if an exception already exists for this mask */
516  for (i = 0; i < nexceptions; i++) {
517  if (!stricmp(mask, exceptions[i].mask)) {
518  if (exceptions[i].limit != limit) {
519  exceptions[i].limit = limit;
520  if (u)
521  notice_lang(s_OperServ, u, OPER_EXCEPTION_CHANGED,
522  mask, exceptions[i].limit);
523  return -2;
524  } else {
525  if (u)
526  notice_lang(s_OperServ, u, OPER_EXCEPTION_EXISTS,
527  mask);
528  return -1;
529  }
530  }
531  }
532 
533  nexceptions++;
534  exceptions = srealloc(exceptions, sizeof(Exception) * nexceptions);
535 
536  exceptions[nexceptions - 1].mask = sstrdup(mask);
537  exceptions[nexceptions - 1].limit = limit;
538  exceptions[nexceptions - 1].reason = sstrdup(reason);
539  exceptions[nexceptions - 1].time = time(NULL);
540  strscpy(exceptions[nexceptions - 1].who, who, NICKMAX);
541  exceptions[nexceptions - 1].expires = expires;
542  exceptions[nexceptions - 1].num = nexceptions - 1;
543 
544  return 1;
545 }
546 
547 /*************************************************************************/
548 
549 static int exception_del(const int index)
550 {
551  if (index < 0 || index >= nexceptions)
552  return 0;
553 
554  free(exceptions[index].mask);
555  free(exceptions[index].reason);
556  nexceptions--;
557  memmove(exceptions + index, exceptions + index + 1,
558  sizeof(Exception) * (nexceptions - index));
559  exceptions = srealloc(exceptions, sizeof(Exception) * nexceptions);
560 
561  return 1;
562 }
563 
564 /* We use the "num" property to keep track of the position of each exception
565  * when deleting using ranges. This is because an exception's position changes
566  * as others are deleted. The positions will be recalculated once the process
567  * is complete. -TheShadow
568  */
569 
570 static int exception_del_callback(User * u, int num, va_list args)
571 {
572  int i;
573  int *last = va_arg(args, int *);
574 
575  *last = num;
576  for (i = 0; i < nexceptions; i++) {
577  if (num - 1 == exceptions[i].num)
578  break;
579  }
580  if (i < nexceptions)
581  return exception_del(i);
582  else
583  return 0;
584 }
585 
586 static int exception_list(User * u, const int index, int *sent_header)
587 {
588  if (index < 0 || index >= nexceptions)
589  return 0;
590  if (!*sent_header) {
591  notice_lang(s_OperServ, u, OPER_EXCEPTION_LIST_HEADER);
592  notice_lang(s_OperServ, u, OPER_EXCEPTION_LIST_COLHEAD);
593  *sent_header = 1;
594  }
595  notice_lang(s_OperServ, u, OPER_EXCEPTION_LIST_FORMAT, index + 1,
596  exceptions[index].limit, exceptions[index].mask);
597  return 1;
598 }
599 
600 static int exception_list_callback(User * u, int num, va_list args)
601 {
602  int *sent_header = va_arg(args, int *);
603 
604  return exception_list(u, num - 1, sent_header);
605 }
606 
607 static int exception_view(User * u, const int index, int *sent_header)
608 {
609  char timebuf[32], expirebuf[256];
610  struct tm tm;
611  time_t t = time(NULL);
612 
613  if (index < 0 || index >= nexceptions)
614  return 0;
615  if (!*sent_header) {
616  notice_lang(s_OperServ, u, OPER_EXCEPTION_LIST_HEADER);
617  *sent_header = 1;
618  }
619 
620  tm = *localtime(exceptions[index].time ? &exceptions[index].time : &t);
621  strftime_lang(timebuf, sizeof(timebuf),
622  u, STRFTIME_SHORT_DATE_FORMAT, &tm);
623 
624  expire_left(u->na, expirebuf, sizeof(expirebuf),
625  exceptions[index].expires);
626 
627  notice_lang(s_OperServ, u, OPER_EXCEPTION_VIEW_FORMAT,
628  index + 1, exceptions[index].mask,
629  *exceptions[index].who ?
630  exceptions[index].who : "<unknown>",
631  timebuf, expirebuf, exceptions[index].limit,
632  exceptions[index].reason);
633  return 1;
634 }
635 
636 static int exception_view_callback(User * u, int num, va_list args)
637 {
638  int *sent_header = va_arg(args, int *);
639 
640  return exception_view(u, num - 1, sent_header);
641 }
642 
643 /*************************************************************************/
644 
645 /* Syntax: EXCEPTION ADD [+expiry] mask limit reason
646  * Adds mask to the exception list with limit as the maximum session
647  * limit and +expiry as an optional expiry time.
648  *
649  * Syntax: EXCEPTION DEL mask
650  * Deletes the first exception that matches mask exactly.
651  *
652  * Syntax: EXCEPTION LIST [mask]
653  * Lists all exceptions or those matching mask.
654  *
655  * Syntax: EXCEPTION VIEW [mask]
656  * Displays detailed information about each exception or those matching
657  * mask.
658  *
659  * Syntax: EXCEPTION MOVE num position
660  * Moves the exception at position num to position.
661  */
662 
664 {
665  char *cmd = strtok(NULL, " ");
666  char *mask, *reason, *expiry, *limitstr;
667  int limit, expires;
668  int i;
669  int x;
670 
671  if (!LimitSessions) {
672  notice_lang(s_OperServ, u, OPER_EXCEPTION_DISABLED);
673  return MOD_CONT;
674  }
675 
676  if (!cmd)
677  cmd = "";
678 
679  if (stricmp(cmd, "ADD") == 0) {
680  if (nexceptions >= 32767) {
681  notice_lang(s_OperServ, u, OPER_EXCEPTION_TOO_MANY);
682  return MOD_CONT;
683  }
684 
685  mask = strtok(NULL, " ");
686  if (mask && *mask == '+') {
687  expiry = mask;
688  mask = strtok(NULL, " ");
689  } else {
690  expiry = NULL;
691  }
692  limitstr = strtok(NULL, " ");
693  reason = strtok(NULL, "");
694 
695  if (!reason) {
696  syntax_error(s_OperServ, u, "EXCEPTION",
697  OPER_EXCEPTION_ADD_SYNTAX);
698  return MOD_CONT;
699  }
700 
701  expires = expiry ? dotime(expiry) : ExceptionExpiry;
702  if (expires < 0) {
703  notice_lang(s_OperServ, u, BAD_EXPIRY_TIME);
704  return MOD_CONT;
705  } else if (expires > 0) {
706  expires += time(NULL);
707  }
708 
709  limit = (limitstr && isdigit(*limitstr)) ? atoi(limitstr) : -1;
710 
711  if (limit < 0 || limit > MaxSessionLimit) {
712  notice_lang(s_OperServ, u, OPER_EXCEPTION_INVALID_LIMIT,
713  MaxSessionLimit);
714  return MOD_CONT;
715 
716  } else {
717  if (strchr(mask, '!') || strchr(mask, '@')) {
719  OPER_EXCEPTION_INVALID_HOSTMASK);
720  return MOD_CONT;
721  }
722 
723  x = exception_add(u, mask, limit, reason, u->nick, expires);
724 
725  if (x == 1) {
726  notice_lang(s_OperServ, u, OPER_EXCEPTION_ADDED, mask,
727  limit);
728  }
729 
730  if (readonly)
731  notice_lang(s_OperServ, u, READ_ONLY_MODE);
732  }
733  } else if (stricmp(cmd, "DEL") == 0) {
734  mask = strtok(NULL, " ");
735 
736  if (!mask) {
737  syntax_error(s_OperServ, u, "EXCEPTION",
738  OPER_EXCEPTION_DEL_SYNTAX);
739  return MOD_CONT;
740  }
741 
742  if (isdigit(*mask) && strspn(mask, "1234567890,-") == strlen(mask)) {
743  int count, deleted, last = -1;
744  deleted =
745  process_numlist(mask, &count, exception_del_callback, u,
746  &last);
747  if (!deleted) {
748  if (count == 1) {
750  OPER_EXCEPTION_NO_SUCH_ENTRY, last);
751  } else {
752  notice_lang(s_OperServ, u, OPER_EXCEPTION_NO_MATCH);
753  }
754  } else if (deleted == 1) {
755  notice_lang(s_OperServ, u, OPER_EXCEPTION_DELETED_ONE);
756  } else {
757  notice_lang(s_OperServ, u, OPER_EXCEPTION_DELETED_SEVERAL,
758  deleted);
759  }
760  } else {
761  int deleted = 0;
762 
763  for (i = 0; i < nexceptions; i++) {
764  if (stricmp(mask, exceptions[i].mask) == 0) {
765  exception_del(i);
766  notice_lang(s_OperServ, u, OPER_EXCEPTION_DELETED,
767  mask);
768  deleted = 1;
769  break;
770  }
771  }
772  if (!deleted && i == nexceptions)
773  notice_lang(s_OperServ, u, OPER_EXCEPTION_NOT_FOUND, mask);
774  }
775 
776  /* Renumber the exception list. I don't believe in having holes in
777  * lists - it makes code more complex, harder to debug and we end up
778  * with huge index numbers. Imho, fixed numbering is only beneficial
779  * when one doesn't have range capable manipulation. -TheShadow */
780 
781  for (i = 0; i < nexceptions; i++)
782  exceptions[i].num = i;
783 
784  if (readonly)
785  notice_lang(s_OperServ, u, READ_ONLY_MODE);
786 
787  } else if (stricmp(cmd, "MOVE") == 0) {
788  Exception *exception;
789  char *n1str = strtok(NULL, " "); /* From position */
790  char *n2str = strtok(NULL, " "); /* To position */
791  int n1, n2;
792 
793  if (!n2str) {
794  syntax_error(s_OperServ, u, "EXCEPTION",
795  OPER_EXCEPTION_MOVE_SYNTAX);
796  return MOD_CONT;
797  }
798 
799  n1 = atoi(n1str) - 1;
800  n2 = atoi(n2str) - 1;
801 
802  if ((n1 >= 0 && n1 < nexceptions) && (n2 >= 0 && n2 < nexceptions)
803  && (n1 != n2)) {
804  exception = scalloc(sizeof(Exception), 1);
805  memcpy(exception, &exceptions[n1], sizeof(Exception));
806 
807  if (n1 < n2) {
808  /* Shift upwards */
809  memmove(&exceptions[n1], &exceptions[n1 + 1],
810  sizeof(Exception) * (n2 - n1));
811  memmove(&exceptions[n2], exception, sizeof(Exception));
812  } else {
813  /* Shift downwards */
814  memmove(&exceptions[n2 + 1], &exceptions[n2],
815  sizeof(Exception) * (n1 - n2));
816  memmove(&exceptions[n2], exception, sizeof(Exception));
817  }
818 
819  free(exception);
820 
821  notice_lang(s_OperServ, u, OPER_EXCEPTION_MOVED,
822  exceptions[n1].mask, n1 + 1, n2 + 1);
823 
824  /* Renumber the exception list. See the DEL block above for why. */
825  for (i = 0; i < nexceptions; i++)
826  exceptions[i].num = i;
827 
828  if (readonly)
829  notice_lang(s_OperServ, u, READ_ONLY_MODE);
830  } else {
831  syntax_error(s_OperServ, u, "EXCEPTION",
832  OPER_EXCEPTION_MOVE_SYNTAX);
833  }
834  } else if (stricmp(cmd, "LIST") == 0) {
835  int sent_header = 0;
837  mask = strtok(NULL, " ");
838  if (mask && strspn(mask, "1234567890,-") == strlen(mask)) {
840  &sent_header);
841  } else {
842  for (i = 0; i < nexceptions; i++) {
843  if (!mask || match_wild_nocase(mask, exceptions[i].mask))
844  exception_list(u, i, &sent_header);
845  }
846  }
847  if (!sent_header)
848  notice_lang(s_OperServ, u, OPER_EXCEPTION_NO_MATCH);
849 
850  } else if (stricmp(cmd, "VIEW") == 0) {
851  int sent_header = 0;
853  mask = strtok(NULL, " ");
854  if (mask && strspn(mask, "1234567890,-") == strlen(mask)) {
856  &sent_header);
857  } else {
858  for (i = 0; i < nexceptions; i++) {
859  if (!mask || match_wild_nocase(mask, exceptions[i].mask))
860  exception_view(u, i, &sent_header);
861  }
862  }
863  if (!sent_header)
864  notice_lang(s_OperServ, u, OPER_EXCEPTION_NO_MATCH);
865 
866  } else {
867  syntax_error(s_OperServ, u, "EXCEPTION", OPER_EXCEPTION_SYNTAX);
868  }
869  return MOD_CONT;
870 }
871 
872 /*************************************************************************/
E int WallExceptionExpire
Definition: extern.h:465
E size_t strspn(const char *s, const char *accept)
Definition: compat.c:106
int hits
Definition: services.h:1148
int rdb_save_exceptions(Exception *e)
Definition: rdb.c:488
E int MaxSessionKill
Definition: extern.h:506
E int process_numlist(const char *numstr, int *count_ret, range_callback_t callback, User *u,...)
Definition: misc.c:292
struct session_ Session
Definition: services.h:245
#define SAFE(x)
Definition: sessions.c:440
E char * ExceptionDBName
Definition: extern.h:509
int nickip
Definition: services.h:340
E int readonly
Definition: extern.h:776
E int ExceptionExpiry
Definition: extern.h:505
E int match_wild_nocase(const char *pattern, const char *str)
Definition: misc.c:268
char nick[NICKMAX]
Definition: services.h:875
E int snprintf(char *buf, size_t size, const char *fmt,...)
Definition: compat.c:37
Exception * find_hostip_exception(const char *host, const char *hostip)
Definition: sessions.c:366
E int checkDefCon(int level)
Definition: operserv.c:1608
int exception_add(User *u, const char *mask, const int limit, const char *reason, const char *who, const time_t expires)
Definition: sessions.c:509
E IRCDVar * ircd
Definition: extern.h:39
#define EXCEPTION_VERSION
Definition: services.h:463
static int exception_list_callback(User *u, int num, va_list args)
Definition: sessions.c:600
static int exception_list(User *u, const int index, int *sent_header)
Definition: sessions.c:586
time_t time
Definition: services.h:1134
E int dotime(const char *s)
Definition: misc.c:364
E int stricmp(const char *s1, const char *s2)
Definition: compat.c:58
E void notice(char *source, char *dest, const char *fmt,...)
Definition: send.c:274
#define read_buffer(buf, f)
Definition: datafiles.h:61
E char * expire_left(NickAlias *na, char *buf, int len, time_t expires)
Definition: misc.c:470
Session * next
Definition: services.h:1145
int do_session(User *u)
Definition: sessions.c:113
static int exception_del_callback(User *u, int num, va_list args)
Definition: sessions.c:570
E char * strscpy(char *d, const char *s, size_t len)
Definition: db-merger.c:1886
void get_exception_stats(long *nrec, long *memuse)
Definition: sessions.c:86
E char * SessionLimitDetailsLoc
Definition: extern.h:510
E int DefConSessionLimit
Definition: extern.h:565
E int SessionAutoKillExpiry
Definition: extern.h:508
E int add_akill(User *u, char *mask, const char *by, const time_t expires, const char *reason)
Definition: operserv.c:722
Session * sessionlist[1024]
Definition: sessions.c:59
E void syntax_error(char *service, User *u, const char *command, int msgnum)
Definition: language.c:295
void save_exceptions()
Definition: sessions.c:453
E void notice_lang(char *source, User *dest, int message,...)
Definition: send.c:169
char * host
Definition: services.h:1146
#define HASH(host)
Definition: sessions.c:57
int add_session(char *nick, char *host, char *hostip)
Definition: sessions.c:205
int32 nsessions
Definition: sessions.c:60
E int read_int16(uint16 *ret, dbFILE *f)
Definition: datafiles.c:405
E void kill_user(char *source, char *user, char *reason)
Definition: actions.c:51
time_t expires
Definition: services.h:1135
E char * sstrdup(const char *s)
Definition: memory.c:105
char * mask
Definition: services.h:1130
int rdb_close()
Definition: rdb.c:47
E void * scalloc(long elsize, long els)
Definition: memory.c:55
static int exception_view(User *u, const int index, int *sent_header)
Definition: sessions.c:607
E void E void E void fatal(const char *fmt,...) FORMAT(printf
E char * s_OperServ
Definition: extern.h:289
E int read_string(char **ret, dbFILE *f)
Definition: db-merger.c:1806
#define DEFCON_REDUCE_SESSION
Definition: services.h:1258
E int get_file_version(dbFILE *f)
Definition: datafiles.c:30
E int write_int16(uint16 val, dbFILE *f)
Definition: db-merger.c:1737
int32_t int32
Definition: db-merger.c:122
Exception * find_host_exception(const char *host)
Definition: sessions.c:351
E void close_db(dbFILE *f)
Definition: db-merger.c:1706
int16 nexceptions
Definition: sessions.c:63
char * reason
Definition: services.h:1133
u_int32_t uint32
Definition: db-merger.c:123
E dbFILE * open_db(const char *service, const char *filename, const char *mode, uint32 version)
Definition: datafiles.c:295
E void alog(const char *fmt,...) FORMAT(printf
void save_rdb_exceptions()
Definition: sessions.c:478
#define MOD_CONT
Definition: modules.h:54
int16_t int16
Definition: db-merger.c:120
Session * prev
Definition: services.h:1145
E int debug
Definition: extern.h:775
static time_t lastwarn
Definition: datafiles.c:19
E char * SessionLimitExceeded
Definition: extern.h:511
void del_session(const char *host)
Definition: sessions.c:265
Exception * exceptions
Definition: sessions.c:62
static int exception_view_callback(User *u, int num, va_list args)
Definition: sessions.c:636
int rdb_clean_table(char *table)
Definition: rdb.c:113
E int LimitSessions
Definition: extern.h:503
E int MaxSessionLimit
Definition: extern.h:507
#define NICKMAX
Definition: config.h:62
static int exception_del(const int index)
Definition: sessions.c:549
int rdb_open()
Definition: rdb.c:31
int count
Definition: services.h:1147
E void * srealloc(void *oldptr, long newsize)
Definition: memory.c:80
struct exception_ Exception
Definition: services.h:238
E int DefSessionLimit
Definition: extern.h:504
int rdb_tag_table(char *table)
Definition: rdb.c:75
void expire_exceptions(void)
Definition: sessions.c:328
E int read_int32(uint32 *ret, dbFILE *f)
Definition: datafiles.c:444
E int strftime_lang(char *buf, int size, User *u, int format, struct tm *tm)
Definition: language.c:240
E int write_string(const char *s, dbFILE *f)
Definition: db-merger.c:1826
#define write_buffer(buf, f)
Definition: datafiles.h:62
E int write_int32(uint32 val, dbFILE *f)
Definition: db-merger.c:1773
#define BUFSIZE
Definition: config.h:47
int do_exception(User *u)
Definition: sessions.c:663
void load_exceptions()
Definition: sessions.c:394
NickAlias * na
Definition: services.h:892
void get_session_stats(long *nrec, long *memuse)
Definition: sessions.c:69
E void anope_cmd_global(char *source, const char *fmt,...)
Definition: ircd.c:506
u_int16_t uint16
Definition: db-merger.c:121
Session * findsession(const char *host)
Definition: sessions.c:180