/[cvs]/eggdrop1.8/src/chanprog.c
ViewVC logotype

Contents of /eggdrop1.8/src/chanprog.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.4 - (show annotations) (download) (as text)
Tue Oct 19 12:13:33 2010 UTC (8 years, 7 months ago) by pseudo
Branch: MAIN
Changes since 1.3: +15 -1 lines
File MIME type: text/x-chdr
Added full SSL support including Tcl commands.
Added support for certificate authentication.
Added support for botnet and partyline encryption using ssl.
Documented the new features and commands.
Fixed add_server() problems with IPv6 addresses in the server list.

1 /*
2 * chanprog.c -- handles:
3 * rmspace()
4 * maintaining the server list
5 * revenge punishment
6 * timers, utimers
7 * telling the current programmed settings
8 * initializing a lot of stuff and loading the tcl scripts
9 *
10 * $Id: chanprog.c,v 1.3 2010/10/10 21:24:43 pseudo Exp $
11 */
12 /*
13 * Copyright (C) 1997 Robey Pointer
14 * Copyright (C) 1999 - 2010 Eggheads Development Team
15 *
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 */
30
31 #include "main.h"
32
33 #ifdef HAVE_GETRUSAGE
34 # include <sys/resource.h>
35 # ifdef HAVE_SYS_RUSAGE_H
36 # include <sys/rusage.h>
37 # endif
38 #endif
39
40 #ifdef HAVE_UNAME
41 # include <sys/utsname.h>
42 #endif
43
44 #include "modules.h"
45
46 extern struct userrec *userlist;
47 extern log_t *logs;
48 extern Tcl_Interp *interp;
49 extern char ver[], botnetnick[], firewall[], motdfile[], userfile[], helpdir[],
50 tempdir[], moddir[], notify_new[], owner[], configfile[];
51 extern time_t now, online_since;
52 extern int backgrd, term_z, con_chan, cache_hit, cache_miss, firewallport,
53 default_flags, max_logs, conmask, protect_readonly, make_userfile,
54 noshare, ignore_time, max_socks;
55 #ifdef TLS
56 extern SSL_CTX *ssl_ctx;
57 #endif
58
59 tcl_timer_t *timer = NULL; /* Minutely timer */
60 tcl_timer_t *utimer = NULL; /* Secondly timer */
61 unsigned long timer_id = 1; /* Next timer of any sort will
62 * have this number */
63 struct chanset_t *chanset = NULL; /* Channel list */
64 char admin[121] = ""; /* Admin info */
65 char origbotname[NICKLEN + 1];
66 char botname[NICKLEN + 1]; /* Primary botname */
67
68
69 /* Remove leading and trailing whitespaces.
70 */
71 void rmspace(char *s)
72 {
73 register char *p = NULL, *q = NULL;
74
75 if (!s || !*s)
76 return;
77
78 /* Remove trailing whitespaces. */
79 for (q = s + strlen(s) - 1; q >= s && egg_isspace(*q); q--);
80 *(q + 1) = 0;
81
82 /* Remove leading whitespaces. */
83 for (p = s; egg_isspace(*p); p++);
84
85 if (p != s)
86 memmove(s, p, q - p + 2);
87 }
88
89
90 /* Returns memberfields if the nick is in the member list.
91 */
92 memberlist *ismember(struct chanset_t *chan, char *nick)
93 {
94 register memberlist *x;
95
96 for (x = chan->channel.member; x && x->nick[0]; x = x->next)
97 if (!rfc_casecmp(x->nick, nick))
98 return x;
99 return NULL;
100 }
101
102 /* Find a chanset by channel name as the server knows it (ie !ABCDEchannel)
103 */
104 struct chanset_t *findchan(const char *name)
105 {
106 register struct chanset_t *chan;
107
108 for (chan = chanset; chan; chan = chan->next)
109 if (!rfc_casecmp(chan->name, name))
110 return chan;
111 return NULL;
112 }
113
114 /* Find a chanset by display name (ie !channel)
115 */
116 struct chanset_t *findchan_by_dname(const char *name)
117 {
118 register struct chanset_t *chan;
119
120 for (chan = chanset; chan; chan = chan->next)
121 if (!rfc_casecmp(chan->dname, name))
122 return chan;
123 return NULL;
124 }
125
126
127 /*
128 * "caching" functions
129 */
130
131 /* Shortcut for get_user_by_host -- might have user record in one
132 * of the channel caches.
133 */
134 struct userrec *check_chanlist(const char *host)
135 {
136 char *nick, *uhost, buf[UHOSTLEN];
137 register memberlist *m;
138 register struct chanset_t *chan;
139
140 strncpyz(buf, host, sizeof buf);
141 uhost = buf;
142 nick = splitnick(&uhost);
143 for (chan = chanset; chan; chan = chan->next)
144 for (m = chan->channel.member; m && m->nick[0]; m = m->next)
145 if (!rfc_casecmp(nick, m->nick) && !egg_strcasecmp(uhost, m->userhost))
146 return m->user;
147 return NULL;
148 }
149
150 /* Shortcut for get_user_by_handle -- might have user record in channels
151 */
152 struct userrec *check_chanlist_hand(const char *hand)
153 {
154 register struct chanset_t *chan;
155 register memberlist *m;
156
157 for (chan = chanset; chan; chan = chan->next)
158 for (m = chan->channel.member; m && m->nick[0]; m = m->next)
159 if (m->user && !egg_strcasecmp(m->user->handle, hand))
160 return m->user;
161 return NULL;
162 }
163
164 /* Clear the user pointers in the chanlists.
165 *
166 * Necessary when a hostmask is added/removed, a user is added or a new
167 * userfile is loaded.
168 */
169 void clear_chanlist(void)
170 {
171 register memberlist *m;
172 register struct chanset_t *chan;
173
174 for (chan = chanset; chan; chan = chan->next)
175 for (m = chan->channel.member; m && m->nick[0]; m = m->next) {
176 m->user = NULL;
177 m->tried_getuser = 0;
178 }
179 }
180
181 /* Clear the user pointer of a specific nick in the chanlists.
182 *
183 * Necessary when a hostmask is added/removed, a nick changes, etc.
184 * Does not completely invalidate the channel cache like clear_chanlist().
185 */
186 void clear_chanlist_member(const char *nick)
187 {
188 register memberlist *m;
189 register struct chanset_t *chan;
190
191 for (chan = chanset; chan; chan = chan->next)
192 for (m = chan->channel.member; m && m->nick[0]; m = m->next)
193 if (!rfc_casecmp(m->nick, nick)) {
194 m->user = NULL;
195 m->tried_getuser = 0;
196 break;
197 }
198 }
199
200 /* If this user@host is in a channel, set it (it was null)
201 */
202 void set_chanlist(const char *host, struct userrec *rec)
203 {
204 char *nick, *uhost, buf[UHOSTLEN];
205 register memberlist *m;
206 register struct chanset_t *chan;
207
208 strncpyz(buf, host, sizeof buf);
209 uhost = buf;
210 nick = splitnick(&uhost);
211 for (chan = chanset; chan; chan = chan->next)
212 for (m = chan->channel.member; m && m->nick[0]; m = m->next)
213 if (!rfc_casecmp(nick, m->nick) && !egg_strcasecmp(uhost, m->userhost))
214 m->user = rec;
215 }
216
217 /* Calculate the memory we should be using
218 */
219 int expmem_chanprog()
220 {
221 register int tot = 0;
222 register tcl_timer_t *t;
223
224 for (t = timer; t; t = t->next)
225 tot += sizeof(tcl_timer_t) + strlen(t->cmd) + 1;
226 for (t = utimer; t; t = t->next)
227 tot += sizeof(tcl_timer_t) + strlen(t->cmd) + 1;
228 return tot;
229 }
230
231 float getcputime()
232 {
233 #ifdef HAVE_GETRUSAGE
234 float stime, utime;
235 struct rusage ru;
236
237 getrusage(RUSAGE_SELF, &ru);
238 utime = ru.ru_utime.tv_sec + (ru.ru_utime.tv_usec / 1000000.00);
239 stime = ru.ru_stime.tv_sec + (ru.ru_stime.tv_usec / 1000000.00);
240 return (utime + stime);
241 #else
242 # ifdef HAVE_CLOCK
243 return (clock() / (CLOCKS_PER_SEC * 1.00));
244 # else
245 return -1.00;
246 # endif
247 #endif
248 }
249
250 /* Dump uptime info out to dcc (guppy 9Jan99)
251 */
252 void tell_verbose_uptime(int idx)
253 {
254 char s[256], s1[121];
255 time_t now2, hr, min;
256
257 now2 = now - online_since;
258 s[0] = 0;
259 if (now2 > 86400) {
260 /* days */
261 sprintf(s, "%d day", (int) (now2 / 86400));
262 if ((int) (now2 / 86400) >= 2)
263 strcat(s, "s");
264 strcat(s, ", ");
265 now2 -= (((int) (now2 / 86400)) * 86400);
266 }
267 hr = (time_t) ((int) now2 / 3600);
268 now2 -= (hr * 3600);
269 min = (time_t) ((int) now2 / 60);
270 sprintf(&s[strlen(s)], "%02d:%02d", (int) hr, (int) min);
271 s1[0] = 0;
272 if (backgrd)
273 strcpy(s1, MISC_BACKGROUND);
274 else {
275 if (term_z)
276 strcpy(s1, MISC_TERMMODE);
277 else if (con_chan)
278 strcpy(s1, MISC_STATMODE);
279 else
280 strcpy(s1, MISC_LOGMODE);
281 }
282 dprintf(idx, "%s %s (%s)\n", MISC_ONLINEFOR, s, s1);
283 }
284
285 /* Dump status info out to dcc
286 */
287 void tell_verbose_status(int idx)
288 {
289 char s[256], s1[121], s2[81];
290 char *vers_t, *uni_t;
291 int i;
292 time_t now2 = now - online_since, hr, min;
293 float cputime;
294 #ifdef HAVE_UNAME
295 struct utsname un;
296
297 if (!uname(&un) < 0) {
298 #endif
299 vers_t = " ";
300 uni_t = "*unknown*";
301 #ifdef HAVE_UNAME
302 } else {
303 vers_t = un.release;
304 uni_t = un.sysname;
305 }
306 #endif
307
308 i = count_users(userlist);
309 dprintf(idx, "I am %s, running %s: %d user%s (mem: %uk).\n",
310 botnetnick, ver, i, i == 1 ? "" : "s",
311 (int) (expected_memory() / 1024));
312
313 s[0] = 0;
314 if (now2 > 86400) {
315 /* days */
316 sprintf(s, "%d day", (int) (now2 / 86400));
317 if ((int) (now2 / 86400) >= 2)
318 strcat(s, "s");
319 strcat(s, ", ");
320 now2 -= (((int) (now2 / 86400)) * 86400);
321 }
322 hr = (time_t) ((int) now2 / 3600);
323 now2 -= (hr * 3600);
324 min = (time_t) ((int) now2 / 60);
325 sprintf(&s[strlen(s)], "%02d:%02d", (int) hr, (int) min);
326 s1[0] = 0;
327 if (backgrd)
328 strcpy(s1, MISC_BACKGROUND);
329 else {
330 if (term_z)
331 strcpy(s1, MISC_TERMMODE);
332 else if (con_chan)
333 strcpy(s1, MISC_STATMODE);
334 else
335 strcpy(s1, MISC_LOGMODE);
336 }
337 cputime = getcputime();
338 if (cputime < 0)
339 sprintf(s2, "CPU: unknown");
340 else {
341 hr = cputime / 60;
342 cputime -= hr * 60;
343 sprintf(s2, "CPU: %02d:%05.2f", (int) hr, cputime); /* Actally min/sec */
344 }
345 dprintf(idx, "%s %s (%s) - %s - %s: %4.1f%%\n", MISC_ONLINEFOR,
346 s, s1, s2, MISC_CACHEHIT,
347 100.0 * ((float) cache_hit) / ((float) (cache_hit + cache_miss)));
348
349 dprintf(idx, "Configured with: " EGG_AC_ARGS "\n");
350 if (admin[0])
351 dprintf(idx, "Admin: %s\n", admin);
352
353 dprintf(idx, "Config file: %s\n", configfile);
354 dprintf(idx, "OS: %s %s\n", uni_t, vers_t);
355 dprintf(idx, "Process ID: %d (parent %d)\n", getpid(), getppid());
356
357 /* info library */
358 dprintf(idx, "%s %s\n", MISC_TCLLIBRARY,
359 ((interp) && (Tcl_Eval(interp, "info library") == TCL_OK)) ?
360 tcl_resultstring() : "*unknown*");
361
362 /* info tclversion/patchlevel */
363 dprintf(idx, "%s %s (%s %s)\n", MISC_TCLVERSION,
364 ((interp) && (Tcl_Eval(interp, "info patchlevel") == TCL_OK)) ?
365 tcl_resultstring() : (Tcl_Eval(interp, "info tclversion") == TCL_OK) ?
366 tcl_resultstring() : "*unknown*", MISC_TCLHVERSION,
367 TCL_PATCH_LEVEL ? TCL_PATCH_LEVEL : "*unknown*");
368
369 if (tcl_threaded())
370 dprintf(idx, "Tcl is threaded.\n");
371 #ifdef TLS
372 dprintf(idx, "TLS support is enabled.\n");
373 dprintf(idx, "TLS library: %s\n", SSLeay_version(SSLEAY_VERSION));
374 #else
375 dprintf(idx, "TLS support is not available.\n");
376 #endif
377 #ifdef IPV6
378 dprintf(idx, "IPv6 support is enabled.\n");
379 #else
380 dprintf(idx, "IPv6 support is not available.\n");
381 #endif
382 dprintf(idx, "Socket table: %d/%d\n", threaddata()->MAXSOCKS, max_socks);
383 }
384
385 /* Show all internal state variables
386 */
387 void tell_settings(int idx)
388 {
389 char s[1024];
390 int i;
391 struct flag_record fr = { FR_GLOBAL, 0, 0, 0, 0, 0 };
392
393 dprintf(idx, "Botnet nickname: %s\n", botnetnick);
394 if (firewall[0])
395 dprintf(idx, "Firewall: %s:%d\n", firewall, firewallport);
396 dprintf(idx, "Userfile: %s\n", userfile);
397 dprintf(idx, "Motd: %s\n", motdfile);
398 dprintf(idx, "Directories:\n");
399 #ifndef STATIC
400 dprintf(idx, " Help : %s\n", helpdir);
401 dprintf(idx, " Temp : %s\n", tempdir);
402 dprintf(idx, " Modules: %s\n", moddir);
403 #else
404 dprintf(idx, " Help: %s\n", helpdir);
405 dprintf(idx, " Temp: %s\n", tempdir);
406 #endif
407 fr.global = default_flags;
408
409 build_flags(s, &fr, NULL);
410 dprintf(idx, "%s [%s], %s: %s\n", MISC_NEWUSERFLAGS, s,
411 MISC_NOTIFY, notify_new);
412 if (owner[0])
413 dprintf(idx, "%s: %s\n", MISC_PERMOWNER, owner);
414 for (i = 0; i < max_logs; i++)
415 if (logs[i].filename != NULL) {
416 dprintf(idx, "Logfile #%d: %s on %s (%s: %s)\n", i + 1,
417 logs[i].filename, logs[i].chname,
418 masktype(logs[i].mask), maskname(logs[i].mask));
419 }
420 dprintf(idx, "Ignores last %d minute%s.\n", ignore_time,
421 (ignore_time != 1) ? "s" : "");
422 }
423
424 void reaffirm_owners()
425 {
426 char *p, *q, s[121];
427 struct userrec *u;
428
429 /* Please stop breaking this function. */
430 if (owner[0]) {
431 q = owner;
432 p = strchr(q, ',');
433 while (p) {
434 strncpyz(s, q, (p - q) + 1);
435 rmspace(s);
436 u = get_user_by_handle(userlist, s);
437 if (u)
438 u->flags = sanity_check(u->flags | USER_OWNER);
439 q = p + 1;
440 p = strchr(q, ',');
441 }
442 strcpy(s, q);
443 rmspace(s);
444 u = get_user_by_handle(userlist, s);
445 if (u)
446 u->flags = sanity_check(u->flags | USER_OWNER);
447 }
448 }
449
450 void chanprog()
451 {
452 int i;
453 FILE *f;
454 char s[161], rands[8];
455
456 admin[0] = 0;
457 helpdir[0] = 0;
458 tempdir[0] = 0;
459 conmask = 0;
460
461 for (i = 0; i < max_logs; i++)
462 logs[i].flags |= LF_EXPIRING;
463
464 /* Turn off read-only variables (make them write-able) for rehash */
465 protect_readonly = 0;
466
467 /* Now read it */
468 if (!readtclprog(configfile))
469 fatal(MISC_NOCONFIGFILE, 0);
470
471 for (i = 0; i < max_logs; i++) {
472 if (logs[i].flags & LF_EXPIRING) {
473 if (logs[i].filename != NULL) {
474 nfree(logs[i].filename);
475 logs[i].filename = NULL;
476 }
477 if (logs[i].chname != NULL) {
478 nfree(logs[i].chname);
479 logs[i].chname = NULL;
480 }
481 if (logs[i].f != NULL) {
482 fclose(logs[i].f);
483 logs[i].f = NULL;
484 }
485 logs[i].mask = 0;
486 logs[i].flags = 0;
487 }
488 }
489
490 /* We should be safe now */
491 call_hook(HOOK_REHASH);
492 protect_readonly = 1;
493
494 if (!botnetnick[0])
495 strncpyz(botnetnick, origbotname, HANDLEN + 1);
496
497 if (!botnetnick[0])
498 fatal("I don't have a botnet nick!!\n", 0);
499
500 if (!userfile[0])
501 fatal(MISC_NOUSERFILE2, 0);
502
503 if (!readuserfile(userfile, &userlist)) {
504 if (!make_userfile) {
505 char tmp[178];
506
507 egg_snprintf(tmp, sizeof tmp, MISC_NOUSERFILE, configfile);
508 fatal(tmp, 0);
509 }
510 printf("\n\n%s\n", MISC_NOUSERFILE2);
511 if (module_find("server", 0, 0))
512 printf(MISC_USERFCREATE1, origbotname);
513 printf("%s\n\n", MISC_USERFCREATE2);
514 } else if (make_userfile) {
515 make_userfile = 0;
516 printf("%s\n", MISC_USERFEXISTS);
517 }
518
519 if (helpdir[0])
520 if (helpdir[strlen(helpdir) - 1] != '/')
521 strcat(helpdir, "/");
522
523 if (tempdir[0])
524 if (tempdir[strlen(tempdir) - 1] != '/')
525 strcat(tempdir, "/");
526
527 /* Test tempdir: it's vital. */
528
529 /* Possible file race condition solved by using a random string
530 * and the process id in the filename.
531 * FIXME: This race is only partitially fixed. We could still be
532 * overwriting an existing file / following a malicious
533 * link.
534 */
535 make_rand_str(rands, 7); /* create random string */
536 sprintf(s, "%s.test-%u-%s", tempdir, getpid(), rands);
537 f = fopen(s, "w");
538 if (f == NULL)
539 fatal(MISC_CANTWRITETEMP, 0);
540 fclose(f);
541 unlink(s);
542 reaffirm_owners();
543 check_tcl_event("userfile-loaded");
544 }
545
546 /* Reload the user file from disk
547 */
548 void reload()
549 {
550 if (!file_readable(userfile)) {
551 putlog(LOG_MISC, "*", MISC_CANTRELOADUSER);
552 return;
553 }
554
555 noshare = 1;
556 clear_userlist(userlist);
557 noshare = 0;
558 userlist = NULL;
559 if (!readuserfile(userfile, &userlist))
560 fatal(MISC_MISSINGUSERF, 0);
561 reaffirm_owners();
562 check_tcl_event("userfile-loaded");
563 call_hook(HOOK_READ_USERFILE);
564 }
565
566 void rehash()
567 {
568 call_hook(HOOK_PRE_REHASH);
569 noshare = 1;
570 clear_userlist(userlist);
571 noshare = 0;
572 userlist = NULL;
573 chanprog();
574 }
575
576 /*
577 * Brief venture into timers
578 */
579
580 /* Add a timer
581 */
582 unsigned long add_timer(tcl_timer_t ** stack, int elapse, char *cmd,
583 unsigned long prev_id)
584 {
585 tcl_timer_t *old = (*stack);
586
587 *stack = nmalloc(sizeof **stack);
588 (*stack)->next = old;
589 (*stack)->mins = elapse;
590 (*stack)->cmd = nmalloc(strlen(cmd) + 1);
591 strcpy((*stack)->cmd, cmd);
592 /* If it's just being added back and already had an id,
593 * don't create a new one.
594 */
595 if (prev_id > 0)
596 (*stack)->id = prev_id;
597 else
598 (*stack)->id = timer_id++;
599 return (*stack)->id;
600 }
601
602 /* Remove a timer, by id
603 */
604 int remove_timer(tcl_timer_t ** stack, unsigned long id)
605 {
606 tcl_timer_t *old;
607 int ok = 0;
608
609 while (*stack) {
610 if ((*stack)->id == id) {
611 ok++;
612 old = *stack;
613 *stack = ((*stack)->next);
614 nfree(old->cmd);
615 nfree(old);
616 } else
617 stack = &((*stack)->next);
618 }
619 return ok;
620 }
621
622 /* Check timers, execute the ones that have expired.
623 */
624 void do_check_timers(tcl_timer_t ** stack)
625 {
626 tcl_timer_t *mark = *stack, *old = NULL;
627 char x[16];
628
629 /* New timers could be added by a Tcl script inside a current timer
630 * so i'll just clear out the timer list completely, and add any
631 * unexpired timers back on.
632 */
633 *stack = NULL;
634 while (mark) {
635 if (mark->mins > 0)
636 mark->mins--;
637 old = mark;
638 mark = mark->next;
639 if (!old->mins) {
640 egg_snprintf(x, sizeof x, "timer%lu", old->id);
641 do_tcl(x, old->cmd);
642 nfree(old->cmd);
643 nfree(old);
644 } else {
645 old->next = *stack;
646 *stack = old;
647 }
648 }
649 }
650
651 /* Wipe all timers.
652 */
653 void wipe_timers(Tcl_Interp *irp, tcl_timer_t **stack)
654 {
655 tcl_timer_t *mark = *stack, *old;
656
657 while (mark) {
658 old = mark;
659 mark = mark->next;
660 nfree(old->cmd);
661 nfree(old);
662 }
663 *stack = NULL;
664 }
665
666 /* Return list of timers
667 */
668 void list_timers(Tcl_Interp *irp, tcl_timer_t *stack)
669 {
670 char mins[10], id[16], *x;
671 EGG_CONST char *argv[3];
672 tcl_timer_t *mark;
673
674 for (mark = stack; mark; mark = mark->next) {
675 egg_snprintf(mins, sizeof mins, "%u", mark->mins);
676 egg_snprintf(id, sizeof id, "timer%lu", mark->id);
677 argv[0] = mins;
678 argv[1] = mark->cmd;
679 argv[2] = id;
680 x = Tcl_Merge(3, argv);
681 Tcl_AppendElement(irp, x);
682 Tcl_Free((char *) x);
683 }
684 }
685
686 /* Oddly enough, written by Sup (former(?) Eggdrop coder)
687 */
688 int isowner(char *name)
689 {
690 register char *ptr = NULL, *s = NULL, *n = NULL;
691
692 if (!name)
693 return 0;
694
695 ptr = owner - 1;
696
697 do {
698 ptr++;
699 if (*ptr && !egg_isspace(*ptr) && *ptr != ',') {
700 if (!s)
701 s = ptr;
702 } else if (s) {
703 for (n = name; *n && *s && s < ptr &&
704 tolower((unsigned) *n) == tolower((unsigned) *s); n++, s++);
705
706 if (s == ptr && !*n)
707 return 1;
708
709 s = NULL;
710 }
711 } while (*ptr);
712
713 return 0;
714 }

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23