X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fircd_parser.y;h=83cab62fb0483bdbb8fc36f4adf6b1bd9e2b0109;hb=refs%2Fheads%2Fupstream;hp=2471248d0200458ce716b98235c1946a869b8cb4;hpb=9bc862d2f77e8a1fe44b44c63dc5741d9dbf1d74;p=ircu2.10.12-pk.git diff --git a/ircd/ircd_parser.y b/ircd/ircd_parser.y index 2471248..83cab62 100644 --- a/ircd/ircd_parser.y +++ b/ircd/ircd_parser.y @@ -1,8 +1,8 @@ /* * ircd_parser.y: A yacc/bison parser for ircd config files. * This is part of ircu, an Internet Relay Chat server. - * The contents of this file are Copyright(C) 2001 by Andrew Miller, the - * ircd-hybrid team and the ircu team. + * The contents of this file are Copyright 2001 Diane Bruce, + * Andrew Miller, the ircd-hybrid team and the ircu team. * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -46,52 +46,67 @@ #include "opercmds.h" #include "parse.h" #include "res.h" +#include "s_auth.h" #include "s_bsd.h" #include "s_conf.h" #include "s_debug.h" #include "s_misc.h" #include "send.h" #include "struct.h" -#include "support.h" #include "sys.h" #include #include #include #include + #define MAX_STRINGS 80 /* Maximum number of feature params. */ +#define USE_IPV4 (1 << 16) +#define USE_IPV6 (1 << 17) + extern struct LocalConf localConf; extern struct DenyConf* denyConfList; extern struct CRuleConf* cruleConfList; extern struct ServerConf* serverConfList; - extern struct qline *GlobalQuarantineList; - + extern struct s_map* GlobalServiceMapList; + extern struct qline* GlobalQuarantineList; int yylex(void); /* Now all the globals we need :/... */ - int tping, tconn, maxlinks, sendq, port; - int stringno; - char *name, *pass, *host; + int tping, tconn, maxlinks, sendq, port, invert, stringno, flags; + char *name, *pass, *host, *ip, *username, *origin, *hub_limit; + struct SLink *hosts; char *stringlist[MAX_STRINGS]; + struct ListenerFlags listen_flags; struct ConnectionClass *c_class; - struct ConfItem *aconf; struct DenyConf *dconf; struct ServerConf *sconf; - struct qline *qconf = NULL; + struct s_map *smap; + struct Privs privs; + struct Privs privs_dirty; static void parse_error(char *pattern,...) { - va_list vl; - struct VarData vd; - va_start(vl,pattern); - vd.vd_format = pattern; - vd.vd_args = vl; - sendto_opmask_butone(0, SNO_OLDSNO, "Config: %v", &vd); - va_end(vl); + static char error_buffer[1024]; + va_list vl; + va_start(vl,pattern); + ircd_vsnprintf(NULL, error_buffer, sizeof(error_buffer), pattern, vl); + va_end(vl); + yyerror(error_buffer); } + +static void free_slist(struct SLink **link) { + struct SLink *next; + while (*link != NULL) { + next = (*link)->next; + MyFree((*link)->value.cp); + free_link(*link); + *link = next; + } +} + %} %token QSTRING %token NUMBER -%token FNAME %token GENERAL %token ADMIN @@ -103,9 +118,12 @@ static void parse_error(char *pattern,...) { %token PINGFREQ %token CONNECTFREQ %token MAXLINKS +%token MAXHOPS %token SENDQ %token NAME %token HOST +%token IP +%token USERNAME %token PASS %token LOCAL %token SECONDS @@ -130,9 +148,7 @@ static void parse_error(char *pattern,...) { %token YES %token NO %token OPER -%token PORT %token VHOST -%token MASK %token HIDDEN %token MOTD %token JUPE @@ -147,21 +163,35 @@ static void parse_error(char *pattern,...) { %token TFILE %token RULE %token ALL -%token IP %token FEATURES %token QUARANTINE -/* and now a lot of priviledges... */ +%token PSEUDO +%token PREPEND +%token USERMODE +%token IAUTH +%token TIMEOUT +%token FAST +%token AUTOCONNECT +%token PROGRAM +%token TOK_IPV4 TOK_IPV6 +%token DNS +%token SSL +%token CERTFILE +%token KEYFILE +%token CAFILE +/* and now a lot of privileges... */ %token TPRIV_CHAN_LIMIT TPRIV_MODE_LCHAN TPRIV_DEOP_LCHAN TPRIV_WALK_LCHAN -%token TPRIV_KILL TPRIV_LOCAL_KILL TPRIV_REHASH TPRIV_RESTART TPRIV_DIE -%token TPRIV_GLINE TPRIV_LOCAL_GLINE TPRIV_JUPE TPRIV_LOCAL_JUPE +%token TPRIV_LOCAL_KILL TPRIV_REHASH TPRIV_RESTART TPRIV_DIE +%token TPRIV_GLINE TPRIV_LOCAL_GLINE TPRIV_LOCAL_JUPE TPRIV_LOCAL_BADCHAN %token TPRIV_LOCAL_OPMODE TPRIV_OPMODE TPRIV_SET TPRIV_WHOX TPRIV_BADCHAN -%token TPRIV_LOCAL_BADCHAN %token TPRIV_SEE_CHAN TPRIV_SHOW_INVIS TPRIV_SHOW_ALL_INVIS TPRIV_PROPAGATE -%token TPRIV_UNLIMIT_QUERY TPRIV_DISPLAY TPRIV_SEE_OPERS TPRIV_WIDE_GLINE +%token TPRIV_UNLIMIT_QUERY TPRIV_UNLIMIT_FLOOD TPRIV_DISPLAY TPRIV_SEE_OPERS +%token TPRIV_WIDE_GLINE TPRIV_FORCE_OPMODE TPRIV_FORCE_LOCAL_OPMODE +%token TPRIV_APASS_OPMODE TPRIV_LIST_CHAN /* and some types... */ %type sizespec %type timespec timefactor factoredtimes factoredtime -%type expr yesorno privtype +%type expr yesorno privtype address_family %left '+' '-' %left '*' '/' @@ -173,9 +203,10 @@ static void parse_error(char *pattern,...) { %% /* Blocks in the config file... */ blocks: blocks block | block; -block: adminblock | generalblock | classblock | connectblock | - serverblock | operblock | portblock | jupeblock | clientblock | - killblock | cruleblock | motdblock | featuresblock | quarantineblock; +block: adminblock | generalblock | classblock | connectblock | sslblock | + uworldblock | operblock | portblock | jupeblock | clientblock | + killblock | cruleblock | motdblock | featuresblock | quarantineblock | + pseudoblock | iauthblock | error ';'; /* The timespec, sizespec and expr was ripped straight from * ircd-hybrid-7. */ @@ -221,7 +252,7 @@ sizespec: expr { } ; -/* this is an arithmatic expression */ +/* this is an arithmetic expression */ expr: NUMBER { $$ = $1; @@ -239,8 +270,7 @@ expr: NUMBER $$ = $1 / $3; } /* leave this out until we find why it makes BSD yacc dump core -larne - | '-' expr %prec NEG - = { + | '-' expr %prec NEG { $$ = -$2; } */ | '(' expr ')' { @@ -251,47 +281,110 @@ expr: NUMBER jupeblock: JUPE '{' jupeitems '}' ';' ; jupeitems: jupeitem jupeitems | jupeitem; jupeitem: jupenick; -jupenick: NICK '=' QSTRING +jupenick: NICK '=' QSTRING ';' { - addNickJupes(yylval.text); -} ';'; + addNickJupes($3); + MyFree($3); +}; -generalblock: GENERAL '{' generalitems '}' ';' ; +generalblock: GENERAL +{ + /* Zero out the vhost addresses, in case they were removed. */ + memset(&VirtualHost_v4.addr, 0, sizeof(VirtualHost_v4.addr)); + memset(&VirtualHost_v6.addr, 0, sizeof(VirtualHost_v6.addr)); +} '{' generalitems '}' ';' { + if (localConf.name == NULL) + parse_error("Your General block must contain a name."); + if (localConf.numeric == 0) + parse_error("Your General block must contain a numeric (between 1 and 4095)."); +}; generalitems: generalitem generalitems | generalitem; -generalitem: generalnumeric | generalname | generalvhost | generaldesc; +generalitem: generalnumeric | generalname | generalvhost | generaldesc + | generaldnsvhost | generaldnsserver; + generalnumeric: NUMERIC '=' NUMBER ';' { if (localConf.numeric == 0) - localConf.numeric = yylval.num; - else - parse_error("Redefinition of server numeric %i (%i)",yylval.num, + localConf.numeric = $3; + else if (localConf.numeric != $3) + parse_error("Redefinition of server numeric %i (%i)", $3, localConf.numeric); }; generalname: NAME '=' QSTRING ';' { if (localConf.name == NULL) - DupString(localConf.name, yylval.text); - else - parse_error("Redefinition of server name %s (%s)",yylval.text, - localConf.name); + localConf.name = $3; + else { + if (strcmp(localConf.name, $3)) + parse_error("Redefinition of server name %s (%s)", $3, + localConf.name); + MyFree($3); + } }; generaldesc: DESCRIPTION '=' QSTRING ';' { MyFree(localConf.description); - DupString(localConf.description, yylval.text); - ircd_strncpy(cli_info(&me), yylval.text, REALLEN); + localConf.description = $3; + ircd_strncpy(cli_info(&me), $3, REALLEN); }; generalvhost: VHOST '=' QSTRING ';' { - if (INADDR_NONE == - (localConf.vhost_address.s_addr = inet_addr(yylval.text))) - localConf.vhost_address.s_addr = INADDR_ANY; + struct irc_in_addr addr; + char *vhost = $3; + + if (!strcmp(vhost, "*")) { + /* This traditionally meant bind to all interfaces and connect + * from the default. */ + } else if (!ircd_aton(&addr, vhost)) + parse_error("Invalid virtual host '%s'.", vhost); + else if (irc_in_addr_is_ipv4(&addr)) + memcpy(&VirtualHost_v4.addr, &addr, sizeof(addr)); + else + memcpy(&VirtualHost_v6.addr, &addr, sizeof(addr)); + MyFree(vhost); }; -adminblock: ADMIN '{' adminitems '}' +generaldnsvhost: DNS VHOST '=' address_family QSTRING ';' +{ + struct irc_in_addr addr; + int families = $4; + char *vhost = $5; + + if (!strcmp(vhost, "*")) { + /* Let the operating system assign the default. */ + } else if (!ircd_aton(&addr, vhost)) + parse_error("Invalid DNS virtual host '%s'.", vhost); + else + { + if ((families & USE_IPV4) + || (!families && irc_in_addr_is_ipv4(&addr))) + memcpy(&VirtualHost_dns_v4.addr, &addr, sizeof(addr)); + if ((families & USE_IPV6) + || (!families && !irc_in_addr_is_ipv4(&addr))) + memcpy(&VirtualHost_dns_v6.addr, &addr, sizeof(addr)); + } + MyFree(vhost); +}; + +generaldnsserver: DNS SERVER '=' QSTRING ';' +{ + char *server = $4; + + add_nameserver(server); + MyFree(server); +}; + +adminblock: ADMIN +{ + MyFree(localConf.location1); + MyFree(localConf.location2); + MyFree(localConf.contact); + localConf.location1 = localConf.location2 = localConf.contact = NULL; +} +'{' adminitems '}' ';' { if (localConf.location1 == NULL) DupString(localConf.location1, ""); @@ -299,538 +392,670 @@ adminblock: ADMIN '{' adminitems '}' DupString(localConf.location2, ""); if (localConf.contact == NULL) DupString(localConf.contact, ""); -} ';'; +}; adminitems: adminitems adminitem | adminitem; adminitem: adminlocation | admincontact; adminlocation: LOCATION '=' QSTRING ';' { - if (localConf.location1 == NULL) - DupString(localConf.location1, yylval.text); - else if (localConf.location2 == NULL) - DupString(localConf.location2, yylval.text); - /* Otherwise just drop it. -A1kmm */ + if (localConf.location1 == NULL) + localConf.location1 = $3; + else if (localConf.location2 == NULL) + localConf.location2 = $3; + else /* Otherwise just drop it. -A1kmm */ + MyFree($3); }; admincontact: CONTACT '=' QSTRING ';' { - if (localConf.contact != NULL) - MyFree(localConf.contact); - DupString(localConf.contact, yylval.text); + MyFree(localConf.contact); + localConf.contact = $3; +}; + +sslblock: SSL +{ + MyFree(localConf.sslcertfile); + MyFree(localConf.sslkeyfile); + MyFree(localConf.sslcafile); + localConf.sslcertfile = localConf.sslkeyfile = localConf.sslcafile = NULL; +} +'{' sslitems '}' ';' +{ + if (localConf.sslcertfile == NULL) + DupString(localConf.sslcertfile, ""); + if (localConf.sslkeyfile == NULL) + DupString(localConf.sslkeyfile, ""); + if (localConf.sslcafile == NULL) + DupString(localConf.sslcafile, ""); +}; +sslitems: sslitems sslitem | sslitem; +sslitem: sslcertfile | sslkeyfile | sslcafile; +sslcertfile: CERTFILE '=' QSTRING ';' +{ + MyFree(localConf.sslcertfile); + localConf.sslcertfile = $3; +}; +sslkeyfile: KEYFILE '=' QSTRING ';' +{ + MyFree(localConf.sslkeyfile); + localConf.sslkeyfile = $3; +}; +sslcafile: CAFILE '=' QSTRING ';' +{ + MyFree(localConf.sslcafile); + localConf.sslcafile = $3; }; classblock: CLASS { - name = NULL; tping = 90; - tconn = 0; - maxlinks = 0; - sendq = 0; -} '{' classitems '}' +} '{' classitems '}' ';' { if (name != NULL) { - add_class(name, tping, tconn, maxlinks, sendq); + struct ConnectionClass *c_class; + add_class(name, tping, tconn, maxlinks, sendq); + c_class = find_class(name); + MyFree(c_class->default_umode); + c_class->default_umode = pass; + memcpy(&c_class->privs, &privs, sizeof(c_class->privs)); + memcpy(&c_class->privs_dirty, &privs_dirty, sizeof(c_class->privs_dirty)); } else { parse_error("Missing name in class block"); } -} ';'; + name = NULL; + pass = NULL; + tconn = 0; + maxlinks = 0; + sendq = 0; + memset(&privs, 0, sizeof(privs)); + memset(&privs_dirty, 0, sizeof(privs_dirty)); +}; classitems: classitem classitems | classitem; classitem: classname | classpingfreq | classconnfreq | classmaxlinks | - classsendq; + classsendq | classusermode | priv; classname: NAME '=' QSTRING ';' { MyFree(name); - DupString(name, yylval.text); + name = $3; }; classpingfreq: PINGFREQ '=' timespec ';' { - tping = yylval.num; + tping = $3; }; classconnfreq: CONNECTFREQ '=' timespec ';' { - tconn = yylval.num; + tconn = $3; }; classmaxlinks: MAXLINKS '=' expr ';' { - maxlinks = yylval.num; + maxlinks = $3; }; classsendq: SENDQ '=' sizespec ';' { - sendq = yylval.num; + sendq = $3; +}; +classusermode: USERMODE '=' QSTRING ';' +{ + MyFree(pass); + pass = $3; }; connectblock: CONNECT { - name = pass = host = NULL; - c_class = NULL; - port = 0; -} '{' connectitems '}' -{ - if (name != NULL && pass != NULL && host != NULL && c_class != NULL && - /*ccount < MAXCONFLINKS &&*/ !strchr(host, '*') && - !strchr(host, '?')) - { - aconf = make_conf(); - aconf->status = CONF_SERVER; + flags = CONF_AUTOCONNECT; +} '{' connectitems '}' ';' +{ + struct ConfItem *aconf = NULL; + if (name == NULL) + parse_error("Missing name in connect block"); + else if (pass == NULL) + parse_error("Missing password in connect block"); + else if (strlen(pass) > PASSWDLEN) + parse_error("Password too long in connect block"); + else if (host == NULL) + parse_error("Missing host in connect block"); + else if (strchr(host, '*') || strchr(host, '?')) + parse_error("Invalid host '%s' in connect block", host); + else if (c_class == NULL) + parse_error("Missing or non-existent class in connect block"); + else { + aconf = make_conf(CONF_SERVER); aconf->name = name; + aconf->origin_name = origin; aconf->passwd = pass; aconf->conn_class = c_class; - aconf->port = port; - aconf->status = CONF_SERVER; + aconf->address.port = port; aconf->host = host; - aconf->next = GlobalConfList; - aconf->ipnum.s_addr = INADDR_NONE; + /* If the user specified a hub allowance, but not maximum links, + * allow an effectively unlimited number of hops. + */ + aconf->maximum = (hub_limit != NULL && maxlinks == 0) ? 65535 : maxlinks; + aconf->hub_limit = hub_limit; + aconf->flags = flags; lookup_confhost(aconf); - GlobalConfList = aconf; } - else - { + if (!aconf) { MyFree(name); MyFree(pass); MyFree(host); - name = pass = host = NULL; - parse_error("Bad connect block"); + MyFree(origin); + MyFree(hub_limit); } -}';'; + name = pass = host = origin = hub_limit = NULL; + c_class = NULL; + port = flags = maxlinks = 0; +}; connectitems: connectitem connectitems | connectitem; connectitem: connectname | connectpass | connectclass | connecthost - | connectport; + | connectport | connectvhost | connectleaf | connecthub + | connecthublimit | connectmaxhops | connectauto; connectname: NAME '=' QSTRING ';' { MyFree(name); - DupString(name, yylval.text); + name = $3; }; connectpass: PASS '=' QSTRING ';' { MyFree(pass); - DupString(pass, yylval.text); + pass = $3; }; connectclass: CLASS '=' QSTRING ';' { - c_class = find_class(yylval.text); + c_class = find_class($3); + if (!c_class) + parse_error("No such connection class '%s' for Connect block", $3); + MyFree($3); }; connecthost: HOST '=' QSTRING ';' { MyFree(host); - DupString(host, yylval.text); + host = $3; }; connectport: PORT '=' NUMBER ';' { - port = yylval.num; + port = $3; }; - -serverblock: SERVER -{ - aconf = (struct ConfItem*) MyMalloc(sizeof(*aconf)); - memset(aconf, 0, sizeof(*aconf)); -} '{' serveritems '}' +connectvhost: VHOST '=' QSTRING ';' { - if (aconf->status == 0) - { - MyFree(aconf->host); - MyFree(aconf->name); - MyFree(aconf); - aconf = NULL; - parse_error("Bad server block"); - } - else - { - aconf->next = GlobalConfList; - GlobalConfList = aconf; - } -} ';'; -serveritems: serveritem serveritems | serveritem; -serveritem: servername | servermask | serverhub | serverleaf | - serveruworld; -servername: NAME '=' QSTRING -{ - MyFree(aconf->name); - DupString(aconf->name, yylval.text); -} ';' ; -servermask: MASK '=' QSTRING -{ - MyFree(aconf->host); - DupString(aconf->host, yylval.text); -} ';' ; -/* XXX - perhaps we should do this the hybrid way in connect blocks - * instead -A1kmm. */ -serverhub: HUB '=' YES ';' -{ - aconf->status |= CONF_HUB; - aconf->status &= ~CONF_LEAF; -} -| HUB '=' NO -{ - aconf->status &= ~CONF_HUB; -} ';'; -serverleaf: LEAF '=' YES ';' + MyFree(origin); + origin = $3; +}; +connectleaf: LEAF ';' { - if (!(aconf->status & CONF_HUB && aconf->status & CONF_UWORLD)) - aconf->status |= CONF_LEAF; - else - parse_error("Server is both leaf and a hub"); -} -| LEAF '=' NO ';' + maxlinks = 0; +}; +connecthub: HUB ';' { - aconf->status &= ~CONF_LEAF; + MyFree(hub_limit); + DupString(hub_limit, "*"); }; -serveruworld: UWORLD '=' YES ';' +connecthublimit: HUB '=' QSTRING ';' { - aconf->status |= CONF_UWORLD; - aconf->status &= ~CONF_LEAF; -} -| UWORLD '=' NO ';' + MyFree(hub_limit); + hub_limit = $3; +}; +connectmaxhops: MAXHOPS '=' expr ';' { - aconf->status &= ~CONF_UWORLD; + maxlinks = $3; }; +connectauto: AUTOCONNECT '=' YES ';' { flags |= CONF_AUTOCONNECT; } + | AUTOCONNECT '=' NO ';' { flags &= ~CONF_AUTOCONNECT; }; -operblock: OPER +uworldblock: UWORLD '{' uworlditems '}' ';'; +uworlditems: uworlditem uworlditems | uworlditem; +uworlditem: uworldname; +uworldname: NAME '=' QSTRING ';' { - aconf = (struct ConfItem*) MyMalloc(sizeof(*aconf)); - memset(aconf, 0, sizeof(*aconf)); - aconf->status = CONF_OPERATOR; -} '{' operitems '}' ';' + make_conf(CONF_UWORLD)->host = $3; +}; + +operblock: OPER '{' operitems '}' ';' { - if (aconf->name != NULL && aconf->passwd != NULL && aconf->host != NULL) - { - aconf->next = GlobalConfList; - GlobalConfList = aconf; - } - else - { - log_write(LS_CONFIG, L_ERROR, 0, "operator blocks need a name, password, and host."); - MyFree(aconf->name); - MyFree(aconf->passwd); - MyFree(aconf->host); - MyFree(aconf); - aconf = NULL; + struct ConfItem *aconf = NULL; + struct SLink *link; + + if (name == NULL) + parse_error("Missing name in operator block"); + else if (pass == NULL) + parse_error("Missing password in operator block"); + /* Do not check password length because it may be crypted. */ + else if (hosts == NULL) + parse_error("Missing host(s) in operator block"); + else if (c_class == NULL) + parse_error("Invalid or missing class in operator block"); + else if (!FlagHas(&privs_dirty, PRIV_PROPAGATE) + && !FlagHas(&c_class->privs_dirty, PRIV_PROPAGATE)) + parse_error("Operator block for %s and class %s have no LOCAL setting", name, c_class->cc_name); + else for (link = hosts; link != NULL; link = link->next) { + aconf = make_conf(CONF_OPERATOR); + DupString(aconf->name, name); + DupString(aconf->passwd, pass); + conf_parse_userhost(aconf, link->value.cp); + aconf->conn_class = c_class; + memcpy(&aconf->privs, &privs, sizeof(aconf->privs)); + memcpy(&aconf->privs_dirty, &privs_dirty, sizeof(aconf->privs_dirty)); } + MyFree(name); + MyFree(pass); + free_slist(&hosts); + name = pass = NULL; + c_class = NULL; + memset(&privs, 0, sizeof(privs)); + memset(&privs_dirty, 0, sizeof(privs_dirty)); }; operitems: operitem | operitems operitem; -operitem: opername | operpass | operlocal | operhost | operclass | operpriv; - +operitem: opername | operpass | operhost | operclass | priv; opername: NAME '=' QSTRING ';' { - MyFree(aconf->name); - DupString(aconf->name, yylval.text); + MyFree(name); + name = $3; }; - operpass: PASS '=' QSTRING ';' { - MyFree(aconf->passwd); - DupString(aconf->passwd, yylval.text); -}; - -operlocal: LOCAL '=' YES ';' -{ - /* XXX it would be good to get rid of local operators and add same - * permission values here. But for now, I am just going with local - * opers... */ - aconf->status = CONF_LOCOP; -} | LOCAL '=' NO ';' -{ - aconf->status = CONF_OPERATOR; + MyFree(pass); + pass = $3; }; - operhost: HOST '=' QSTRING ';' { - MyFree(aconf->host); - if (!strchr(yylval.text, '@')) + struct SLink *link; + link = make_link(); + if (!strchr($3, '@')) { int uh_len; - char *b = (char*) MyMalloc((uh_len = strlen(yylval.text)+3)); - ircd_snprintf(0, b, uh_len, "*@%s", yylval.text); - aconf->host = b; + link->value.cp = (char*) MyMalloc((uh_len = strlen($3)+3)); + ircd_snprintf(0, link->value.cp, uh_len, "*@%s", $3); } else - DupString(aconf->host, yylval.text); + DupString(link->value.cp, $3); + MyFree($3); + link->next = hosts; + hosts = link; }; - operclass: CLASS '=' QSTRING ';' { - aconf->conn_class = find_class(yylval.text); + c_class = find_class($3); + if (!c_class) + parse_error("No such connection class '%s' for Operator block", $3); + MyFree($3); }; -operpriv: privtype '=' yesorno ';' +priv: privtype '=' yesorno ';' { - if ($3 == 1) - { - PrivSet(&aconf->privs_dirty, $1); - PrivSet(&aconf->privs, $1); - } + FlagSet(&privs_dirty, $1); + if (($3 == 1) ^ invert) + FlagSet(&privs, $1); else - { - PrivSet(&aconf->privs_dirty, $1); - PrivClr(&aconf->privs, $1); - } + FlagClr(&privs, $1); + invert = 0; }; privtype: TPRIV_CHAN_LIMIT { $$ = PRIV_CHAN_LIMIT; } | TPRIV_MODE_LCHAN { $$ = PRIV_MODE_LCHAN; } | TPRIV_DEOP_LCHAN { $$ = PRIV_DEOP_LCHAN; } | TPRIV_WALK_LCHAN { $$ = PRIV_WALK_LCHAN; } | - TPRIV_KILL { $$ = PRIV_KILL; } | + KILL { $$ = PRIV_KILL; } | TPRIV_LOCAL_KILL { $$ = PRIV_LOCAL_KILL; } | TPRIV_REHASH { $$ = PRIV_REHASH; } | TPRIV_RESTART { $$ = PRIV_RESTART; } | TPRIV_DIE { $$ = PRIV_DIE; } | TPRIV_GLINE { $$ = PRIV_GLINE; } | TPRIV_LOCAL_GLINE { $$ = PRIV_LOCAL_GLINE; } | - TPRIV_JUPE { $$ = PRIV_JUPE; } | + JUPE { $$ = PRIV_JUPE; } | TPRIV_LOCAL_JUPE { $$ = PRIV_LOCAL_JUPE; } | TPRIV_LOCAL_OPMODE { $$ = PRIV_LOCAL_OPMODE; } | TPRIV_OPMODE { $$ = PRIV_OPMODE; }| TPRIV_SET { $$ = PRIV_SET; } | TPRIV_WHOX { $$ = PRIV_WHOX; } | TPRIV_BADCHAN { $$ = PRIV_BADCHAN; } | - TPRIV_LOCAL_BADCHAN { $$ = TPRIV_LOCAL_BADCHAN; } | + TPRIV_LOCAL_BADCHAN { $$ = PRIV_LOCAL_BADCHAN; } | TPRIV_SEE_CHAN { $$ = PRIV_SEE_CHAN; } | TPRIV_SHOW_INVIS { $$ = PRIV_SHOW_INVIS; } | TPRIV_SHOW_ALL_INVIS { $$ = PRIV_SHOW_ALL_INVIS; } | TPRIV_PROPAGATE { $$ = PRIV_PROPAGATE; } | TPRIV_UNLIMIT_QUERY { $$ = PRIV_UNLIMIT_QUERY; } | + TPRIV_UNLIMIT_FLOOD { $$ = PRIV_UNLIMIT_FLOOD; } | TPRIV_DISPLAY { $$ = PRIV_DISPLAY; } | TPRIV_SEE_OPERS { $$ = PRIV_SEE_OPERS; } | - TPRIV_WIDE_GLINE { $$ = PRIV_WIDE_GLINE; }; + TPRIV_WIDE_GLINE { $$ = PRIV_WIDE_GLINE; } | + TPRIV_LIST_CHAN { $$ = PRIV_LIST_CHAN; } | + LOCAL { $$ = PRIV_PROPAGATE; invert = 1; } | + TPRIV_FORCE_OPMODE { $$ = PRIV_FORCE_OPMODE; } | + TPRIV_FORCE_LOCAL_OPMODE { $$ = PRIV_FORCE_LOCAL_OPMODE; } | + TPRIV_APASS_OPMODE { $$ = PRIV_APASS_OPMODE; } ; yesorno: YES { $$ = 1; } | NO { $$ = 0; }; +/* not a recursive definition because some pedant will just come along + * and whine that the parser accepts "ipv4 ipv4 ipv4 ipv4" + */ +address_family: + { $$ = 0; } + | TOK_IPV4 { $$ = USE_IPV4; } + | TOK_IPV6 { $$ = USE_IPV6; } + | TOK_IPV4 TOK_IPV6 { $$ = USE_IPV4 | USE_IPV6; } + | TOK_IPV6 TOK_IPV4 { $$ = USE_IPV6 | USE_IPV4; } + ; + /* The port block... */ -portblock: PORT { - port = 0; - host = NULL; - /* Hijack these for is_server, is_hidden to cut down on globals... */ - tconn = 0; - tping = 0; - /* and this for mask... */ - pass = NULL; -} '{' portitems '}' ';' -{ - if (port > 0 && port <= 0xFFFF) - { - add_listener(port, host, pass, tconn, tping); - host = pass = NULL; +portblock: PORT '{' portitems '}' ';' { + struct ListenerFlags flags_here; + struct SLink *link; + if (hosts == NULL) { + struct SLink *link; + link = make_link(); + DupString(link->value.cp, "*"); + link->flags = 0; + link->next = hosts; + hosts = link; } - else - { - MyFree(host); - MyFree(pass); - parse_error("Bad port block"); + for (link = hosts; link != NULL; link = link->next) { + memcpy(&flags_here, &listen_flags, sizeof(flags_here)); + switch (link->flags & (USE_IPV4 | USE_IPV6)) { + case USE_IPV4: + FlagSet(&flags_here, LISTEN_IPV4); + break; + case USE_IPV6: + FlagSet(&flags_here, LISTEN_IPV6); + break; + default: /* 0 or USE_IPV4|USE_IPV6 */ + FlagSet(&flags_here, LISTEN_IPV4); + FlagSet(&flags_here, LISTEN_IPV6); + break; + } + + if (link->flags & 65535) + port = link->flags & 65535; + add_listener(port, link->value.cp, pass, &flags_here); } + free_slist(&hosts); + MyFree(pass); + memset(&listen_flags, 0, sizeof(listen_flags)); + pass = NULL; + port = 0; }; portitems: portitem portitems | portitem; -portitem: portnumber | portvhost | portmask | portserver | porthidden; -portnumber: PORT '=' NUMBER ';' -{ - port = yylval.num; +portitem: portnumber | portvhost | portvhostnumber | portmask | portserver | portssl | porthidden; +portnumber: PORT '=' address_family NUMBER ';' +{ + if ($4 < 1 || $4 > 65535) { + parse_error("Port %d is out of range", port); + } else { + port = $3 | $4; + if (hosts && (0 == (hosts->flags & 65535))) + hosts->flags = (hosts->flags & ~65535) | port; + } }; -portvhost: VHOST '=' QSTRING ';' -{ - MyFree(host); - DupString(host, yylval.text); +portvhost: VHOST '=' address_family QSTRING ';' +{ + struct SLink *link; + link = make_link(); + link->value.cp = $4; + link->flags = $3 | port; + link->next = hosts; + hosts = link; +}; + +portvhostnumber: VHOST '=' address_family QSTRING NUMBER ';' +{ + if ($5 < 1 || $5 > 65535) { + parse_error("Port %d is out of range", port); + } else { + struct SLink *link; + link = make_link(); + link->value.cp = $4; + link->flags = $3 | $5; + link->next = hosts; + hosts = link; + } }; portmask: MASK '=' QSTRING ';' { MyFree(pass); - DupString(pass, yylval.text); + pass = $3; }; portserver: SERVER '=' YES ';' { - tconn = -1; + FlagSet(&listen_flags, LISTEN_SERVER); } | SERVER '=' NO ';' { - tconn = 0; + FlagClr(&listen_flags, LISTEN_SERVER); +}; + +portssl: SSL '=' YES ';' +{ + FlagSet(&listen_flags, LISTEN_SSL); +} | SSL '=' NO ';' +{ + FlagClr(&listen_flags, LISTEN_SSL); }; porthidden: HIDDEN '=' YES ';' { - tping = -1; + FlagSet(&listen_flags, LISTEN_HIDDEN); } | HIDDEN '=' NO ';' { - tping = 0; + FlagClr(&listen_flags, LISTEN_HIDDEN); }; clientblock: CLIENT { - aconf = (struct ConfItem*) MyMalloc(sizeof(*aconf)); - memset(aconf, 0, sizeof(*aconf)); - aconf->status = CONF_CLIENT; -} '{' clientitems '}' + maxlinks = 65535; + port = 0; +} +'{' clientitems '}' ';' { - if ((aconf->host != NULL || aconf->name!=NULL)) - { - if (aconf->host == NULL) - DupString(aconf->host, ""); - if (aconf->name == NULL) - DupString(aconf->name, ""); - if (aconf->conn_class == NULL) - aconf->conn_class = find_class("default"); - aconf->next = GlobalConfList; - GlobalConfList = aconf; - aconf = NULL; + struct ConfItem *aconf = 0; + struct irc_in_addr addr; + unsigned char addrbits = 0; + + if (!c_class) + parse_error("Invalid or missing class in Client block"); + else if (pass && strlen(pass) > PASSWDLEN) + parse_error("Password too long in connect block"); + else if (ip && !ipmask_parse(ip, &addr, &addrbits)) + parse_error("Invalid IP address %s in Client block", ip); + else { + aconf = make_conf(CONF_CLIENT); + aconf->username = username; + aconf->host = host; + if (ip) + memcpy(&aconf->address.addr, &addr, sizeof(aconf->address.addr)); + else + memset(&aconf->address.addr, 0, sizeof(aconf->address.addr)); + aconf->address.port = port; + aconf->addrbits = addrbits; + aconf->name = ip; + aconf->conn_class = c_class; + aconf->maximum = maxlinks; + aconf->passwd = pass; } - else - { - MyFree(aconf->host); - MyFree(aconf->passwd); - MyFree(aconf); - aconf = NULL; - parse_error("Bad client block"); + if (!aconf) { + MyFree(username); + MyFree(host); + MyFree(ip); + MyFree(pass); } -} ';'; + host = NULL; + username = NULL; + c_class = NULL; + maxlinks = 0; + ip = NULL; + pass = NULL; + port = 0; +}; clientitems: clientitem clientitems | clientitem; -clientitem: clienthost | clientclass | clientpass | clientip; +clientitem: clienthost | clientip | clientusername | clientclass | clientpass | clientmaxlinks | clientport; +clienthost: HOST '=' QSTRING ';' +{ + char *sep = strchr($3, '@'); + MyFree(host); + if (sep) { + *sep++ = '\0'; + MyFree(username); + DupString(host, sep); + username = $3; + } else { + host = $3; + } +}; clientip: IP '=' QSTRING ';' { - MyFree(aconf->host); - DupString(aconf->host, yylval.text); + char *sep; + sep = strchr($3, '@'); + MyFree(ip); + if (sep) { + *sep++ = '\0'; + MyFree(username); + DupString(ip, sep); + username = $3; + } else { + ip = $3; + } }; - -clienthost: HOST '=' QSTRING ';' +clientusername: USERNAME '=' QSTRING ';' { - MyFree(aconf->name); - DupString(aconf->name, yylval.text); + MyFree(username); + username = $3; }; - clientclass: CLASS '=' QSTRING ';' { - aconf->conn_class = find_class(yylval.text); + c_class = find_class($3); + if (!c_class) + parse_error("No such connection class '%s' for Client block", $3); + MyFree($3); }; - clientpass: PASS '=' QSTRING ';' { - MyFree(aconf->passwd); - DupString(aconf->passwd, yylval.text); + MyFree(pass); + pass = $3; +}; +clientmaxlinks: MAXLINKS '=' expr ';' +{ + maxlinks = $3; +}; +clientport: PORT '=' expr ';' +{ + port = $3; }; killblock: KILL { - dconf = (struct DenyConf*) MyMalloc(sizeof(*dconf)); - memset(dconf, 0, sizeof(*dconf)); -} '{' killitems '}' + dconf = (struct DenyConf*) MyCalloc(1, sizeof(*dconf)); +} '{' killitems '}' ';' { - if (dconf->hostmask != NULL) - { - if (dconf->usermask == NULL) - DupString(dconf->usermask, "*"); + if (dconf->usermask || dconf->hostmask ||dconf->realmask) { dconf->next = denyConfList; denyConfList = dconf; - dconf = NULL; } else { + MyFree(dconf->usermask); MyFree(dconf->hostmask); + MyFree(dconf->realmask); MyFree(dconf->message); MyFree(dconf); - dconf = NULL; - parse_error("Bad kill block"); + parse_error("Kill block must match on at least one of username, host or realname"); } -} ';'; + dconf = NULL; +}; killitems: killitem killitems | killitem; -killitem: killuhost | killreal | killreasonfile | killreason; +killitem: killuhost | killreal | killusername | killreasonfile | killreason; killuhost: HOST '=' QSTRING ';' { - char *u, *h; - dconf->flags &= ~DENY_FLAGS_REALNAME; + char *h; MyFree(dconf->hostmask); MyFree(dconf->usermask); - if ((h = strchr(yylval.text, '@')) == NULL) + if ((h = strchr($3, '@')) == NULL) { - u = "*"; - h = yylval.text; + DupString(dconf->usermask, "*"); + dconf->hostmask = $3; } else { - u = yylval.text; - h++; - } - DupString(dconf->hostmask, h); - DupString(dconf->usermask, u); - if (strchr(yylval.text, '.')) - { - int c_class; - char ipname[16]; - int ad[4] = { 0 }; - int bits2 = 0; - dconf->flags |= DENY_FLAGS_IP; - c_class = sscanf(dconf->hostmask, "%d.%d.%d.%d/%d", - &ad[0], &ad[1], &ad[2], &ad[3], &bits2); - if (c_class != 5) { - dconf->bits = c_class * 8; - } - else { - dconf->bits = bits2; - } - ircd_snprintf(0, ipname, sizeof(ipname), "%d.%d.%d.%d", ad[0], ad[1], - ad[2], ad[3]); - dconf->address = inet_addr(ipname); + *h++ = '\0'; + DupString(dconf->hostmask, h); + dconf->usermask = $3; } + ipmask_parse(dconf->hostmask, &dconf->address, &dconf->bits); +}; + +killusername: USERNAME '=' QSTRING ';' +{ + MyFree(dconf->usermask); + dconf->usermask = $3; }; killreal: REAL '=' QSTRING ';' { - dconf->flags &= ~DENY_FLAGS_IP; - dconf->flags |= DENY_FLAGS_REALNAME; - MyFree(dconf->hostmask); - /* Leave usermask so you can specify user and real... */ - DupString(dconf->hostmask, yylval.text); + MyFree(dconf->realmask); + dconf->realmask = $3; }; killreason: REASON '=' QSTRING ';' { - dconf->flags &= DENY_FLAGS_FILE; + dconf->flags &= ~DENY_FLAGS_FILE; MyFree(dconf->message); - DupString(dconf->message, yylval.text); + dconf->message = $3; }; killreasonfile: TFILE '=' QSTRING ';' { dconf->flags |= DENY_FLAGS_FILE; MyFree(dconf->message); - DupString(dconf->message, yylval.text); + dconf->message = $3; }; cruleblock: CRULE { - host = pass = NULL; tconn = CRULE_AUTO; -} '{' cruleitems '}' +} '{' cruleitems '}' ';' { - struct CRuleNode *node; - if (host != NULL && pass != NULL && (node=crule_parse(pass)) != NULL) + struct CRuleNode *node = NULL; + struct SLink *link; + + if (hosts == NULL) + parse_error("Missing server(s) 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 for (link = hosts; link != NULL; link = link->next) { struct CRuleConf *p = (struct CRuleConf*) MyMalloc(sizeof(*p)); - p->hostmask = host; - p->rule = pass; + if (node == NULL) + node = crule_parse(pass); + DupString(p->hostmask, link->value.cp); + DupString(p->rule, pass); p->type = tconn; p->node = node; + node = NULL; p->next = cruleConfList; cruleConfList = p; } - else - { - MyFree(host); - MyFree(pass); - parse_error("Bad CRule block"); - } -} ';'; + free_slist(&hosts); + MyFree(pass); + pass = NULL; + tconn = 0; +}; cruleitems: cruleitem cruleitems | cruleitem; cruleitem: cruleserver | crulerule | cruleall; cruleserver: SERVER '=' QSTRING ';' { - MyFree(host); - collapse(yylval.text); - DupString(host, yylval.text); + struct SLink *link; + link = make_link(); + link->value.cp = $3; + link->next = hosts; + hosts = link; }; crulerule: RULE '=' QSTRING ';' { MyFree(pass); - DupString(pass, yylval.text); + pass = $3; }; cruleall: ALL '=' YES ';' @@ -841,27 +1066,32 @@ cruleall: ALL '=' YES ';' tconn = CRULE_AUTO; }; -motdblock: MOTD { - pass = host = NULL; -} '{' motditems '}' +motdblock: MOTD '{' motditems '}' ';' { - if (host != NULL && pass != NULL) - motd_add(host, pass); - MyFree(host); + struct SLink *link; + if (pass != NULL) + for (link = hosts; link != NULL; link = link->next) + motd_add(link->value.cp, pass); + free_slist(&hosts); MyFree(pass); - host = pass = NULL; -} ';'; + pass = NULL; +}; motditems: motditem motditems | motditem; motditem: motdhost | motdfile; motdhost: HOST '=' QSTRING ';' { - DupString(host, yylval.text); + struct SLink *link; + link = make_link(); + link->value.cp = $3; + link->next = hosts; + hosts = link; }; motdfile: TFILE '=' QSTRING ';' { - DupString(pass, yylval.text); + MyFree(pass); + pass = $3; }; featuresblock: FEATURES '{' featureitems '}' ';'; @@ -871,53 +1101,110 @@ featureitem: QSTRING { stringlist[0] = $1; stringno = 1; -} '=' stringlist ';'; - -stringlist: QSTRING -{ - stringlist[1] = $1; - stringno = 2; -} posextrastrings -{ +} '=' stringlist ';' { + unsigned int ii; feature_set(NULL, (const char * const *)stringlist, stringno); + for (ii = 0; ii < stringno; ++ii) + MyFree(stringlist[ii]); }; -posextrastrings: /* empty */ | extrastrings; -extrastrings: extrastrings extrastring | extrastring; + +stringlist: stringlist extrastring | extrastring; extrastring: QSTRING { if (stringno < MAX_STRINGS) stringlist[stringno++] = $1; + else + MyFree($1); }; -quarantineblock: QUARANTINE '{' +quarantineblock: QUARANTINE '{' quarantineitems '}' ';'; +quarantineitems: quarantineitems quarantineitem | quarantineitem; +quarantineitem: QSTRING '=' QSTRING ';' { - if (qconf != NULL) - qconf = (struct qline*) MyMalloc(sizeof(*qconf)); + struct qline *qconf = MyCalloc(1, sizeof(*qconf)); + qconf->chname = $1; + qconf->reason = $3; + qconf->next = GlobalQuarantineList; + GlobalQuarantineList = qconf; +}; + +pseudoblock: PSEUDO QSTRING '{' +{ + smap = MyCalloc(1, sizeof(struct s_map)); + smap->command = $2; +} +pseudoitems '}' ';' +{ + int valid = 0; + + if (!smap->name) + parse_error("Missing name in pseudo %s block", smap->command); + else if (!smap->services) + parse_error("Missing nick in pseudo %s block", smap->command); + else if (!strIsAlpha(smap->command)) + parse_error("Pseudo command %s invalid: must all be letters", smap->command); else + valid = 1; + if (valid && register_mapping(smap)) { - if (qconf->chname != NULL) - MyFree(qconf->chname); - if (qconf->reason != NULL) - MyFree(qconf->reason); + smap->next = GlobalServiceMapList; + GlobalServiceMapList = smap; } - memset(qconf, 0, sizeof(*qconf)); -} quarantineitems '}' ';' -{ - if (qconf->chname == NULL || qconf->reason == NULL) + else { - log_write(LS_CONFIG, L_ERROR, 0, "quarantine blocks need a channel name " - "and a reason."); - return 0; + free_mapping(smap); } - qconf->next = GlobalQuarantineList; - GlobalQuarantineList = qconf; - qconf = NULL; + smap = NULL; }; -quarantineitems: CHANNEL NAME '=' QSTRING ';' +pseudoitems: pseudoitem pseudoitems | pseudoitem; +pseudoitem: pseudoname | pseudoprepend | pseudonick | pseudoflags; +pseudoname: NAME '=' QSTRING ';' { - DupString(qconf->chname, yylval.text); -} | REASON '=' QSTRING ';' + MyFree(smap->name); + smap->name = $3; +}; +pseudoprepend: PREPEND '=' QSTRING ';' { - DupString(qconf->reason, yylval.text); + MyFree(smap->prepend); + smap->prepend = $3; }; +pseudonick: NICK '=' QSTRING ';' +{ + char *sep = strchr($3, '@'); + + if (sep != NULL) { + size_t slen = strlen($3); + struct nick_host *nh = MyMalloc(sizeof(*nh) + slen); + memcpy(nh->nick, $3, slen + 1); + nh->nicklen = sep - $3; + nh->next = smap->services; + smap->services = nh; + } + MyFree($3); +}; +pseudoflags: FAST ';' +{ + smap->flags |= SMAP_FAST; +}; + +iauthblock: IAUTH '{' iauthitems '}' ';' +{ + auth_spawn(stringno, stringlist); + while (stringno > 0) + { + --stringno; + MyFree(stringlist[stringno]); + } +}; + +iauthitems: iauthitem iauthitems | iauthitem; +iauthitem: iauthprogram; +iauthprogram: PROGRAM '=' +{ + while (stringno > 0) + { + --stringno; + MyFree(stringlist[stringno]); + } +} stringlist ';';