README revision 64562
111394SswallaceThis directory contains the source files for libmilter. 211394Sswallace 311394SswallaceThe sendmail Mail Filter API (Milter) is designed to allow third-party 411394Sswallaceprograms access to mail messages as they are being processed in order to 511394Sswallacefilter meta-information and content. 611394Sswallace 711394SswallaceThis README file describes the steps needed to compile and run a filter, 811394Sswallacethrough reference to a sample filter which is attached at the end of this 911394Sswallacefile. It is necessary to first build libmilter.a, which can be done by 1011394Sswallaceissuing the './Build' command in SRCDIR/libmilter . 1111394Sswallace 1211394SswallaceNOTE: Both libmilter and the callouts in sendmail are marked as an FFR (For 1311394SswallaceFuture Release). If you intend to use them in 8.10.X, you must compiled 1411394Sswallaceboth libmilter and sendmail with -D_FFR_MILTER defined. You can do this by 1511394Sswallaceadding the following to your devtools/Site/site.config.m4 file: 1611394Sswallace 1711394Sswallace dnl Milter 1811394Sswallace APPENDDEF(`conf_sendmail_ENVDEF', `-D_FFR_MILTER=1') 1911394Sswallace APPENDDEF(`conf_libmilter_ENVDEF', `-D_FFR_MILTER=1') 2011394Sswallace 2111394SswallaceYou will also need to define _FFR_MILTER when building your .cf file using 2211394Sswallacem4. 2311394Sswallace 2411394Sswallace+-------------------+ 2511394Sswallace| BUILDING A FILTER | 2611394Sswallace+-------------------+ 2711394Sswallace 2811394SswallaceThe following command presumes that the sample code from the end of this 2911394SswallaceREADME is saved to a file named 'sample.c' and built in the local platform- 3011394Sswallacespecific build subdirectory (SRCDIR/obj.*/libmilter). 3111394Sswallace 3211394Sswallace cc -I../../sendmail -I../../include -o sample sample.c libmilter.a ../libsmutil/libsmutil.a -pthread 3311394Sswallace 3411394SswallaceIt is recommended that you build your filters in a location outside of 3511394Sswallacethe sendmail source tree. Modify the compiler include references (-I) 3611394Sswallaceand the library locations accordingly. Also, some operating systems may 3711394Sswallacerequire additional libraries. For example, SunOS 5.X requires '-lresolv 3811394Sswallace-lsocket -lnsl'. Depending on your OS you may need a library instead 3911394Sswallaceof the option -pthread, e.g., -lpthread. 4011394Sswallace 4111394SswallaceFilters must be thread-safe! Many operating systems now provide support for 4228750SbdePOSIX threads in the standard C libraries. The compiler flag to link with 4328750Sbdethreading support differs according to the compiler and linker used. Check 4428750Sbdethe Makefile in your appropriate obj.*/libmilter build subdirectory if you 4528750Sbdeare unsure of the local flag used. 4611397Sswallace 4712662Sdg 4811394Sswallace+----------------------------------------+ 4911394Sswallace| SPECIFYING FILTERS IN SENDMAIL CONFIGS | 5014331Speter+----------------------------------------+ 5114331Speter 5211394SswallaceFilters are specified with a key letter ``X'' (for ``eXternal''). 5311397Sswallace 5411397SswallaceFor example: 5511397Sswallace 5611397Sswallace Xfilter1, S=local:/var/run/f1.sock, F=R 5711397Sswallace Xfilter2, S=inet6:999@localhost, F=T, T=S:1s;R:1s;E:5m 5811397Sswallace Xfilter3, S=inet:3333@localhost 5911397Sswallace 6011394Sswallacespecifies three filters. Filters can be specified in your .mc file using 6111394Sswallacethe following: 6211394Sswallace 6314464Speter INPUT_MAIL_FILTER(`filter1', `S=local:/var/run/f1.sock, F=R') 6441871Sbde INPUT_MAIL_FILTER(`filter2', `S=inet6:999@localhost, F=T, T=S:1s;R:1s;E:5m') 6511394Sswallace INPUT_MAIL_FILTER(`filter3', `S=inet:3333@localhost') 6611394Sswallace 6711394SswallaceThe first attaches to a Unix-domain socket in the /var/run directory; the 6811394Sswallacesecond uses an IPv6 socket on port 999 of localhost, and the third uses an 6911394SswallaceIPv4 socket on port 3333 of localhost. The current flags (F=) are: 7011394Sswallace 7111394Sswallace R Reject connection if filter unavailable 7211394Sswallace T Temporary fail connection if filter unavailable 7311394Sswallace 7411394SswallaceFinally, you can override the default timeouts used by sendmail when 7511394Sswallacetalking to the filters using the T= equate. There are three fields inside 7611394Sswallaceof the T= equate: 7711394Sswallace 7811394SswallaceLetter Meaning 7911394Sswallace S Timeout for sending information from the MTA to a filter 8011394Sswallace R Timeout for reading reply from the filter 8111394Sswallace E Overall timeout between sending end-of-message to filter 8211394Sswallace and waiting for the final acknowledgment 8311394Sswallace 8411394SswallaceNote the separator between each is a ';' as a ',' already separates equates 8511394Sswallaceand therefore can't separate timeouts. The default values (if not set in the config) are: 8611394Sswallace 8711394SswallaceT=S:10s;R:10s;E:5m 8811394Sswallace 8911394Sswallacewhere 's' is seconds and 'm' is minutes. 9011394Sswallace 9111394SswallaceActual sequencing is handled by the InputMailFilters option which is set 9211394Sswallaceautomatically according to the order of the INPUT_MAIL_FILTER commands 9311394Sswallacein your .mc file. Alternatively, you can reset its value by setting 9420652SbdeconfINPUT_MAIL_FILTERS in your .mc file. This options causes the three 9530994Sphkfilters to be called in the same order they were specified. It allows 9620652Sbdefor possible future filtering on output (although this is not intended 9720652Sbdefor this release). 9811394Sswallace 99Also note that a filter can be defined without adding it to the input 100filter list by using MAIL_FILTER() instead of INPUT_MAIL_FILTER() in your 101.mc file. 102 103To test sendmail with the sample filter, the following might be added (in 104the appropriate locations) to your .mc file: 105 106 INPUT_MAIL_FILTER(`sample', `S=local:/var/run/f1.sock') 107 108 109+------------------+ 110| TESTING A FILTER | 111+------------------+ 112 113Once you have compiled a filter, modified your .mc file and restarted 114the sendmail process, you will want to test that the filter performs as 115intended. 116 117The sample filter takes one argument -p, which indicates the local port 118on which to create a listening socket for the filter. Maintaining 119consistency with the suggested options for sendmail.cf, this would be the 120UNIX domain socket located in /var/run/f1.sock. 121 122 % ./sample -p local:/var/run/f1.sock 123 124If the sample filter returns immediately to a command line, there was either 125an error with your command or a problem creating the specified socket. 126Further logging can be captured through the syslogd daemon. Using the 127'netstat -a' command can ensure that your filter process is listening on 128the appropriate local socket. 129 130Email messages must be injected via SMTP to be filtered. There are two 131simple means of doing this; either using the 'sendmail -bs' command, or 132by telnetting to port 25 of the machine configured for milter. Once 133connected via one of these options, the session can be continued through 134the use of standard SMTP commands. 135 136% sendmail -bs 137220 test.sendmail.com ESMTP Sendmail 8.10.0.Beta8/8.10.0.Beta8; Mon, 6 Dec 1999 19:34:23 -0800 (PST) 138HELO localhost 139250 test.sendmail.com Hello testy@localhost, pleased to meet you 140MAIL From:<testy> 141250 2.1.0 <testy>... Sender ok 142RCPT To:<root> 143250 2.1.5 <root>... Recipient ok 144DATA 145354 Enter mail, end with "." on a line by itself 146From: testy@test.sendmail.com 147To: root@test.sendmail.com 148Subject: testing sample filter 149 150Sample body 151. 152250 2.0.0 dB73Zxi25236 Message accepted for delivery 153QUIT 154221 2.0.0 test.sendmail.com closing connection 155 156In the above example, the lines beginning with numbers are output by the 157mail server, and those without are your input. If everything is working 158properly, you will find a file in /tmp by the name of msg.XXXXXXXX (where 159the Xs represent any combination of letters and numbers). This file should 160contain the message body and headers from the test email entered above. 161 162If the sample filter did not log your test email, there are a number of 163methods to narrow down the source of the problem. Check your system 164logs written by syslogd and see if there are any pertinent lines. You 165may need to reconfigure syslogd to capture all relevant data. Additionally, 166the logging level of sendmail can be raised with the LogLevel option. 167See the sendmail(8) manual page for more information. 168 169 170+--------------------------+ 171| SOURCE FOR SAMPLE FILTER | 172+--------------------------+ 173 174/* A trivial filter that logs all email to a file. */ 175 176#include <sys/types.h> 177#include <stdio.h> 178#include <stdlib.h> 179#include <string.h> 180#include <sysexits.h> 181#include <unistd.h> 182 183#include "libmilter/mfapi.h" 184 185typedef int bool; 186 187#ifndef FALSE 188# define FALSE 0 189#endif /* ! FALSE*/ 190#ifndef TRUE 191# define TRUE 1 192#endif /* ! TRUE*/ 193 194struct mlfiPriv 195{ 196 char *mlfi_fname; 197 FILE *mlfi_fp; 198}; 199 200#define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) 201 202extern sfsistat mlfi_cleanup(SMFICTX *, bool); 203 204sfsistat 205mlfi_envfrom(ctx, envfrom) 206 SMFICTX *ctx; 207 char **envfrom; 208{ 209 struct mlfiPriv *priv; 210 int fd; 211 212 /* allocate some private memory */ 213 priv = malloc(sizeof *priv); 214 if (priv == NULL) 215 { 216 /* can't accept this message right now */ 217 return SMFIS_TEMPFAIL; 218 } 219 memset(priv, '\0', sizeof *priv); 220 221 /* open a file to store this message */ 222 priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX"); 223 if (priv->mlfi_fname == NULL) 224 { 225 free(priv); 226 return SMFIS_TEMPFAIL; 227 } 228 if ((fd = mkstemp(priv->mlfi_fname)) < 0 || 229 (priv->mlfi_fp = fdopen(fd, "w+")) == NULL) 230 { 231 free(priv->mlfi_fname); 232 free(priv); 233 return SMFIS_TEMPFAIL; 234 } 235 236 /* save the private data */ 237 smfi_setpriv(ctx, priv); 238 239 /* continue processing */ 240 return SMFIS_CONTINUE; 241} 242 243sfsistat 244mlfi_header(ctx, headerf, headerv) 245 SMFICTX *ctx; 246 char *headerf; 247 char *headerv; 248{ 249 /* write the header to the log file */ 250 fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv); 251 252 /* continue processing */ 253 return SMFIS_CONTINUE; 254} 255 256sfsistat 257mlfi_eoh(ctx) 258 SMFICTX *ctx; 259{ 260 /* output the blank line between the header and the body */ 261 fprintf(MLFIPRIV->mlfi_fp, "\r\n"); 262 263 /* continue processing */ 264 return SMFIS_CONTINUE; 265} 266 267sfsistat 268mlfi_body(ctx, bodyp, bodylen) 269 SMFICTX *ctx; 270 u_char *bodyp; 271 size_t bodylen; 272{ 273 /* output body block to log file */ 274 if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0) 275 { 276 /* write failed */ 277 (void) mlfi_cleanup(ctx, FALSE); 278 return SMFIS_TEMPFAIL; 279 } 280 281 /* continue processing */ 282 return SMFIS_CONTINUE; 283} 284 285sfsistat 286mlfi_eom(ctx) 287 SMFICTX *ctx; 288{ 289 return mlfi_cleanup(ctx, TRUE); 290} 291 292sfsistat 293mlfi_close(ctx) 294 SMFICTX *ctx; 295{ 296 return SMFIS_ACCEPT; 297} 298 299sfsistat 300mlfi_abort(ctx) 301 SMFICTX *ctx; 302{ 303 return mlfi_cleanup(ctx, FALSE); 304} 305 306sfsistat 307mlfi_cleanup(ctx, ok) 308 SMFICTX *ctx; 309 bool ok; 310{ 311 sfsistat rstat = SMFIS_CONTINUE; 312 struct mlfiPriv *priv = MLFIPRIV; 313 char *p; 314 char host[512]; 315 char hbuf[1024]; 316 317 if (priv == NULL) 318 return rstat; 319 320 /* close the archive file */ 321 if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF) 322 { 323 /* failed; we have to wait until later */ 324 rstat = SMFIS_TEMPFAIL; 325 (void) unlink(priv->mlfi_fname); 326 } 327 else if (ok) 328 { 329 /* add a header to the message announcing our presence */ 330 if (gethostname(host, sizeof host) < 0) 331 strlcpy(host, "localhost", sizeof host); 332 p = strrchr(priv->mlfi_fname, '/'); 333 if (p == NULL) 334 p = priv->mlfi_fname; 335 else 336 p++; 337 snprintf(hbuf, sizeof hbuf, "%s@%s", p, host); 338 smfi_addheader(ctx, "X-Archived", hbuf); 339 } 340 else 341 { 342 /* message was aborted -- delete the archive file */ 343 (void) unlink(priv->mlfi_fname); 344 } 345 346 /* release private memory */ 347 free(priv->mlfi_fname); 348 free(priv); 349 smfi_setpriv(ctx, NULL); 350 351 /* return status */ 352 return rstat; 353} 354 355struct smfiDesc smfilter = 356{ 357 "SampleFilter", /* filter name */ 358 SMFI_VERSION, /* version code -- do not change */ 359 SMFIF_ADDHDRS, /* flags */ 360 NULL, /* connection info filter */ 361 NULL, /* SMTP HELO command filter */ 362 mlfi_envfrom, /* envelope sender filter */ 363 NULL, /* envelope recipient filter */ 364 mlfi_header, /* header filter */ 365 mlfi_eoh, /* end of header */ 366 mlfi_body, /* body block filter */ 367 mlfi_eom, /* end of message */ 368 mlfi_abort, /* message aborted */ 369 mlfi_close /* connection cleanup */ 370}; 371 372 373int 374main(argc, argv) 375 int argc; 376 char *argv[]; 377{ 378 int c; 379 const char *args = "p:"; 380 381 /* Process command line options */ 382 while ((c = getopt(argc, argv, args)) != -1) 383 { 384 switch (c) 385 { 386 case 'p': 387 if (optarg == NULL || *optarg == '\0') 388 { 389 (void) fprintf(stderr, "Illegal conn: %s\n", 390 optarg); 391 exit(EX_USAGE); 392 } 393 (void) smfi_setconn(optarg); 394 break; 395 396 } 397 } 398 if (smfi_register(smfilter) == MI_FAILURE) 399 { 400 fprintf(stderr, "smfi_register failed\n"); 401 exit(EX_UNAVAILABLE); 402 } 403 return smfi_main(); 404} 405 406/* eof */ 407 408$Revision: 8.9.2.1.2.8 $, Last updated $Date: 2000/07/18 15:43:26 $ 409