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