3 * connection rule patch
4 * by Tony Vencill (Tonto on IRC) <vencill@bga.com>
6 * The majority of this file is a recusive descent parser used to convert
7 * connection rules into expression trees when the conf file is read.
8 * All parsing structures and types are hidden in the interest of good
9 * programming style and to make possible future data structure changes
10 * without affecting the interface between this patch and the rest of the
11 * server. The only functions accessible externally are crule_parse,
12 * crule_free, and crule_eval. Prototypes for these functions can be
15 * Please direct any connection rule or SmartRoute questions to Tonto on
16 * IRC or by email to vencill@bga.com.
18 * For parser testing, defining CR_DEBUG generates a stand-alone parser
19 * that takes rules from stdin and prints out memory allocation
20 * information and the parsed rule. This stand alone parser is ignorant
21 * of the irc server and thus cannot do rule evaluation. Do not define
22 * this flag when compiling the server! If you wish to generate the
23 * test parser, compile from the ircd directory with a line similar to
24 * cc -o parser -DCR_DEBUG crule.c
26 * The define CR_CHKCONF is provided to generate routines needed in
27 * chkconf. These consist of the parser, a different crule_parse that
28 * prints errors to stderr, and crule_free (just for good style and to
29 * more closely simulate the actual ircd environment). crule_eval and
30 * the rule functions are made empty functions as in the stand-alone
38 /* ircd functions and types we need */
41 #include "ircd_alloc.h"
42 #include "ircd_chattr.h"
43 #include "ircd_string.h"
52 #else /* includes and defines to make the stand-alone test parser */
57 #define BadPtr(x) (!(x) || (*(x) == '\0'))
58 #define DupString(x,y) \
60 x = (char*) MyMalloc(strlen(y)+1); \
64 /* We don't care about collation discrepacies here, it seems.... */
65 #define ircd_strcmp strcasecmp
72 #if defined(CR_DEBUG) || defined(CR_CHKCONF)
75 #define MyMalloc malloc
81 /* some constants and shared data types */
82 #define CR_MAXARGLEN 80 /* why 80? why not? it's > hostname lengths */
83 #define CR_MAXARGS 3 /* There's a better way to do this,
87 * Some symbols for easy reading
91 CR_UNKNOWN, CR_END, CR_AND, CR_OR, CR_NOT, CR_OPENPAREN, CR_CLOSEPAREN,
96 CR_NOERR, CR_UNEXPCTTOK, CR_UNKNWTOK, CR_EXPCTAND, CR_EXPCTOR,
97 CR_EXPCTPRIM, CR_EXPCTOPEN, CR_EXPCTCLOSE, CR_UNKNWFUNC, CR_ARGMISMAT
101 * Expression tree structure, function pointer, and tree pointer local!
103 typedef int (*crule_funcptr) (int, void **);
106 crule_funcptr funcptr;
108 void *arg[CR_MAXARGS]; /* For operators arg points to a tree element;
109 for functions arg points to a char string. */
112 typedef struct CRuleNode* CRuleNodePtr;
114 /* local rule function prototypes */
115 static int crule_connected(int, void **);
116 static int crule_directcon(int, void **);
117 static int crule_via(int, void **);
118 static int crule_directop(int, void **);
119 static int crule__andor(int, void **);
120 static int crule__not(int, void **);
122 /* local parsing function prototypes */
123 static int crule_gettoken(int* token, const char** str);
124 static void crule_getword(char*, int*, size_t, const char**);
125 static int crule_parseandexpr(CRuleNodePtr*, int *, const char**);
126 static int crule_parseorexpr(CRuleNodePtr*, int *, const char**);
127 static int crule_parseprimary(CRuleNodePtr*, int *, const char**);
128 static int crule_parsefunction(CRuleNodePtr*, int *, const char**);
129 static int crule_parsearglist(CRuleNodePtr, int *, const char**);
131 #if defined(CR_DEBUG) || defined(CR_CHKCONF)
133 * Prototypes for the test parser; if not debugging,
134 * these are defined in h.h
136 struct CRuleNode* crule_parse(const char*);
137 void crule_free(struct CRuleNode**);
139 void print_tree(CRuleNodePtr);
144 char *crule_errstr[] = {
145 "Unknown error", /* NOERR? - for completeness */
146 "Unexpected token", /* UNEXPCTTOK */
147 "Unknown token", /* UNKNWTOK */
148 "And expr expected", /* EXPCTAND */
149 "Or expr expected", /* EXPCTOR */
150 "Primary expected", /* EXPCTPRIM */
151 "( expected", /* EXPCTOPEN */
152 ") expected", /* EXPCTCLOSE */
153 "Unknown function", /* UNKNWFUNC */
154 "Argument mismatch" /* ARGMISMAT */
157 /* function table - null terminated */
158 struct crule_funclistent {
159 char name[15]; /* MAXIMUM FUNCTION NAME LENGTH IS 14 CHARS!! */
161 crule_funcptr funcptr;
164 struct crule_funclistent crule_funclist[] = {
165 /* maximum function name length is 14 chars */
166 {"connected", 1, crule_connected},
167 {"directcon", 1, crule_directcon},
168 {"via", 2, crule_via},
169 {"directop", 0, crule_directop},
170 {"", 0, NULL} /* this must be here to mark end of list */
173 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
174 static int crule_connected(int numargs, void *crulearg[])
176 struct Client *acptr;
178 /* taken from m_links */
179 for (acptr = GlobalClientList; acptr; acptr = acptr->next)
181 if (!IsServer(acptr) && !IsMe(acptr))
183 if (match((char *)crulearg[0], acptr->name))
190 static int crule_connected(int numargs, void **crulearg)
196 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
197 static int crule_directcon(int numargs, void *crulearg[])
200 struct Client *acptr;
202 /* adapted from m_trace and exit_one_client */
203 for (i = 0; i <= HighestFd; i++)
205 if (!(acptr = LocalClientArray[i]) || !IsServer(acptr))
207 if (match((char *)crulearg[0], acptr->name))
214 static int crule_directcon(int numargs, void **crulearg)
220 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
221 static int crule_via(int numargs, void *crulearg[])
223 struct Client *acptr;
225 /* adapted from m_links */
226 for (acptr = GlobalClientList; acptr; acptr = acptr->next)
228 if (!IsServer(acptr) && !IsMe(acptr))
230 if (match((char *)crulearg[1], acptr->name))
232 if (match((char *)crulearg[0], (LocalClientArray[acptr->from->fd])->name))
239 static int crule_via(int numargs, void **crulearg)
245 static int crule_directop(int numargs, void **crulearg)
247 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
249 struct Client *acptr;
251 /* adapted from m_trace */
252 for (i = 0; i <= HighestFd; i++)
254 if (!(acptr = LocalClientArray[i]) || !IsAnOper(acptr))
262 static int crule__andor(int numargs, void *crulearg[])
266 result1 = ((CRuleNodePtr) crulearg[0])->funcptr
267 (((CRuleNodePtr) crulearg[0])->numargs,
268 ((CRuleNodePtr) crulearg[0])->arg);
269 if (crulearg[2]) /* or */
271 ((CRuleNodePtr) crulearg[1])->funcptr
272 (((CRuleNodePtr) crulearg[1])->numargs,
273 ((CRuleNodePtr) crulearg[1])->arg));
276 ((CRuleNodePtr) crulearg[1])->funcptr
277 (((CRuleNodePtr) crulearg[1])->numargs,
278 ((CRuleNodePtr) crulearg[1])->arg));
281 static int crule__not(int numargs, void *crulearg[])
283 return (!((CRuleNodePtr) crulearg[0])->funcptr
284 (((CRuleNodePtr) crulearg[0])->numargs,
285 ((CRuleNodePtr) crulearg[0])->arg));
288 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
289 int crule_eval(struct CRuleNode* rule)
291 return (rule->funcptr(rule->numargs, rule->arg));
295 static int crule_gettoken(int* next_tokp, const char** ruleptr)
299 *next_tokp = CR_UNKNOWN;
300 while (*next_tokp == CR_UNKNOWN)
301 switch (*(*ruleptr)++)
309 else if (pending == '&')
312 return (CR_UNKNWTOK);
317 else if (pending == '|')
320 return (CR_UNKNWTOK);
326 *next_tokp = CR_OPENPAREN;
329 *next_tokp = CR_CLOSEPAREN;
332 *next_tokp = CR_COMMA;
342 if ((IsAlpha(*(--(*ruleptr)))) || (**ruleptr == '*') ||
343 (**ruleptr == '?') || (**ruleptr == '.') || (**ruleptr == '-'))
344 *next_tokp = CR_WORD;
346 return (CR_UNKNWTOK);
352 static void crule_getword(char* word, int* wordlenp, size_t maxlen, const char** ruleptr)
357 while ((size_t)(word_ptr - word) < maxlen
358 && (IsAlnum(**ruleptr)
359 || **ruleptr == '*' || **ruleptr == '?'
360 || **ruleptr == '.' || **ruleptr == '-'))
361 *word_ptr++ = *(*ruleptr)++;
363 *wordlenp = word_ptr - word;
369 * orexpr END END is end of input or :
381 * word ( ) word is alphanumeric string, first character
382 * word ( arglist ) must be a letter
387 struct CRuleNode* crule_parse(const char *rule)
389 const char* ruleptr = rule;
391 struct CRuleNode* ruleroot = 0;
392 int errcode = CR_NOERR;
394 if ((errcode = crule_gettoken(&next_tok, &ruleptr)) == CR_NOERR) {
395 if ((errcode = crule_parseorexpr(&ruleroot, &next_tok, &ruleptr)) == CR_NOERR) {
396 if (ruleroot != NULL) {
397 if (next_tok == CR_END)
400 errcode = CR_UNEXPCTTOK;
403 errcode = CR_EXPCTOR;
406 if (ruleroot != NULL)
407 crule_free(&ruleroot);
408 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
409 Debug((DEBUG_ERROR, "%s in rule: %s", crule_errstr[errcode], rule));
411 fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule);
416 static int crule_parseorexpr(CRuleNodePtr * orrootp, int *next_tokp, const char** ruleptr)
418 int errcode = CR_NOERR;
419 CRuleNodePtr andexpr;
423 while (errcode == CR_NOERR)
425 errcode = crule_parseandexpr(&andexpr, next_tokp, ruleptr);
426 if ((errcode == CR_NOERR) && (*next_tokp == CR_OR))
428 orptr = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
430 fprintf(stderr, "allocating or element at %ld\n", orptr);
432 orptr->funcptr = crule__andor;
434 orptr->arg[2] = (void *)1;
435 if (*orrootp != NULL)
437 (*orrootp)->arg[1] = andexpr;
438 orptr->arg[0] = *orrootp;
441 orptr->arg[0] = andexpr;
446 if (*orrootp != NULL)
450 (*orrootp)->arg[1] = andexpr;
455 (*orrootp)->arg[1] = NULL; /* so free doesn't seg fault */
456 return (CR_EXPCTAND);
465 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
471 static int crule_parseandexpr(CRuleNodePtr * androotp, int *next_tokp, const char** ruleptr)
473 int errcode = CR_NOERR;
474 CRuleNodePtr primary;
478 while (errcode == CR_NOERR)
480 errcode = crule_parseprimary(&primary, next_tokp, ruleptr);
481 if ((errcode == CR_NOERR) && (*next_tokp == CR_AND))
483 andptr = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
485 fprintf(stderr, "allocating and element at %ld\n", andptr);
487 andptr->funcptr = crule__andor;
489 andptr->arg[2] = (void *)0;
490 if (*androotp != NULL)
492 (*androotp)->arg[1] = primary;
493 andptr->arg[0] = *androotp;
496 andptr->arg[0] = primary;
501 if (*androotp != NULL)
505 (*androotp)->arg[1] = primary;
510 (*androotp)->arg[1] = NULL; /* so free doesn't seg fault */
511 return (CR_EXPCTPRIM);
520 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
526 static int crule_parseprimary(CRuleNodePtr* primrootp, int *next_tokp, const char** ruleptr)
528 CRuleNodePtr *insertionp;
529 int errcode = CR_NOERR;
532 insertionp = primrootp;
533 while (errcode == CR_NOERR)
538 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
540 if ((errcode = crule_parseorexpr(insertionp, next_tokp, ruleptr)) != CR_NOERR)
542 if (*insertionp == NULL)
544 errcode = CR_EXPCTAND;
547 if (*next_tokp != CR_CLOSEPAREN)
549 errcode = CR_EXPCTCLOSE;
552 errcode = crule_gettoken(next_tokp, ruleptr);
555 *insertionp = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
557 fprintf(stderr, "allocating primary element at %ld\n", *insertionp);
559 (*insertionp)->funcptr = crule__not;
560 (*insertionp)->numargs = 1;
561 (*insertionp)->arg[0] = NULL;
562 insertionp = (CRuleNodePtr *) & ((*insertionp)->arg[0]);
563 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
567 errcode = crule_parsefunction(insertionp, next_tokp, ruleptr);
570 if (*primrootp == NULL)
573 errcode = CR_EXPCTPRIM;
581 static int crule_parsefunction(CRuleNodePtr* funcrootp, int* next_tokp, const char** ruleptr)
583 int errcode = CR_NOERR;
584 char funcname[CR_MAXARGLEN];
589 crule_getword(funcname, &namelen, CR_MAXARGLEN - 1, ruleptr);
590 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
592 if (*next_tokp == CR_OPENPAREN)
594 for (funcnum = 0;; funcnum++)
596 if (0 == ircd_strcmp(crule_funclist[funcnum].name, funcname))
598 if (crule_funclist[funcnum].name[0] == '\0')
599 return (CR_UNKNWFUNC);
601 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
603 *funcrootp = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode));
605 fprintf(stderr, "allocating function element at %ld\n", *funcrootp);
607 (*funcrootp)->funcptr = NULL; /* for freeing aborted trees */
609 crule_parsearglist(*funcrootp, next_tokp, ruleptr)) != CR_NOERR)
611 if (*next_tokp != CR_CLOSEPAREN)
612 return (CR_EXPCTCLOSE);
613 if ((crule_funclist[funcnum].reqnumargs != (*funcrootp)->numargs) &&
614 (crule_funclist[funcnum].reqnumargs != -1))
615 return (CR_ARGMISMAT);
616 if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR)
618 (*funcrootp)->funcptr = crule_funclist[funcnum].funcptr;
622 return (CR_EXPCTOPEN);
625 static int crule_parsearglist(CRuleNodePtr argrootp, int *next_tokp, const char** ruleptr)
627 int errcode = CR_NOERR;
628 char *argelemp = NULL;
629 char currarg[CR_MAXARGLEN];
631 char word[CR_MAXARGLEN];
634 argrootp->numargs = 0;
636 while (errcode == CR_NOERR)
641 crule_getword(word, &wordlen, CR_MAXARGLEN - 1, ruleptr);
642 if (currarg[0] != '\0')
644 if ((arglen + wordlen) < (CR_MAXARGLEN - 1))
646 strcat(currarg, " ");
647 strcat(currarg, word);
648 arglen += wordlen + 1;
653 strcpy(currarg, word);
656 errcode = crule_gettoken(next_tokp, ruleptr);
659 #if !defined(CR_DEBUG) && !defined(CR_CHKCONF)
662 if (!BadPtr(currarg))
664 DupString(argelemp, currarg);
665 argrootp->arg[argrootp->numargs++] = (void *)argelemp;
667 if (*next_tokp != CR_COMMA)
670 errcode = crule_gettoken(next_tokp, ruleptr);
678 * This function is recursive.. I wish I knew a nonrecursive way but
679 * I dont. anyway, recursion is fun.. :)
680 * DO NOT CALL THIS FUNTION WITH A POINTER TO A NULL POINTER
681 * (ie: If *elem is NULL, you're doing it wrong - seg fault)
683 void crule_free(struct CRuleNode** elem)
687 if ((*(elem))->funcptr == crule__not)
689 /* type conversions and ()'s are fun! ;) here have an asprin.. */
690 if ((*(elem))->arg[0] != NULL)
691 crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
693 else if ((*(elem))->funcptr == crule__andor)
695 crule_free((struct CRuleNode**) &((*(elem))->arg[0]));
696 if ((*(elem))->arg[1] != NULL)
697 crule_free((struct CRuleNode**) &((*(elem))->arg[1]));
701 numargs = (*(elem))->numargs;
702 for (arg = 0; arg < numargs; arg++)
703 MyFree((*(elem))->arg[arg]);
706 fprintf(stderr, "freeing element at %ld\n", *elem);
713 static void print_tree(CRuleNodePtr printelem)
717 if (printelem->funcptr == crule__not)
720 print_tree((CRuleNodePtr) printelem->arg[0]);
723 else if (printelem->funcptr == crule__andor)
726 print_tree((CRuleNodePtr) printelem->arg[0]);
727 if (printelem->arg[2])
731 print_tree((CRuleNodePtr) printelem->arg[1]);
736 for (funcnum = 0;; funcnum++)
738 if (printelem->funcptr == crule_funclist[funcnum].funcptr)
740 if (crule_funclist[funcnum].funcptr == NULL)
743 printf("%s(", crule_funclist[funcnum].name);
744 for (arg = 0; arg < printelem->numargs; arg++)
748 printf("%s", (char *)printelem->arg[arg]);
763 while (fgets(indata, 256, stdin) != NULL)
765 indata[strlen(indata) - 1] = '\0'; /* lose the newline */
766 if ((rule = crule_parse(indata)) != NULL)
768 printf("equivalent rule: ");
769 print_tree((CRuleNodePtr) rule);