📄 milter.c
字号:
static char *
milter_command(command, data, sz, macros, e, state)
char command;
void *data;
ssize_t sz;
char **macros;
ENVELOPE *e;
char *state;
{
int i;
char *response = NULL;
if (tTd(64, 10))
dprintf("milter_command: cmd %c len %ld\n",
(char) command, (long) sz);
*state = SMFIR_CONTINUE;
for (i = 0; InputFilters[i] != NULL; i++)
{
struct milter *m = InputFilters[i];
/* sanity check */
if (m->mf_sock < 0 ||
(m->mf_state != SMFS_OPEN && m->mf_state != SMFS_INMSG))
continue;
/* send macros (regardless of whether we send command) */
if (macros != NULL && macros[0] != NULL)
{
milter_send_macros(m, macros, command, e);
if (m->mf_state == SMFS_ERROR)
{
MILTER_CHECK_ERROR(continue);
break;
}
}
response = milter_send_command(m, command, data, sz, e, state);
if (*state != SMFIR_CONTINUE)
break;
}
return response;
}
/*
** MILTER_NEGOTIATE -- get version and flags from filter
**
** Parameters:
** m -- milter filter structure.
** e -- current envelope.
**
** Returns:
** 0 on success, -1 otherwise
*/
static int
milter_negotiate(m, e)
struct milter *m;
ENVELOPE *e;
{
char rcmd;
mi_int32 fvers;
mi_int32 fflags;
mi_int32 pflags;
char *response;
ssize_t rlen;
char data[MILTER_OPTLEN];
/* sanity check */
if (m->mf_sock < 0 || m->mf_state != SMFS_OPEN)
{
if (LogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
"milter_negotiate(%s): impossible state",
m->mf_name);
milter_error(m);
return -1;
}
fvers = htonl(SMFI_VERSION);
fflags = htonl(SMFI_CURR_ACTS);
pflags = htonl(SMFI_CURR_PROT);
(void) memcpy(data, (char *) &fvers, MILTER_LEN_BYTES);
(void) memcpy(data + MILTER_LEN_BYTES,
(char *) &fflags, MILTER_LEN_BYTES);
(void) memcpy(data + (MILTER_LEN_BYTES * 2),
(char *) &pflags, MILTER_LEN_BYTES);
(void) milter_write(m, SMFIC_OPTNEG, data, sizeof data,
m->mf_timeout[SMFTO_WRITE], e);
if (m->mf_state == SMFS_ERROR)
return -1;
response = milter_read(m, &rcmd, &rlen, m->mf_timeout[SMFTO_READ], e);
if (m->mf_state == SMFS_ERROR)
return -1;
if (rcmd != SMFIC_OPTNEG)
{
if (tTd(64, 5))
dprintf("milter_negotiate(%s): returned %c instead of %c\n",
m->mf_name, rcmd, SMFIC_OPTNEG);
if (LogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
"milter_negotiate(%s): returned %c instead of %c",
m->mf_name, rcmd, SMFIC_OPTNEG);
if (response != NULL)
free(response);
milter_error(m);
return -1;
}
/* Make sure we have enough bytes for the version */
if (response == NULL || rlen < MILTER_LEN_BYTES)
{
if (tTd(64, 5))
dprintf("milter_negotiate(%s): did not return valid info\n",
m->mf_name);
if (LogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
"milter_negotiate(%s): did not return valid info",
m->mf_name);
if (response != NULL)
free(response);
milter_error(m);
return -1;
}
/* extract information */
(void) memcpy((char *) &fvers, response, MILTER_LEN_BYTES);
/* Now make sure we have enough for the feature bitmap */
if (rlen != MILTER_OPTLEN)
{
if (tTd(64, 5))
dprintf("milter_negotiate(%s): did not return enough info\n",
m->mf_name);
if (LogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
"milter_negotiate(%s): did not return enough info",
m->mf_name);
if (response != NULL)
free(response);
milter_error(m);
return -1;
}
(void) memcpy((char *) &fflags, response + MILTER_LEN_BYTES,
MILTER_LEN_BYTES);
(void) memcpy((char *) &pflags, response + (MILTER_LEN_BYTES * 2),
MILTER_LEN_BYTES);
free(response);
response = NULL;
m->mf_fvers = ntohl(fvers);
m->mf_fflags = ntohl(fflags);
m->mf_pflags = ntohl(pflags);
/* check for version compatibility */
if (m->mf_fvers == 1 ||
m->mf_fvers > SMFI_VERSION)
{
if (tTd(64, 5))
dprintf("milter_negotiate(%s): version %lu != MTA milter version %d\n",
m->mf_name, m->mf_fvers, SMFI_VERSION);
if (LogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
"milter_negotiate(%s): version %ld != MTA milter version %d",
m->mf_name, m->mf_fvers, SMFI_VERSION);
milter_error(m);
return -1;
}
/* check for filter feature mismatch */
if ((m->mf_fflags & SMFI_CURR_ACTS) != m->mf_fflags)
{
if (tTd(64, 5))
dprintf("milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n",
m->mf_name, m->mf_fflags,
(u_long) SMFI_CURR_ACTS);
if (LogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
"milter_negotiate(%s): filter abilities 0x%lx != MTA milter abilities 0x%lx\n",
m->mf_name, m->mf_fflags,
(u_long) SMFI_CURR_ACTS);
milter_error(m);
return -1;
}
/* check for protocol feature mismatch */
if ((m->mf_pflags & SMFI_CURR_PROT) != m->mf_pflags)
{
if (tTd(64, 5))
dprintf("milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n",
m->mf_name, m->mf_pflags,
(u_long) SMFI_CURR_PROT);
if (LogLevel > 0)
sm_syslog(LOG_ERR, e->e_id,
"milter_negotiate(%s): protocol abilities 0x%lx != MTA milter abilities 0x%lx\n",
m->mf_name, m->mf_pflags,
(u_long) SMFI_CURR_PROT);
milter_error(m);
return -1;
}
if (tTd(64, 5))
dprintf("milter_negotiate(%s): version %lu, fflags 0x%lx, pflags 0x%lx\n",
m->mf_name, m->mf_fvers, m->mf_fflags, m->mf_pflags);
return 0;
}
/*
** MILTER_PER_CONNECTION_CHECK -- checks on per-connection commands
**
** Reduce code duplication by putting these checks in one place
**
** Parameters:
** e -- current envelope.
**
** Returns:
** none
*/
static void
milter_per_connection_check(e)
ENVELOPE *e;
{
int i;
/* see if we are done with any of the filters */
for (i = 0; InputFilters[i] != NULL; i++)
{
struct milter *m = InputFilters[i];
if (m->mf_state == SMFS_DONE)
milter_quit_filter(m, e);
}
}
/*
** MILTER_ERROR -- Put a milter filter into error state
**
** Parameters:
** m -- the broken filter.
**
** Returns:
** none
*/
static void
milter_error(m)
struct milter *m;
{
/*
** We could send a quit here but
** we may have gotten here due to
** an I/O error so we don't want
** to try to make things worse.
*/
if (m->mf_sock >= 0)
{
(void) close(m->mf_sock);
m->mf_sock = -1;
}
m->mf_state = SMFS_ERROR;
}
/*
** MILTER_HEADERS -- send headers to a single milter filter
**
** Parameters:
** m -- current filter.
** e -- current envelope.
** state -- return state from response.
**
** Returns:
** response string (may be NULL)
*/
static char *
milter_headers(m, e, state)
struct milter *m;
ENVELOPE *e;
char *state;
{
char *response = NULL;
HDR *h;
for (h = e->e_header; h != NULL; h = h->h_link)
{
char *buf;
ssize_t s;
/* don't send over deleted headers */
if (h->h_value == NULL)
{
/* strip H_USER so not counted in milter_chgheader() */
h->h_flags &= ~H_USER;
continue;
}
/* skip auto-generated */
if (!bitset(H_USER, h->h_flags))
continue;
if (tTd(64, 10))
dprintf("milter_headers: %s: %s\n",
h->h_field, h->h_value);
s = strlen(h->h_field) + 1 +
strlen(h->h_value) + 1;
buf = (char *) xalloc(s);
snprintf(buf, s, "%s%c%s", h->h_field, '\0', h->h_value);
/* send it over */
response = milter_send_command(m, SMFIC_HEADER, buf,
s, e, state);
free(buf);
if (m->mf_state == SMFS_ERROR ||
m->mf_state == SMFS_DONE ||
*state != SMFIR_CONTINUE)
break;
}
return response;
}
/*
** MILTER_BODY -- send the body to a filter
**
** Parameters:
** m -- current filter.
** e -- current envelope.
** state -- return state from response.
**
** Returns:
** response string (may be NULL)
*/
static char *
milter_body(m, e, state)
struct milter *m;
ENVELOPE *e;
char *state;
{
char bufchar = '\0';
char prevchar = '\0';
int c;
char *response = NULL;
char *bp;
char buf[MILTER_CHUNK_SIZE];
if (tTd(64, 10))
dprintf("milter_body\n");
if (bfrewind(e->e_dfp) < 0)
{
ExitStat = EX_IOERR;
*state = SMFIR_TEMPFAIL;
syserr("milter_body: %s/df%s: rewind error",
qid_printqueue(e->e_queuedir), e->e_id);
return NULL;
}
bp = buf;
while ((c = getc(e->e_dfp)) != EOF)
{
/* Change LF to CRLF */
if (c == '\n')
{
/* Not a CRLF already? */
if (prevchar != '\r')
{
/* Room for CR now? */
if (bp + 2 > &buf[sizeof buf])
{
/* No room, buffer LF */
bufchar = c;
/* and send CR now */
c = '\r';
}
else
{
/* Room to do it now */
*bp++ = '\r';
prevchar = '\r';
}
}
}
*bp++ = (char) c;
prevchar = c;
if (bp >= &buf[sizeof buf])
{
/* send chunk */
response = milter_send_command(m, SMFIC_BODY, buf,
bp - buf, e, state);
bp = buf;
if (bufchar != '\0')
{
*bp++ = bufchar;
bufchar = '\0';
prevchar = bufchar;
}
}
if (m->mf_state == SMFS_ERROR ||
m->mf_state == SMFS_DONE ||
*state != SMFIR_CONTINUE)
break;
}
/* check for read errors */
if (ferror(e->e_dfp))
{
ExitStat = EX_IOERR;
if (*state == SMFIR_CONTINUE ||
*state == SMFIR_ACCEPT)
{
*state = SMFIR_TEMPFAIL;
if (response != NULL)
{
free(response);
response = NULL;
}
}
syserr("milter_body: %s/df%s: read error",
qid_printqueue(e->e_queuedir), e->e_id);
return response;
}
/* send last body chunk */
if (bp > buf &&
m->mf_state != SMFS_ERROR &&
m->mf_state != SMFS_DONE &&
*state == SMFIR_CONTINUE)
{
/* send chunk */
response = milter_send_command(m, SMFIC_BODY, buf, bp - buf,
e, state);
bp = buf;
}
return response;
}
/*
** Actions
*/
/*
** MILTER_ADDHEADER -- Add the supplied header to the message
**
** Parameters:
** response -- encoded form of header/value.
** rlen -- length of response.
** e -- current envelope.
**
** Returns:
** none
*/
static void
milter_addheader(response, rlen, e)
char *response;
ssize_t rlen;
ENVELOPE *e;
{
char *val;
if (tTd(64, 10))
dprintf("milter_addheader: ");
/* sanity checks */
if (response == NULL)
{
if (tTd(64, 10))
dprintf("NULL response\n");
return;
}
if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
{
if (tTd(64, 10))
dprintf("didn't follow protocol (total len)\n");
return;
}
/* Find separating NUL */
val = response + strlen(response) + 1;
/* another sanity check */
if (strlen(response) + strlen(val) + 2 != (size_t) rlen)
{
if (tTd(64, 10))
dprintf("didn't follow protocol (part len)\n");
return;
}
if (*response == '\0')
{
if (tTd(64, 10))
dprintf("empty field name\n");
return;
}
/* add to e_msgsize */
e->e_msgsize += strlen(response) + 2 + strlen(val);
if (tTd(64, 10))
dprintf("Add %s: %s\n", response, val);
addheader(newstr(response), val, H_USER, &e->e_header);
}
/*
** MILTER_CHANGEHEADER -- Change the supplied header in the message
**
** Parameters:
** response -- encoded form of header/index/value.
** rlen -- length of response.
** e -- current envelope.
**
** Returns:
** none
*/
static void
milter_changeheader(response, rlen, e)
char *response;
ssize_t rlen;
ENVELOPE *e;
{
mi_int32 i, index;
char *field, *val;
HDR *h;
if (tTd(64, 10))
dprintf("milter_changeheader: ");
/* sanity checks */
if (response == NULL)
{
if (tTd(64, 10))
dprintf("NULL response\n");
return;
}
if (rlen < 2 || strlen(response) + 1 >= (size_t) rlen)
{
if (tTd(64, 10))
dprintf("didn't follow protocol (total len)\n");
return;
}
/* Find separating NUL */
(void) memcpy((char *) &i, response, MILTER_LEN_BYTES);
index = ntohl(i);
field = response + MILTER_LEN_BYTES;
val = field + strlen(field) + 1;
/* another sanity check */
if (MILTER_LEN_BYTES + strlen(field) + 1 +
strlen(val) + 1 != (size_t) rlen)
{
if (tTd(64, 10))
dprintf("didn't follow protocol (part len)\n");
return;
}
if (*field == '\0')
{
if (tTd(64, 10))
dprintf("empty field name\n");
return;
}
for (h = e->e_header; h != NULL; h = h->h_link)
{
if (bitset(H_USER, h->h_flags) &&
strcasecmp(h->h_field, field) == 0 &&
--index <= 0)
break;
}
if (h == NULL)
{
if (*val == '\0')
{
if (tTd(64, 10))
dprintf("Delete (noop) %s:\n", field);
}
else
{
/* treat modify value with no existing header as add */
if (tTd(64, 10))
dprintf("Add %s: %s\n", field, val);
addheader(newstr(field), val, H_USER, &e->e_header);
}
return;
}
if (tTd(64, 10))
{
if (*val == '\0')
{
dprintf("Delete %s: %s\n", field,
h->h_value == NULL ? "<NULL>" : h->h_value);
}
else
{
dprintf("Change %s: from %s to %s\n",
field,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -