]> jfr.im git - irc/quakenet/newserv.git/blame - nterface/nterfacer_relay.c
Disable kill.
[irc/quakenet/newserv.git] / nterface / nterfacer_relay.c
CommitLineData
18f8bd28
CP
1/*
2 nterfacer relay4
3 Copyright (C) 2004-2005 Chris Porter.
4
6bc02131
CP
5 v1.14
6 - made it return an error instead of a timeout when sending to local users
3193eda0
CP
7 v1.13
8 - found a load of stuff on froo's box
99f1fd92
CP
9 v1.12
10 - added nickprefix
95ee3dac
CP
11 v1.11
12 - made sure stats buffer was checked (no security problem, just helps pauline)
16d29ce2
CP
13 v1.10
14 - added stats support
af0c1aed
CP
15 v1.06
16 - found \r bug, and another format string problem...
18f8bd28
CP
17 v1.05
18 - oops, fixed quit bug
19 - oops, forgot about rehash hook
20 v1.04
21 - added S support, though it's gross
22 v1.03
23 - fixed audit found format string vunerability
24 v1.02
25 - split O username and password from .h file into config file
26 - added rehash support
27 v1.01
28 - item->schedule was not unset and checked upon timeout
29*/
30
31#include <stdio.h>
32#include <string.h>
af0c1aed 33#include <stdarg.h>
18f8bd28
CP
34
35#include "../lib/irc_string.h"
c5c119a6 36#include "../lib/strlfunc.h"
18f8bd28
CP
37#include "../core/schedule.h"
38#include "../irc/irc.h"
39#include "../core/config.h"
40#include "library.h"
41#include "nterfacer_relay.h"
42
43struct service_node *node;
44sstring *ousername, *opassword;
45regex onickname;
46int number = 1;
47char started = 0;
c5c119a6 48char nickprefix[NICKLEN - 5 + 1];
18f8bd28 49
16d29ce2
CP
50int stats_handler(struct rline *ri, int argc, char **argv);
51
18f8bd28
CP
52void _init(void) {
53 if(!load_config())
54 return;
55
56 node = register_service("R");
57 MemCheck(node);
58
59 register_handler(node, "relay", 4, relay_handler);
16d29ce2
CP
60 register_handler(node, "stats", 2, stats_handler);
61
3193eda0 62 registerhook(HOOK_NICK_LOSTNICK, &relay_quits);
18f8bd28
CP
63 registerhook(HOOK_IRC_DISCON, &relay_disconnect);
64 registerhook(HOOK_CORE_REHASH, &relay_rehash);
65
66 started = 1;
67}
68
69int load_config(void) {
70 /* eww, I don't know how to make this any nicer! */
c5c119a6 71 sstring *ou, *op, *on, *opr;
18f8bd28
CP
72 regex newregex;
73
74 memset(&newregex, 0, sizeof(newregex));
75
c5c119a6
CP
76 opr = getcopyconfigitem("nterfacer", "nickprefix", DEFAULT_NICK_PREFIX, sizeof(nickprefix) - 1); /* the terminator is included in nickprefix */
77 strlcpy(nickprefix, (opr&&opr->content)?opr->content:DEFAULT_NICK_PREFIX, sizeof(nickprefix));
78 freesstring(opr); /* works fine with NULL */
79
18f8bd28
CP
80 ou = getcopyconfigitem("nterfacer", "serviceusername", "nterfacer", 100);
81 op = getcopyconfigitem("nterfacer", "servicepassword", "setme", 100);
82 on = getcopyconfigitem("nterfacer", "servicenickname", "^(O|S)$", 100);
83
84 if(on) {
85 int erroroffset;
86 const char *rerror;
87 newregex.phrase = pcre_compile(on->content, PCRE_CASELESS, &rerror, &erroroffset, NULL);
88 if(newregex.phrase) {
89 newregex.hint = pcre_study(newregex.phrase, 0, &rerror);
90 if(rerror) {
91 pcre_free(newregex.phrase);
92 newregex.phrase = NULL;
93 }
94 }
95 }
96
97 if(!ou || !op || !on || !newregex.phrase) {
c5c119a6
CP
98 freesstring(ou);
99 freesstring(op);
100 freesstring(on);
18f8bd28
CP
101 if(newregex.phrase) {
102 pcre_free(newregex.phrase);
103 if(newregex.hint)
104 pcre_free(newregex.hint);
105 }
106 MemError();
107 return 0;
108 }
109
110 if(started) {
111 if(ousername)
112 freesstring(ousername);
113 if(opassword)
114 freesstring(opassword);
115 if(onickname.phrase) {
116 pcre_free(onickname.phrase);
117 if(onickname.hint)
118 pcre_free(onickname.hint);
119 }
120 }
121
122 freesstring(on);
123
124 ousername = ou;
125 opassword = op;
126 memcpy(&onickname, &newregex, sizeof(onickname));
127 return 1;
128}
129
130void relay_rehash(int hook, void *args) {
131 load_config();
132}
133
134void _fini(void) {
135 struct rld *np, *lp;
136
137 if(!started)
138 return;
139
140 if(ousername)
141 freesstring(ousername);
142 if(opassword)
143 freesstring(opassword);
144 if(onickname.phrase) {
145 pcre_free(onickname.phrase);
146 if(onickname.hint)
147 pcre_free(onickname.hint);
148 }
149
3193eda0 150 deregisterhook(HOOK_NICK_LOSTNICK, &relay_quits);
18f8bd28
CP
151 deregisterhook(HOOK_IRC_DISCON, &relay_disconnect);
152 deregisterhook(HOOK_CORE_REHASH, &relay_rehash);
153
154 for(np=list;np;) {
155 lp = np->next;
156 ri_error(np->rline, RELAY_UNLOADED, "Module was unloaded");
157 dispose_rld(np);
158
159 np = lp;
160 }
161
162 if(node)
163 deregister_service(node);
164}
165
16d29ce2
CP
166nick *relay_getnick(void) {
167 static char ournick[NICKLEN + 1];
168 int attempts = 100;
af0c1aed 169
16d29ce2 170 do {
c5c119a6 171 snprintf(ournick, sizeof(ournick), "%s%d", nickprefix, number++);
16d29ce2
CP
172 if(number > 60000)
173 number = 1;
af0c1aed 174
16d29ce2
CP
175 if(!getnickbynick(ournick))
176 return registerlocaluser(ournick, "nterf", "cer", "nterfacer relay", "nterfacer", UMODE_SERVICE | UMODE_DEAF | UMODE_OPER | UMODE_INV | UMODE_ACCOUNT, &relay_messages);
177
178 attempts--;
179 } while(attempts > 0);
180
181 return NULL;
af0c1aed
CP
182}
183
18f8bd28 184int relay_handler(struct rline *ri, int argc, char **argv) {
18f8bd28 185 struct rld *np;
16d29ce2 186 int lines = 0, i;
18f8bd28
CP
187 nick *dest;
188 const char *rerror;
189 int erroroffset;
190
191 if(!connected)
192 return ri_error(ri, RELAY_NOT_ON_IRC, "Not currently on IRC");
193
194 if(argv[0][0] == '1') {
195 lines = positive_atoi(argv[1]);
196 if(lines < 1)
197 return ri_error(ri, RELAY_PARSE_ERROR, "Parse error");
198 } else if(argv[0][0] != '2') {
199 return ri_error(ri, RELAY_PARSE_ERROR, "Parse error");
200 }
201
202 dest = getnickbynick(argv[2]);
203 if(!dest)
204 return ri_error(ri, RELAY_NICK_NOT_FOUND, "Nickname not found!");
205
6bc02131
CP
206 if(homeserver(dest->numeric) == mylongnum)
207 return ri_error(ri, RELAY_LOCAL_USER, "Cannot relay to local users");
208
16d29ce2
CP
209 for(i=3;i<argc;i++)
210 if(strchr(argv[i], '\r'))
211 return ri_error(ri, RELAY_INVALID_CHARS, "Invalid character in input");
212
18f8bd28
CP
213 np = malloc(sizeof(struct rld));
214 MemCheckR(np, ri_error(ri, RELAY_MEMORY_ERROR, "Memory error"));
215
216 np->rline = ri;
217 if(!lines) {
218 np->termination.pcre.phrase = pcre_compile(argv[1], PCRE_FLAGS, &rerror, &erroroffset, NULL);
219 if(!np->termination.pcre.phrase) {
220 MemError();
221 free(np);
222 return ri_error(ri, RELAY_REGEX_ERROR, "Regex compliation error");
223 }
224 np->termination.pcre.hint = pcre_study(np->termination.pcre.phrase, 0, &rerror);
225 if(rerror) {
226 pcre_free(np->termination.pcre.phrase);
227 free(np);
228 return ri_error(ri, RELAY_REGEX_HINT_ERROR, "Regex hint error");
229 }
230
231 np->mode = MODE_TAG;
232 } else {
233 np->mode = MODE_LINES;
234 np->termination.remaining_lines = lines;
235 }
236
16d29ce2 237 if(!(np->nick = relay_getnick())) {
18f8bd28
CP
238 if(!lines) {
239 pcre_free(np->termination.pcre.phrase);
240 if(np->termination.pcre.hint)
241 pcre_free(np->termination.pcre.hint);
242 }
243 free(np);
244 return ri_error(ri, RELAY_NICK_ERROR, "Unable to get a nickname!");
245 }
246
18f8bd28
CP
247 np->schedule = scheduleoneshot(time(NULL) + MESSAGE_TIMEOUT, &relay_timeout, np);
248 if(!np->schedule) {
249 MemError();
250 if(!lines) {
251 pcre_free(np->termination.pcre.phrase);
252 if(np->termination.pcre.hint)
253 pcre_free(np->termination.pcre.hint);
254 }
255 deregisterlocaluser(np->nick, NULL);
256 free(np);
16d29ce2 257 return ri_error(ri, RELAY_MEMORY_ERROR, "Memory error");
18f8bd28
CP
258 }
259
260 np->dest = dest;
261
262 np->next = list;
263 list = np;
264
265 if(pcre_exec(onickname.phrase, onickname.hint, dest->nick, strlen(dest->nick), 0, 0, NULL, 0) >= 0) {
266 np->mode|=MODE_IS_O;
16d29ce2 267 sendsecuremessagetouser(np->nick, dest, serverlist[dest->numeric>>18].name->content, "AUTH %s %s", ousername->content, opassword->content);
18f8bd28
CP
268 }
269
270 for(i=3;i<argc;i++)
16d29ce2
CP
271 sendmessagetouser(np->nick, dest, "%s", argv[i]);
272
273 return RE_OK;
274}
275
276int stats_handler(struct rline *ri, int argc, char **argv) {
277 struct rld *np;
278 char *server = argv[0], *command = argv[1], *more;
279 int serverid = findserver(server);
280 char targetnumeric[3];
281
282 if(serverid == -1)
283 return ri_error(ri, RELAY_SERVER_NOT_FOUND, "Server not found");
284
285 if(!command || !command[0] || (command[0] == ' ') || (command[0] == '\r') || (command[0] == ':'))
286 return ri_error(ri, RELAY_INVALID_COMMAND, "Invalid command");
287
288 if(argc > 2) {
289 more = argv[2];
290 if(strchr(more, '\r'))
291 return ri_error(ri, RELAY_INVALID_CHARS, "Invalid character in input");
292 } else {
293 more = NULL;
294 }
295
296 np = malloc(sizeof(struct rld));
297 MemCheckR(np, ri_error(ri, RELAY_MEMORY_ERROR, "Memory error"));
298
299 np->rline = ri;
300 np->mode = MODE_STATS;
301 np->dest = NULL;
302
303 if(!(np->nick = relay_getnick())) {
304 free(np);
305 return ri_error(ri, RELAY_NICK_ERROR, "Unable to get a nickname!");
306 }
307
308 np->schedule = scheduleoneshot(time(NULL) + MESSAGE_TIMEOUT, &relay_timeout, np);
309 if(!np->schedule) {
310 MemError();
311 deregisterlocaluser(np->nick, NULL);
312 free(np);
313 }
314
315 np->next = list;
316 list = np;
317
318 memcpy(targetnumeric, longtonumeric(serverid, 2), 3);
319 targetnumeric[2] = '\0';
320 if(more) {
321 irc_send("%s R %c %s :%s\r\n", longtonumeric(np->nick->numeric,5), command[0], targetnumeric, more);
322 } else {
323 irc_send("%s R %c :%s\r\n", longtonumeric(np->nick->numeric,5), command[0], targetnumeric);
324 }
18f8bd28
CP
325
326 return RE_OK;
327}
328
329void relay_messages(nick *target, int messagetype, void **args) {
330 struct rld *item, *prev = NULL;
331 for(item=list;item;prev=item,item=item->next)
332 if(item->nick == target)
333 break;
334
335 if(!item)
336 return;
337
338 switch(messagetype) {
339 case LU_PRIVNOTICE:
340 case LU_PRIVMSG:
341 case LU_SECUREMSG:
16d29ce2 342 if(!item->dest || (item->dest != (nick *)args[0]))
18f8bd28
CP
343 return;
344
345 if((item->mode & MODE_IS_O) && !(item->mode & MODE_O_AUTH2)) {
346 if(item->mode & MODE_O_AUTH1) {
347 if(!strncmp((char *)args[1], "Your authlevel is ", 18)) {
348 item->mode|=MODE_O_AUTH2;
349 } else {
350 ri_error(item->rline, RELAY_O_AUTH_ERROR, "Unable to auth with O");
351 dispose_rld_prev(item, prev);
352 }
353 } else {
354 if(!strcmp((char *)args[1], "Auth successful.")) {
355 item->mode|=MODE_O_AUTH1;
356 } else {
357 ri_error(item->rline, RELAY_O_AUTH_ERROR, "Unable to auth with O");
358 dispose_rld_prev(item, prev);
359 }
360 }
361 } else if(ri_append(item->rline, "%s", (char *)args[1]) == BF_OVER) {
362 ri_error(item->rline, BF_OVER, "Buffer overflow");
363 dispose_rld_prev(item, prev);
364 } else {
365 if(((item->mode & MODE_LINES) && (!--item->termination.remaining_lines)) || ((item->mode & MODE_TAG) && (pcre_exec(item->termination.pcre.phrase, item->termination.pcre.hint, (char *)args[1], strlen((char *)args[1]), 0, 0, NULL, 0) >= 0))) {
366 ri_final(item->rline);
367 dispose_rld_prev(item, prev);
368 }
369 }
370 break;
371
372 case LU_KILLED:
373 ri_error(item->rline, RELAY_KILLED, "Killed");
b00ef0b1
CP
374 if(prev) {
375 prev->next = item->next;
376 } else {
377 list = item->next;
378 }
379 dispose_rld_dontquit(item, 1);
18f8bd28 380 break;
16d29ce2
CP
381
382 case LU_STATS:
383 if(item->mode != MODE_STATS)
384 return;
95ee3dac
CP
385 if(ri_append(item->rline, "%s", (char *)args[2]) == BF_OVER) {
386 ri_error(item->rline, BF_OVER, "Buffer overflow");
387 dispose_rld_prev(item, prev);
388 }
16d29ce2
CP
389 break;
390
391 case LU_STATS_END:
392 if(item->mode != MODE_STATS)
393 return;
394 ri_final(item->rline);
395 dispose_rld_prev(item, prev);
396 break;
18f8bd28
CP
397 }
398}
399
400void dispose_rld_prev(struct rld *item, struct rld *prev) {
401 if(prev) {
402 prev->next = item->next;
403 } else {
404 list = item->next;
405 }
406 dispose_rld(item);
407}
408
409void dispose_rld_dontquit(struct rld *item, int dontquit) {
410 if(item->schedule)
411 deleteschedule(item->schedule, &relay_timeout, item);
b00ef0b1 412 if(!dontquit)
18f8bd28
CP
413 deregisterlocaluser(item->nick, NULL);
414 if(item->mode & MODE_TAG) {
415 pcre_free(item->termination.pcre.phrase);
416 if(item->termination.pcre.hint)
417 pcre_free(item->termination.pcre.hint);
418 }
419 free(item);
420}
421
422void relay_timeout(void *arg) {
423 struct rld *item = (struct rld *)arg, *lp = NULL, *np;
424
425 item->schedule = NULL;
426
427 ri_error(item->rline, RELAY_TIMEOUT, "Timed out");
428
429 for(np=list;np;lp=np,np=np->next) {
430 if(np == item) {
431 dispose_rld_prev(item, lp);
432 break;
433 }
434 }
435}
436
437void relay_quits(int hook, void *args) {
3193eda0 438 nick *np = (nick *)args;
18f8bd28
CP
439 struct rld *cp, *lp;
440
441 for(lp=NULL,cp=list;cp;) {
442 if(cp->dest == np) {
443 ri_error(cp->rline, RELAY_TARGET_LEFT, "Target left QuakeNet");
444 if(lp) {
445 lp->next = cp->next;
b00ef0b1 446 dispose_rld(cp);
18f8bd28
CP
447 cp = lp->next;
448 } else {
449 list = cp->next;
b00ef0b1 450 dispose_rld(cp);
18f8bd28
CP
451 cp = list;
452 }
453 } else {
454 lp = cp;
455 cp = cp->next;
456 }
457 }
458}
459
460void relay_disconnect(int hook, void *args) {
461 struct rld *np, *lp = NULL;
462 while(list) {
463 ri_error(list->rline, RELAY_DISCONNECTED, "Disconnected from IRC");
464 dispose_rld_prev(list, NULL);
465
466 np = lp;
467 }
468}