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