]>
Commit | Line | Data |
---|---|---|
18f8bd28 CP |
1 | /* |
2 | nterfacer relay4 | |
3 | Copyright (C) 2004-2005 Chris Porter. | |
4 | ||
95ee3dac CP |
5 | v1.11 |
6 | - made sure stats buffer was checked (no security problem, just helps pauline) | |
16d29ce2 CP |
7 | v1.10 |
8 | - added stats support | |
af0c1aed CP |
9 | v1.06 |
10 | - found \r bug, and another format string problem... | |
18f8bd28 CP |
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> | |
af0c1aed | 27 | #include <stdarg.h> |
18f8bd28 CP |
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 | ||
16d29ce2 CP |
42 | int stats_handler(struct rline *ri, int argc, char **argv); |
43 | ||
18f8bd28 CP |
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); | |
16d29ce2 CP |
52 | register_handler(node, "stats", 2, stats_handler); |
53 | ||
18f8bd28 CP |
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 | ||
16d29ce2 CP |
157 | nick *relay_getnick(void) { |
158 | static char ournick[NICKLEN + 1]; | |
159 | int attempts = 100; | |
af0c1aed | 160 | |
16d29ce2 CP |
161 | do { |
162 | snprintf(ournick, sizeof(ournick), "nterfacer%d", number++); | |
163 | if(number > 60000) | |
164 | number = 1; | |
af0c1aed | 165 | |
16d29ce2 CP |
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; | |
af0c1aed CP |
173 | } |
174 | ||
18f8bd28 | 175 | int relay_handler(struct rline *ri, int argc, char **argv) { |
18f8bd28 | 176 | struct rld *np; |
16d29ce2 | 177 | int lines = 0, i; |
18f8bd28 CP |
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 | ||
16d29ce2 CP |
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 | ||
18f8bd28 CP |
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 | ||
16d29ce2 | 225 | if(!(np->nick = relay_getnick())) { |
18f8bd28 CP |
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 | ||
18f8bd28 CP |
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); | |
16d29ce2 | 245 | return ri_error(ri, RELAY_MEMORY_ERROR, "Memory error"); |
18f8bd28 CP |
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; | |
16d29ce2 | 255 | sendsecuremessagetouser(np->nick, dest, serverlist[dest->numeric>>18].name->content, "AUTH %s %s", ousername->content, opassword->content); |
18f8bd28 CP |
256 | } |
257 | ||
258 | for(i=3;i<argc;i++) | |
16d29ce2 CP |
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 | } | |
18f8bd28 CP |
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: | |
16d29ce2 | 330 | if(!item->dest || (item->dest != (nick *)args[0])) |
18f8bd28 CP |
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"); | |
b00ef0b1 CP |
362 | if(prev) { |
363 | prev->next = item->next; | |
364 | } else { | |
365 | list = item->next; | |
366 | } | |
367 | dispose_rld_dontquit(item, 1); | |
18f8bd28 | 368 | break; |
16d29ce2 CP |
369 | |
370 | case LU_STATS: | |
371 | if(item->mode != MODE_STATS) | |
372 | return; | |
95ee3dac CP |
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 | } | |
16d29ce2 CP |
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; | |
18f8bd28 CP |
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); | |
b00ef0b1 | 400 | if(!dontquit) |
18f8bd28 CP |
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; | |
b00ef0b1 | 435 | dispose_rld(cp); |
18f8bd28 CP |
436 | cp = lp->next; |
437 | } else { | |
438 | list = cp->next; | |
b00ef0b1 | 439 | dispose_rld(cp); |
18f8bd28 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 | } |