diff options
Diffstat (limited to 'contrib/sendmail/libmilter/smfi.c')
-rw-r--r-- | contrib/sendmail/libmilter/smfi.c | 889 |
1 files changed, 889 insertions, 0 deletions
diff --git a/contrib/sendmail/libmilter/smfi.c b/contrib/sendmail/libmilter/smfi.c new file mode 100644 index 0000000..138623e --- /dev/null +++ b/contrib/sendmail/libmilter/smfi.c @@ -0,0 +1,889 @@ +/* + * Copyright (c) 1999-2007 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#include <sm/gen.h> +SM_RCSID("@(#)$Id: smfi.c,v 8.83 2007/04/23 16:44:39 ca Exp $") +#include <sm/varargs.h> +#include "libmilter.h" + +static int smfi_header __P((SMFICTX *, int, int, char *, char *)); +static int myisenhsc __P((const char *, int)); + +/* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */ +#define MAXREPLYLEN 980 /* max. length of a reply string */ +#define MAXREPLIES 32 /* max. number of reply strings */ + +/* +** SMFI_HEADER -- send a header to the MTA +** +** Parameters: +** ctx -- Opaque context structure +** cmd -- Header modification command +** hdridx -- Header index +** headerf -- Header field name +** headerv -- Header field value +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +static int +smfi_header(ctx, cmd, hdridx, headerf, headerv) + SMFICTX *ctx; + int cmd; + int hdridx; + char *headerf; + char *headerv; +{ + size_t len, l1, l2, offset; + int r; + mi_int32 v; + char *buf; + struct timeval timeout; + + if (headerf == NULL || *headerf == '\0' || headerv == NULL) + return MI_FAILURE; + timeout.tv_sec = ctx->ctx_timeout; + timeout.tv_usec = 0; + l1 = strlen(headerf) + 1; + l2 = strlen(headerv) + 1; + len = l1 + l2; + if (hdridx >= 0) + len += MILTER_LEN_BYTES; + buf = malloc(len); + if (buf == NULL) + return MI_FAILURE; + offset = 0; + if (hdridx >= 0) + { + v = htonl(hdridx); + (void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES); + offset += MILTER_LEN_BYTES; + } + (void) memcpy(buf + offset, headerf, l1); + (void) memcpy(buf + offset + l1, headerv, l2); + r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len); + free(buf); + return r; +} + +/* +** SMFI_ADDHEADER -- send a new header to the MTA +** +** Parameters: +** ctx -- Opaque context structure +** headerf -- Header field name +** headerv -- Header field value +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +int +smfi_addheader(ctx, headerf, headerv) + SMFICTX *ctx; + char *headerf; + char *headerv; +{ + if (!mi_sendok(ctx, SMFIF_ADDHDRS)) + return MI_FAILURE; + + return smfi_header(ctx, SMFIR_ADDHEADER, -1, headerf, headerv); +} + +/* +** SMFI_INSHEADER -- send a new header to the MTA (to be inserted) +** +** Parameters: +** ctx -- Opaque context structure +** hdridx -- index into header list where insertion should occur +** headerf -- Header field name +** headerv -- Header field value +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +int +smfi_insheader(ctx, hdridx, headerf, headerv) + SMFICTX *ctx; + int hdridx; + char *headerf; + char *headerv; +{ + if (!mi_sendok(ctx, SMFIF_ADDHDRS) || hdridx < 0) + return MI_FAILURE; + + return smfi_header(ctx, SMFIR_INSHEADER, hdridx, headerf, headerv); +} + +/* +** SMFI_CHGHEADER -- send a changed header to the MTA +** +** Parameters: +** ctx -- Opaque context structure +** headerf -- Header field name +** hdridx -- Header index value +** headerv -- Header field value +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +int +smfi_chgheader(ctx, headerf, hdridx, headerv) + SMFICTX *ctx; + char *headerf; + mi_int32 hdridx; + char *headerv; +{ + if (!mi_sendok(ctx, SMFIF_CHGHDRS) || hdridx < 0) + return MI_FAILURE; + if (headerv == NULL) + headerv = ""; + + return smfi_header(ctx, SMFIR_CHGHEADER, hdridx, headerf, headerv); +} + +#if 0 +/* +** BUF_CRT_SEND -- construct buffer to send from arguments +** +** Parameters: +** ctx -- Opaque context structure +** cmd -- command +** arg0 -- first argument +** argv -- list of arguments (NULL terminated) +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +static int +buf_crt_send __P((SMFICTX *, int cmd, char *, char **)); + +static int +buf_crt_send(ctx, cmd, arg0, argv) + SMFICTX *ctx; + int cmd; + char *arg0; + char **argv; +{ + size_t len, l0, l1, offset; + int r; + char *buf, *arg, **argvl; + struct timeval timeout; + + if (arg0 == NULL || *arg0 == '\0') + return MI_FAILURE; + timeout.tv_sec = ctx->ctx_timeout; + timeout.tv_usec = 0; + l0 = strlen(arg0) + 1; + len = l0; + argvl = argv; + while (argvl != NULL && (arg = *argv) != NULL && *arg != '\0') + { + l1 = strlen(arg) + 1; + len += l1; + SM_ASSERT(len > l1); + } + + buf = malloc(len); + if (buf == NULL) + return MI_FAILURE; + (void) memcpy(buf, arg0, l0); + offset = l0; + + argvl = argv; + while (argvl != NULL && (arg = *argv) != NULL && *arg != '\0') + { + l1 = strlen(arg) + 1; + SM_ASSERT(offset < len); + SM_ASSERT(offset + l1 <= len); + (void) memcpy(buf + offset, arg, l1); + offset += l1; + SM_ASSERT(offset > l1); + } + + r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len); + free(buf); + return r; +} +#endif /* 0 */ + +/* +** SEND2 -- construct buffer to send from arguments +** +** Parameters: +** ctx -- Opaque context structure +** cmd -- command +** arg0 -- first argument +** argv -- list of arguments (NULL terminated) +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +static int +send2 __P((SMFICTX *, int cmd, char *, char *)); + +static int +send2(ctx, cmd, arg0, arg1) + SMFICTX *ctx; + int cmd; + char *arg0; + char *arg1; +{ + size_t len, l0, l1, offset; + int r; + char *buf; + struct timeval timeout; + + if (arg0 == NULL || *arg0 == '\0') + return MI_FAILURE; + timeout.tv_sec = ctx->ctx_timeout; + timeout.tv_usec = 0; + l0 = strlen(arg0) + 1; + len = l0; + if (arg1 != NULL) + { + l1 = strlen(arg1) + 1; + len += l1; + SM_ASSERT(len > l1); + } + + buf = malloc(len); + if (buf == NULL) + return MI_FAILURE; + (void) memcpy(buf, arg0, l0); + offset = l0; + + if (arg1 != NULL) + { + l1 = strlen(arg1) + 1; + SM_ASSERT(offset < len); + SM_ASSERT(offset + l1 <= len); + (void) memcpy(buf + offset, arg1, l1); + offset += l1; + SM_ASSERT(offset > l1); + } + + r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len); + free(buf); + return r; +} + +/* +** SMFI_CHGFROM -- change enveloper sender ("from") address +** +** Parameters: +** ctx -- Opaque context structure +** from -- new envelope sender address ("MAIL From") +** args -- ESMTP arguments +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +int +smfi_chgfrom(ctx, from, args) + SMFICTX *ctx; + char *from; + char *args; +{ + if (from == NULL || *from == '\0') + return MI_FAILURE; + if (!mi_sendok(ctx, SMFIF_CHGFROM)) + return MI_FAILURE; + return send2(ctx, SMFIR_CHGFROM, from, args); +} + +/* +** SMFI_SETSYMLIST -- set list of macros that the MTA should send. +** +** Parameters: +** ctx -- Opaque context structure +** where -- SMTP stage +** macros -- list of macros +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +int +smfi_setsymlist(ctx, where, macros) + SMFICTX *ctx; + int where; + char *macros; +{ + SM_ASSERT(ctx != NULL); + + if (macros == NULL || *macros == '\0') + return MI_FAILURE; + if (where < SMFIM_FIRST || where > SMFIM_LAST) + return MI_FAILURE; + if (where < 0 || where >= MAX_MACROS_ENTRIES) + return MI_FAILURE; + + if (ctx->ctx_mac_list[where] != NULL) + return MI_FAILURE; + + ctx->ctx_mac_list[where] = strdup(macros); + if (ctx->ctx_mac_list[where] == NULL) + return MI_FAILURE; + + return MI_SUCCESS; +} + +/* +** SMFI_ADDRCPT_PAR -- send an additional recipient to the MTA +** +** Parameters: +** ctx -- Opaque context structure +** rcpt -- recipient address +** args -- ESMTP arguments +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +int +smfi_addrcpt_par(ctx, rcpt, args) + SMFICTX *ctx; + char *rcpt; + char *args; +{ + if (rcpt == NULL || *rcpt == '\0') + return MI_FAILURE; + if (!mi_sendok(ctx, SMFIF_ADDRCPT_PAR)) + return MI_FAILURE; + return send2(ctx, SMFIR_ADDRCPT_PAR, rcpt, args); +} + +/* +** SMFI_ADDRCPT -- send an additional recipient to the MTA +** +** Parameters: +** ctx -- Opaque context structure +** rcpt -- recipient address +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +int +smfi_addrcpt(ctx, rcpt) + SMFICTX *ctx; + char *rcpt; +{ + size_t len; + struct timeval timeout; + + if (rcpt == NULL || *rcpt == '\0') + return MI_FAILURE; + if (!mi_sendok(ctx, SMFIF_ADDRCPT)) + return MI_FAILURE; + timeout.tv_sec = ctx->ctx_timeout; + timeout.tv_usec = 0; + len = strlen(rcpt) + 1; + return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len); +} + +/* +** SMFI_DELRCPT -- send a recipient to be removed to the MTA +** +** Parameters: +** ctx -- Opaque context structure +** rcpt -- recipient address +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +int +smfi_delrcpt(ctx, rcpt) + SMFICTX *ctx; + char *rcpt; +{ + size_t len; + struct timeval timeout; + + if (rcpt == NULL || *rcpt == '\0') + return MI_FAILURE; + if (!mi_sendok(ctx, SMFIF_DELRCPT)) + return MI_FAILURE; + timeout.tv_sec = ctx->ctx_timeout; + timeout.tv_usec = 0; + len = strlen(rcpt) + 1; + return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len); +} + +/* +** SMFI_REPLACEBODY -- send a body chunk to the MTA +** +** Parameters: +** ctx -- Opaque context structure +** bodyp -- body chunk +** bodylen -- length of body chunk +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +int +smfi_replacebody(ctx, bodyp, bodylen) + SMFICTX *ctx; + unsigned char *bodyp; + int bodylen; +{ + int len, off, r; + struct timeval timeout; + + if (bodylen < 0 || + (bodyp == NULL && bodylen > 0)) + return MI_FAILURE; + if (!mi_sendok(ctx, SMFIF_CHGBODY)) + return MI_FAILURE; + timeout.tv_sec = ctx->ctx_timeout; + timeout.tv_usec = 0; + + /* split body chunk if necessary */ + off = 0; + do + { + len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE : + bodylen; + if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY, + (char *) (bodyp + off), len)) != MI_SUCCESS) + return r; + off += len; + bodylen -= len; + } while (bodylen > 0); + return MI_SUCCESS; +} + +/* +** SMFI_QUARANTINE -- quarantine an envelope +** +** Parameters: +** ctx -- Opaque context structure +** reason -- why? +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +int +smfi_quarantine(ctx, reason) + SMFICTX *ctx; + char *reason; +{ + size_t len; + int r; + char *buf; + struct timeval timeout; + + if (reason == NULL || *reason == '\0') + return MI_FAILURE; + if (!mi_sendok(ctx, SMFIF_QUARANTINE)) + return MI_FAILURE; + timeout.tv_sec = ctx->ctx_timeout; + timeout.tv_usec = 0; + len = strlen(reason) + 1; + buf = malloc(len); + if (buf == NULL) + return MI_FAILURE; + (void) memcpy(buf, reason, len); + r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len); + free(buf); + return r; +} + +/* +** MYISENHSC -- check whether a string contains an enhanced status code +** +** Parameters: +** s -- string with possible enhanced status code. +** delim -- delim for enhanced status code. +** +** Returns: +** 0 -- no enhanced status code. +** >4 -- length of enhanced status code. +** +** Side Effects: +** none. +*/ + +static int +myisenhsc(s, delim) + const char *s; + int delim; +{ + int l, h; + + if (s == NULL) + return 0; + if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.')) + return 0; + h = 0; + l = 2; + while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) + ++h; + if (h == 0 || s[l + h] != '.') + return 0; + l += h + 1; + h = 0; + while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h])) + ++h; + if (h == 0 || s[l + h] != delim) + return 0; + return l + h; +} + +/* +** SMFI_SETREPLY -- set the reply code for the next reply to the MTA +** +** Parameters: +** ctx -- Opaque context structure +** rcode -- The three-digit (RFC 821) SMTP reply code. +** xcode -- The extended (RFC 2034) reply code. +** message -- The text part of the SMTP reply. +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +int +smfi_setreply(ctx, rcode, xcode, message) + SMFICTX *ctx; + char *rcode; + char *xcode; + char *message; +{ + size_t len; + char *buf; + + if (rcode == NULL || ctx == NULL) + return MI_FAILURE; + + /* ### <sp> \0 */ + len = strlen(rcode) + 2; + if (len != 5) + return MI_FAILURE; + if ((rcode[0] != '4' && rcode[0] != '5') || + !isascii(rcode[1]) || !isdigit(rcode[1]) || + !isascii(rcode[2]) || !isdigit(rcode[2])) + return MI_FAILURE; + if (xcode != NULL) + { + if (!myisenhsc(xcode, '\0')) + return MI_FAILURE; + len += strlen(xcode) + 1; + } + if (message != NULL) + { + size_t ml; + + /* XXX check also for unprintable chars? */ + if (strpbrk(message, "\r\n") != NULL) + return MI_FAILURE; + ml = strlen(message); + if (ml > MAXREPLYLEN) + return MI_FAILURE; + len += ml + 1; + } + buf = malloc(len); + if (buf == NULL) + return MI_FAILURE; /* oops */ + (void) sm_strlcpy(buf, rcode, len); + (void) sm_strlcat(buf, " ", len); + if (xcode != NULL) + (void) sm_strlcat(buf, xcode, len); + if (message != NULL) + { + if (xcode != NULL) + (void) sm_strlcat(buf, " ", len); + (void) sm_strlcat(buf, message, len); + } + if (ctx->ctx_reply != NULL) + free(ctx->ctx_reply); + ctx->ctx_reply = buf; + return MI_SUCCESS; +} + +/* +** SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA +** +** Parameters: +** ctx -- Opaque context structure +** rcode -- The three-digit (RFC 821) SMTP reply code. +** xcode -- The extended (RFC 2034) reply code. +** txt, ... -- The text part of the SMTP reply, +** MUST be terminated with NULL. +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +int +#if SM_VA_STD +smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...) +#else /* SM_VA_STD */ +smfi_setmlreply(ctx, rcode, xcode, va_alist) + SMFICTX *ctx; + const char *rcode; + const char *xcode; + va_dcl +#endif /* SM_VA_STD */ +{ + size_t len; + size_t rlen; + int args; + char *buf, *txt; + const char *xc; + char repl[16]; + SM_VA_LOCAL_DECL + + if (rcode == NULL || ctx == NULL) + return MI_FAILURE; + + /* ### <sp> */ + len = strlen(rcode) + 1; + if (len != 4) + return MI_FAILURE; + if ((rcode[0] != '4' && rcode[0] != '5') || + !isascii(rcode[1]) || !isdigit(rcode[1]) || + !isascii(rcode[2]) || !isdigit(rcode[2])) + return MI_FAILURE; + if (xcode != NULL) + { + if (!myisenhsc(xcode, '\0')) + return MI_FAILURE; + xc = xcode; + } + else + { + if (rcode[0] == '4') + xc = "4.0.0"; + else + xc = "5.0.0"; + } + + /* add trailing space */ + len += strlen(xc) + 1; + rlen = len; + args = 0; + SM_VA_START(ap, xcode); + while ((txt = SM_VA_ARG(ap, char *)) != NULL) + { + size_t tl; + + tl = strlen(txt); + if (tl > MAXREPLYLEN) + break; + + /* this text, reply codes, \r\n */ + len += tl + 2 + rlen; + if (++args > MAXREPLIES) + break; + + /* XXX check also for unprintable chars? */ + if (strpbrk(txt, "\r\n") != NULL) + break; + } + SM_VA_END(ap); + if (txt != NULL) + return MI_FAILURE; + + /* trailing '\0' */ + ++len; + buf = malloc(len); + if (buf == NULL) + return MI_FAILURE; /* oops */ + (void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc); + (void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-", + xc, " "); + SM_VA_START(ap, xcode); + txt = SM_VA_ARG(ap, char *); + if (txt != NULL) + { + (void) sm_strlcat2(buf, " ", txt, len); + while ((txt = SM_VA_ARG(ap, char *)) != NULL) + { + if (--args <= 1) + repl[3] = ' '; + (void) sm_strlcat2(buf, "\r\n", repl, len); + (void) sm_strlcat(buf, txt, len); + } + } + if (ctx->ctx_reply != NULL) + free(ctx->ctx_reply); + ctx->ctx_reply = buf; + SM_VA_END(ap); + return MI_SUCCESS; +} + +/* +** SMFI_SETPRIV -- set private data +** +** Parameters: +** ctx -- Opaque context structure +** privatedata -- pointer to private data +** +** Returns: +** MI_SUCCESS/MI_FAILURE +*/ + +int +smfi_setpriv(ctx, privatedata) + SMFICTX *ctx; + void *privatedata; +{ + if (ctx == NULL) + return MI_FAILURE; + ctx->ctx_privdata = privatedata; + return MI_SUCCESS; +} + +/* +** SMFI_GETPRIV -- get private data +** +** Parameters: +** ctx -- Opaque context structure +** +** Returns: +** pointer to private data +*/ + +void * +smfi_getpriv(ctx) + SMFICTX *ctx; +{ + if (ctx == NULL) + return NULL; + return ctx->ctx_privdata; +} + +/* +** SMFI_GETSYMVAL -- get the value of a macro +** +** See explanation in mfapi.h about layout of the structures. +** +** Parameters: +** ctx -- Opaque context structure +** symname -- name of macro +** +** Returns: +** value of macro (NULL in case of failure) +*/ + +char * +smfi_getsymval(ctx, symname) + SMFICTX *ctx; + char *symname; +{ + int i; + char **s; + char one[2]; + char braces[4]; + + if (ctx == NULL || symname == NULL || *symname == '\0') + return NULL; + + if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}') + { + one[0] = symname[1]; + one[1] = '\0'; + } + else + one[0] = '\0'; + if (strlen(symname) == 1) + { + braces[0] = '{'; + braces[1] = *symname; + braces[2] = '}'; + braces[3] = '\0'; + } + else + braces[0] = '\0'; + + /* search backwards through the macro array */ + for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i) + { + if ((s = ctx->ctx_mac_ptr[i]) == NULL || + ctx->ctx_mac_buf[i] == NULL) + continue; + while (s != NULL && *s != NULL) + { + if (strcmp(*s, symname) == 0) + return *++s; + if (one[0] != '\0' && strcmp(*s, one) == 0) + return *++s; + if (braces[0] != '\0' && strcmp(*s, braces) == 0) + return *++s; + ++s; /* skip over macro value */ + ++s; /* points to next macro name */ + } + } + return NULL; +} + +/* +** SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature +** timeouts during long milter-side operations +** +** Parameters: +** ctx -- Opaque context structure +** +** Return value: +** MI_SUCCESS/MI_FAILURE +*/ + +int +smfi_progress(ctx) + SMFICTX *ctx; +{ + struct timeval timeout; + + if (ctx == NULL) + return MI_FAILURE; + + timeout.tv_sec = ctx->ctx_timeout; + timeout.tv_usec = 0; + + return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0); +} + +/* +** SMFI_VERSION -- return (runtime) version of libmilter +** +** Parameters: +** major -- (pointer to) major version +** minor -- (pointer to) minor version +** patchlevel -- (pointer to) patchlevel version +** +** Return value: +** MI_SUCCESS +*/ + +int +smfi_version(major, minor, patchlevel) + unsigned int *major; + unsigned int *minor; + unsigned int *patchlevel; +{ + if (major != NULL) + *major = SM_LM_VRS_MAJOR(SMFI_VERSION); + if (minor != NULL) + *minor = SM_LM_VRS_MINOR(SMFI_VERSION); + if (patchlevel != NULL) + *patchlevel = SM_LM_VRS_PLVL(SMFI_VERSION); + return MI_SUCCESS; +} |