]> jfr.im git - irc/quakenet/newserv.git/blob - nterfacer/nterfacer_relay.c
TRUSTS: require sqlite
[irc/quakenet/newserv.git] / nterfacer / nterfacer_relay.c
1 /*
2 nterfacer relay4
3 Copyright (C) 2004-2005 Chris Porter.
4
5 v1.14
6 - made it return an error instead of a timeout when sending to local users
7 v1.13
8 - found a load of stuff on froo's box
9 v1.12
10 - added nickprefix
11 v1.11
12 - made sure stats buffer was checked (no security problem, just helps pauline)
13 v1.10
14 - added stats support
15 v1.06
16 - found \r bug, and another format string problem...
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>
33 #include <stdarg.h>
34
35 #include "../lib/irc_string.h"
36 #include "../lib/strlfunc.h"
37 #include "../core/schedule.h"
38 #include "../irc/irc.h"
39 #include "../core/config.h"
40 #include "../lib/version.h"
41 #include "library.h"
42 #include "nterfacer_relay.h"
43
44 MODULE_VERSION("");
45
46 struct service_node *node;
47 sstring *ousername, *opassword;
48 regex onickname;
49 int number = 1;
50 char started = 0;
51 char nickprefix[NICKLEN - 5 + 1];
52 static unsigned long userid;
53
54 int stats_handler(struct rline *ri, int argc, char **argv);
55
56 void _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);
64 register_handler(node, "stats", 2, stats_handler);
65
66 registerhook(HOOK_NICK_LOSTNICK, &relay_quits);
67 registerhook(HOOK_IRC_DISCON, &relay_disconnect);
68 registerhook(HOOK_CORE_REHASH, &relay_rehash);
69
70 started = 1;
71 }
72
73 int load_config(void) {
74 /* eww, I don't know how to make this any nicer! */
75 sstring *ou, *op, *on, *opr;
76 regex newregex;
77
78 memset(&newregex, 0, sizeof(newregex));
79
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
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
90 ou = getcopyconfigitem("nterfacer", "serviceusername", "nterfacer", 100);
91 op = getcopyconfigitem("nterfacer", "servicepassword", "setme", 100);
92 on = getcopyconfigitem("nterfacer", "servicenickname", "^O$", 100);
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) {
108 freesstring(ou);
109 freesstring(op);
110 freesstring(on);
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
140 void relay_rehash(int hook, void *args) {
141 load_config();
142 }
143
144 void _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
160 deregisterhook(HOOK_NICK_LOSTNICK, &relay_quits);
161 deregisterhook(HOOK_IRC_DISCON, &relay_disconnect);
162 deregisterhook(HOOK_CORE_REHASH, &relay_rehash);
163
164 for(np=list;np;) {
165 struct rline *ri = np->rline;
166 lp = np->next;
167
168 dispose_rld(np);
169 ri_error(ri, RELAY_UNLOADED, "Module was unloaded");
170
171 np = lp;
172 }
173
174 if(node)
175 deregister_service(node);
176 }
177
178 nick *relay_getnick(void) {
179 static char ournick[NICKLEN + 1];
180 int attempts = 100;
181
182 do {
183 snprintf(ournick, sizeof(ournick), "%s%d", nickprefix, number++);
184 if(number > 60000)
185 number = 1;
186
187 if(!getnickbynick(ournick))
188 return registerlocaluserflags(ournick, "nterf", "cer", "nterfacer relay", "nterfacer", userid, 0, UMODE_SERVICE | UMODE_DEAF | UMODE_OPER | UMODE_INV | UMODE_ACCOUNT, &relay_messages);
189
190 attempts--;
191 } while(attempts > 0);
192
193 return NULL;
194 }
195
196 int relay_handler(struct rline *ri, int argc, char **argv) {
197 struct rld *np;
198 int lines = 0, i;
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
218 if(homeserver(dest->numeric) == mylongnum)
219 return ri_error(ri, RELAY_LOCAL_USER, "Cannot relay to local users");
220
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
225 np = ntmalloc(sizeof(struct rld));
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();
233 ntfree(np);
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);
239 ntfree(np);
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
249 if(!(np->nick = relay_getnick())) {
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 ntfree(np);
256 return ri_error(ri, RELAY_NICK_ERROR, "Unable to get a nickname!");
257 }
258
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);
268 ntfree(np);
269 return ri_error(ri, RELAY_MEMORY_ERROR, "Memory error");
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;
279 sendsecuremessagetouser(np->nick, dest, serverlist[dest->numeric>>18].name->content, "AUTH %s %s", ousername->content, opassword->content);
280 }
281
282 for(i=3;i<argc;i++)
283 sendmessagetouser(np->nick, dest, "%s", argv[i]);
284
285 return RE_OK;
286 }
287
288 int 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
308 np = ntmalloc(sizeof(struct rld));
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())) {
316 ntfree(np);
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);
324 ntfree(np);
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 }
337
338 return RE_OK;
339 }
340
341 void relay_messages(nick *target, int messagetype, void **args) {
342 struct rld *item, *prev = NULL;
343 struct rline *ri;
344
345 for(item=list;item;prev=item,item=item->next)
346 if(item->nick == target)
347 break;
348
349 if(!item)
350 return;
351
352 ri = item->rline;
353
354 switch(messagetype) {
355 case LU_PRIVNOTICE:
356 case LU_PRIVMSG:
357 case LU_SECUREMSG:
358 if(!item->dest || (item->dest != (nick *)args[0]))
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 {
366 dispose_rld_prev(item, prev);
367 ri_error(ri, RELAY_O_AUTH_ERROR, "Unable to auth with O");
368 }
369 } else {
370 if(!strcmp((char *)args[1], "Auth successful.")) {
371 item->mode|=MODE_O_AUTH1;
372 } else {
373 dispose_rld_prev(item, prev);
374 ri_error(ri, RELAY_O_AUTH_ERROR, "Unable to auth with O");
375 }
376 }
377 } else if(ri_append(ri, "%s", (char *)args[1]) == BF_OVER) {
378 dispose_rld_prev(item, prev);
379 ri_error(ri, BF_OVER, "Buffer overflow");
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))) {
382 dispose_rld_prev(item, prev);
383 ri_final(ri);
384 }
385 }
386 break;
387
388 case LU_KILLED:
389 if(prev) {
390 prev->next = item->next;
391 } else {
392 list = item->next;
393 }
394 dispose_rld_dontquit(item, 1);
395 ri_error(ri, RELAY_KILLED, "Killed");
396 break;
397
398 case LU_STATS:
399 if(item->mode != MODE_STATS)
400 return;
401 if(ri_append(ri, "%s", (char *)args[2]) == BF_OVER) {
402 dispose_rld_prev(item, prev);
403 ri_error(ri, BF_OVER, "Buffer overflow");
404 }
405 break;
406
407 case LU_STATS_END:
408 if(item->mode != MODE_STATS)
409 return;
410
411 dispose_rld_prev(item, prev);
412
413 ri_final(ri);
414 break;
415 }
416 }
417
418 void 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
427 void dispose_rld_dontquit(struct rld *item, int dontquit) {
428 if(item->schedule)
429 deleteschedule(item->schedule, &relay_timeout, item);
430 if(!dontquit)
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 }
437 ntfree(item);
438 }
439
440 void relay_timeout(void *arg) {
441 struct rld *item = (struct rld *)arg, *lp = NULL, *np;
442 struct rline *ri = item->rline;
443
444 item->schedule = NULL;
445 for(np=list;np;lp=np,np=np->next) {
446 if(np == item) {
447 dispose_rld_prev(item, lp);
448 break;
449 }
450 }
451
452 ri_error(ri, RELAY_TIMEOUT, "Timed out");
453 }
454
455 void relay_quits(int hook, void *args) {
456 nick *np = (nick *)args;
457 struct rld *cp, *lp, *freed = NULL;
458
459 /* get them out of the list before freeing */
460 for(lp=NULL,cp=list;cp;) {
461 if(cp->dest == np) {
462 if(lp) {
463 lp->next = cp->next;
464 cp->next = freed;
465 freed = cp;
466 cp = lp->next;
467 } else {
468 list = cp->next;
469 cp->next = freed;
470 freed = cp;
471 cp = list;
472 }
473 } else {
474 lp = cp;
475 cp = cp->next;
476 }
477 }
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 }
489 }
490
491 void relay_disconnect(int hook, void *args) {
492 while(list) {
493 struct rline *ri = list->rline;
494
495 dispose_rld_prev(list, NULL);
496
497 ri_error(ri, RELAY_DISCONNECTED, "Disconnected from IRC");
498 }
499 }