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