Good idea? Bad idea? Good idea but bad code? Would other crule functions be useful? Should I be spending more time on other feature requests?
(doc/example.conf is not yet updated; the syntax is basically the same as before, but with quoted strings instead of unquoted strings. The crule_expr production in ircd_parser.y should be clear.) Entrope 2006-07-25 Michael Poole <[EMAIL PROTECTED]> * include/crule.h (crule_free): Remove a level of indirection. (crule_parse): Remove declaration. (crule_text): New function. (crule_make_and): New function. (crule_make_or): New function. (crule_make_not): New function. (crule_make_connected): New function. (crule_make_directcon): New function. (crule_make_via): New function. (crule_make_directop): New function. * ircd/crule.c: Rewrite. * ircd/ircd_lexer.l: Recognize new tokens &&, ||, CONNECTED, DIRECTCON, DIRECTOP, VIA. * ircd/ircd_parser.y: Declare the new tokens. (optall): New production with type <num>. (crule_expr): New production with type <crule>. (%union): Add new member to expression type union. (cruleblock): Rewrite to not use {} and to use optall, crule_expr. * ircd/s_conf.c (conf_erase_crule_list): Remove a level of indirection for crule_free(). --- orig/include/crule.h +++ mod/include/crule.h @@ -6,16 +6,23 @@ #define INCLUDED_crule_h /* - * Proto types - */ - -/* * opaque node pointer */ struct CRuleNode; -extern void crule_free(struct CRuleNode** elem); extern int crule_eval(struct CRuleNode* rule); -extern struct CRuleNode* crule_parse(const char* rule); +extern char *crule_text(struct CRuleNode *rule); + +extern struct CRuleNode* crule_make_and(struct CRuleNode *left, + struct CRuleNode *right); +extern struct CRuleNode* crule_make_or(struct CRuleNode *left, + struct CRuleNode *right); +extern struct CRuleNode* crule_make_not(struct CRuleNode *arg); +extern struct CRuleNode* crule_make_connected(char *arg); +extern struct CRuleNode* crule_make_directcon(char *arg); +extern struct CRuleNode* crule_make_via(char *neighbor, + char *server); +extern struct CRuleNode* crule_make_directop(void); +extern void crule_free(struct CRuleNode* elem); #endif /* INCLUDED_crule_h */ --- orig/ircd/crule.c +++ mod/ircd/crule.c @@ -4,866 +4,294 @@ * @version $Id: crule.c,v 1.11 2006/01/06 11:39:04 isomer Exp $ * * by Tony Vencill (Tonto on IRC) <[EMAIL PROTECTED]> + * rewritten by Michael Poole <[EMAIL PROTECTED]> * - * The majority of this file is a recursive descent parser used to convert - * connection rules into expression trees when the conf file is read. - * All parsing structures and types are hidden in the interest of good - * programming style and to make possible future data structure changes - * without affecting the interface between this patch and the rest of the - * server. The only functions accessible externally are crule_parse, - * crule_free, and crule_eval. Prototypes for these functions can be - * found in h.h. - * - * Please direct any connection rule or SmartRoute questions to Tonto on - * IRC or by email to [EMAIL PROTECTED] - * - * For parser testing, defining CR_DEBUG generates a stand-alone parser - * that takes rules from stdin and prints out memory allocation - * information and the parsed rule. This stand alone parser is ignorant - * of the irc server and thus cannot do rule evaluation. Do not define - * this flag when compiling the server! If you wish to generate the - * test parser, compile from the ircd directory with a line similar to - * cc -o parser -DCR_DEBUG crule.c - * - * The define CR_CHKCONF is provided to generate routines needed in - * chkconf. These consist of the parser, a different crule_parse that - * prints errors to stderr, and crule_free (just for good style and to - * more closely simulate the actual ircd environment). crule_eval and - * the rule functions are made empty functions as in the stand-alone - * test parser. - * - * The production rules for the grammar are as follows ("rule" is the - * starting production): - * - * rule: - * orexpr END END is end of input or : - * orexpr: - * andexpr - * andexpr || orexpr - * andexpr: - * primary - * primary && andexpr - * primary: - * function - * ! primary - * ( orexpr ) - * function: - * word ( ) word is alphanumeric string, first character - * word ( arglist ) must be a letter - * arglist: - * word - * word , arglist + * This used to have a recursive descent parser and debugging helper + * code. That all got ripped out and moved into the config parser. */ -#include "config.h" +#include "config.h" #include "crule.h" -#ifndef CR_DEBUG - -/* ircd functions and types we need */ #include "client.h" #include "ircd.h" #include "ircd_alloc.h" -#include "ircd_chattr.h" -#include "ircd_log.h" +#include "ircd_log.h" /* for assert() */ #include "ircd_string.h" +#include "ircd_snprintf.h" +#include "list.h" #include "match.h" #include "s_bsd.h" -#include "s_debug.h" #include "struct.h" -#include <stdio.h> -#include <stdlib.h> - -#else /* includes and defines to make the stand-alone test parser */ - -#include <stdio.h> -#include <stdlib.h> - -#define BadPtr(x) (!(x) || (*(x) == '\0')) -#define DupString(x,y) \ - do { \ - x = (char*) MyMalloc(strlen(y)+1); \ - strcpy(x,y); \ - } while(0) - -/* We don't care about collation discrepancies here, it seems.... */ -#define ircd_strcmp strcasecmp - -#endif - -#include <string.h> - - -#if defined(CR_DEBUG) || defined(CR_CHKCONF) -#undef MyMalloc -#undef malloc -#define MyMalloc malloc -#undef MyFree -#undef free -#define MyFree free -#endif - -/* some constants and shared data types */ -#define CR_MAXARGLEN 80 /**< Maximum arg length (must be > HOSTLEN) */ -#define CR_MAXARGS 3 /**< Maximum number of args for a rule */ - -/* - * Some symbols for easy reading - */ - -/** Input scanner tokens. */ -enum crule_token { - CR_UNKNOWN, /**< Unknown token type. */ - CR_END, /**< End of input ('\\0' or ':'). */ - CR_AND, /**< Logical and operator (&&). */ - CR_OR, /**< Logical or operator (||). */ - CR_NOT, /**< Logical not operator (!). */ - CR_OPENPAREN, /**< Open parenthesis. */ - CR_CLOSEPAREN, /**< Close parenthesis. */ - CR_COMMA, /**< Comma. */ - CR_WORD /**< Something that looks like a hostmask (alphanumerics, "*?.-"). */ -}; - -/** Parser error codes. */ -enum crule_errcode { - CR_NOERR, /**< No error. */ - CR_UNEXPCTTOK, /**< Invalid token given context. */ - CR_UNKNWTOK, /**< Input did not form a valid token. */ - CR_EXPCTAND, /**< Did not see expected && operator. */ - CR_EXPCTOR, /**< Did not see expected || operator. */ - CR_EXPCTPRIM, /**< Expected a primitive (parentheses, ! or word). */ - CR_EXPCTOPEN, /**< Expected an open parenthesis after function name. */ - CR_EXPCTCLOSE, /**< Expected a close parenthesis to match open parenthesis. */ - CR_UNKNWFUNC, /**< Attempt to use an unknown function. */ - CR_ARGMISMAT /**< Wrong number of arguments to function. */ -}; - -/* - * Expression tree structure, function pointer, and tree pointer local! - */ /** Evaluation function for a connection rule. */ -typedef int (*crule_funcptr) (int, void **); +typedef int (*crule_funcptr) (struct CRuleNode *); /** Node in a connection rule tree. */ struct CRuleNode { crule_funcptr funcptr; /**< Evaluation function for this node. */ int numargs; /**< Number of arguments. */ - void *arg[CR_MAXARGS]; /**< Array of arguments. For operators, each arg + void *arg[1]; /**< Array of arguments. For operators, each arg is a tree element; for functions, each arg is a string. */ }; -/** Typedef to save typing effort. */ -typedef struct CRuleNode* CRuleNodePtr; - -/* local rule function prototypes */ -static int crule_connected(int, void *[]); -static int crule_directcon(int, void *[]); -static int crule_via(int, void *[]); -static int crule_directop(int, void *[]); -static int crule__andor(int, void *[]); -static int crule__not(int, void *[]); - -/* local parsing function prototypes */ -static int crule_gettoken(int* token, const char** str); -static void crule_getword(char*, int*, size_t, const char**); -static int crule_parseandexpr(CRuleNodePtr*, int *, const char**); -static int crule_parseorexpr(CRuleNodePtr*, int *, const char**); -static int crule_parseprimary(CRuleNodePtr*, int *, const char**); -static int crule_parsefunction(CRuleNodePtr*, int *, const char**); -static int crule_parsearglist(CRuleNodePtr, int *, const char**); - -#if defined(CR_DEBUG) || defined(CR_CHKCONF) -/* - * Prototypes for the test parser; if not debugging, - * these are defined in h.h - */ -struct CRuleNode* crule_parse(const char*); -void crule_free(struct CRuleNode**); -#ifdef CR_DEBUG -void print_tree(CRuleNodePtr); -#endif -#endif - -/** Error messages, indexed by the corresponding crule_errcode. */ -static char *crule_errstr[] = { - "Unknown error", /* NOERR? - for completeness */ - "Unexpected token", /* UNEXPCTTOK */ - "Unknown token", /* UNKNWTOK */ - "And expr expected", /* EXPCTAND */ - "Or expr expected", /* EXPCTOR */ - "Primary expected", /* EXPCTPRIM */ - "( expected", /* EXPCTOPEN */ - ") expected", /* EXPCTCLOSE */ - "Unknown function", /* UNKNWFUNC */ - "Argument mismatch" /* ARGMISMAT */ -}; - -/** Connection rule function table entry. */ -struct crule_funclistent { - char name[15]; /**< Function name. */ - int reqnumargs; /**< Required number of arguments. */ - crule_funcptr funcptr; /**< Handler function. */ -}; +/** Create a CRuleNode. + * @param[in] funcptr Evaluation function for the node. + * @param[in] numargs Number of arguments. + * @param[in] ... Arguments as void pointers. + * @return Newly allocated CRuleNode. + */ +static struct CRuleNode *crule_make(crule_funcptr funcptr, int numargs, ...) +{ + struct CRuleNode *res; + va_list va; + int ii; + + /* be sure we allocate at least sizeof(struct CRuleNode) */ + if (numargs > 0) + res = MyMalloc(sizeof(*res) + sizeof(res->arg[0]) * (numargs - 1)); + else + res = MyMalloc(sizeof(*res)); + res->funcptr = funcptr; + res->numargs = numargs; + va_start(va, numargs); + for (ii = 0; ii < numargs; ++ii) + res->arg[ii] = va_arg(va, void*); + va_end(va); + return res; +} + +/** Find any linked server with a name matching \a mask behind \a start. + * @param[in] mask Server name mask to search for. + * @param[in] start Server at which to start search. + * @return A pointer to a server matching \a mask, or NULL if none exist. + */ +static int crule_connected_from(const char *mask, struct Client *start) +{ + struct DLink *dl; + + assert(cli_serv(start) != NULL); + + for (dl = cli_serv(start)->down; dl; dl = dl->next) + if (match(mask, cli_name(dl->value.cptr)) + || crule_connected_from(mask, dl->value.cptr)) + return 1; -/** Defined connection rules. */ -static struct crule_funclistent crule_funclist[] = { - /* maximum function name length is 14 chars */ - {"connected", 1, crule_connected}, - {"directcon", 1, crule_directcon}, - {"via", 2, crule_via}, - {"directop", 0, crule_directop}, - {"", 0, NULL} /* this must be here to mark end of list */ -}; + return 0; +} -/** Check whether any connected server matches crulearg[0]. - * @param[in] numargs Number of valid args in \a crulearg. - * @param[in] crulearg Argument array. +/** Check whether any connected server matches \a rule->arg[0]. + * @param[in] rule The rule to evaluate. * @return Non-zero if the condition is true, zero if not. */ -static int crule_connected(int numargs, void *crulearg[]) +static int crule_connected(struct CRuleNode *rule) { -#if !defined(CR_DEBUG) && !defined(CR_CHKCONF) - struct Client *acptr; + assert(rule->numargs == 1); - /* taken from m_links */ - for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) - { - if (!IsServer(acptr) && !IsMe(acptr)) - continue; - if (match((char *)crulearg[0], cli_name(acptr))) - continue; - return (1); - } -#endif - return (0); + return crule_connected_from(rule->arg[0], &me); } -/** Check whether any directly connected server matches crulearg[0]. - * @param[in] numargs Number of valid args in \a crulearg. - * @param[in] crulearg Argument array. +/** Check whether any directly connected server matches \a rule->arg[0]. + * @param[in] rule The rule to evaluate. * @return Non-zero if the condition is true, zero if not. */ -static int crule_directcon(int numargs, void *crulearg[]) +static int crule_directcon(struct CRuleNode *rule) { -#if !defined(CR_DEBUG) && !defined(CR_CHKCONF) - int i; - struct Client *acptr; + struct DLink *dl; - /* adapted from m_trace and exit_one_client */ - for (i = 0; i <= HighestFd; i++) - { - if (!(acptr = LocalClientArray[i]) || !IsServer(acptr)) - continue; - if (match((char *)crulearg[0], cli_name(acptr))) - continue; - return (1); - } -#endif - return (0); + assert(rule->numargs == 1); + + for (dl = cli_serv(&me)->down; dl; dl = dl->next) + if (match(rule->arg[0], cli_name(dl->value.cptr))) + return 1; + + return 0; } -/** Check whether a connected server matching crulearg[1] is - * connnected to me behind one matching crulearg[0]. - * @param[in] numargs Number of valid args in \a crulearg. - * @param[in] crulearg Argument array. +/** Check whether a connected server matching \a rule->arg[1] is + * connnected to me behind one matching \a rule->arg[0]. + * @param[in] rule The rule to evaluate. * @return Non-zero if the condition is true, zero if not. */ -static int crule_via(int numargs, void *crulearg[]) +static int crule_via(struct CRuleNode *rule) { -#if !defined(CR_DEBUG) && !defined(CR_CHKCONF) - struct Client *acptr; + struct DLink *dl; - /* adapted from m_links */ - for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) - { - if (!IsServer(acptr) && !IsMe(acptr)) - continue; - if (match((char *)crulearg[1], cli_name(acptr))) - continue; - if (match((char *)crulearg[0], cli_name(cli_from(acptr)))) - continue; - return (1); - } -#endif - return (0); + assert(rule->numargs == 2); + + for (dl = cli_serv(&me)->down; dl; dl = dl->next) + if (match(rule->arg[0], cli_name(dl->value.cptr)) + && crule_connected_from(rule->arg[1], dl->value.cptr)) + return 1; + + return 0; } /** Check whether we have a local IRC operator. - * @param[in] numargs Number of valid args in \a crulearg. - * @param[in] crulearg Argument array. + * @param[in] rule The rule to evaluate. * @return Non-zero if the condition is true, zero if not. */ -static int crule_directop(int numargs, void *crulearg[]) +static int crule_directop(struct CRuleNode *rule) { -#if !defined(CR_DEBUG) && !defined(CR_CHKCONF) - int i; struct Client *acptr; + int i; + + assert(rule->numargs == 0); - /* adapted from m_trace */ for (i = 0; i <= HighestFd; i++) - { - if (!(acptr = LocalClientArray[i]) || !IsAnOper(acptr)) - continue; - return (1); - } -#endif - return (0); + if ((acptr = LocalClientArray[i]) && IsAnOper(acptr)) + return 1; + + return 0; } -/** Evaluate a connection rule. - * @param[in] rule Rule to evalute. - * @return Non-zero if the rule allows the connection, zero otherwise. +/** Perform an 'and' test on \a rule->arg[0] and \a rule->arg[1]. + * @param[in] rule Rule to evaluate. + * @return Non-zero if the condition is true, zero if not. */ -int crule_eval(struct CRuleNode* rule) +static int crule_and(struct CRuleNode *rule) { - return (rule->funcptr(rule->numargs, rule->arg)); + assert(rule->numargs == 2); + return crule_eval(rule->arg[0]) && crule_eval(rule->arg[1]); } -/** Perform an and-or-or test on crulearg[0] and crulearg[1]. - * If crulearg[2] is non-NULL, it means do OR; if it is NULL, do AND. - * @param[in] numargs Number of valid args in \a crulearg. - * @param[in] crulearg Argument array. +/** Perform an 'or' test on \a rule->arg[0] and \a rule->arg[1]. + * @param[in] rule Rule to evaluate. * @return Non-zero if the condition is true, zero if not. */ -static int crule__andor(int numargs, void *crulearg[]) +static int crule_or(struct CRuleNode *rule) { - int result1; - - result1 = crule_eval(crulearg[0]); - if (crulearg[2]) /* or */ - return (result1 || crule_eval(crulearg[1])); - else - return (result1 && crule_eval(crulearg[1])); + assert(rule->numargs == 2); + return crule_eval(rule->arg[0]) || crule_eval(rule->arg[1]); } -/** Logically invert the result of crulearg[0]. - * @param[in] numargs Number of valid args in \a crulearg. - * @param[in] crulearg Argument array. +/** Invert the logical sense of \a rule->arg[0]. + * @param[in] rule Rule to evaluate. * @return Non-zero if the condition is true, zero if not. */ -static int crule__not(int numargs, void *crulearg[]) +static int crule_not(struct CRuleNode *rule) { - return (!crule_eval(crulearg[0])); -} - -/** Scan an input token from \a ruleptr. - * @param[out] next_tokp Receives type of next token. - * @param[in,out] ruleptr Next readable character from input. - * @return Either CR_UNKNWTOK if the input was unrecognizable, else CR_NOERR. - */ -static int crule_gettoken(int* next_tokp, const char** ruleptr) -{ - char pending = '\0'; - - *next_tokp = CR_UNKNOWN; - while (*next_tokp == CR_UNKNOWN) - switch (*(*ruleptr)++) - { - case ' ': - case '\t': - break; - case '&': - if (pending == '\0') - pending = '&'; - else if (pending == '&') - *next_tokp = CR_AND; - else - return (CR_UNKNWTOK); - break; - case '|': - if (pending == '\0') - pending = '|'; - else if (pending == '|') - *next_tokp = CR_OR; - else - return (CR_UNKNWTOK); - break; - case '!': - *next_tokp = CR_NOT; - break; - case '(': - *next_tokp = CR_OPENPAREN; - break; - case ')': - *next_tokp = CR_CLOSEPAREN; - break; - case ',': - *next_tokp = CR_COMMA; - break; - case '\0': - (*ruleptr)--; - *next_tokp = CR_END; - break; - case ':': - *next_tokp = CR_END; - break; - default: - if ((IsAlpha(*(--(*ruleptr)))) || (**ruleptr == '*') || - (**ruleptr == '?') || (**ruleptr == '.') || (**ruleptr == '-')) - *next_tokp = CR_WORD; - else - return (CR_UNKNWTOK); - break; - } - return CR_NOERR; -} - -/** Scan a word from \a ruleptr. - * @param[out] word Output buffer. - * @param[out] wordlenp Length of word written to \a word (not including terminating NUL). - * @param[in] maxlen Maximum number of bytes writable to \a word. - * @param[in,out] ruleptr Next readable character from input. - */ -static void crule_getword(char* word, int* wordlenp, size_t maxlen, const char** ruleptr) -{ - char *word_ptr; - - word_ptr = word; - while ((size_t)(word_ptr - word) < maxlen - && (IsAlnum(**ruleptr) - || **ruleptr == '*' || **ruleptr == '?' - || **ruleptr == '.' || **ruleptr == '-')) - *word_ptr++ = *(*ruleptr)++; - *word_ptr = '\0'; - *wordlenp = word_ptr - word; -} - -/** Parse an entire rule. - * @param[in] rule Text form of rule. - * @return CRuleNode for rule, or NULL if there was a parse error. - */ -struct CRuleNode* crule_parse(const char *rule) -{ - const char* ruleptr = rule; - int next_tok; - struct CRuleNode* ruleroot = 0; - int errcode = CR_NOERR; - - if ((errcode = crule_gettoken(&next_tok, &ruleptr)) == CR_NOERR) { - if ((errcode = crule_parseorexpr(&ruleroot, &next_tok, &ruleptr)) == CR_NOERR) { - if (ruleroot != NULL) { - if (next_tok == CR_END) - return (ruleroot); - else - errcode = CR_UNEXPCTTOK; - } - else - errcode = CR_EXPCTOR; - } - } - if (ruleroot != NULL) - crule_free(&ruleroot); -#if !defined(CR_DEBUG) && !defined(CR_CHKCONF) - log_write(LS_CONFIG, L_WARNING, 0, "%s in rule: %s", - crule_errstr[errcode], rule); -#else - fprintf(stderr, "%s in rule: %s\n", crule_errstr[errcode], rule); -#endif - return 0; -} - -/** Parse an or expression. - * @param[out] orrootp Receives parsed node. - * @param[in,out] next_tokp Next input token type. - * @param[in,out] ruleptr Next input character. - * @return A crule_errcode value. - */ -static int crule_parseorexpr(CRuleNodePtr * orrootp, int *next_tokp, const char** ruleptr) -{ - int errcode = CR_NOERR; - CRuleNodePtr andexpr; - CRuleNodePtr orptr; - - *orrootp = NULL; - while (errcode == CR_NOERR) - { - errcode = crule_parseandexpr(&andexpr, next_tokp, ruleptr); - if ((errcode == CR_NOERR) && (*next_tokp == CR_OR)) - { - orptr = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode)); -#ifdef CR_DEBUG - fprintf(stderr, "allocating or element at %ld\n", orptr); -#endif - orptr->funcptr = crule__andor; - orptr->numargs = 3; - orptr->arg[2] = (void *)1; - if (*orrootp != NULL) - { - (*orrootp)->arg[1] = andexpr; - orptr->arg[0] = *orrootp; - } - else - orptr->arg[0] = andexpr; - *orrootp = orptr; - } - else - { - if (*orrootp != NULL) - { - if (andexpr != NULL) - { - (*orrootp)->arg[1] = andexpr; - return (errcode); - } - else - { - (*orrootp)->arg[1] = NULL; /* so free doesn't seg fault */ - return (CR_EXPCTAND); - } - } - else - { - *orrootp = andexpr; - return (errcode); - } - } - if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR) - return (errcode); - } - return (errcode); + assert(rule->numargs == 1); + return !crule_eval(rule->arg[0]); } -/** Parse an and expression. - * @param[out] androotp Receives parsed node. - * @param[in,out] next_tokp Next input token type. - * @param[in,out] ruleptr Next input character. - * @return A crule_errcode value. +/** Evaluate a connection rule. + * @param[in] rule Rule to evalute. + * @return Non-zero if the rule allows the connection, zero otherwise. */ -static int crule_parseandexpr(CRuleNodePtr * androotp, int *next_tokp, const char** ruleptr) +int crule_eval(struct CRuleNode* rule) { - int errcode = CR_NOERR; - CRuleNodePtr primary; - CRuleNodePtr andptr; - - *androotp = NULL; - while (errcode == CR_NOERR) - { - errcode = crule_parseprimary(&primary, next_tokp, ruleptr); - if ((errcode == CR_NOERR) && (*next_tokp == CR_AND)) - { - andptr = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode)); -#ifdef CR_DEBUG - fprintf(stderr, "allocating and element at %ld\n", andptr); -#endif - andptr->funcptr = crule__andor; - andptr->numargs = 3; - andptr->arg[2] = (void *)0; - if (*androotp != NULL) - { - (*androotp)->arg[1] = primary; - andptr->arg[0] = *androotp; - } - else - andptr->arg[0] = primary; - *androotp = andptr; - } - else - { - if (*androotp != NULL) - { - if (primary != NULL) - { - (*androotp)->arg[1] = primary; - return (errcode); - } - else - { - (*androotp)->arg[1] = NULL; /* so free doesn't seg fault */ - return (CR_EXPCTPRIM); - } - } - else - { - *androotp = primary; - return (errcode); - } - } - if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR) - return (errcode); - } - return (errcode); + return (rule->funcptr(rule)); } -/** Parse a primary expression. - * @param[out] primrootp Receives parsed node. - * @param[in,out] next_tokp Next input token type. - * @param[in,out] ruleptr Next input character. - * @return A crule_errcode value. +/** Free a connection rule and all its children. + * @param[in] elem Pointer to element to free. */ -static int crule_parseprimary(CRuleNodePtr* primrootp, int *next_tokp, const char** ruleptr) +void crule_free(struct CRuleNode* elem) { - CRuleNodePtr *insertionp; - int errcode = CR_NOERR; - - *primrootp = NULL; - insertionp = primrootp; - while (errcode == CR_NOERR) + if (!elem) + return; + if (elem->funcptr == crule_not) { - switch (*next_tokp) - { - case CR_OPENPAREN: - if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR) - break; - if ((errcode = crule_parseorexpr(insertionp, next_tokp, ruleptr)) != CR_NOERR) - break; - if (*insertionp == NULL) - { - errcode = CR_EXPCTAND; - break; - } - if (*next_tokp != CR_CLOSEPAREN) - { - errcode = CR_EXPCTCLOSE; - break; - } - errcode = crule_gettoken(next_tokp, ruleptr); - break; - case CR_NOT: - *insertionp = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode)); -#ifdef CR_DEBUG - fprintf(stderr, "allocating primary element at %ld\n", *insertionp); -#endif - (*insertionp)->funcptr = crule__not; - (*insertionp)->numargs = 1; - (*insertionp)->arg[0] = NULL; - insertionp = (CRuleNodePtr *) & ((*insertionp)->arg[0]); - if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR) - break; - continue; - case CR_WORD: - errcode = crule_parsefunction(insertionp, next_tokp, ruleptr); - break; - default: - if (*primrootp == NULL) - errcode = CR_NOERR; - else - errcode = CR_EXPCTPRIM; - break; - } - return (errcode); + crule_free(elem->arg[0]); } - return (errcode); -} - -/** Parse a function call. - * @param[out] funcrootp Receives parsed node. - * @param[in,out] next_tokp Next input token type. - * @param[in,out] ruleptr Next input character. - * @return A crule_errcode value. - */ -static int crule_parsefunction(CRuleNodePtr* funcrootp, int* next_tokp, const char** ruleptr) -{ - int errcode = CR_NOERR; - char funcname[CR_MAXARGLEN]; - int namelen; - int funcnum; - - *funcrootp = NULL; - crule_getword(funcname, &namelen, CR_MAXARGLEN - 1, ruleptr); - if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR) - return (errcode); - if (*next_tokp == CR_OPENPAREN) + else if (elem->funcptr == crule_and || elem->funcptr == crule_or) { - for (funcnum = 0;; funcnum++) - { - if (0 == ircd_strcmp(crule_funclist[funcnum].name, funcname)) - break; - if (crule_funclist[funcnum].name[0] == '\0') - return (CR_UNKNWFUNC); - } - if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR) - return (errcode); - *funcrootp = (CRuleNodePtr) MyMalloc(sizeof(struct CRuleNode)); -#ifdef CR_DEBUG - fprintf(stderr, "allocating function element at %ld\n", *funcrootp); -#endif - (*funcrootp)->funcptr = NULL; /* for freeing aborted trees */ - if ((errcode = - crule_parsearglist(*funcrootp, next_tokp, ruleptr)) != CR_NOERR) - return (errcode); - if (*next_tokp != CR_CLOSEPAREN) - return (CR_EXPCTCLOSE); - if ((crule_funclist[funcnum].reqnumargs != (*funcrootp)->numargs) && - (crule_funclist[funcnum].reqnumargs != -1)) - return (CR_ARGMISMAT); - if ((errcode = crule_gettoken(next_tokp, ruleptr)) != CR_NOERR) - return (errcode); - (*funcrootp)->funcptr = crule_funclist[funcnum].funcptr; - return (CR_NOERR); + crule_free(elem->arg[0]); + crule_free(elem->arg[1]); } else - return (CR_EXPCTOPEN); -} - -/** Parse the argument list to a CRuleNode. - * @param[in,out] argrootp Node whos argument list is being populated. - * @param[in,out] next_tokp Next input token type. - * @param[in,out] ruleptr Next input character. - * @return A crule_errcode value. - */ -static int crule_parsearglist(CRuleNodePtr argrootp, int *next_tokp, const char** ruleptr) -{ - int errcode = CR_NOERR; - char *argelemp = NULL; - char currarg[CR_MAXARGLEN]; - int arglen = 0; - char word[CR_MAXARGLEN]; - int wordlen = 0; - - argrootp->numargs = 0; - currarg[0] = '\0'; - while (errcode == CR_NOERR) { - switch (*next_tokp) - { - case CR_WORD: - crule_getword(word, &wordlen, CR_MAXARGLEN - 1, ruleptr); - if (currarg[0] != '\0') - { - if ((arglen + wordlen) < (CR_MAXARGLEN - 1)) - { - strcat(currarg, " "); - strcat(currarg, word); - arglen += wordlen + 1; - } - } - else - { - strcpy(currarg, word); - arglen = wordlen; - } - errcode = crule_gettoken(next_tokp, ruleptr); - break; - default: -#if !defined(CR_DEBUG) && !defined(CR_CHKCONF) - collapse(currarg); -#endif - if (!BadPtr(currarg)) - { - DupString(argelemp, currarg); - argrootp->arg[argrootp->numargs++] = (void *)argelemp; - } - if (*next_tokp != CR_COMMA) - return (CR_NOERR); - currarg[0] = '\0'; - errcode = crule_gettoken(next_tokp, ruleptr); - break; - } + while (elem->numargs) + MyFree(elem->arg[--elem->numargs]); } - return (errcode); + MyFree(elem); } -/* - * This function is recursive.. I wish I knew a nonrecursive way but - * I don't. Anyway, recursion is fun.. :) - * DO NOT CALL THIS FUNCTION WITH A POINTER TO A NULL POINTER - * (i.e.: If *elem is NULL, you're doing it wrong - seg fault) - */ -/** Free a connection rule and all its children. - * @param[in,out] elem Pointer to pointer to element to free. MUST NOT BE NULL. - */ -void crule_free(struct CRuleNode** elem) +struct CRuleNode * +crule_make_and(struct CRuleNode *left, struct CRuleNode *right) { - int arg, numargs; + return crule_make(crule_and, 2, left, right); +} - if ((*(elem))->funcptr == crule__not) - { - /* type conversions and ()'s are fun! ;) here have an aspirin.. */ - if ((*(elem))->arg[0] != NULL) - crule_free((struct CRuleNode**) &((*(elem))->arg[0])); - } - else if ((*(elem))->funcptr == crule__andor) - { - crule_free((struct CRuleNode**) &((*(elem))->arg[0])); - if ((*(elem))->arg[1] != NULL) - crule_free((struct CRuleNode**) &((*(elem))->arg[1])); - } - else - { - numargs = (*(elem))->numargs; - for (arg = 0; arg < numargs; arg++) - MyFree((*(elem))->arg[arg]); - } -#ifdef CR_DEBUG - fprintf(stderr, "freeing element at %ld\n", *elem); -#endif - MyFree(*elem); - *elem = 0; +struct CRuleNode * +crule_make_or(struct CRuleNode *left, struct CRuleNode *right) +{ + return crule_make(crule_or, 2, left, right); } -#ifdef CR_DEBUG -/** Display a connection rule as text. - * @param[in] printelem Connection rule to display. - */ -static void print_tree(CRuleNodePtr printelem) +struct CRuleNode * +crule_make_not(struct CRuleNode *arg) { - int funcnum, arg; + return crule_make(crule_not, 1, arg); +} - if (printelem->funcptr == crule__not) - { - printf("!( "); - print_tree((CRuleNodePtr) printelem->arg[0]); - printf(") "); - } - else if (printelem->funcptr == crule__andor) - { - printf("( "); - print_tree((CRuleNodePtr) printelem->arg[0]); - if (printelem->arg[2]) - printf("|| "); - else - printf("&& "); - print_tree((CRuleNodePtr) printelem->arg[1]); - printf(") "); - } - else - { - for (funcnum = 0;; funcnum++) - { - if (printelem->funcptr == crule_funclist[funcnum].funcptr) - break; - if (crule_funclist[funcnum].funcptr == NULL) - MyCoreDump; - } - printf("%s(", crule_funclist[funcnum].name); - for (arg = 0; arg < printelem->numargs; arg++) - { - if (arg != 0) - printf(","); - printf("%s", (char *)printelem->arg[arg]); - } - printf(") "); - } +struct CRuleNode * +crule_make_connected(char *arg) +{ + return crule_make(crule_connected, 1, arg); } -#endif +struct CRuleNode * +crule_make_directcon(char *arg) +{ + return crule_make(crule_directcon, 1, arg); +} -#ifdef CR_DEBUG -/** Read connection rules from stdin and display parsed forms as text. - * @return Zero. - */ -int main(void) +struct CRuleNode * +crule_make_via(char *neighbor, char *server) { - char indata[256]; - CRuleNode* rule; + return crule_make(crule_via, 2, neighbor, server); +} - printf("rule: "); - while (fgets(indata, 256, stdin) != NULL) - { - indata[strlen(indata) - 1] = '\0'; /* lose the newline */ - if ((rule = crule_parse(indata)) != NULL) - { - printf("equivalent rule: "); - print_tree((CRuleNodePtr) rule); - printf("\n"); - crule_free(&rule); - } - printf("\nrule: "); - } - printf("\n"); +struct CRuleNode * +crule_make_directop(void) +{ + return crule_make(crule_directop, 0); +} - return 0; +static int +crule_cat(struct CRuleNode *rule, char *buf, size_t remain) +{ + if (rule->funcptr == crule_connected) { + assert(rule->numargs == 1); + return ircd_snprintf(NULL, buf, remain, "connected(\"%s\")", rule->arg[0]); + } else if (rule->funcptr == crule_directcon) { + assert(rule->numargs == 1); + return ircd_snprintf(NULL, buf, remain, "directcon(\"%s\")", rule->arg[0]); + } else if (rule->funcptr == crule_via) { + assert(rule->numargs == 2); + return ircd_snprintf(NULL, buf, remain, "via(\"%s\", \"%s\")", + rule->arg[0], rule->arg[1]); + } else if (rule->funcptr == crule_directop) { + assert(rule->numargs == 0); + return ircd_snprintf(NULL, buf, remain, "directop()", rule->arg[0]); + } else if (rule->funcptr == crule_and || rule->funcptr == crule_or) { + const char *op = (rule->funcptr == crule_and) ? " && " : " || "; + size_t used = 0; + + assert(rule->numargs == 2); + if (remain > used++) + buf[used - 1] = '('; + used += crule_cat(rule->arg[0], buf + used, (remain < used) ? 0 : remain - used); + if (remain > used) + used += ircd_snprintf(NULL, buf + used, remain - used, op); + used += crule_cat(rule->arg[1], buf + used, (remain < used) ? 0 : remain - used); + if (remain > used++) + buf[used - 1] = ')'; + return used; + } else if (rule->funcptr == crule_not) { + if (remain) + buf[0] = '!'; + return 1 + crule_cat(rule->arg[0], buf + 1, (remain < 1) ? 0 : remain - 1); + } else + return ircd_snprintf(NULL, buf, remain, "???"); } -#endif +char *crule_text(struct CRuleNode *rule) +{ + char buf[BUFSIZE], *res; + buf[sizeof(buf) - 1] = '\0'; + crule_cat(rule, buf, sizeof(buf) - 1); + return DupString(res, buf); +} --- orig/ircd/ircd_lexer.l +++ mod/ircd/ircd_lexer.l @@ -73,6 +73,8 @@ #.* ; <<EOF>> { yypop_buffer_state(); if (YY_CURRENT_BUFFER) return TEOF; else yyterminate(); } +\&\& return LOGICAL_AND; +\|\| return LOGICAL_OR; ADMIN return ADMIN; ADMINISTRATOR return ADMIN; APASS_OPMODE return TPRIV_APASS_OPMODE; @@ -86,6 +88,7 @@ CLASS return CLASS; CLIENT return CLIENT; CONNECT return CONNECT; +CONNECTED return CONNECTED; CONNECTFREQ return CONNECTFREQ; CONTACT return CONTACT; CRULE return CRULE; @@ -94,6 +97,8 @@ DEOP_LCHAN return TPRIV_DEOP_LCHAN; DESCRIPTION return DESCRIPTION; DIE return TPRIV_DIE; +DIRECTCON return DIRECTCON; +DIRECTOP return DIRECTOP; DISPLAY return TPRIV_DISPLAY; FAST return FAST; FEATURES return FEATURES; @@ -175,6 +180,7 @@ USERNAME return USERNAME; UWORLD return UWORLD; VHOST return VHOST; +VIA return VIA; WALK_LCHAN return TPRIV_WALK_LCHAN; WEEKS return WEEKS; WIDE_GLINE return TPRIV_WIDE_GLINE; --- orig/ircd/ircd_parser.y +++ mod/ircd/ircd_parser.y @@ -207,6 +207,8 @@ %token LINESYNC %token FROM %token TEOF +%token LOGICAL_AND LOGICAL_OR +%token CONNECTED DIRECTCON VIA DIRECTOP /* and now a lot of privileges... */ %token TPRIV_CHAN_LIMIT TPRIV_MODE_LCHAN TPRIV_DEOP_LCHAN TPRIV_WALK_LCHAN %token TPRIV_LOCAL_KILL TPRIV_REHASH TPRIV_RESTART TPRIV_DIE @@ -221,10 +223,17 @@ %type <num> timespec timefactor factoredtimes factoredtime %type <num> expr yesorno privtype %type <num> blocklimit blocktypes blocktype +%type <num> optall +%type <crule> crule_expr +%left LOGICAL_OR +%left LOGICAL_AND %left '+' '-' %left '*' '/' +%nonassoc '!' +%nonassoc '(' ')' %union{ + struct CRuleNode *crule; char *text; int num; } @@ -946,62 +955,33 @@ dconf->message = $3; }; -cruleblock: CRULE +cruleblock: CRULE optall QSTRING optall crule_expr ';' { - tconn = CRULE_AUTO; -} '{' cruleitems '}' ';' -{ - struct CRuleNode *node = NULL; - if (!permitted(BLOCK_CRULE, 1)) - ; - else if (host == NULL) - parse_error("Missing host in crule block"); - else if (pass == NULL) - parse_error("Missing rule in crule block"); - else if ((node = crule_parse(pass)) == NULL) - parse_error("Invalid rule '%s' in crule block", pass); - else + if (permitted(BLOCK_CRULE, 1) && $5) { struct CRuleConf *p = (struct CRuleConf*) MyMalloc(sizeof(*p)); - p->hostmask = host; - p->rule = pass; - p->type = tconn; - p->node = node; + p->hostmask = collapse($3); + p->rule = crule_text($5); + p->type = ($2 || $4) ? CRULE_ALL : CRULE_AUTO; + p->node = $5; p->next = cruleConfList; cruleConfList = p; } - if (!node) - { - MyFree(host); - MyFree(pass); - } - host = pass = NULL; - tconn = 0; }; -cruleitems: cruleitem cruleitems | cruleitem; -cruleitem: cruleserver | crulerule | cruleall; +optall: { $$ = 0; }; + | ALL { $$ = 1; }; -cruleserver: SERVER '=' QSTRING ';' -{ - MyFree(host); - collapse($3); - host = $3; -}; - -crulerule: RULE '=' QSTRING ';' -{ - MyFree(pass); - pass = $3; -}; - -cruleall: ALL '=' YES ';' -{ - tconn = CRULE_ALL; -} | ALL '=' NO ';' -{ - tconn = CRULE_AUTO; -}; +crule_expr: + '(' crule_expr ')' { $$ = $2; } + | crule_expr LOGICAL_AND crule_expr { $$ = crule_make_and($1, $3); } + | crule_expr LOGICAL_OR crule_expr { $$ = crule_make_or($1, $3); } + | '!' crule_expr { $$ = crule_make_not($2); } + | CONNECTED '(' QSTRING ')' { $$ = crule_make_connected($3); } + | DIRECTCON '(' QSTRING ')' { $$ = crule_make_directcon($3); } + | VIA '(' QSTRING ',' QSTRING ')' { $$ = crule_make_via($3, $5); } + | DIRECTOP '(' ')' { $$ = crule_make_directop(); } + ; motdblock: MOTD '{' motditems '}' ';' { --- orig/ircd/s_conf.c +++ mod/ircd/s_conf.c @@ -751,7 +751,7 @@ for ( ; p; p = next) { next = p->next; - crule_free(&p->node); + crule_free(p->node); MyFree(p->hostmask); MyFree(p->rule); MyFree(p); _______________________________________________ Coder-com mailing list Coder-com@undernet.org http://undernet.sbg.org/mailman/listinfo/coder-com