1<html> 2<head><title>A Sample Filter</title></head> 3<body> 4<!-- 5$Id: sample.html,v 1.18 2004/02/27 00:49:28 msk 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, and rejects a disallowed 11recipient address given with the -r flag. It recognizes the following 12options: 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 365struct smfiDesc smfilter = 366{ 367 "SampleFilter", /* filter name */ 368 SMFI_VERSION, /* version code -- do not change */ 369 SMFIF_ADDHDRS|SMFIF_ADDRCPT, 370 /* flags */ 371 <a href="xxfi_connect.html">mlfi_connect</a>, /* connection info filter */ 372 <a href="xxfi_helo.html">mlfi_helo</a>, /* SMTP HELO command filter */ 373 <a href="xxfi_envfrom.html">mlfi_envfrom</a>, /* envelope sender filter */ 374 <a href="xxfi_envrcpt.html">mlfi_envrcpt</a>, /* envelope recipient filter */ 375 <a href="xxfi_header.html">mlfi_header</a>, /* header filter */ 376 <a href="xxfi_eoh.html">mlfi_eoh</a>, /* end of header */ 377 <a href="xxfi_body.html">mlfi_body</a>, /* body block filter */ 378 <a href="xxfi_eom.html">mlfi_eom</a>, /* end of message */ 379 <a href="xxfi_abort.html">mlfi_abort</a>, /* message aborted */ 380 <a href="xxfi_close.html">mlfi_close</a>, /* connection cleanup */ 381}; 382 383static void 384usage(prog) 385 char *prog; 386{ 387 fprintf(stderr, 388 "Usage: %s -p socket-addr [-t timeout] [-r reject-addr] [-a add-addr]\n", 389 prog); 390} 391 392int 393main(argc, argv) 394 int argc; 395 char **argv; 396{ 397 bool setconn = FALSE; 398 int c; 399 const char *args = "p:t:r:a:h"; 400 extern char *optarg; 401 402 /* Process command line options */ 403 while ((c = getopt(argc, argv, args)) != -1) 404 { 405 switch (c) 406 { 407 case 'p': 408 if (optarg == NULL || *optarg == '\0') 409 { 410 (void) fprintf(stderr, "Illegal conn: %s\n", 411 optarg); 412 exit(EX_USAGE); 413 } 414 if (<a href="smfi_setconn.html">smfi_setconn</a>(optarg) == MI_FAILURE) 415 { 416 (void) fprintf(stderr, 417 "smfi_setconn failed\n"); 418 exit(EX_SOFTWARE); 419 } 420 421 /* 422 ** If we're using a local socket, make sure it 423 ** doesn't already exist. Don't ever run this 424 ** code as root!! 425 */ 426 427 if (strncasecmp(optarg, "unix:", 5) == 0) 428 unlink(optarg + 5); 429 else if (strncasecmp(optarg, "local:", 6) == 0) 430 unlink(optarg + 6); 431 setconn = TRUE; 432 break; 433 434 case 't': 435 if (optarg == NULL || *optarg == '\0') 436 { 437 (void) fprintf(stderr, "Illegal timeout: %s\n", 438 optarg); 439 exit(EX_USAGE); 440 } 441 if (<a href="smfi_settimeout.html">smfi_settimeout</a>(atoi(optarg)) == MI_FAILURE) 442 { 443 (void) fprintf(stderr, 444 "smfi_settimeout failed\n"); 445 exit(EX_SOFTWARE); 446 } 447 break; 448 449 case 'r': 450 if (optarg == NULL) 451 { 452 (void) fprintf(stderr, 453 "Illegal reject rcpt: %s\n", 454 optarg); 455 exit(EX_USAGE); 456 } 457 reject = optarg; 458 break; 459 460 case 'a': 461 if (optarg == NULL) 462 { 463 (void) fprintf(stderr, 464 "Illegal add rcpt: %s\n", 465 optarg); 466 exit(EX_USAGE); 467 } 468 add = optarg; 469 smfilter.xxfi_flags |= SMFIF_ADDRCPT; 470 break; 471 472 case 'h': 473 default: 474 usage(argv[0]); 475 exit(EX_USAGE); 476 } 477 } 478 if (!setconn) 479 { 480 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]); 481 usage(argv[0]); 482 exit(EX_USAGE); 483 } 484 if (<a href="smfi_register.html">smfi_register</a>(smfilter) == MI_FAILURE) 485 { 486 fprintf(stderr, "smfi_register failed\n"); 487 exit(EX_UNAVAILABLE); 488 } 489 return <a href="smfi_main.html">smfi_main</a>(); 490} 491 492/* eof */ 493 494</pre> 495<hr size="1"> 496<font size="-1"> 497Copyright (c) 2000-2004 Sendmail, Inc. and its suppliers. 498All rights reserved. 499<br> 500By using this file, you agree to the terms and conditions set 501forth in the LICENSE. 502</font> 503</body> 504</html> 505