diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-05-07 01:42:03 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-05-07 01:42:03 +0300 |
commit | 9f8f65324491b4186f4fd3c2580a8e0b14a248d9 (patch) | |
tree | 4b89cee9f26b33494ed917bab471ae233956bc93 | |
parent | 2ed8f73ceee292855a07483d758d1f7181867ec9 (diff) | |
download | mailfromd-9f8f65324491b4186f4fd3c2580a8e0b14a248d9.tar.gz mailfromd-9f8f65324491b4186f4fd3c2580a8e0b14a248d9.tar.bz2 |
Implement Milter protocol version 6.
* gacopyz/Makefile.am (trans.h): Pass -vheader_file
argument to the trans.awk script.
* gacopyz/dummy.c (smfilter): Update initialization.
* gacopyz/gacopyz.c (trans_fixup): New function.
(convert_sfsistat): Handle new codes.
(make_optneg_buf): New function.
(send_reply): Handle SMFIP_NR_* flags.
Update SMFIC_OPTNEG case.
(shan_optneg): Implement milter 1.0 (proto 6)
(gacopyz_context_loop): Call trans_fixup.
(ok_to_send): Use the negotiated aflags.
(gacopyz_addrcpt_par, gacopyz_chgfrom)
(gacopyz_setsymlist): New functions.
* gacopyz/gacopyz.h (GACOPYZ_VERSION_MAJOR): Raise to 2.
(SMFI_VERSION): Set to 0x01000000
(SMFI_PROT_VERSION, SMFI_PROT_VERSION_MIN): New defines.
(SMFIR_ADDRCPT_PAR, SMFIR_CHGFROM): New defines.
(SMFIP_NR_HDR, SMFIP_SKIP, SMFIP_RCPT_REJ): New defines.
(SMFIP_NR_CONN, SMFIP_NR_HELO, SMFIP_NR_MAIL)
(SMFIP_NR_RCPT, SMFIP_NR_DATA, SMFIP_NR_UNKN)
(SMFIP_NR_EOH, SMFIP_NR_BODY, SMFIP_HDR_LEADSPC)
(SMFI_DEFAULT_PROT, SMFIF_CHGFROM)
(SMFIF_ADDRCPT_PAR): New defines.
(SMFIS_NOREPLY, SMFIS_SKIP, SMFIS_ALL_OPTS): New constants.
(enum macro_index): New data type, from gacopyz_priv.h
(SMFIM_CONNECT, SMFIM_HELO, SMFIM_ENVFROM)
(SMFIM_ENVRCPT, SMFIM_DATA, SMFIM_EOM)
(SMFIM_EOH): New defines.
(struct gacopyz_milter_descr): New member: xxfi_negotiate
(smfi_addrcpt_par, smfi_chgfrom, smfi_setsymlist)
(gacopyz_addrcpt_par, gacopyz_setsymlist)
(gacopyz_chgfrom): New protos.
* gacopyz/gacopyz_priv.h (struct macro_assoc): Rename flags to pflags.
New members: aflags, version, mta_pflags, req_macros.
* gacopyz/server.c (struct gacopyz_srv): Remove flags.
(struct gacopyz_srv.version, acts, proto): Change type.
(gacopyz_srv_create): Change defaults.
(gacopyz_srv_negotiate): Rewrite.
* gacopyz/smfi.c (smfi_addrcpt_par, smfi_chgfrom)
(smfi_setsymlist): New functions.
* gacopyz/trans.awk (END): Print the state_nr_mask array.
* mfd/bi_db.m4: Add missing includes.
* mfd/engine.c: Implement mlfi_negotiate.
* mfd/gram.y (register_macro): Arrange stored macros by
gacopyz macro index. All uses updated.
(get_stage_macro_string): New function.
* mfd/mailfromd.h (get_stage_macro_string): New proto.
(check_tbf_rate): Add proto.
-rw-r--r-- | gacopyz/Makefile.am | 5 | ||||
-rw-r--r-- | gacopyz/dummy.c | 2 | ||||
-rw-r--r-- | gacopyz/gacopyz.c | 397 | ||||
-rw-r--r-- | gacopyz/gacopyz.h | 103 | ||||
-rw-r--r-- | gacopyz/gacopyz_priv.h | 21 | ||||
-rw-r--r-- | gacopyz/server.c | 58 | ||||
-rw-r--r-- | gacopyz/smfi.c | 23 | ||||
-rw-r--r-- | gacopyz/trans.awk | 11 | ||||
-rw-r--r-- | mfd/bi_db.m4 | 1 | ||||
-rw-r--r-- | mfd/engine.c | 29 | ||||
-rw-r--r-- | mfd/gram.y | 113 | ||||
-rw-r--r-- | mfd/mailfromd.h | 6 | ||||
-rw-r--r-- | mfd/tbf_rate.c | 3 |
13 files changed, 601 insertions, 171 deletions
diff --git a/gacopyz/Makefile.am b/gacopyz/Makefile.am index 4dbef83a..7d8c012b 100644 --- a/gacopyz/Makefile.am +++ b/gacopyz/Makefile.am @@ -1,5 +1,5 @@ # This file is part of mailfrom filter. -# Copyright (C) 2006, 2007, 2008 Sergey Poznyakoff +# Copyright (C) 2006, 2007, 2008, 2009 Sergey Poznyakoff # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -33,6 +33,7 @@ EXTRA_DIST=trans.tab trans.awk BUILT_SOURCES=trans.h INCLUDES=-I$(top_srcdir)/gnu -I../gnu -trans.h: ${top_srcdir}/gacopyz/trans.tab ${top_srcdir}/gacopyz/trans.awk +trans.h: ${top_srcdir}/gacopyz/trans.tab ${top_srcdir}/gacopyz/trans.awk ${top_srcdir}/gacopyz/gacopyz.h $(AWK) -f ${top_srcdir}/gacopyz/trans.awk \ + -vheader_file=${top_srcdir}/gacopyz/gacopyz.h\ ${top_srcdir}/gacopyz/trans.tab > trans.h diff --git a/gacopyz/dummy.c b/gacopyz/dummy.c index 01c8eef5..be577300 100644 --- a/gacopyz/dummy.c +++ b/gacopyz/dummy.c @@ -68,6 +68,8 @@ struct smfiDesc smfilter = NULL, /* connection cleanup */ NULL, /* unknown command handler */ NULL, /* data handler */ + NULL, /* negotiate */ + NULL, /* child start */ NULL, /* child finish */ NULL /* idle callback */ diff --git a/gacopyz/gacopyz.c b/gacopyz/gacopyz.c index 02c833e9..f29681be 100644 --- a/gacopyz/gacopyz.c +++ b/gacopyz/gacopyz.c @@ -1,5 +1,5 @@ /* This file is part of gacopyz. - Copyright (C) 2006, 2007, 2008 Sergey Poznyakoff + Copyright (C) 2006, 2007, 2008, 2009 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -526,6 +526,21 @@ trans_ok(enum state from, enum state to) } } +/* Fix up the transition table */ +static void +trans_fixup(SMFICTX *ctx) +{ + transtab[st_conn][st_skip] = !!(ctx->pflags & SMFIP_NOCONNECT); + transtab[st_helo][st_skip] = !!(ctx->pflags & SMFIP_NOHELO); + transtab[st_mail][st_skip] = !!(ctx->pflags & SMFIP_NOMAIL); + transtab[st_rcpt][st_skip] = !!(ctx->pflags & SMFIP_NORCPT); + transtab[st_hdrs][st_skip] = !!(ctx->pflags & SMFIP_NOHDRS); + transtab[st_eohs][st_skip] = !!(ctx->pflags & SMFIP_NOEOH); + transtab[st_body][st_skip] = !!(ctx->pflags & SMFIP_NOBODY); + transtab[st_data][st_skip] = !!(ctx->pflags & SMFIP_NODATA); + transtab[st_unkn][st_skip] = !!(ctx->pflags & SMFIP_NOUNKNOWN); +} + static int ctx_read(SMFICTX *ctx, char *buf, size_t size) { @@ -713,6 +728,84 @@ get_command(SMFICTX *ctx, unsigned char *cmd, size_t *pcount, return MI_SUCCESS; } +#define _GACOPYZ_R_NOREPLY 0 + +static unsigned char +convert_sfsistat(sfsistat stat) +{ + switch (stat) { + case SMFIS_CONTINUE: + return SMFIR_CONTINUE; + case SMFIS_REJECT: + return SMFIR_REJECT; + case SMFIS_DISCARD: + return SMFIR_DISCARD; + case SMFIS_ACCEPT: + return SMFIR_ACCEPT; + case SMFIS_TEMPFAIL: + return SMFIR_TEMPFAIL; + case SMFIS_NOREPLY: + return _GACOPYZ_R_NOREPLY; + case SMFIS_SKIP: + return SMFIR_SKIP; + default: + break; + } + return SMFIR_TEMPFAIL; /* Just in case */ +} + +#define OPTLEN 3*sizeof(gacopyz_uint32_t) + +static int +make_optneg_buf(SMFICTX *ctx, gacopyz_uint32_t *vbuf, + size_t *psize, char **pbuf) +{ + char *buf; + size_t bufsize = 0; + int i; + + for (i = 0; i < maci_max; i++) { + if (ctx->req_macros[i]) + bufsize += strlen(ctx->req_macros[i]) + 1 + + sizeof(gacopyz_uint32_t); + } + + if (bufsize == 0) + buf = (char*) vbuf; + else { + bufsize += OPTLEN; + buf = malloc(bufsize); + if (!buf) + return MI_FAILURE; + vbuf = (gacopyz_uint32_t*)buf; + } + vbuf[0] = htonl(ctx->version); + vbuf[1] = htonl(ctx->aflags); + vbuf[2] = htonl(ctx->pflags); + + if (bufsize) { + /*char *endp = buf + bufsize;*/ + *psize = bufsize; + *pbuf = buf; + + buf += OPTLEN; + for (i = 0; i < maci_max; i++) { + if (ctx->req_macros[i]) { + gacopyz_uint32_t v; + size_t len; + + v = htonl(i); + memcpy(buf, &v, sizeof(v)); + buf += sizeof(v); + len = strlen(ctx->req_macros[i]) + 1; + memcpy(buf, ctx->req_macros[i], len); + buf += len; + } + } + } + return MI_SUCCESS; +} + static int send_reply(SMFICTX *ctx, unsigned char cmd) { @@ -720,8 +813,29 @@ send_reply(SMFICTX *ctx, unsigned char cmd) union header header; char *buf = NULL; size_t bufsize = 0; + char *alloc_mem = NULL; + gacopyz_uint32_t v[3]; + unsigned long nrmask = state_nr_mask[ctx->state]; + + if (nrmask && (ctx->pflags & nrmask) && cmd != _GACOPYZ_R_NOREPLY) { + if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_WARN)) + gacopyz_log(SMI_LOG_WARN, + _("%s: milter claimed not to reply in state %d, but it did"), + ctx->desc->xxfi_name, + ctx->state); + cmd = _GACOPYZ_R_NOREPLY; + } switch (cmd) { + case _GACOPYZ_R_NOREPLY: + if (nrmask && (ctx->pflags & nrmask)) { + if (ctx->mta_pflags & nrmask) + return 0; + else + cmd = SMFIR_CONTINUE; + } + break; + case SMFIR_CONTINUE: break; @@ -748,15 +862,14 @@ send_reply(SMFICTX *ctx, unsigned char cmd) break; case SMFIC_OPTNEG: - { - gacopyz_uint32_t v[3]; - v[0] = htonl(ctx->desc->xxfi_version); - v[1] = htonl(ctx->desc->xxfi_flags); - v[2] = htonl(ctx->flags); buf = (char*) v; bufsize = sizeof v; + if (make_optneg_buf(ctx, v, &bufsize, &alloc_mem) + != MI_SUCCESS) + return MI_FAILURE; + if (alloc_mem) + buf = alloc_mem; break; - } default: /* Ignore */ break; @@ -767,10 +880,12 @@ send_reply(SMFICTX *ctx, unsigned char cmd) header.hdr.size = htonl(bufsize + 1); header.hdr.cmd = cmd; rc = ctx_write(ctx, header.buf, sizeof header.buf); - if (rc != MI_SUCCESS) - return rc; - if (bufsize) - rc = ctx_write(ctx, buf, bufsize); + if (rc == MI_SUCCESS) { + if (bufsize) + rc = ctx_write(ctx, buf, bufsize); + } + if (alloc_mem) + free(alloc_mem); return rc; } @@ -836,24 +951,6 @@ struct state_disp { int macro_ind; }; -static unsigned char -convert_sfsistat(sfsistat stat) -{ - switch (stat) { - case SMFIS_CONTINUE: - return SMFIR_CONTINUE; - case SMFIS_REJECT: - return SMFIR_REJECT; - case SMFIS_DISCARD: - return SMFIR_DISCARD; - case SMFIS_ACCEPT: - return SMFIR_ACCEPT; - case SMFIS_TEMPFAIL: - return SMFIR_TEMPFAIL; - } - return SMFIR_TEMPFAIL; /* Just in case */ -} - static state_ret_type shan_abort(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) { @@ -924,10 +1021,10 @@ static state_ret_type shan_body(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) { if (ctx->desc->xxfi_body) { - *cmd = convert_sfsistat( + *cmd = convert_sfsistat( ctx->desc->xxfi_body(ctx, - arg->string.ptr, - arg->string.len)); + arg->string.ptr, + arg->string.len)); } else *cmd = SMFIR_CONTINUE; return sret_reply; @@ -999,11 +1096,11 @@ shan_connect(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) } } - *cmd = convert_sfsistat( + *cmd = convert_sfsistat( ctx->desc->xxfi_connect(ctx, arg->strings[0], family != SMFIA_UNKNOWN ? - &sockaddr : NULL)); + &sockaddr : NULL)); return sret_reply; } @@ -1011,14 +1108,14 @@ static state_ret_type shan_endm(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) { if (arg->string.len && ctx->desc->xxfi_body) { - sfsistat r = ctx->desc->xxfi_body(ctx, + sfsistat r = ctx->desc->xxfi_body(ctx, arg->string.ptr, arg->string.len); if (r != SMFIS_CONTINUE - && send_reply(ctx, convert_sfsistat(r) != MI_SUCCESS)) + && send_reply(ctx, convert_sfsistat(r)) != MI_SUCCESS) return sret_abort; } - if (ctx->desc->xxfi_eom) + if (ctx->desc->xxfi_eom) *cmd = convert_sfsistat(ctx->desc->xxfi_eom(ctx)); else *cmd = SMFIR_CONTINUE; @@ -1029,7 +1126,7 @@ static state_ret_type shan_helo(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) { if (ctx->desc->xxfi_helo) - *cmd = convert_sfsistat( + *cmd = convert_sfsistat( ctx->desc->xxfi_helo(ctx, arg->string.ptr)); else *cmd = SMFIR_CONTINUE; @@ -1039,8 +1136,8 @@ shan_helo(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) static state_ret_type shan_header(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) { - if (ctx->desc->xxfi_header) - *cmd = convert_sfsistat( + if (ctx->desc->xxfi_header) + *cmd = convert_sfsistat( ctx->desc->xxfi_header(ctx, arg->strings[0], arg->strings[1])); @@ -1053,54 +1150,143 @@ static state_ret_type shan_mail(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) { if (ctx->desc->xxfi_envfrom) { - *cmd = convert_sfsistat( + *cmd = convert_sfsistat( ctx->desc->xxfi_envfrom(ctx, arg->argv.v)); } else *cmd = SMFIR_CONTINUE; return sret_reply; } +#define ALL_NR_FLAGS \ + (SMFIP_NR_CONN|\ + SMFIP_NR_HELO|\ + SMFIP_NR_MAIL|\ + SMFIP_NR_RCPT|\ + SMFIP_NR_DATA|\ + SMFIP_NR_UNKN|\ + SMFIP_NR_HDR|\ + SMFIP_NR_EOH|\ + SMFIP_NR_BODY) + + static state_ret_type shan_optneg(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) { gacopyz_uint32_t val; - - if (arg->ints[0] < ctx->desc->xxfi_version) { + unsigned long mta_aflags, mta_pflags; + + val = arg->ints[0]; + if (val < SMFI_PROT_VERSION_MIN) { if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR)) gacopyz_log(SMI_LOG_ERR, - _("%s: shan_optneg: version mismatch; " - "MTA: %ld < milter: %d"), - ctx->desc->xxfi_name, - arg->ints[0], - ctx->desc->xxfi_version); + _("protocol version too old: %lu"), + val); return sret_abort; } - + if (val > SMFI_PROT_VERSION) + val = SMFI_PROT_VERSION; + ctx->version = val; + val = arg->ints[1]; if (!val) val = SMFI_V1_ACTS; - if ((val & ctx->desc->xxfi_flags) != ctx->desc->xxfi_flags) { - if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR)) - gacopyz_log(SMI_LOG_ERR, - _("%s: shan_optneg: flags %#x do not match " - "actions requirements %#x"), - ctx->desc->xxfi_name, val, - ctx->desc->xxfi_flags); - return sret_abort; - } + mta_aflags = val; val = arg->ints[2]; if (!val) val = SMFI_V1_PROT; - if ((val & ctx->flags) != ctx->flags) { + ctx->mta_pflags = mta_pflags = val; + + ctx->aflags = ctx->desc->xxfi_flags; + if (ctx->version > SMFI_PROT_VERSION_MIN + && ctx->desc->xxfi_negotiate) { + int res; + unsigned long m_aflags = mta_aflags, m_pflags = ctx->pflags; + unsigned long m_f2 = 0, m_f3 = 0; /* reserved */ + + if (mta_pflags & SMFIP_SKIP) + m_pflags |= SMFIP_SKIP; + res = ctx->desc->xxfi_negotiate(ctx, + mta_aflags, + mta_pflags|ALL_NR_FLAGS, + 0, 0, + &m_aflags, &m_pflags, + &m_f2, &m_f3); + switch (res) { + case SMFIS_ALL_OPTS: + ctx->aflags = mta_aflags; + /* ctx->pflags not changed */ + if (mta_pflags & SMFIP_SKIP) + ctx->pflags |= SMFIP_SKIP; + break; + + case SMFIS_CONTINUE: + ctx->aflags = m_aflags; + ctx->pflags = m_pflags; + break; + + default: + if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR)) + gacopyz_log(SMI_LOG_ERR, + _("xxfi_negotiate returned %d (protocol options=0x%lx, actions=0x%lx)"), + res, mta_pflags, mta_aflags); + return sret_abort; + } + + if ((mta_pflags & ctx->pflags) != ctx->pflags) { + unsigned i; + + for (i = 0; i < 32; i++) { + unsigned long bit = 1 << i; + if ((mta_pflags & bit) != bit + && (bit & ALL_NR_FLAGS) == bit) + ctx->pflags &= ~bit; + } + } + } + + if ((mta_aflags & ctx->aflags) != ctx->aflags) { if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR)) gacopyz_log(SMI_LOG_ERR, - _("%s: shan_optneg: flags %#x do not match " - "protocol requirement %#x"), - ctx->desc->xxfi_name, val, - ctx->flags); + _("%s: shan_optneg: NTA flags %#lx do not match " + "actions requirements %#lx"), + ctx->desc->xxfi_name, + mta_aflags, + ctx->aflags); + return sret_abort; + } + + if ((mta_pflags & ctx->pflags) != ctx->pflags) { + /* Disable protocol steps not supported by older MTAs */ + if ((ctx->pflags & SMFIP_NODATA) + && !(mta_pflags & SMFIP_NODATA)) + ctx->pflags &= ~SMFIP_NODATA; + + if ((ctx->pflags & SMFIP_NOUNKNOWN) + && !(mta_pflags & SMFIP_NOUNKNOWN)) + ctx->pflags &= ~SMFIP_NOUNKNOWN; + } + + if ((mta_pflags & ctx->pflags) != ctx->pflags) { + if (GACOPYZ_CTX_LOG_MATCH(ctx, SMI_LOG_ERR)) + gacopyz_log(SMI_LOG_ERR, + _("%s: shan_optneg: MTA flags %#lx do not match " + "protocol requirements %#lx"), + ctx->desc->xxfi_name, + mta_pflags, + ctx->pflags); return sret_abort; } + + if (GACOPYZ_DESC_LOG_MATCH(ctx->desc, SMI_LOG_DEBUG)) + gacopyz_log(SMI_LOG_DEBUG, + "version=%#lx, mta_aflags=%#lx, mta_pflags=%#lx" + " aflags=%#lx, pflags=%#lx", + ctx->version, + mta_aflags, mta_pflags, + ctx->aflags, ctx->pflags); + trans_fixup(ctx); + *cmd = SMFIC_OPTNEG; return sret_reply; } @@ -1108,7 +1294,7 @@ shan_optneg(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) static state_ret_type shan_eoh(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) { - if (ctx->desc->xxfi_eoh) + if (ctx->desc->xxfi_eoh) *cmd = convert_sfsistat(ctx->desc->xxfi_eoh(ctx)); else *cmd = SMFIR_CONTINUE; @@ -1124,7 +1310,7 @@ shan_quit(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) static state_ret_type shan_data(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) { - if (ctx->desc->xxfi_data) + if (ctx->desc->xxfi_data) *cmd = convert_sfsistat(ctx->desc->xxfi_data(ctx)); else *cmd = SMFIR_CONTINUE; @@ -1135,7 +1321,7 @@ static state_ret_type shan_rcpt(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) { if (ctx->desc->xxfi_envrcpt) { - *cmd = convert_sfsistat( + *cmd = convert_sfsistat( ctx->desc->xxfi_envrcpt(ctx, arg->argv.v)); } else *cmd = SMFIR_CONTINUE; @@ -1146,7 +1332,7 @@ static state_ret_type shan_unkn(SMFICTX *ctx, union state_arg *arg, unsigned char *cmd) { if (ctx->desc->xxfi_unknown) { - *cmd = convert_sfsistat( + *cmd = convert_sfsistat( ctx->desc->xxfi_unknown(ctx, arg->string.ptr)); } else *cmd = SMFIR_CONTINUE; @@ -1322,29 +1508,26 @@ gacopyz_context_loop(int fd, struct smfiDesc *desc) ctx.desc = desc; ctx.sd = fd; if (!desc->xxfi_connect) - ctx.flags |= SMFIP_NOCONNECT; + ctx.pflags |= SMFIP_NOCONNECT; if (!desc->xxfi_helo) - ctx.flags |= SMFIP_NOHELO; + ctx.pflags |= SMFIP_NOHELO; if (!desc->xxfi_envfrom) - ctx.flags |= SMFIP_NOMAIL; + ctx.pflags |= SMFIP_NOMAIL; if (!desc->xxfi_envrcpt) - ctx.flags |= SMFIP_NORCPT; + ctx.pflags |= SMFIP_NORCPT; if (!desc->xxfi_header) - ctx.flags |= SMFIP_NOHDRS; + ctx.pflags |= SMFIP_NOHDRS; if (!desc->xxfi_eoh) - ctx.flags |= SMFIP_NOEOH; + ctx.pflags |= SMFIP_NOEOH; if (!desc->xxfi_body) - ctx.flags |= SMFIP_NOBODY; - - /* Fix up the transition table */ - transtab[st_conn][st_skip] = !!(ctx.flags & SMFIP_NOCONNECT); - transtab[st_helo][st_skip] = !!(ctx.flags & SMFIP_NOHELO); - transtab[st_mail][st_skip] = !!(ctx.flags & SMFIP_NOMAIL); - transtab[st_rcpt][st_skip] = !!(ctx.flags & SMFIP_NORCPT); - transtab[st_hdrs][st_skip] = !!(ctx.flags & SMFIP_NOHDRS); - transtab[st_eohs][st_skip] = !!(ctx.flags & SMFIP_NOEOH); - transtab[st_body][st_skip] = !!(ctx.flags & SMFIP_NOBODY); - + ctx.pflags |= SMFIP_NOBODY; + if (!desc->xxfi_data) + ctx.pflags |= SMFIP_NODATA; + if (!desc->xxfi_unknown) + ctx.pflags |= SMFIP_NOUNKNOWN; + + trans_fixup(&ctx); + if (GACOPYZ_DESC_LOG_MATCH(desc, SMI_LOG_DEBUG)) gacopyz_log(SMI_LOG_DEBUG, _("Begin context loop")); @@ -1421,11 +1604,11 @@ gacopyz_context_loop(int fd, struct smfiDesc *desc) clear_macros(&ctx, sd->macro_ind + 1); ret = sd->fn(&ctx, &arg, &cmd); arg_free(&arg, sd->arg_type); - + switch (ret) { case sret_noreply: break; - + case sret_reply: rc = send_reply(&ctx, cmd); break; @@ -1733,12 +1916,15 @@ gacopyz_rcpt_command(SMFICTX *ctx, unsigned char cmd, const char *rcpt) return rc; } +#define gacopyz_arg2_command(ctx, cmd, arg0, arg1) \ + gacopyz_header_command0(ctx, cmd, -1, arg0, arg1) + static int ok_to_send(SMFICTX *ctx, int cmd) { if (!ctx) return 0; - if (cmd && !(ctx->desc->xxfi_flags & cmd)) + if (cmd && !(ctx->aflags & cmd)) return 0; return ctx->state == st_endm; } @@ -1838,3 +2024,40 @@ gacopyz_quarantine(SMFICTX *ctx, const char *reason) return MI_FAILURE; return gacopyz_rcpt_command(ctx, SMFIR_QUARANTINE, reason); } + +int +gacopyz_addrcpt_par(SMFICTX *ctx, const char *rcpt, const char *args) +{ + if (!rcpt || !*rcpt) + return MI_FAILURE; + if (!ok_to_send(ctx, SMFIF_ADDRCPT_PAR)) + return MI_FAILURE; + return gacopyz_arg2_command(ctx, SMFIR_ADDRCPT_PAR, rcpt, args); +} + +int +gacopyz_chgfrom(SMFICTX *ctx, const char *from, const char *args) +{ + if (!from || !*from) + return MI_FAILURE; + if (!ok_to_send(ctx, SMFIF_CHGFROM)) + return MI_FAILURE; + return gacopyz_arg2_command(ctx, SMFIR_CHGFROM, from, args); +} + +int +gacopyz_setsymlist(SMFICTX *ctx, enum macro_index ind, const char *macros) +{ + if (ind < 0 || ind >= maci_max) + return MI_FAILURE; + if (ctx->req_macros[ind]) + free(ctx->req_macros[ind]); + if (macros == NULL) + ctx->req_macros[ind] = NULL; + else { + ctx->req_macros[ind] = strdup(macros); + if (!ctx->req_macros[ind]) + return MI_FAILURE; + } + return MI_SUCCESS; +} diff --git a/gacopyz/gacopyz.h b/gacopyz/gacopyz.h index 706a9c87..f0b5bff2 100644 --- a/gacopyz/gacopyz.h +++ b/gacopyz/gacopyz.h @@ -1,5 +1,5 @@ /* This file is part of gacopyz. - Copyright (C) 2006, 2007, 2008 Sergey Poznyakoff + Copyright (C) 2006, 2007, 2008, 2009 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,11 +42,17 @@ extern "C" { #endif -#define GACOPYZ_VERSION_MAJOR 1 +#define GACOPYZ_VERSION_MAJOR 2 #define GACOPYZ_VERSION_MINOR 0 /* Implementation version number */ -#define SMFI_VERSION 2 +#define SMFI_VERSION 0x01000000 + +/* Milter protocol version */ +#define SMFI_PROT_VERSION 6 + +/* Minimal supported protocol version */ +#define SMFI_PROT_VERSION_MIN 2 /* commands: don't use anything smaller than ' ' */ #define SMFIC_ABORT 'A' /* Abort */ @@ -67,20 +73,26 @@ extern "C" { /* actions (replies) */ #define SMFIR_ADDRCPT '+' /* add recipient */ #define SMFIR_DELRCPT '-' /* remove recipient */ +#define SMFIR_ADDRCPT_PAR '2' /* add recipient */ +#define SMFIR_SHUTDOWN '4' /* 421: shutdown (internal to MTA) */ #define SMFIR_ACCEPT 'a' /* accept */ #define SMFIR_REPLBODY 'b' /* replace body (chunk) */ #define SMFIR_CONTINUE 'c' /* continue */ #define SMFIR_DISCARD 'd' /* discard */ +#define SMFIR_CHGFROM 'e' /* change envelope sender (from) */ #define SMFIR_CONN_FAIL 'f' /* cause a connection failure */ +#define SMFIR_ADDHEADER 'h' /* add header */ +#define SMFIR_INSHEADER 'i' /* insert header */ +#if 0 +#define SMFIR_SETSYMLIST 'l' /* set list of macros (unused) */ +#endif #define SMFIR_CHGHEADER 'm' /* change header */ #define SMFIR_PROGRESS 'p' /* progress */ +#define SMFIR_QUARANTINE 'q' /* quarantine */ #define SMFIR_REJECT 'r' /* reject */ +#define SMFIR_SKIP 's' /* skip */ #define SMFIR_TEMPFAIL 't' /* tempfail */ -#define SMFIR_SHUTDOWN '4' /* 421: shutdown (internal to MTA) */ -#define SMFIR_ADDHEADER 'h' /* add header */ -#define SMFIR_INSHEADER 'i' /* insert header */ #define SMFIR_REPLYCODE 'y' /* reply code etc */ -#define SMFIR_QUARANTINE 'q' /* quarantine */ /* What the MTA can send/filter wants in protocol */ #define SMFIP_NOCONNECT 0x00000001L /* MTA should not send connect info */ @@ -90,17 +102,33 @@ extern "C" { #define SMFIP_NOBODY 0x00000010L /* MTA should not send body */ #define SMFIP_NOHDRS 0x00000020L /* MTA should not send headers */ #define SMFIP_NOEOH 0x00000040L /* MTA should not send EOH */ -#define SMFIP_NOHREPL 0x00000080L /* No reply for headers */ +#define SMFIP_NR_HDR 0x00000080L /* No reply for headers */ +#define SMFIP_NOHREPL SMFIP_NR_HDR /* No reply for headers */ #define SMFIP_NOUNKNOWN 0x00000100L /* MTA should not send unknown - command */ + commands */ #define SMFIP_NODATA 0x00000200L /* MTA should not send DATA */ - +#define SMFIP_SKIP 0x00000400L /* MTA understands SMFIS_SKIP */ +#define SMFIP_RCPT_REJ 0x00000800L /* MTA should also send rejected + RCPTs */ +#define SMFIP_NR_CONN 0x00001000L /* No reply for connect */ +#define SMFIP_NR_HELO 0x00002000L /* No reply for HELO */ +#define SMFIP_NR_MAIL 0x00004000L /* No reply for MAIL */ +#define SMFIP_NR_RCPT 0x00008000L /* No reply for RCPT */ +#define SMFIP_NR_DATA 0x00010000L /* No reply for DATA */ +#define SMFIP_NR_UNKN 0x00020000L /* No reply for UNKN */ +#define SMFIP_NR_EOH 0x00040000L /* No reply for eoh */ +#define SMFIP_NR_BODY 0x00080000L /* No reply for body chunk */ +#define SMFIP_HDR_LEADSPC 0x00100000L /* header value leading space */ + /* The protocol of V1 filter */ #define SMFI_V1_PROT (SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NOMAIL|\ SMFIP_NORCPT|SMFIP_NOBODY|SMFIP_NOHDRS) /* The protocol of V2 filter */ #define SMFI_V2_PROT (SMFI_V1_PROT|SMFIP_NOEOH) +/* All defined prot bits */ +#define SMFI_DEFAULT_PROT 0x001FFFFFL + /* Flags, defining milter capabilities */ #define SMFIF_NONE 0x00000000L /* no flags */ #define SMFIF_ADDHDRS 0x00000001L /* filter may add headers */ @@ -110,12 +138,14 @@ extern "C" { #define SMFIF_DELRCPT 0x00000008L /* filter may delete recipients */ #define SMFIF_CHGHDRS 0x00000010L /* filter may change/delete headers */ #define SMFIF_QUARANTINE 0x00000020L /* filter may quarantine envelope */ - +#define SMFIF_CHGFROM 0x00000040L /* filter may change envelope "from" */ +#define SMFIF_ADDRCPT_PAR 0x00000080L /* add recipients incl. args */ + #define SMFI_V1_ACTS (SMFIF_ADDHDRS|SMFIF_CHGBODY|SMFIF_ADDRCPT|SMFIF_DELRCPT) #define SMFI_V2_ACTS (SMFI_V1_ACTS|SMFIF_CHGHDRS|SMFIF_QUARANTINE) - -#define SMFI_DEFAULT_ACTS SMFI_V2_ACTS -#define SMFI_DEFAULT_PROT SMFI_V2_PROT +#define SMFI_V6_ACTS (SMFI_V2_ACTS|SMFIF_CHGFROM|SMFIF_ADDRCPT_PAR) + +#define SMFI_DEFAULT_ACTS SMFI_V6_ACTS /* Log levels */ #define SMI_LOG_PROTO 0 @@ -157,15 +187,39 @@ typedef enum { SMFIS_REJECT, SMFIS_DISCARD, SMFIS_ACCEPT, - SMFIS_TEMPFAIL + SMFIS_TEMPFAIL, + SMFIS_NOREPLY = 7, + SMFIS_SKIP = 8, + SMFIS_ALL_OPTS = 10 } sfsistat; - + typedef union { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_un sunix; } milter_sockaddr_t; +enum macro_index { + maci_conn, + maci_helo, + maci_mail, + maci_rcpt, + maci_data, + maci_eom, + maci_eoh, + + maci_max, + maci_none = maci_max +}; + +#define SMFIM_CONNECT maci_conn +#define SMFIM_HELO maci_helo +#define SMFIM_ENVFROM maci_mail +#define SMFIM_ENVRCPT maci_rcpt +#define SMFIM_DATA maci_data +#define SMFIM_EOM maci_eom +#define SMFIM_EOH maci_eoh + #define _SOCK_ADDR milter_sockaddr_t #define smfiDesc gacopyz_milter_descr @@ -201,7 +255,13 @@ struct gacopyz_milter_descr sfsistat (*xxfi_unknown) (SMFICTX *, char *); /* SMTP DATA command (SMFI_VERSION > 3) */ sfsistat (*xxfi_data) (SMFICTX *); - + /* Milter negotiation (SMFI_VERSION > 3) */ + sfsistat (*xxfi_negotiate) (SMFICTX *, + unsigned long, unsigned long, + unsigned long, unsigned long, + unsigned long *, unsigned long *, + unsigned long *, unsigned long *); + /* Extensions: */ int (*xxfi_start) (); /* Child start callback */ @@ -234,10 +294,13 @@ int smfi_delrcpt (SMFICTX *, char *); int smfi_progress (SMFICTX *); int smfi_replacebody (SMFICTX *, unsigned char *, int); int smfi_quarantine (SMFICTX *ctx, char *reason); +int smfi_addrcpt_par(SMFICTX *ctx, char *rcpt, char *args); +int smfi_chgfrom(SMFICTX *ctx, char *from, char *args); #define smfi_setpriv gacopyz_setpriv #define smfi_getpriv gacopyz_getpriv size_t smfi_setmaxdatasize (size_t); - +int smfi_setsymlist(SMFICTX *ctx, int stage, char *macros); + /* Replies: */ extern int smfi_setreply (SMFICTX *, char *, char *, char *); int smfi_setmlreply (SMFICTX *, const char *, const char *, ...); @@ -302,7 +365,11 @@ int gacopyz_replace_body(SMFICTX *ctx, const unsigned char *bodyp, size_t bodylen); int gacopyz_progress(SMFICTX *ctx); int gacopyz_quarantine(SMFICTX *ctx, const char *reason); +int gacopyz_addrcpt_par(SMFICTX *ctx, const char *rcpt, const char *args); +int gacopyz_chgfrom(SMFICTX *ctx, const char *from, const char *args); +int gacopyz_setsymlist(SMFICTX *ctx, enum macro_index ind, const char *macros); + int gacopyz_setpriv (SMFICTX *ctx, void *data); void *gacopyz_getpriv (SMFICTX *ctx); diff --git a/gacopyz/gacopyz_priv.h b/gacopyz/gacopyz_priv.h index 3f29546a..76be1611 100644 --- a/gacopyz/gacopyz_priv.h +++ b/gacopyz/gacopyz_priv.h @@ -1,5 +1,5 @@ /* This file is part of gacopyz. - Copyright (C) 2005, 2006, 2007, 2008 Sergey Poznyakoff + Copyright (C) 2005, 2006, 2007, 2008, 2009 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,19 +20,6 @@ #include <gacopyz.h> -enum macro_index { - maci_conn, - maci_helo, - maci_mail, - maci_rcpt, - maci_data, - maci_eom, - maci_eoh, - - maci_max, - maci_none = maci_max -}; - typedef struct macro_assoc { char **argv; char *buffer; @@ -42,9 +29,13 @@ struct smfi_str { struct smfiDesc *desc; /* parent description */ int sd; /* socket descriptor */ int state; /* state; FIXME: should be enum state */ - unsigned long flags; /* protocol flags */ + unsigned long version; /* negotiated protocol version */ + unsigned long pflags; /* protocol flags */ + unsigned long mta_pflags; /* pflags supported by MTA */ + unsigned long aflags; /* milter action flags (from xxfi_flags) */ int nmacros; /* Number of entries in macros */ macro_assoc_t macros[maci_max]; /* Macro tables */ + char *req_macros[maci_max]; /* Required macros */ char *reply; /* reply code */ void *privdata; /* private data */ }; diff --git a/gacopyz/server.c b/gacopyz/server.c index fb0c52f3..a61836e8 100644 --- a/gacopyz/server.c +++ b/gacopyz/server.c @@ -38,17 +38,18 @@ struct gacopyz_srv { char *portspec; /* Port spec */ struct gacopyz_iod iod; enum gacopyz_srv_state state; - unsigned long flags; + gacopyz_macro_def_t def; size_t ndefs; size_t maxdefs; gacopyz_uint32_t source_addr; int onerror; void (*memerror) (gacopyz_srv_t, const char *, unsigned int); - int version; - int acts; - int proto; + unsigned long version; + unsigned long acts; + unsigned long proto; + int (*cb_reply) (gacopyz_srv_t srv, int cmd, int rcmd, void *data); void *cb_data; @@ -305,8 +306,8 @@ gacopyz_srv_create(gacopyz_srv_t *p, const char *name, srv->source_addr = INADDR_ANY; srv->onerror = SMFIR_TEMPFAIL; srv->version = SMFI_VERSION; - srv->proto = SMFI_V2_PROT; - srv->acts = SMFI_V2_ACTS; + srv->proto = SMFI_DEFAULT_PROT; + srv->acts = SMFI_DEFAULT_ACTS; srv->memerror = default_memerror; *p = srv; return MI_SUCCESS; @@ -865,6 +866,7 @@ gacopyz_srv_negotiate(gacopyz_srv_t srv) gacopyz_uint32_t ibuf[3]; size_t size; unsigned char cmd; |