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