ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/pxys-hybrid/trunk/pxyservd/src/irc_parser.c
Revision: 3414
Committed: Mon Apr 28 23:36:14 2014 UTC (9 years, 11 months ago) by michael
Content type: text/x-csrc
File size: 14295 byte(s)
Log Message:
- irc_parser.c:irc_parser_parse_line(): properly handle SERVER messages

File Contents

# Content
1 /* Copyright (C) 2003, 2004 Stephane Thiell
2 *
3 * This file is part of pxyservd (from pxys)
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 */
20 #define RCSID "$Id: irc_parser.c,v 1.5 2004/01/10 18:15:36 mbuna Exp $"
21
22 /* Here is my exotic/experimental partial IRCU P10 commands parser...
23 * Its main goal is performance.
24 * If i had more time, i would try with a "static" peak's dictionary
25 * and compare the performance, but as IRC doesn't have tons of commands,
26 * not sure the internal peak's dictionary hash table can beat a few integer
27 * comparaisons (see parser_dispatch())...
28 * Some compilers like gcc on i386 worry about multi-character character
29 * constant and print a warning by default. You can usually safely ignore them
30 * for this file. But if there is a problem, please let me know! --mbuna
31 */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include "irc_parser.h"
38
39 #include <assert.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43
44 #include "cfgloader.h"
45 #include "debug.h"
46 #include "pxyservd_log.h"
47 #include "session.h"
48 #include "tokenizer.h"
49
50 #include "irc_client.h"
51 #include "irc_mode.h"
52 #include "irc_msg.h"
53 #include "irc_network.h"
54 #include "irc_numnicks.h"
55 #include "irc_send.h"
56 #include "irc_string.h"
57 #include "irc_userbase.h"
58 #include "irc_yxx.h"
59
60 #define NOT_BURSTING 0
61 #define BURSTING 1
62
63 #if 0
64 #define RET_BOGUS do { msg_bogus++; return; } while (0)
65 #else
66 #define RET_BOGUS do { msg_bogus++; \
67 log_system("irc_parser:%d BOGUS!", __LINE__); \
68 return; } while (0)
69 #endif
70
71 int msg_bogus; /* exported to cmd_status */
72 static time_t burst_start, burst_delay; /* fun */
73
74 struct Message
75 {
76 const char *cmd;
77 unsigned int count; /* number of times command used */
78 unsigned int rcount; /* number of times command used by server */
79 unsigned int args_min; /* at least this many args must be passed
80 * or an error will be sent to the user
81 * before the m_func is even called
82 */
83 unsigned int args_max; /* maximum permitted parameters */
84 unsigned int flags; /* bit 0 set means that this command is allowed
85 * to be used only on the average of once per 2
86 * seconds -SRB
87 */
88 uint64_t bytes; /* bytes received for this message */
89
90 void (*handler)(toktabptr);
91 };
92
93 /*
94 * (based on orabidoo's parser code)
95 *
96 * This has always just been a trie. Look at volume III of Knuth ACP
97 *
98 *
99 * ok, you start out with an array of pointers, each one corresponds
100 * to a letter at the current position in the command being examined.
101 *
102 * so roughly you have this for matching 'trie' or 'tie'
103 *
104 * 't' points -> [MessageTree *] 'r' -> [MessageTree *] -> 'i'
105 * -> [MessageTree *] -> [MessageTree *] -> 'e' and matches
106 *
107 * 'i' -> [MessageTree *] -> 'e' and matches
108 *
109 * BUGS (Limitations!)
110 *
111 * I designed this trie to parse ircd commands. Hence it currently
112 * casefolds. This is trivial to fix by increasing MAXPTRLEN.
113 * This trie also "folds" '{' etc. down. This means, the input to this
114 * trie must be alpha tokens only. This again, is a limitation that
115 * can be overcome by increasing MAXPTRLEN to include upper/lower case
116 * at the expense of more memory. At the extreme end, you could make
117 * MAXPTRLEN 128.
118 *
119 * This is also not a patricia trie. On short ircd tokens, this is
120 * not likely going to matter.
121 *
122 * Diane Bruce (Dianora), June 6 2003
123 */
124
125 #define MAXPTRLEN 32
126 /* Must be a power of 2, and
127 * larger than 26 [a-z]|[A-Z]
128 * its used to allocate the set
129 * of pointers at each node of the tree
130 * There are MAXPTRLEN pointers at each node.
131 * Obviously, there have to be more pointers
132 * Than ASCII letters. 32 is a nice number
133 * since there is then no need to shift
134 * 'A'/'a' to base 0 index, at the expense
135 * of a few never used pointers. For a small
136 * parser like this, this is a good compromise
137 * and does make it somewhat faster.
138 *
139 * - Dianora
140 */
141
142 struct MessageTree
143 {
144 int links; /* Count of all pointers (including msg) at this node
145 * used as reference count for deletion of _this_ node.
146 */
147 struct Message *msg;
148 struct MessageTree *pointers[MAXPTRLEN];
149 };
150
151 static struct MessageTree msg_tree;
152
153 /* add_msg_element()
154 *
155 * inputs - pointer to MessageTree
156 * - pointer to Message to add for given command
157 * - pointer to current portion of command being added
158 * output - NONE
159 * side effects - recursively build the Message Tree ;-)
160 */
161 /*
162 * How this works.
163 *
164 * The code first checks to see if its reached the end of the command
165 * If so, that struct MessageTree has a msg pointer updated and the links
166 * count incremented, since a msg pointer is a reference.
167 * Then the code descends recursively, building the trie.
168 * If a pointer index inside the struct MessageTree is NULL a new
169 * child struct MessageTree has to be allocated.
170 * The links (reference count) is incremented as they are created
171 * in the parent.
172 */
173 static void
174 add_msg_element(struct MessageTree *mtree_p, struct Message *msg_p,
175 const char *cmd)
176 {
177 struct MessageTree *ntree_p;
178
179 if (*cmd == '\0')
180 {
181 mtree_p->msg = msg_p;
182 mtree_p->links++; /* Have msg pointer, so up ref count */
183 }
184 else
185 {
186 /*
187 * *cmd & (MAXPTRLEN-1)
188 * convert the char pointed to at *cmd from ASCII to an integer
189 * between 0 and MAXPTRLEN.
190 * Thus 'A' -> 0x1 'B' -> 0x2 'c' -> 0x3 etc.
191 */
192 if ((ntree_p = mtree_p->pointers[*cmd & (MAXPTRLEN - 1)]) == NULL)
193 {
194 ntree_p = calloc(1, sizeof(struct MessageTree));
195 mtree_p->pointers[*cmd & (MAXPTRLEN - 1)] = ntree_p;
196
197 mtree_p->links++; /* Have new pointer, so up ref count */
198 }
199
200 add_msg_element(ntree_p, msg_p, cmd + 1);
201 }
202 }
203
204 /* del_msg_element()
205 *
206 * inputs - Pointer to MessageTree to delete from
207 * - pointer to command name to delete
208 * output - NONE
209 * side effects - recursively deletes a token from the Message Tree ;-)
210 */
211 /*
212 * How this works.
213 *
214 * Well, first off, the code recursively descends into the trie
215 * until it finds the terminating letter of the command being removed.
216 * Once it has done that, it marks the msg pointer as NULL then
217 * reduces the reference count on that allocated struct MessageTree
218 * since a command counts as a reference.
219 *
220 * Then it pops up the recurse stack. As it comes back up the recurse
221 * The code checks to see if the child now has no pointers or msg
222 * i.e. the links count has gone to zero. If its no longer used, the
223 * child struct MessageTree can be deleted. The parent reference
224 * to this child is then removed and the parents link count goes down.
225 * Thus, we continue to go back up removing all unused MessageTree(s)
226 */
227 static void
228 del_msg_element(struct MessageTree *mtree_p, const char *cmd)
229 {
230 struct MessageTree *ntree_p;
231
232 /*
233 * In case this is called for a nonexistent command
234 * check that there is a msg pointer here, else links-- goes -ve
235 * -db
236 */
237 if (*cmd == '\0' && mtree_p->msg)
238 {
239 mtree_p->msg = NULL;
240 mtree_p->links--;
241 }
242 else
243 {
244 if ((ntree_p = mtree_p->pointers[*cmd & (MAXPTRLEN - 1)]))
245 {
246 del_msg_element(ntree_p, cmd + 1);
247
248 if (ntree_p->links == 0)
249 {
250 mtree_p->pointers[*cmd & (MAXPTRLEN - 1)] = NULL;
251 mtree_p->links--;
252 free(ntree_p);
253 }
254 }
255 }
256 }
257
258 /* msg_tree_parse()
259 *
260 * inputs - Pointer to command to find
261 * - Pointer to MessageTree root
262 * output - Find given command returning Message * if found NULL if not
263 * side effects - none
264 */
265 static struct Message *
266 msg_tree_parse(const char *cmd)
267 {
268 struct MessageTree *mtree = &msg_tree;
269
270 assert(cmd && *cmd);
271
272 while (IsAlpha(*cmd) && (mtree = mtree->pointers[*cmd & (MAXPTRLEN - 1)]))
273 if (*++cmd == '\0')
274 return mtree->msg;
275
276 return NULL;
277 }
278
279 void
280 irc_parser_parse_line(char *msg)
281 {
282 toktabptr ttab;
283
284 Debug((DL_IRCMSG, "[IN]: %s", msg));
285
286 ttab = tokenize(msg, 14);
287 if (ttab->size < 2)
288 {
289 Debug((DL_BASIC, "BOGUS IRC MSG (%s)", ttab->tok[0]));
290 RET_BOGUS;
291 }
292
293 if (irc_network_get_remote_server_count() > 0)
294 {
295 struct Message *mptr = msg_tree_parse(ttab->tok[1]);
296
297 if (mptr)
298 mptr->handler(ttab);
299 }
300 else
301 {
302 /* Our direct link only */
303 msg_bogus = 0;
304
305 if (strcmp(ttab->tok[0], MSG_SERVER) == 0)
306 {
307 /* 0 1 2 3 */
308 /* SERVER hades.arpa 1 :ircd-hybrid test server */
309
310 if (ttab->size < 4)
311 RET_BOGUS;
312
313 numyxx = yxx_to_int(ttab->tok[6]);
314 irc_network_add_server(gMe.nserv, ttab->tok[1], NULL, BURSTING, 0, 0);
315
316 burst_start = peak_time(); /* = time(0) */
317 }
318 }
319 }
320
321 /* CJAAA P BjAAA :blah */
322 static void
323 parse_privmsg(toktabptr ttab)
324 {
325 if (ttab->size < 3)
326 RET_BOGUS;
327
328 /* Shouldn't receive any channel messages unless the client isn't +d */
329 if (!IsChannelName(ttab->tok[2]))
330 irc_client_handle_private(ttab);
331 }
332
333 /* CJAAH M testouil :+w */
334 static void
335 parse_mode(toktabptr ttab)
336 {
337 char *mode_change;
338
339 if (ttab->size <= 3)
340 RET_BOGUS;
341
342 mode_change = ttab->tok[3];
343 if (*mode_change == ':')
344 mode_change++;
345
346 /* Ignore channel modes changes. */
347 if (!IsChannelName(ttab->tok[2]))
348 irc_mode_handle(ttab->tok[0], mode_change);
349 }
350
351 /* AB N TomSOYer 4 1070318877 soyer thorongil.arda +i DAqAFk ABAAC :So
352 * CJAAA N mbu 1053984498
353 */
354 static void
355 parse_nick(toktabptr ttab)
356 {
357 /* Check for nick change */
358 if (ttab->size != 4)
359 RET_BOGUS;
360
361 irc_userbase_nick_change(ttab->tok[0], ttab->tok[2]);
362 }
363
364 static void
365 parse_uid(toktabptr ttab)
366 {
367 if (0)
368 {
369
370 }
371 else
372 {
373 char *mode, *ip, *nn;
374 time_t firsttime;
375 ClientAddr addr;
376
377 if (ttab->size < 10)
378 RET_BOGUS;
379
380 firsttime = atoi(ttab->tok[4]);
381
382 if (*ttab->tok[7] == '+')
383 {
384 /* Modes ! */
385 #define ARG_MODES_NUM 2
386 const char arg_modes[ARG_MODES_NUM] =
387 {
388 'r' /* undernet; account username */,
389 'h' /* asuka; hostname */
390 };
391 char *p;
392 int i, k = 8;
393
394 for (i = 0; i < ARG_MODES_NUM; i++)
395 for (p = ttab->tok[7]; *p; p++)
396 if (*p == arg_modes[i])
397 k++;
398
399 if (k - 8 > ARG_MODES_NUM)
400 RET_BOGUS; /* doh; +rrhrhhrhrh ? */
401
402 mode = ttab->tok[7];
403 ip = ttab->tok[k]; /* base64 IP */
404 nn = ttab->tok[k + 1]; /* base64 numnick */
405 }
406 else
407 {
408 mode = NULL;
409 ip = ttab->tok[7];
410 nn = ttab->tok[8];
411 }
412
413 /* Add IPv6 support here when available.
414 */
415 if (strlen(ip) != 6)
416 RET_BOGUS;
417
418 addr.ip4.s_addr = htonl(base64toint(ip));
419 irc_userbase_add(ttab->tok[2], ttab->tok[5], firsttime, mode, 0, addr, nn);
420 }
421
422 /* 0 1 2 3 4 5 */
423 /* :0MC SID hades.arpa 2 4XY :ircd-hybrid test server */
424 static void
425 parse_sid(toktabptr ttab)
426 {
427 if (ttab->size < 6)
428 RET_BOGUS;
429
430 irc_network_add_server(ttab->tok[0], ttab->tok[2], ttab->tok[4]);
431 }
432
433 static void
434 parse_quit(toktabptr ttab)
435 {
436 irc_userbase_remove(ttab->tok[0]);
437 }
438
439 static void
440 parse_squit(toktabptr ttab)
441 {
442 if (ttab->size < 4)
443 RET_BOGUS;
444
445 irc_network_remove_server_name(ttab->tok[2]);
446 }
447
448 static void
449 parse_kill(toktabptr ttab)
450 {
451 if (ttab->size < 4)
452 RET_BOGUS;
453
454 irc_userbase_remove(ttab->tok[2]);
455 }
456
457 /* CJ G !1053960261.525240 proxyscan.undernet.org 1053960261.525240
458 * -> CJ G !1053966530.81669 hub5.eu.jeb.com.fr 1053966530.81669
459 * <- Bk Z Bk !1053966530.81669 1053966530.81669 95363 1053966625.445512
460 */
461 static void
462 parse_ping(toktabptr ttab)
463 {
464 if (irc_network_is_my_downlink(base64toint(ttab->tok[0])))
465 send_raw(":%s PONG %s" CRLF, gMe.yy, gMe.yy);
466 }
467
468 static void
469 parse_pong(toktabptr ttab)
470 {
471 }
472
473 static void
474 parse_eob(toktabptr ttab)
475 {
476 unsigned int yy_int = base64toint(ttab->tok[0]);
477
478 irc_network_ack_end_of_burst(yy_int);
479
480 /* Only reply to EB from our direct downlink. */
481 if (irc_network_is_my_downlink(base64toint(ttab->tok[0])))
482 {
483 size_t recvBytes = peak_stream_get_read_count(gIRCStream);
484
485 burst_delay = peak_time();
486 burst_delay -= burst_start;
487
488 send_to_console("[%s] Loaded %ld clients in %ld seconds "
489 "(%ldKB - %4.2fKB/s)",
490 gConfig->server.id, irc_userbase_get_count(), burst_delay,
491 (int)(recvBytes/1024),
492 burst_delay ? (recvBytes/1024.)/burst_delay
493 : recvBytes/1024.);
494 }
495 }
496
497 static struct Message parse_msgtab[] =
498 {
499 { "EOB", 0, 0, 0, MAXPARA, 0, 0, parse_eob },
500 { "KILL", 0, 0, 0, MAXPARA, 0, 0, parse_kill },
501 { "MODE", 0, 0, 0, MAXPARA, 0, 0, parse_mode },
502 { "NICK", 0, 0, 0, MAXPARA, 0, 0, parse_nick },
503 { "PING", 0, 0, 0, MAXPARA, 0, 0, parse_ping },
504 { "PONG", 0, 0, 0, MAXPARA, 0, 0, parse_pong },
505 { "PRIVMSG", 0, 0, 0, MAXPARA, 0, 0, parse_privmsg },
506 { "QUIT", 0, 0, 0, MAXPARA, 0, 0, parse_quit },
507 { "SERVER", 0, 0, 0, MAXPARA, 0, 0, parse_server },
508 { "SID", 0, 0, 0, MAXPARA, 0, 0, parse_sid },
509 { "SQUIT", 0, 0, 0, MAXPARA, 0, 0, parse_squit },
510 { "UID", 0, 0, 0, MAXPARA, 0, 0, parse_uid },
511 { NULL, 0, 0, 0, 0, 0, 0, NULL }
512 };
513
514 void
515 irc_parser_init(void)
516 {
517 struct Message *tab = parse_msgtab;
518
519 tokenizer_init();
520
521 for (; tab->handler; ++tab)
522 add_msg_element(&msg_tree, tab, tab->cmd);
523 }