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

Annotation of /eggdrop1.8/src/dcc.c

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


Revision 1.2 - (hide annotations) (download) (as text)
Thu Aug 5 18:12:05 2010 UTC (8 years, 11 months ago) by pseudo
Branch: MAIN
Changes since 1.1: +39 -36 lines
File MIME type: text/x-chdr
Added new, full IPv6 support to eggdrop.

1 simple 1.1 /*
2     * dcc.c -- handles:
3     * activity on a dcc socket
4     * disconnect on a dcc socket
5     * ...and that's it! (but it's a LOT)
6     *
7 pseudo 1.2 * $Id: dcc.c,v 1.1.1.1 2010/07/26 21:11:06 simple Exp $
8 simple 1.1 */
9     /*
10     * Copyright (C) 1997 Robey Pointer
11     * Copyright (C) 1999 - 2010 Eggheads Development Team
12     *
13     * This program is free software; you can redistribute it and/or
14     * modify it under the terms of the GNU General Public License
15     * as published by the Free Software Foundation; either version 2
16     * of the License, or (at your option) any later version.
17     *
18     * This program is distributed in the hope that it will be useful,
19     * but WITHOUT ANY WARRANTY; without even the implied warranty of
20     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21     * GNU General Public License for more details.
22     *
23     * You should have received a copy of the GNU General Public License
24     * along with this program; if not, write to the Free Software
25     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26     */
27    
28     #include "main.h"
29     #include <ctype.h>
30     #include <errno.h>
31     #include "modules.h"
32     #include "tandem.h"
33    
34     /* Includes for botnet md5 challenge/response code <cybah> */
35     #include "md5/md5.h"
36    
37     extern struct userrec *userlist;
38     extern struct chanset_t *chanset;
39     extern Tcl_Interp *interp;
40     extern time_t now;
41     extern char botnetnick[], ver[], origbotname[], notify_new[];
42     extern int egg_numver, connect_timeout, conmask, backgrd, max_dcc,
43     make_userfile, default_flags, raw_log, ignore_time,
44     par_telnet_flood;
45    
46     struct dcc_t *dcc = NULL; /* DCC list */
47     int dcc_total = 0; /* Total dcc's */
48     char tempdir[121] = ""; /* Temporary directory
49     * (default: current directory) */
50     int require_p = 0; /* Require 'p' access to get on the
51     * party line? */
52     int allow_new_telnets = 0; /* Allow people to introduce themselves
53     * via telnet */
54     int stealth_telnets = 0; /* Be paranoid? <cybah> */
55     int use_telnet_banner = 0; /* Display telnet banner? */
56     char network[41] = "unknown-net"; /* Name of the IRC network you're on */
57     int password_timeout = 180; /* Time to wait for a password from a user */
58     int bot_timeout = 60; /* Bot timeout value */
59     int identtimeout = 5; /* Timeout value for ident lookups */
60     int dupwait_timeout = 5; /* Timeout for rejecting duplicate entries */
61     int protect_telnet = 1; /* Even bother with ident lookups :) */
62     int flood_telnet_thr = 5; /* Number of telnet connections to be
63     * considered a flood */
64     int flood_telnet_time = 60; /* In how many seconds? */
65     char bannerfile[121] = "text/banner"; /* File displayed on telnet login */
66    
67     static void dcc_telnet_hostresolved(int);
68     static void dcc_telnet_got_ident(int, char *);
69     static void dcc_telnet_pass(int, int);
70    
71    
72     /* This is not a universal telnet detector. You need to send WILL STATUS to the
73     * other side and pass the reply to this function. A telnet client will respond
74     * to this with either DO or DONT STATUS.
75     */
76     static int detect_telnet(unsigned char *buf)
77     {
78     if (!buf || !buf[0] || !buf[1])
79     return 0;
80     while (buf[2]) {
81     if (buf[0] == TLN_IAC && (buf[1] == TLN_DO || buf[1] == TLN_DONT) &&
82     buf[2] == TLN_STATUS)
83     return 1;
84     buf++;
85     }
86     return 0;
87     }
88    
89     /* Escape telnet IAC and prepend CR to LF */
90     static char *escape_telnet(char *s)
91     {
92     static char buf[1024];
93     char *p;
94    
95     for (p = buf; *s && (p < (buf + sizeof(buf) - 2)); *p++ = *s++)
96     if ((unsigned char) *s == TLN_IAC)
97     *p++ = *s;
98     else if (*s == '\n')
99     *p++ = '\r';
100     *p = 0;
101    
102     return buf;
103     }
104    
105     static void strip_telnet(int sock, char *buf, int *len)
106     {
107     unsigned char *p = (unsigned char *) buf, *o = (unsigned char *) buf;
108     int mark;
109    
110     while (*p != 0) {
111     while ((*p != TLN_IAC) && (*p != 0))
112     *o++ = *p++;
113     if (*p == TLN_IAC) {
114     p++;
115     mark = 2;
116     if (!*p)
117     mark = 1; /* bogus */
118     if ((*p >= TLN_WILL) && (*p <= TLN_DONT)) {
119     mark = 3;
120     if (!*(p + 1))
121     mark = 2; /* bogus */
122     } else if (*p == TLN_WILL) {
123     /* WILL X -> response: DONT X */
124     /* except WILL ECHO which we just smile and ignore */
125     if (*(p + 1) != TLN_ECHO) {
126     write(sock, TLN_IAC_C TLN_DONT_C, 2);
127     write(sock, p + 1, 1);
128     }
129     } else if (*p == TLN_DO) {
130     /* DO X -> response: WONT X */
131     /* except DO ECHO which we just smile and ignore */
132     if (*(p + 1) != TLN_ECHO) {
133     write(sock, TLN_IAC_C TLN_WONT_C, 2);
134     write(sock, p + 1, 1);
135     }
136     } else if (*p == TLN_AYT) {
137     /* "Are You There?" */
138     /* response is: "Hell, yes!" */
139     write(sock, "\r\nHell, yes!\r\n", 14);
140     } else if (*p == TLN_IAC) {
141     /* IAC character in data, escaped with another IAC */
142     *o++ = *p++;
143     mark = 1;
144     }
145     /* Anything else can probably be ignored */
146     p += mark - 1;
147     *len = *len - mark;
148     }
149     }
150     *o = *p;
151     }
152    
153     static void greet_new_bot(int idx)
154     {
155     int bfl = bot_flags(dcc[idx].user);
156     int i;
157    
158     dcc[idx].timeval = now;
159     dcc[idx].u.bot->version[0] = 0;
160     dcc[idx].u.bot->numver = 0;
161     if (bfl & BOT_REJECT) {
162     putlog(LOG_BOTS, "*", DCC_REJECT, dcc[idx].nick);
163     dprintf(idx, "bye %s\n", "rejected");
164     killsock(dcc[idx].sock);
165     lostdcc(idx);
166     return;
167     }
168     if (bfl & BOT_LEAF)
169     dcc[idx].status |= STAT_LEAF;
170     dcc[idx].status |= STAT_LINKING;
171     #ifndef NO_OLD_BOTNET
172     dprintf(idx, "version %d %d %s <%s>\n", egg_numver, HANDLEN, ver, network);
173     #else
174     dprintf(idx, "v %d %d %s <%s>\n", egg_numver, HANDLEN, ver, network);
175     #endif
176     for (i = 0; i < dcc_total; i++)
177     if (dcc[i].type == &DCC_FORK_BOT) {
178     killsock(dcc[i].sock);
179     lostdcc(i);
180     }
181     }
182    
183     static void bot_version(int idx, char *par)
184     {
185     char x[1024];
186     int l;
187    
188     dcc[idx].timeval = now;
189     if (in_chain(dcc[idx].nick)) {
190     dprintf(idx, "error Sorry, already connected.\n");
191     dprintf(idx, "bye\n");
192     killsock(dcc[idx].sock);
193     lostdcc(idx);
194     return;
195     }
196     if ((par[0] >= '0') && (par[0] <= '9')) {
197     char *work;
198    
199     work = newsplit(&par);
200     dcc[idx].u.bot->numver = atoi(work);
201     } else
202     dcc[idx].u.bot->numver = 0;
203    
204     #ifndef NO_OLD_BOTNET
205     if (b_numver(idx) < NEAT_BOTNET) {
206     #if HANDLEN != 9
207     putlog(LOG_BOTS, "*", "Non-matching handle lengths with %s, they use 9 "
208     "characters.", dcc[idx].nick);
209     dprintf(idx, "error Non-matching handle length: mine %d, yours 9\n",
210     HANDLEN);
211     dprintf(idx, "bye %s\n", "bad handlen");
212     killsock(dcc[idx].sock);
213     lostdcc(idx);
214     return;
215     #else
216     dprintf(idx, "thisbot %s\n", botnetnick);
217     #endif
218     } else {
219     #endif
220     dprintf(idx, "tb %s\n", botnetnick);
221     l = atoi(newsplit(&par));
222     if (l != HANDLEN) {
223     putlog(LOG_BOTS, "*", "Non-matching handle lengths with %s, they use %d "
224     "characters.", dcc[idx].nick, l);
225     dprintf(idx, "error Non-matching handle length: mine %d, yours %d\n",
226     HANDLEN, l);
227     dprintf(idx, "bye %s\n", "bad handlen");
228     killsock(dcc[idx].sock);
229     lostdcc(idx);
230     return;
231     }
232     #ifndef NO_OLD_BOTNET
233     }
234     #endif
235     strncpyz(dcc[idx].u.bot->version, par, 120);
236     putlog(LOG_BOTS, "*", DCC_LINKED, dcc[idx].nick);
237     chatout("*** Linked to %s\n", dcc[idx].nick);
238     botnet_send_nlinked(idx, dcc[idx].nick, botnetnick, '!',
239     dcc[idx].u.bot->numver);
240     touch_laston(dcc[idx].user, "linked", now);
241     dump_links(idx);
242     dcc[idx].type = &DCC_BOT;
243     addbot(dcc[idx].nick, dcc[idx].nick, botnetnick, '-', dcc[idx].u.bot->numver);
244     check_tcl_link(dcc[idx].nick, botnetnick);
245     egg_snprintf(x, sizeof x, "v %d", dcc[idx].u.bot->numver);
246     bot_share(idx, x);
247     dprintf(idx, "el\n");
248     }
249    
250     void failed_link(int idx)
251     {
252     char s[81], s1[512];
253    
254     if (dcc[idx].port >= dcc[idx].u.bot->port + 3) {
255     if (dcc[idx].u.bot->linker[0]) {
256     egg_snprintf(s, sizeof s, "Couldn't link to %s.", dcc[idx].nick);
257     strcpy(s1, dcc[idx].u.bot->linker);
258     add_note(s1, botnetnick, s, -2, 0);
259     }
260     if (dcc[idx].u.bot->numver >= -1)
261     putlog(LOG_BOTS, "*", DCC_LINKFAIL, dcc[idx].nick);
262     killsock(dcc[idx].sock);
263     strcpy(s, dcc[idx].nick);
264     lostdcc(idx);
265     autolink_cycle(s); /* Check for more auto-connections */
266     return;
267     }
268    
269     /* Try next port */
270     killsock(dcc[idx].sock);
271     dcc[idx].port++;
272     dcc[idx].timeval = now;
273 pseudo 1.2 dcc[idx].sock = getsock(setsockname(&dcc[idx].sockname, dcc[idx].host,
274     dcc[idx].port, 1), SOCK_STRONGCONN);
275 simple 1.1 if (dcc[idx].sock < 0 ||
276 pseudo 1.2 open_telnet_raw(dcc[idx].sock, &dcc[idx].sockname) < 0)
277 simple 1.1 failed_link(idx);
278     }
279    
280     static void cont_link(int idx, char *buf, int i)
281     {
282     char x[1024];
283     int atr = bot_flags(dcc[idx].user);
284     int users, bots;
285    
286     if (atr & BOT_HUB) {
287     /* Disconnect all +a bots because we just got a hub */
288     for (i = 0; i < dcc_total; i++) {
289     if ((i != idx) && (bot_flags(dcc[i].user) & BOT_ALT)) {
290     if ((dcc[i].type == &DCC_FORK_BOT) || (dcc[i].type == &DCC_BOT_NEW)) {
291     killsock(dcc[i].sock);
292     lostdcc(i);
293     }
294     }
295     }
296     /* Just those currently in the process of linking */
297     if (in_chain(dcc[idx].nick)) {
298     i = nextbot(dcc[idx].nick);
299     if (i > 0) {
300     bots = bots_in_subtree(findbot(dcc[idx].nick));
301     users = users_in_subtree(findbot(dcc[idx].nick));
302     egg_snprintf(x, sizeof x,
303     "Unlinked %s (restructure) (lost %d bot%s and %d user%s)",
304     dcc[i].nick, bots, (bots != 1) ? "s" : "",
305     users, (users != 1) ? "s" : "");
306     chatout("*** %s\n", x);
307     botnet_send_unlinked(i, dcc[i].nick, x);
308     dprintf(i, "bye %s\n", "restructure");
309     killsock(dcc[i].sock);
310     lostdcc(i);
311     }
312     }
313     }
314     dcc[idx].type = &DCC_BOT_NEW;
315     dcc[idx].u.bot->numver = 0;
316    
317     /* Don't send our password here, just the username. The code later on
318     * will determine if the password needs to be sent in cleartext or if
319     * we can send an MD5 digest. <cybah>
320     */
321     dprintf(idx, "%s\n", botnetnick);
322     return;
323     }
324    
325     /* This function generates a digest by combining 'challenge' with
326     * 'password' and then sends it to the other bot. <Cybah>
327     */
328     static void dcc_bot_digest(int idx, char *challenge, char *password)
329     {
330     MD5_CTX md5context;
331     char digest_string[33]; /* 32 for digest in hex + null */
332     unsigned char digest[16];
333     int i;
334    
335     MD5_Init(&md5context);
336     MD5_Update(&md5context, (unsigned char *) challenge, strlen(challenge));
337     MD5_Update(&md5context, (unsigned char *) password, strlen(password));
338     MD5_Final(digest, &md5context);
339    
340     for (i = 0; i < 16; i++)
341     sprintf(digest_string + (i * 2), "%.2x", digest[i]);
342     dprintf(idx, "digest %s\n", digest_string);
343     putlog(LOG_BOTS, "*", "Received challenge from %s... sending response ...",
344     dcc[idx].nick);
345     }
346    
347     static void dcc_bot_new(int idx, char *buf, int x)
348     {
349     struct userrec *u = get_user_by_handle(userlist, dcc[idx].nick);
350     char *code;
351    
352     code = newsplit(&buf);
353     if (!egg_strcasecmp(code, "*hello!"))
354     greet_new_bot(idx);
355     else if (!egg_strcasecmp(code, "version") || !egg_strcasecmp(code, "v"))
356     bot_version(idx, buf);
357     else if (!egg_strcasecmp(code, "badpass"))
358     /* We entered the wrong password */
359     putlog(LOG_BOTS, "*", DCC_BADPASS, dcc[idx].nick);
360     else if (!egg_strcasecmp(code, "passreq")) {
361     char *pass = get_user(&USERENTRY_PASS, u);
362    
363     if (!pass || !strcmp(pass, "-")) {
364     putlog(LOG_BOTS, "*", DCC_PASSREQ, dcc[idx].nick);
365     dprintf(idx, "-\n");
366     } else {
367     /* Determine if the other end supports an MD5 digest instead of a
368     * cleartext password. <Cybah>
369     */
370     if (buf && buf[0] && strchr(buf, '<') && strchr(buf + 1, '>'))
371     dcc_bot_digest(idx, buf, pass);
372     else
373     dprintf(idx, "%s\n", pass);
374     }
375     } else if (!egg_strcasecmp(code, "error"))
376     putlog(LOG_BOTS, "*", DCC_LINKERROR, dcc[idx].nick, buf);
377     /* Ignore otherwise */
378     }
379    
380     static void eof_dcc_bot_new(int idx)
381     {
382     putlog(LOG_BOTS, "*", DCC_LOSTBOT, dcc[idx].nick, dcc[idx].port);
383     killsock(dcc[idx].sock);
384     lostdcc(idx);
385     }
386    
387     static void timeout_dcc_bot_new(int idx)
388     {
389     putlog(LOG_BOTS, "*", DCC_TIMEOUT, dcc[idx].nick,
390     dcc[idx].host, dcc[idx].port);
391     killsock(dcc[idx].sock);
392     lostdcc(idx);
393     }
394    
395     static void display_dcc_bot_new(int idx, char *buf)
396     {
397     long tv;
398    
399     tv = now - dcc[idx].timeval;
400     sprintf(buf, "bot* waited %lis", tv);
401     }
402    
403     static int expmem_dcc_bot_(void *x)
404     {
405     return sizeof(struct bot_info);
406     }
407    
408     static void free_dcc_bot_(int n, void *x)
409     {
410     if (dcc[n].type == &DCC_BOT) {
411     unvia(n, findbot(dcc[n].nick));
412     rembot(dcc[n].nick);
413     }
414     nfree(x);
415     }
416    
417     struct dcc_table DCC_BOT_NEW = {
418     "BOT_NEW",
419     0,
420     eof_dcc_bot_new,
421     dcc_bot_new,
422     &bot_timeout,
423     timeout_dcc_bot_new,
424     display_dcc_bot_new,
425     expmem_dcc_bot_,
426     free_dcc_bot_,
427     NULL
428     };
429    
430     /* Hash function for tandem bot commands */
431     extern botcmd_t C_bot[];
432    
433     static void dcc_bot(int idx, char *code, int i)
434     {
435     char *msg;
436     int f;
437    
438     if (raw_log) {
439     if (code[0] == 's')
440     putlog(LOG_BOTSHARE, "*", "{%s} %s", dcc[idx].nick, code + 2);
441     else
442     putlog(LOG_BOTNET, "*", "[%s] %s", dcc[idx].nick, code);
443     }
444     msg = strchr(code, ' ');
445     if (msg) {
446     *msg = 0;
447     msg++;
448     } else
449     msg = "";
450     for (f = i = 0; C_bot[i].name && !f; i++) {
451     int y = egg_strcasecmp(code, C_bot[i].name);
452    
453     if (!y) {
454     /* Found a match */
455     (C_bot[i].func) (idx, msg);
456     f = 1;
457     } else if (y < 0)
458     return;
459     }
460     }
461    
462     static void eof_dcc_bot(int idx)
463     {
464     char x[1024];
465     int bots, users;
466    
467     bots = bots_in_subtree(findbot(dcc[idx].nick));
468     users = users_in_subtree(findbot(dcc[idx].nick));
469     egg_snprintf(x, sizeof x,
470     "Lost bot: %s (lost %d bot%s and %d user%s)",
471     dcc[idx].nick, bots, (bots != 1) ? "s" : "", users,
472     (users != 1) ? "s" : "");
473     putlog(LOG_BOTS, "*", "%s.", x);
474     chatout("*** %s\n", x);
475     botnet_send_unlinked(idx, dcc[idx].nick, x);
476     killsock(dcc[idx].sock);
477     lostdcc(idx);
478     }
479    
480     static void display_dcc_bot(int idx, char *buf)
481     {
482     int i = simple_sprintf(buf, "bot flags: ");
483    
484     buf[i++] = b_status(idx) & STAT_PINGED ? 'P' : 'p';
485     buf[i++] = b_status(idx) & STAT_SHARE ? 'U' : 'u';
486     buf[i++] = b_status(idx) & STAT_CALLED ? 'C' : 'c';
487     buf[i++] = b_status(idx) & STAT_OFFERED ? 'O' : 'o';
488     buf[i++] = b_status(idx) & STAT_SENDING ? 'S' : 's';
489     buf[i++] = b_status(idx) & STAT_GETTING ? 'G' : 'g';
490     buf[i++] = b_status(idx) & STAT_WARNED ? 'W' : 'w';
491     buf[i++] = b_status(idx) & STAT_LEAF ? 'L' : 'l';
492     buf[i++] = b_status(idx) & STAT_LINKING ? 'I' : 'i';
493     buf[i++] = b_status(idx) & STAT_AGGRESSIVE ? 'a' : 'A';
494     buf[i++] = 0;
495     }
496    
497     static void display_dcc_fork_bot(int idx, char *buf)
498     {
499     sprintf(buf, "conn bot");
500     }
501    
502     struct dcc_table DCC_BOT = {
503     "BOT",
504     DCT_BOT | DCT_VALIDIDX,
505     eof_dcc_bot,
506     dcc_bot,
507     NULL,
508     NULL,
509     display_dcc_bot,
510     expmem_dcc_bot_,
511     free_dcc_bot_,
512     NULL
513     };
514    
515     struct dcc_table DCC_FORK_BOT = {
516     "FORK_BOT",
517     0,
518     failed_link,
519     cont_link,
520     &connect_timeout,
521     failed_link,
522     display_dcc_fork_bot,
523     expmem_dcc_bot_,
524     free_dcc_bot_,
525     NULL
526     };
527    
528     /* This function generates a digest by combining a challenge consisting
529     * of our process id + connection time + botnetnick. The digest is then
530     * compared to the one given by the remote bot.
531     *
532     * Returns 1 if the digest matches, otherwise returns 0.
533     * <Cybah>
534     */
535     static int dcc_bot_check_digest(int idx, char *remote_digest)
536     {
537     MD5_CTX md5context;
538     char digest_string[33]; /* 32 for digest in hex + null */
539     unsigned char digest[16];
540     int i;
541     char *password = get_user(&USERENTRY_PASS, dcc[idx].user);
542    
543     if (!password)
544     return 1;
545    
546     MD5_Init(&md5context);
547    
548     egg_snprintf(digest_string, 33, "<%x%x@", getpid(),
549     (unsigned int) dcc[idx].timeval);
550     MD5_Update(&md5context, (unsigned char *) digest_string,
551     strlen(digest_string));
552     MD5_Update(&md5context, (unsigned char *) botnetnick, strlen(botnetnick));
553     MD5_Update(&md5context, (unsigned char *) ">", 1);
554     MD5_Update(&md5context, (unsigned char *) password, strlen(password));
555    
556     MD5_Final(digest, &md5context);
557    
558     for (i = 0; i < 16; i++)
559     sprintf(digest_string + (i * 2), "%.2x", digest[i]);
560    
561     if (!strcmp(digest_string, remote_digest))
562     return 1;
563    
564     putlog(LOG_BOTS, "*", "Response (password hash) from %s incorrect",
565     dcc[idx].nick);
566     return 0;
567     }
568    
569     static void dcc_chat_pass(int idx, char *buf, int atr)
570     {
571     if (!atr)
572     return;
573     if (dcc[idx].status & STAT_TELNET)
574     strip_telnet(dcc[idx].sock, buf, &atr);
575     else if (detect_telnet((unsigned char *) buf))
576     buf += 3; /* 'IAC','DO(DONT)','STATUS' */
577     atr = dcc[idx].user ? dcc[idx].user->flags : 0;
578    
579     /* Check for MD5 digest from remote _bot_. <cybah> */
580     if ((atr & USER_BOT) && !egg_strncasecmp(buf, "digest ", 7)) {
581     if (dcc_bot_check_digest(idx, buf + 7)) {
582     nfree(dcc[idx].u.chat);
583     dcc[idx].type = &DCC_BOT_NEW;
584     dcc[idx].u.bot = get_data_ptr(sizeof(struct bot_info));
585     dcc[idx].status = STAT_CALLED;
586     dprintf(idx, "*hello!\n");
587     greet_new_bot(idx);
588     return;
589     } else {
590     /* Invalid password/digest */
591     dprintf(idx, "badpass\n");
592     putlog(LOG_MISC, "*", DCC_BADLOGIN, dcc[idx].nick, dcc[idx].host,
593     dcc[idx].port);
594     killsock(dcc[idx].sock);
595     lostdcc(idx);
596     return;
597     }
598     }
599    
600     if (u_pass_match(dcc[idx].user, buf)) {
601     if (atr & USER_BOT) {
602     nfree(dcc[idx].u.chat);
603     dcc[idx].type = &DCC_BOT_NEW;
604     dcc[idx].u.bot = get_data_ptr(sizeof(struct bot_info));
605    
606     dcc[idx].status = STAT_CALLED;
607     dprintf(idx, "*hello!\n");
608     greet_new_bot(idx);
609     } else {
610     /* Log entry for successful login -slennox 3/28/1999 */
611     putlog(LOG_MISC, "*", DCC_LOGGEDIN, dcc[idx].nick,
612     dcc[idx].host, dcc[idx].port);
613     if (dcc[idx].u.chat->away) {
614     nfree(dcc[idx].u.chat->away);
615     dcc[idx].u.chat->away = NULL;
616     }
617     dcc[idx].type = &DCC_CHAT;
618     dcc[idx].status &= ~STAT_CHAT;
619     dcc[idx].u.chat->con_flags = (atr & USER_MASTER) ? conmask : 0;
620     dcc[idx].u.chat->channel = -2;
621     /* Turn echo back on for telnet sessions (send IAC WON'T ECHO). */
622     if (dcc[idx].status & STAT_TELNET)
623     tputs(dcc[idx].sock, TLN_IAC_C TLN_WONT_C TLN_ECHO_C "\n", 4);
624     dcc_chatter(idx);
625     }
626     } else {
627     if (atr & USER_BOT)
628     dprintf(idx, "badpass\n");
629     else
630     dprintf(idx, DCC_HOUSTON);
631     putlog(LOG_MISC, "*", DCC_BADLOGIN, dcc[idx].nick,
632     dcc[idx].host, dcc[idx].port);
633     if (dcc[idx].u.chat->away) { /* su from a dumb user */
634     /* Turn echo back on for telnet sessions (send IAC WON'T ECHO). */
635     if (dcc[idx].status & STAT_TELNET)
636     tputs(dcc[idx].sock, TLN_IAC_C TLN_WONT_C TLN_ECHO_C "\n", 4);
637     dcc[idx].user = get_user_by_handle(userlist, dcc[idx].u.chat->away);
638     strcpy(dcc[idx].nick, dcc[idx].u.chat->away);
639     nfree(dcc[idx].u.chat->away);
640     nfree(dcc[idx].u.chat->su_nick);
641     dcc[idx].u.chat->away = NULL;
642     dcc[idx].u.chat->su_nick = NULL;
643     dcc[idx].type = &DCC_CHAT;
644     if (dcc[idx].u.chat->channel < GLOBAL_CHANS)
645     botnet_send_join_idx(idx, -1);
646     chanout_but(-1, dcc[idx].u.chat->channel, DCC_JOIN, dcc[idx].nick);
647     } else {
648     killsock(dcc[idx].sock);
649     lostdcc(idx);
650     }
651     }
652     }
653    
654     static void eof_dcc_general(int idx)
655     {
656     putlog(LOG_MISC, "*", DCC_LOSTDCC, dcc[idx].nick,
657     dcc[idx].host, dcc[idx].port);
658     killsock(dcc[idx].sock);
659     lostdcc(idx);
660     }
661    
662     static void tout_dcc_chat_pass(int idx)
663     {
664     dprintf(idx, "Timeout.\n");
665     putlog(LOG_MISC, "*", DCC_PWDTIMEOUT, dcc[idx].nick, dcc[idx].host);
666     killsock(dcc[idx].sock);
667     lostdcc(idx);
668     }
669    
670     static void display_dcc_chat_pass(int idx, char *buf)
671     {
672     long tv;
673    
674     tv = now - dcc[idx].timeval;
675     sprintf(buf, "pass waited %lis", tv);
676     }
677    
678     static int expmem_dcc_general(void *x)
679     {
680     register struct chat_info *p = (struct chat_info *) x;
681     int tot = sizeof(struct chat_info);
682    
683     if (p->away)
684     tot += strlen(p->away) + 1;
685     if (p->buffer) {
686     struct msgq *q = p->buffer;
687    
688     while (q) {
689     tot += sizeof(struct list_type);
690    
691     tot += q->len + 1;
692     q = q->next;
693     }
694     }
695     if (p->su_nick)
696     tot += strlen(p->su_nick) + 1;
697     return tot;
698     }
699    
700     static void kill_dcc_general(int idx, void *x)
701     {
702     register struct chat_info *p = (struct chat_info *) x;
703    
704     if (p) {
705     if (p->buffer) {
706     struct msgq *r, *q;
707    
708     for (r = dcc[idx].u.chat->buffer; r; r = q) {
709     q = r->next;
710     nfree(r->msg);
711     nfree(r);
712     }
713     }
714     if (p->away) {
715     nfree(p->away);
716     }
717     nfree(p);
718     }
719     }
720    
721     /* Remove the color control codes that mIRC,pIRCh etc use to make
722     * their client seem so fecking cool! (Sorry, Khaled, you are a nice
723     * guy, but when you added this feature you forced people to either
724     * use your *SHAREWARE* client or face screenfulls of crap!)
725     */
726     void strip_mirc_codes(int flags, char *text)
727     {
728     char *dd = text;
729    
730     while (*text) {
731     switch (*text) {
732     case 2: /* Bold text */
733     if (flags & STRIP_BOLD) {
734     text++;
735     continue;
736     }
737     break;
738     case 3: /* mIRC colors? */
739     if (flags & STRIP_COLOR) {
740     if (egg_isdigit(text[1])) { /* Is the first char a number? */
741     text += 2; /* Skip over the ^C and the first digit */
742     if (egg_isdigit(*text))
743     text++; /* Is this a double digit number? */
744     if (*text == ',') { /* Do we have a background color next? */
745     if (egg_isdigit(text[1]))
746     text += 2; /* Skip over the first background digit */
747     if (egg_isdigit(*text))
748     text++; /* Is it a double digit? */
749     }
750     } else
751     text++;
752     continue;
753     }
754     break;
755     case 7:
756     if (flags & STRIP_BELLS) {
757     text++;
758     continue;
759     }
760     break;
761     case 0x16: /* Reverse video */
762     if (flags & STRIP_REV) {
763     text++;
764     continue;
765     }
766     break;
767     case 0x1f: /* Underlined text */
768     if (flags & STRIP_UNDER) {
769     text++;
770     continue;
771     }
772     break;
773     case 033:
774     if (flags & STRIP_ANSI) {
775     text++;
776     if (*text == '[') {
777     text++;
778     while ((*text == ';') || egg_isdigit(*text))
779     text++;
780     if (*text)
781     text++; /* also kill the following char */
782     }
783     continue;
784     }
785     break;
786     }
787     *dd++ = *text++; /* Move on to the next char */
788     }
789     *dd = 0;
790     }
791    
792     static void append_line(int idx, char *line)
793     {
794     int l = strlen(line);
795     struct msgq *p, *q;
796     struct chat_info *c = (dcc[idx].type == &DCC_CHAT) ? dcc[idx].u.chat :
797     dcc[idx].u.file->chat;
798    
799     if (c->current_lines > 1000) {
800     /* They're probably trying to fill up the bot nuke the sods :) */
801     for (p = c->buffer; p; p = q) {
802     q = p->next;
803     nfree(p->msg);
804     nfree(p);
805     }
806     c->buffer = 0;
807     dcc[idx].status &= ~STAT_PAGE;
808     do_boot(idx, botnetnick, "too many pages - sendq full");
809     return;
810     }
811     if ((c->line_count < c->max_line) && (c->buffer == NULL)) {
812     c->line_count++;
813     tputs(dcc[idx].sock, line, l);
814     } else {
815     c->current_lines++;
816     if (c->buffer == NULL)
817     q = NULL;
818     else
819     for (q = c->buffer; q->next; q = q->next);
820    
821     p = get_data_ptr(sizeof(struct msgq));
822    
823     p->len = l;
824     p->msg = get_data_ptr(l + 1);
825     p->next = NULL;
826     strcpy(p->msg, line);
827     if (q == NULL)
828     c->buffer = p;
829     else
830     q->next = p;
831     }
832     }
833    
834    
835     static void out_dcc_general(int idx, char *buf, void *x)
836     {
837     register struct chat_info *p = (struct chat_info *) x;
838     char *y = buf;
839    
840     strip_mirc_codes(p->strip_flags, buf);
841     if (dcc[idx].status & STAT_TELNET)
842     y = escape_telnet(buf);
843     if (dcc[idx].status & STAT_PAGE)
844     append_line(idx, y);
845     else
846     tputs(dcc[idx].sock, y, strlen(y));
847     }
848    
849     struct dcc_table DCC_CHAT_PASS = {
850     "CHAT_PASS",
851     0,
852     eof_dcc_general,
853     dcc_chat_pass,
854     &password_timeout,
855     tout_dcc_chat_pass,
856     display_dcc_chat_pass,
857     expmem_dcc_general,
858     kill_dcc_general,
859     out_dcc_general
860     };
861    
862     /* Make sure ANSI code is just for color-changing */
863     int check_ansi(char *v)
864     {
865     int count = 2;
866    
867     if (*v++ != '\033')
868     return 1;
869     if (*v++ != '[')
870     return 1;
871     while (*v) {
872     if (*v == 'm')
873     return 0;
874     if ((*v != ';') && ((*v < '0') || (*v > '9')))
875     return count;
876     v++;
877     count++;
878     }
879     return count;
880     }
881    
882     static void eof_dcc_chat(int idx)
883     {
884     putlog(LOG_MISC, "*", DCC_LOSTDCC, dcc[idx].nick,
885     dcc[idx].host, dcc[idx].port);
886     if (dcc[idx].u.chat->channel >= 0) {
887     chanout_but(idx, dcc[idx].u.chat->channel, "*** %s lost dcc link.\n",
888     dcc[idx].nick);
889     if (dcc[idx].u.chat->channel < GLOBAL_CHANS)
890     botnet_send_part_idx(idx, "lost dcc link");
891     check_tcl_chpt(botnetnick, dcc[idx].nick, dcc[idx].sock,
892     dcc[idx].u.chat->channel);
893     }
894     check_tcl_chof(dcc[idx].nick, dcc[idx].sock);
895     killsock(dcc[idx].sock);
896     lostdcc(idx);
897     }
898    
899     static void dcc_chat(int idx, char *buf, int i)
900     {
901     int nathan = 0, doron = 0, fixed = 0;
902     char *v, *d, filtbuf[2048];
903    
904     if (dcc[idx].status & STAT_TELNET)
905     strip_telnet(dcc[idx].sock, buf, &i);
906     if (buf[0] && (buf[0] != '.') &&
907     detect_dcc_flood(&dcc[idx].timeval, dcc[idx].u.chat, idx))
908     return;
909     dcc[idx].timeval = now;
910     if (buf[0]) {
911     const char *filt = check_tcl_filt(idx, buf);
912     if (filt != buf) {
913     strncpyz(filtbuf, filt, sizeof(filtbuf));
914     buf = filtbuf;
915     }
916     }
917     if (buf[0]) {
918     /* Check for beeps and cancel annoying ones */
919     v = buf;
920     d = buf;
921     while (*v)
922     switch (*v) {
923     case 7: /* Beep - no more than 3 */
924     nathan++;
925     if (nathan > 3)
926     v++;
927     else
928     *d++ = *v++;
929     break;
930     case 8: /* Backspace - for lame telnet's :) */
931     if (d > buf) {
932     d--;
933     }
934     v++;
935     break;
936     case 27: /* ESC - ansi code? */
937     doron = check_ansi(v);
938     /* If it's valid, append a return-to-normal code at the end */
939     if (!doron) {
940     *d++ = *v++;
941     fixed = 1;
942     } else
943     v += doron;
944     break;
945     case '\r': /* Weird pseudo-linefeed */
946     v++;
947     break;
948     default:
949     *d++ = *v++;
950     }
951     if (fixed)
952     strcpy(d, "\033[0m");
953     else
954     *d = 0;
955     if (buf[0]) { /* Nothing to say - maybe paging... */
956     if ((buf[0] == '.') || (dcc[idx].u.chat->channel < 0)) {
957     if (buf[0] == '.')
958     buf++;
959     v = newsplit(&buf);
960     rmspace(buf);
961     if (check_tcl_dcc(v, idx, buf)) {
962     if (dcc[idx].u.chat->channel >= 0)
963     check_tcl_chpt(botnetnick, dcc[idx].nick, dcc[idx].sock,
964     dcc[idx].u.chat->channel);
965     check_tcl_chof(dcc[idx].nick, dcc[idx].sock);
966     dprintf(idx, "*** Ja mata!\n");
967     flush_lines(idx, dcc[idx].u.chat);
968     putlog(LOG_MISC, "*", DCC_CLOSED, dcc[idx].nick, dcc[idx].host);
969     if (dcc[idx].u.chat->channel >= 0) {
970     chanout_but(-1, dcc[idx].u.chat->channel,
971     "*** %s left the party line%s%s\n",
972     dcc[idx].nick, buf[0] ? ": " : ".", buf);
973     if (dcc[idx].u.chat->channel < GLOBAL_CHANS)
974     botnet_send_part_idx(idx, buf);
975     }
976     if (dcc[idx].u.chat->su_nick) {
977     dcc[idx].user = get_user_by_handle(userlist,
978     dcc[idx].u.chat->su_nick);
979     strcpy(dcc[idx].nick, dcc[idx].u.chat->su_nick);
980     dcc[idx].type = &DCC_CHAT;
981     dprintf(idx, "Returning to real nick %s!\n",
982     dcc[idx].u.chat->su_nick);
983     nfree(dcc[idx].u.chat->su_nick);
984     dcc[idx].u.chat->su_nick = NULL;
985     dcc_chatter(idx);
986     if (dcc[idx].u.chat->channel < GLOBAL_CHANS &&
987     dcc[idx].u.chat->channel >= 0)
988     botnet_send_join_idx(idx, -1);
989     return;
990     } else if ((dcc[idx].sock != STDOUT) || backgrd) {
991     killsock(dcc[idx].sock);
992     lostdcc(idx);
993     return;
994     } else {
995     dprintf(DP_STDOUT, "\n### SIMULATION RESET\n\n");
996     dcc_chatter(idx);
997     return;
998     }
999     }
1000     } else if (buf[0] == ',') {
1001     int me = 0;
1002    
1003     if ((buf[1] == 'm') && (buf[2] == 'e') && buf[3] == ' ')
1004     me = 1;
1005     for (i = 0; i < dcc_total; i++) {
1006     int ok = 0;
1007    
1008     if ((dcc[i].type->flags & DCT_MASTER) &&
1009     ((dcc[i].type != &DCC_CHAT) || (dcc[i].u.chat->channel >= 0)) &&
1010     ((i != idx) || (dcc[idx].status & STAT_ECHO)))
1011     ok = 1;
1012     if (ok) {
1013     struct userrec *u = get_user_by_handle(userlist, dcc[i].nick);
1014    
1015     if (u && (u->flags & USER_MASTER)) {
1016     if (me)
1017     dprintf(i, "-> %s%s\n", dcc[idx].nick, buf + 3);
1018     else
1019     dprintf(i, "-%s-> %s\n", dcc[idx].nick, buf + 1);
1020     }
1021     }
1022     }
1023     } else if (buf[0] == '\'') {
1024     int me = 0;
1025    
1026     if ((buf[1] == 'm') && (buf[2] == 'e') &&
1027     ((buf[3] == ' ') || (buf[3] == '\'') || (buf[3] == ',')))
1028     me = 1;
1029     for (i = 0; i < dcc_total; i++) {
1030     if (dcc[i].type->flags & DCT_CHAT) {
1031     if (me)
1032     dprintf(i, "=> %s%s\n", dcc[idx].nick, buf + 3);
1033     else
1034     dprintf(i, "=%s=> %s\n", dcc[idx].nick, buf + 1);
1035     }
1036     }
1037     } else {
1038     if (dcc[idx].u.chat->away != NULL)
1039     not_away(idx);
1040     if (dcc[idx].status & STAT_ECHO)
1041     chanout_but(-1, dcc[idx].u.chat->channel,
1042     "<%s> %s\n", dcc[idx].nick, buf);
1043     else
1044     chanout_but(idx, dcc[idx].u.chat->channel, "<%s> %s\n",
1045     dcc[idx].nick, buf);
1046     botnet_send_chan(-1, botnetnick, dcc[idx].nick,
1047     dcc[idx].u.chat->channel, buf);
1048     check_tcl_chat(dcc[idx].nick, dcc[idx].u.chat->channel, buf);
1049     }
1050     }
1051     }
1052     if (dcc[idx].type == &DCC_CHAT) /* Could have change to files */
1053     if (dcc[idx].status & STAT_PAGE)
1054     flush_lines(idx, dcc[idx].u.chat);
1055     }
1056    
1057     static void display_dcc_chat(int idx, char *buf)
1058     {
1059     int i = simple_sprintf(buf, "chat flags: ");
1060    
1061     buf[i++] = dcc[idx].status & STAT_CHAT ? 'C' : 'c';
1062     buf[i++] = dcc[idx].status & STAT_PARTY ? 'P' : 'p';
1063     buf[i++] = dcc[idx].status & STAT_TELNET ? 'T' : 't';
1064     buf[i++] = dcc[idx].status & STAT_ECHO ? 'E' : 'e';
1065     buf[i++] = dcc[idx].status & STAT_PAGE ? 'P' : 'p';
1066     simple_sprintf(buf + i, "/%d", dcc[idx].u.chat->channel);
1067     }
1068    
1069     struct dcc_table DCC_CHAT = {
1070     "CHAT",
1071     DCT_CHAT | DCT_MASTER | DCT_SHOWWHO | DCT_VALIDIDX | DCT_SIMUL |
1072     DCT_CANBOOT | DCT_REMOTEWHO,
1073     eof_dcc_chat,
1074     dcc_chat,
1075     NULL,
1076     NULL,
1077     display_dcc_chat,
1078     expmem_dcc_general,
1079     kill_dcc_general,
1080     out_dcc_general
1081     };
1082    
1083     static int lasttelnets;
1084     static char lasttelnethost[81];
1085     static time_t lasttelnettime;
1086    
1087     /* A modified detect_flood for incoming telnet flood protection.
1088     */
1089     static int detect_telnet_flood(char *floodhost)
1090     {
1091     struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0 };
1092    
1093     get_user_flagrec(get_user_by_host(floodhost), &fr, NULL);
1094     if (!flood_telnet_thr || (glob_friend(fr) && !par_telnet_flood))
1095     return 0; /* No flood protection */
1096     if (egg_strcasecmp(lasttelnethost, floodhost)) { /* New... */
1097     strcpy(lasttelnethost, floodhost);
1098     lasttelnettime = now;
1099     lasttelnets = 0;
1100     return 0;
1101     }
1102     if (lasttelnettime < now - flood_telnet_time) {
1103     /* Flood timer expired, reset it */
1104     lasttelnettime = now;
1105     lasttelnets = 0;
1106     return 0;
1107     }
1108     lasttelnets++;
1109     if (lasttelnets >= flood_telnet_thr) { /* FLOOD! */
1110     /* Reset counters */
1111     lasttelnets = 0;
1112     lasttelnettime = 0;
1113     lasttelnethost[0] = 0;
1114     putlog(LOG_MISC, "*", IRC_TELNETFLOOD, floodhost);
1115     addignore(floodhost, origbotname, "Telnet connection flood",
1116     now + (60 * ignore_time));
1117     return 1;
1118     }
1119     return 0;
1120     }
1121    
1122     static void dcc_telnet(int idx, char *buf, int i)
1123     {
1124     unsigned short port;
1125     int j = 0, sock;
1126    
1127     if (dcc_total + 1 > max_dcc && increase_socks_max()) {
1128 pseudo 1.2 sockname_t name;
1129     j = answer(dcc[idx].sock, &name, &port, 0);
1130 simple 1.1 if (j != -1) {
1131     dprintf(-j, "Sorry, too many connections already.\r\n");
1132     killsock(j);
1133     }
1134     return;
1135     }
1136 pseudo 1.2 i = new_dcc(&DCC_DNSWAIT, sizeof(struct dns_info));
1137     sock = answer(dcc[idx].sock, &dcc[i].sockname, &port, 0);
1138 simple 1.1 while ((sock == -1) && (errno == EAGAIN))
1139 pseudo 1.2 sock = answer(dcc[idx].sock, &dcc[i].sockname, &port, 0);
1140 simple 1.1 if (sock < 0) {
1141 pseudo 1.2 putlog(LOG_MISC, "*", DCC_FAILED, strerror(errno));
1142 simple 1.1 return;
1143     }
1144     /* Buffer data received on this socket. */
1145     sockoptions(sock, EGG_OPTION_SET, SOCK_BUFFER);
1146    
1147     #if (SIZEOF_SHORT == 2)
1148     if (port < 1024) {
1149     #else
1150     if (port < 1024 || port > 65535) {
1151     #endif
1152 pseudo 1.2 putlog(LOG_BOTS, "*", DCC_BADSRC, iptostr(&dcc[i].sockname.addr.sa), port);
1153 simple 1.1 killsock(sock);
1154 pseudo 1.2 lostdcc(i);
1155 simple 1.1 return;
1156     }
1157    
1158 pseudo 1.2 dcc[i].u.dns->ip = &dcc[i].sockname;
1159 simple 1.1 dcc[i].sock = sock;
1160     dcc[i].port = port;
1161     dcc[i].timeval = now;
1162     strcpy(dcc[i].nick, "*");
1163     dcc[i].u.dns->dns_success = dcc_telnet_hostresolved;
1164     dcc[i].u.dns->dns_failure = dcc_telnet_hostresolved;
1165     dcc[i].u.dns->dns_type = RES_HOSTBYIP;
1166     dcc[i].u.dns->ibuf = dcc[idx].sock;
1167     dcc[i].u.dns->type = &DCC_IDENTWAIT;
1168 pseudo 1.2 dcc_dnshostbyip(&dcc[i].sockname);
1169 simple 1.1 }
1170    
1171     static void dcc_telnet_hostresolved(int i)
1172     {
1173     int idx;
1174     int j = 0, sock;
1175 pseudo 1.2 char s[UHOSTLEN + 20];
1176 simple 1.1
1177     strncpyz(dcc[i].host, dcc[i].u.dns->host, UHOSTLEN);
1178    
1179     for (idx = 0; idx < dcc_total; idx++)
1180     if ((dcc[idx].type == &DCC_TELNET) &&
1181     (dcc[idx].sock == dcc[i].u.dns->ibuf)) {
1182     break;
1183     }
1184     if (dcc_total == idx) {
1185     putlog(LOG_BOTS, "*", "Lost listening socket while resolving %s",
1186     dcc[i].host);
1187     killsock(dcc[i].sock);
1188     lostdcc(i);
1189     return;
1190     }
1191     if (dcc[idx].host[0] == '@') {
1192     /* Restrict by hostname */
1193     if (!wild_match(dcc[idx].host + 1, dcc[i].host)) {
1194     putlog(LOG_BOTS, "*", DCC_BADHOST, dcc[i].host);
1195     killsock(dcc[i].sock);
1196     lostdcc(i);
1197     return;
1198     }
1199     }
1200 pseudo 1.2 sprintf(s, "-telnet!telnet@%s", dcc[i].host);
1201     if (match_ignore(s) || detect_telnet_flood(s)) {
1202 simple 1.1 killsock(dcc[i].sock);
1203     lostdcc(i);
1204     return;
1205     }
1206    
1207     changeover_dcc(i, &DCC_IDENTWAIT, 0);
1208     dcc[i].timeval = now;
1209     dcc[i].u.ident_sock = dcc[idx].sock;
1210 pseudo 1.2 sock = -1;
1211     j = new_dcc(&DCC_IDENT, 0);
1212     if (j < 0)
1213     putlog(LOG_MISC, "*", DCC_IDENTFAIL, dcc[i].host, strerror(errno));
1214     else {
1215     egg_memcpy(&dcc[j].sockname, &dcc[i].sockname, sizeof(sockname_t));
1216     dcc[j].sock = getsock(dcc[j].sockname.family, 0);
1217     if (dcc[j].sock >= 0) {
1218     sockname_t name;
1219     name.addrlen = sizeof(name.addr);
1220     getsockname(dcc[i].sock, &name.addr.sa, &name.addrlen);
1221     bind(dcc[j].sock, &name.addr.sa, name.addrlen);
1222     setsnport(dcc[j].sockname, 113);
1223     if (connect(dcc[j].sock, &dcc[j].sockname.addr.sa,
1224     dcc[j].sockname.addrlen) < 0 && (errno != EINPROGRESS)) {
1225     killsock(dcc[j].sock);
1226     lostdcc(j);
1227     putlog(LOG_MISC, "*", DCC_IDENTFAIL, dcc[i].host, strerror(errno));
1228     j = 0;
1229     }
1230     sock = dcc[j].sock;
1231 simple 1.1 }
1232     }
1233 pseudo 1.2 if (j < 0) {
1234 simple 1.1 sprintf(s, "telnet@%s", dcc[i].host);
1235     dcc_telnet_got_ident(i, s);
1236     return;
1237     }
1238     dcc[j].sock = sock;
1239     dcc[j].port = 113;
1240     dcc[j].addr = dcc[i].addr;
1241     strcpy(dcc[j].host, dcc[i].host);
1242     strcpy(dcc[j].nick, "*");
1243     dcc[j].u.ident_sock = dcc[i].sock;
1244     dcc[j].timeval = now;
1245     dprintf(j, "%d, %d\n", dcc[i].port, dcc[idx].port);
1246     }
1247    
1248     static void eof_dcc_telnet(int idx)
1249     {
1250     putlog(LOG_MISC, "*", DCC_PORTDIE, dcc[idx].port);
1251     killsock(dcc[idx].sock);
1252     lostdcc(idx);
1253     }
1254    
1255     static void display_telnet(int idx, char *buf)
1256     {
1257     sprintf(buf, "lstn %d%s", dcc[idx].port,
1258     (dcc[idx].status & LSTN_PUBLIC) ? " pub" : "");
1259     }
1260    
1261     struct dcc_table DCC_TELNET = {
1262     "TELNET",
1263     DCT_LISTEN,
1264     eof_dcc_telnet,
1265     dcc_telnet,
1266     NULL,
1267     NULL,
1268     display_telnet,
1269     NULL,
1270     NULL,
1271     NULL
1272     };
1273    
1274     static void eof_dcc_dupwait(int idx)
1275     {
1276     putlog(LOG_BOTS, "*", DCC_LOSTDUP, dcc[idx].host);
1277     killsock(dcc[idx].sock);
1278     lostdcc(idx);
1279     }
1280    
1281     static void dcc_dupwait(int idx, char *buf, int i)
1282     {
1283     /* We just ignore any data at this point. */
1284     return;
1285     }
1286    
1287     /* We now check again. If the bot is still marked as duplicate, there is no
1288     * botnet lag we could push it on, so we just drop the connection.
1289     */
1290     static void timeout_dupwait(int idx)
1291     {
1292     char x[100];
1293    
1294     /* Still duplicate? */
1295     if (in_chain(dcc[idx].nick)) {
1296     egg_snprintf(x, sizeof x, "%s!%s", dcc[idx].nick, dcc[idx].host);
1297     dprintf(idx, "error Already connected.\n");
1298     putlog(LOG_BOTS, "*", DCC_DUPLICATE, x);
1299     killsock(dcc[idx].sock);
1300     lostdcc(idx);
1301     } else {
1302     /* Ha! Now it's gone and we can grant this bot access. */
1303     dcc_telnet_pass(idx, dcc[idx].u.dupwait->atr);
1304     }
1305     }
1306    
1307     static void display_dupwait(int idx, char *buf)
1308     {
1309     sprintf(buf, "wait duplicate?");
1310     }
1311    
1312     static int expmem_dupwait(void *x)
1313     {
1314     register struct dupwait_info *p = (struct dupwait_info *) x;
1315     int tot = sizeof(struct dupwait_info);
1316    
1317     if (p && p->chat && DCC_CHAT.expmem)
1318     tot += DCC_CHAT.expmem(p->chat);
1319     return tot;
1320     }
1321    
1322     static void kill_dupwait(int idx, void *x)
1323     {
1324     register struct dupwait_info *p = (struct dupwait_info *) x;
1325    
1326     if (p) {
1327     if (p->chat && DCC_CHAT.kill)
1328     DCC_CHAT.kill(idx, p->chat);
1329     nfree(p);
1330     }
1331     }
1332    
1333     struct dcc_table DCC_DUPWAIT = {
1334     "DUPWAIT",
1335     DCT_VALIDIDX,
1336     eof_dcc_dupwait,
1337     dcc_dupwait,
1338     &dupwait_timeout,
1339     timeout_dupwait,
1340     display_dupwait,
1341     expmem_dupwait,
1342     kill_dupwait,
1343     NULL
1344     };
1345    
1346     /* This function is called if a bot gets removed from the list. It checks
1347     * wether we have a pending duplicate connection for that bot and continues
1348     * with the login in that case.
1349     */
1350     void dupwait_notify(char *who)
1351     {
1352     register int idx;
1353    
1354     Assert(who);
1355     for (idx = 0; idx < dcc_total; idx++)
1356     if ((dcc[idx].type == &DCC_DUPWAIT) &&
1357     !egg_strcasecmp(dcc[idx].nick, who)) {
1358     dcc_telnet_pass(idx, dcc[idx].u.dupwait->atr);
1359     break;
1360     }
1361     }
1362    
1363     static void dcc_telnet_id(int idx, char *buf, int atr)
1364     {
1365     int ok = 0;
1366     struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0 };
1367    
1368     if (detect_telnet((unsigned char *) buf)) {
1369     dcc[idx].status |= STAT_TELNET;
1370     strip_telnet(dcc[idx].sock, buf, &atr);
1371     } else
1372     dcc[idx].status &= ~STAT_TELNET;
1373     buf[HANDLEN] = 0;
1374     /* Toss out bad nicknames */
1375     if (dcc[idx].nick[0] != '@' && !wild_match(dcc[idx].nick, buf)) {
1376     dprintf(idx, "Sorry, that nickname format is invalid.\n");
1377     putlog(LOG_BOTS, "*", DCC_BADNICK, dcc[idx].host);
1378     killsock(dcc[idx].sock);
1379     lostdcc(idx);
1380     return;
1381     }
1382     dcc[idx].user = get_user_by_handle(userlist, buf);
1383     get_user_flagrec(dcc[idx].user, &fr, NULL);
1384     /* Make sure users-only/bots-only connects are honored */
1385     if ((dcc[idx].status & STAT_BOTONLY) && !glob_bot(fr)) {
1386     dprintf(idx, "This telnet port is for bots only.\n");
1387     putlog(LOG_BOTS, "*", DCC_NONBOT, dcc[idx].host);
1388     killsock(dcc[idx].sock);
1389     lostdcc(idx);
1390     return;
1391     }
1392     if ((dcc[idx].status & STAT_USRONLY) && glob_bot(fr)) {
1393     dprintf(idx, "error Only users may connect at this port.\n");
1394     putlog(LOG_BOTS, "*", DCC_NONUSER, dcc[idx].host);
1395     killsock(dcc[idx].sock);
1396     lostdcc(idx);
1397     return;
1398     }
1399     dcc[idx].status &= ~(STAT_BOTONLY | STAT_USRONLY);
1400     if (!egg_strcasecmp(buf, "NEW") && (allow_new_telnets || make_userfile)) {
1401     dcc[idx].type = &DCC_TELNET_NEW;
1402     dcc[idx].timeval = now;
1403     dprintf(idx, "\n");
1404     dprintf(idx, IRC_TELNET, botnetnick);
1405     dprintf(idx, IRC_TELNET1);
1406     dprintf(idx, "\nEnter the nickname you would like to use.\n");
1407     return;
1408     }
1409     if (chan_op(fr)) {
1410     if (!require_p)
1411     ok = 1;
1412     }
1413     if (!ok && (glob_party(fr) || glob_bot(fr)))
1414     ok = 1;
1415    
1416     if (!ok) {
1417     dprintf(idx, "You don't have access.\n");
1418     putlog(LOG_BOTS, "*", DCC_INVHANDLE, dcc[idx].host, buf);
1419     killsock(dcc[idx].sock);
1420     lostdcc(idx);
1421     return;
1422     }
1423     correct_handle(buf);
1424     strcpy(dcc[idx].nick, buf);
1425     if (glob_bot(fr)) {
1426     if (!egg_strcasecmp(botnetnick, dcc[idx].nick)) {
1427     dprintf(idx, "error You cannot link using my botnetnick.\n");
1428     putlog(LOG_BOTS, "*", DCC_MYBOTNETNICK, dcc[idx].host);
1429     killsock(dcc[idx].sock);
1430     lostdcc(idx);
1431     return;
1432     } else if (in_chain(dcc[idx].nick)) {
1433     struct chat_info *ci;
1434    
1435     ci = dcc[idx].u.chat;
1436     dcc[idx].type = &DCC_DUPWAIT;
1437     dcc[idx].u.dupwait = get_data_ptr(sizeof(struct dupwait_info));
1438     dcc[idx].u.dupwait->chat = ci;
1439     dcc[idx].u.dupwait->atr = atr;
1440     return;
1441     }
1442     }
1443     dcc_telnet_pass(idx, atr);
1444     }
1445    
1446     static void dcc_telnet_pass(int idx, int atr)
1447     {
1448     int ok = 0;
1449     struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0 };
1450    
1451     get_user_flagrec(dcc[idx].user, &fr, NULL);
1452     /* No password set? */
1453     if (u_pass_match(dcc[idx].user, "-")) {
1454     if (glob_bot(fr)) {
1455     char ps[20];
1456    
1457     makepass(ps);
1458     set_user(&USERENTRY_PASS, dcc[idx].user, ps);
1459     changeover_dcc(idx, &DCC_BOT_NEW, sizeof(struct bot_info));
1460    
1461     dcc[idx].status = STAT_CALLED;
1462     dprintf(idx, "*hello!\n");
1463     greet_new_bot(idx);
1464     #ifdef NO_OLD_BOTNET
1465     dprintf(idx, "h %s\n", ps);
1466     #else
1467     dprintf(idx, "handshake %s\n", ps);
1468     #endif
1469     return;
1470     }
1471     dprintf(idx, "Can't telnet until you have a password set.\n");
1472     putlog(LOG_MISC, "*", DCC_NOPASS, dcc[idx].nick, dcc[idx].host);
1473     killsock(dcc[idx].sock);
1474     lostdcc(idx);
1475     return;
1476     }
1477     ok = 0;
1478     if (dcc[idx].type == &DCC_DUPWAIT) {
1479     struct chat_info *ci;
1480    
1481     ci = dcc[idx].u.dupwait->chat;
1482     nfree(dcc[idx].u.dupwait);
1483     dcc[idx].u.chat = ci;
1484     }
1485     dcc[idx].type = &DCC_CHAT_PASS;
1486     dcc[idx].timeval = now;
1487     if (glob_botmast(fr))
1488     ok = 1;
1489     else if (chan_op(fr)) {
1490     if (!require_p)
1491     ok = 1;
1492     else if (glob_party(fr))
1493     ok = 1;
1494     } else if (glob_party(fr)) {
1495     ok = 1;
1496     dcc[idx].status |= STAT_PARTY;
1497     }
1498     if (glob_bot(fr))
1499     ok = 1;
1500     if (!ok) {
1501     struct chat_info *ci;
1502    
1503     ci = dcc[idx].u.chat;
1504     dcc[idx].u.file = get_data_ptr(sizeof(struct file_info));
1505     dcc[idx].u.file->chat = ci;
1506     }
1507    
1508     if (glob_bot(fr)) {
1509     /* Must generate a string consisting of our process ID and the current
1510     * time. The bot will add it's password to the end and use it to generate
1511     * an MD5 checksum (always 128bit). The checksum is sent back and this
1512     * end does the same. The remote bot is only allowed access if the
1513     * checksums match.
1514     *
1515     * Please don't fuck with 'timeval', or the digest we generate later for
1516     * authentication will not be correct - you've been warned ;)
1517     * <Cybah>
1518     */
1519     putlog(LOG_BOTS, "*", "Challenging %s...", dcc[idx].nick);
1520     dprintf(idx, "passreq <%x%x@%s>\n", getpid(), dcc[idx].timeval, botnetnick);
1521     } else {
1522     /* NOTE: The MD5 digest used above to prevent cleartext passwords being
1523     * sent across the net will _only_ work when we have the cleartext
1524     * password. User passwords are encrypted (with blowfish usually)
1525     * so the same thing cant be done. Botnet passwords are always
1526     * stored in cleartext, or at least something that can be reversed.
1527     * <Cybah>
1528     */
1529    
1530     /* Turn off remote telnet echo (send IAC WILL ECHO). */
1531     if (dcc[idx].status & STAT_TELNET) {
1532     char buf[1030];
1533     snprintf(buf, sizeof buf, "\n%s%s\r\n", escape_telnet(DCC_ENTERPASS),
1534     TLN_IAC_C TLN_WILL_C TLN_ECHO_C);
1535     tputs(dcc[idx].sock, buf, strlen(buf));
1536     } else
1537     dprintf(idx, "\n%s\n", DCC_ENTERPASS);
1538     }
1539     }
1540    
1541     static void eof_dcc_telnet_id(int idx)
1542     {
1543     putlog(LOG_MISC, "*", DCC_LOSTCON, dcc[idx].host, dcc[idx].port);
1544     killsock(dcc[idx].sock);
1545     lostdcc(idx);
1546     }
1547    
1548     static void timeout_dcc_telnet_id(int idx)
1549     {
1550     dprintf(idx, "Timeout.\n");
1551     putlog(LOG_MISC, "*", DCC_TTIMEOUT, dcc[idx].host);
1552     killsock(dcc[idx].sock);
1553     lostdcc(idx);
1554     }
1555    
1556     static void display_dcc_telnet_id(int idx, char *buf)
1557     {
1558     long tv;
1559    
1560     tv = now - dcc[idx].timeval;
1561     sprintf(buf, "t-in waited %lis", tv);
1562     }
1563    
1564     struct dcc_table DCC_TELNET_ID = {
1565     "TELNET_ID",
1566     0,
1567     eof_dcc_telnet_id,
1568     dcc_telnet_id,
1569     &password_timeout,
1570     timeout_dcc_telnet_id,
1571     display_dcc_telnet_id,
1572     expmem_dcc_general,
1573     kill_dcc_general,
1574     out_dcc_general
1575     };
1576    
1577     static void dcc_telnet_new(int idx, char *buf, int x)
1578     {
1579     int ok = 1;
1580     char work[1024], *p, *q, *r;
1581    
1582     buf[HANDLEN] = 0;
1583     if (dcc[idx].status & STAT_TELNET)
1584     strip_telnet(dcc[idx].sock, buf, &x);
1585     dcc[idx].timeval = now;
1586     for (x = 0; x < strlen(buf); x++)
1587     if ((buf[x] <= 32) || (buf[x] >= 127))
1588     ok = 0;
1589     if (!ok) {
1590     dprintf(idx, "\nYou can't use weird symbols in your nick.\n");
1591     dprintf(idx, "Try another one please:\n");
1592     } else if (strchr(BADHANDCHARS, buf[0]) != NULL) {
1593     dprintf(idx, "\nYou can't start your nick with the character '%c'\n",
1594     buf[0]);
1595     dprintf(idx, "Try another one please:\n");
1596     } else if (get_user_by_handle(userlist, buf)) {
1597     dprintf(idx, "\nSorry, that nickname is taken already.\n");
1598     dprintf(idx, "Try another one please:\n");
1599     return;
1600     } else if (!egg_strcasecmp(buf, botnetnick))
1601     dprintf(idx, "Sorry, can't use my name for a nick.\n");
1602     else {
1603     strcpy(dcc[idx].nick, buf);
1604     if (make_userfile)
1605     userlist = adduser(userlist,
1606     buf, "-telnet!*@*", "-", sanity_check(default_flags |
1607     USER_PARTY | USER_MASTER | USER_OWNER));
1608     else {
1609     p = strchr(dcc[idx].host, '@');
1610     if (p) {
1611     q = p;
1612     *q = 0;
1613     p++;
1614     r = strchr(p, '.');
1615     if (!r)
1616     simple_sprintf(work, "-telnet!%s@%s", dcc[idx].host, p);
1617     else
1618     simple_sprintf(work, "-telnet!%s@*%s", dcc[idx].host, r);
1619     *q = '@';
1620     } else
1621     simple_sprintf(work, "-telnet!*@*%s", dcc[idx].host);
1622     userlist = adduser(userlist, buf, work, "-",
1623     sanity_check(USER_PARTY | default_flags));
1624     }
1625     reaffirm_owners();
1626     dcc[idx].status = STAT_ECHO | STAT_TELNET;
1627     dcc[idx].type = &DCC_CHAT; /* Just so next line will work */
1628     dcc[idx].user = get_user_by_handle(userlist, buf);
1629     check_dcc_attrs(dcc[idx].user, USER_PARTY | default_flags);
1630     dcc[idx].type = &DCC_TELNET_PW;
1631     if (make_userfile) {
1632     dprintf(idx, "\nYOU ARE THE MASTER/OWNER ON THIS BOT NOW\n");
1633     dprintf(idx, IRC_LIMBO);
1634     putlog(LOG_MISC, "*", DCC_INSTCOMPL, buf);
1635     make_userfile = 0;
1636     write_userfile(-1);
1637     add_note(buf, botnetnick, "Welcome to eggdrop! :)", -1, 0);
1638     }
1639     dprintf(idx, "\nOkay, now choose and enter a password:\n");
1640     dprintf(idx, "(Only the first 15 letters are significant.)\n");
1641     }
1642     }
1643    
1644     static void dcc_telnet_pw(int idx, char *buf, int x)
1645     {
1646     char *newpass;
1647     int ok;
1648    
1649     if (dcc[idx].status & STAT_TELNET)
1650     strip_telnet(dcc[idx].sock, buf, &x);
1651     buf[16] = 0;
1652     ok = 1;
1653     if (strlen(buf) < 4) {
1654     dprintf(idx, "\nTry to use at least 4 characters in your password.\n");
1655     dprintf(idx, "Choose and enter a password:\n");
1656     return;
1657     }
1658     for (x = 0; x < strlen(buf); x++)
1659     if ((buf[x] <= 32) || (buf[x] == 127))
1660     ok = 0;
1661     if (!ok) {
1662     dprintf(idx, "\nYou can't use weird symbols in your password.\n");
1663     dprintf(idx, "Try another one please:\n");
1664     return;
1665     }
1666     putlog(LOG_MISC, "*", DCC_NEWUSER, dcc[idx].nick, dcc[idx].host,
1667     dcc[idx].port);
1668     if (notify_new[0]) {
1669     char s[121], s1[121], s2[121];
1670    
1671     sprintf(s, "Introduced to %s, %s", dcc[idx].nick, dcc[idx].host);
1672     strcpy(s1, notify_new);
1673     splitc(s2, s1, ',');
1674     while (s2[0]) {
1675     rmspace(s2);
1676     add_note(s2, botnetnick, s, -1, 0);
1677     splitc(s2, s1, ',');
1678     }
1679     rmspace(s1);
1680     add_note(s1, botnetnick, s, -1, 0);
1681     }
1682     newpass = newsplit(&buf);
1683     set_user(&USERENTRY_PASS, dcc[idx].user, newpass);
1684     dprintf(idx, "\nRemember that! You'll need it next time you log in.\n");
1685     dprintf(idx, "You now have an account on %s...\n\n\n", botnetnick);
1686     dcc[idx].type = &DCC_CHAT;
1687     dcc[idx].u.chat->channel = -2;
1688     dcc_chatter(idx);
1689     }
1690    
1691     static void eof_dcc_telnet_new(int idx)
1692     {
1693     putlog(LOG_MISC, "*", DCC_LOSTNEWUSER, dcc[idx].host, dcc[idx].port);
1694     killsock(dcc[idx].sock);
1695     lostdcc(idx);
1696     }
1697    
1698     static void eof_dcc_telnet_pw(int idx)
1699     {
1700     putlog(LOG_MISC, "*", DCC_LOSTNEWUSR2, dcc[idx].nick, dcc[idx].host,
1701     dcc[idx].port);
1702     deluser(dcc[idx].nick);
1703     killsock(dcc[idx].sock);
1704     lostdcc(idx);
1705     }
1706    
1707     static void tout_dcc_telnet_new(int idx)
1708     {
1709     dprintf(idx, "Guess you're not there. Bye.\n");
1710     putlog(LOG_MISC, "*", DCC_TIMEOUTUSER, dcc[idx].host, dcc[idx].port);
1711     killsock(dcc[idx].sock);
1712     lostdcc(idx);
1713     }
1714    
1715     static void tout_dcc_telnet_pw(int idx)
1716     {
1717     dprintf(idx, "Guess you're not there. Bye.\n");
1718     putlog(LOG_MISC, "*", DCC_TIMEOUTUSR2, dcc[idx].nick,
1719     dcc[idx].host, dcc[idx].port);
1720     killsock(dcc[idx].sock);
1721     lostdcc(idx);
1722     }
1723    
1724     static void display_dcc_telnet_new(int idx, char *buf)
1725     {
1726     long tv;
1727    
1728     tv = now - dcc[idx].timeval;
1729     sprintf(buf, "new waited %lis", tv);
1730     }
1731    
1732     static void display_dcc_telnet_pw(int idx, char *buf)
1733     {
1734     long tv;
1735    
1736     tv = now - dcc[idx].timeval;
1737     sprintf(buf, "newp waited %lis", tv);
1738     }
1739    
1740     struct dcc_table DCC_TELNET_NEW = {
1741     "TELNET_NEW",
1742     0,
1743     eof_dcc_telnet_new,
1744     dcc_telnet_new,
1745     &password_timeout,
1746     tout_dcc_telnet_new,
1747     display_dcc_telnet_new,
1748     expmem_dcc_general,
1749     kill_dcc_general,
1750     out_dcc_general
1751     };
1752    
1753     struct dcc_table DCC_TELNET_PW = {
1754     "TELNET_PW",
1755     0,
1756     eof_dcc_telnet_pw,
1757     dcc_telnet_pw,
1758     &password_timeout,
1759     tout_dcc_telnet_pw,
1760     display_dcc_telnet_pw,
1761     expmem_dcc_general,
1762     kill_dcc_general,
1763     out_dcc_general
1764     };
1765    
1766     static int call_tcl_func(char *name, int idx, char *args)
1767     {
1768     char s[11];
1769    
1770     sprintf(s, "%d", idx);
1771     Tcl_SetVar(interp, "_n", s, 0);
1772     Tcl_SetVar(interp, "_a", args, 0);
1773     if (Tcl_VarEval(interp, name, " $_n $_a", NULL) == TCL_ERROR) {
1774     putlog(LOG_MISC, "*", DCC_TCLERROR, name, tcl_resultstring());
1775     return -1;
1776     }
1777     return tcl_resultint();
1778     }
1779    
1780     static void dcc_script(int idx, char *buf, int len)
1781     {
1782     long oldsock;
1783    
1784     if (dcc[idx].status & STAT_TELNET)
1785     strip_telnet(dcc[idx].sock, buf, &len);
1786     if (!len)
1787     return;
1788    
1789     dcc[idx].timeval = now;
1790     oldsock = dcc[idx].sock; /* Remember the socket number. */
1791     if (call_tcl_func(dcc[idx].u.script->command, dcc[idx].sock, buf)) {
1792     void *old_other = NULL;
1793    
1794     /* Check whether the socket and dcc entry are still valid. They
1795     * might have been killed by `killdcc'. */
1796     if (dcc[idx].sock != oldsock || idx > max_dcc)
1797     return;
1798    
1799     old_other = dcc[idx].u.script->u.other;
1800     dcc[idx].type = dcc[idx].u.script->type;
1801     nfree(dcc[idx].u.script);
1802     dcc[idx].u.other = old_other;
1803     if (dcc[idx].type == &DCC_SOCKET) {
1804     /* Kill the whole thing off */
1805     killsock(dcc[idx].sock);
1806     lostdcc(idx);
1807     return;
1808     }
1809     if (dcc[idx].type == &DCC_CHAT) {
1810     if (dcc[idx].u.chat->channel >= 0) {
1811     chanout_but(-1, dcc[idx].u.chat->channel, DCC_JOIN, dcc[idx].nick);
1812     if (dcc[idx].u.chat->channel < 10000)
1813     botnet_send_join_idx(idx, -1);
1814     check_tcl_chjn(botnetnick, dcc[idx].nick, dcc[idx].u.chat->channel,
1815     geticon(idx), dcc[idx].sock, dcc[idx].host);
1816     }
1817     check_tcl_chon(dcc[idx].nick, dcc[idx].sock);
1818     }
1819     }
1820     }
1821    
1822     static void eof_dcc_script(int idx)
1823     {
1824     void *old;
1825     int oldflags;
1826    
1827     /* This will stop a killdcc from working, incase the script tries
1828     * to kill it's controlling socket while handling an EOF <cybah>
1829     */
1830     oldflags = dcc[idx].type->flags;
1831     dcc[idx].type->flags &= ~(DCT_VALIDIDX);
1832     /* Tell the script they're gone: */
1833     call_tcl_func(dcc[idx].u.script->command, dcc[idx].sock, "");
1834     /* Restore the flags */
1835     dcc[idx].type->flags = oldflags;
1836     old = dcc[idx].u.script->u.other;
1837     dcc[idx].type = dcc[idx].u.script->type;
1838     nfree(dcc[idx].u.script);
1839     dcc[idx].u.other = old;
1840     /* Then let it fall thru to the real one */
1841     if (dcc[idx].type && dcc[idx].type->eof)
1842     dcc[idx].type->eof(idx);
1843     else {
1844     putlog(LOG_MISC, "*", DCC_DEADSOCKET, dcc[idx].sock, dcc[idx].type->name);
1845     killsock(dcc[idx].sock);
1846     lostdcc(idx);
1847     }
1848     }
1849    
1850     static void display_dcc_script(int idx, char *buf)
1851     {
1852     sprintf(buf, "scri %s", dcc[idx].u.script->command);
1853     }
1854    
1855     static int expmem_dcc_script(void *x)
1856     {
1857     register struct script_info *p = (struct script_info *) x;
1858     int tot = sizeof(struct script_info);
1859    
1860     if (p->type && p->u.other)
1861     tot += p->type->expmem(p->u.other);
1862     return tot;
1863     }
1864    
1865     static void kill_dcc_script(int idx, void *x)
1866     {
1867     register struct script_info *p = (struct script_info *) x;
1868    
1869     if (p->type && p->u.other)
1870     p->type->kill(idx, p->u.other);
1871     nfree(p);
1872     }
1873    
1874     static void out_dcc_script(int idx, char *buf, void *x)
1875     {
1876     register struct script_info *p = (struct script_info *) x;
1877    
1878     if (p && p->type && p->u.other)
1879     p->type->output(idx, buf, p->u.other);
1880     else
1881     tputs(dcc[idx].sock, buf, strlen(buf));
1882     }
1883    
1884     struct dcc_table DCC_SCRIPT = {
1885