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