Back to index

opendkim  2.6.6
miltertest.c
Go to the documentation of this file.
00001 /*
00002 **  Copyright (c) 2009-2011, The OpenDKIM Project.  All rights reserved.
00003 **
00004 **  $Id: miltertest.c,v 1.42 2010/09/23 18:37:32 cm-msk Exp $
00005 */
00006 
00007 #ifndef lint
00008 static char miltertest_c_id[] = "$Id: miltertest.c,v 1.42 2010/09/23 18:37:32 cm-msk Exp $";
00009 #endif /* ! lint */
00010 
00011 #include "build-config.h"
00012 
00013 /* system includes */
00014 #include <sys/types.h>
00015 #include <sys/param.h>
00016 #include <sys/stat.h>
00017 #include <sys/socket.h>
00018 #include <sys/wait.h>
00019 #include <sys/un.h>
00020 #include <sys/uio.h>
00021 #include <sys/time.h>
00022 #include <sys/resource.h>
00023 #include <netinet/in.h>
00024 #include <arpa/inet.h>
00025 #include <sysexits.h>
00026 #include <signal.h>
00027 #include <string.h>
00028 #include <stdlib.h>
00029 #include <fcntl.h>
00030 #include <stdio.h>
00031 #include <errno.h>
00032 #include <assert.h>
00033 #include <unistd.h>
00034 #include <netdb.h>
00035 #include <time.h>
00036 
00037 /* libmilter includes */
00038 #include <libmilter/mfapi.h>
00039 #ifndef SMFI_PROT_VERSION
00040 # define SMFI_PROT_VERSION  SMFI_VERSION
00041 #endif /* ! SMFI_PROT_VERSION */
00042 
00043 /* libopendkim includes */
00044 #include <dkim-strl.h>
00045 #include <dkim.h>
00046 
00047 /* Lua includes */
00048 #include <lua.h>
00049 #include <lualib.h>
00050 #include <lauxlib.h>
00051 
00052 /* types */
00053 #ifndef HAVE_USECONDS_T
00054 typedef unsigned int useconds_t;
00055 #endif /* ! HAVE_USECONDS_T */
00056 
00057 /* macros */
00058 #ifndef FALSE
00059 # define FALSE                     0
00060 #endif /* ! FALSE */
00061 #ifndef TRUE
00062 # define TRUE               1
00063 #endif /* ! TRUE */
00064 
00065 #ifdef SMFIP_NODATA
00066 # define CHECK_MPOPTS(c,o)  (((c)->ctx_mpopts & (o)) != 0)
00067 #else /* SMFIP_NODATA */
00068 # define CHECK_MPOPTS(c,o)  0
00069 #endif /* SMFIP_NODATA */
00070 
00071 #ifndef SMFIP_NR_CONN
00072 # define SMFIP_NR_CONN             0
00073 # define SMFIP_NR_HELO             0
00074 # define SMFIP_NR_MAIL             0
00075 # define SMFIP_NR_RCPT             0
00076 # define SMFIP_NR_DATA             0
00077 # define SMFIP_NR_HDR              0
00078 # define SMFIP_NR_EOH              0
00079 # define SMFIP_NR_BODY             0
00080 # define SMFIP_NR_UNKN             0
00081 #endif /* SMFIP_NR_CONN */
00082 
00083 #define       MT_PRODUCT           "OpenDKIM milter test facility"
00084 #define       MT_VERSION           "1.4.0"
00085 
00086 #define       BUFRSZ               1024
00087 #define       CHUNKSZ                     65536
00088 
00089 #define       CMDLINEOPTS          "D:s:uvVw"
00090 
00091 #define       DEFBODY                     "Dummy message body.\r\n"
00092 #define       DEFCLIENTPORT        12345
00093 #define DEFCLIENTHOST              "test.example.com"
00094 #define DEFCLIENTIP         "12.34.56.78"
00095 #define       DEFHEADERNAME        "From"
00096 #define DEFSENDER           "<sender@example.com>"
00097 #define       DEFTIMEOUT           10
00098 #define DEFRECIPIENT        "<recipient@example.com>"
00099 
00100 #define       STATE_UNKNOWN        (-1)
00101 #define       STATE_INIT           0
00102 #define       STATE_NEGOTIATED     1
00103 #define       STATE_CONNINFO              2
00104 #define       STATE_HELO           3
00105 #define       STATE_ENVFROM        4
00106 #define       STATE_ENVRCPT        5
00107 #define       STATE_DATA           6
00108 #define       STATE_HEADER         7
00109 #define       STATE_EOH            8
00110 #define       STATE_BODY           9
00111 #define       STATE_EOM            10
00112 #define       STATE_DEAD           99
00113 
00114 #define MT_HDRADD           1
00115 #define MT_HDRINSERT        2
00116 #define MT_HDRCHANGE        3
00117 #define MT_HDRDELETE        4
00118 #define MT_RCPTADD          5
00119 #define MT_RCPTDELETE              6
00120 #define MT_BODYCHANGE              7
00121 #define MT_QUARANTINE              8
00122 #define MT_SMTPREPLY        9
00123 
00124 /* prototypes */
00125 int mt_abort(lua_State *);
00126 int mt_bodyfile(lua_State *);
00127 int mt_bodyrandom(lua_State *);
00128 int mt_bodystring(lua_State *);
00129 int mt_chdir(lua_State *);
00130 int mt_connect(lua_State *);
00131 int mt_conninfo(lua_State *);
00132 int mt_data(lua_State *);
00133 int mt_disconnect(lua_State *);
00134 int mt_echo(lua_State *);
00135 int mt_eoh(lua_State *);
00136 int mt_eom(lua_State *);
00137 int mt_eom_check(lua_State *);
00138 int mt_getcwd(lua_State *);
00139 int mt_getheader(lua_State *);
00140 int mt_getreply(lua_State *);
00141 int mt_header(lua_State *);
00142 int mt_helo(lua_State *);
00143 int mt_macro(lua_State *);
00144 int mt_mailfrom(lua_State *);
00145 int mt_negotiate(lua_State *);
00146 int mt_rcptto(lua_State *);
00147 int mt_set_timeout(lua_State *);
00148 int mt_signal(lua_State *);
00149 int mt_sleep(lua_State *);
00150 int mt_startfilter(lua_State *);
00151 int mt_test_action(lua_State *);
00152 int mt_test_option(lua_State *);
00153 int mt_unknown(lua_State *);
00154 
00155 /* data types */
00156 struct mt_eom_request
00157 {
00158        char          eom_request;         /* request code */
00159        size_t        eom_rlen;            /* request length */
00160        char *        eom_rdata;           /* request data */
00161        struct mt_eom_request * eom_next;  /* next request */
00162 };
00163 
00164 struct mt_context
00165 {
00166        char          ctx_response;        /* milter response code */
00167        int           ctx_fd;                     /* descriptor */
00168        int           ctx_state;           /* current state */
00169        unsigned long ctx_mactions;        /* requested actions */
00170        unsigned long ctx_mpopts;          /* requested protocol opts */
00171        struct mt_eom_request * ctx_eomreqs;      /* EOM requests */
00172 };
00173 
00174 struct mt_lua_io
00175 {
00176        _Bool         lua_io_done;
00177        size_t        lua_io_scriptlen;
00178        const char *  lua_io_script;
00179 };
00180 
00181 static const luaL_Reg mt_library[] =
00182 {
00183        { "abort",           mt_abort      },
00184        { "bodyfile",        mt_bodyfile   },
00185        { "bodyrandom",             mt_bodyrandom },
00186        { "bodystring",             mt_bodystring },
00187        { "chdir",           mt_chdir      },
00188        { "connect",         mt_connect    },
00189        { "conninfo",        mt_conninfo   },
00190        { "data",            mt_data              },
00191        { "disconnect",             mt_disconnect },
00192        { "echo",            mt_echo              },
00193        { "eoh",             mt_eoh        },
00194        { "eom",             mt_eom        },
00195        { "eom_check",              mt_eom_check  },
00196        { "getcwd",          mt_getcwd     },
00197        { "getheader",              mt_getheader  },
00198        { "getreply",        mt_getreply   },
00199        { "header",          mt_header     },
00200        { "helo",            mt_helo              },
00201        { "macro",           mt_macro      },
00202        { "mailfrom",        mt_mailfrom   },
00203        { "negotiate",              mt_negotiate  },
00204        { "rcptto",          mt_rcptto     },
00205        { "set_timeout",     mt_set_timeout       },
00206        { "signal",          mt_signal     },
00207        { "sleep",           mt_sleep      },
00208        { "startfilter",     mt_startfilter       },
00209        { "test_action",     mt_test_action       },
00210        { "test_option",     mt_test_option       },
00211        { "unknown",         mt_unknown    },
00212        { NULL,                     NULL          }
00213 };
00214 
00215 /* globals */
00216 _Bool rusage;
00217 _Bool nowait;
00218 int verbose;
00219 unsigned int tmo;
00220 pid_t filterpid;
00221 char scriptbuf[BUFRSZ];
00222 char *progname;
00223 
00224 /*
00225 **  MT_INET_NTOA -- thread-safe inet_ntoa()
00226 **
00227 **  Parameters:
00228 **     a -- (struct in_addr) to be converted
00229 **     buf -- destination buffer
00230 **     buflen -- number of bytes at buf
00231 **
00232 **  Return value:
00233 **     Size of the resultant string.  If the result is greater than buflen,
00234 **     then buf does not contain the complete result.
00235 */
00236 
00237 size_t
00238 mt_inet_ntoa(struct in_addr a, char *buf, size_t buflen)
00239 {
00240        in_addr_t addr;
00241 
00242        assert(buf != NULL);
00243 
00244        addr = ntohl(a.s_addr);
00245 
00246        return snprintf(buf, buflen, "%d.%d.%d.%d",
00247                        (addr >> 24), (addr >> 16) & 0xff,
00248                        (addr >> 8) & 0xff, addr & 0xff);
00249 }
00250 
00251 /*
00252 **  MT_LUA_READER -- "read" a script and make it available to Lua
00253 **
00254 **  Parameters:
00255 **     l -- Lua state
00256 **     data -- pointer to a Lua I/O structure
00257 **     size -- size (returned)
00258 **
00259 **  Return value:
00260 **     Pointer to the data.
00261 */
00262 
00263 const char *
00264 mt_lua_reader(lua_State *l, void *data, size_t *size)
00265 {
00266        struct mt_lua_io *io;
00267 
00268        assert(l != NULL);
00269        assert(data != NULL);
00270        assert(size != NULL);
00271 
00272        io = (struct mt_lua_io *) data;
00273 
00274        if (io->lua_io_done)
00275        {
00276               *size = 0;
00277               return NULL;
00278        }
00279        else if (io->lua_io_script != NULL)
00280        {
00281               io->lua_io_done = TRUE;
00282               *size = io->lua_io_scriptlen;
00283               return io->lua_io_script;
00284        }
00285        else
00286        {
00287               size_t rlen;
00288 
00289               memset(scriptbuf, '\0', sizeof scriptbuf);
00290 
00291               if (feof(stdin))
00292               {
00293                      *size = 0;
00294                      io->lua_io_done = TRUE;
00295                      return NULL;
00296               }
00297 
00298               rlen = fread(scriptbuf, 1, sizeof scriptbuf, stdin);
00299               *size = rlen;
00300               return (const char *) scriptbuf;
00301        }
00302 }
00303 
00304 /*
00305 **  MT_LUA_ALLOC -- allocate memory
00306 **
00307 **  Parameters:
00308 **     ud -- context (not used)
00309 **     ptr -- pointer (for realloc())
00310 **     osize -- old size
00311 **     nsize -- new size
00312 **
00313 **  Return value:
00314 **     Allocated memory, or NULL on failure.
00315 */
00316 
00317 void *
00318 mt_lua_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
00319 {
00320        if (nsize == 0 && osize != 0)
00321        {
00322               free(ptr);
00323               return NULL;
00324        }
00325        else if (nsize != 0 && osize == 0)
00326        {
00327               return malloc(nsize);
00328        }
00329        else
00330        {
00331               return realloc(ptr, nsize);
00332        }
00333 }
00334 
00335 /*
00336 **  MT_FLUSH_EOMREQS -- free EOM requests
00337 **
00338 **  Parameters:
00339 **     ctx -- mt_context handle
00340 **
00341 **  Return value:
00342 **     None.
00343 */
00344 
00345 void
00346 mt_flush_eomreqs(struct mt_context *ctx)
00347 {
00348        struct mt_eom_request *r;
00349 
00350        assert(ctx != NULL);
00351 
00352        while (ctx->ctx_eomreqs != NULL)
00353        {
00354               r = ctx->ctx_eomreqs;
00355               if (r->eom_rdata != NULL)
00356                      free(r->eom_rdata);
00357               ctx->ctx_eomreqs = r->eom_next;
00358               free(r);
00359        }
00360 }
00361 
00362 /*
00363 **  MT_EOM_REQUEST -- record a request received during EOM
00364 **
00365 **  Parameters:
00366 **     ctx -- mt_context handle
00367 **     cmd -- command received
00368 **     len -- length of data
00369 **     data -- data received (i.e. request parameters)
00370 **
00371 **  Return value:
00372 **     TRUE iff addition was successful.
00373 */
00374 
00375 _Bool
00376 mt_eom_request(struct mt_context *ctx, char cmd, size_t len, char *data)
00377 {
00378        struct mt_eom_request *r;
00379 
00380        assert(ctx != NULL);
00381 
00382        r = (struct mt_eom_request *) malloc(sizeof *r);
00383        if (r == NULL)
00384               return FALSE;
00385 
00386        r->eom_request = cmd;
00387        r->eom_rlen = len;
00388        r->eom_rdata = malloc(len);
00389        if (r->eom_rdata == NULL)
00390        {
00391               free(r);
00392               return FALSE;
00393        }
00394        memcpy(r->eom_rdata, data, len);
00395 
00396        r->eom_next = ctx->ctx_eomreqs;
00397        ctx->ctx_eomreqs = r;
00398 
00399        return TRUE;
00400 }
00401 
00402 /*
00403 **  MT_MILTER_READ -- read from a connected filter
00404 **
00405 **  Parameters:
00406 **     fd -- descriptor to which to write
00407 **     cmd -- milter command received (returned)
00408 **     buf -- where to write data
00409 **     buflen -- bytes available at "buf" (updated)
00410 ** 
00411 **  Return value:
00412 **     TRUE iff successful.
00413 */
00414 
00415 _Bool
00416 mt_milter_read(int fd, char *cmd, const char *buf, size_t *len)
00417 {
00418        int i;
00419        int expl;
00420        size_t rlen;
00421        fd_set fds;
00422        struct timeval timeout;
00423        char data[MILTER_LEN_BYTES + 1];
00424 
00425        assert(fd >= 0);
00426 
00427        FD_ZERO(&fds);
00428        FD_SET(fd, &fds);
00429 
00430        timeout.tv_sec = tmo;
00431        timeout.tv_usec = 0;
00432 
00433        i = select(fd + 1, &fds, NULL, NULL, &timeout);
00434        if (i == 0)
00435        {
00436               fprintf(stderr, "%s: select(): timeout on fd %d\n", progname,
00437                       fd);
00438 
00439               return FALSE;
00440        }
00441        else if (i == -1)
00442        {
00443               fprintf(stderr, "%s: select(): fd %d: %s\n", progname, fd,
00444                       strerror(errno));
00445 
00446               return FALSE;
00447        }
00448 
00449        rlen = read(fd, data, sizeof data);
00450        if (rlen != sizeof data)
00451        {
00452               fprintf(stderr, "%s: read(%d): returned %ld, expected %ld\n",
00453                       progname, fd, (long) rlen, (long) sizeof data);
00454 
00455               return FALSE;
00456        }
00457 
00458        *cmd = data[MILTER_LEN_BYTES];
00459        data[MILTER_LEN_BYTES] = '\0';
00460        (void) memcpy(&i, data, MILTER_LEN_BYTES);
00461        expl = ntohl(i) - 1;
00462 
00463        rlen = 0;
00464 
00465        if (expl > 0)
00466        {
00467               rlen = read(fd, (void *) buf, expl);
00468               if (rlen != expl)
00469               {
00470                      fprintf(stderr,
00471                              "%s: read(%d): returned %ld, expected %ld\n",
00472                              progname, fd, (long) rlen, (long) expl);
00473 
00474                      return FALSE;
00475               }
00476        }
00477 
00478        if (verbose > 1)
00479        {
00480               fprintf(stdout, "%s: mt_milter_read(%d): cmd %c, len %ld\n",
00481                       progname, fd, *cmd, (long) rlen);
00482        }
00483 
00484        *len = rlen;
00485 
00486        return (expl == rlen);
00487 }
00488 
00489 /*
00490 **  MT_MILTER_WRITE -- write to a connected filter
00491 **
00492 **  Parameters:
00493 **     fd -- descriptor to which to write
00494 **     cmd -- command to send (an SMFIC_* constant)
00495 **     buf -- command data (or NULL)
00496 **     len -- length of data at "buf"
00497 **
00498 **  Return value:
00499 **     TRUE iff successful.
00500 */
00501 
00502 _Bool
00503 mt_milter_write(int fd, int cmd, const char *buf, size_t len)
00504 {
00505        char command = (char) cmd;
00506        ssize_t sl, i;
00507        int num_vectors;
00508        uint32_t nl;
00509        char data[MILTER_LEN_BYTES + 1];
00510        struct iovec vector[2];
00511 
00512        assert(fd >= 0);
00513 
00514        if (verbose > 1)
00515        {
00516               fprintf(stdout, "%s: mt_milter_write(%d): cmd %c, len %ld\n",
00517                       progname, fd, command, (long) len);
00518        }
00519 
00520        nl = htonl(len + 1);
00521        (void) memcpy(data, (char *) &nl, MILTER_LEN_BYTES);
00522        data[MILTER_LEN_BYTES] = command;
00523        sl = MILTER_LEN_BYTES + 1;
00524 
00525        /* set up the vector for the size / command */
00526        vector[0].iov_base = (void *) data;
00527        vector[0].iov_len  = sl;
00528 
00529        /*
00530        **  Determine if there is command data.  If so, there will be two
00531        **  vectors.  If not, there will be only one.  The vectors are set
00532        **  up here and 'num_vectors' and 'sl' are set appropriately.
00533        */
00534 
00535        if (len <= 0 || buf == NULL)
00536        {
00537               num_vectors = 1;
00538        }
00539        else
00540        {
00541               num_vectors = 2;
00542               sl += len;
00543               vector[1].iov_base = (void *) buf;
00544               vector[1].iov_len  = len;
00545        }
00546 
00547        /* write the vector(s) */
00548        i = writev(fd, vector, num_vectors);
00549        if (i != sl)
00550        {
00551               fprintf(stderr, "%s: writev(%d): returned %ld, expected %ld\n",
00552                       progname, fd, (long) i, (long) sl);
00553        }
00554 
00555        return (i == sl);
00556 }
00557 
00558 /*
00559 **  MT_ASSERT_STATE -- bring a connection up to a given state
00560 **
00561 **  Parameters:
00562 **     ctx -- miltertest context
00563 **     state -- desired state
00564 **
00565 **  Return value:
00566 **     TRUE if successful, FALSE otherwise.
00567 */
00568 
00569 _Bool
00570 mt_assert_state(struct mt_context *ctx, int state)
00571 {
00572        size_t len;
00573        size_t s;
00574        uint16_t port;
00575        char buf[BUFRSZ];
00576 
00577        assert(ctx != NULL);
00578 
00579        if (state >= STATE_NEGOTIATED && ctx->ctx_state < STATE_NEGOTIATED)
00580        {
00581               char rcmd;
00582               size_t buflen;
00583               uint32_t mta_version;
00584               uint32_t mta_protoopts;
00585               uint32_t mta_actions;
00586               uint32_t nvers;
00587               uint32_t npopts;
00588               uint32_t nacts;
00589 
00590               buflen = sizeof buf;
00591 
00592               mta_version = SMFI_PROT_VERSION;
00593               mta_protoopts = SMFI_CURR_PROT;
00594               mta_actions = SMFI_CURR_ACTS;
00595 
00596               nvers = htonl(mta_version);
00597               nacts = htonl(mta_actions);
00598               npopts = htonl(mta_protoopts);
00599 
00600               (void) memcpy(buf, (char *) &nvers, MILTER_LEN_BYTES);
00601               (void) memcpy(buf + MILTER_LEN_BYTES,
00602                             (char *) &nacts, MILTER_LEN_BYTES);
00603               (void) memcpy(buf + (MILTER_LEN_BYTES * 2),
00604                             (char *) &npopts, MILTER_LEN_BYTES);
00605 
00606               if (!mt_milter_write(ctx->ctx_fd, SMFIC_OPTNEG, buf,
00607                                    MILTER_OPTLEN))
00608                      return FALSE;
00609 
00610               if (!mt_milter_read(ctx->ctx_fd, &rcmd, buf, &buflen))
00611                      return FALSE;
00612 
00613               if (rcmd != SMFIC_OPTNEG)
00614               {
00615                      if (verbose > 0)
00616                      {
00617                             fprintf(stdout,
00618                                     "%s: filter returned status %d to option negotiation on fd %d\n", 
00619                                     progname, rcmd, ctx->ctx_fd);
00620                      }
00621 
00622                      ctx->ctx_state = STATE_DEAD;
00623                      return FALSE;
00624               }
00625 
00626               /* decode and store requested protocol steps and actions */
00627               (void) memcpy((char *) &nvers, buf, MILTER_LEN_BYTES);
00628               (void) memcpy((char *) &nacts, buf + MILTER_LEN_BYTES,
00629                             MILTER_LEN_BYTES);
00630               (void) memcpy((char *) &npopts, buf + (MILTER_LEN_BYTES * 2),
00631                             MILTER_LEN_BYTES);
00632 
00633               ctx->ctx_mactions = ntohl(nacts);
00634               ctx->ctx_mpopts = ntohl(npopts);
00635 
00636               ctx->ctx_state = STATE_NEGOTIATED;
00637        }
00638 
00639        if (state >= STATE_CONNINFO && ctx->ctx_state < STATE_CONNINFO)
00640        {
00641               if (!CHECK_MPOPTS(ctx, SMFIP_NOCONNECT))
00642               {
00643                      char rcmd;
00644                      size_t buflen;
00645 
00646                      buflen = sizeof buf;
00647 
00648                      port = htons(DEFCLIENTPORT);
00649                      len = strlcpy(buf, DEFCLIENTHOST, sizeof buf);
00650                      buf[len++] = '\0';
00651                      buf[len++] = '4';           /* IPv4 only for now */
00652                      memcpy(&buf[len], &port, sizeof port);
00653                      len += sizeof port;
00654                      memcpy(&buf[len], DEFCLIENTIP,
00655                             strlen(DEFCLIENTIP) + 1);
00656 
00657                      s = len + strlen(DEFCLIENTIP) + 1;
00658 
00659                      if (!mt_milter_write(ctx->ctx_fd, SMFIC_CONNECT,
00660                                           buf, s))
00661                             return FALSE;
00662 
00663                      rcmd = SMFIR_CONTINUE;
00664 
00665                      if (!CHECK_MPOPTS(ctx, SMFIP_NR_CONN))
00666                      {
00667                             if (!mt_milter_read(ctx->ctx_fd, &rcmd,
00668                                                 buf, &buflen))
00669                                    return FALSE;
00670 
00671                             ctx->ctx_response = rcmd;
00672                      }
00673 
00674                      if (rcmd != SMFIR_CONTINUE)
00675                      {
00676                             if (verbose > 0)
00677                             {
00678                                    fprintf(stdout,
00679                                            "%s: filter returned status %d to connection information on fd %d\n", 
00680                                            progname, rcmd, ctx->ctx_fd);
00681                             }
00682 
00683                             ctx->ctx_state = STATE_DEAD;
00684                      }
00685               }
00686 
00687               ctx->ctx_state = STATE_CONNINFO;
00688        }
00689 
00690        if (state >= STATE_HELO && ctx->ctx_state < STATE_HELO)
00691        {
00692               if (!CHECK_MPOPTS(ctx, SMFIP_NOHELO))
00693               {
00694                      char rcmd;
00695                      size_t buflen;
00696 
00697                      buflen = sizeof buf;
00698 
00699                      len = strlcpy(buf, DEFCLIENTHOST, sizeof buf);
00700                      buf[len++] = '\0';
00701 
00702                      if (!mt_milter_write(ctx->ctx_fd, SMFIC_HELO,
00703                                           buf, len))
00704                             return FALSE;
00705 
00706                      rcmd = SMFIR_CONTINUE;
00707 
00708                      if (!CHECK_MPOPTS(ctx, SMFIP_NR_HELO))
00709                      {
00710                             if (!mt_milter_read(ctx->ctx_fd, &rcmd,
00711                                                 buf, &buflen))
00712                                    return FALSE;
00713 
00714                             ctx->ctx_response = rcmd;
00715                      }
00716 
00717                      if (rcmd != SMFIR_CONTINUE)
00718                      {
00719                             if (verbose > 0)
00720                             {
00721                                    fprintf(stdout,
00722                                            "%s: filter returned status %d to HELO on fd %d\n", 
00723                                            progname, rcmd, ctx->ctx_fd);
00724                             }
00725 
00726                             ctx->ctx_state = STATE_DEAD;
00727                      }
00728               }
00729 
00730               ctx->ctx_state = STATE_HELO;
00731        }
00732 
00733        if (state >= STATE_ENVFROM && ctx->ctx_state < STATE_ENVFROM)
00734        {
00735               if (!CHECK_MPOPTS(ctx, SMFIP_NOMAIL))
00736               {
00737                      char rcmd;
00738                      size_t buflen;
00739 
00740                      buflen = sizeof buf;
00741 
00742                      len = strlcpy(buf, DEFSENDER, sizeof buf);
00743                      buf[len++] = '\0';
00744 
00745                      if (!mt_milter_write(ctx->ctx_fd, SMFIC_MAIL,
00746                                           buf, len))
00747                             return FALSE;
00748 
00749                      rcmd = SMFIR_CONTINUE;
00750 
00751                      if (!CHECK_MPOPTS(ctx, SMFIP_NR_MAIL))
00752                      {
00753                             if (!mt_milter_read(ctx->ctx_fd, &rcmd,
00754                                                 buf, &buflen))
00755                                    return FALSE;
00756 
00757                             ctx->ctx_response = rcmd;
00758                      }
00759 
00760                      if (rcmd != SMFIR_CONTINUE)
00761                      {
00762                             if (verbose > 0)
00763                             {
00764                                    fprintf(stdout,
00765                                            "%s: filter returned status %d to MAIL on fd %d\n", 
00766                                            progname, rcmd, ctx->ctx_fd);
00767                             }
00768 
00769                             ctx->ctx_state = STATE_DEAD;
00770                      }
00771               }
00772 
00773               ctx->ctx_state = STATE_ENVFROM;
00774        }
00775 
00776        if (state >= STATE_ENVRCPT && ctx->ctx_state < STATE_ENVRCPT)
00777        {
00778               if (!CHECK_MPOPTS(ctx, SMFIP_NORCPT))
00779               {
00780                      char rcmd;
00781                      size_t buflen;
00782 
00783                      buflen = sizeof buf;
00784 
00785                      len = strlcpy(buf, DEFRECIPIENT, sizeof buf);
00786                      buf[len++] = '\0';
00787 
00788                      if (!mt_milter_write(ctx->ctx_fd, SMFIC_RCPT,
00789                                           buf, len))
00790                             return FALSE;
00791 
00792                      rcmd = SMFIR_CONTINUE;
00793 
00794                      if ((ctx->ctx_mpopts & SMFIP_NR_RCPT) == 0)
00795                      {
00796                             if (!mt_milter_read(ctx->ctx_fd, &rcmd,
00797                                                 buf, &buflen))
00798                                    return FALSE;
00799 
00800                             ctx->ctx_response = rcmd;
00801                      }
00802 
00803                      if (rcmd != SMFIR_CONTINUE)
00804                      {
00805                             if (verbose > 0)
00806                             {
00807                                    fprintf(stdout,
00808                                            "%s: filter returned status %d to RCPT on fd %d\n", 
00809                                            progname, rcmd, ctx->ctx_fd);
00810                             }
00811 
00812                             ctx->ctx_state = STATE_DEAD;
00813                      }
00814               }
00815 
00816               ctx->ctx_state = STATE_ENVRCPT;
00817        }
00818 
00819        if (state >= STATE_DATA && ctx->ctx_state < STATE_DATA)
00820        {
00821 #ifdef SMFIC_DATA
00822               if (!CHECK_MPOPTS(ctx, SMFIP_NODATA))
00823               {
00824                      char rcmd;
00825                      size_t buflen;
00826 
00827                      buflen = sizeof buf;
00828 
00829                      if (!mt_milter_write(ctx->ctx_fd, SMFIC_DATA, NULL, 0))
00830                             return FALSE;
00831 
00832                      rcmd = SMFIR_CONTINUE;
00833 
00834                      if (!CHECK_MPOPTS(ctx, SMFIP_NR_DATA))
00835                      {
00836                             if (!mt_milter_read(ctx->ctx_fd, &rcmd,
00837                                                 buf, &buflen))
00838                                    return FALSE;
00839 
00840                             ctx->ctx_response = rcmd;
00841                      }
00842 
00843                      if (rcmd != SMFIR_CONTINUE)
00844                      {
00845                             if (verbose > 0)
00846                             {
00847                                    fprintf(stdout,
00848                                            "%s: filter returned status %d to DATA on fd %d\n", 
00849                                            progname, rcmd, ctx->ctx_fd);
00850                             }
00851 
00852                             ctx->ctx_state = STATE_DEAD;
00853                      }
00854               }
00855 #endif /* SMFIC_DATA */
00856 
00857               ctx->ctx_state = STATE_DATA;
00858        }
00859 
00860        if (state >= STATE_HEADER && ctx->ctx_state < STATE_HEADER)
00861        {
00862               if (!CHECK_MPOPTS(ctx, SMFIP_NOHDRS))
00863               {
00864                      char rcmd;
00865                      size_t buflen;
00866 
00867                      buflen = sizeof buf;
00868 
00869                      len = strlcpy(buf, DEFHEADERNAME, sizeof buf);
00870                      buf[len++] = '\0';
00871                      len += strlcpy(buf + len, DEFSENDER, sizeof buf - len);
00872                      buf[len++] = '\0';
00873 
00874                      if (!mt_milter_write(ctx->ctx_fd, SMFIC_HEADER,
00875                                           buf, len))
00876                             return FALSE;
00877 
00878                      rcmd = SMFIR_CONTINUE;
00879 
00880                      if (!CHECK_MPOPTS(ctx, SMFIP_NR_HDR))
00881                      {
00882                             if (!mt_milter_read(ctx->ctx_fd, &rcmd,
00883                                                 buf, &buflen))
00884                                    return FALSE;
00885 
00886                             ctx->ctx_response = rcmd;
00887                      }
00888 
00889                      if (rcmd != SMFIR_CONTINUE)
00890                      {
00891                             if (verbose > 0)
00892                             {
00893                                    fprintf(stdout,
00894                                            "%s: filter returned status %d to header on fd %d\n", 
00895                                            progname, rcmd, ctx->ctx_fd);
00896                             }
00897 
00898                             ctx->ctx_state = STATE_DEAD;
00899                      }
00900               }
00901 
00902               ctx->ctx_state = STATE_HEADER;
00903        }
00904 
00905        if (state >= STATE_EOH && ctx->ctx_state < STATE_EOH)
00906        {
00907               if (!CHECK_MPOPTS(ctx, SMFIP_NOEOH))
00908               {
00909                      char rcmd;
00910                      size_t buflen;
00911 
00912                      buflen = sizeof buf;
00913 
00914                      if (!mt_milter_write(ctx->ctx_fd, SMFIC_EOH, NULL, 0))
00915                             return FALSE;
00916 
00917                      rcmd = SMFIR_CONTINUE;
00918 
00919                      if (!CHECK_MPOPTS(ctx, SMFIP_NR_EOH))
00920                      {
00921                             if (!mt_milter_read(ctx->ctx_fd, &rcmd,
00922                                                 buf, &buflen))
00923                                    return FALSE;
00924 
00925                             ctx->ctx_response = rcmd;
00926                      }
00927 
00928                      if (rcmd != SMFIR_CONTINUE)
00929                      {
00930                             if (verbose > 0)
00931                             {
00932                                    fprintf(stdout,
00933                                            "%s: filter returned status %d to EOH on fd %d\n", 
00934                                            progname, rcmd, ctx->ctx_fd);
00935                             }
00936        
00937                             ctx->ctx_state = STATE_DEAD;
00938                      }
00939               }
00940 
00941               ctx->ctx_state = STATE_EOH;
00942        }
00943 
00944        if (state >= STATE_BODY && ctx->ctx_state < STATE_BODY)
00945        {
00946               if (!CHECK_MPOPTS(ctx, SMFIP_NOBODY))
00947               {
00948                      char rcmd;
00949                      size_t buflen;
00950 
00951                      buflen = sizeof buf;
00952 
00953                      if (!mt_milter_write(ctx->ctx_fd, SMFIC_BODY, DEFBODY,
00954                                           strlen(DEFBODY)))
00955                             return FALSE;
00956 
00957                      rcmd = SMFIR_CONTINUE;
00958 
00959                      if (!CHECK_MPOPTS(ctx, SMFIP_NR_BODY))
00960                      {
00961                             if (!mt_milter_read(ctx->ctx_fd, &rcmd,
00962                                                 buf, &buflen))
00963                                    return FALSE;
00964 
00965                             ctx->ctx_response = rcmd;
00966                      }
00967 
00968                      if (rcmd != SMFIR_CONTINUE)
00969                      {
00970                             if (verbose > 0)
00971                             {
00972                                    fprintf(stdout,
00973                                            "%s: filter returned status %d to body on fd %d\n", 
00974                                            progname, rcmd, ctx->ctx_fd);
00975                             }
00976        
00977                             ctx->ctx_state = STATE_DEAD;
00978                      }
00979               }
00980 
00981               ctx->ctx_state = STATE_BODY;
00982        }
00983 
00984        return TRUE;
00985 }
00986 
00987 /*
00988 **  MT_ECHO -- echo a string
00989 **
00990 **  Parameters:
00991 **     l -- Lua state
00992 **
00993 **  Return value:
00994 **     nil (on the Lua stack)
00995 */
00996 
00997 int
00998 mt_echo(lua_State *l)
00999 {
01000        char *str;
01001 
01002        assert(l != NULL);
01003 
01004        if (lua_gettop(l) != 1 || !lua_isstring(l, 1))
01005        {
01006               lua_pushstring(l, "mt.echo(): Invalid argument");
01007               lua_error(l);
01008        }
01009 
01010        str = (char *) lua_tostring(l, 1);
01011        lua_pop(l, 1);
01012 
01013        fprintf(stdout, "%s\n", str);
01014 
01015        return 0;
01016 }
01017 
01018 /*
01019 **  MT_CHDIR -- change working directory
01020 **
01021 **  Parameters:
01022 **     l -- Lua state
01023 **
01024 **  Return value:
01025 **     nil (on the Lua stack)
01026 */
01027 
01028 int
01029 mt_chdir(lua_State *l)
01030 {
01031        char *str;
01032 
01033        assert(l != NULL);
01034 
01035        if (lua_gettop(l) != 1 || !lua_isstring(l, 1))
01036        {
01037               lua_pushstring(l, "mt.chdir(): Invalid argument");
01038               lua_error(l);
01039        }
01040 
01041        str = (char *) lua_tostring(l, 1);
01042        lua_pop(l, 1);
01043 
01044        if (chdir(str) != 0)
01045        {
01046               lua_pushfstring(l, "mt.chdir(): %s: %s", str, strerror(errno));
01047               lua_error(l);
01048        }
01049 
01050        if (verbose > 1)
01051               fprintf(stderr, "%s: now in directory %s\n", progname, str);
01052 
01053        return 0;
01054 }
01055 
01056 /*
01057 **  MT_GETCWD -- get current working directory
01058 **
01059 **  Parameters:
01060 **     l -- Lua state
01061 **
01062 **  Return value:
01063 **     String containing current working directory.
01064 */
01065 
01066 int
01067 mt_getcwd(lua_State *l)
01068 {
01069        char dir[MAXPATHLEN + 1];
01070 
01071        assert(l != NULL);
01072 
01073        if (lua_gettop(l) != 0)
01074        {
01075               lua_pushstring(l, "mt.getcwd(): Invalid argument");
01076               lua_error(l);
01077        }
01078 
01079        memset(dir, '\0', sizeof dir);
01080 
01081        if (getcwd(dir, MAXPATHLEN) == NULL)
01082        {
01083               lua_pushstring(l, "mt.getcwd(): getcwd returned error");
01084               lua_error(l);
01085        }
01086 
01087        lua_pushstring(l, dir);
01088 
01089        return 1;
01090 }
01091 
01092 /*
01093 **  MT_SET_TIMEOUT -- set read timeout
01094 **
01095 **  Parameters:
01096 **     l -- Lua state
01097 **
01098 **  Return value:
01099 **     nil (on the Lua stack)
01100 */
01101 
01102 int
01103 mt_set_timeout(lua_State *l)
01104 {
01105        assert(l != NULL);
01106 
01107        if (lua_gettop(l) != 1 || !lua_isnumber(l, 1))
01108        {
01109               lua_pushstring(l, "mt.set_timeout(): Invalid argument");
01110               lua_error(l);
01111        }
01112 
01113        tmo = (unsigned int) lua_tonumber(l, 1);
01114        lua_pop(l, 1);
01115 
01116        return 0;
01117 }
01118 
01119 /*
01120 **  MT_STARTFILTER -- start a filter
01121 **
01122 **  Parameters:
01123 **     l -- Lua state
01124 **
01125 **  Return value:
01126 **     nil (on the Lua stack)
01127 */
01128 
01129 int
01130 mt_startfilter(lua_State *l)
01131 {
01132        const char **argv;
01133        int c;
01134        int status;
01135        int args;
01136        int fds[2];
01137        pid_t child;
01138 
01139        assert(l != NULL);
01140 
01141        args = lua_gettop(l);
01142        if (args < 1)
01143        {
01144               lua_pushstring(l, "mt.startfilter(): Invalid argument");
01145               lua_error(l);
01146        }
01147 
01148        for (c = 1; c <= args; c++)
01149        {
01150               if (!lua_isstring(l, c))
01151               {
01152                      lua_pushstring(l,
01153                                     "mt.startfilter(): Invalid argument");
01154                      lua_error(l);
01155               }
01156        }
01157 
01158        argv = (const char **) malloc(sizeof(char *) * (args + 1));
01159        if (argv == NULL)
01160        {
01161               lua_pushfstring(l, "mt.startfilter(): malloc(): %s",
01162                               strerror(errno));
01163               lua_error(l);
01164        }
01165 
01166        for (c = 1; c <= args; c++)
01167               argv[c - 1] = lua_tostring(l, c);
01168        argv[c - 1] = NULL;
01169        lua_pop(l, c);
01170 
01171        if (pipe(fds) != 0)
01172        {
01173               lua_pushfstring(l, "mt.startfilter(): pipe(): %s",
01174                               strerror(errno));
01175               lua_error(l);
01176        }
01177 
01178        if (fcntl(fds[1], F_SETFD, FD_CLOEXEC) != 0)
01179        {
01180               lua_pushfstring(l, "mt.startfilter(): fcntl(): %s",
01181                               strerror(errno));
01182               lua_error(l);
01183        }
01184 
01185        child = fork();
01186        switch (child)
01187        {
01188          case -1:
01189               lua_pushfstring(l, "mt.startfilter(): fork(): %s",
01190                               strerror(errno));
01191               lua_error(l);
01192 
01193          case 0:
01194               close(fds[0]);
01195               execv(argv[0], (char * const *) argv);
01196               exit(1);
01197 
01198          default:
01199               close(fds[1]);
01200 
01201               c = read(fds[0], &args, sizeof(args));
01202               if (c == -1)
01203               {
01204                      lua_pushfstring(l, "mt.startfilter(): read(): %s",
01205                                      strerror(errno));
01206                      lua_error(l);
01207               }
01208               else if (c != 0)
01209               {
01210                      lua_pushfstring(l,
01211                                      "mt.startfilter(): read(): got %d, expecting 0",
01212                                      c);
01213                      lua_error(l);
01214               }
01215 
01216               close(fds[0]);
01217 
01218               filterpid = child;
01219 
01220               child = wait4(filterpid, &status, WNOHANG, NULL);
01221               if (child != 0)
01222               {
01223                      lua_pushfstring(l,
01224                                      "mt.startfilter(): wait4(): child %d exited prematurely, status %d",
01225                                      child, status);
01226                      lua_error(l);
01227               }
01228 
01229               if (verbose > 0)
01230               {
01231                      fprintf(stderr, "%s: '%s' started in process %d\n",
01232                              progname, argv[0], filterpid);
01233               }
01234 
01235               free((void *) argv);
01236 
01237               break;
01238        }
01239 
01240        lua_pushnil(l);
01241 
01242        return 1;
01243 }
01244 
01245 /*
01246 **  MT_SIGNAL -- signal a filter
01247 **
01248 **  Parameters:
01249 **     l -- Lua state
01250 **
01251 **  Return value:
01252 **     nil (on the Lua stack)
01253 */
01254 
01255 int
01256 mt_signal(lua_State *l)
01257 {
01258        int signum;
01259 
01260        assert(l != NULL);
01261 
01262        if (lua_gettop(l) != 1 || !lua_isnumber(l, 1))
01263        {
01264               lua_pushstring(l, "mt.signal(): Invalid argument");
01265               lua_error(l);
01266        }
01267 
01268        signum = lua_tonumber(l, 1);
01269        lua_pop(l, 1);
01270 
01271        if (filterpid <= 1)
01272        {
01273               lua_pushstring(l, "mt.signal(): Filter not running");
01274               lua_error(l);
01275        }
01276 
01277        if (kill(filterpid, signum) != 0)
01278        {
01279               lua_pushfstring(l, "mt.signal(): kill(): %s", strerror(errno));
01280               lua_error(l);
01281        }
01282 
01283        if (verbose > 0)
01284               fprintf(stderr, "%s: sent signal %d\n", progname, signum);
01285 
01286        lua_pushnil(l);
01287 
01288        return 1;
01289 }
01290 
01291 /*
01292 **  MT_CONNECT -- connect to a filter, returning a handle
01293 **
01294 **  Parameters:
01295 **     l -- Lua state
01296 **
01297 **  Return value:
01298 **     A new connection handle (on the Lua stack).
01299 */
01300 
01301 int
01302 mt_connect(lua_State *l)
01303 {
01304        int top;
01305        int af;
01306        int fd = -1;
01307        int saverr = 0;
01308        u_int count = 1;
01309        useconds_t interval = 0;
01310        char *at;
01311        char *p;
01312        const char *sockinfo;
01313        struct mt_context *new;
01314 
01315        assert(l != NULL);
01316 
01317        top = lua_gettop(l);
01318 
01319        if (!(top == 1 && lua_isstring(l, 1)) &&
01320            !(top == 3 && lua_isstring(l, 1) && lua_isnumber(l, 2) &&
01321                          lua_isnumber(l, 3)))
01322        {
01323               lua_pushstring(l, "mt.connect(): Invalid argument");
01324               lua_error(l);
01325        }
01326 
01327        sockinfo = lua_tostring(l, 1);
01328        if (top == 3)
01329        {
01330               count = (u_int) lua_tonumber(l, 2);
01331               interval = (useconds_t) (1000000. * lua_tonumber(l, 3));
01332        }
01333        lua_pop(l, top);
01334 
01335        af = AF_UNSPEC;
01336        p = strchr(sockinfo, ':');
01337        if (p == NULL)
01338        {
01339               af = AF_UNIX;
01340        }
01341        else
01342        {
01343               *p = '\0';
01344               if (strcasecmp(sockinfo, "inet") == 0)
01345                      af = AF_INET;
01346               else if (strcasecmp(sockinfo, "unix") == 0 ||
01347                        strcasecmp(sockinfo, "local") == 0)
01348                      af = AF_UNIX;
01349               *p = ':';
01350        }
01351 
01352        if (af == AF_UNSPEC)
01353        {
01354               lua_pushstring(l, "mt.connect(): Invalid argument");
01355               lua_error(l);
01356        }
01357 
01358        switch (af)
01359        {
01360          case AF_UNIX:
01361          {
01362               struct sockaddr_un sa;
01363 
01364               memset(&sa, '\0', sizeof sa);
01365               sa.sun_family = AF_UNIX;
01366 #ifdef HAVE_SUN_LEN
01367               sa.sun_len = sizeof sa;
01368 #endif /* HAVE_SUN_LEN */
01369               strlcpy(sa.sun_path, p + 1, sizeof sa.sun_path);
01370 
01371               fd = socket(PF_UNIX, SOCK_STREAM, 0);
01372               if (fd < 0)
01373               {
01374                      lua_pushfstring(l, "mt.connect(): socket(): %s",
01375                                      strerror(errno));
01376                      lua_error(l);
01377               }
01378 
01379               while (count > 0)
01380               {
01381                      saverr = 0;
01382 
01383                      if (connect(fd, (struct sockaddr *) &sa,
01384                                  sizeof sa) == 0)
01385                             break;
01386 
01387                      saverr = errno;
01388 
01389                      if (verbose > 1)
01390                      {
01391                             fprintf(stdout,
01392                                     "%s: connect(): %s; %u tr%s left\n",
01393                                     progname, strerror(errno), count - 1,
01394                                     count == 2 ? "y" : "ies");
01395                      }
01396 
01397                      usleep(interval);
01398 
01399                      count--;
01400               }
01401 
01402               if (saverr != 0)
01403               {
01404                      lua_pushfstring(l, "mt.connect(): %s: connect(): %s",
01405                                      sockinfo, strerror(errno));
01406                      lua_error(l);
01407               }
01408 
01409               break;
01410          }
01411 
01412          case AF_INET:
01413          {
01414               struct servent *srv;
01415               struct sockaddr_in sa;
01416 
01417               memset(&sa, '\0', sizeof sa);
01418               sa.sin_family = AF_INET;
01419 
01420               p++;
01421 
01422               at = strchr(p, '@');
01423               if (at == NULL)
01424               {
01425                      sa.sin_addr.s_addr = INADDR_ANY;
01426               }
01427               else
01428               {
01429                      struct hostent *h;
01430 
01431                      *at = '\0';
01432 
01433                      h = gethostbyname(at + 1);
01434                      if (h != NULL)
01435                      {
01436                             memcpy(&sa.sin_addr.s_addr, h->h_addr,
01437                                    sizeof sa.sin_addr.s_addr);
01438                      }
01439                      else
01440                      {
01441                             sa.sin_addr.s_addr = inet_addr(at + 1);
01442                      }
01443               }
01444 
01445               srv = getservbyname(p, "tcp");
01446               if (srv != NULL)
01447               {
01448                      sa.sin_port = srv->s_port;
01449               }
01450               else
01451               {
01452                      int port;
01453                      char *q;
01454 
01455                      port = strtoul(p, &q, 10);
01456                      if (*q != '\0')
01457                      {
01458                             lua_pushstring(l,
01459                                            "mt.connect(): Invalid argument");
01460                             lua_error(l);
01461                      }
01462 
01463                      sa.sin_port = htons(port);
01464               }
01465 
01466               if (at != NULL)
01467                      *at = '@';
01468 
01469               fd = socket(PF_INET, SOCK_STREAM, 0);
01470               if (fd < 0)
01471               {
01472                      lua_pushfstring(l, "mt.connect(): socket(): %s",
01473                                      strerror(errno));
01474                      lua_error(l);
01475               }
01476 
01477               while (count > 0)
01478               {
01479                      saverr = 0;
01480 
01481                      if (connect(fd, (struct sockaddr *) &sa,
01482                                  sizeof sa) == 0)
01483                             break;
01484 
01485                      saverr = errno;
01486 
01487                      if (verbose > 1)
01488                      {
01489                             fprintf(stdout,
01490                                     "%s: connect(): %s; %u tr%s left\n",
01491                                     progname, strerror(errno), count - 1,
01492                                     count == 2 ? "y" : "ies");
01493                      }
01494 
01495                      usleep(interval);
01496 
01497                      count--;
01498               }
01499 
01500               if (saverr != 0)
01501               {
01502                      lua_pushfstring(l, "mt.connect(): %s: connect(): %s",
01503                                      sockinfo, strerror(errno));
01504                      lua_error(l);
01505               }
01506 
01507               break;
01508          }
01509 
01510          default:
01511               assert(0);
01512        }
01513 
01514        new = (struct mt_context *) malloc(sizeof *new);
01515        if (new == NULL)
01516        {
01517               lua_pushfstring(l, "mt.connect(): malloc(): %s",
01518                               strerror(errno));
01519               lua_error(l);
01520        }
01521 
01522        new->ctx_state = STATE_INIT;
01523        new->ctx_fd = fd;
01524        new->ctx_response = '\0';
01525        new->ctx_eomreqs = NULL;
01526        new->ctx_mactions = 0;
01527        new->ctx_mpopts = 0;
01528 
01529        lua_pushlightuserdata(l, new);
01530 
01531        if (verbose > 0)
01532        {
01533               fprintf(stdout, "%s: connected to '%s', fd %d\n",
01534                       progname, sockinfo, fd);
01535        }
01536 
01537        return 1;
01538 }
01539 
01540 /*
01541 **  MT_SLEEP -- sleep
01542 **
01543 **  Parameters:
01544 **     l -- Lua state
01545 **
01546 **  Return value:
01547 **     nil (on the Lua stack)
01548 */
01549 
01550 int
01551 mt_sleep(lua_State *l)
01552 {
01553        double p;
01554        useconds_t usecs;
01555 
01556        assert(l != NULL);
01557 
01558        if (lua_gettop(l) != 1 ||
01559            !lua_isnumber(l, 1))
01560        {
01561               lua_pushstring(l, "mt.sleep(): Invalid argument");
01562               lua_error(l);
01563        }
01564 
01565        p = lua_tonumber(l, 1);
01566        usecs = (useconds_t) (1000000. * p);
01567        lua_pop(l, 1);
01568 
01569        if (verbose > 1)
01570        {
01571               fprintf(stdout, "%s: pausing for %f second%s\n",
01572                       progname, p, usecs == 1000000 ? "" : "s");
01573        }
01574 
01575        usleep(usecs);
01576 
01577        lua_pushnil(l);
01578 
01579        return 1;
01580 }
01581 
01582 /*
01583 **  MT_DISCONNECT -- disconnect from a filter
01584 **
01585 **  Parameters:
01586 **     l -- Lua state
01587 **
01588 **  Return value:
01589 **     nil (on the Lua stack)
01590 */
01591 
01592 int
01593 mt_disconnect(lua_State *l)
01594 {
01595        struct mt_context *ctx;
01596 
01597        assert(l != NULL);
01598 
01599        if (lua_gettop(l) != 1 ||
01600            !lua_islightuserdata(l, 1))
01601        {
01602               lua_pushstring(l, "mt.disconnect(): Invalid argument");
01603               lua_error(l);
01604        }
01605 
01606        ctx = (struct mt_context *) lua_touserdata(l, 1);
01607        lua_pop(l, 1);
01608 
01609        (void) mt_milter_write(ctx->ctx_fd, SMFIC_QUIT, NULL, 0);
01610 
01611        (void) close(ctx->ctx_fd);
01612 
01613        if (verbose > 0)
01614        {
01615               fprintf(stdout, "%s: disconnected fd %d\n",
01616                       progname, ctx->ctx_fd);
01617        }
01618 
01619        free(ctx);
01620 
01621        lua_pushnil(l);
01622 
01623        return 1;
01624 }
01625 
01626 /*
01627 **  MT_TEST_ACTION -- send an action bit
01628 **
01629 **  Parameters:
01630 **     l -- Lua state
01631 **
01632 **  Return value:
01633 **     Boolean (true/false)
01634 */
01635 
01636 int
01637 mt_test_action(lua_State *l)
01638 {
01639        struct mt_context *ctx;
01640        unsigned long action;
01641 
01642        assert(l != NULL);
01643 
01644        if (lua_gettop(l) != 2 ||
01645            !lua_islightuserdata(l, 1) ||
01646            !lua_isnumber(l, 2))
01647        {
01648               lua_pushstring(l, "mt.test_action(): Invalid argument");
01649               lua_error(l);
01650        }
01651 
01652        ctx = (struct mt_context *) lua_touserdata(l, 1);
01653        action = lua_tonumber(l, 2);
01654        lua_pop(l, 2);
01655 
01656        if (!mt_assert_state(ctx, STATE_NEGOTIATED))
01657               lua_error(l);
01658 
01659        lua_pushboolean(l, (ctx->ctx_mactions & action) != 0);
01660 
01661        return 1;
01662 }
01663 
01664 /*
01665 **  MT_TEST_OPTION -- send a protocol option bit
01666 **
01667 **  Parameters:
01668 **     l -- Lua state
01669 **
01670 **  Return value:
01671 **     Boolean (true/false)
01672 */
01673 
01674 int
01675 mt_test_option(lua_State *l)
01676 {
01677        unsigned long option;
01678        struct mt_context *ctx;
01679 
01680        assert(l != NULL);
01681 
01682        if (lua_gettop(l) != 2 ||
01683            !lua_islightuserdata(l, 1) ||
01684            !lua_isnumber(l, 2))
01685        {
01686               lua_pushstring(l, "mt.test_option(): Invalid argument");
01687               lua_error(l);
01688        }
01689 
01690        ctx = (struct mt_context *) lua_touserdata(l, 1);
01691        option = lua_tonumber(l, 2);
01692        lua_pop(l, 2);
01693 
01694        if (!mt_assert_state(ctx, STATE_NEGOTIATED))
01695               lua_error(l);
01696 
01697        lua_pushboolean(l, (ctx->ctx_mpopts & option) != 0);
01698 
01699        return 1;
01700 }
01701 
01702 /*
01703 **  MT_NEGOTIATE -- option negotiation
01704 **
01705 **  Parameters:
01706 **     l -- Lua state
01707 **
01708 **  Return value:
01709 **     nil (on the Lua stack)
01710 */
01711 
01712 int
01713 mt_negotiate(lua_State *l)
01714 {
01715        char rcmd;
01716        size_t buflen;
01717        uint32_t mta_version;
01718        uint32_t mta_protoopts;
01719        uint32_t mta_actions;
01720        uint32_t nvers;
01721        uint32_t npopts;
01722        uint32_t nacts;
01723        struct mt_context *ctx;
01724        char buf[BUFRSZ];
01725 
01726        if (lua_gettop(l) != 4 ||
01727            !lua_islightuserdata(l, 1) ||
01728            (!lua_isnil(l, 2) && !lua_isnumber(l, 2)) ||
01729            (!lua_isnil(l, 3) && !lua_isnumber(l, 3)) ||
01730            (!lua_isnil(l, 4) && !lua_isnumber(l, 4)))
01731        {
01732               lua_pushstring(l, "mt.negotiate(): Invalid argument");
01733               lua_error(l);
01734        }
01735 
01736        ctx = (struct mt_context *) lua_touserdata(l, 1);
01737 
01738        buflen = sizeof buf;
01739 
01740        if (lua_isnumber(l, 2))
01741               mta_version = lua_tonumber(l, 2);
01742        else
01743               mta_version = SMFI_PROT_VERSION;
01744 
01745        if (lua_isnumber(l, 3))
01746               mta_protoopts = lua_tonumber(l, 3);
01747        else
01748               mta_protoopts = SMFI_CURR_PROT;
01749 
01750        if (lua_isnumber(l, 4))
01751               mta_actions = lua_tonumber(l, 4);
01752        else
01753               mta_actions = SMFI_CURR_ACTS;
01754 
01755        lua_pop(l, lua_gettop(l));
01756 
01757        nvers = htonl(mta_version);
01758        nacts = htonl(mta_actions);
01759        npopts = htonl(mta_protoopts);
01760 
01761        (void) memcpy(buf, (char *) &nvers, MILTER_LEN_BYTES);
01762        (void) memcpy(buf + MILTER_LEN_BYTES,
01763                      (char *) &nacts, MILTER_LEN_BYTES);
01764        (void) memcpy(buf + (MILTER_LEN_BYTES * 2),
01765                      (char *) &npopts, MILTER_LEN_BYTES);
01766 
01767        if (!mt_milter_write(ctx->ctx_fd, SMFIC_OPTNEG, buf, MILTER_OPTLEN))
01768        {
01769               lua_pushstring(l, "mt.milter_write() failed");
01770               return 1;
01771        }
01772 
01773        buflen = sizeof buf;
01774 
01775        if (!mt_milter_read(ctx->ctx_fd, &rcmd, buf, &buflen))
01776        {
01777               lua_pushstring(l, "mt.milter_read() failed");
01778               return 1;
01779        }
01780 
01781        if (rcmd != SMFIC_OPTNEG)
01782        {
01783               if (verbose > 0)
01784               {
01785                      fprintf(stdout,
01786                              "%s: filter returned status %d to option negotiation on fd %d\n", 
01787                              progname, rcmd, ctx->ctx_fd);
01788               }
01789 
01790               ctx->ctx_state = STATE_DEAD;
01791 
01792               lua_pushnil(l);
01793               return 1;
01794        }
01795 
01796        ctx->ctx_response = rcmd;
01797        ctx->ctx_state = STATE_NEGOTIATED;
01798 
01799        /* decode and store requested protocol steps and actions */
01800        (void) memcpy((char *) &nvers, buf, MILTER_LEN_BYTES);
01801        (void) memcpy((char *) &nacts, buf + MILTER_LEN_BYTES,
01802                      MILTER_LEN_BYTES);
01803        (void) memcpy((char *) &npopts, buf + (MILTER_LEN_BYTES * 2),
01804                      MILTER_LEN_BYTES);
01805 
01806        ctx->ctx_mactions = ntohl(nacts);
01807        ctx->ctx_mpopts = ntohl(npopts);
01808 
01809        if (verbose > 0)
01810        {
01811               fprintf(stdout,
01812                       "%s: option negotiation sent on fd %d, reply '%c'\n",
01813                       progname, ctx->ctx_fd, rcmd);
01814        }
01815 
01816        lua_pushnil(l);
01817        return 1;
01818 }
01819 
01820 /*
01821 **  MT_MACRO -- send a macro
01822 **
01823 **  Parameters:
01824 **     l -- Lua state
01825 **
01826 **  Return value:
01827 **     nil (on the Lua stack)
01828 */
01829 
01830 int
01831 mt_macro(lua_State *l)
01832 {
01833        int type;
01834        int top;
01835        int n = 0;
01836        int c;
01837        size_t s;
01838        struct mt_context *ctx;
01839        char *bp;
01840        char *name;
01841        char *value;
01842        char buf[BUFRSZ];
01843 
01844        assert(l != NULL);
01845 
01846        top = lua_gettop(l);
01847 
01848        if (top < 4 ||
01849            !lua_islightuserdata(l, 1) ||
01850            !lua_isnumber(l, 2) ||
01851            !lua_isstring(l, 3) ||
01852            !lua_isstring(l, 4))
01853        {
01854               lua_pushstring(l, "mt.macro(): Invalid argument");
01855               lua_error(l);
01856        }
01857 
01858        ctx = (struct mt_context *) lua_touserdata(l, 1);
01859        type = lua_tonumber(l, 2);
01860 
01861        if (!mt_assert_state(ctx, STATE_NEGOTIATED))
01862               lua_error(l);
01863 
01864        s = 1;
01865        buf[0] = type;
01866        bp = buf + 1;
01867 
01868        for (c = 3; c < top; c += 2)
01869        {
01870               if (c + 1 > top ||
01871                   !lua_isstring(l, c) ||
01872                   !lua_isstring(l, c + 1))
01873               {
01874                      lua_pop(l, top);
01875                      lua_pushstring(l, "mt.macro(): Invalid argument");
01876                      lua_error(l);
01877               }
01878 
01879               name = (char *) lua_tostring(l, c);
01880               value = (char *) lua_tostring(l, c + 1);
01881 
01882               if (strlen(name) + strlen(value) + 2 + bp > buf + sizeof buf)
01883               {
01884                      lua_pop(l, top);
01885                      lua_pushstring(l, "mt.macro(): Buffer overflow");
01886                      lua_error(l);
01887               }
01888 
01889               memcpy(bp, name, strlen(name) + 1);
01890               bp += strlen(name) + 1;
01891               memcpy(bp, value, strlen(value) + 1);
01892               bp += strlen(value) + 1;
01893               s += strlen(name) + 1 + strlen(value) + 1;
01894               n++;
01895        }
01896 
01897        lua_pop(l, top);
01898 
01899        if (!mt_milter_write(ctx->ctx_fd, SMFIC_MACRO, buf, s))
01900        {
01901               lua_pushstring(l, "mt.milter_write() failed");
01902               return 1;
01903        }
01904 
01905        if (verbose > 0)
01906        {
01907               fprintf(stdout, "%s: %d '%c' macro(s) sent on fd %d\n",
01908                       progname, n, type, ctx->ctx_fd);
01909        }
01910 
01911        lua_pushnil(l);
01912 
01913        return 1;
01914 }
01915 
01916 /*
01917 **  MT_CONNINFO -- send connection information
01918 **
01919 **  Parameters:
01920 **     l -- Lua state
01921 **
01922 **  Return value:
01923 **     nil (on the Lua stack)
01924 */
01925 
01926 int
01927 mt_conninfo(lua_State *l)
01928 {
01929        char rcmd;
01930        char family = 'U';
01931        size_t buflen;
01932        size_t s;
01933        uint16_t port;
01934        struct mt_context *ctx;
01935        char *host;
01936        char *bp;
01937        char *ipstr;
01938        char buf[BUFRSZ];
01939        char tmp[BUFRSZ];
01940 
01941        assert(l != NULL);
01942 
01943        if (lua_gettop(l) != 3 ||
01944            !lua_islightuserdata(l, 1) ||
01945            (!lua_isnil(l, 2) && !lua_isstring(l, 2)) ||
01946            (!lua_isnil(l, 3) && !lua_isstring(l, 3)))
01947        {
01948               lua_pushstring(l, "mt.conninfo(): Invalid argument");
01949               lua_error(l);
01950        }
01951 
01952        ctx = (struct mt_context *) lua_touserdata(l, 1);
01953        if (lua_isstring(l, 2))
01954               host = (char *) lua_tostring(l, 2);
01955        else
01956               host = DEFCLIENTHOST;
01957        if (lua_isstring(l, 3))
01958               ipstr = (char *) lua_tostring(l, 3);
01959        else
01960               ipstr = NULL;
01961 
01962        lua_pop(l, 3);
01963 
01964        if (!mt_assert_state(ctx, STATE_NEGOTIATED))
01965               lua_error(l);
01966 
01967        if (CHECK_MPOPTS(ctx, SMFIP_NOCONNECT))
01968        {
01969               lua_pushstring(l, "mt.conninfo(): negotiated SMFIP_NOCONNECT");
01970               lua_error(l);
01971        }
01972 
01973        if (ipstr == NULL)
01974        {
01975 #if (HAVE_GETADDRINFO && HAVE_INET_NTOP)
01976               char *a = NULL;
01977               struct addrinfo *res;
01978               struct sockaddr_in *s4;
01979               struct sockaddr_in6 *s6;
01980 
01981               if (getaddrinfo(host, NULL, NULL, &res) != 0)
01982               {
01983                      lua_pushfstring(l, "mt.conninfo(): host '%s' unknown",
01984                                      host);
01985                      lua_error(l);
01986               }
01987 
01988               if (res->ai_family == AF_INET)
01989               {
01990                      s4 = (struct sockaddr_in *) res->ai_addr;
01991                      a = (char *) &s4->sin_addr;
01992                      family = '4';
01993               }
01994               else if (res->ai_family == AF_INET6)
01995               {
01996                      s6 = (struct sockaddr_in6 *) res->ai_addr;
01997                      a = (char *) &s6->sin6_addr;
01998                      family = '6';
01999               }
02000 
02001               if (family != 'U')
02002               {
02003                      memset(tmp, '\0', sizeof tmp);
02004 
02005                      if (inet_ntop(res->ai_family, a,
02006                                    tmp, sizeof tmp - 1) == NULL)
02007                      {
02008                             freeaddrinfo(res);
02009                             lua_pushfstring(l,
02010                                             "mt.conninfo(): can't convert address for host '%s' to text",
02011                                             host);
02012                             lua_error(l);
02013                      }
02014               }
02015 
02016               freeaddrinfo(res);
02017               ipstr = tmp;
02018 #else /* HAVE_GETADDRINFO && HAVE_INET_NTOP */
02019               struct hostent *h;
02020               struct in_addr sa;
02021 
02022               h = gethostbyname(host);
02023               if (h == NULL)
02024               {
02025                      lua_pushfstring(l, "mt.conninfo(): host '%s' unknown",
02026                                      host);
02027                      lua_error(l);
02028               }
02029 
02030               memcpy(&sa.s_addr, h->h_addr, sizeof sa.s_addr);
02031               mt_inet_ntoa(sa, tmp, sizeof tmp);
02032               ipstr = tmp;
02033               family = '4';
02034 #endif /* HAVE_GETADDRINFO && HAVE_INET_NTOP */
02035        }
02036        else if (strcasecmp(ipstr, "unspec") != 0)
02037        {
02038 #ifdef HAVE_INET_PTON
02039               struct in_addr a;
02040               struct in6_addr a6;
02041 
02042               if (inet_pton(AF_INET6, ipstr, &a6.s6_addr) == 1)
02043               {
02044                      family = '6';
02045               }
02046               else if (inet_pton(AF_INET, ipstr, &a.s_addr) == 1)
02047               {
02048                      family = '4';
02049               }
02050               else
02051               {
02052                      lua_pushfstring(l,
02053                                      "mt.conninfo(): invalid IP address '%s'",
02054                                      ipstr);
02055                      lua_error(l);
02056               }
02057 #else /* HAVE_INET_PTON */
02058               struct in_addr sa;
02059 
02060               sa.s_addr = inet_addr(ipstr);
02061               if (sa.s_addr == INADDR_NONE)
02062               {
02063                      lua_pushfstring(l,
02064                                      "mt.conninfo(): invalid IPv4 address '%s'",
02065                                      ipstr);
02066                      lua_error(l);
02067               }
02068               family = '4';
02069 #endif /* HAVE_INET_PTON */
02070        }
02071 
02072        bp = buf;
02073        memcpy(bp, host, strlen(host));
02074        bp += strlen(host);
02075        *bp++ = '\0';
02076        memcpy(bp, &family, sizeof family);
02077        bp += sizeof family;
02078 
02079        s = strlen(host) + 1 + sizeof(char);
02080 
02081        if (family != 'U')                 /* known family data */
02082        {
02083               port = htons(DEFCLIENTPORT);       /* don't really need this */
02084 
02085               memcpy(bp, &port, sizeof port);
02086               bp += sizeof port;
02087               memcpy(bp, ipstr, strlen(ipstr) + 1);
02088 
02089               s += sizeof port + strlen(ipstr) + 1;
02090        }
02091 
02092        if (!mt_milter_write(ctx->ctx_fd, SMFIC_CONNECT, buf, s))
02093        {
02094               lua_pushstring(l, "mt.milter_write() failed");
02095               return 1;
02096        }
02097 
02098        rcmd = SMFIR_CONTINUE;
02099 
02100        if (!CHECK_MPOPTS(ctx, SMFIP_NR_CONN))
02101        {
02102               if (!mt_milter_read(ctx->ctx_fd, &rcmd, buf, &buflen))
02103               {
02104                      lua_pushstring(l, "mt.milter_read() failed");
02105                      return 1;
02106               }
02107        }
02108 
02109        ctx->ctx_response = rcmd;
02110        ctx->ctx_state = STATE_CONNINFO;
02111 
02112        if (verbose > 0)
02113        {
02114               fprintf(stdout,
02115                       "%s: connection details sent on fd %d, reply '%c'\n",
02116                       progname, ctx->ctx_fd, rcmd);
02117        }
02118 
02119        lua_pushnil(l);
02120 
02121        return 1;
02122 }
02123 
02124 /*
02125 **  MT_UNKNOWN -- send unknown command information
02126 **
02127 **  Parameters:
02128 **     l -- Lua state
02129 **
02130 **  Return value:
02131 **     nil (on the Lua stack)
02132 */
02133 
02134 int
02135 mt_unknown(lua_State *l)
02136 {
02137 #ifdef SMFIC_UNKNOWN
02138        char rcmd;
02139        size_t buflen;
02140        size_t s;
02141        struct mt_context *ctx;
02142        char *cmd;
02143        char *bp;
02144        char buf[BUFRSZ];
02145 #endif /* SMFIC_UNKNOWN */
02146 
02147        assert(l != NULL);
02148 
02149        if (lua_gettop(l) != 2 ||
02150            !lua_islightuserdata(l, 1) ||
02151            !lua_isstring(l, 2))
02152        {
02153               lua_pushstring(l, "mt.unknown(): Invalid argument");
02154               lua_error(l);
02155        }
02156 
02157 #ifndef SMFIC_UNKNOWN
02158        lua_pushstring(l, "mt.unknown(): Operation not supported");
02159        lua_error(l);
02160 #else /* ! SMFIC_UNKNOWN */
02161        ctx = (struct mt_context *) lua_touserdata(l, 1);
02162        cmd = (char *) lua_tostring(l, 2);
02163        lua_pop(l, 2);
02164 
02165        if (!mt_assert_state(ctx, STATE_CONNINFO))
02166               lua_error(l);
02167 
02168        if (CHECK_MPOPTS(ctx, SMFIP_NOUNKNOWN))
02169        {
02170               lua_pushstring(l, "mt.unknown(): negotiated SMFIP_NOUNKNOWN");
02171               lua_error(l);
02172        }
02173 
02174        s = strlen(cmd) + 1;
02175 
02176        bp = buf;
02177        memcpy(bp, cmd, strlen(cmd));
02178        bp += strlen(cmd);
02179        *bp++ = '\0';
02180 
02181        if (!mt_milter_write(ctx->ctx_fd, SMFIC_UNKNOWN, buf, s))
02182        {
02183               lua_pushstring(l, "mt.milter_write() failed");
02184               return 1;
02185        }
02186 
02187        buflen = sizeof buf;
02188 
02189        rcmd = SMFIR_CONTINUE;
02190 
02191        if (!CHECK_MPOPTS(ctx, SMFIP_NR_UNKN))
02192        {
02193               if (!mt_milter_read(ctx->ctx_fd, &rcmd, buf, &buflen))
02194               {
02195                      lua_pushstring(l, "mt.milter_read() failed");
02196                      return 1;
02197               }
02198        }
02199 
02200        ctx->ctx_response = rcmd;
02201 
02202        if (verbose > 0)
02203        {
02204               fprintf(stdout,
02205                       "%s: UNKNOWN sent on fd %d, reply '%c'\n",
02206                       progname, ctx->ctx_fd, rcmd);
02207        }
02208 
02209        lua_pushnil(l);
02210 #endif /* ! SMFIC_UNKNOWN */
02211 
02212        return 1;
02213 }
02214 
02215 /*
02216 **  MT_HELO -- send HELO information
02217 **
02218 **  Parameters:
02219 **     l -- Lua state
02220 **
02221 **  Return value:
02222 **     nil (on the Lua stack)
02223 */
02224 
02225 int
02226 mt_helo(lua_State *l)
02227 {
02228        char rcmd;
02229        size_t buflen;
02230        size_t s;
02231        struct mt_context *ctx;
02232        char *host;
02233        char *bp;
02234        char buf[BUFRSZ];
02235 
02236        assert(l != NULL);
02237 
02238        if (lua_gettop(l) != 2 ||
02239            !lua_islightuserdata(l, 1) ||
02240            !lua_isstring(l, 2))
02241        {
02242               lua_pushstring(l, "mt.helo(): Invalid argument");
02243               lua_error(l);
02244        }
02245 
02246        ctx = (struct mt_context *) lua_touserdata(l, 1);
02247        host = (char *) lua_tostring(l, 2);
02248        lua_pop(l, 2);
02249 
02250        if (!mt_assert_state(ctx, STATE_CONNINFO))
02251               lua_error(l);
02252 
02253        if (CHECK_MPOPTS(ctx, SMFIP_NOHELO))
02254        {
02255               lua_pushstring(l, "mt.helo(): negotiated SMFIP_NOHELO");
02256               lua_error(l);
02257        }
02258 
02259        s = strlen(host) + 1;
02260 
02261        bp = buf;
02262        memcpy(bp, host, strlen(host));
02263        bp += strlen(host);
02264        *bp++ = '\0';
02265 
02266        if (!mt_milter_write(ctx->ctx_fd, SMFIC_HELO, buf, s))
02267        {
02268               lua_pushstring(l, "mt.milter_write() failed");
02269               return 1;
02270        }
02271 
02272        buflen = sizeof buf;
02273 
02274        rcmd = SMFIR_CONTINUE;
02275 
02276        if (!CHECK_MPOPTS(ctx, SMFIP_NR_HELO))
02277        {
02278               if (!mt_milter_read(ctx->ctx_fd, &rcmd, buf, &buflen))
02279               {
02280                      lua_pushstring(l, "mt.milter_read() failed");
02281                      return 1;
02282               }
02283        }
02284 
02285        ctx->ctx_response = rcmd;
02286        ctx->ctx_state = STATE_HELO;
02287 
02288        if (verbose > 0)
02289        {
02290               fprintf(stdout,
02291                       "%s: HELO sent on fd %d, reply '%c'\n",
02292                       progname, ctx->ctx_fd, rcmd);
02293        }
02294 
02295        lua_pushnil(l);
02296 
02297        return 1;
02298 }
02299 
02300 /*
02301 **  MT_MAILFROM -- send MAIL FROM information
02302 **
02303 **  Parameters:
02304 **     l -- Lua state
02305 **
02306 **  Return value:
02307 **     nil (on the Lua stack)
02308 */
02309 
02310 int
02311 mt_mailfrom(lua_State *l)
02312 {
02313        char rcmd;
02314        int c;
02315        size_t buflen;
02316        size_t s;
02317        char *p;
02318        char *bp;
02319        struct mt_context *ctx;
02320        char buf[BUFRSZ];
02321 
02322        assert(l != NULL);
02323 
02324        if (lua_gettop(l) < 2 ||
02325            !lua_islightuserdata(l, 1))
02326        {
02327               lua_pushstring(l, "mt.mailfrom(): Invalid argument");
02328               lua_error(l);
02329        }
02330 
02331        ctx = (struct mt_context *) lua_touserdata(l, 1);
02332 
02333        s = 0;
02334        bp = buf;
02335 
02336        for (c = 2; c <= lua_gettop(l); c++)
02337        {
02338               p = (char *) lua_tostring(l, c);
02339 
02340               s += strlen(p) + 1;
02341 
02342               memcpy(bp, p, strlen(p) + 1);
02343 
02344               bp += strlen(p) + 1;
02345 
02346               /* XXX -- watch for overruns */
02347        }
02348 
02349        lua_pop(l, lua_gettop(l));
02350 
02351        if (!mt_assert_state(ctx, STATE_HELO))
02352               lua_error(l);
02353 
02354        if (CHECK_MPOPTS(ctx, SMFIP_NOMAIL))
02355        {
02356               lua_pushstring(l, "mt.mailfrom(): negotiated SMFIP_NOMAIL");
02357               lua_error(l);
02358        }
02359 
02360        if (!mt_milter_write(ctx->ctx_fd, SMFIC_MAIL, buf, s))
02361        {
02362               lua_pushstring(l, "mt.milter_write() failed");
02363               return 1;
02364        }
02365 
02366        buflen = sizeof buf;
02367 
02368        rcmd = SMFIR_CONTINUE;
02369 
02370        if (!CHECK_MPOPTS(ctx, SMFIP_NR_MAIL))
02371        {
02372               if (!mt_milter_read(ctx->ctx_fd, &rcmd, buf, &buflen))
02373               {
02374                      lua_pushstring(l, "mt.milter_read() failed");
02375                      return 1;
02376               }
02377        }
02378 
02379        ctx->ctx_response = rcmd;
02380        ctx->ctx_state = STATE_ENVFROM;
02381        mt_flush_eomreqs(ctx);
02382 
02383        if (verbose > 0)
02384        {
02385               fprintf(stdout,
02386                       "%s: MAIL sent on fd %d, reply '%c'\n",
02387                       progname, ctx->ctx_fd, rcmd);
02388        }
02389 
02390        lua_pushnil(l);
02391 
02392        return 1;
02393 }
02394 
02395 /*
02396 **  MT_RCPTTO -- send RCPT TO information
02397 **
02398 **  Parameters:
02399 **     l -- Lua state
02400 **
02401 **  Return value:
02402 **     nil (on the Lua stack)
02403 */
02404 
02405 int
02406 mt_rcptto(lua_State *l)
02407 {
02408        char rcmd;
02409        int c;
02410        size_t buflen;
02411        size_t s;
02412        char *p;
02413        char *bp;
02414        char *end;
02415        struct mt_context *ctx;
02416        char buf[BUFRSZ];
02417 
02418        assert(l != NULL);
02419 
02420        if (lua_gettop(l) < 2 ||
02421            !lua_islightuserdata(l, 1))
02422        {
02423               lua_pushstring(l, "mt.rcptto(): Invalid argument");
02424               lua_error(l);
02425        }
02426 
02427        ctx = (struct mt_context *) lua_touserdata(l, 1);
02428 
02429        s = 0;
02430        bp = buf;
02431        end = bp + sizeof buf;
02432        memset(buf, '\0', sizeof buf);
02433 
02434        for (c = 2; c <= lua_gettop(l); c++)
02435        {
02436               p = (char *) lua_tostring(l, c);
02437 
02438               s += strlen(p) + 1;
02439 
02440               if (bp + strlen(p) >= end)
02441               {
02442                      lua_pushstring(l, "mt.rcptto(): Input overflow");
02443                      lua_error(l);
02444               }
02445 
02446               memcpy(bp, p, strlen(p) + 1);
02447 
02448               bp += strlen(p) + 1;
02449        }
02450 
02451        lua_pop(l, lua_gettop(l));
02452 
02453        if (!mt_assert_state(ctx, STATE_ENVFROM))
02454               lua_error(l);
02455 
02456        if (CHECK_MPOPTS(ctx, SMFIP_NORCPT))
02457        {
02458               lua_pushstring(l, "mt.rcptto(): negotiated SMFIP_NORCPT");
02459               lua_error(l);
02460        }
02461 
02462        if (!mt_milter_write(ctx->ctx_fd, SMFIC_RCPT, buf, s))
02463        {
02464               lua_pushstring(l, "mt.milter_write() failed");
02465               return 1;
02466        }
02467 
02468        buflen = sizeof buf;
02469 
02470        rcmd = SMFIR_CONTINUE;
02471 
02472        if (!CHECK_MPOPTS(ctx, SMFIP_NR_RCPT))
02473        {
02474               if (!mt_milter_read(ctx->ctx_fd, &rcmd, buf, &buflen))
02475               {
02476                      lua_pushstring(l, "mt.milter_read() failed");
02477                      return 1;
02478               }
02479        }
02480 
02481        ctx->ctx_response = rcmd;
02482        ctx->ctx_state = STATE_ENVRCPT;
02483 
02484        if (verbose > 0)
02485        {
02486               fprintf(stdout,
02487                       "%s: RCPT sent on fd %d, reply '%c'\n",
02488                       progname, ctx->ctx_fd, rcmd);
02489        }
02490 
02491        lua_pushnil(l);
02492 
02493        return 1;
02494 }
02495 
02496 /*
02497 **  MT_DATA -- send DATA notice
02498 **
02499 **  Parameters:
02500 **     l -- Lua state
02501 **
02502 **  Return value:
02503 **     nil (on the Lua stack)
02504 */
02505 
02506 int
02507 mt_data(lua_State *l)
02508 {
02509 #ifdef SMFIC_DATA
02510        char rcmd;
02511        size_t buflen;
02512        struct mt_context *ctx;
02513        char buf[BUFRSZ];
02514 #endif /* SMFIC_DATA */
02515 
02516        assert(l != NULL);
02517 
02518        if (lua_gettop(l) != 1 ||
02519            !lua_islightuserdata(l, 1))
02520        {
02521               lua_pushstring(l, "mt.data(): Invalid argument");
02522               lua_error(l);
02523        }
02524 
02525 #ifndef SMFIC_DATA
02526        lua_pushstring(l, "mt.ata(): Operation not supported");
02527        lua_error(l);
02528 #else /* ! SMFIC_DATA */
02529        ctx = (struct mt_context *) lua_touserdata(l, 1);
02530        lua_pop(l, 1);
02531 
02532        if (!mt_assert_state(ctx, STATE_DATA))
02533               lua_error(l);
02534 
02535        if (CHECK_MPOPTS(ctx, SMFIP_NODATA))
02536        {
02537               lua_pushstring(l, "mt.data(): negotiated SMFIP_NODATA");
02538               lua_error(l);
02539        }
02540 
02541        if (!mt_milter_write(ctx->ctx_fd, SMFIC_DATA, NULL, 0))
02542        {
02543               lua_pushstring(l, "mt.milter_write() failed");
02544               return 1;
02545        }
02546 
02547        buflen = sizeof buf;
02548 
02549        rcmd = SMFIR_CONTINUE;
02550 
02551        if (!CHECK_MPOPTS(ctx, SMFIP_NR_DATA))
02552        {
02553               if (!mt_milter_read(ctx->ctx_fd, &rcmd, buf, &buflen))
02554               {
02555                      lua_pushstring(l, "mt.milter_read() failed");
02556                      return 1;
02557               }
02558        }
02559 
02560        ctx->ctx_response = rcmd;
02561        ctx->ctx_state = STATE_DATA;
02562 
02563        if (verbose > 0)
02564        {
02565               fprintf(stdout,
02566                       "%s: DATA sent on fd %d, reply '%c'\n",
02567                       progname, ctx->ctx_fd, rcmd);
02568        }
02569 
02570        lua_pushnil(l);
02571 #endif /* ! SMFIC_DATA */
02572 
02573        return 1;
02574 }
02575 
02576 /*
02577 **  MT_HEADER -- send header field information
02578 **
02579 **  Parameters:
02580 **     l -- Lua state
02581 **
02582 **  Return value:
02583 **     nil (on the Lua stack)
02584 */
02585 
02586 int
02587 mt_header(lua_State *l)
02588 {
02589        char rcmd;
02590        size_t buflen;
02591        size_t s;
02592        char *bp;
02593        char *name;
02594        char *value;
02595        struct mt_context *ctx;
02596        char buf[BUFRSZ];
02597 
02598        assert(l != NULL);
02599 
02600        if (lua_gettop(l) != 3 ||
02601            !lua_islightuserdata(l, 1) ||
02602            !lua_isstring(l, 2) ||
02603            !lua_isstring(l, 3))
02604        {
02605               lua_pushstring(l, "mt.header(): Invalid argument");
02606               lua_error(l);
02607        }
02608 
02609        ctx = (struct mt_context *) lua_touserdata(l, 1);
02610        name = (char *) lua_tostring(l, 2);
02611        value = (char *) lua_tostring(l, 3);
02612        lua_pop(l, 3);
02613 
02614        s = strlen(name) + 1 + strlen(value) + 1;
02615 #ifdef SMFIP_HDR_LEADSPC
02616        if (CHECK_MPOPTS(ctx, SMFIP_HDR_LEADSPC))
02617               s++;
02618 #endif /* SMFIP_HDR_LEADSPC */
02619 
02620        bp = buf;
02621        memcpy(buf, name, strlen(name) + 1);
02622        bp += strlen(name) + 1;
02623 #ifdef SMFIP_HDR_LEADSPC
02624        if (CHECK_MPOPTS(ctx, SMFIP_HDR_LEADSPC))
02625               *bp++ = ' ';
02626 #endif /* SMFIP_HDR_LEADSPC */
02627        memcpy(bp, value, strlen(value) + 1);
02628 
02629        if (!mt_assert_state(ctx, STATE_ENVRCPT))
02630               lua_error(l);
02631 
02632        if (CHECK_MPOPTS(ctx, SMFIP_NOHDRS))
02633        {
02634               lua_pushstring(l, "mt.header(): negotiated SMFIP_NOHDRS");
02635               lua_error(l);
02636        }
02637 
02638        if (!mt_milter_write(ctx->ctx_fd, SMFIC_HEADER, buf, s))
02639        {
02640               lua_pushstring(l, "mt.milter_write() failed");
02641               return 1;
02642        }
02643 
02644        buflen = sizeof buf;
02645 
02646        rcmd = SMFIR_CONTINUE;
02647 
02648        if (!CHECK_MPOPTS(ctx, SMFIP_NR_HDR))
02649        {
02650               if (!mt_milter_read(ctx->ctx_fd, &rcmd, buf, &buflen))
02651               {
02652                      lua_pushstring(l, "mt.milter_read() failed");
02653                      return 1;
02654               }
02655        }
02656 
02657        ctx->ctx_response = rcmd;
02658        ctx->ctx_state = STATE_HEADER;
02659 
02660        if (verbose > 0)
02661        {
02662               fprintf(stdout,
02663                       "%s: header sent on fd %d, reply '%c'\n",
02664                       progname, ctx->ctx_fd, rcmd);
02665        }
02666 
02667        lua_pushnil(l);
02668 
02669        return 1;
02670 }
02671 
02672 /*
02673 **  MT_EOH -- send end-of-header notice
02674 **
02675 **  Parameters:
02676 **     l -- Lua state
02677 **
02678 **  Return value:
02679 **     nil (on the Lua stack)
02680 */
02681 
02682 int
02683 mt_eoh(lua_State *l)
02684 {
02685        char rcmd;
02686        size_t buflen;
02687        struct mt_context *ctx;
02688        char buf[BUFRSZ];
02689 
02690        assert(l != NULL);
02691 
02692        if (lua_gettop(l) != 1 ||
02693            !lua_islightuserdata(l, 1))
02694        {
02695               lua_pushstring(l, "mt.eoh(): Invalid argument");
02696               lua_error(l);
02697        }
02698 
02699        ctx = (struct mt_context *) lua_touserdata(l, 1);
02700        lua_pop(l, 1);
02701 
02702        if (!mt_assert_state(ctx, STATE_HEADER))
02703               lua_error(l);
02704 
02705        if (CHECK_MPOPTS(ctx, SMFIP_NOEOH))
02706        {
02707               lua_pushstring(l, "mt.eoh(): negotiated SMFIP_NOEOH");
02708               lua_error(l);
02709        }
02710 
02711        if (!mt_milter_write(ctx->ctx_fd, SMFIC_EOH, NULL, 0))
02712        {
02713               lua_pushstring(l, "mt.milter_write() failed");
02714               return 1;
02715        }
02716 
02717        buflen = sizeof buf;
02718 
02719        rcmd = SMFIR_CONTINUE;
02720 
02721        if (!CHECK_MPOPTS(ctx, SMFIP_NR_EOH))
02722        {
02723               if (!mt_milter_read(ctx->ctx_fd, &rcmd, buf, &buflen))
02724               {
02725                      lua_pushstring(l, "mt.milter_read() failed");
02726                      return 1;
02727               }
02728        }
02729 
02730        ctx->ctx_response = rcmd;
02731        ctx->ctx_state = STATE_EOH;
02732 
02733        if (verbose > 0)
02734        {
02735               fprintf(stdout,
02736                       "%s: EOH sent on fd %d, reply '%c'\n",
02737                       progname, ctx->ctx_fd, rcmd);
02738        }
02739 
02740        lua_pushnil(l);
02741 
02742        return 1;
02743 }
02744 
02745 /*
02746 **  MT_BODYSTRING -- send a string of body
02747 **
02748 **  Parameters:
02749 **     l -- Lua state
02750 **
02751 **  Return value:
02752 **     nil (on the Lua stack)
02753 */
02754 
02755 int
02756 mt_bodystring(lua_State *l)
02757 {
02758        char rcmd;
02759        size_t buflen;
02760        struct mt_context *ctx;
02761        char *str;
02762        char buf[BUFRSZ];
02763 
02764        assert(l != NULL);
02765 
02766        if (lua_gettop(l) != 2 ||
02767            !lua_islightuserdata(l, 1) ||
02768            !lua_isstring(l, 2))
02769        {
02770               lua_pushstring(l, "mt.bodystring(): Invalid argument");
02771               lua_error(l);
02772        }
02773 
02774        ctx = (struct mt_context *) lua_touserdata(l, 1);
02775        str = (char *) lua_tostring(l, 2);
02776        lua_pop(l, 2);
02777 
02778        if (!mt_assert_state(ctx, STATE_EOH))
02779               lua_error(l);
02780 
02781        if (CHECK_MPOPTS(ctx, SMFIP_NOBODY))
02782        {
02783               lua_pushstring(l, "mt.bodystring(): negotiated SMFIP_NOBODY");
02784               lua_error(l);
02785        }
02786 
02787        if (!mt_milter_write(ctx->ctx_fd, SMFIC_BODY, str, strlen(str)))
02788        {
02789               lua_pushstring(l, "mt.milter_write() failed");
02790               return 1;
02791        }
02792 
02793        buflen = sizeof buf;
02794 
02795        rcmd = SMFIR_CONTINUE;
02796 
02797        if (!CHECK_MPOPTS(ctx, SMFIP_NR_BODY))
02798        {
02799               if (!mt_milter_read(ctx->ctx_fd, &rcmd, buf, &buflen))
02800               {
02801                      lua_pushstring(l, "mt.milter_read() failed");
02802                      return 1;
02803               }
02804        }
02805 
02806        ctx->ctx_response = rcmd;
02807        ctx->ctx_state = STATE_BODY;
02808 
02809        if (verbose > 0)
02810        {
02811               fprintf(stdout,
02812                       "%s: %lu byte(s) of body sent on fd %d, reply '%c'\n",
02813                       progname, strlen(str), ctx->ctx_fd, rcmd);
02814        }
02815 
02816        lua_pushnil(l);
02817 
02818        return 1;
02819 }
02820 
02821 /*
02822 **  MT_BODYRANDOM -- send a random chunk of body
02823 **
02824 **  Parameters:
02825 **     l -- Lua state
02826 **
02827 **  Return value:
02828 **     nil (on the Lua stack)
02829 */
02830 
02831 int
02832 mt_bodyrandom(lua_State *l)
02833 {
02834        char rcmd;
02835        unsigned long rw;
02836        unsigned long rl;
02837        int c;
02838        size_t buflen;
02839        struct mt_context *ctx;
02840        char buf[BUFRSZ];
02841 
02842        assert(l != NULL);
02843 
02844        if (lua_gettop(l) != 2 ||
02845            !lua_islightuserdata(l, 1) ||
02846            !lua_isnumber(l, 2))
02847        {
02848               lua_pushstring(l, "mt.bodyrandom(): Invalid argument");
02849               lua_error(l);
02850        }
02851 
02852        ctx = (struct mt_context *) lua_touserdata(l, 1);
02853        rw = (unsigned long) lua_tonumber(l, 2);
02854        lua_pop(l, 2);
02855 
02856        if (!mt_assert_state(ctx, STATE_EOH))
02857               lua_error(l);
02858 
02859        if (CHECK_MPOPTS(ctx, SMFIP_NOBODY))
02860        {
02861               lua_pushstring(l, "mt.bodyrandom(): negotiated SMFIP_NOBODY");
02862               lua_error(l);
02863        }
02864 
02865        while (rw > 0)
02866        {
02867               memset(buf, '\0', sizeof buf);
02868 
02869               rl = random() % (sizeof buf - 3);
02870               if (rl > rw)
02871                      rl = rw;
02872 
02873               for (c = 0; c < rl; c++)
02874                      buf[c] = (random() % 95) + 32;
02875               strlcat(buf, "\r\n", sizeof buf);
02876 
02877               if (!mt_milter_write(ctx->ctx_fd, SMFIC_BODY, buf,
02878                                    strlen(buf)))
02879               {
02880                      lua_pushstring(l, "mt.milter_write() failed");
02881                      return 1;
02882               }
02883 
02884               buflen = sizeof buf;
02885 
02886               rcmd = SMFIR_CONTINUE;
02887 
02888               if (!CHECK_MPOPTS(ctx, SMFIP_NR_BODY))
02889               {
02890                      if (!mt_milter_read(ctx->ctx_fd, &rcmd, buf, &buflen))
02891                      {
02892                             lua_pushstring(l, "mt.milter_read() failed");
02893                             return 1;
02894                      }
02895               }
02896 
02897               ctx->ctx_response = rcmd;
02898               ctx->ctx_state = STATE_BODY;
02899 
02900               if (verbose > 0)
02901               {
02902                      fprintf(stdout,
02903                              "%s: %lu byte(s) of body sent on fd %d, reply '%c'\n",
02904                              progname, strlen(buf), ctx->ctx_fd, rcmd);
02905               }
02906 
02907               if (rcmd != SMFIR_CONTINUE)
02908                      break;
02909 
02910               rw -= rl;
02911        }
02912 
02913        lua_pushnil(l);
02914 
02915        return 1;
02916 }
02917 
02918 /*
02919 **  MT_BODYFILE -- send contents of a file as body
02920 **
02921 **  Parameters:
02922 **     l -- Lua state
02923 **
02924 **  Return value:
02925 **     nil (on the Lua stack)
02926 */
02927 
02928 int
02929 mt_bodyfile(lua_State *l)
02930 {
02931        char rcmd;
02932        char *file;
02933        FILE *f;
02934        ssize_t rlen;
02935        struct mt_context *ctx;
02936        char chunk[CHUNKSZ];
02937 
02938        assert(l != NULL);
02939 
02940        if (lua_gettop(l) != 2 ||
02941            !lua_islightuserdata(l, 1) ||
02942            !lua_isstring(l, 2))
02943        {
02944               lua_pushstring(l, "mt.bodyfile(): Invalid argument");
02945               lua_error(l);
02946        }
02947 
02948        ctx = (struct mt_context *) lua_touserdata(l, 1);
02949        file = (char *) lua_tostring(l, 2);
02950        lua_pop(l, 2);
02951 
02952        if (!mt_assert_state(ctx, STATE_EOH))
02953               lua_error(l);
02954 
02955        if (CHECK_MPOPTS(ctx, SMFIP_NOBODY))
02956        {
02957               lua_pushstring(l, "mt.bodyfile(): negotiated SMFIP_NOBODY");
02958               lua_error(l);
02959        }
02960 
02961        f = fopen(file, "r");
02962        if (f == NULL)
02963        {
02964               lua_pushfstring(l, "mt.bodyfile(): %s: fopen(): %s",
02965                               file, strerror(errno));
02966               lua_error(l);
02967        }
02968 
02969        for (;;)
02970        {
02971               rlen =  fread(chunk, 1, sizeof chunk, f);
02972 
02973               if (rlen > 0)
02974               {
02975                      size_t buflen;
02976 
02977                      if (!mt_milter_write(ctx->ctx_fd, SMFIC_BODY, chunk,
02978                                           rlen))
02979                      {
02980                             fclose(f);
02981                             lua_pushstring(l, "mt.milter_write() failed");
02982                             return 1;
02983                      }
02984 
02985                      buflen = sizeof chunk;
02986 
02987                      rcmd = SMFIR_CONTINUE;
02988 
02989                      if (!CHECK_MPOPTS(ctx, SMFIP_NR_BODY))
02990                      {
02991                             if (!mt_milter_read(ctx->ctx_fd, &rcmd, chunk,
02992                                                 &buflen))
02993                             {
02994                                    fclose(f);
02995                                    lua_pushstring(l,
02996                                                   "mt.milter_read() failed");
02997                                    return 1;
02998                             }
02999                      }
03000 
03001                      if (verbose > 0)
03002                      {
03003                             fprintf(stdout,
03004                                     "%s: %lu byte(s) of body sent on fd %d, reply '%c'\n",
03005                                     progname, rlen, ctx->ctx_fd, rcmd);
03006                      }
03007               }
03008 
03009               if (rlen < sizeof chunk || rcmd != SMFIR_CONTINUE)
03010                      break;
03011        }
03012 
03013        fclose(f);
03014 
03015        ctx->ctx_response = rcmd;
03016        ctx->ctx_state = STATE_BODY;
03017 
03018        lua_pushnil(l);
03019 
03020        return 1;
03021 }
03022 
03023 /*
03024 **  MT_EOM -- send end-of-message notice, collect requests
03025 **
03026 **  Parameters:
03027 **     l -- Lua state
03028 **
03029 **  Return value:
03030 **     nil (on the Lua stack)
03031 */
03032 
03033 int
03034 mt_eom(lua_State *l)
03035 {
03036        char rcmd;
03037        size_t buflen;
03038        struct mt_context *ctx;
03039        char buf[BUFRSZ];
03040 
03041        assert(l != NULL);
03042 
03043        if (lua_gettop(l) != 1 ||
03044            !lua_islightuserdata(l, 1))
03045        {
03046               lua_pushstring(l, "mt.eom(): Invalid argument");
03047               lua_error(l);
03048        }
03049 
03050        ctx = (struct mt_context *) lua_touserdata(l, 1);
03051        lua_pop(l, 1);
03052 
03053        if (!mt_assert_state(ctx, STATE_BODY))
03054               lua_error(l);
03055 
03056        if (!mt_milter_write(ctx->ctx_fd, SMFIC_BODYEOB, NULL, 0))
03057        {
03058               lua_pushstring(l, "mt.milter_write() failed");
03059               return 1;
03060        }
03061 
03062        rcmd = '\0';
03063 
03064        for (;;)
03065        {
03066               buflen = sizeof buf;
03067 
03068               if (!mt_milter_read(ctx->ctx_fd, &rcmd, buf, &buflen))
03069               {
03070                      lua_pushstring(l, "mt.milter_read() failed");
03071                      return 1;
03072               }
03073 
03074               if (rcmd == SMFIR_CONTINUE ||
03075                   rcmd == SMFIR_ACCEPT ||
03076                   rcmd == SMFIR_REJECT ||
03077                   rcmd == SMFIR_TEMPFAIL ||
03078                   rcmd == SMFIR_DISCARD)
03079                      break;
03080 
03081               if (!mt_eom_request(ctx, rcmd, buflen,
03082                                   buflen == 0 ? NULL : buf))
03083               {
03084                      lua_pushstring(l, "mt.eom_request() failed");
03085                      return 1;
03086               }
03087 
03088               if (rcmd == SMFIR_REPLYCODE)
03089                      break;
03090        }
03091 
03092        ctx->ctx_response = rcmd;
03093        ctx->ctx_state = STATE_EOM;
03094 
03095        if (verbose > 0)
03096        {
03097               fprintf(stdout, "%s: EOM sent on fd %d, reply '%c'\n",
03098                       progname, ctx->ctx_fd, rcmd);
03099        }
03100 
03101        lua_pushnil(l);
03102 
03103        return 1;
03104 }
03105 
03106 /*
03107 **  MT_EOM_CHECK -- test for a specific end-of-message action
03108 **
03109 **  Parameters:
03110 **     l -- Lua state
03111 **
03112 **  Return value:
03113 **     nil (on the Lua stack)
03114 */
03115 
03116 int
03117 mt_eom_check(lua_State *l)
03118 {
03119        int op;
03120        struct mt_context *ctx;
03121        struct mt_eom_request *r;
03122 
03123        assert(l != NULL);
03124 
03125        if (lua_gettop(l) < 2 || lua_gettop(l) > 5 ||
03126            !lua_islightuserdata(l, 1) ||
03127            !lua_isnumber(l, 2))
03128        {
03129               lua_pushstring(l, "mt.eom_check(): Invalid argument");
03130               lua_error(l);
03131        }
03132 
03133        ctx = (struct mt_context *) lua_touserdata(l, 1);
03134        op = lua_tonumber(l, 2);
03135 
03136        switch (op)
03137        {
03138          case MT_HDRADD:
03139          {
03140               char *name = NULL;
03141               char *value = NULL;
03142 
03143               if (lua_gettop(l) >= 3)
03144               {
03145                      if (!lua_isstring(l, 3))
03146                      {
03147                             lua_pushstring(l,
03148                                            "mt.eom_check(): Invalid argument");
03149                             lua_error(l);
03150                      }
03151 
03152                      name = (char *) lua_tostring(l, 3);
03153               }
03154 
03155               if (lua_gettop(l) == 4)
03156               {
03157                      if (!lua_isstring(l, 4))
03158                      {
03159                             lua_pushstring(l,
03160                                            "mt.eom_check(): Invalid argument");
03161                             lua_error(l);
03162                      }
03163 
03164                      value = (char *) lua_tostring(l, 4);
03165               }
03166 
03167               if (lua_gettop(l) == 5)
03168               {
03169                      lua_pushstring(l, "mt.eom_check(): Invalid argument");
03170                      lua_error(l);
03171               }
03172 
03173               lua_pop(l, lua_gettop(l));
03174 
03175               for (r = ctx->ctx_eomreqs; r != NULL; r = r->eom_next)
03176               {
03177                      if (r->eom_request == SMFIR_ADDHEADER)
03178                      {
03179                             char *rname;
03180                             char *rvalue;
03181 
03182                             rname = r->eom_rdata;
03183                             rvalue = r->eom_rdata + strlen(rname) + 1;
03184 
03185                             if ((name == NULL ||
03186                                  strcmp(name, rname) == 0) &&
03187                                 (value == NULL ||
03188                                  strcmp(value, rvalue) == 0))
03189                             {
03190                                    lua_pushboolean(l, 1);
03191                                    return 1;
03192                             }
03193                      }
03194               }
03195 
03196               lua_pushboolean(l, 0);
03197               return 1;
03198          }
03199 
03200          case MT_HDRINSERT:
03201          {
03202 #ifdef SMFIR_INSHEADER
03203               int idx = -1;
03204               char *name = NULL;
03205               char *value = NULL;
03206 
03207               if (lua_gettop(l) >= 3)
03208               {
03209                      if (!lua_isstring(l, 3))
03210                      {
03211                             lua_pushstring(l,
03212                                            "mt.eom_check(): Invalid argument");
03213                             lua_error(l);
03214                      }
03215 
03216                      name = (char *) lua_tostring(l, 3);
03217               }
03218 
03219               if (lua_gettop(l) >= 4)
03220               {
03221                      if (!lua_isstring(l, 4))
03222                      {
03223                             lua_pushstring(l,
03224                                            "mt.eom_check(): Invalid argument");
03225                             lua_error(l);
03226                      }
03227 
03228                      value = (char *) lua_tostring(l, 4);
03229               }
03230 
03231               if (lua_gettop(l) == 5)
03232               {
03233                      if (!lua_isnumber(l, 5))
03234                      {
03235                             lua_pushstring(l,
03236                                            "mt.eom_check(): Invalid argument");
03237                             lua_error(l);
03238                      }
03239 
03240                      idx = lua_tonumber(l, 5);
03241               }
03242 
03243               lua_pop(l, lua_gettop(l));
03244 
03245               for (r = ctx->ctx_eomreqs; r != NULL; r = r->eom_next)
03246               {
03247                      if (r->eom_request == SMFIR_INSHEADER)
03248                      {
03249                             int ridx;
03250                             char *rname;
03251                             char *rvalue;
03252 
03253                             memcpy(&ridx, r->eom_rdata, MILTER_LEN_BYTES);
03254                             ridx = ntohl(ridx);
03255                             rname = r->eom_rdata + MILTER_LEN_BYTES;
03256                             rvalue = r->eom_rdata + MILTER_LEN_BYTES +
03257                                      strlen(rname) + 1;
03258 
03259                             if ((name == NULL ||
03260                                  strcmp(name, rname) == 0) &&
03261                                 (value == NULL ||
03262                                  strcmp(value, rvalue) == 0) &&
03263                                 (idx == -1 || ridx == idx))
03264                             {
03265                                    lua_pushboolean(l, 1);
03266                                    return 1;
03267                             }
03268                      }
03269               }
03270 #endif /* SMFIR_INSHEADER */
03271 
03272               lua_pushboolean(l, 0);
03273               return 1;
03274          }
03275 
03276          case MT_HDRCHANGE:
03277          {
03278               int idx = -1;
03279               char *name = NULL;
03280               char *value = NULL;
03281 
03282               if (lua_gettop(l) >= 3)
03283               {
03284                      if (!lua_isstring(l, 3))
03285                      {
03286                             lua_pushstring(l,
03287                                            "mt.eom_check(): Invalid argument");
03288                             lua_error(l);
03289                      }
03290 
03291                      name = (char *) lua_tostring(l, 3);
03292               }
03293 
03294               if (lua_gettop(l) >= 4)
03295               {
03296                      if (!lua_isstring(l, 4))
03297                      {
03298                             lua_pushstring(l,
03299                                            "mt.eom_check(): Invalid argument");
03300                             lua_error(l);
03301                      }
03302 
03303                      value = (char *) lua_tostring(l, 4);
03304               }
03305 
03306               if (lua_gettop(l) == 5)
03307               {
03308                      if (!lua_isnumber(l, 5))
03309                      {
03310                             lua_pushstring(l,
03311                                            "mt.eom_check(): Invalid argument");
03312                             lua_error(l);
03313                      }
03314 
03315                      idx = lua_tonumber(l, 4);
03316               }
03317 
03318               lua_pop(l, lua_gettop(l));
03319 
03320               for (r = ctx->ctx_eomreqs; r != NULL; r = r->eom_next)
03321               {
03322                      if (r->eom_request == SMFIR_CHGHEADER)
03323                      {
03324                             int ridx;
03325                             char *rname;
03326                             char *rvalue;
03327 
03328                             memcpy(&ridx, r->eom_rdata, MILTER_LEN_BYTES);
03329                             ridx = ntohl(ridx);
03330                             rname = r->eom_rdata + MILTER_LEN_BYTES;
03331                             rvalue = r->eom_rdata + MILTER_LEN_BYTES +
03332                                      strlen(rname) + 1;
03333 
03334                             if ((name == NULL ||
03335                                  strcmp(name, rname) == 0) &&
03336                                 (value == NULL ||
03337                                  strcmp(value, rvalue) == 0) &&
03338                                 (idx == -1 || ridx == idx))
03339                             {
03340                                    lua_pushboolean(l, 1);
03341                                    return 1;
03342                             }
03343                      }
03344               }
03345 
03346               lua_pushboolean(l, 0);
03347               return 1;
03348          }
03349 
03350          case MT_HDRDELETE:
03351          {
03352               int idx = -1;
03353               char *name = NULL;
03354 
03355               if (lua_gettop(l) >= 3)
03356               {
03357                      if (!lua_isstring(l, 3))
03358                      {
03359                             lua_pushstring(l,
03360                                            "mt.eom_check(): Invalid argument");
03361                             lua_error(l);
03362                      }
03363 
03364                      name = (char *) lua_tostring(l, 3);
03365               }
03366 
03367               if (lua_gettop(l) == 4)
03368               {
03369                      if (!lua_isnumber(l, 4))
03370                      {
03371                             lua_pushstring(l,
03372                                            "mt.eom_check(): Invalid argument");
03373                             lua_error(l);
03374                      }
03375 
03376                      idx = lua_tonumber(l, 4);
03377               }
03378 
03379               if (lua_gettop(l) == 5)
03380               {
03381                      lua_pushstring(l, "mt.eom_check(): Invalid argument");
03382                      lua_error(l);
03383               }
03384 
03385               lua_pop(l, lua_gettop(l));
03386 
03387               for (r = ctx->ctx_eomreqs; r != NULL; r = r->eom_next)
03388               {
03389                      if (r->eom_request == SMFIR_CHGHEADER)
03390                      {
03391                             int ridx;
03392                             char *rname;
03393                             char *rvalue;
03394 
03395                             memcpy(&ridx, r->eom_rdata, MILTER_LEN_BYTES);
03396                             ridx = ntohl(ridx);
03397                             rname = r->eom_rdata + MILTER_LEN_BYTES;
03398                             rvalue = r->eom_rdata + MILTER_LEN_BYTES +
03399                                      strlen(rname) + 1;
03400 
03401                             if ((name == NULL ||
03402                                  strcmp(name, rname) == 0) &&
03403                                 rvalue[0] == '\0' &&
03404                                 (idx == -1 || ridx == idx))
03405                             {
03406                                    lua_pushboolean(l, 1);
03407                                    return 1;
03408                             }
03409                      }
03410               }
03411 
03412               lua_pushboolean(l, 0);
03413               return 1;
03414          }
03415 
03416          case MT_RCPTADD:
03417          {
03418               char *rcpt;
03419 
03420               if (lua_gettop(l) != 3 ||
03421                   !lua_isstring(l, 3))
03422               {
03423                      lua_pushstring(l, "mt.eom_check(): Invalid argument");
03424                      lua_error(l);
03425               }
03426 
03427               rcpt = (char *) lua_tostring(l, 3);
03428 
03429               lua_pop(l, lua_gettop(l));
03430 
03431               for (r = ctx->ctx_eomreqs; r != NULL; r = r->eom_next)
03432               {
03433                      if (r->eom_request == SMFIR_ADDRCPT)
03434                      {
03435                             char *rname;
03436 
03437                             rname = r->eom_rdata;
03438 
03439                             if (strcmp(rcpt, rname) == 0)
03440                             {
03441                                    lua_pushboolean(l, 1);
03442                                    return 1;
03443                             }
03444                      }
03445               }
03446 
03447               lua_pushboolean(l, 0);
03448               return 1;
03449          }
03450 
03451          case MT_RCPTDELETE:
03452          {
03453               char *rcpt;
03454 
03455               if (lua_gettop(l) != 3 ||
03456                   !lua_isstring(l, 3))
03457               {
03458                      lua_pushstring(l, "mt.eom_check(): Invalid argument");
03459                      lua_error(l);
03460               }
03461 
03462               rcpt = (char *) lua_tostring(l, 3);
03463 
03464               lua_pop(l, lua_gettop(l));
03465 
03466               for (r = ctx->ctx_eomreqs; r != NULL; r = r->eom_next)
03467               {
03468                      if (r->eom_request == SMFIR_DELRCPT)
03469                      {
03470                             char *rname;
03471 
03472                             rname = r->eom_rdata;
03473 
03474                             if (strcmp(rcpt, rname) == 0)
03475                             {
03476                                    lua_pushboolean(l, 1);
03477                                    return 1;
03478                             }
03479                      }
03480               }
03481 
03482               lua_pushboolean(l, 0);
03483               return 1;
03484          }
03485 
03486          case MT_BODYCHANGE:
03487          {
03488               char *newbody = NULL;
03489 
03490               if (lua_gettop(l) < 2 || lua_gettop(l) > 3 ||
03491                   (lua_gettop(l) == 3 && !lua_isstring(l, 3)))
03492               {
03493                      lua_pushstring(l, "mt.eom_check(): Invalid argument");
03494                      lua_error(l);
03495               }
03496 
03497               if (lua_gettop(l) == 3)
03498                      newbody = (char *) lua_tostring(l, 3);
03499 
03500               lua_pop(l, lua_gettop(l));
03501 
03502               for (r = ctx->ctx_eomreqs; r != NULL; r = r->eom_next)
03503               {
03504                      if (r->eom_request == SMFIR_REPLBODY)
03505                      {
03506                             char *rbody;
03507 
03508                             rbody = r->eom_rdata;
03509 
03510                             if (newbody == NULL ||
03511                                 (strlen(newbody) == r->eom_rlen &&
03512                                  memcmp(rbody, newbody, r->eom_rlen) == 0))
03513                             {
03514                                    lua_pushboolean(l, 1);
03515                                    return 1;
03516                             }
03517                      }
03518               }
03519 
03520               lua_pushboolean(l, 0);
03521               return 1;
03522          }
03523 
03524 #ifdef SMFIR_QUARANTINE
03525          case MT_QUARANTINE:
03526          {
03527               char *reason = NULL;
03528 
03529               if (lua_gettop(l) < 2 || lua_gettop(l) > 3 ||
03530                   (lua_gettop(l) == 3 && !lua_isstring(l, 3)))
03531               {
03532                      lua_pushstring(l, "mt.eom_check(): Invalid argument");
03533                      lua_error(l);
03534               }
03535 
03536               if (lua_gettop(l) == 3)
03537                      reason = (char *) lua_tostring(l, 3);
03538 
03539               lua_pop(l, lua_gettop(l));
03540 
03541               for (r = ctx->ctx_eomreqs; r != NULL; r = r->eom_next)
03542               {
03543                      if (r->eom_request == SMFIR_QUARANTINE)
03544                      {
03545                             char *rreason;
03546 
03547                             rreason = r->eom_rdata;
03548 
03549                             if (reason == NULL ||
03550                                 strcmp(reason, rreason) == 0)
03551                             {
03552                                    lua_pushboolean(l, 1);
03553                                    return 1;
03554                             }
03555                      }
03556               }
03557 
03558               lua_pushboolean(l, 0);
03559               return 1;
03560          }
03561 #endif /* SMFIR_QUARANTINE */
03562 
03563          case MT_SMTPREPLY:
03564          {
03565               char *smtp = NULL;
03566               char *esc = NULL;
03567               char *text = NULL;
03568 
03569               if (lua_gettop(l) < 3 || !lua_isstring(l, 3))
03570               {
03571                      lua_pushstring(l, "mt.eom_check(): Invalid argument");
03572                      lua_error(l);
03573               }
03574 
03575               smtp = (char *) lua_tostring(l, 3);
03576 
03577               if (lua_gettop(l) >= 4)
03578               {
03579                      if (!lua_isstring(l, 4))
03580                      {
03581                             lua_pushstring(l,
03582                                            "mt.eom_check(): Invalid argument");
03583                             lua_error(l);
03584                      }
03585 
03586                      esc = (char *) lua_tostring(l, 4);
03587               }
03588 
03589               if (lua_gettop(l) == 5)
03590               {
03591                      if (!lua_isstring(l, 5))
03592                      {
03593                             lua_pushstring(l,
03594                                            "mt.eom_check(): Invalid argument");
03595                             lua_error(l);
03596                      }
03597 
03598                      text = (char *) lua_tostring(l, 5);
03599               }
03600 
03601               lua_pop(l, lua_gettop(l));
03602 
03603               for (r = ctx->ctx_eomreqs; r != NULL; r = r->eom_next)
03604               {
03605                      if (r->eom_request == SMFIR_REPLYCODE)
03606                      {
03607                             char rbuf[BUFRSZ];
03608 
03609                             snprintf(rbuf, sizeof rbuf, "%s%s%s%s%s",
03610                                      smtp,
03611                                      esc == NULL ? "" : " ", esc,
03612                                      text == NULL ? "" : " ", text);
03613 
03614                             if (strcmp(rbuf, (char *) r->eom_rdata) == 0)
03615                             {
03616                                    lua_pushboolean(l, 1);
03617                                    return 1;
03618                             }
03619                      }
03620               }
03621 
03622               lua_pushboolean(l, 0);
03623               return 1;
03624          }
03625 
03626          default:
03627               lua_pushstring(l, "mt.eom_check(): Invalid argument");
03628               lua_error(l);
03629        }
03630 
03631        return 1;
03632 }
03633 
03634 /*
03635 **  MT_ABORT -- send transaction abort notice
03636 **
03637 **  Parameters:
03638 **     l -- Lua state
03639 **
03640 **  Return value:
03641 **     nil (on the Lua stack)
03642 */
03643 
03644 int
03645 mt_abort(lua_State *l)
03646 {
03647        struct mt_context *ctx;
03648 
03649        assert(l != NULL);
03650 
03651        if (lua_gettop(l) != 1 ||
03652            !lua_islightuserdata(l, 1))
03653        {
03654               lua_pushstring(l, "mt.abort(): Invalid argument");
03655               lua_error(l);
03656        }
03657 
03658        ctx = (struct mt_context *) lua_touserdata(l, 1);
03659        lua_pop(l, 1);
03660 
03661        if (!mt_milter_write(ctx->ctx_fd, SMFIC_ABORT, NULL, 0))
03662        {
03663               lua_pushstring(l, "mt.milter_write() failed");
03664               return 1;
03665        }
03666 
03667        ctx->ctx_state = STATE_HELO;
03668 
03669        if (verbose > 0)
03670        {
03671               fprintf(stdout, "%s: ABORT sent on fd %d\n",
03672                       progname, ctx->ctx_fd);
03673        }
03674 
03675        lua_pushnil(l);
03676 
03677        return 1;
03678 }
03679 
03680 /*
03681 **  MT_GETREPLY -- get last reply
03682 **
03683 **  Parameters:
03684 **     l -- Lua state
03685 **
03686 **  Return value:
03687 **     Last reply received, as an integer (on the Lua stack).
03688 */
03689 
03690 int
03691 mt_getreply(lua_State *l)
03692 {
03693        struct mt_context *ctx;
03694 
03695        assert(l != NULL);
03696 
03697        if (lua_gettop(l) != 1 ||
03698            !lua_islightuserdata(l, 1))
03699        {
03700               lua_pushstring(l, "mt.getreply(): Invalid argument");
03701               lua_error(l);
03702        }
03703 
03704        ctx = (struct mt_context *) lua_touserdata(l, 1);
03705        lua_pop(l, 1);
03706 
03707        lua_pushnumber(l, ctx->ctx_response);
03708 
03709        return 1;
03710 }
03711 
03712 /*
03713 **  MT_GETHEADER -- retrieve a header field added during EOM
03714 **
03715 **  Parameters:
03716 **     l -- Lua state
03717 **
03718 **  Return value:
03719 **     Last reply received, as a string (on the Lua stack).
03720 */
03721 
03722 int
03723 mt_getheader(lua_State *l)
03724 {
03725        int idx;
03726        char *name;
03727        struct mt_context *ctx;
03728        struct mt_eom_request *r;
03729 
03730        assert(l != NULL);
03731 
03732        if (lua_gettop(l) != 3 ||
03733            !lua_islightuserdata(l, 1) ||
03734            !lua_isstring(l, 2) ||
03735            !lua_isnumber(l, 3))
03736        {
03737               lua_pushstring(l, "mt.getheader(): Invalid argument");
03738               lua_error(l);
03739        }
03740 
03741        ctx = (struct mt_context *) lua_touserdata(l, 1);
03742        name = (char *) lua_tostring(l, 2);
03743        idx = lua_tonumber(l, 3);
03744        lua_pop(l, 3);
03745 
03746        for (r = ctx->ctx_eomreqs; r != NULL; r = r->eom_next)
03747        {
03748 #ifdef SMFIR_INSHEADER
03749               if (r->eom_request == SMFIR_ADDHEADER ||
03750                   r->eom_request == SMFIR_INSHEADER)
03751 #else /* SMFIR_INSHEADER */
03752               if (r->eom_request == SMFIR_ADDHEADER)
03753 #endif /* SMFIR_INSHEADER */
03754               {
03755                      char *rname;
03756                      char *rvalue;
03757 
03758 #ifdef SMFIR_INSHEADER
03759                      if (r->eom_request == SMFIR_INSHEADER)
03760                      {
03761                             rname = r->eom_rdata + MILTER_LEN_BYTES;
03762                             rvalue = r->eom_rdata + MILTER_LEN_BYTES +
03763                                      strlen(rname) + 1;
03764                      }
03765                      else
03766 #endif /* SMFIR_INSHEADER */
03767                      {
03768                             rname = r->eom_rdata;
03769                             rvalue = r->eom_rdata + strlen(rname) + 1;
03770                      }
03771 
03772                      if (strcmp(name, rname) == 0 && rvalue != NULL)
03773                      {
03774                             if (idx == 0)
03775                             {
03776                                    lua_pushstring(l, rvalue);
03777                                    return 1;
03778                             }
03779                             else
03780                             {
03781                                    idx--;
03782                             }
03783                      }
03784               }
03785        }
03786 
03787        lua_pushnil(l);
03788 
03789        return 1;
03790 }
03791 
03792 /*
03793 **  USAGE -- print usage message
03794 ** 
03795 **  Parameters:
03796 **     Not now.  Maybe later.
03797 **
03798 **  Return value:
03799 **     EX_USAGE
03800 */
03801 
03802 int
03803 usage(void)
03804 {
03805        fprintf(stderr, "%s: usage: %s [options]\n"
03806                        "\t-D name[=value]\tdefine global variable\n"
03807                        "\t-s script      \tscript to run (default = stdin)\n"
03808                        "\t-u             \treport usage statistics\n"
03809                        "\t-v             \tverbose mode\n"
03810                        "\t-V             \tprint version number and exit\n"
03811                        "\t-w             \tdon't wait for child at shutdown\n",
03812                        progname, progname);
03813 
03814        return EX_USAGE;
03815 }
03816 
03817 /*
03818 **  MAIN -- program mainline
03819 **
03820 **  Parameters:
03821 **     argc, argv -- the usual
03822 **
03823 **  Return value:
03824 **     Exit status.
03825 */
03826 
03827 int
03828 main(int argc, char **argv)
03829 {
03830        int c;
03831        int status;
03832        int fd;
03833        int retval = 0;
03834        ssize_t rlen;
03835        char *p;
03836        char *script = NULL;
03837        lua_State *l;
03838        struct mt_lua_io io;
03839        struct stat s;
03840 
03841        progname = (p = strrchr(argv[0], '/')) == NULL ? argv[0] : p + 1;
03842 
03843        verbose = 0;
03844        filterpid = 0;
03845        tmo = DEFTIMEOUT;
03846        rusage = FALSE;
03847        nowait = FALSE;
03848 
03849        l = lua_newstate(mt_lua_alloc, NULL);
03850        if (l == NULL)
03851        {
03852               fprintf(stderr, "%s: unable to allocate new Lua state\n",
03853                       progname);
03854               return 1;
03855        }
03856 
03857        luaL_openlibs(l);
03858 
03859        while ((c = getopt(argc, argv, CMDLINEOPTS)) != -1)
03860        {
03861               switch (c)
03862               {
03863                 case 'D':
03864                      p = strchr(optarg, '=');
03865                      if (p != NULL)
03866                      {
03867                             *p = '\0';
03868                             lua_pushstring(l, p + 1);
03869                      }
03870                      else
03871                      {
03872                             lua_pushnumber(l, 1);
03873                      }
03874 
03875                      lua_setglobal(l, optarg);
03876 
03877                      break;
03878 
03879                 case 's':
03880                      if (script != NULL)
03881                      {
03882                             fprintf(stderr,
03883                                     "%s: multiple use of '-%c' not permitted\n",
03884                                     progname, c);
03885                             lua_close(l);
03886                             return EX_USAGE;
03887                      }
03888 
03889                      script = optarg;
03890                      break;
03891 
03892                 case 'u':
03893                      rusage = TRUE;
03894                      break;
03895 
03896                 case 'v':
03897                      verbose++;
03898                      break;
03899 
03900                 case 'V':
03901                      fprintf(stdout, "%s: %s v%s\n", progname, MT_PRODUCT,
03902                              MT_VERSION);
03903                      return 0;
03904 
03905                 case 'w':
03906                      nowait = TRUE;
03907                      break;
03908 
03909                 default:
03910                      lua_close(l);
03911                      return usage();
03912               }
03913        }
03914 
03915        if (optind != argc)
03916        {
03917               lua_close(l);
03918               return usage();
03919        }
03920 
03921        io.lua_io_done = FALSE;
03922 
03923        if (script != NULL)
03924        {
03925               fd = open(script, O_RDONLY);
03926               if (fd < 0)
03927               {
03928                      fprintf(stderr, "%s: %s: open(): %s\n", progname,
03929                              script, strerror(errno));
03930                      lua_close(l);
03931                      return 1;
03932               }
03933 
03934               if (fstat(fd, &s) != 0)
03935               {
03936                      fprintf(stderr, "%s: %s: fstat(): %s\n", progname,
03937                              script, strerror(errno));
03938                      close(fd);
03939                      lua_close(l);
03940                      return 1;
03941               }
03942 
03943               io.lua_io_script = (const char *) malloc(s.st_size);
03944               if (io.lua_io_script == NULL)
03945               {
03946                      fprintf(stderr, "%s: malloc(): %s\n", progname,
03947                              strerror(errno));
03948                      close(fd);
03949                      lua_close(l);
03950                      return 1;
03951               }
03952 
03953               rlen = read(fd, (void *) io.lua_io_script, s.st_size);
03954               if (rlen != s.st_size)
03955               {
03956                      fprintf(stderr,
03957                              "%s: %s: read() returned %lu (expecting %ld)\n",
03958                              progname, script, rlen, s.st_size);
03959                      free((void *) io.lua_io_script);
03960                      close(fd);
03961                      lua_close(l);
03962                      return 1;
03963               }
03964 
03965               io.lua_io_scriptlen = (size_t) s.st_size;
03966 
03967               close(fd);
03968        }
03969        else
03970        {
03971               io.lua_io_script = NULL;
03972        }
03973 
03974        /* register functions */
03975        luaL_register(l, "mt", mt_library);
03976        lua_pop(l, 1);
03977 
03978        /* register constants */
03979        lua_pushnumber(l, MT_HDRINSERT);
03980        lua_setglobal(l, "MT_HDRINSERT");
03981        lua_pushnumber(l, MT_HDRADD);
03982        lua_setglobal(l, "MT_HDRADD");
03983        lua_pushnumber(l, MT_HDRCHANGE);
03984        lua_setglobal(l, "MT_HDRCHANGE");
03985        lua_pushnumber(l, MT_HDRDELETE);
03986        lua_setglobal(l, "MT_HDRDELETE");
03987        lua_pushnumber(l, MT_RCPTADD);
03988        lua_setglobal(l, "MT_RCPTADD");
03989        lua_pushnumber(l, MT_RCPTDELETE);
03990        lua_setglobal(l, "MT_RCPTDELETE");
03991        lua_pushnumber(l, MT_BODYCHANGE);
03992        lua_setglobal(l, "MT_BODYCHANGE");
03993        lua_pushnumber(l, MT_QUARANTINE);
03994        lua_setglobal(l, "MT_QUARANTINE");
03995        lua_pushnumber(l, MT_SMTPREPLY);
03996        lua_setglobal(l, "MT_SMTPREPLY");
03997 
03998        lua_pushnumber(l, SMFIR_CONTINUE);
03999        lua_setglobal(l, "SMFIR_CONTINUE");
04000        lua_pushnumber(l, SMFIR_ACCEPT);
04001        lua_setglobal(l, "SMFIR_ACCEPT");
04002        lua_pushnumber(l, SMFIR_REJECT);
04003        lua_setglobal(l, "SMFIR_REJECT");
04004        lua_pushnumber(l, SMFIR_TEMPFAIL);
04005        lua_setglobal(l, "SMFIR_TEMPFAIL");
04006        lua_pushnumber(l, SMFIR_DISCARD);
04007        lua_setglobal(l, "SMFIR_DISCARD");
04008        lua_pushnumber(l, SMFIR_REPLYCODE);
04009        lua_setglobal(l, "SMFIR_REPLYCODE");
04010 #ifdef SMFIR_SKIP
04011        lua_pushnumber(l, SMFIR_SKIP);
04012        lua_setglobal(l, "SMFIR_SKIP");
04013 #endif /* SMFIR_SKIP */
04014 
04015        lua_pushnumber(l, SMFIC_CONNECT);
04016        lua_setglobal(l, "SMFIC_CONNECT");
04017        lua_pushnumber(l, SMFIC_HELO);
04018        lua_setglobal(l, "SMFIC_HELO");
04019        lua_pushnumber(l, SMFIC_MAIL);
04020        lua_setglobal(l, "SMFIC_MAIL");
04021        lua_pushnumber(l, SMFIC_RCPT);
04022        lua_setglobal(l, "SMFIC_RCPT");
04023 
04024        lua_pushnumber(l, SMFIP_NOCONNECT);
04025        lua_setglobal(l, "SMFIP_NOCONNECT");
04026        lua_pushnumber(l, SMFIP_NOHELO);
04027        lua_setglobal(l, "SMFIP_NOHELO");
04028        lua_pushnumber(l, SMFIP_NOMAIL);
04029        lua_setglobal(l, "SMFIP_NOMAIL");
04030        lua_pushnumber(l, SMFIP_NORCPT);
04031        lua_setglobal(l, "SMFIP_NORCPT");
04032        lua_pushnumber(l, SMFIP_NOBODY);
04033        lua_setglobal(l, "SMFIP_NOBODY");
04034        lua_pushnumber(l, SMFIP_NOHDRS);
04035        lua_setglobal(l, "SMFIP_NOHDRS");
04036        lua_pushnumber(l, SMFIP_NOEOH);
04037        lua_setglobal(l, "SMFIP_NOEOH");
04038 #ifdef SMFIP_NR_HDR
04039        lua_pushnumber(l, SMFIP_NR_HDR);
04040        lua_setglobal(l, "SMFIP_NR_HDR");
04041 #endif /* SMFIP_NR_HDR */
04042 #ifdef SMFIP_NOHREPL
04043        lua_pushnumber(l, SMFIP_NOHREPL);
04044        lua_setglobal(l, "SMFIP_NOHREPL");
04045 #endif /* SMFIP_NOHREPL */
04046 #ifdef SMFIP_NOUNKNOWN
04047        lua_pushnumber(l, SMFIP_NOUNKNOWN);
04048        lua_setglobal(l, "SMFIP_NOUNKNOWN");
04049 #endif /* SMFIP_NOUNKNOWN */
04050 #ifdef SMFIP_NODATA
04051        lua_pushnumber(l, SMFIP_NODATA);
04052        lua_setglobal(l, "SMFIP_NODATA");
04053 #endif /* SMFIP_NODATA */
04054 #ifdef SMFIP_SKIP
04055        lua_pushnumber(l, SMFIP_SKIP);
04056        lua_setglobal(l, "SMFIP_SKIP");
04057 #endif /* SMFIP_SKIP */
04058 #ifdef SMFIP_RCPT_REJ
04059        lua_pushnumber(l, SMFIP_RCPT_REJ);
04060        lua_setglobal(l, "SMFIP_RCPT_REJ");
04061 #endif /* SMFIP_RCPT_REJ */
04062        lua_pushnumber(l, SMFIP_NR_CONN);
04063        lua_setglobal(l, "SMFIP_NR_CONN");
04064        lua_pushnumber(l, SMFIP_NR_HELO);
04065        lua_setglobal(l, "SMFIP_NR_HELO");
04066        lua_pushnumber(l, SMFIP_NR_MAIL);
04067        lua_setglobal(l, "SMFIP_NR_MAIL");
04068        lua_pushnumber(l, SMFIP_NR_RCPT);
04069        lua_setglobal(l, "SMFIP_NR_RCPT");
04070        lua_pushnumber(l, SMFIP_NR_DATA);
04071        lua_setglobal(l, "SMFIP_NR_DATA");
04072        lua_pushnumber(l, SMFIP_NR_UNKN);
04073        lua_setglobal(l, "SMFIP_NR_UNKN");
04074        lua_pushnumber(l, SMFIP_NR_EOH);
04075        lua_setglobal(l, "SMFIP_NR_EOH");
04076        lua_pushnumber(l, SMFIP_NR_BODY);
04077        lua_setglobal(l, "SMFIP_NR_BODY");
04078 #ifdef SMFIP_HDR_LEADSPC
04079        lua_pushnumber(l, SMFIP_HDR_LEADSPC);
04080        lua_setglobal(l, "SMFIP_HDR_LEADSPC");
04081 #endif /* SMFIP_HDR_LEADSPC */
04082 #ifdef SMFIP_MDS_256K
04083        lua_pushnumber(l, SMFIP_MDS_256K);
04084        lua_setglobal(l, "SMFIP_MDS_256K");
04085 #endif /* SMFIP_MDS_256K */
04086 #ifdef SMFIP_MDS_1M
04087        lua_pushnumber(l, SMFIP_MDS_1M);
04088        lua_setglobal(l, "SMFIP_MDS_1M");
04089 #endif /* SMFIP_MDS_1M */
04090 #ifdef SMFIP_TEST
04091        lua_pushnumber(l, SMFIP_TEST);
04092        lua_setglobal(l, "SMFIP_TEST");
04093 #endif /* SMFIP_TEST */
04094 
04095        lua_pushnumber(l, SMFIF_ADDHDRS);
04096        lua_setglobal(l, "SMFIF_ADDHDRS");
04097        lua_pushnumber(l, SMFIF_CHGBODY);
04098        lua_setglobal(l, "SMFIF_CHGBODY");
04099        lua_pushnumber(l, SMFIF_MODBODY);
04100        lua_setglobal(l, "SMFIF_MODBODY");
04101        lua_pushnumber(l, SMFIF_ADDRCPT);
04102        lua_setglobal(l, "SMFIF_ADDRCPT");
04103        lua_pushnumber(l, SMFIF_DELRCPT);
04104        lua_setglobal(l, "SMFIF_DELRCPT");
04105        lua_pushnumber(l, SMFIF_CHGHDRS);
04106        lua_setglobal(l, "SMFIF_CHGHDRS");
04107 #ifdef SMFIF_QUARANTINE
04108        lua_pushnumber(l, SMFIF_QUARANTINE);
04109        lua_setglobal(l, "SMFIF_QUARANTINE");
04110 #endif /* SMFIF_QUARANTINE */
04111 #ifdef SMFIF_CHGFROM
04112        lua_pushnumber(l, SMFIF_CHGFROM);
04113        lua_setglobal(l, "SMFIF_CHGFROM");
04114 #endif /* SMFIF_CHGFROM */
04115 #ifdef SMFIF_ADDRCPT_PAR
04116        lua_pushnumber(l, SMFIF_ADDRCPT_PAR);
04117        lua_setglobal(l, "SMFIF_ADDRCPT_PAR");
04118 #endif /* SMFIF_ADDRCPT_PAR */
04119 #ifdef SMFIF_SETSYMLIST
04120        lua_pushnumber(l, SMFIF_SETSYMLIST);
04121        lua_setglobal(l, "SMFIF_SETSYMLIST");
04122 #endif /* SMFIF_SETSYMLIST */
04123 
04124        switch (lua_load(l, mt_lua_reader, (void *) &io,
04125                         script == NULL ? "(stdin)" : script))
04126        {
04127          case 0:
04128               break;
04129 
04130          case LUA_ERRSYNTAX:
04131          case LUA_ERRMEM:
04132               if (lua_isstring(l, 1))
04133               {
04134                      fprintf(stderr, "%s: %s: %s\n", progname,
04135                              script == NULL ? "(stdin)" : script,
04136                              lua_tostring(l, 1));
04137               }
04138               lua_close(l);
04139               if (io.lua_io_script != NULL)
04140                      free((void *) io.lua_io_script);
04141               return 1;
04142 
04143          default:
04144               assert(0);
04145        }
04146 
04147        (void) srandom(time(NULL));
04148 
04149        status = lua_pcall(l, 0, LUA_MULTRET, 0);
04150        if (lua_gettop(l) == 1 && lua_isstring(l, 1))
04151        {
04152               fprintf(stderr, "%s: %s: %s\n", progname,
04153                       script == NULL ? "(stdin)" : script,
04154                       lua_tostring(l, 1));
04155        }
04156 
04157        if (rusage)
04158        {
04159               struct rusage u;
04160 
04161               if (getrusage(RUSAGE_SELF, &u) != 0)
04162               {
04163                      fprintf(stderr,
04164                              "%s: getrusage(RUSAGE_SELF): %s\n",
04165                              progname, strerror(errno));
04166 
04167                      retval = 2;
04168               }
04169 
04170               fprintf(stdout, "%s: self:  user %u.%06u, system %u.%06u\n",
04171                       progname,
04172                       (unsigned) u.ru_utime.tv_sec,
04173                       (unsigned) u.ru_utime.tv_usec,
04174                       (unsigned) u.ru_stime.tv_sec,
04175                       (unsigned) u.ru_stime.tv_usec);
04176        }
04177 
04178        if (status != 0)
04179               retval = 1;
04180 
04181        lua_close(l);
04182        if (io.lua_io_script != NULL)
04183               free((void *) io.lua_io_script);
04184 
04185        if (filterpid != 0)
04186        {
04187               if (kill(filterpid, SIGTERM) != 0)
04188               {
04189                      fprintf(stderr, "%s: %d: kill() %s\n", progname,
04190                              filterpid, strerror(errno));
04191               }
04192               else if (!nowait)
04193               {
04194                      if (verbose > 1)
04195                      {
04196                             fprintf(stdout,
04197                                     "%s: waiting for process %d\n",
04198                                     progname, filterpid);
04199                      }
04200 
04201                      (void) wait(&status);
04202 
04203                      if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
04204                      {
04205                             fprintf(stderr,
04206                                     "%s: filter process exited with status %d\n",
04207                                     progname, WEXITSTATUS(status));
04208 
04209                             retval = 1;
04210                      }
04211                      else if (WIFSIGNALED(status) &&
04212                               WTERMSIG(status) != SIGTERM)
04213                      {
04214                             fprintf(stderr,
04215                                     "%s: filter process died with signal %d\n",
04216                                     progname, WTERMSIG(status));
04217 
04218                             retval = 1;
04219                      }
04220               }
04221        }
04222 
04223        if (rusage && !nowait)
04224        {
04225               struct rusage u;
04226 
04227               if (getrusage(RUSAGE_CHILDREN, &u) != 0)
04228               {
04229                      fprintf(stderr,
04230                              "%s: getrusage(RUSAGE_CHILDREN): %s\n",
04231                              progname, strerror(errno));
04232 
04233                      retval = 2;
04234               }
04235 
04236               fprintf(stdout, "%s: child: user %u.%06u, system %u.%06u\n",
04237                       progname,
04238                       (unsigned) u.ru_utime.tv_sec,
04239                       (unsigned) u.ru_utime.tv_usec,
04240                       (unsigned) u.ru_stime.tv_sec,
04241                       (unsigned) u.ru_stime.tv_usec);
04242        }
04243 
04244        return retval;
04245 }