]> jfr.im git - irc/quakenet/newserv.git/blame - nterfacer/nterfacer_relay.c
BUILD: add require-all build mode
[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;) {
ecc4fa34 165 struct rline *ri = np->rline;
18f8bd28 166 lp = np->next;
ecc4fa34 167
18f8bd28 168 dispose_rld(np);
ecc4fa34 169 ri_error(ri, RELAY_UNLOADED, "Module was unloaded");
18f8bd28
CP
170
171 np = lp;
172 }
173
174 if(node)
175 deregister_service(node);
176}
177
16d29ce2
CP
178nick *relay_getnick(void) {
179 static char ournick[NICKLEN + 1];
180 int attempts = 100;
af0c1aed 181
16d29ce2 182 do {
c5c119a6 183 snprintf(ournick, sizeof(ournick), "%s%d", nickprefix, number++);
16d29ce2
CP
184 if(number > 60000)
185 number = 1;
af0c1aed 186
16d29ce2 187 if(!getnickbynick(ournick))
c4ffdb9b 188 return registerlocaluserflags(ournick, "nterf", "cer", "nterfacer relay", "nterfacer", userid, 0, UMODE_SERVICE | UMODE_DEAF | UMODE_OPER | UMODE_INV | UMODE_ACCOUNT, &relay_messages);
16d29ce2
CP
189
190 attempts--;
191 } while(attempts > 0);
192
193 return NULL;
af0c1aed
CP
194}
195
18f8bd28 196int relay_handler(struct rline *ri, int argc, char **argv) {
18f8bd28 197 struct rld *np;
16d29ce2 198 int lines = 0, i;
18f8bd28
CP
199 nick *dest;
200 const char *rerror;
201 int erroroffset;
202
203 if(!connected)
204 return ri_error(ri, RELAY_NOT_ON_IRC, "Not currently on IRC");
205
206 if(argv[0][0] == '1') {
207 lines = positive_atoi(argv[1]);
208 if(lines < 1)
209 return ri_error(ri, RELAY_PARSE_ERROR, "Parse error");
210 } else if(argv[0][0] != '2') {
211 return ri_error(ri, RELAY_PARSE_ERROR, "Parse error");
212 }
213
214 dest = getnickbynick(argv[2]);
215 if(!dest)
216 return ri_error(ri, RELAY_NICK_NOT_FOUND, "Nickname not found!");
217
6bc02131
CP
218 if(homeserver(dest->numeric) == mylongnum)
219 return ri_error(ri, RELAY_LOCAL_USER, "Cannot relay to local users");
220
16d29ce2
CP
221 for(i=3;i<argc;i++)
222 if(strchr(argv[i], '\r'))
223 return ri_error(ri, RELAY_INVALID_CHARS, "Invalid character in input");
224
5ab07198 225 np = ntmalloc(sizeof(struct rld));
18f8bd28
CP
226 MemCheckR(np, ri_error(ri, RELAY_MEMORY_ERROR, "Memory error"));
227
228 np->rline = ri;
229 if(!lines) {
230 np->termination.pcre.phrase = pcre_compile(argv[1], PCRE_FLAGS, &rerror, &erroroffset, NULL);
231 if(!np->termination.pcre.phrase) {
232 MemError();
5ab07198 233 ntfree(np);
18f8bd28
CP
234 return ri_error(ri, RELAY_REGEX_ERROR, "Regex compliation error");
235 }
236 np->termination.pcre.hint = pcre_study(np->termination.pcre.phrase, 0, &rerror);
237 if(rerror) {
238 pcre_free(np->termination.pcre.phrase);
5ab07198 239 ntfree(np);
18f8bd28
CP
240 return ri_error(ri, RELAY_REGEX_HINT_ERROR, "Regex hint error");
241 }
242
243 np->mode = MODE_TAG;
244 } else {
245 np->mode = MODE_LINES;
246 np->termination.remaining_lines = lines;
247 }
248
16d29ce2 249 if(!(np->nick = relay_getnick())) {
18f8bd28
CP
250 if(!lines) {
251 pcre_free(np->termination.pcre.phrase);
252 if(np->termination.pcre.hint)
253 pcre_free(np->termination.pcre.hint);
254 }
5ab07198 255 ntfree(np);
18f8bd28
CP
256 return ri_error(ri, RELAY_NICK_ERROR, "Unable to get a nickname!");
257 }
258
18f8bd28
CP
259 np->schedule = scheduleoneshot(time(NULL) + MESSAGE_TIMEOUT, &relay_timeout, np);
260 if(!np->schedule) {
261 MemError();
262 if(!lines) {
263 pcre_free(np->termination.pcre.phrase);
264 if(np->termination.pcre.hint)
265 pcre_free(np->termination.pcre.hint);
266 }
267 deregisterlocaluser(np->nick, NULL);
5ab07198 268 ntfree(np);
16d29ce2 269 return ri_error(ri, RELAY_MEMORY_ERROR, "Memory error");
18f8bd28
CP
270 }
271
272 np->dest = dest;
273
274 np->next = list;
275 list = np;
276
277 if(pcre_exec(onickname.phrase, onickname.hint, dest->nick, strlen(dest->nick), 0, 0, NULL, 0) >= 0) {
278 np->mode|=MODE_IS_O;
16d29ce2 279 sendsecuremessagetouser(np->nick, dest, serverlist[dest->numeric>>18].name->content, "AUTH %s %s", ousername->content, opassword->content);
18f8bd28
CP
280 }
281
282 for(i=3;i<argc;i++)
16d29ce2
CP
283 sendmessagetouser(np->nick, dest, "%s", argv[i]);
284
285 return RE_OK;
286}
287
288int stats_handler(struct rline *ri, int argc, char **argv) {
289 struct rld *np;
290 char *server = argv[0], *command = argv[1], *more;
291 int serverid = findserver(server);
292 char targetnumeric[3];
293
294 if(serverid == -1)
295 return ri_error(ri, RELAY_SERVER_NOT_FOUND, "Server not found");
296
297 if(!command || !command[0] || (command[0] == ' ') || (command[0] == '\r') || (command[0] == ':'))
298 return ri_error(ri, RELAY_INVALID_COMMAND, "Invalid command");
299
300 if(argc > 2) {
301 more = argv[2];
302 if(strchr(more, '\r'))
303 return ri_error(ri, RELAY_INVALID_CHARS, "Invalid character in input");
304 } else {
305 more = NULL;
306 }
307
5ab07198 308 np = ntmalloc(sizeof(struct rld));
16d29ce2
CP
309 MemCheckR(np, ri_error(ri, RELAY_MEMORY_ERROR, "Memory error"));
310
311 np->rline = ri;
312 np->mode = MODE_STATS;
313 np->dest = NULL;
314
315 if(!(np->nick = relay_getnick())) {
5ab07198 316 ntfree(np);
16d29ce2
CP
317 return ri_error(ri, RELAY_NICK_ERROR, "Unable to get a nickname!");
318 }
319
320 np->schedule = scheduleoneshot(time(NULL) + MESSAGE_TIMEOUT, &relay_timeout, np);
321 if(!np->schedule) {
322 MemError();
323 deregisterlocaluser(np->nick, NULL);
5ab07198 324 ntfree(np);
16d29ce2
CP
325 }
326
327 np->next = list;
328 list = np;
329
330 memcpy(targetnumeric, longtonumeric(serverid, 2), 3);
331 targetnumeric[2] = '\0';
332 if(more) {
333 irc_send("%s R %c %s :%s\r\n", longtonumeric(np->nick->numeric,5), command[0], targetnumeric, more);
334 } else {
335 irc_send("%s R %c :%s\r\n", longtonumeric(np->nick->numeric,5), command[0], targetnumeric);
336 }
18f8bd28
CP
337
338 return RE_OK;
339}
340
341void relay_messages(nick *target, int messagetype, void **args) {
342 struct rld *item, *prev = NULL;
ecc4fa34
CP
343 struct rline *ri;
344
18f8bd28
CP
345 for(item=list;item;prev=item,item=item->next)
346 if(item->nick == target)
347 break;
348
349 if(!item)
350 return;
ecc4fa34
CP
351
352 ri = item->rline;
353
18f8bd28
CP
354 switch(messagetype) {
355 case LU_PRIVNOTICE:
356 case LU_PRIVMSG:
357 case LU_SECUREMSG:
16d29ce2 358 if(!item->dest || (item->dest != (nick *)args[0]))
18f8bd28
CP
359 return;
360
361 if((item->mode & MODE_IS_O) && !(item->mode & MODE_O_AUTH2)) {
362 if(item->mode & MODE_O_AUTH1) {
363 if(!strncmp((char *)args[1], "Your authlevel is ", 18)) {
364 item->mode|=MODE_O_AUTH2;
365 } else {
18f8bd28 366 dispose_rld_prev(item, prev);
ecc4fa34 367 ri_error(ri, RELAY_O_AUTH_ERROR, "Unable to auth with O");
18f8bd28
CP
368 }
369 } else {
370 if(!strcmp((char *)args[1], "Auth successful.")) {
371 item->mode|=MODE_O_AUTH1;
372 } else {
18f8bd28 373 dispose_rld_prev(item, prev);
ecc4fa34 374 ri_error(ri, RELAY_O_AUTH_ERROR, "Unable to auth with O");
18f8bd28
CP
375 }
376 }
ecc4fa34 377 } else if(ri_append(ri, "%s", (char *)args[1]) == BF_OVER) {
18f8bd28 378 dispose_rld_prev(item, prev);
ecc4fa34 379 ri_error(ri, BF_OVER, "Buffer overflow");
18f8bd28
CP
380 } else {
381 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))) {
18f8bd28 382 dispose_rld_prev(item, prev);
ecc4fa34 383 ri_final(ri);
18f8bd28
CP
384 }
385 }
386 break;
387
388 case LU_KILLED:
b00ef0b1
CP
389 if(prev) {
390 prev->next = item->next;
391 } else {
392 list = item->next;
393 }
394 dispose_rld_dontquit(item, 1);
ecc4fa34 395 ri_error(ri, RELAY_KILLED, "Killed");
18f8bd28 396 break;
16d29ce2
CP
397
398 case LU_STATS:
399 if(item->mode != MODE_STATS)
400 return;
ecc4fa34 401 if(ri_append(ri, "%s", (char *)args[2]) == BF_OVER) {
95ee3dac 402 dispose_rld_prev(item, prev);
ecc4fa34 403 ri_error(ri, BF_OVER, "Buffer overflow");
95ee3dac 404 }
16d29ce2
CP
405 break;
406
407 case LU_STATS_END:
408 if(item->mode != MODE_STATS)
409 return;
ecc4fa34 410
16d29ce2 411 dispose_rld_prev(item, prev);
ecc4fa34
CP
412
413 ri_final(ri);
16d29ce2 414 break;
18f8bd28
CP
415 }
416}
417
418void dispose_rld_prev(struct rld *item, struct rld *prev) {
419 if(prev) {
420 prev->next = item->next;
421 } else {
422 list = item->next;
423 }
424 dispose_rld(item);
425}
426
427void dispose_rld_dontquit(struct rld *item, int dontquit) {
428 if(item->schedule)
429 deleteschedule(item->schedule, &relay_timeout, item);
b00ef0b1 430 if(!dontquit)
18f8bd28
CP
431 deregisterlocaluser(item->nick, NULL);
432 if(item->mode & MODE_TAG) {
433 pcre_free(item->termination.pcre.phrase);
434 if(item->termination.pcre.hint)
435 pcre_free(item->termination.pcre.hint);
436 }
5ab07198 437 ntfree(item);
18f8bd28
CP
438}
439
440void relay_timeout(void *arg) {
441 struct rld *item = (struct rld *)arg, *lp = NULL, *np;
ecc4fa34 442 struct rline *ri = item->rline;
18f8bd28
CP
443
444 item->schedule = NULL;
18f8bd28
CP
445 for(np=list;np;lp=np,np=np->next) {
446 if(np == item) {
447 dispose_rld_prev(item, lp);
448 break;
449 }
450 }
ecc4fa34
CP
451
452 ri_error(ri, RELAY_TIMEOUT, "Timed out");
18f8bd28
CP
453}
454
455void relay_quits(int hook, void *args) {
3193eda0 456 nick *np = (nick *)args;
ecc4fa34
CP
457 struct rld *cp, *lp, *freed = NULL;
458
459 /* get them out of the list before freeing */
18f8bd28
CP
460 for(lp=NULL,cp=list;cp;) {
461 if(cp->dest == np) {
18f8bd28
CP
462 if(lp) {
463 lp->next = cp->next;
ecc4fa34
CP
464 cp->next = freed;
465 freed = cp;
18f8bd28
CP
466 cp = lp->next;
467 } else {
468 list = cp->next;
ecc4fa34
CP
469 cp->next = freed;
470 freed = cp;
18f8bd28
CP
471 cp = list;
472 }
473 } else {
474 lp = cp;
475 cp = cp->next;
476 }
477 }
ecc4fa34
CP
478
479 /* now free them up */
480 while(freed) {
481 struct rline *ri = freed->rline;
482 cp = freed->next;
483
484 dispose_rld(freed);
485 ri_error(ri, RELAY_TARGET_LEFT, "Target left QuakeNet");
486
487 freed = cp;
488 }
18f8bd28
CP
489}
490
491void relay_disconnect(int hook, void *args) {
18f8bd28 492 while(list) {
ecc4fa34
CP
493 struct rline *ri = list->rline;
494
18f8bd28
CP
495 dispose_rld_prev(list, NULL);
496
ecc4fa34 497 ri_error(ri, RELAY_DISCONNECTED, "Disconnected from IRC");
18f8bd28
CP
498 }
499}