rmilter/cfg_file.y
Vsevolod Stakhov 6524af4b6a Various fixes to whitelists.
Now whitelists are hash tables based.
Update documentation.
Ip field now may handle IPv6 addresses.
Update to 1.5.41.
2013-05-29 14:25:16 +01:00

1635 lines
30 KiB
Text

/*
* Copyright (c) 2007-2012, Vsevolod Stakhov
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. Redistributions in binary form
* must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with
* the distribution. Neither the name of the author nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
%{
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <syslog.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/mman.h>
#include <fcntl.h>
#include "pcre.h"
#include "cfg_file.h"
#define YYDEBUG 0
extern struct config_file *cfg;
extern int yylineno;
extern char *yytext;
struct condl *cur_conditions;
struct dkim_domain_entry *cur_domain;
uint8_t cur_flags = 0;
%}
%union
{
char *string;
struct condition *cond;
struct action *action;
size_t limit;
bucket_t bucket;
char flag;
unsigned int seconds;
unsigned int number;
double frac;
}
%token ERROR STRING QUOTEDSTRING FLAG FLOAT
%token ACCEPT REJECTL TEMPFAIL DISCARD QUARANTINE
%token CONNECT HELO ENVFROM ENVRCPT HEADER MACRO BODY
%token AND OR NOT
%token TEMPDIR LOGFILE PIDFILE RULE CLAMAV SERVERS ERROR_TIME DEAD_TIME MAXERRORS CONNECT_TIMEOUT PORT_TIMEOUT RESULTS_TIMEOUT SPF DCC
%token FILENAME REGEXP QUOTE SEMICOLON OBRACE EBRACE COMMA EQSIGN
%token BINDSOCK SOCKCRED DOMAIN IPADDR IPNETWORK HOSTPORT NUMBER GREYLISTING WHITELIST TIMEOUT EXPIRE EXPIRE_WHITE
%token MAXSIZE SIZELIMIT SECONDS BUCKET USEDCC MEMCACHED PROTOCOL AWL_ENABLE AWL_POOL AWL_TTL AWL_HITS SERVERS_WHITE SERVERS_LIMITS SERVERS_GREY
%token LIMITS LIMIT_TO LIMIT_TO_IP LIMIT_TO_IP_FROM LIMIT_WHITELIST LIMIT_WHITELIST_RCPT LIMIT_BOUNCE_ADDRS LIMIT_BOUNCE_TO LIMIT_BOUNCE_TO_IP
%token SPAMD REJECT_MESSAGE SERVERS_ID ID_PREFIX GREY_PREFIX WHITE_PREFIX RSPAMD_METRIC ALSO_CHECK DIFF_DIR CHECK_SYMBOLS SYMBOLS_DIR
%token BEANSTALK ID_REGEXP LIFETIME COPY_SERVER GREYLISTED_MESSAGE SPAMD_SOFT_FAIL
%token SEND_BEANSTALK_COPY SEND_BEANSTALK_HEADERS SEND_BEANSTALK_SPAM SPAM_SERVER STRICT_AUTH
%token TRACE_SYMBOL TRACE_ADDR WHITELIST_FROM SPAM_HEADER SPAMD_GREYLIST EXTENDED_SPAM_HEADERS
%token DKIM_SECTION DKIM_KEY DKIM_DOMAIN DKIM_SELECTOR DKIM_HEADER_CANON DKIM_BODY_CANON
%token DKIM_SIGN_ALG DKIM_RELAXED DKIM_SIMPLE DKIM_SHA1 DKIM_SHA256 COPY_PROBABILITY
%type <string> STRING
%type <string> QUOTEDSTRING
%type <string> FILENAME
%type <string> REGEXP
%type <string> SOCKCRED
%type <string> IPADDR IPNETWORK
%type <string> HOSTPORT
%type <string> ip_net memcached_hosts beanstalk_hosts clamav_addr spamd_addr
%type <cond> expr_l expr term
%type <action> action
%type <string> DOMAIN
%type <limit> SIZELIMIT
%type <flag> FLAG
%type <bucket> BUCKET;
%type <seconds> SECONDS;
%type <number> NUMBER;
%type <frac> FLOAT;
%%
file : /* empty */
| file command SEMICOLON { }
;
command :
tempdir
| strictauth
| pidfile
| rule
| clamav
| spamd
| spf
| bindsock
| maxsize
| usedcc
| memcached
| beanstalk
| limits
| greylisting
| whitelist
| dkim
;
tempdir :
TEMPDIR EQSIGN FILENAME {
struct stat st;
if (stat ($3, &st) == -1) {
yyerror ("yyparse: cannot stat directory \"%s\": %s", $3, strerror (errno));
YYERROR;
}
if (!S_ISDIR (st.st_mode)) {
yyerror ("yyparse: \"%s\" is not a directory", $3);
YYERROR;
}
cfg->temp_dir = $3;
}
;
pidfile :
PIDFILE EQSIGN FILENAME {
cfg->pid_file = $3;
}
;
strictauth:
STRICT_AUTH EQSIGN FLAG {
cfg->strict_auth = $3;
}
;
rule :
RULE OBRACE rulebody EBRACE
;
rulebody :
action SEMICOLON expr_l {
struct rule *cur_rule;
cur_rule = (struct rule *) malloc (sizeof (struct rule));
if (cur_rule == NULL) {
yyerror ("yyparse: malloc: %s", strerror (errno));
YYERROR;
}
cur_rule->act = $1;
cur_rule->conditions = cur_conditions;
cur_rule->flags = cur_flags;
cur_flags = 0;
LIST_INSERT_HEAD (&cfg->rules, cur_rule, next);
}
;
action :
REJECTL QUOTEDSTRING {
$$ = create_action(ACTION_REJECT, $2);
if ($$ == NULL) {
yyerror ("yyparse: create_action");
YYERROR;
}
free($2);
}
| TEMPFAIL QUOTEDSTRING {
$$ = create_action(ACTION_TEMPFAIL, $2);
if ($$ == NULL) {
yyerror ("yyparse: create_action");
YYERROR;
}
free($2);
}
| QUARANTINE QUOTEDSTRING {
$$ = create_action(ACTION_QUARANTINE, $2);
if ($$ == NULL) {
yyerror ("yyparse: create_action");
YYERROR;
}
free($2);
}
| DISCARD {
$$ = create_action(ACTION_DISCARD, "");
if ($$ == NULL) {
yyerror ("yyparse: create_action");
YYERROR;
}
}
| ACCEPT {
$$ = create_action(ACTION_ACCEPT, "");
if ($$ == NULL) {
yyerror ("yyparse: create_action");
YYERROR;
}
}
;
expr_l :
expr SEMICOLON {
cur_conditions = (struct condl *)malloc (sizeof (struct condl));
if (cur_conditions == NULL) {
yyerror ("yyparse: malloc: %s", strerror (errno));
YYERROR;
}
LIST_INIT (cur_conditions);
$$ = $1;
if ($$ == NULL) {
yyerror ("yyparse: malloc: %s", strerror(errno));
YYERROR;
}
LIST_INSERT_HEAD (cur_conditions, $$, next);
}
| expr_l expr SEMICOLON {
$$ = $2;
if ($$ == NULL) {
yyerror ("yyparse: malloc: %s", strerror(errno));
YYERROR;
}
LIST_INSERT_HEAD (cur_conditions, $$, next);
}
;
expr :
term {
$$ = $1;
}
| NOT term {
struct condition *tmp;
tmp = $2;
if (tmp != NULL) {
tmp->args[0].not = 1;
tmp->args[1].not = 1;
}
$$ = tmp;
}
;
term :
CONNECT REGEXP REGEXP {
$$ = create_cond(COND_CONNECT, $2, $3);
if ($$ == NULL) {
yyerror ("yyparse: malloc: %s", strerror(errno));
YYERROR;
}
cur_flags |= COND_CONNECT_FLAG;
free($2);
free($3);
}
| HELO REGEXP {
$$ = create_cond(COND_HELO, $2, NULL);
if ($$ == NULL) {
yyerror ("yyparse: malloc: %s", strerror(errno));
YYERROR;
}
cur_flags |= COND_HELO_FLAG;
free($2);
}
| ENVFROM REGEXP {
$$ = create_cond(COND_ENVFROM, $2, NULL);
if ($$ == NULL) {
yyerror ("yyparse: malloc: %s", strerror(errno));
YYERROR;
}
cur_flags |= COND_ENVFROM_FLAG;
free($2);
}
| ENVRCPT REGEXP {
$$ = create_cond(COND_ENVRCPT, $2, NULL);
if ($$ == NULL) {
yyerror ("yyparse: malloc: %s", strerror(errno));
YYERROR;
}
cur_flags |= COND_ENVRCPT_FLAG;
free($2);
}
| HEADER REGEXP REGEXP {
$$ = create_cond(COND_HEADER, $2, $3);
if ($$ == NULL) {
yyerror ("yyparse: malloc: %s", strerror(errno));
YYERROR;
}
cur_flags |= COND_HEADER_FLAG;
free($2);
free($3);
}
| BODY REGEXP {
$$ = create_cond(COND_BODY, $2, NULL);
if ($$ == NULL) {
yyerror ("yyparse: malloc: %s", strerror(errno));
YYERROR;
}
cur_flags |= COND_BODY_FLAG;
free($2);
}
;
clamav:
CLAMAV OBRACE clamavbody EBRACE
;
clamavbody:
clamavcmd SEMICOLON
| clamavbody clamavcmd SEMICOLON
;
clamavcmd:
clamav_servers
| clamav_connect_timeout
| clamav_port_timeout
| clamav_results_timeout
| clamav_error_time
| clamav_dead_time
| clamav_maxerrors
;
clamav_servers:
SERVERS EQSIGN clamav_server
;
clamav_server:
clamav_params
| clamav_server COMMA clamav_params
;
clamav_params:
clamav_addr {
if (!add_clamav_server (cfg, $1)) {
yyerror ("yyparse: add_clamav_server");
YYERROR;
}
free ($1);
}
;
clamav_addr:
STRING {
$$ = $1;
}
| IPADDR{
$$ = $1;
}
| DOMAIN {
$$ = $1;
}
| HOSTPORT {
$$ = $1;
}
| FILENAME {
$$ = $1;
}
;
clamav_error_time:
ERROR_TIME EQSIGN NUMBER {
cfg->clamav_error_time = $3;
}
;
clamav_dead_time:
DEAD_TIME EQSIGN NUMBER {
cfg->clamav_dead_time = $3;
}
;
clamav_maxerrors:
MAXERRORS EQSIGN NUMBER {
cfg->clamav_maxerrors = $3;
}
;
clamav_connect_timeout:
CONNECT_TIMEOUT EQSIGN SECONDS {
cfg->clamav_connect_timeout = $3;
}
;
clamav_port_timeout:
PORT_TIMEOUT EQSIGN SECONDS {
cfg->clamav_port_timeout = $3;
}
;
clamav_results_timeout:
RESULTS_TIMEOUT EQSIGN SECONDS {
cfg->clamav_results_timeout = $3;
}
;
spamd:
SPAMD OBRACE spamdbody EBRACE
;
spamdbody:
spamdcmd SEMICOLON
| spamdbody spamdcmd SEMICOLON
;
spamdcmd:
spamd_servers
| spamd_connect_timeout
| spamd_results_timeout
| spamd_error_time
| spamd_dead_time
| spamd_maxerrors
| spamd_reject_message
| spamd_whitelist
| extra_spamd_servers
| spamd_rspamd_metric
| diff_dir
| symbols_dir
| check_symbols
| spamd_soft_fail
| trace_symbol
| trace_addr
| spamd_spam_header
| spamd_greylist
| extended_spam_headers
;
diff_dir :
DIFF_DIR EQSIGN FILENAME {
struct stat st;
if (stat ($3, &st) == -1) {
yyerror ("yyparse: cannot stat directory \"%s\": %s", $3, strerror (errno));
YYERROR;
}
if (!S_ISDIR (st.st_mode)) {
yyerror ("yyparse: \"%s\" is not a directory", $3);
YYERROR;
}
cfg->diff_dir = $3;
}
;
symbols_dir:
SYMBOLS_DIR EQSIGN FILENAME {
struct stat st;
if (stat ($3, &st) == -1) {
yyerror ("yyparse: cannot stat directory \"%s\": %s", $3, strerror (errno));
YYERROR;
}
if (!S_ISDIR (st.st_mode)) {
yyerror ("yyparse: \"%s\" is not a directory", $3);
YYERROR;
}
cfg->symbols_dir = $3;
}
;
check_symbols:
CHECK_SYMBOLS EQSIGN QUOTEDSTRING {
size_t len = strlen ($3);
char *c = $3;
/* Trim quotes */
if (*c == '"') {
c++;
len--;
}
if (c[len - 1] == '"') {
len--;
}
if (cfg->check_symbols) {
free (cfg->check_symbols);
}
cfg->check_symbols = (char *)malloc (len + 1);
if (!cfg->check_symbols) {
yyerror ("yyparse: malloc failed");
YYERROR;
}
strlcpy (cfg->check_symbols, c, len + 1);
free ($3);
}
;
spamd_servers:
SERVERS EQSIGN spamd_server
;
spamd_server:
spamd_params
| spamd_server COMMA spamd_params
;
spamd_params:
spamd_addr {
if (!add_spamd_server (cfg, $1, 0)) {
yyerror ("yyparse: add_spamd_server");
YYERROR;
}
free ($1);
}
;
extra_spamd_servers:
ALSO_CHECK EQSIGN extra_spamd_server
;
extra_spamd_server:
extra_spamd_params
| extra_spamd_server COMMA extra_spamd_params
;
extra_spamd_params:
spamd_addr {
if (!add_spamd_server (cfg, $1, 1)) {
yyerror ("yyparse: add_spamd_server");
YYERROR;
}
free ($1);
}
;
spamd_addr:
STRING {
$$ = $1;
}
| IPADDR{
$$ = $1;
}
| DOMAIN {
$$ = $1;
}
| HOSTPORT {
$$ = $1;
}
| FILENAME {
$$ = $1;
}
;
spamd_error_time:
ERROR_TIME EQSIGN NUMBER {
cfg->spamd_error_time = $3;
}
;
spamd_dead_time:
DEAD_TIME EQSIGN NUMBER {
cfg->spamd_dead_time = $3;
}
;
spamd_maxerrors:
MAXERRORS EQSIGN NUMBER {
cfg->spamd_maxerrors = $3;
}
;
spamd_connect_timeout:
CONNECT_TIMEOUT EQSIGN SECONDS {
cfg->spamd_connect_timeout = $3;
}
;
spamd_results_timeout:
RESULTS_TIMEOUT EQSIGN SECONDS {
cfg->spamd_results_timeout = $3;
}
;
spamd_reject_message:
REJECT_MESSAGE EQSIGN QUOTEDSTRING {
size_t len = strlen ($3);
char *c = $3;
/* Trim quotes */
if (*c == '"') {
c++;
len--;
}
if (c[len - 1] == '"') {
len--;
}
if (cfg->spamd_reject_message) {
free (cfg->spamd_reject_message);
}
cfg->spamd_reject_message = (char *)malloc (len + 1);
if (!cfg->spamd_reject_message) {
yyerror ("yyparse: malloc failed");
YYERROR;
}
strlcpy (cfg->spamd_reject_message, c, len + 1);
free ($3);
}
;
spamd_whitelist:
WHITELIST EQSIGN spamd_ip_list
;
spamd_ip_list:
spamd_ip
| spamd_ip_list COMMA spamd_ip
;
spamd_ip:
ip_net {
if (add_ip_radix (cfg->spamd_whitelist, $1) == 0) {
YYERROR;
}
}
;
spamd_rspamd_metric:
RSPAMD_METRIC EQSIGN QUOTEDSTRING {
size_t len = strlen ($3);
char *c = $3;
/* Trim quotes */
if (*c == '"') {
c++;
len--;
}
if (c[len - 1] == '"') {
len--;
}
if (cfg->rspamd_metric) {
free (cfg->rspamd_metric);
}
cfg->rspamd_metric = (char *)malloc (len + 1);
if (!cfg->rspamd_metric) {
yyerror ("yyparse: malloc failed");
YYERROR;
}
strlcpy (cfg->rspamd_metric, c, len + 1);
free ($3);
}
;
spamd_soft_fail:
SPAMD_SOFT_FAIL EQSIGN FLAG {
if ($3) {
cfg->spamd_soft_fail = 1;
}
}
;
extended_spam_headers:
EXTENDED_SPAM_HEADERS EQSIGN FLAG {
if ($3) {
cfg->extended_spam_headers = 1;
}
}
;
spamd_greylist:
SPAMD_GREYLIST EQSIGN FLAG {
if ($3) {
cfg->spamd_greylist = 1;
}
}
;
spamd_spam_header:
SPAM_HEADER EQSIGN QUOTEDSTRING {
if ($3) {
size_t len = strlen ($3);
char *c = $3;
/* Trim quotes */
if (*c == '"') {
c++;
len--;
}
if (c[len - 1] == '"') {
len--;
}
if (cfg->spam_header) {
free (cfg->spam_header);
}
cfg->spam_header = (char *)malloc (len + 1);
if (!cfg->spam_header) {
yyerror ("yyparse: malloc failed");
YYERROR;
}
strlcpy (cfg->spam_header, c, len + 1);
free ($3);
}
}
;
trace_symbol:
TRACE_SYMBOL EQSIGN QUOTEDSTRING {
size_t len = strlen ($3);
char *c = $3;
/* Trim quotes */
if (*c == '"') {
c++;
len--;
}
if (c[len - 1] == '"') {
len--;
}
if (cfg->trace_symbol) {
free (cfg->trace_symbol);
}
cfg->trace_symbol = (char *)malloc (len + 1);
if (!cfg->trace_symbol) {
yyerror ("yyparse: malloc failed");
YYERROR;
}
strlcpy (cfg->trace_symbol, c, len + 1);
free ($3);
}
;
trace_addr:
TRACE_ADDR EQSIGN QUOTEDSTRING {
size_t len = strlen ($3);
char *c = $3;
/* Trim quotes */
if (*c == '"') {
c++;
len--;
}
if (c[len - 1] == '"') {
len--;
}
if (cfg->trace_addr) {
free (cfg->trace_addr);
}
cfg->trace_addr = (char *)malloc (len + 1);
if (!cfg->trace_addr) {
yyerror ("yyparse: malloc failed");
YYERROR;
}
strlcpy (cfg->trace_addr, c, len + 1);
free ($3);
}
;
spf:
SPF EQSIGN spf_params
;
spf_params:
spf_domain
| spf_params COMMA spf_domain
;
spf_domain:
DOMAIN {
if (!add_spf_domain (cfg, $1)) {
yyerror ("yyparse: add_spf_domain");
YYERROR;
}
}
| STRING {
if (!add_spf_domain (cfg, $1)) {
yyerror ("yyparse: add_spf_domain");
YYERROR;
}
}
;
bindsock:
BINDSOCK EQSIGN SOCKCRED {
cfg->sock_cred = $3;
}
;
maxsize:
MAXSIZE EQSIGN SIZELIMIT {
cfg->sizelimit = $3;
}
| MAXSIZE EQSIGN NUMBER {
cfg->sizelimit = $3;
}
;
usedcc:
USEDCC EQSIGN FLAG {
if ($3 == -1) {
yyerror ("yyparse: parse flag");
YYERROR;
}
cfg->use_dcc = $3;
}
;
greylisting:
GREYLISTING OBRACE greylistingbody EBRACE
;
greylistingbody:
greylistingcmd SEMICOLON
| greylistingbody greylistingcmd SEMICOLON
;
greylistingcmd:
greylisting_whitelist
| greylisting_timeout
| greylisting_expire
| greylisting_whitelist_expire
| greylisted_message
| awl_enable
| awl_hits
| awl_pool
| awl_ttl
;
greylisting_timeout:
TIMEOUT EQSIGN SECONDS {
/* This value is in seconds, not in milliseconds */
cfg->greylisting_timeout = $3 / 1000;
}
;
greylisting_expire:
EXPIRE EQSIGN SECONDS {
/* This value is in seconds, not in milliseconds */
cfg->greylisting_expire = $3 / 1000;
}
;
greylisting_whitelist_expire:
EXPIRE_WHITE EQSIGN SECONDS {
/* This value is in seconds, not in milliseconds */
cfg->whitelisting_expire = $3 / 1000;
}
;
greylisting_whitelist:
WHITELIST EQSIGN greylisting_ip_list
;
greylisting_ip_list:
greylisting_ip
| greylisting_ip_list COMMA greylisting_ip
;
greylisting_ip:
ip_net {
if (add_ip_radix (cfg->grey_whitelist_tree, $1) == 0) {
YYERROR;
}
}
;
awl_enable:
AWL_ENABLE EQSIGN FLAG {
if ($3 == -1) {
yyerror ("yyparse: cannot parse flag");
YYERROR;
}
cfg->awl_enable = $3;
}
;
awl_hits:
AWL_HITS EQSIGN NUMBER {
cfg->awl_max_hits = $3;
}
;
awl_pool:
AWL_POOL EQSIGN SIZELIMIT {
cfg->awl_pool_size = $3;
}
;
awl_ttl:
AWL_TTL EQSIGN SECONDS {
/* Time is in seconds */
cfg->awl_ttl = $3 / 1000;
}
;
greylisted_message:
GREYLISTED_MESSAGE EQSIGN QUOTEDSTRING {
size_t len = strlen ($3);
char *c = $3;
/* Trim quotes */
if (*c == '"') {
c++;
len--;
}
if (c[len - 1] == '"') {
len--;
}
if (cfg->greylisted_message) {
free (cfg->greylisted_message);
}
cfg->greylisted_message = (char *)malloc (len + 1);
if (!cfg->greylisted_message) {
yyerror ("yyparse: malloc failed");
YYERROR;
}
strlcpy (cfg->greylisted_message, c, len + 1);
free ($3);
}
;
ip_net:
IPADDR
| IPNETWORK
;
memcached:
MEMCACHED OBRACE memcachedbody EBRACE
;
memcachedbody:
memcachedcmd SEMICOLON
| memcachedbody memcachedcmd SEMICOLON
;
memcachedcmd:
memcached_grey_servers
| memcached_white_servers
| memcached_limits_servers
| memcached_id_servers
| memcached_connect_timeout
| memcached_error_time
| memcached_dead_time
| memcached_maxerrors
| memcached_protocol
| memcached_id_prefix
| memcached_grey_prefix
| memcached_white_prefix
;
memcached_grey_servers:
SERVERS_GREY EQSIGN memcached_grey_server
;
memcached_grey_server:
memcached_grey_params
| memcached_grey_server COMMA memcached_grey_params
;
memcached_grey_params:
OBRACE memcached_hosts COMMA memcached_hosts EBRACE {
if (!add_memcached_server (cfg, $2, $4, MEMCACHED_SERVER_GREY)) {
yyerror ("yyparse: add_memcached_server");
YYERROR;
}
free ($2);
free ($4);
}
| memcached_hosts {
if (!add_memcached_server (cfg, $1, NULL, MEMCACHED_SERVER_GREY)) {
yyerror ("yyparse: add_memcached_server");
YYERROR;
}
free ($1);
}
;
memcached_white_servers:
SERVERS_WHITE EQSIGN memcached_white_server
;
memcached_white_server:
memcached_white_params
| memcached_white_server COMMA memcached_white_params
;
memcached_white_params:
OBRACE memcached_hosts COMMA memcached_hosts EBRACE {
if (!add_memcached_server (cfg, $2, $4, MEMCACHED_SERVER_WHITE)) {
yyerror ("yyparse: add_memcached_server");
YYERROR;
}
free ($2);
free ($4);
}
| memcached_hosts {
if (!add_memcached_server (cfg, $1, NULL, MEMCACHED_SERVER_WHITE)) {
yyerror ("yyparse: add_memcached_server");
YYERROR;
}
free ($1);
}
;
memcached_limits_servers:
SERVERS_LIMITS EQSIGN memcached_limits_server
;
memcached_limits_server:
memcached_limits_params
| memcached_limits_server COMMA memcached_limits_params
;
memcached_limits_params:
memcached_hosts {
if (!add_memcached_server (cfg, $1, NULL, MEMCACHED_SERVER_LIMITS)) {
yyerror ("yyparse: add_memcached_server");
YYERROR;
}
free ($1);
}
;
memcached_id_servers:
SERVERS_ID EQSIGN memcached_id_server
;
memcached_id_server:
memcached_id_params
| memcached_id_server COMMA memcached_id_params
;
memcached_id_params:
memcached_hosts {
if (!add_memcached_server (cfg, $1, NULL, MEMCACHED_SERVER_ID)) {
yyerror ("yyparse: add_memcached_server");
YYERROR;
}
free ($1);
}
;
memcached_hosts:
STRING
| IPADDR
| DOMAIN
| HOSTPORT
;
memcached_error_time:
ERROR_TIME EQSIGN NUMBER {
cfg->memcached_error_time = $3;
}
;
memcached_dead_time:
DEAD_TIME EQSIGN NUMBER {
cfg->memcached_dead_time = $3;
}
;
memcached_maxerrors:
MAXERRORS EQSIGN NUMBER {
cfg->memcached_maxerrors = $3;
}
;
memcached_connect_timeout:
CONNECT_TIMEOUT EQSIGN SECONDS {
cfg->memcached_connect_timeout = $3;
}
;
memcached_protocol:
PROTOCOL EQSIGN STRING {
if (strncasecmp ($3, "udp", sizeof ("udp") - 1) == 0) {
cfg->memcached_protocol = UDP_TEXT;
}
else if (strncasecmp ($3, "tcp", sizeof ("tcp") - 1) == 0) {
cfg->memcached_protocol = TCP_TEXT;
}
else {
yyerror ("yyparse: cannot recognize protocol: %s", $3);
YYERROR;
}
}
;
memcached_id_prefix:
ID_PREFIX EQSIGN QUOTEDSTRING {
size_t len = strlen ($3);
char *c = $3;
/* Trim quotes */
if (*c == '"') {
c++;
len--;
}
if (c[len - 1] == '"') {
len--;
}
if (cfg->id_prefix) {
free (cfg->id_prefix);
}
cfg->id_prefix = (char *)malloc (len + 1);
if (!cfg->id_prefix) {
yyerror ("yyparse: malloc failed");
YYERROR;
}
strlcpy (cfg->id_prefix, c, len + 1);
free ($3);
}
;
memcached_grey_prefix:
GREY_PREFIX EQSIGN QUOTEDSTRING {
size_t len = strlen ($3);
char *c = $3;
/* Trim quotes */
if (*c == '"') {
c++;
len--;
}
if (c[len - 1] == '"') {
len--;
}
if (cfg->grey_prefix) {
free (cfg->grey_prefix);
}
cfg->grey_prefix = (char *)malloc (len + 1);
if (!cfg->grey_prefix) {
yyerror ("yyparse: malloc failed");
YYERROR;
}
strlcpy (cfg->grey_prefix, c, len + 1);
free ($3);
}
;
memcached_white_prefix:
WHITE_PREFIX EQSIGN QUOTEDSTRING {
size_t len = strlen ($3);
char *c = $3;
/* Trim quotes */
if (*c == '"') {
c++;
len--;
}
if (c[len - 1] == '"') {
len--;
}
if (cfg->white_prefix) {
free (cfg->white_prefix);
}
cfg->white_prefix = (char *)malloc (len + 1);
if (!cfg->white_prefix) {
yyerror ("yyparse: malloc failed");
YYERROR;
}
strlcpy (cfg->white_prefix, c, len + 1);
free ($3);
}
;
beanstalk:
BEANSTALK OBRACE beanstalkbody EBRACE
;
beanstalkbody:
beanstalkcmd SEMICOLON
| beanstalkbody beanstalkcmd SEMICOLON
;
beanstalkcmd:
beanstalk_servers
| beanstalk_copy_server
| beanstalk_spam_server
| beanstalk_connect_timeout
| beanstalk_error_time
| beanstalk_dead_time
| beanstalk_maxerrors
| beanstalk_protocol
| beanstalk_id_regexp
| beanstalk_lifetime
| send_beanstalk_headers
| send_beanstalk_spam
| send_beanstalk_copy
| beanstalk_copy_prob
;
beanstalk_servers:
SERVERS EQSIGN beanstalk_server
;
beanstalk_server:
beanstalk_params
| beanstalk_server COMMA beanstalk_params
;
beanstalk_params:
beanstalk_hosts {
if (!add_beanstalk_server (cfg, $1, 0)) {
yyerror ("yyparse: add_beanstalk_server");
YYERROR;
}
free ($1);
}
;
beanstalk_copy_server:
COPY_SERVER EQSIGN beanstalk_hosts {
if (!add_beanstalk_server (cfg, $3, 1)) {
yyerror ("yyparse: add_beanstalk_server");
YYERROR;
}
free ($3);
}
;
beanstalk_spam_server:
SPAM_SERVER EQSIGN beanstalk_hosts {
if (!add_beanstalk_server (cfg, $3, 2)) {
yyerror ("yyparse: add_beanstalk_server");
YYERROR;
}
free ($3);
}
;
beanstalk_hosts:
STRING
| IPADDR
| DOMAIN
| HOSTPORT
;
beanstalk_error_time:
ERROR_TIME EQSIGN NUMBER {
cfg->beanstalk_error_time = $3;
}
;
beanstalk_dead_time:
DEAD_TIME EQSIGN NUMBER {
cfg->beanstalk_dead_time = $3;
}
;
beanstalk_maxerrors:
MAXERRORS EQSIGN NUMBER {
cfg->beanstalk_maxerrors = $3;
}
;
beanstalk_connect_timeout:
CONNECT_TIMEOUT EQSIGN SECONDS {
cfg->beanstalk_connect_timeout = $3;
}
;
beanstalk_protocol:
PROTOCOL EQSIGN STRING {
if (strncasecmp ($3, "udp", sizeof ("udp") - 1) == 0) {
cfg->beanstalk_protocol = BEANSTALK_UDP_TEXT;
}
else if (strncasecmp ($3, "tcp", sizeof ("tcp") - 1) == 0) {
cfg->beanstalk_protocol = BEANSTALK_TCP_TEXT;
}
else {
yyerror ("yyparse: cannot recognize protocol: %s", $3);
YYERROR;
}
}
;
beanstalk_id_regexp:
ID_REGEXP EQSIGN QUOTEDSTRING {
size_t len = strlen ($3);
char *c = $3;
int offset;
const char *read_err;
/* Trim quotes */
if (*c == '"') {
c++;
len--;
}
if (*c == '/') {
c ++;
len --;
}
if (c[len - 1] == '"') {
c[len - 1] = '\0';
len --;
}
if (c[len - 1] == '/') {
c[len - 1] = '\0';
len --;
}
if (cfg->special_mid_re) {
pcre_free (cfg->special_mid_re);
}
cfg->special_mid_re = pcre_compile (c, 0, &read_err, &offset, NULL);
if (cfg->special_mid_re == NULL) {
yyerror ("yyparse: pcre_compile failed: %s", read_err);
YYERROR;
}
free ($3);
}
;
beanstalk_lifetime:
LIFETIME EQSIGN NUMBER {
cfg->beanstalk_lifetime = $3;
}
;
send_beanstalk_headers:
SEND_BEANSTALK_HEADERS EQSIGN FLAG {
if ($3) {
cfg->send_beanstalk_headers = 1;
}
else {
cfg->send_beanstalk_headers = 0;
}
}
;
send_beanstalk_copy:
SEND_BEANSTALK_COPY EQSIGN FLAG {
if ($3) {
cfg->send_beanstalk_copy = 1;
}
else {
cfg->send_beanstalk_copy = 0;
}
}
;
send_beanstalk_spam:
SEND_BEANSTALK_SPAM EQSIGN FLAG {
if ($3) {
cfg->send_beanstalk_spam = 1;
}
else {
cfg->send_beanstalk_spam = 0;
}
}
;
beanstalk_copy_prob:
COPY_PROBABILITY EQSIGN NUMBER {
cfg->beanstalk_copy_prob = $3;
}
| COPY_PROBABILITY EQSIGN FLOAT {
cfg->beanstalk_copy_prob = $3;
}
;
limits:
LIMITS OBRACE limitsbody EBRACE
;
limitsbody:
limitcmd SEMICOLON
| limitsbody limitcmd SEMICOLON
;
limitcmd:
limit_to
| limit_to_ip
| limit_to_ip_from
| limit_whitelist
| limit_whitelist_rcpt
| limit_bounce_addrs
| limit_bounce_to
| limit_bounce_to_ip
;
limit_to:
LIMIT_TO EQSIGN BUCKET {
cfg->limit_to.burst = $3.burst;
cfg->limit_to.rate = $3.rate;
}
;
limit_to_ip:
LIMIT_TO_IP EQSIGN BUCKET {
cfg->limit_to_ip.burst = $3.burst;
cfg->limit_to_ip.rate = $3.rate;
}
;
limit_to_ip_from:
LIMIT_TO_IP_FROM EQSIGN BUCKET {
cfg->limit_to_ip_from.burst = $3.burst;
cfg->limit_to_ip_from.rate = $3.rate;
}
;
limit_whitelist:
LIMIT_WHITELIST EQSIGN whitelist_ip_list
;
whitelist_ip_list:
ip_net {
if (add_ip_radix (cfg->limit_whitelist_tree, $1) == 0) {
YYERROR;
}
}
| whitelist_ip_list COMMA ip_net {
if (add_ip_radix (cfg->limit_whitelist_tree, $3) == 0) {
YYERROR;
}
}
;
limit_whitelist_rcpt:
LIMIT_WHITELIST_RCPT EQSIGN whitelist_rcpt_list
;
whitelist_rcpt_list:
STRING {
add_rcpt_whitelist (cfg, $1, 0);
}
| whitelist_rcpt_list COMMA STRING {
add_rcpt_whitelist (cfg, $3, 0);
}
;
limit_bounce_addrs:
LIMIT_BOUNCE_ADDRS EQSIGN bounce_addr_list
;
bounce_addr_list:
STRING {
struct addr_list_entry *t;
t = (struct addr_list_entry *)malloc (sizeof (struct addr_list_entry));
t->addr = strdup ($1);
t->len = strlen (t->addr);
LIST_INSERT_HEAD (&cfg->bounce_addrs, t, next);
}
| bounce_addr_list COMMA STRING {
struct addr_list_entry *t;
t = (struct addr_list_entry *)malloc (sizeof (struct addr_list_entry));
t->addr = strdup ($3);
t->len = strlen (t->addr);
LIST_INSERT_HEAD (&cfg->bounce_addrs, t, next);
}
;
limit_bounce_to:
LIMIT_BOUNCE_TO EQSIGN BUCKET {
cfg->limit_bounce_to.burst = $3.burst;
cfg->limit_bounce_to.rate = $3.rate;
}
;
limit_bounce_to_ip:
LIMIT_BOUNCE_TO_IP EQSIGN BUCKET {
cfg->limit_bounce_to_ip.burst = $3.burst;
cfg->limit_bounce_to_ip.rate = $3.rate;
}
;
whitelist:
WHITELIST EQSIGN whitelist_list
;
whitelist_list:
STRING {
add_rcpt_whitelist (cfg, $1, 1);
}
| whitelist_list COMMA STRING {
add_rcpt_whitelist (cfg, $3, 1);
}
;
dkim:
DKIM_SECTION OBRACE dkimbody EBRACE
;
dkimbody:
dkimcmd SEMICOLON
| dkimbody dkimcmd SEMICOLON
;
dkimcmd:
dkim_key
| dkim_domain
| dkim_header_canon
| dkim_body_canon
| dkim_sign_alg
;
dkim_domain:
DKIM_DOMAIN OBRACE dkim_domain_body EBRACE {
if (cur_domain == NULL || cur_domain->domain == NULL || cur_domain->selector == NULL) {
yyerror ("yyparse: incomplete dkim definition");
YYERROR;
}
if (!cur_domain->is_loaded) {
/* Assume it as wildcard domain */
cur_domain->is_wildcard = 1;
}
HASH_ADD_KEYPTR (hh, cfg->dkim_domains, cur_domain->domain, strlen (cur_domain->domain), cur_domain);
cur_domain = NULL;
}
;
dkim_domain_body:
dkim_domain_cmd SEMICOLON
| dkim_domain_body dkim_domain_cmd SEMICOLON
;
dkim_domain_cmd:
dkim_key
| dkim_domain
| dkim_selector
;
dkim_key:
DKIM_KEY EQSIGN FILENAME {
struct stat st;
int fd;
if (cur_domain == NULL) {
cur_domain = malloc (sizeof (struct dkim_domain_entry));
memset (cur_domain, 0, sizeof (struct dkim_domain_entry));
}
if (stat ($3, &st) != -1 && S_ISREG (st.st_mode)) {
cur_domain->keylen = st.st_size;
if ((fd = open ($3, O_RDONLY)) != -1) {
if ((cur_domain->key = mmap (NULL, cur_domain->keylen, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
yyerror ("yyparse: cannot mmap: %s, %s", $3, strerror (errno));
close (fd);
YYERROR;
}
else {
cur_domain->is_loaded = 1;
}
close (fd);
}
}
cur_domain->keyfile = strdup ($3);
}
;
dkim_domain:
DKIM_DOMAIN EQSIGN QUOTEDSTRING {
size_t len = strlen ($3);
char *c = $3;
if (cur_domain == NULL) {
cur_domain = malloc (sizeof (struct dkim_domain_entry));
memset (cur_domain, 0, sizeof (struct dkim_domain_entry));
}
/* Trim quotes */
if (*c == '"') {
c++;
len--;
}
if (*c == '/') {
c ++;
len --;
}
if (c[len - 1] == '"') {
c[len - 1] = '\0';
len --;
}
if (c[len - 1] == '/') {
c[len - 1] = '\0';
len --;
}
cur_domain->domain = strdup (c);
free ($3);
}
;
dkim_selector:
DKIM_SELECTOR EQSIGN QUOTEDSTRING {
size_t len = strlen ($3);
char *c = $3;
if (cur_domain == NULL) {
cur_domain = malloc (sizeof (struct dkim_domain_entry));
memset (cur_domain, 0, sizeof (struct dkim_domain_entry));
}
/* Trim quotes */
if (*c == '"') {
c++;
len--;
}
if (*c == '/') {
c ++;
len --;
}
if (c[len - 1] == '"') {
c[len - 1] = '\0';
len --;
}
if (c[len - 1] == '/') {
c[len - 1] = '\0';
len --;
}
cur_domain->selector = strdup (c);
free ($3);
}
;
dkim_header_canon:
DKIM_HEADER_CANON EQSIGN DKIM_SIMPLE {
cfg->dkim_relaxed_header = 0;
}
| DKIM_HEADER_CANON EQSIGN DKIM_RELAXED {
cfg->dkim_relaxed_header = 1;
}
;
dkim_body_canon:
DKIM_BODY_CANON EQSIGN DKIM_SIMPLE {
cfg->dkim_relaxed_body = 0;
}
| DKIM_BODY_CANON EQSIGN DKIM_RELAXED {
cfg->dkim_relaxed_body = 1;
}
;
dkim_sign_alg:
DKIM_SIGN_ALG EQSIGN DKIM_SHA1 {
cfg->dkim_sign_sha256 = 0;
}
| DKIM_SIGN_ALG EQSIGN DKIM_SHA256 {
cfg->dkim_sign_sha256 = 1;
}
;
%%
/*
* vi:ts=4
*/