1 /*
2  * Copyright (c) 2007-2012, Vsevolod Stakhov
3  * All rights reserved.
4 
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer. Redistributions in binary form
9  * must reproduce the above copyright notice, this list of conditions and the
10  * following disclaimer in the documentation and/or other materials provided with
11  * the distribution. Neither the name of the author nor the names of its
12  * contributors may be used to endorse or promote products derived from this
13  * software without specific prior written permission.
14 
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 %{
28 
29 #include "cfg_file.h"
30 
31 #define YYDEBUG 1
32 
33 extern struct config_file *cfg;
34 extern int yylineno;
35 extern char *yytext;
36 
37 struct condl *cur_conditions;
38 struct dkim_domain_entry *cur_domain;
39 uint8_t cur_flags = 0;
40 
41 %}
42 %union
43 {
44 	char *string;
45 	size_t limit;
46 	bucket_t bucket;
47 	char flag;
48 	unsigned int seconds;
49 	unsigned int number;
50 	double frac;
51 }
52 
53 %token	ERROR STRING QUOTEDSTRING FLAG FLOAT
54 %token  TEMPDIR LOGFILE PIDFILE CLAMAV SERVERS ERROR_TIME DEAD_TIME MAXERRORS CONNECT_TIMEOUT PORT_TIMEOUT RESULTS_TIMEOUT SPF DCC
55 %token  FILENAME REGEXP QUOTE SEMICOLON OBRACE EBRACE COMMA EQSIGN
56 %token  BINDSOCK SOCKCRED DOMAIN_STR IPADDR IPNETWORK HOSTPORT NUMBER GREYLISTING WHITELIST TIMEOUT EXPIRE EXPIRE_WHITE
57 %token  MAXSIZE SIZELIMIT SECONDS BUCKET USEDCC MEMCACHED PROTOCOL SERVERS_WHITE SERVERS_LIMITS SERVERS_GREY SERVERS_COPY SERVERS_SPAM
58 %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
59 %token  SPAMD REJECT_MESSAGE SERVERS_ID ID_PREFIX GREY_PREFIX WHITE_PREFIX RSPAMD_METRIC ALSO_CHECK DIFF_DIR CHECK_SYMBOLS SYMBOLS_DIR
60 %token  BEANSTALK ID_REGEXP LIFETIME COPY_SERVER GREYLISTED_MESSAGE SPAMD_SOFT_FAIL STRICT_AUTH
61 %token	TRACE_SYMBOL TRACE_ADDR WHITELIST_FROM SPAM_HEADER SPAM_HEADER_VALUE SPAMD_GREYLIST EXTENDED_SPAM_HEADERS
62 %token  DKIM_SECTION DKIM_KEY DKIM_DOMAIN DKIM_SELECTOR DKIM_HEADER_CANON DKIM_BODY_CANON
63 %token  DKIM_SIGN_ALG DKIM_RELAXED DKIM_SIMPLE DKIM_SHA1 DKIM_SHA256 DKIM_AUTH_ONLY COPY_PROBABILITY
64 %token  SEND_BEANSTALK_SPAM_EXTRA_DIFF DKIM_FOLD_HEADER SPAMD_RETRY_COUNT SPAMD_RETRY_TIMEOUT SPAMD_TEMPFAIL
65 %token  SPAMD_NEVER_REJECT TEMPFILES_MODE USE_REDIS REDIS DKIM_SIGN_NETWORKS OUR_NETWORKS SPAM_BAR_CHAR
66 %token  SPAM_NO_AUTH_HEADER PASSWORD DBNAME SPAMD_SETTINGS_ID SPAMD_SPAM_ADD_HEADER
67 %token  COPY_FULL COPY_CHANNEL SPAM_CHANNEL ENABLE EQPLUS COMPRESSION DKIM_RSPAMD_SIGN
68 %token  EXTENDED_HEADERS_RCPT
69 
70 %type	<string>	STRING
71 %type	<string>	QUOTEDSTRING
72 %type	<string>	FILENAME
73 %type	<string>	REGEXP
74 %type   <string>  	SOCKCRED
75 %type	<string>	IPADDR IPNETWORK
76 %type	<string>	HOSTPORT
77 %type 	<string>	ip_net cache_hosts clamav_addr spamd_addr bounce_addr
78 %type	<string>	DOMAIN_STR
79 %type	<limit>		SIZELIMIT
80 %type	<flag>		FLAG
81 %type	<bucket>	BUCKET;
82 %type	<seconds>	SECONDS;
83 %type	<number>	NUMBER;
84 %type	<frac>		FLOAT;
85 %type	<frac>		prob_num;
86 %%
87 
88 input:
89 	empty
90 	| command separator input
91 	;
92 
93 separator:
94 	SEMICOLON
95 	| empty
96 	;
97 
98 empty:
99 	/* %empty */
100 	;
101 
102 command	:
103 	tempdir
104 	| tempfiles_mode
105 	| strictauth
106 	| pidfile
107 	| clamav
108 	| spamd
109 	| spf
110 	| bindsock
111 	| maxsize
112 	| usedcc
113 	| cache
114 	| limits
115 	| greylisting
116 	| whitelist
117 	| dkim
118 	| use_redis
119 	| our_networks
120 	;
121 
122 tempdir :
123 	TEMPDIR EQSIGN FILENAME {
124 		struct stat st;
125 
126 		if (stat ($3, &st) == -1) {
127 			yyerror ("yyparse: cannot stat directory \"%s\": %s", $3, strerror (errno));
128 			YYERROR;
129 		}
130 		if (!S_ISDIR (st.st_mode)) {
131 			yyerror ("yyparse: \"%s\" is not a directory", $3);
132 			YYERROR;
133 		}
134 
135 		cfg->temp_dir = $3;
136 	}
137 	;
138 
139 tempfiles_mode:
140 	TEMPFILES_MODE EQSIGN NUMBER {
141 		/*
142 		 * We likely have here decimal number, so we need to treat it as
143 		 * octal one that means oct -> dec conversion
144 		 */
145 		int i = 1;
146 		cfg->tempfiles_mode = 0;
147 
148 		while ($3 > 0) {
149 			cfg->tempfiles_mode += $3 % 10 * i;
150 			i *= 8;
151 			$3 /= 10;
152 		}
153 	}
154 	| TEMPFILES_MODE EQSIGN QUOTEDSTRING {
155 		char *err_str;
156 
157 		cfg->tempfiles_mode = strtoul ($3, &err_str, 8);
158 
159 		if (err_str != NULL && *err_str != '\0') {
160 			yyerror ("yyparse: cannot convert \"%s\" to octal number: %s", $3,
161 					strerror (errno));
162 			YYERROR;
163 		}
164 
165 		free ($3);
166 	}
167 	;
168 
169 pidfile :
170 	PIDFILE EQSIGN FILENAME {
171 		cfg->pid_file = $3;
172 	}
173 	;
174 
175 strictauth:
176 	STRICT_AUTH EQSIGN FLAG {
177 		cfg->strict_auth = $3;
178 	}
179 	;
180 
181 clamav:
182 	CLAMAV OBRACE clamavbody EBRACE
183 	| CLAMAV OBRACE empty EBRACE
184 	;
185 
186 clamavbody:
187 	clamavcmd SEMICOLON
188 	| clamavbody clamavcmd SEMICOLON
189 	;
190 
191 clamavcmd:
192 	clamav_servers
193 	| clamav_connect_timeout
194 	| clamav_port_timeout
195 	| clamav_results_timeout
196 	| clamav_error_time
197 	| clamav_dead_time
198 	| clamav_maxerrors
199 	| clamav_whitelist
200 	;
201 
202 clamav_servers:
203 	SERVERS EQSIGN {
204 		cfg->clamav_servers_num = 0;
205 	} clamav_server
206 	| SERVERS EQPLUS clamav_server
207 	;
208 
209 clamav_server:
210 	clamav_params
211 	| clamav_server COMMA clamav_params
212 	| empty
213 	;
214 
215 clamav_params:
216 	clamav_addr	{
217 		if (!add_clamav_server (cfg, $1)) {
218 			yyerror ("yyparse: add_clamav_server");
219 			YYERROR;
220 		}
221 		free ($1);
222 	}
223 	;
224 clamav_addr:
225 	STRING {
226 		$$ = $1;
227 	}
228 	| QUOTEDSTRING {
229 		$$ = $1;
230 	}
231 	| IPADDR{
232 		$$ = $1;
233 	}
234 	| DOMAIN_STR {
235 		$$ = $1;
236 	}
237 	| HOSTPORT {
238 		$$ = $1;
239 	}
240 	| FILENAME {
241 		$$ = $1;
242 	}
243 	;
244 clamav_error_time:
245 	ERROR_TIME EQSIGN NUMBER {
246 		cfg->clamav_error_time = $3;
247 	}
248 	;
249 clamav_dead_time:
250 	DEAD_TIME EQSIGN NUMBER {
251 		cfg->clamav_dead_time = $3;
252 	}
253 	;
254 clamav_maxerrors:
255 	MAXERRORS EQSIGN NUMBER {
256 		cfg->clamav_maxerrors = $3;
257 	}
258 	;
259 clamav_connect_timeout:
260 	CONNECT_TIMEOUT EQSIGN SECONDS {
261 		cfg->clamav_connect_timeout = $3;
262 	}
263 	;
264 clamav_port_timeout:
265 	PORT_TIMEOUT EQSIGN SECONDS {
266 		cfg->clamav_port_timeout = $3;
267 	}
268 	;
269 clamav_results_timeout:
270 	RESULTS_TIMEOUT EQSIGN SECONDS {
271 		cfg->clamav_results_timeout = $3;
272 	}
273 	;
274 clamav_whitelist:
275 	WHITELIST EQSIGN clamav_ip_list
276 	;
277 
278 clamav_ip_list:
279 	clamav_ip
280 	| clamav_ip_list COMMA clamav_ip
281 	;
282 
283 clamav_ip:
284 	ip_net {
285 		if (add_ip_radix (&cfg->clamav_whitelist, $1) == 0) {
286 			YYERROR;
287 		}
288 	}
289 	;
290 
291 spamd:
292 	SPAMD OBRACE spamdbody EBRACE
293 	| SPAMD OBRACE empty EBRACE
294 	;
295 
296 spamdbody:
297 	spamdcmd SEMICOLON
298 	| spamdbody spamdcmd SEMICOLON
299 	;
300 
301 spamdcmd:
302 	spamd_servers
303 	| spamd_connect_timeout
304 	| spamd_results_timeout
305 	| spamd_error_time
306 	| spamd_dead_time
307 	| spamd_maxerrors
308 	| spamd_reject_message
309 	| spamd_whitelist
310 	| extra_spamd_servers
311 	| spamd_rspamd_metric
312 	| diff_dir
313 	| symbols_dir
314 	| check_symbols
315 	| spamd_soft_fail
316 	| trace_symbol
317 	| trace_addr
318 	| spamd_spam_add_header
319 	| spamd_spam_header
320 	| spamd_spam_header_value
321 	| spamd_greylist
322 	| extended_spam_headers
323 	| spamd_retry_count
324 	| spamd_retry_timeout
325 	| spamd_tempfail
326 	| spamd_never_reject
327 	| spam_bar_char
328 	| spam_no_auth_header
329 	| spamd_settings_id
330 	| spamd_compression
331 	| spamd_extended_rcpts
332 	;
333 
334 diff_dir :
335 	DIFF_DIR EQSIGN FILENAME {
336 		struct stat st;
337 
338 		if (stat ($3, &st) == -1) {
339 			yyerror ("yyparse: cannot stat directory \"%s\": %s", $3, strerror (errno));
340 			YYERROR;
341 		}
342 		if (!S_ISDIR (st.st_mode)) {
343 			yyerror ("yyparse: \"%s\" is not a directory", $3);
344 			YYERROR;
345 		}
346 
347 		if (cfg->diff_dir) {
348 			free (cfg->diff_dir);
349 		}
350 
351 		cfg->diff_dir = $3;
352 	}
353 	;
354 symbols_dir:
355 	SYMBOLS_DIR EQSIGN FILENAME {
356 		struct stat st;
357 
358 		if (stat ($3, &st) == -1) {
359 			yyerror ("yyparse: cannot stat directory \"%s\": %s", $3, strerror (errno));
360 			YYERROR;
361 		}
362 		if (!S_ISDIR (st.st_mode)) {
363 			yyerror ("yyparse: \"%s\" is not a directory", $3);
364 			YYERROR;
365 		}
366 
367 		if (cfg->symbols_dir) {
368 			free (cfg->symbols_dir);
369 		}
370 
371 		cfg->symbols_dir = $3;
372 	}
373 	;
374 
375 check_symbols:
376 	CHECK_SYMBOLS EQSIGN QUOTEDSTRING {
377 		free (cfg->check_symbols);
378 		cfg->check_symbols = $3;
379 	}
380 	;
381 
382 
383 spamd_servers:
384 	SERVERS EQSIGN {
385 		cfg->spamd_servers_num = 0;
386 	}
387 	spamd_server
388 	| SERVERS EQPLUS spamd_server
389 	;
390 
391 spamd_server:
392 	spamd_params
393 	| spamd_server COMMA spamd_params
394 	| empty
395 	;
396 
397 spamd_params:
398 	spamd_addr	{
399 		if (!add_spamd_server (cfg, $1, 0)) {
400 			yyerror ("yyparse: add_spamd_server");
401 			YYERROR;
402 		}
403 		free ($1);
404 	}
405 	;
406 
407 extra_spamd_servers:
408 	ALSO_CHECK EQSIGN extra_spamd_server
409 	;
410 
411 extra_spamd_server:
412 	extra_spamd_params
413 	| extra_spamd_server COMMA extra_spamd_params
414 	;
415 
416 extra_spamd_params:
417 	spamd_addr	{
418 		if (!add_spamd_server (cfg, $1, 1)) {
419 			yyerror ("yyparse: add_spamd_server");
420 			YYERROR;
421 		}
422 		free ($1);
423 	}
424 	;
425 
426 spamd_addr:
427 	STRING {
428 		$$ = $1;
429 	}
430 	| QUOTEDSTRING {
431 		$$ = $1;
432 	}
433 	| IPADDR{
434 		$$ = $1;
435 	}
436 	| DOMAIN_STR {
437 		$$ = $1;
438 	}
439 	| HOSTPORT {
440 		$$ = $1;
441 	}
442 	| FILENAME {
443 		$$ = $1;
444 	}
445 	;
446 spamd_error_time:
447 	ERROR_TIME EQSIGN NUMBER {
448 		cfg->spamd_error_time = $3;
449 	}
450 	;
451 spamd_dead_time:
452 	DEAD_TIME EQSIGN NUMBER {
453 		cfg->spamd_dead_time = $3;
454 	}
455 	;
456 spamd_maxerrors:
457 	MAXERRORS EQSIGN NUMBER {
458 		cfg->spamd_maxerrors = $3;
459 	}
460 	;
461 spamd_connect_timeout:
462 	CONNECT_TIMEOUT EQSIGN SECONDS {
463 		cfg->spamd_connect_timeout = $3;
464 	}
465 	;
466 spamd_results_timeout:
467 	RESULTS_TIMEOUT EQSIGN SECONDS {
468 		cfg->spamd_results_timeout = $3;
469 	}
470 	;
471 spamd_reject_message:
472 	REJECT_MESSAGE EQSIGN QUOTEDSTRING {
473 		free (cfg->spamd_reject_message);
474 		cfg->spamd_reject_message = $3;
475 	}
476 	;
477 spamd_whitelist:
478 	WHITELIST EQSIGN {
479 		if (cfg->spamd_whitelist) {
480 			radix_destroy_compressed (cfg->spamd_whitelist);
481 			cfg->spamd_whitelist = NULL;
482 		}
483 	} spamd_ip_list
484 	| WHITELIST EQPLUS spamd_ip_list
485 	;
486 
487 spamd_ip_list:
488 	spamd_ip
489 	| spamd_ip_list COMMA spamd_ip
490 	| empty
491 	;
492 
493 spamd_ip:
494 	ip_net {
495 		if (add_ip_radix (&cfg->spamd_whitelist, $1) == 0) {
496 			YYERROR;
497 		}
498 	}
499 	;
500 
501 spamd_rspamd_metric:
502 	RSPAMD_METRIC EQSIGN QUOTEDSTRING {
503 		free (cfg->rspamd_metric);
504 		cfg->rspamd_metric = $3;
505 	}
506 	;
507 
508 spamd_soft_fail:
509 	SPAMD_SOFT_FAIL EQSIGN FLAG {
510 		cfg->spamd_soft_fail = $3;
511 	}
512 	;
513 
514 spamd_never_reject:
515 	SPAMD_NEVER_REJECT EQSIGN FLAG {
516 		cfg->spamd_never_reject = $3;
517 	}
518 	;
519 
520 spamd_spam_add_header:
521 	SPAMD_SPAM_ADD_HEADER EQSIGN FLAG {
522 		cfg->spamd_spam_add_header = $3;
523 	}
524 	;
525 
526 extended_spam_headers:
527 	EXTENDED_SPAM_HEADERS EQSIGN FLAG {
528 		cfg->extended_spam_headers = $3;
529 	}
530 	;
531 
532 spamd_greylist:
533 	SPAMD_GREYLIST EQSIGN FLAG {
534 		cfg->spamd_greylist = $3;
535 	}
536 	;
537 
538 spamd_spam_header:
539 	SPAM_HEADER EQSIGN QUOTEDSTRING {
540 		free (cfg->spam_header);
541 		cfg->spam_header = $3;
542 	}
543 	;
544 
545 spamd_spam_header_value:
546 	SPAM_HEADER_VALUE EQSIGN QUOTEDSTRING {
547 		free (cfg->spam_header_value);
548 		cfg->spam_header_value = $3;
549 	}
550 	;
551 
552 trace_symbol:
553 	TRACE_SYMBOL EQSIGN QUOTEDSTRING {
554 		free (cfg->trace_symbol);
555 		cfg->trace_symbol = $3;
556 	}
557 	;
558 
559 trace_addr:
560 	TRACE_ADDR EQSIGN QUOTEDSTRING {
561 		free (cfg->trace_addr);
562 		cfg->trace_addr = $3;
563 	}
564 	;
565 spamd_retry_timeout:
566 	SPAMD_RETRY_TIMEOUT EQSIGN SECONDS {
567 		cfg->spamd_retry_timeout = $3;
568 	}
569 	;
570 spamd_retry_count:
571 	SPAMD_RETRY_COUNT EQSIGN NUMBER {
572 		cfg->spamd_retry_count = $3;
573 	}
574 	;
575 spamd_tempfail:
576 	SPAMD_TEMPFAIL EQSIGN FLAG {
577 		cfg->spamd_temp_fail = $3;
578 	}
579 	;
580 spam_bar_char:
581 	SPAM_BAR_CHAR EQSIGN QUOTEDSTRING {
582 		free (cfg->spam_bar_char);
583 		cfg->spam_bar_char = $3;
584 	}
585 	;
586 spam_no_auth_header:
587 	SPAM_NO_AUTH_HEADER EQSIGN FLAG {
588 		cfg->spam_no_auth_header = $3;
589 	}
590 	;
591 
592 spamd_settings_id:
593 	SPAMD_SETTINGS_ID EQSIGN QUOTEDSTRING {
594 		free (cfg->spamd_settings_id);
595 		cfg->spamd_settings_id = $3;
596 	}
597 	;
598 
599 spamd_compression:
600 	COMPRESSION EQSIGN FLAG {
601 		cfg->compression_enable = $3;
602 	}
603 	;
604 
605 spamd_extended_rcpts:
606 	EXTENDED_HEADERS_RCPT EQSIGN {
607 		clear_rcpt_whitelist (&cfg->extended_rcpts);
608 	} extended_rcpt_list
609 	| EXTENDED_HEADERS_RCPT EQPLUS extended_rcpt_list
610 	;
611 extended_rcpt_list:
612 	STRING {
613 		add_rcpt_whitelist (&cfg->extended_rcpts, $1);
614 	}
615 	| QUOTEDSTRING {
616 		add_rcpt_whitelist (&cfg->extended_rcpts, $1);
617 	}
618 	| extended_rcpt_list COMMA STRING {
619 		add_rcpt_whitelist (&cfg->extended_rcpts, $3);
620 	}
621 	| extended_rcpt_list COMMA QUOTEDSTRING {
622 		add_rcpt_whitelist (&cfg->extended_rcpts, $3);
623 	}
624 	| empty
625 	;
626 
627 spf:
628 	SPF EQSIGN spf_params
629 	;
630 spf_params:
631 	spf_domain
632 	| spf_params COMMA spf_domain
633 	;
634 
635 spf_domain:
636 	DOMAIN_STR {
637 		yywarn ("spf support is removed from rmilter");
638 	}
639 	| STRING {
640 		yywarn ("spf support is removed from rmilter");
641 	}
642 	| QUOTEDSTRING {
643 		yywarn ("spf support is removed from rmilter");
644 	}
645 	;
646 
647 bindsock:
648 	BINDSOCK EQSIGN SOCKCRED {
649 		cfg->sock_cred = $3;
650 	}
651 	| BINDSOCK EQSIGN QUOTEDSTRING {
652 		cfg->sock_cred = $3;
653 	}
654 	;
655 
656 
657 maxsize:
658 	MAXSIZE EQSIGN SIZELIMIT {
659 		cfg->sizelimit = $3;
660 	}
661 	| MAXSIZE EQSIGN NUMBER {
662 		cfg->sizelimit = $3;
663 	}
664 	;
665 usedcc:
666 	USEDCC EQSIGN FLAG {
667 		cfg->use_dcc = $3;
668 	}
669 	;
670 
671 greylisting:
672 	GREYLISTING OBRACE greylistingbody EBRACE
673 	| GREYLISTING OBRACE empty EBRACE
674 	;
675 
676 greylistingbody:
677 	greylistingcmd SEMICOLON
678 	| greylistingbody greylistingcmd SEMICOLON
679 	;
680 
681 greylistingcmd:
682 	greylisting_whitelist
683 	| greylisting_timeout
684 	| greylisting_expire
685 	| greylisting_whitelist_expire
686 	| greylisted_message
687 	| greylisting_enable
688 	;
689 
690 greylisting_timeout:
691 	TIMEOUT EQSIGN SECONDS {
692 		/* This value is in seconds, not in milliseconds */
693 		cfg->greylisting_timeout = $3 / 1000;
694 	}
695 	;
696 
697 greylisting_expire:
698 	EXPIRE EQSIGN SECONDS {
699 		/* This value is in seconds, not in milliseconds */
700 		cfg->greylisting_expire = $3 / 1000;
701 	}
702 	;
703 
704 greylisting_whitelist_expire:
705 	EXPIRE_WHITE EQSIGN SECONDS {
706 		/* This value is in seconds, not in milliseconds */
707 		cfg->whitelisting_expire = $3 / 1000;
708 	}
709 	;
710 
711 greylisting_whitelist:
712 	WHITELIST EQSIGN {
713 		if (cfg->grey_whitelist_tree) {
714 			radix_destroy_compressed (cfg->grey_whitelist_tree);
715 			cfg->grey_whitelist_tree = NULL;
716 		}
717 	}
718 	greylisting_ip_list
719 	| WHITELIST EQPLUS greylisting_ip_list
720 	;
721 
722 greylisting_ip_list:
723 	greylisting_ip
724 	| greylisting_ip_list COMMA greylisting_ip
725 	;
726 
727 greylisting_ip:
728 	ip_net {
729 		if (add_ip_radix (&cfg->grey_whitelist_tree, $1) == 0) {
730 			YYERROR;
731 		}
732 	}
733 	;
734 
735 greylisted_message:
736 	GREYLISTED_MESSAGE EQSIGN QUOTEDSTRING {
737 		free (cfg->greylisted_message);
738 		cfg->greylisted_message = $3;
739 	}
740 	;
741 
742 greylisting_enable:
743 	ENABLE EQSIGN FLAG {
744 		cfg->greylisting_enable = $3;
745 	}
746 	;
747 
748 ip_net:
749 	IPADDR
750 	| IPNETWORK
751 	| QUOTEDSTRING
752 	;
753 
754 cache:
755 	MEMCACHED OBRACE cachebody EBRACE
756 	| REDIS { cfg->cache_use_redis = 1; } OBRACE cachebody EBRACE
757 	;
758 
759 cachebody:
760 	cahcebody_commands
761 	| empty
762 	;
763 cahcebody_commands:
764 	cachecmd SEMICOLON
765 	| cahcebody_commands cachecmd SEMICOLON
766 	;
767 
768 cachecmd:
769 	cache_grey_servers
770 	| cache_white_servers
771 	| cache_limits_servers
772 	| cache_id_servers
773 	| cache_spam_servers
774 	| cache_copy_servers
775 	| cache_connect_timeout
776 	| cache_error_time
777 	| cache_dead_time
778 	| cache_maxerrors
779 	| cache_protocol
780 	| cache_id_prefix
781 	| cache_grey_prefix
782 	| cache_white_prefix
783 	| cache_password
784 	| cache_dbname
785 	| cache_spam_channel
786 	| cache_copy_channel
787 	| cache_copy_probability
788 	;
789 
790 cache_grey_servers:
791 	SERVERS_GREY EQSIGN
792 	{
793 		cfg->cache_servers_grey_num = 0;
794 	}
795 	cache_grey_server
796 	| SERVERS_GREY EQPLUS cache_grey_server
797 	;
798 
799 cache_grey_server:
800 	cache_grey_params
801 	| cache_grey_server COMMA cache_grey_params
802 	| empty
803 	;
804 
805 cache_grey_params:
806 	OBRACE cache_hosts COMMA cache_hosts EBRACE {
807 		if (!add_cache_server (cfg, $2, $4, CACHE_SERVER_GREY)) {
808 			yyerror ("yyparse: add_cache_server");
809 			YYERROR;
810 		}
811 		free ($2);
812 		free ($4);
813 	}
814 	| cache_hosts {
815 		if (!add_cache_server (cfg, $1, NULL, CACHE_SERVER_GREY)) {
816 			yyerror ("yyparse: add_cache_server");
817 			YYERROR;
818 		}
819 		free ($1);
820 	}
821 	;
822 
823 cache_white_servers:
824 	SERVERS_WHITE EQSIGN
825 	{
826 		cfg->cache_servers_white_num = 0;
827 	}
828 	cache_white_server
829 	| SERVERS_WHITE EQPLUS cache_white_server
830 	;
831 
832 cache_white_server:
833 	cache_white_params
834 	| cache_white_server COMMA cache_white_params
835 	;
836 
837 cache_white_params:
838 	OBRACE cache_hosts COMMA cache_hosts EBRACE {
839 		if (!add_cache_server (cfg, $2, $4, CACHE_SERVER_WHITE)) {
840 			yyerror ("yyparse: add_cache_server");
841 			YYERROR;
842 		}
843 		free ($2);
844 		free ($4);
845 	}
846 	| cache_hosts {
847 		if (!add_cache_server (cfg, $1, NULL, CACHE_SERVER_WHITE)) {
848 			yyerror ("yyparse: add_cache_server");
849 			YYERROR;
850 		}
851 		free ($1);
852 	}
853 	;
854 
855 cache_limits_servers:
856 	SERVERS_LIMITS EQSIGN
857 	{
858 		cfg->cache_servers_limits_num = 0;
859 	}
860 	cache_limits_server
861 	| SERVERS_LIMITS EQPLUS cache_limits_server
862 	;
863 
864 cache_limits_server:
865 	cache_limits_params
866 	| cache_limits_server COMMA cache_limits_params
867 	| empty
868 	;
869 
870 cache_limits_params:
871 	cache_hosts {
872 		if (!add_cache_server (cfg, $1, NULL, CACHE_SERVER_LIMITS)) {
873 			yyerror ("yyparse: add_cache_server");
874 			YYERROR;
875 		}
876 		free ($1);
877 	}
878 	;
879 
880 cache_id_servers:
881 	SERVERS_ID EQSIGN
882 	{
883 		cfg->cache_servers_id_num = 0;
884 	}
885 	cache_id_server
886 	| SERVERS_ID EQPLUS cache_id_server
887 	;
888 
889 cache_id_server:
890 	cache_id_params
891 	| cache_id_server COMMA cache_id_params
892 	| empty
893 	;
894 
895 cache_id_params:
896 	cache_hosts {
897 		if (!add_cache_server (cfg, $1, NULL, CACHE_SERVER_ID)) {
898 			yyerror ("yyparse: add_cache_server");
899 			YYERROR;
900 		}
901 		free ($1);
902 	}
903 	;
904 
905 cache_copy_servers:
906 	SERVERS_COPY EQSIGN
907 	{
908 		cfg->cache_servers_copy_num = 0;
909 	}
910 	cache_copy_server
911 	| SERVERS_COPY EQPLUS cache_copy_server
912 	;
913 
914 cache_copy_server:
915 	cache_copy_params
916 	| cache_copy_server COMMA cache_copy_params
917 	| empty
918 	;
919 
920 cache_copy_params:
921 	cache_hosts {
922 		if (!add_cache_server (cfg, $1, NULL, CACHE_SERVER_COPY)) {
923 			yyerror ("yyparse: add_cache_server");
924 			YYERROR;
925 		}
926 		free ($1);
927 	}
928 	;
929 
930 cache_spam_servers:
931 	SERVERS_SPAM EQSIGN
932 	{
933 		cfg->cache_servers_spam_num = 0;
934 	}
935 	cache_spam_server
936 	| SERVERS_SPAM EQPLUS cache_spam_server
937 	;
938 
939 cache_spam_server:
940 	cache_spam_params
941 	| cache_spam_server COMMA cache_spam_params
942 	| empty
943 	;
944 
945 cache_spam_params:
946 	cache_hosts {
947 		if (!add_cache_server (cfg, $1, NULL, CACHE_SERVER_SPAM)) {
948 			yyerror ("yyparse: add_cache_server");
949 			YYERROR;
950 		}
951 		free ($1);
952 	}
953 	;
954 
955 cache_hosts:
956 	STRING
957 	| QUOTEDSTRING
958 	| IPADDR
959 	| DOMAIN_STR
960 	| HOSTPORT
961 	;
962 cache_error_time:
963 	ERROR_TIME EQSIGN NUMBER {
964 		cfg->cache_error_time = $3;
965 	}
966 	;
967 cache_dead_time:
968 	DEAD_TIME EQSIGN NUMBER {
969 		cfg->cache_dead_time = $3;
970 	}
971 	;
972 cache_maxerrors:
973 	MAXERRORS EQSIGN NUMBER {
974 		cfg->cache_maxerrors = $3;
975 	}
976 	;
977 cache_connect_timeout:
978 	CONNECT_TIMEOUT EQSIGN SECONDS {
979 		cfg->cache_connect_timeout = $3;
980 	}
981 	;
982 
983 cache_protocol:
984 	PROTOCOL EQSIGN STRING {
985 		/* Do nothing now*/
986 	}
987 	;
988 cache_id_prefix:
989 	ID_PREFIX EQSIGN QUOTEDSTRING {
990 		free (cfg->id_prefix);
991 		cfg->id_prefix = $3;
992 	}
993 	;
994 
995 cache_grey_prefix:
996 	GREY_PREFIX EQSIGN QUOTEDSTRING {
997 		free (cfg->grey_prefix);
998 		cfg->grey_prefix = $3;
999 	}
1000 	;
1001 
1002 cache_white_prefix:
1003 	WHITE_PREFIX EQSIGN QUOTEDSTRING {
1004 		free (cfg->white_prefix);
1005 		cfg->white_prefix = $3;
1006 	}
1007 	;
1008 
1009 cache_password:
1010 	PASSWORD EQSIGN QUOTEDSTRING {
1011 		free (cfg->cache_password);
1012 		cfg->cache_password = $3;
1013 	}
1014 	;
1015 
1016 cache_dbname:
1017 	DBNAME EQSIGN QUOTEDSTRING {
1018 		free (cfg->cache_dbname);
1019 		cfg->cache_dbname = $3;
1020 	}
1021 	;
1022 
1023 cache_copy_channel:
1024 	COPY_CHANNEL EQSIGN QUOTEDSTRING {
1025 		free (cfg->cache_copy_channel);
1026 		cfg->cache_copy_channel = $3;
1027 	}
1028 	;
1029 
1030 cache_copy_probability:
1031 	COPY_PROBABILITY EQSIGN prob_num {
1032 		if ($3 < 0) {
1033 			YYERROR;
1034 		}
1035 		if ($3 > 1.0) {
1036 			if ($3 > 100.0) {
1037 				YYERROR;
1038 			}
1039 			cfg->cache_copy_prob = $3 / 100.0;
1040 		}
1041 		else {
1042 			cfg->cache_copy_prob = $3;
1043 		}
1044 	}
1045 	;
1046 prob_num:
1047 	NUMBER { $$ = (double)$1; }
1048 	| FLOAT
1049 	;
1050 cache_spam_channel:
1051 	SPAM_CHANNEL EQSIGN QUOTEDSTRING {
1052 		free (cfg->cache_spam_channel);
1053 		cfg->cache_spam_channel = $3;
1054 	}
1055 	;
1056 
1057 limits:
1058 	LIMITS OBRACE limitsbody EBRACE
1059 	| LIMITS OBRACE empty EBRACE
1060 	;
1061 
1062 limitsbody:
1063 	limitcmd SEMICOLON
1064 	| limitsbody limitcmd SEMICOLON
1065 	;
1066 limitcmd:
1067 	limit_to
1068 	| limit_to_ip
1069 	| limit_to_ip_from
1070 	| limit_whitelist
1071 	| limit_whitelist_rcpt
1072 	| limit_bounce_addrs
1073 	| limit_bounce_to
1074 	| limit_bounce_to_ip
1075 	| limit_enable
1076 	;
1077 
1078 limit_to:
1079 	LIMIT_TO EQSIGN BUCKET {
1080 		cfg->limit_to.burst = $3.burst;
1081 		cfg->limit_to.rate = $3.rate;
1082 	}
1083 	;
1084 limit_to_ip:
1085 	LIMIT_TO_IP EQSIGN BUCKET {
1086 		cfg->limit_to_ip.burst = $3.burst;
1087 		cfg->limit_to_ip.rate = $3.rate;
1088 	}
1089 	;
1090 limit_to_ip_from:
1091 	LIMIT_TO_IP_FROM EQSIGN BUCKET {
1092 		cfg->limit_to_ip_from.burst = $3.burst;
1093 		cfg->limit_to_ip_from.rate = $3.rate;
1094 	}
1095 	;
1096 limit_whitelist:
1097 	LIMIT_WHITELIST EQSIGN {
1098 		if (cfg->limit_whitelist_tree) {
1099 			radix_destroy_compressed (cfg->limit_whitelist_tree);
1100 			cfg->limit_whitelist_tree = NULL;
1101 		}
1102 	}
1103 	whitelist_ip_list
1104 	| LIMIT_WHITELIST EQPLUS whitelist_ip_list
1105 	;
1106 whitelist_ip_list:
1107 	ip_net {
1108 		if (add_ip_radix (&cfg->limit_whitelist_tree, $1) == 0) {
1109 			YYERROR;
1110 		}
1111 	}
1112 	| whitelist_ip_list COMMA ip_net {
1113 		if (add_ip_radix (&cfg->limit_whitelist_tree, $3) == 0) {
1114 			YYERROR;
1115 		}
1116 	}
1117 	| empty
1118 	;
1119 
1120 limit_whitelist_rcpt:
1121 	LIMIT_WHITELIST_RCPT EQSIGN {
1122 		clear_rcpt_whitelist (&cfg->wlist_rcpt_limit);
1123 	} whitelist_rcpt_list
1124 	| LIMIT_WHITELIST_RCPT EQPLUS whitelist_rcpt_list
1125 	;
1126 whitelist_rcpt_list:
1127 	STRING {
1128 		add_rcpt_whitelist (&cfg->wlist_rcpt_limit, $1);
1129 	}
1130 	| QUOTEDSTRING {
1131 		add_rcpt_whitelist (&cfg->wlist_rcpt_limit, $1);
1132 	}
1133 	| whitelist_rcpt_list COMMA STRING {
1134 		add_rcpt_whitelist (&cfg->wlist_rcpt_limit, $3);
1135 	}
1136 	| whitelist_rcpt_list COMMA QUOTEDSTRING {
1137 		add_rcpt_whitelist (&cfg->wlist_rcpt_limit, $3);
1138 	}
1139 	| empty
1140 	;
1141 
1142 limit_bounce_addrs:
1143 	LIMIT_BOUNCE_ADDRS EQSIGN {
1144 		struct addr_list_entry *t, *tmp;
1145 
1146 		HASH_ITER (hh, cfg->bounce_addrs, t, tmp) {
1147 			HASH_DEL (cfg->bounce_addrs, t);
1148 			free (t->addr);
1149 			free (t);
1150 		}
1151 	} bounce_addr_list
1152 	| LIMIT_BOUNCE_ADDRS EQPLUS bounce_addr_list
1153 	;
1154 bounce_addr_list:
1155 	bounce_addr {
1156 		struct addr_list_entry *t;
1157 		t = calloc (1, sizeof (struct addr_list_entry));
1158 		t->addr = strdup ($1);
1159 		t->len = strlen (t->addr);
1160 		HASH_ADD_KEYPTR(hh, cfg->bounce_addrs, t->addr, t->len, t);
1161 	}
1162 	| bounce_addr_list COMMA bounce_addr {
1163 		struct addr_list_entry *t;
1164 		t = calloc (1, sizeof (struct addr_list_entry));
1165 		t->addr = strdup ($3);
1166 		t->len = strlen (t->addr);
1167 		HASH_ADD_KEYPTR(hh, cfg->bounce_addrs, t->addr, t->len, t);
1168 	}
1169 	| empty
1170 	;
1171 
1172 bounce_addr:
1173 	STRING
1174 	| QUOTEDSTRING
1175 
1176 limit_bounce_to:
1177 	LIMIT_BOUNCE_TO EQSIGN BUCKET {
1178 		cfg->limit_bounce_to.burst = $3.burst;
1179 		cfg->limit_bounce_to.rate = $3.rate;
1180 	}
1181 	;
1182 
1183 limit_bounce_to_ip:
1184 	LIMIT_BOUNCE_TO_IP EQSIGN BUCKET {
1185 		cfg->limit_bounce_to_ip.burst = $3.burst;
1186 		cfg->limit_bounce_to_ip.rate = $3.rate;
1187 	}
1188 	;
1189 
1190 limit_enable:
1191 	ENABLE EQSIGN FLAG {
1192 		cfg->ratelimit_enable = $3;
1193 	}
1194 	;
1195 
1196 whitelist:
1197 	WHITELIST EQSIGN {
1198 		clear_rcpt_whitelist (&cfg->wlist_rcpt_global);
1199 	} whitelist_list
1200 	| WHITELIST EQPLUS whitelist_list
1201 	;
1202 whitelist_list:
1203 	STRING {
1204 		add_rcpt_whitelist (&cfg->wlist_rcpt_global, $1);
1205 	}
1206 	| QUOTEDSTRING {
1207 		add_rcpt_whitelist (&cfg->wlist_rcpt_global, $1);
1208 	}
1209 	| whitelist_list COMMA STRING {
1210 		add_rcpt_whitelist (&cfg->wlist_rcpt_global, $3);
1211 	}
1212 	| whitelist_list COMMA QUOTEDSTRING {
1213 		add_rcpt_whitelist (&cfg->wlist_rcpt_global, $3);
1214 	}
1215 	| empty
1216 	;
1217 
1218 
1219 dkim:
1220 	DKIM_SECTION OBRACE dkimbody EBRACE
1221 	| DKIM_SECTION OBRACE empty EBRACE
1222 	;
1223 
1224 dkimbody:
1225 	dkimcmd SEMICOLON
1226 	| dkimbody dkimcmd SEMICOLON
1227 	;
1228 
1229 dkimcmd:
1230 	dkim_key
1231 	| dkim_domain
1232 	| dkim_header_canon
1233 	| dkim_body_canon
1234 	| dkim_sign_alg
1235 	| dkim_auth_only
1236 	| dkim_fold_header
1237 	| dkim_sign_networks
1238 	| dkim_enable
1239 	| dkim_rspamd_sign
1240 	;
1241 
1242 dkim_domain:
1243 	DKIM_DOMAIN OBRACE dkim_domain_body EBRACE {
1244 		if (cur_domain == NULL || cur_domain->domain == NULL ||
1245 			cur_domain->selector == NULL) {
1246 			yyerror ("yyparse: incomplete dkim definition");
1247 			YYERROR;
1248 		}
1249 		if (!cur_domain->is_loaded) {
1250 			/* Assume it as wildcard domain */
1251 			cur_domain->is_wildcard = 1;
1252 		}
1253 
1254 
1255 		rmilter_str_lc (cur_domain->domain, strlen (cur_domain->domain));
1256 		HASH_ADD_KEYPTR (hh, cfg->dkim_domains, cur_domain->domain,
1257 			strlen (cur_domain->domain), cur_domain);
1258 		cur_domain = NULL;
1259 	}
1260 	;
1261 
1262 dkim_domain_body:
1263 	dkim_domain_cmd SEMICOLON
1264 	| dkim_domain_body dkim_domain_cmd SEMICOLON
1265 	;
1266 
1267 dkim_domain_cmd:
1268 	dkim_key
1269 	| dkim_domain
1270 	| dkim_selector
1271 	;
1272 
1273 dkim_key:
1274 	DKIM_KEY EQSIGN FILENAME {
1275 		struct stat st;
1276 		int fd;
1277 		if (cur_domain == NULL) {
1278 			cur_domain = malloc (sizeof (struct dkim_domain_entry));
1279 			memset (cur_domain, 0, sizeof (struct dkim_domain_entry));
1280 		}
1281 		if (stat ($3, &st) != -1 && S_ISREG (st.st_mode)) {
1282 			cur_domain->keylen = st.st_size;
1283 			if ((fd = open ($3, O_RDONLY)) != -1) {
1284 				if ((cur_domain->key = mmap (NULL, cur_domain->keylen, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
1285 					yyerror ("yyparse: cannot mmap: %s, %s", $3, strerror (errno));
1286 					close (fd);
1287 					YYERROR;
1288 				}
1289 				else {
1290 					cur_domain->is_loaded = 1;
1291 				}
1292 				close (fd);
1293 			}
1294 			else {
1295 				yyerror ("yyparse: cannot open: %s, %s", $3, strerror (errno));
1296 				YYERROR;
1297 			}
1298 		}
1299 		cur_domain->keyfile = strdup ($3);
1300 	}
1301 	| DKIM_KEY EQSIGN QUOTEDSTRING {
1302 		struct stat st;
1303 		int fd;
1304 		if (cur_domain == NULL) {
1305 			cur_domain = malloc (sizeof (struct dkim_domain_entry));
1306 			memset (cur_domain, 0, sizeof (struct dkim_domain_entry));
1307 		}
1308 		if (stat ($3, &st) != -1 && S_ISREG (st.st_mode)) {
1309 			cur_domain->keylen = st.st_size;
1310 			if ((fd = open ($3, O_RDONLY)) != -1) {
1311 				if ((cur_domain->key = mmap (NULL, cur_domain->keylen, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
1312 					yyerror ("yyparse: cannot mmap: %s, %s", $3, strerror (errno));
1313 					close (fd);
1314 					YYERROR;
1315 				}
1316 				else {
1317 					cur_domain->is_loaded = 1;
1318 				}
1319 				close (fd);
1320 			}
1321 			else {
1322 				yyerror ("yyparse: cannot open: %s, %s", $3, strerror (errno));
1323 				YYERROR;
1324 			}
1325 		}
1326 		cur_domain->keyfile = strdup ($3);
1327 	}
1328 	;
1329 
1330 dkim_domain:
1331 	DKIM_DOMAIN EQSIGN QUOTEDSTRING {
1332 
1333 		if (cur_domain == NULL) {
1334 			cur_domain = malloc (sizeof (struct dkim_domain_entry));
1335 			memset (cur_domain, 0, sizeof (struct dkim_domain_entry));
1336 		}
1337 		else {
1338 			free (cur_domain->domain);
1339 		}
1340 
1341 		cur_domain->domain = $3;
1342 	}
1343 	;
1344 
1345 dkim_selector:
1346 	DKIM_SELECTOR EQSIGN QUOTEDSTRING {
1347 
1348 		if (cur_domain == NULL) {
1349 			cur_domain = malloc (sizeof (struct dkim_domain_entry));
1350 			memset (cur_domain, 0, sizeof (struct dkim_domain_entry));
1351 		}
1352 		else {
1353 			free (cur_domain->selector);
1354 		}
1355 		cur_domain->selector = $3;
1356 	}
1357 	;
1358 
1359 dkim_header_canon:
1360 	DKIM_HEADER_CANON EQSIGN DKIM_SIMPLE {
1361 		cfg->dkim_relaxed_header = 0;
1362 	}
1363 	| DKIM_HEADER_CANON EQSIGN DKIM_RELAXED {
1364 		cfg->dkim_relaxed_header = 1;
1365 	}
1366 	;
1367 
1368 dkim_body_canon:
1369 	DKIM_BODY_CANON EQSIGN DKIM_SIMPLE {
1370 		cfg->dkim_relaxed_body = 0;
1371 	}
1372 	| DKIM_BODY_CANON EQSIGN DKIM_RELAXED {
1373 		cfg->dkim_relaxed_body = 1;
1374 	}
1375 	;
1376 
1377 dkim_sign_alg:
1378 	DKIM_SIGN_ALG EQSIGN DKIM_SHA1 {
1379 		cfg->dkim_sign_sha256 = 0;
1380 	}
1381 	| DKIM_SIGN_ALG EQSIGN DKIM_SHA256 {
1382 		cfg->dkim_sign_sha256 = 1;
1383 	}
1384 	;
1385 
1386 dkim_auth_only:
1387 	DKIM_AUTH_ONLY EQSIGN FLAG {
1388 		cfg->dkim_auth_only = $3;
1389 	}
1390 	;
1391 
1392 dkim_fold_header:
1393 	DKIM_FOLD_HEADER EQSIGN FLAG {
1394 		cfg->dkim_fold_header = $3;
1395 	}
1396 	;
1397 dkim_sign_networks:
1398 	DKIM_SIGN_NETWORKS EQSIGN {
1399 		if (cfg->dkim_ip_tree) {
1400 			radix_destroy_compressed (cfg->dkim_ip_tree);
1401 			cfg->dkim_ip_tree = NULL;
1402 		}
1403 	} dkim_ip_list
1404 	| DKIM_SIGN_NETWORKS EQPLUS dkim_ip_list
1405 	;
1406 dkim_ip_list:
1407 	ip_net {
1408 		if (add_ip_radix (&cfg->dkim_ip_tree, $1) == 0) {
1409 			YYERROR;
1410 		}
1411 	}
1412 	| dkim_ip_list COMMA ip_net {
1413 		if (add_ip_radix (&cfg->dkim_ip_tree, $3) == 0) {
1414 			YYERROR;
1415 		}
1416 	}
1417 	| empty
1418 	;
1419 
1420 dkim_enable:
1421 	ENABLE EQSIGN FLAG {
1422 		cfg->dkim_enable = $3;
1423 	}
1424 	;
1425 
1426 dkim_rspamd_sign:
1427 	DKIM_RSPAMD_SIGN EQSIGN FLAG {
1428 		cfg->rspamd_dkim_sign = $3;
1429 	}
1430 	;
1431 
1432 use_redis:
1433 	USE_REDIS EQSIGN FLAG {
1434 		cfg->cache_use_redis = $3;
1435 	}
1436 	;
1437 
1438 our_networks:
1439 	OUR_NETWORKS EQSIGN {
1440 		if (cfg->our_networks) {
1441 			radix_destroy_compressed (cfg->our_networks);
1442 			cfg->our_networks = NULL;
1443 		}
1444 	}our_networks_list
1445 	| OUR_NETWORKS EQPLUS our_networks_list
1446 	;
1447 
1448 our_networks_list:
1449 	our_networks_elt
1450 	| our_networks_list COMMA our_networks_elt
1451 	| empty
1452 	;
1453 
1454 our_networks_elt:
1455 	ip_net {
1456 		if (add_ip_radix (&cfg->our_networks, $1) == 0) {
1457 			YYERROR;
1458 		}
1459 	}
1460 	;
1461 %%
1462 /*
1463  * vi:ts=4
1464  */
1465