1 #include "policyd.h"
2
3 /*
4 *
5 *
6 * Policy Daemon
7 *
8 * policy daemon is used in conjuction with postfix to combat spam.
9 *
10 * Copyright (C) 2004 Cami Sardinha (cami@mweb.co.za)
11 *
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2 of the License, or (at your
16 * option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 *
27 *
28 *
29 */
30
31 /*
32 * function: read_conf
33 * purpose: read config options from file and store memory
34 */
35 void
read_conf(unsigned int prog)36 read_conf(unsigned int prog)
37 {
38
39 memset(confbuf, 0x00, 256);
40
41 /* file should exist */
42 if((fd_config=freopen(configpath, "r", stdin)) == NULL)
43 {
44 fprintf(stderr, "fopen(): %s: %s\n", strerror(errno), configpath);
45 exit(-1);
46 }
47
48 /* cycle through file, grab config options */
49 while(fgets(confbuf, 256, fd_config) != NULL)
50 {
51 memset(extract_array[0], 0x00, 256);
52
53 /* SYSLOG FACILITY */
54 if(strncmp(confbuf, "SYSLOG_FACILITY=", 16) == 0)
55 {
56 extract(0, confbuf, 15);
57 SYSLOG_FACILITY=parse_syslog_priority(extract_array[0]);
58 }
59
60 /* DAEMON MODE */
61 if(strncmp(confbuf, "DAEMON=", 7) == 0)
62 {
63 extract(0, confbuf, 6);
64 DAEMON=atol(extract_array[0]);
65 }
66
67 /* CHROOT */
68 if(strncmp(confbuf, "CHROOT=", 7) == 0)
69 {
70 extract(0, confbuf, 6);
71 if((CHROOT=malloc(strlen(extract_array[0])+1)) != NULL)
72 strcpy(CHROOT, extract_array[0]);
73 else {
74 logmessage("malloc(): %s\n", strerror(errno));
75 exit(-1);
76 }
77 }
78
79 /* UID */
80 if(strncmp(confbuf, "UID=", 4) == 0)
81 {
82 extract(0, confbuf, 3);
83 UID=atol(extract_array[0]);
84 }
85
86 /* GID */
87 if(strncmp(confbuf, "GID=", 4) == 0)
88 {
89 extract(0, confbuf, 3);
90 GID=atol(extract_array[0]);
91 }
92
93 /* DEBUG MODE */
94 if(strncmp(confbuf, "DEBUG=", 6) == 0)
95 {
96 extract(0, confbuf, 5);
97 DEBUG=atol(extract_array[0]);
98 }
99
100 /* PORT TO BIND TO */
101 if(strncmp(confbuf, "BINDPORT=", 9) == 0)
102 {
103 extract(0, confbuf, 8);
104 BINDPORT=atol(extract_array[0]);
105 }
106
107 /* HOST TO BIND TO */
108 if(strncmp(confbuf, "BINDHOST=", 9) == 0)
109 {
110 extract(0, confbuf, 8);
111 if((BINDHOST=malloc(strlen(extract_array[0])+1)) != NULL)
112 strcpy(BINDHOST,extract_array[0]);
113 else {
114 logmessage("malloc(): %s\n", strerror(errno));
115 exit(-1);
116 }
117 }
118
119 /* CONNECTION ACL */
120 if(strncmp(confbuf, "CONN_ACL=", 9) == 0)
121 {
122 extract(0, confbuf, 8);
123 if((CONN_ACL=malloc(strlen(extract_array[0])+1)) != NULL)
124 strcpy(CONN_ACL,extract_array[0]);
125 else {
126 logmessage("malloc(): %s\n", strerror(errno));
127 exit(-1);
128 }
129 }
130
131 /* PID TO WRITE TO */
132 if(strncmp(confbuf, "PIDFILE=", 8) == 0)
133 {
134 extract(0, confbuf, 7);
135 if((PIDFILE=malloc(strlen(extract_array[0])+1)) != NULL)
136 strcpy(PIDFILE,extract_array[0]);
137 else {
138 logmessage("malloc(): %s\n", strerror(errno));
139 exit(-1);
140 }
141 }
142
143 /* GREYLISTING */
144 if(strncmp(confbuf, "GREYLISTING=", 12) == 0)
145 {
146 extract(0, confbuf, 11);
147 GREYLISTING=atol(extract_array[0]);
148 }
149
150 /* GREYLIST_HOSTADDR */
151 if(strncmp(confbuf, "GREYLIST_HOSTADDR=", 18) == 0)
152 {
153 extract(0, confbuf, 17);
154 GREYLIST_HOSTADDR=atol(extract_array[0]);
155 }
156
157 /* TRAINING_MODE */
158 if(strncmp(confbuf, "TRAINING_MODE=", 14) == 0)
159 {
160 extract(0, confbuf, 13);
161 TRAINING_MODE=atol(extract_array[0]);
162 }
163
164 /* TRAINING_POLICY_TIMEOUT */
165 if(strncmp(confbuf, "TRAINING_POLICY_TIMEOUT=", 24) == 0)
166 {
167 extract(0, confbuf, 23);
168 TRAINING_POLICY_TIMEOUT=extract_seconds(extract_array[0]);
169 }
170
171 /* GREYLIST_X_HEADER */
172 if(strncmp(confbuf, "GREYLIST_X_HEADER=", 18) == 0)
173 {
174 extract(0, confbuf, 17);
175 GREYLIST_X_HEADER=atol(extract_array[0]);
176 }
177
178 /* WHITELISTING */
179 if(strncmp(confbuf, "WHITELISTING=", 13) == 0)
180 {
181 extract(0, confbuf, 12);
182 WHITELISTING=atol(extract_array[0]);
183 }
184
185 /* AUTO_WHITELIST_EXPIRE */
186 if(strncmp(confbuf, "AUTO_WHITELIST_EXPIRE=", 22) == 0)
187 {
188 extract(0, confbuf, 21);
189 AUTO_WHITELIST_EXPIRE=extract_seconds(extract_array[0]);
190 }
191
192 /* AUTO_BLACKLIST_EXPIRE */
193 if(strncmp(confbuf, "AUTO_BLACKLIST_EXPIRE=", 22) == 0)
194 {
195 extract(0, confbuf, 21);
196 AUTO_BLACKLIST_EXPIRE=extract_seconds(extract_array[0]);
197 }
198
199 /* AUTO_WHITELIST_NETBLOCK */
200 if(strncmp(confbuf, "AUTO_WHITELIST_NETBLOCK=", 24) == 0)
201 {
202 extract(0, confbuf, 23);
203 AUTO_WHITELIST_NETBLOCK=atol(extract_array[0]);
204 }
205
206 /* AUTO_WHITELIST_NUMBER */
207 if(strncmp(confbuf, "AUTO_WHITELIST_NUMBER=", 22) == 0)
208 {
209 extract(0, confbuf, 21);
210 AUTO_WHITELIST_NUMBER=atol(extract_array[0]);
211 }
212
213 /* AUTO_BLACKLIST_NUMBER */
214 if(strncmp(confbuf, "AUTO_BLACKLIST_NUMBER=", 22) == 0)
215 {
216 extract(0, confbuf, 21);
217 AUTO_BLACKLIST_NUMBER=atol(extract_array[0]);
218 }
219
220 /* AUTO_WHITE_LISTING */
221 if(strncmp(confbuf, "AUTO_WHITE_LISTING=", 19) == 0)
222 {
223 extract(0, confbuf, 18);
224 AUTO_WHITE_LISTING=atol(extract_array[0]);
225 }
226
227 /* SPAMTRAP_AUTO_EXPIRE */
228 if(strncmp(confbuf, "SPAMTRAP_AUTO_EXPIRE=", 21) == 0)
229 {
230 extract(0, confbuf, 20);
231 SPAMTRAP_AUTO_EXPIRE=extract_seconds(extract_array[0]);
232 }
233
234 /* HELO CHECK */
235 if(strncmp(confbuf, "HELO_CHECK=", 11) == 0)
236 {
237 extract(0, confbuf, 10);
238 HELO_CHECK=atol(extract_array[0]);
239 }
240
241 /* HELO_MAX_COUNT */
242 if(strncmp(confbuf, "HELO_MAX_COUNT=", 15) == 0)
243 {
244 extract(0, confbuf, 14);
245 HELO_MAX_COUNT=atol(extract_array[0]);
246 }
247
248 /* HELO_BLACKLIST_AUTO_EXPIRE */
249 if(strncmp(confbuf, "HELO_BLACKLIST_AUTO_EXPIRE=", 27) == 0)
250 {
251 extract(0, confbuf, 26);
252 HELO_BLACKLIST_AUTO_EXPIRE=extract_seconds(extract_array[0]);
253 }
254
255 /* HELO_AUTO_EXPIRE */
256 if(strncmp(confbuf, "HELO_AUTO_EXPIRE=", 17) == 0)
257 {
258 extract(0, confbuf, 16);
259 HELO_AUTO_EXPIRE=extract_seconds(extract_array[0]);
260 }
261
262 /* BLACKLISTING */
263 if(strncmp(confbuf, "BLACKLISTING=", 13) == 0)
264 {
265 extract(0, confbuf, 12);
266 BLACKLISTING=atol(extract_array[0]);
267 }
268
269 /* AUTO_BLACK_LISTING */
270 if(strncmp(confbuf, "AUTO_BLACK_LISTING=", 19) == 0)
271 {
272 extract(0, confbuf, 18);
273 AUTO_BLACK_LISTING=atol(extract_array[0]);
274 }
275
276 /* BLACKLIST_HELO */
277 if(strncmp(confbuf, "BLACKLIST_HELO=", 15) == 0)
278 {
279 extract(0, confbuf, 14);
280 BLACKLIST_HELO=atol(extract_array[0]);
281 }
282
283 /* BLACKLIST_HELO_AUTO_EXPIRE */
284 if(strncmp(confbuf, "BLACKLIST_HELO_AUTO_EXPIRE=", 27) == 0)
285 {
286 extract(0, confbuf, 26);
287 BLACKLIST_HELO_AUTO_EXPIRE=extract_seconds(extract_array[0]);
288 }
289
290 /* SPAMTRAPPING */
291 if(strncmp(confbuf, "SPAMTRAPPING=", 13) == 0)
292 {
293 extract(0, confbuf, 12);
294 SPAMTRAPPING=atol(extract_array[0]);
295 }
296
297 /* BLACKLIST_TEMP_REJECT */
298 if(strncmp(confbuf, "BLACKLIST_TEMP_REJECT=", 22) == 0)
299 {
300 extract(0, confbuf, 21);
301 BLACKLIST_TEMP_REJECT=atol(extract_array[0]);
302 }
303
304 /* BLACKLIST_TIMEOUT */
305 if(strncmp(confbuf, "BLACKLIST_TIMEOUT=", 18) == 0)
306 {
307 extract(0, confbuf, 17);
308 BLACKLIST_TIMEOUT=extract_seconds(extract_array[0]);
309 }
310
311 /* BLACKLIST_NETBLOCK */
312 if(strncmp(confbuf, "BLACKLIST_NETBLOCK=", 19) == 0)
313 {
314 extract(0, confbuf, 18);
315 BLACKLIST_NETBLOCK=atol(extract_array[0]);
316 }
317
318 /* BLACKLIST SENDER */
319 if(strncmp(confbuf, "BLACKLISTSENDER=", 16) == 0)
320 {
321 extract(0, confbuf, 15);
322 BLACKLISTSENDER=atol(extract_array[0]);
323 }
324
325 /* BLACKLIST DNS NAME */
326 if(strncmp(confbuf, "BLACKLISTDNSNAME=", 17) == 0)
327 {
328 extract(0, confbuf, 16);
329 BLACKLISTDNSNAME=atol(extract_array[0]);
330 }
331
332 /* WHITELIST NULL SENDER */
333 if(strncmp(confbuf, "WHITELISTNULL=", 14) == 0)
334 {
335 extract(0, confbuf, 13);
336 WHITELISTNULL=atol(extract_array[0]);
337 }
338
339 /* WHITELIST SENDER */
340 if(strncmp(confbuf, "WHITELISTSENDER=", 16) == 0)
341 {
342 extract(0, confbuf, 15);
343 WHITELISTSENDER=atol(extract_array[0]);
344 }
345
346 /* WHITELIST DNS NAME */
347 if(strncmp(confbuf, "WHITELISTDNSNAME=", 17) == 0)
348 {
349 extract(0, confbuf, 16);
350 WHITELISTDNSNAME=atol(extract_array[0]);
351 }
352
353 /* SENDERTHROTTLE */
354 if(strncmp(confbuf, "SENDERTHROTTLE=", 15) == 0)
355 {
356 extract(0, confbuf, 14);
357 SENDERTHROTTLE=atol(extract_array[0]);
358 }
359
360 /* SENDER_THROTTLE_SASL */
361 if(strncmp(confbuf, "SENDER_THROTTLE_SASL=", 21) == 0)
362 {
363 extract(0, confbuf, 20);
364 SENDER_THROTTLE_SASL=atol(extract_array[0]);
365 }
366
367 /* SENDER_THROTTLE_HOST */
368 if(strncmp(confbuf, "SENDER_THROTTLE_HOST=", 21) == 0)
369 {
370 extract(0, confbuf, 20);
371 SENDER_THROTTLE_HOST=atol(extract_array[0]);
372 }
373
374 /* MAXIMUM SENDER MESSAGE LIMIT */
375 if(strncmp(confbuf, "SENDERMSGLIMIT=", 15) == 0)
376 {
377 extract(0, confbuf, 14);
378 SENDERMSGLIMIT=atol(extract_array[0]);
379 }
380
381 /* MAXIMUM SENDER RCPT LIMIT */
382 if(strncmp(confbuf, "SENDERRCPTLIMIT=", 16) == 0)
383 {
384 extract(0, confbuf, 15);
385 SENDERRCPTLIMIT=atol(extract_array[0]);
386 }
387
388 /* MAX SENDER VOLUME/QUOTA SENDER TIME LIMIT */
389 if(strncmp(confbuf, "SENDERQUOTALIMIT=", 17) == 0)
390 {
391 extract(0, confbuf, 16);
392 SENDERQUOTALIMIT=atol(extract_array[0]);
393 }
394
395 /* MAX MAIL SIZE PER SENDER */
396 if(strncmp(confbuf, "SENDERMSGSIZE=", 14) == 0)
397 {
398 extract(0, confbuf, 13);
399 SENDERMSGSIZE=atol(extract_array[0]);
400 }
401
402 /* MAX SENDER TIME LIMIT */
403 if(strncmp(confbuf, "SENDERTIMELIMIT=", 16) == 0)
404 {
405 extract(0, confbuf, 15);
406 SENDERTIMELIMIT=extract_seconds(extract_array[0]);
407 }
408
409 /* INACTIVE SENDER EXPIRATION TIME LIMIT */
410 if(strncmp(confbuf, "SENDER_INACTIVE_EXPIRE=", 23) == 0)
411 {
412 extract(0, confbuf, 22);
413 SENDER_INACTIVE_EXPIRE=extract_seconds(extract_array[0]);
414 }
415
416 /* SENDER_THROTTLE_AUTOBLACKLIST */
417 if(strncmp(confbuf, "SENDER_THROTTLE_AUTOBLACKLIST=", 30) == 0)
418 {
419 extract(0, confbuf, 29);
420 SENDER_THROTTLE_AUTOBLACKLIST=atol(extract_array[0]);
421 }
422
423 /* SENDER_THROTTLE_AUTOBLACKLIST_NUMBER */
424 if(strncmp(confbuf, "SENDER_THROTTLE_AUTOBLACKLIST_NUMBER=", 37) == 0)
425 {
426 extract(0, confbuf, 36);
427 SENDER_THROTTLE_AUTOBLACKLIST_NUMBER=atol(extract_array[0]);
428 }
429
430 /* SENDER_THROTTLE_AUTOBLACKLIST_EXPIRE */
431 if(strncmp(confbuf, "SENDER_THROTTLE_AUTOBLACKLIST_EXPIRE=", 37) == 0)
432 {
433 extract(0, confbuf, 36);
434 SENDER_THROTTLE_AUTOBLACKLIST_EXPIRE=extract_seconds(extract_array[0]);
435 }
436
437
438 /* RECIPIENTTHROTTLE */
439 if(strncmp(confbuf, "RECIPIENTTHROTTLE=", 18) == 0)
440 {
441 extract(0, confbuf, 17);
442 RECIPIENTTHROTTLE=atol(extract_array[0]);
443 }
444
445 /* MAX RECIPIENT MESSAGES LIMIT */
446 if(strncmp(confbuf, "RECIPIENTMSGLIMIT=", 18) == 0)
447 {
448 extract(0, confbuf, 17);
449 RECIPIENTMSGLIMIT=atol(extract_array[0]);
450 }
451
452 /* MAX RECIPIENT TIME LIMIT */
453 if(strncmp(confbuf, "RECIPIENTTIMELIMIT=", 19) == 0)
454 {
455 extract(0, confbuf, 18);
456 RECIPIENTTIMELIMIT=extract_seconds(extract_array[0]);
457 }
458
459 /* INACTIVE RECIPIENT EXPIRATION TIME LIMIT */
460 if(strncmp(confbuf, "RECIPIENT_INACTIVE_EXPIRE=", 26) == 0)
461 {
462 extract(0, confbuf, 25);
463 RECIPIENT_INACTIVE_EXPIRE=extract_seconds(extract_array[0]);
464 }
465
466 /* TRIPLET TIMEOUT */
467 if(strncmp(confbuf, "TRIPLET_TIME=", 13) == 0)
468 {
469 extract(0, confbuf, 12);
470 TRIPLET_TIME=extract_seconds(extract_array[0]);
471 }
472
473 /* TRIPLET_AUTH_TIMEOUT */
474 if(strncmp(confbuf, "TRIPLET_AUTH_TIMEOUT=", 21) == 0)
475 {
476 extract(0, confbuf, 20);
477 TRIPLET_AUTH_TIMEOUT=extract_seconds(extract_array[0]);
478 }
479
480 /* TRIPLET_UNAUTH_TIMEOUT */
481 if(strncmp(confbuf, "TRIPLET_UNAUTH_TIMEOUT=", 23) == 0)
482 {
483 extract(0, confbuf, 22);
484 TRIPLET_UNAUTH_TIMEOUT=extract_seconds(extract_array[0]);
485 }
486
487 /* OPT-IN / OPT-OUT */
488 if(strncmp(confbuf, "OPTINOUT=", 9) == 0)
489 {
490 extract(0, confbuf, 8);
491 OPTINOUT=atol(extract_array[0]);
492 }
493
494 /* OPT-IN / OPT-OUT */
495 if(strncmp(confbuf, "OPTINOUTALL=", 12) == 0)
496 {
497 extract(0, confbuf, 11);
498 OPTINOUTALL=atol(extract_array[0]);
499 }
500
501 /* FAILOVER MODE */
502 if(strncmp(confbuf, "FAILSAFE=", 9) == 0)
503 {
504 extract(0, confbuf, 8);
505 FAILSAFE=atol(extract_array[0]);
506 }
507
508 /* DATABASE_KEEPALIVE */
509 if(strncmp(confbuf, "DATABASE_KEEPALIVE=", 19) == 0)
510 {
511 extract(0, confbuf, 18);
512 DATABASE_KEEPALIVE=atol(extract_array[0]);
513 }
514
515 /* MYSQL HOST */
516 if(strncmp(confbuf, "MYSQLHOST=", 10) == 0)
517 {
518 extract_conf(0, confbuf, 9);
519 if((MYSQLHOST=malloc(strlen(extract_array_conf[0])+1)) != NULL)
520 strcpy(MYSQLHOST, extract_array_conf[0]);
521 else {
522 logmessage("malloc(): %s\n", strerror(errno));
523 exit(-1);
524 }
525 }
526
527 /* MYSQL DATABASE */
528 if(strncmp(confbuf, "MYSQLDBASE=", 11) == 0)
529 {
530 extract_conf(0, confbuf, 10);
531 if((MYSQLDBASE=malloc(strlen(extract_array_conf[0])+1)) != NULL)
532 strcpy(MYSQLDBASE, extract_array_conf[0]);
533 else {
534 logmessage("malloc(): %s\n", strerror(errno));
535 exit(-1);
536 }
537 }
538
539 /* MYSQL USER */
540 if(strncmp(confbuf, "MYSQLUSER=", 10) == 0)
541 {
542 extract_conf(0, confbuf, 9);
543 if((MYSQLUSER=malloc(strlen(extract_array_conf[0])+1)) != NULL)
544 strcpy(MYSQLUSER, extract_array_conf[0]);
545 else {
546 logmessage("malloc(): %s\n", strerror(errno));
547 exit(-1);
548 }
549 }
550
551 /* MYSQL PASS */
552 if(strncmp(confbuf, "MYSQLPASS=", 10) == 0)
553 {
554 extract_conf(0, confbuf, 9);
555 if((MYSQLPASS=malloc(strlen(extract_array_conf[0])+1)) != NULL)
556 strcpy(MYSQLPASS, extract_array_conf[0]);
557 else {
558 logmessage("malloc(): %s\n", strerror(errno));
559 exit(-1);
560 }
561 }
562
563 /* MYSQL OPTIONS */
564 if(strncmp(confbuf, "MYSQLOPT=", 9) == 0)
565 {
566 extract_conf(0, confbuf, 8);
567 if((MYSQLOPT=malloc(strlen(extract_array_conf[0])+1)) != NULL)
568 strcpy(MYSQLOPT, extract_array_conf[0]);
569 else {
570 logmessage("malloc(): %s\n", strerror(errno));
571 exit(-1);
572 }
573 }
574
575 /* MYSQL PORT */
576 if(strncmp(confbuf, "MYSQLPORT=", 10) == 0)
577 {
578 extract(0, confbuf, 9);
579 MYSQLPORT=atol(extract_array[0]);
580 }
581
582 /* GREYLIST_REJECTION */
583 if(strncmp(confbuf, "GREYLIST_REJECTION=", 19) == 0)
584 {
585 extract_conf(0, confbuf, 18);
586 if((postfix_greylist=malloc(512)) != NULL)
587 snprintf(postfix_greylist, 512, "%s %s\n\n", POSTFIX_GREYLIST, extract_array_conf[0]);
588 else {
589 logmessage("malloc(): %s\n", strerror(errno));
590 exit(-1);
591 }
592 }
593
594 /* BLACKLIST_REJECTION */
595 if(strncmp(confbuf, "BLACKLIST_REJECTION=", 20) == 0)
596 {
597 extract_conf(0, confbuf, 19);
598 if((postfix_blacklist=malloc(512)) != NULL)
599 {
600 if(BLACKLIST_TEMP_REJECT==1)
601 snprintf(postfix_blacklist, 512, "%s %s\n\n", POSTFIX_BLACKLIST_TEMP, extract_array_conf[0]);
602 else
603 snprintf(postfix_blacklist, 512, "%s %s\n\n", POSTFIX_BLACKLIST_PERM, extract_array_conf[0]);
604 }
605 else {
606 logmessage("malloc(): %s\n", strerror(errno));
607 exit(-1);
608 }
609 }
610
611 /* THROTTLE BAD SIZE */
612 if(strncmp(confbuf, "SENDER_SIZE_REJECTION=", 22) == 0)
613 {
614 extract_conf(0, confbuf, 21);
615 if((postfix_bad_size=malloc(512)) != NULL)
616 snprintf(postfix_bad_size, 512, "%s %s\n\n", POSTFIX_BAD_SIZE, extract_array_conf[0]);
617 else {
618 logmessage("malloc(): %s\n", strerror(errno));
619 exit(-1);
620 }
621 }
622
623 /* SPAMTRAP */
624 if(strncmp(confbuf, "SPAMTRAP_REJECTION=", 19) == 0)
625 {
626 extract_conf(0, confbuf, 18);
627 if((postfix_spamtrap=malloc(512)) != NULL)
628 snprintf(postfix_spamtrap, 512, "%s %s\n\n", POSTFIX_SPAMTRAP, extract_array_conf[0]);
629 else {
630 logmessage("malloc(): %s\n", strerror(errno));
631 exit(-1);
632 }
633 }
634
635 /* MAX SENDER QUOTA EXCEEDED */
636 if(strncmp(confbuf, "SENDER_QUOTA_REJECTION=", 23) == 0)
637 {
638 extract_conf(0, confbuf, 22);
639 if((postfix_sender_quota_exceeded=malloc(512)) != NULL)
640 {
641 if(QUOTA_EXCEEDED_TEMP_REJECT==1)
642 snprintf(postfix_sender_quota_exceeded, 512, "%s %s\n\n", POSTFIX_QUOTA_EXCEEDED_TEMP, extract_array_conf[0]);
643 else
644 snprintf(postfix_sender_quota_exceeded, 512, "%s %s\n\n", POSTFIX_QUOTA_EXCEEDED_PERM, extract_array_conf[0]);
645 }
646 else {
647 logmessage("malloc(): %s\n", strerror(errno));
648 exit(-1);
649 }
650 }
651
652 /* MAX RECIPIENT QUOTA EXCEEDED */
653 if(strncmp(confbuf, "RECIPIENT_QUOTA_REJECTION=", 26) == 0) {
654 extract_conf(0, confbuf, 25);
655 if((postfix_recipient_quota_exceeded=malloc(512)) != NULL)
656 {
657 if(QUOTA_EXCEEDED_TEMP_REJECT==1)
658 snprintf(postfix_recipient_quota_exceeded, 512, "%s %s\n\n", POSTFIX_QUOTA_EXCEEDED_TEMP, extract_array_conf[0]);
659 else
660 snprintf(postfix_recipient_quota_exceeded, 512, "%s %s\n\n", POSTFIX_QUOTA_EXCEEDED_PERM, extract_array_conf[0]);
661 }
662 else {
663 logmessage("malloc(): %s\n", strerror(errno));
664 exit(-1);
665 }
666 }
667
668 /* QUOTA_EXCEEDED_TEMP_REJECT */
669 if(strncmp(confbuf, "QUOTA_EXCEEDED_TEMP_REJECT=", 27) == 0)
670 {
671 extract(0, confbuf, 26);
672 QUOTA_EXCEEDED_TEMP_REJECT=atol(extract_array[0]);
673 }
674
675 memset(confbuf, 0x00, 256);
676 }
677
678 /* backward compatible with old configs */
679 if(!SYSLOG_FACILITY)
680 SYSLOG_FACILITY=LOG_MAIL|LOG_INFO;
681
682 /* close config file when we're done */
683 if(fclose(fd_config) != 0)
684 {
685 logmessage("fclose(): %s: %s\n", configpath, strerror(errno));
686 exit(-1);
687 }
688
689 /* background policyd */
690 if(DAEMON)
691 {
692
693 /* dont let cleanup run in the background */
694 if(prog == 0)
695 {
696 if(daemonize(0,0) == -1)
697 {
698 fprintf(stderr, "daemon(): %s\n", strerror(errno));
699 exit(-1);
700 }
701 }
702 }
703
704 /* dump all debugging info */
705 if(DEBUG > 0)
706 {
707 logmessage(" ---- DAEMON CONFIG ----\n");
708 logmessage("config: version> %s\n", VERSION);
709 logmessage("config: debug> %d\n", DEBUG);
710 logmessage("config: daemon mode> %d\n", DAEMON);
711 logmessage("config: bindhost> %s\n", BINDHOST);
712 logmessage("config: bindport> %d\n", BINDPORT);
713 logmessage("config: pidfile> %s\n", PIDFILE);
714 logmessage("config: syslog> %d\n", SYSLOG_FACILITY);
715 logmessage("config: chroot> %s\n", CHROOT);
716 logmessage("config: uid> %d\n", UID);
717 logmessage("config: gid> %d\n", GID);
718 logmessage("config: conn acl> %s\n", CONN_ACL);
719 logmessage("\n");
720
721 logmessage(" ---- DATABASE CONFIG ----\n");
722 logmessage("config: host> %s\n", MYSQLHOST);
723 logmessage("config: user> %s\n", MYSQLUSER);
724 logmessage("config: pass> %s\n", MYSQLPASS);
725 logmessage("config: database> %s\n", MYSQLDBASE);
726 logmessage("config: options> %s\n", MYSQLOPT);
727 logmessage("config: failsafe> %d\n", FAILSAFE);
728 logmessage("config: keep alive> %d\n", DATABASE_KEEPALIVE);
729 logmessage("config: version> %d\n", MYSQL_VERSION_ID);
730 logmessage("\n");
731
732 logmessage(" ---- WHITELISTING ----\n");
733 logmessage("config: whitelisting> %d\n", WHITELISTING);
734 logmessage("config: whitelistnullsender> %d\n", WHITELISTNULL);
735 logmessage("config: whitelistsender> %d\n", WHITELISTSENDER);
736 logmessage("config: whitelistdnsname> %d\n", WHITELISTDNSNAME);
737 logmessage("config: autowhitelisting> %d\n", AUTO_WHITE_LISTING);
738 logmessage("config: autowhitelist_number> %d\n", AUTO_WHITELIST_NUMBER);
739 logmessage("config: autowhitelist_netblock> %d\n", AUTO_WHITELIST_NETBLOCK);
740 logmessage("config: autowhitelist_expire> %d\n", AUTO_WHITELIST_EXPIRE);
741 logmessage("\n");
742
743 logmessage(" ---- BLACKLISTING ----\n");
744 logmessage("config: blacklisting> %d\n", BLACKLISTING);
745 logmessage("config: blacklisting_temp_reject> %d\n", BLACKLIST_TEMP_REJECT);
746 logmessage("config: blacklisting_netblock> %d\n", BLACKLIST_NETBLOCK);
747 logmessage("config: postfix_blacklist> %s\n", postfix_blacklist);
748 logmessage("config: autoblacklisting> %d\n", AUTO_BLACK_LISTING);
749 logmessage("config: autoblacklist_number> %d\n", AUTO_BLACKLIST_NUMBER);
750 logmessage("config: autoblacklist_expire> %d\n", AUTO_BLACKLIST_EXPIRE);
751 logmessage("config: blacklist_rejection> %s\n", postfix_blacklist);
752 logmessage("\n");
753
754 logmessage(" ---- HELO (HRP) ----\n");
755 logmessage("config: helo> %d\n", HELO_CHECK);
756 logmessage("config: helo_max_count> %d\n", HELO_MAX_COUNT);
757 logmessage("config: helo_blacklist_auto_expire> %d\n", HELO_BLACKLIST_AUTO_EXPIRE);
758 logmessage("config: helo_auto_expire> %d\n", HELO_AUTO_EXPIRE);
759 logmessage("\n");
760
761 logmessage(" ---- SPAMTRAP CONFIG ----\n");
762 logmessage("config: spamtrap> %d\n", SPAMTRAPPING);
763 logmessage("config: postfix_spamtrap> %s\n", postfix_spamtrap);
764 logmessage("config: spamtrapauto_expire> %d\n", SPAMTRAP_AUTO_EXPIRE);
765 logmessage("\n");
766
767 logmessage(" ---- GREYLISTING CONFIG ----\n");
768 logmessage("config: greylisting> %d\n", GREYLISTING);
769 logmessage("config: greylist_hostaddr> %d\n", GREYLIST_HOSTADDR);
770 logmessage("config: postfix_greylist> %s\n", postfix_greylist);
771 logmessage("config: greylist_x_header> %d\n", GREYLIST_X_HEADER);
772 logmessage("config: trainingmode> %d\n", TRAINING_MODE);
773 logmessage("config: training_policyd_timeout> %d\n", TRAINING_POLICY_TIMEOUT);
774 logmessage("config: triplet timeout> %d\n", TRIPLET_TIME);
775 logmessage("config: optin/optout> %d\n", OPTINOUT);
776 logmessage("config: optin all in> %d\n", OPTINOUTALL);
777 logmessage("config: triplet auth timeout> %d\n", TRIPLET_AUTH_TIMEOUT);
778 logmessage("config: triplet unauth timeout> %d\n", TRIPLET_UNAUTH_TIMEOUT);
779 logmessage("\n");
780
781 logmessage(" ---- SENDER THROTTLE CONFIG ----\n");
782 logmessage("config: sender throttle> %d\n", SENDERTHROTTLE);
783 logmessage("config: sender throttle sasl> %d\n", SENDER_THROTTLE_SASL);
784 logmessage("config: sender throttle host> %d\n", SENDER_THROTTLE_HOST);
785 logmessage("config: postfix_sender_quota_exceeded> %s\n", postfix_sender_quota_exceeded);
786 logmessage("config: quota_exceeded_temp_reject> %d\n", QUOTA_EXCEEDED_TEMP_REJECT);
787 logmessage("config: postfix_bad_size> %s\n", postfix_bad_size);
788 logmessage("config: sender msglimit> %d\n", SENDERMSGLIMIT);
789 logmessage("config: sender quotalimit> %d\n", SENDERQUOTALIMIT);
790 logmessage("config: sender timelimit> %d\n", SENDERTIMELIMIT);
791 logmessage("config: sender msgsize> %d\n", SENDERMSGSIZE);
792 logmessage("config: sender expire inactive> %d\n", SENDER_INACTIVE_EXPIRE);
793 logmessage("config: sender throttle autoblacklisting> %d\n", SENDER_THROTTLE_AUTOBLACKLIST);
794 logmessage("config: sender throttle autoblacklist number> %d\n", SENDER_THROTTLE_AUTOBLACKLIST_NUMBER);
795 logmessage("config: sender throttle autoblacklist expire> %d\n", SENDER_THROTTLE_AUTOBLACKLIST_EXPIRE);
796 logmessage("\n");
797
798 logmessage(" ---- RECIPIENT THROTTLE CONFIG ----\n");
799 logmessage("config: recipient throttle> %d\n", RECIPIENTTHROTTLE);
800 logmessage("config: recipient msglimit> %d\n", RECIPIENTMSGLIMIT);
801 logmessage("config: recipient timelimit> %d\n", RECIPIENTTIMELIMIT);
802 logmessage("config: recipient expire inactive> %d\n", RECIPIENT_INACTIVE_EXPIRE);
803 logmessage("config: postfix_recipient_quota_exceeded> %s\n", postfix_recipient_quota_exceeded);
804 logmessage("config: quota_exceeded_temp_reject> %d\n", QUOTA_EXCEEDED_TEMP_REJECT);
805 logmessage("\n");
806 }
807
808 if((SENDER_THROTTLE_HOST) && (SENDER_THROTTLE_SASL))
809 {
810 logmessage("FATAL: you may NOT have SENDER_THROTTLE_HOST and SENDER_THROTTLE_SASL enabled\n");
811 exit (-1);
812 }
813
814 /* quick acl check */
815 if((!CONN_ACL) || (!strlen(CONN_ACL)))
816 {
817 logmessage("FATAL: you did not upgrade correctly or have broken something. "
818 "Please read the Changelog.txt. You're missing the CONN_ACL setting\n");
819 exit (-1);
820 }
821 }
822
823
824
825 /*
826 * function: w_string_strip
827 * purpose:
828 * return:
829 */
830 int
w_string_strip(void * str,char * token)831 w_string_strip(void *str, char *token)
832 {
833 char *ptr;
834 ptr = strtok(str, token);
835
836 if(ptr != NULL)
837 {
838 return (1); /* found */
839 } else {
840 return (0); /* not found */
841 }
842 }
843
844
845
846 /*
847 * function: logmessage
848 * purpose: log messages to syslog or stdout/stderr
849 * return: nada
850 */
851 void
logmessage(const char * fmt,...)852 logmessage(const char *fmt, ...)
853 {
854 va_list ap;
855 va_start(ap, fmt);
856
857 if(DAEMON == 0)
858 {
859 vfprintf(stdout, fmt, ap);
860 fflush(stdout);
861 }
862 else
863 vsyslog(SYSLOG_FACILITY, fmt, ap);
864
865 va_end(ap);
866 }
867
868
869
870
871 /*
872 * function: usage
873 * purpose: print out usage information
874 * return: nada
875 */
876 void
usage(char * usag)877 usage(char *usag)
878 {
879 logmessage("policyd %s\n", VERSION);
880 logmessage("usage: %s -c /path/to/policyd.conf\n", usag);
881 exit(-1);
882 }
883
884
885
886
887 /*
888 * function: extract_seconds
889 * purpose: convert token to seconds
890 * return: seconds
891 */
892 int
extract_seconds(char * token)893 extract_seconds(char *token)
894 {
895 char tmp[32];
896 unsigned int multiplier=0;
897
898 /* allow values of 0 */
899 if((isdigit(token[0]) != 0) && (atol(token) == 0))
900 return 0;
901
902 memset(tmp, 0x00, 32);
903 switch(token[strlen(token) - 1])
904 {
905
906 case 's':
907 multiplier = 1;
908 break;
909
910 case 'm':
911 multiplier = 60;
912 break;
913
914 case 'h':
915 multiplier = 60 * 60;
916 break;
917
918 case 'd':
919 multiplier = 60 * 60 * 24;
920 break;
921
922 case 'w':
923 multiplier = 60 * 60 * 24 * 7;
924 break;
925
926 case 'M':
927 multiplier = 60 * 60 * 24 * 31 ;
928 break;
929
930 case 'Y':
931 multiplier = 60 * 60 * 24 * 31 * 12;
932 break;
933
934 default:
935 logmessage("fatal: invalid time unit: %s\n", token);
936 exit(-1);
937 }
938
939 strncpy(tmp, token, sizeof(tmp) - 2);
940 return (atol(tmp) * multiplier);
941 }
942
943
944
945
946 /*
947 * function: extract
948 * purpose: extract policy variable
949 * return: policy variable (into an array)
950 */
951 void
extract(unsigned int fd,char * token,unsigned int startlen)952 extract(unsigned int fd, char *token, unsigned int startlen)
953 {
954 unsigned int y, clen=startlen, tlen;
955
956 memset(extract_array[fd], 0x00, 64);
957 tlen=strlen(token);
958
959 for( ; clen <= tlen && clen <= 63 ; clen++) {
960 if(token[clen]=='=') {
961 for(clen++,y=0; clen<=tlen&&clen<=63;clen++) {
962 /* we only want characters [A-Z][a-z][0-9]/@ and . */
963 if((isalnum(token[clen]) != 0)
964 || (token[clen] == '@')
965 || (token[clen] == '|')
966 || (token[clen] == '.')
967 || (token[clen] == '_')
968 || (token[clen] == '-')
969 || (token[clen] == ' ')
970 || (token[clen] == '/')) {
971 extract_array[fd][y]=token[clen]; y++;
972 }
973 }
974 }
975 }
976 }
977
978
979
980
981 /*
982 * function: extract_conf
983 * purpose: extract policy variable
984 * return: policy variable (into an array)
985 */
986 void
extract_conf(unsigned int fd,char * token,unsigned int startlen)987 extract_conf(unsigned int fd, char *token, unsigned int startlen)
988 {
989 unsigned int y, clen=startlen, tlen;
990
991 memset(extract_array_conf[fd], 0x00, 512);
992 tlen=strlen(token);
993
994 for( ; clen <= tlen && clen <= 511 ; clen++) {
995 if(token[clen]=='=') {
996 for(clen++,y=0; clen<=tlen&&clen<=511;clen++) {
997 /* we only want characters [A-Z][a-z][0-9]/@ and . */
998 if((isalnum(token[clen]) != 0) || isascii(token[clen] != 0)) {
999 if((token[clen] != '"') && (token[clen] != '\n')) {
1000 extract_array_conf[fd][y]=token[clen]; y++; }
1001 }
1002 }
1003 }
1004 }
1005 }
1006
1007
1008
1009
1010
1011 /*
1012 * function: extract_ip
1013 * purpose: extract ip address from policy variable
1014 * return: policy variable (into an array)
1015 */
1016 void
extract_ip(unsigned int fd,char * token)1017 extract_ip(unsigned int fd, char *token)
1018 {
1019 unsigned int x=15, y=0, z=0, len;
1020
1021 memset(extract_ip_array[fd], 0x00, 64);
1022 len=strlen(token);
1023
1024 for(x=15,z=0,y=0;x<len||x<64;x++) {
1025 if(token[x] == '\n') break;
1026
1027 /* we only want characters [0-9] and . */
1028 if((isdigit(token[x]) != 0) || (token[x] == '.'))
1029 {
1030 if(token[x] == '.') z++;
1031 if(z == GREYLIST_HOSTADDR) break;
1032 extract_ip_array[fd][y]=token[x]; y++;
1033 }
1034 }
1035 }
1036
1037
1038
1039
1040 /*
1041 * function: extract_ip_array
1042 * purpose: extract ip address from policy variable
1043 * return: policy variable (into an array)
1044 */
1045 void
extract_ipfill(unsigned int fd,char * token)1046 extract_ipfill(unsigned int fd, char *token)
1047 {
1048 unsigned int x=15, y=0, z=0, len, w=5;
1049
1050 memset(extract_ip_array[fd], 0x00, 64);
1051 len=strlen(token);
1052
1053 for(x=15,z=0,y=0;x<len||x<64;x++) {
1054 if(token[x] == '\n') break;
1055
1056 /* we only want characters [0-9] and . */
1057 if((isdigit(token[x]) != 0) || (token[x] == '.'))
1058 {
1059 if(token[x] == '.')
1060 {
1061 if(z == 0)
1062 snprintf(host_array[fd][w], 64, "%s.%%.%%.%%", extract_ip_array[fd]);
1063
1064 if(z == 1)
1065 snprintf(host_array[fd][w], 64, "%s.%%.%%", extract_ip_array[fd]);
1066
1067 if(z == 2)
1068 snprintf(host_array[fd][w], 64, "%s.%%", extract_ip_array[fd]);
1069
1070 z++; w--;
1071 }
1072
1073 if(z == GREYLIST_HOSTADDR)
1074 break;
1075 extract_ip_array[fd][y]=token[x];
1076 y++;
1077 }
1078 }
1079 }
1080
1081
1082
1083
1084 /*
1085 * function: fold
1086 * purpose: shut down all open connections and close policyd gracefully
1087 * return: nada
1088 */
1089 void
fold()1090 fold()
1091 {
1092 logmessage("shutting down..\n");
1093 shutdown(msock, SHUT_RDWR);
1094 shutdown(ssock, SHUT_RDWR);
1095 exit(0);
1096 }
1097
1098
1099
1100
1101 /*
1102 * function: gettime
1103 * purpose: get current time
1104 * return: return current time
1105 */
1106 int
gettime(void)1107 gettime(void)
1108 {
1109 /* get current time */
1110 if(gettimeofday(&timevalue, NULL) != 0)
1111 {
1112 logmessage("gettimeofday(): %s\n", strerror(errno));
1113 exit(-1);
1114 }
1115
1116 return (timevalue.tv_sec);
1117 }
1118
1119
1120
1121
1122 /*
1123 * function: drop_privs
1124 * purpose: drop privledges
1125 * return: nada
1126 */
1127 void
drop_privs(void)1128 drop_privs(void)
1129 {
1130 /*
1131 * 1) quick sanity check
1132 * 2) ensure backward compatibility with old configs
1133 */
1134 if(PIDFILE)
1135 {
1136 /* write to pid file */
1137 if((pidfile=fopen(PIDFILE, "w")) == NULL)
1138 {
1139 fprintf(stderr, "fopen(): %s: %s\n", strerror(errno), PIDFILE);
1140 exit(-1);
1141 }
1142 fprintf(pidfile, "%d\n", (unsigned int)getpid());
1143
1144 /* we're done, clean up */
1145 if(fclose(pidfile) != 0)
1146 {
1147 logmessage("fclose(): %s: %s\n", PIDFILE, strerror(errno));
1148 exit(-1);
1149 }
1150 }
1151
1152 /* change root */
1153 if(chdir(CHROOT) == -1)
1154 {
1155 logmessage("chdir(): %s\n", strerror(errno));
1156 exit(-1);
1157 }
1158
1159 /* chroot */
1160 if(chroot(".") == -1)
1161 {
1162 logmessage("chroot(): %s\n", strerror(errno));
1163 exit(-1);
1164 }
1165
1166 /* change gid */
1167 if(setgid(GID) == -1)
1168 {
1169 logmessage("setgid(): %s\n", strerror(errno));
1170 exit(-1);
1171 }
1172
1173 /* change uid */
1174 if(setuid(UID) == -1)
1175 {
1176 logmessage("setuid(): %s\n", strerror(errno));
1177 exit(-1);
1178 }
1179
1180 }
1181
1182
1183
1184
1185 /*
1186 * function: policy_reply
1187 * purpose: reply/talk to Postfix
1188 * return: 0=sucessfull write(), 1=failed write()
1189 */
1190 void
policy_reply(unsigned int fd,int code,int status)1191 policy_reply(unsigned int fd, int code, int status)
1192 {
1193 /* keep gcc quiet for now */
1194 status = 0;
1195
1196
1197 switch (code)
1198 {
1199 /* accept: always allow */
1200 case 0:
1201 /* dump greylisting information into mail? */
1202 if((GREYLISTING==1) && (GREYLIST_X_HEADER==1))
1203 {
1204
1205 /* whitelisted */
1206 snprintf(xgreylist_array[fd], 128, "%s host: %s\n\n",
1207 POSTFIX_X_HEADER, host_array[fd][2]);
1208
1209 /* if not whitelisted, greylist.c already filled in all the details */
1210 buf_write(fd, xgreylist_array[fd], strlen(xgreylist_array[fd]));
1211 } else {
1212 buf_write(fd, POSTFIX_GOOD, strlen(POSTFIX_GOOD));
1213 }
1214
1215 break;
1216
1217 /* reject: greylisting */
1218 case -1:
1219 buf_write(fd, postfix_greylist, strlen(postfix_greylist));
1220 break;
1221
1222 /* reject: blacklisted */
1223 case -2:
1224 buf_write(fd, postfix_blacklist, strlen(postfix_blacklist));
1225 break;
1226
1227 /* reject: message size too big */
1228 case -3:
1229 buf_write(fd, postfix_bad_size, strlen(postfix_bad_size));
1230 break;
1231
1232 /* reject: spam trap address */
1233 case -4:
1234 buf_write(fd, postfix_spamtrap, strlen(postfix_spamtrap));
1235 break;
1236
1237 /* reject: max sender quota exceeded */
1238 case -5:
1239 buf_write(fd, postfix_sender_quota_exceeded, strlen(postfix_sender_quota_exceeded));
1240 break;
1241
1242 /* reject: helo checking */
1243 case -6:
1244 buf_write(fd, postfix_blacklist, strlen(postfix_blacklist));
1245 break;
1246
1247 /* reject: max recipient quota exceeded */
1248 case -7:
1249 buf_write(fd, postfix_recipient_quota_exceeded, strlen(postfix_recipient_quota_exceeded));
1250 break;
1251
1252 /* reject: max recipient quota exceeded */
1253 case -8:
1254 buf_write(fd, POSTFIX_MODULE_FAILURE, strlen(POSTFIX_MODULE_FAILURE));
1255 break;
1256
1257 /* something bad happened */
1258 default:
1259 logmessage("WARNING: policy_reply called with unknown code\n");
1260 buf_write(fd, POSTFIX_MODULE_FAILURE, strlen(POSTFIX_MODULE_FAILURE));
1261 break;
1262 }
1263 }
1264
1265
1266
1267
1268 /*
1269 * function: db_failure
1270 * purpose: handle database failures so policyd isnt a single point of failure.
1271 * return: -20 for failures (talk to Postfix first)
1272 */
1273 int
db_failure(unsigned int fd,char * module)1274 db_failure(unsigned int fd, char *module)
1275 {
1276
1277 if(FAILSAFE==1)
1278 {
1279 /* do not fail */
1280 logmessage("rcpt=%lu, %s=bypass, host=%s (%s), from=%s, to=%s, size=%s\n",
1281 rcpt_count, /* recipient count */
1282 module, /* module name */
1283 host_array[fd][2], /* host */
1284 host_array[fd][0], /* hostname */
1285 triplet_array[fd][1], /* from */
1286 triplet_array[fd][2], /* rcpt */
1287 triplet_array[fd][3] /* size */
1288 );
1289
1290 mysql_failure_count++;
1291 policy_reply(fd, 0, 0);
1292 return (-20);
1293 }
1294
1295 if(FAILSAFE==0)
1296 {
1297 /* fail as requested */
1298 logmessage("rcpt=%lu, %s=failed, host=%s (%s), from=%s, to=%s, size=%s\n",
1299 rcpt_count, /* recipient count */
1300 module, /* module name */
1301 host_array[fd][2], /* host */
1302 host_array[fd][0], /* hostname */
1303 triplet_array[fd][1], /* from */
1304 triplet_array[fd][2], /* rcpt */
1305 triplet_array[fd][3] /* size */
1306 );
1307
1308 policy_reply(fd, -1, 0);
1309 return (-20);
1310 }
1311
1312 return (0); /* not reached */
1313 }
1314
1315
1316 void
sigalrm_handler(void)1317 sigalrm_handler (void)
1318 {
1319 alarm (0); /* reset alarm timer */
1320 siglongjmp (sjmp, 1); /* jump back */
1321 }
1322
1323
1324
1325 /* EOF */
1326