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