1<HTML> 2<HEAD><TITLE>A Sample Filter</TITLE></HEAD> 3<BODY> 4<!-- 5$Id: sample.html,v 1.23 2013-11-22 20:51:39 ca Exp $ 6--> 7<H1>A Sample Filter</H1> 8 9The following sample logs each message to a separate temporary file, 10adds a recipient given with the -a flag, 11and rejects a disallowed recipient address given with the -r flag. 12It recognizes the following options: 13<P> 14<CENTER> 15<TABLE border="1" cellpadding=2 cellspacing=1> 16<TR><TD><CODE>-p port</CODE></TD><TD>The port through which the MTA will connect to the filter.</TD></TR> 17<TR><TD><CODE>-t sec</CODE></TD><TD>The timeout value.</TD></TR> 18<TR><TD><CODE>-r addr</CODE></TD><TD>A recipient to reject.</TD></TR> 19<TR><TD><CODE>-a addr</CODE></TD><TD>A recipient to add.</TD></TR> 20</TABLE> 21</CENTER> 22<HR> 23<PRE> 24#include <sys/types.h> 25#include <sys/stat.h> 26#include <errno.h> 27#include <stdio.h> 28#include <stdlib.h> 29#include <string.h> 30#include <sysexits.h> 31#include <unistd.h> 32 33#include "libmilter/mfapi.h" 34 35#ifndef bool 36# define bool int 37# define TRUE 1 38# define FALSE 0 39#endif /* ! bool */ 40 41 42struct mlfiPriv 43{ 44 char *mlfi_fname; 45 char *mlfi_connectfrom; 46 char *mlfi_helofrom; 47 FILE *mlfi_fp; 48}; 49 50#define MLFIPRIV ((struct mlfiPriv *) <A href="smfi_getpriv.html">smfi_getpriv</A>(ctx)) 51 52extern sfsistat mlfi_cleanup(SMFICTX *, bool); 53 54/* recipients to add and reject (set with -a and -r options) */ 55char *add = NULL; 56char *reject = NULL; 57 58sfsistat 59<A href="xxfi_connect.html">mlfi_connect</A>(ctx, hostname, hostaddr) 60 SMFICTX *ctx; 61 char *hostname; 62 _SOCK_ADDR *hostaddr; 63{ 64 struct mlfiPriv *priv; 65 char *ident; 66 67 /* allocate some private memory */ 68 priv = malloc(sizeof *priv); 69 if (priv == NULL) 70 { 71 /* can't accept this message right now */ 72 return SMFIS_TEMPFAIL; 73 } 74 memset(priv, '\0', sizeof *priv); 75 76 /* save the private data */ 77 <A href="smfi_setpriv.html">smfi_setpriv</A>(ctx, priv); 78 79 ident = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "_"); 80 if (ident == NULL) 81 ident = "???"; 82 if ((priv->mlfi_connectfrom = strdup(ident)) == NULL) 83 { 84 (void) mlfi_cleanup(ctx, FALSE); 85 return SMFIS_TEMPFAIL; 86 } 87 88 /* continue processing */ 89 return SMFIS_CONTINUE; 90} 91 92sfsistat 93<A href="xxfi_helo.html">mlfi_helo</A>(ctx, helohost) 94 SMFICTX *ctx; 95 char *helohost; 96{ 97 size_t len; 98 char *tls; 99 char *buf; 100 struct mlfiPriv *priv = MLFIPRIV; 101 102 tls = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{tls_version}"); 103 if (tls == NULL) 104 tls = "No TLS"; 105 if (helohost == NULL) 106 helohost = "???"; 107 len = strlen(tls) + strlen(helohost) + 3; 108 if ((buf = (char*) malloc(len)) == NULL) 109 { 110 (void) mlfi_cleanup(ctx, FALSE); 111 return SMFIS_TEMPFAIL; 112 } 113 snprintf(buf, len, "%s, %s", helohost, tls); 114 if (priv->mlfi_helofrom != NULL) 115 free(priv->mlfi_helofrom); 116 priv->mlfi_helofrom = buf; 117 118 /* continue processing */ 119 return SMFIS_CONTINUE; 120} 121 122sfsistat 123<A href="xxfi_envfrom.html">mlfi_envfrom</A>(ctx, argv) 124 SMFICTX *ctx; 125 char **argv; 126{ 127 int fd = -1; 128 int argc = 0; 129 struct mlfiPriv *priv = MLFIPRIV; 130 char *mailaddr = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{mail_addr}"); 131 132 /* open a file to store this message */ 133 if ((priv->mlfi_fname = strdup("/tmp/msg.XXXXXX")) == NULL) 134 { 135 (void) mlfi_cleanup(ctx, FALSE); 136 return SMFIS_TEMPFAIL; 137 } 138 139 if ((fd = mkstemp(priv->mlfi_fname)) == -1) 140 { 141 (void) mlfi_cleanup(ctx, FALSE); 142 return SMFIS_TEMPFAIL; 143 } 144 145 if ((priv->mlfi_fp = fdopen(fd, "w+")) == NULL) 146 { 147 (void) close(fd); 148 (void) mlfi_cleanup(ctx, FALSE); 149 return SMFIS_TEMPFAIL; 150 } 151 152 /* count the arguments */ 153 while (*argv++ != NULL) 154 ++argc; 155 156 /* log the connection information we stored earlier: */ 157 if (fprintf(priv->mlfi_fp, "Connect from %s (%s)\n\n", 158 priv->mlfi_helofrom, priv->mlfi_connectfrom) == EOF) 159 { 160 (void) mlfi_cleanup(ctx, FALSE); 161 return SMFIS_TEMPFAIL; 162 } 163 /* log the sender */ 164 if (fprintf(priv->mlfi_fp, "FROM %s (%d argument%s)\n", 165 mailaddr ? mailaddr : "???", argc, 166 (argc == 1) ? "" : "s") == EOF) 167 { 168 (void) mlfi_cleanup(ctx, FALSE); 169 return SMFIS_TEMPFAIL; 170 } 171 172 /* continue processing */ 173 return SMFIS_CONTINUE; 174} 175 176sfsistat 177<A href="xxfi_envrcpt.html">mlfi_envrcpt</A>(ctx, argv) 178 SMFICTX *ctx; 179 char **argv; 180{ 181 struct mlfiPriv *priv = MLFIPRIV; 182 char *rcptaddr = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{rcpt_addr}"); 183 int argc = 0; 184 185 /* count the arguments */ 186 while (*argv++ != NULL) 187 ++argc; 188 189 /* log this recipient */ 190 if (reject != NULL && rcptaddr != NULL && 191 (strcasecmp(rcptaddr, reject) == 0)) 192 { 193 if (fprintf(priv->mlfi_fp, "RCPT %s -- REJECTED\n", 194 rcptaddr) == EOF) 195 { 196 (void) mlfi_cleanup(ctx, FALSE); 197 return SMFIS_TEMPFAIL; 198 } 199 return SMFIS_REJECT; 200 } 201 if (fprintf(priv->mlfi_fp, "RCPT %s (%d argument%s)\n", 202 rcptaddr ? rcptaddr : "???", argc, 203 (argc == 1) ? "" : "s") == EOF) 204 { 205 (void) mlfi_cleanup(ctx, FALSE); 206 return SMFIS_TEMPFAIL; 207 } 208 209 /* continue processing */ 210 return SMFIS_CONTINUE; 211} 212 213sfsistat 214<A href="xxfi_header.html">mlfi_header</A>(ctx, headerf, headerv) 215 SMFICTX *ctx; 216 char *headerf; 217 unsigned char *headerv; 218{ 219 /* write the header to the log file */ 220 if (fprintf(MLFIPRIV->mlfi_fp, "%s: %s\n", headerf, headerv) == EOF) 221 { 222 (void) mlfi_cleanup(ctx, FALSE); 223 return SMFIS_TEMPFAIL; 224 } 225 226 /* continue processing */ 227 return SMFIS_CONTINUE; 228} 229 230sfsistat 231<A href="xxfi_eoh.html">mlfi_eoh</A>(ctx) 232 SMFICTX *ctx; 233{ 234 /* output the blank line between the header and the body */ 235 if (fprintf(MLFIPRIV->mlfi_fp, "\n") == EOF) 236 { 237 (void) mlfi_cleanup(ctx, FALSE); 238 return SMFIS_TEMPFAIL; 239 } 240 241 /* continue processing */ 242 return SMFIS_CONTINUE; 243} 244 245sfsistat 246<A href="xxfi_body.html">mlfi_body</A>(ctx, bodyp, bodylen) 247 SMFICTX *ctx; 248 unsigned char *bodyp; 249 size_t bodylen; 250{ 251 struct mlfiPriv *priv = MLFIPRIV; 252 253 /* output body block to log file */ 254 if (fwrite(bodyp, bodylen, 1, priv->mlfi_fp) != 1) 255 { 256 /* write failed */ 257 fprintf(stderr, "Couldn't write file %s: %s\n", 258 priv->mlfi_fname, strerror(errno)); 259 (void) mlfi_cleanup(ctx, FALSE); 260 return SMFIS_TEMPFAIL; 261 } 262 263 /* continue processing */ 264 return SMFIS_CONTINUE; 265} 266 267sfsistat 268<A href="xxfi_eom.html">mlfi_eom</A>(ctx) 269 SMFICTX *ctx; 270{ 271 bool ok = TRUE; 272 273 /* change recipients, if requested */ 274 if (add != NULL) 275 ok = (<A href="smfi_addrcpt.html">smfi_addrcpt</A>(ctx, add) == MI_SUCCESS); 276 return mlfi_cleanup(ctx, ok); 277} 278 279sfsistat 280<A href="xxfi_abort.html">mlfi_abort</A>(ctx) 281 SMFICTX *ctx; 282{ 283 return mlfi_cleanup(ctx, FALSE); 284} 285 286sfsistat 287mlfi_cleanup(ctx, ok) 288 SMFICTX *ctx; 289 bool ok; 290{ 291 sfsistat rstat = SMFIS_CONTINUE; 292 struct mlfiPriv *priv = MLFIPRIV; 293 char *p; 294 char host[512]; 295 char hbuf[1024]; 296 297 if (priv == NULL) 298 return rstat; 299 300 /* close the archive file */ 301 if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF) 302 { 303 /* failed; we have to wait until later */ 304 fprintf(stderr, "Couldn't close archive file %s: %s\n", 305 priv->mlfi_fname, strerror(errno)); 306 rstat = SMFIS_TEMPFAIL; 307 (void) unlink(priv->mlfi_fname); 308 } 309 else if (ok) 310 { 311 /* add a header to the message announcing our presence */ 312 if (gethostname(host, sizeof host) < 0) 313 snprintf(host, sizeof host, "localhost"); 314 p = strrchr(priv->mlfi_fname, '/'); 315 if (p == NULL) 316 p = priv->mlfi_fname; 317 else 318 p++; 319 snprintf(hbuf, sizeof hbuf, "%s@%s", p, host); 320 if (<A href="smfi_addheader.html">smfi_addheader</A>(ctx, "X-Archived", hbuf) != MI_SUCCESS) 321 { 322 /* failed; we have to wait until later */ 323 fprintf(stderr, 324 "Couldn't add header: X-Archived: %s\n", 325 hbuf); 326 ok = FALSE; 327 rstat = SMFIS_TEMPFAIL; 328 (void) unlink(priv->mlfi_fname); 329 } 330 } 331 else 332 { 333 /* message was aborted -- delete the archive file */ 334 fprintf(stderr, "Message aborted. Removing %s\n", 335 priv->mlfi_fname); 336 rstat = SMFIS_TEMPFAIL; 337 (void) unlink(priv->mlfi_fname); 338 } 339 340 /* release private memory */ 341 if (priv->mlfi_fname != NULL) 342 free(priv->mlfi_fname); 343 344 /* return status */ 345 return rstat; 346} 347 348sfsistat 349<A href="xxfi_close.html">mlfi_close</A>(ctx) 350 SMFICTX *ctx; 351{ 352 struct mlfiPriv *priv = MLFIPRIV; 353 354 if (priv == NULL) 355 return SMFIS_CONTINUE; 356 if (priv->mlfi_connectfrom != NULL) 357 free(priv->mlfi_connectfrom); 358 if (priv->mlfi_helofrom != NULL) 359 free(priv->mlfi_helofrom); 360 free(priv); 361 <A href="smfi_setpriv.html">smfi_setpriv</A>(ctx, NULL); 362 return SMFIS_CONTINUE; 363} 364 365sfsistat 366<A href="xxfi_unknown.html">mlfi_unknown</A>(ctx, cmd) 367 SMFICTX *ctx; 368 char *cmd; 369{ 370 return SMFIS_CONTINUE; 371} 372 373sfsistat 374<A href="xxfi_data.html">mlfi_data</A>(ctx) 375 SMFICTX *ctx; 376{ 377 return SMFIS_CONTINUE; 378} 379 380sfsistat 381<A href="xxfi_negotiate.html">mlfi_negotiate</A>(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3) 382 SMFICTX *ctx; 383 unsigned long f0; 384 unsigned long f1; 385 unsigned long f2; 386 unsigned long f3; 387 unsigned long *pf0; 388 unsigned long *pf1; 389 unsigned long *pf2; 390 unsigned long *pf3; 391{ 392 return SMFIS_ALL_OPTS; 393} 394 395struct smfiDesc smfilter = 396{ 397 "SampleFilter", /* filter name */ 398 SMFI_VERSION, /* version code -- do not change */ 399 SMFIF_ADDHDRS|SMFIF_ADDRCPT, 400 /* flags */ 401 <A href="xxfi_connect.html">mlfi_connect</A>, /* connection info filter */ 402 <A href="xxfi_helo.html">mlfi_helo</A>, /* SMTP HELO command filter */ 403 <A href="xxfi_envfrom.html">mlfi_envfrom</A>, /* envelope sender filter */ 404 <A href="xxfi_envrcpt.html">mlfi_envrcpt</A>, /* envelope recipient filter */ 405 <A href="xxfi_header.html">mlfi_header</A>, /* header filter */ 406 <A href="xxfi_eoh.html">mlfi_eoh</A>, /* end of header */ 407 <A href="xxfi_body.html">mlfi_body</A>, /* body block filter */ 408 <A href="xxfi_eom.html">mlfi_eom</A>, /* end of message */ 409 <A href="xxfi_abort.html">mlfi_abort</A>, /* message aborted */ 410 <A href="xxfi_close.html">mlfi_close</A>, /* connection cleanup */ 411 <A href="xxfi_unknown.html">mlfi_unknown</A>, /* unknown SMTP commands */ 412 <A href="xxfi_data.html">mlfi_data</A>, /* DATA command */ 413 <A href="xxfi_negotiate.html">mlfi_negotiate</A> /* Once, at the start of each SMTP connection */ 414}; 415 416static void 417usage(prog) 418 char *prog; 419{ 420 fprintf(stderr, 421 "Usage: %s -p socket-addr [-t timeout] [-r reject-addr] [-a add-addr]\n", 422 prog); 423} 424 425int 426main(argc, argv) 427 int argc; 428 char **argv; 429{ 430 bool setconn = FALSE; 431 int c; 432 const char *args = "p:t:r:a:h"; 433 extern char *optarg; 434 435 /* Process command line options */ 436 while ((c = getopt(argc, argv, args)) != -1) 437 { 438 switch (c) 439 { 440 case 'p': 441 if (optarg == NULL || *optarg == '\0') 442 { 443 (void) fprintf(stderr, "Illegal conn: %s\n", 444 optarg); 445 exit(EX_USAGE); 446 } 447 if (<A href="smfi_setconn.html">smfi_setconn</A>(optarg) == MI_FAILURE) 448 { 449 (void) fprintf(stderr, 450 "smfi_setconn failed\n"); 451 exit(EX_SOFTWARE); 452 } 453 454 /* 455 ** If we're using a local socket, make sure it 456 ** doesn't already exist. Don't ever run this 457 ** code as root!! 458 */ 459 460 if (strncasecmp(optarg, "unix:", 5) == 0) 461 unlink(optarg + 5); 462 else if (strncasecmp(optarg, "local:", 6) == 0) 463 unlink(optarg + 6); 464 setconn = TRUE; 465 break; 466 467 case 't': 468 if (optarg == NULL || *optarg == '\0') 469 { 470 (void) fprintf(stderr, "Illegal timeout: %s\n", 471 optarg); 472 exit(EX_USAGE); 473 } 474 if (<A href="smfi_settimeout.html">smfi_settimeout</A>(atoi(optarg)) == MI_FAILURE) 475 { 476 (void) fprintf(stderr, 477 "smfi_settimeout failed\n"); 478 exit(EX_SOFTWARE); 479 } 480 break; 481 482 case 'r': 483 if (optarg == NULL) 484 { 485 (void) fprintf(stderr, 486 "Illegal reject rcpt: %s\n", 487 optarg); 488 exit(EX_USAGE); 489 } 490 reject = optarg; 491 break; 492 493 case 'a': 494 if (optarg == NULL) 495 { 496 (void) fprintf(stderr, 497 "Illegal add rcpt: %s\n", 498 optarg); 499 exit(EX_USAGE); 500 } 501 add = optarg; 502 smfilter.xxfi_flags |= SMFIF_ADDRCPT; 503 break; 504 505 case 'h': 506 default: 507 usage(argv[0]); 508 exit(EX_USAGE); 509 } 510 } 511 if (!setconn) 512 { 513 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]); 514 usage(argv[0]); 515 exit(EX_USAGE); 516 } 517 if (<A href="smfi_register.html">smfi_register</A>(smfilter) == MI_FAILURE) 518 { 519 fprintf(stderr, "smfi_register failed\n"); 520 exit(EX_UNAVAILABLE); 521 } 522 return <A href="smfi_main.html">smfi_main</A>(); 523} 524 525/* eof */ 526 527</PRE> 528<HR size="1"> 529<FONT size="-1"> 530Copyright (c) 2000-2004, 2006 Proofpoint, Inc. and its suppliers. 531All rights reserved. 532<BR> 533By using this file, you agree to the terms and conditions set 534forth in the LICENSE. 535</FONT> 536</BODY> 537</HTML> 538