smfi.c revision 157001
1/*
2 *  Copyright (c) 1999-2005 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 *
9 */
10
11#include <sm/gen.h>
12SM_RCSID("@(#)$Id: smfi.c,v 8.74 2005/03/30 00:44:07 ca Exp $")
13#include <sm/varargs.h>
14#include "libmilter.h"
15
16static int smfi_header __P((SMFICTX *, int, int, char *, char *));
17static int myisenhsc __P((const char *, int));
18
19/* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */
20#define MAXREPLYLEN	980	/* max. length of a reply string */
21#define MAXREPLIES	32	/* max. number of reply strings */
22
23/*
24**  SMFI_HEADER -- send a header to the MTA
25**
26**	Parameters:
27**		ctx -- Opaque context structure
28**		cmd -- Header modification command
29**		hdridx -- Header index
30**		headerf -- Header field name
31**		headerv -- Header field value
32**
33**
34**	Returns:
35**		MI_SUCCESS/MI_FAILURE
36*/
37
38static int
39smfi_header(ctx, cmd, hdridx, headerf, headerv)
40	SMFICTX *ctx;
41	int cmd;
42	int hdridx;
43	char *headerf;
44	char *headerv;
45{
46	size_t len, l1, l2, offset;
47	int r;
48	mi_int32 v;
49	char *buf;
50	struct timeval timeout;
51
52	if (headerf == NULL || *headerf == '\0' || headerv == NULL)
53		return MI_FAILURE;
54	timeout.tv_sec = ctx->ctx_timeout;
55	timeout.tv_usec = 0;
56	l1 = strlen(headerf) + 1;
57	l2 = strlen(headerv) + 1;
58	len = l1 + l2;
59	if (hdridx >= 0)
60		len += MILTER_LEN_BYTES;
61	buf = malloc(len);
62	if (buf == NULL)
63		return MI_FAILURE;
64	offset = 0;
65	if (hdridx >= 0)
66	{
67		v = htonl(hdridx);
68		(void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
69		offset += MILTER_LEN_BYTES;
70	}
71	(void) memcpy(buf + offset, headerf, l1);
72	(void) memcpy(buf + offset + l1, headerv, l2);
73	r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
74	free(buf);
75	return r;
76}
77
78/*
79**  SMFI_ADDHEADER -- send a new header to the MTA
80**
81**	Parameters:
82**		ctx -- Opaque context structure
83**		headerf -- Header field name
84**		headerv -- Header field value
85**
86**	Returns:
87**		MI_SUCCESS/MI_FAILURE
88*/
89
90int
91smfi_addheader(ctx, headerf, headerv)
92	SMFICTX *ctx;
93	char *headerf;
94	char *headerv;
95{
96	if (!mi_sendok(ctx, SMFIF_ADDHDRS))
97		return MI_FAILURE;
98
99	return smfi_header(ctx, SMFIR_ADDHEADER, -1, headerf, headerv);
100}
101
102/*
103**  SMFI_INSHEADER -- send a new header to the MTA (to be inserted)
104**
105**	Parameters:
106**		ctx -- Opaque context structure
107**  		hdridx -- index into header list where insertion should occur
108**		headerf -- Header field name
109**		headerv -- Header field value
110**
111**	Returns:
112**		MI_SUCCESS/MI_FAILURE
113*/
114
115int
116smfi_insheader(ctx, hdridx, headerf, headerv)
117	SMFICTX *ctx;
118	int hdridx;
119	char *headerf;
120	char *headerv;
121{
122	if (!mi_sendok(ctx, SMFIF_ADDHDRS) || hdridx < 0)
123		return MI_FAILURE;
124
125	return smfi_header(ctx, SMFIR_INSHEADER, hdridx, headerf, headerv);
126}
127
128/*
129**  SMFI_CHGHEADER -- send a changed header to the MTA
130**
131**	Parameters:
132**		ctx -- Opaque context structure
133**		headerf -- Header field name
134**		hdridx -- Header index value
135**		headerv -- Header field value
136**
137**	Returns:
138**		MI_SUCCESS/MI_FAILURE
139*/
140
141int
142smfi_chgheader(ctx, headerf, hdridx, headerv)
143	SMFICTX *ctx;
144	char *headerf;
145	mi_int32 hdridx;
146	char *headerv;
147{
148	if (!mi_sendok(ctx, SMFIF_CHGHDRS) || hdridx < 0)
149		return MI_FAILURE;
150	if (headerv == NULL)
151		headerv = "";
152
153	return smfi_header(ctx, SMFIR_CHGHEADER, hdridx, headerf, headerv);
154}
155
156/*
157**  SMFI_ADDRCPT -- send an additional recipient to the MTA
158**
159**	Parameters:
160**		ctx -- Opaque context structure
161**		rcpt -- recipient address
162**
163**	Returns:
164**		MI_SUCCESS/MI_FAILURE
165*/
166
167int
168smfi_addrcpt(ctx, rcpt)
169	SMFICTX *ctx;
170	char *rcpt;
171{
172	size_t len;
173	struct timeval timeout;
174
175	if (rcpt == NULL || *rcpt == '\0')
176		return MI_FAILURE;
177	if (!mi_sendok(ctx, SMFIF_ADDRCPT))
178		return MI_FAILURE;
179	timeout.tv_sec = ctx->ctx_timeout;
180	timeout.tv_usec = 0;
181	len = strlen(rcpt) + 1;
182	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
183}
184
185/*
186**  SMFI_DELRCPT -- send a recipient to be removed to the MTA
187**
188**	Parameters:
189**		ctx -- Opaque context structure
190**		rcpt -- recipient address
191**
192**	Returns:
193**		MI_SUCCESS/MI_FAILURE
194*/
195
196int
197smfi_delrcpt(ctx, rcpt)
198	SMFICTX *ctx;
199	char *rcpt;
200{
201	size_t len;
202	struct timeval timeout;
203
204	if (rcpt == NULL || *rcpt == '\0')
205		return MI_FAILURE;
206	if (!mi_sendok(ctx, SMFIF_DELRCPT))
207		return MI_FAILURE;
208	timeout.tv_sec = ctx->ctx_timeout;
209	timeout.tv_usec = 0;
210	len = strlen(rcpt) + 1;
211	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
212}
213
214/*
215**  SMFI_REPLACEBODY -- send a body chunk to the MTA
216**
217**	Parameters:
218**		ctx -- Opaque context structure
219**		bodyp -- body chunk
220**		bodylen -- length of body chunk
221**
222**	Returns:
223**		MI_SUCCESS/MI_FAILURE
224*/
225
226int
227smfi_replacebody(ctx, bodyp, bodylen)
228	SMFICTX *ctx;
229	unsigned char *bodyp;
230	int bodylen;
231{
232	int len, off, r;
233	struct timeval timeout;
234
235	if (bodylen < 0 ||
236	    (bodyp == NULL && bodylen > 0))
237		return MI_FAILURE;
238	if (!mi_sendok(ctx, SMFIF_CHGBODY))
239		return MI_FAILURE;
240	timeout.tv_sec = ctx->ctx_timeout;
241	timeout.tv_usec = 0;
242
243	/* split body chunk if necessary */
244	off = 0;
245	do
246	{
247		len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
248						       bodylen;
249		if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
250				(char *) (bodyp + off), len)) != MI_SUCCESS)
251			return r;
252		off += len;
253		bodylen -= len;
254	} while (bodylen > 0);
255	return MI_SUCCESS;
256}
257
258/*
259**  SMFI_QUARANTINE -- quarantine an envelope
260**
261**	Parameters:
262**		ctx -- Opaque context structure
263**		reason -- why?
264**
265**	Returns:
266**		MI_SUCCESS/MI_FAILURE
267*/
268
269int
270smfi_quarantine(ctx, reason)
271	SMFICTX *ctx;
272	char *reason;
273{
274	size_t len;
275	int r;
276	char *buf;
277	struct timeval timeout;
278
279	if (reason == NULL || *reason == '\0')
280		return MI_FAILURE;
281	if (!mi_sendok(ctx, SMFIF_QUARANTINE))
282		return MI_FAILURE;
283	timeout.tv_sec = ctx->ctx_timeout;
284	timeout.tv_usec = 0;
285	len = strlen(reason) + 1;
286	buf = malloc(len);
287	if (buf == NULL)
288		return MI_FAILURE;
289	(void) memcpy(buf, reason, len);
290	r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
291	free(buf);
292	return r;
293}
294
295/*
296**  MYISENHSC -- check whether a string contains an enhanced status code
297**
298**	Parameters:
299**		s -- string with possible enhanced status code.
300**		delim -- delim for enhanced status code.
301**
302**	Returns:
303**		0  -- no enhanced status code.
304**		>4 -- length of enhanced status code.
305**
306**	Side Effects:
307**		none.
308*/
309
310static int
311myisenhsc(s, delim)
312	const char *s;
313	int delim;
314{
315	int l, h;
316
317	if (s == NULL)
318		return 0;
319	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
320		return 0;
321	h = 0;
322	l = 2;
323	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
324		++h;
325	if (h == 0 || s[l + h] != '.')
326		return 0;
327	l += h + 1;
328	h = 0;
329	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
330		++h;
331	if (h == 0 || s[l + h] != delim)
332		return 0;
333	return l + h;
334}
335
336/*
337**  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
338**
339**	Parameters:
340**		ctx -- Opaque context structure
341**		rcode -- The three-digit (RFC 821) SMTP reply code.
342**		xcode -- The extended (RFC 2034) reply code.
343**		message -- The text part of the SMTP reply.
344**
345**	Returns:
346**		MI_SUCCESS/MI_FAILURE
347*/
348
349int
350smfi_setreply(ctx, rcode, xcode, message)
351	SMFICTX *ctx;
352	char *rcode;
353	char *xcode;
354	char *message;
355{
356	size_t len;
357	char *buf;
358
359	if (rcode == NULL || ctx == NULL)
360		return MI_FAILURE;
361
362	/* ### <sp> \0 */
363	len = strlen(rcode) + 2;
364	if (len != 5)
365		return MI_FAILURE;
366	if ((rcode[0] != '4' && rcode[0] != '5') ||
367	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
368	    !isascii(rcode[2]) || !isdigit(rcode[2]))
369		return MI_FAILURE;
370	if (xcode != NULL)
371	{
372		if (!myisenhsc(xcode, '\0'))
373			return MI_FAILURE;
374		len += strlen(xcode) + 1;
375	}
376	if (message != NULL)
377	{
378		size_t ml;
379
380		/* XXX check also for unprintable chars? */
381		if (strpbrk(message, "\r\n") != NULL)
382			return MI_FAILURE;
383		ml = strlen(message);
384		if (ml > MAXREPLYLEN)
385			return MI_FAILURE;
386		len += ml + 1;
387	}
388	buf = malloc(len);
389	if (buf == NULL)
390		return MI_FAILURE;		/* oops */
391	(void) sm_strlcpy(buf, rcode, len);
392	(void) sm_strlcat(buf, " ", len);
393	if (xcode != NULL)
394		(void) sm_strlcat(buf, xcode, len);
395	if (message != NULL)
396	{
397		if (xcode != NULL)
398			(void) sm_strlcat(buf, " ", len);
399		(void) sm_strlcat(buf, message, len);
400	}
401	if (ctx->ctx_reply != NULL)
402		free(ctx->ctx_reply);
403	ctx->ctx_reply = buf;
404	return MI_SUCCESS;
405}
406
407/*
408**  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
409**
410**	Parameters:
411**		ctx -- Opaque context structure
412**		rcode -- The three-digit (RFC 821) SMTP reply code.
413**		xcode -- The extended (RFC 2034) reply code.
414**		txt, ... -- The text part of the SMTP reply,
415**			MUST be terminated with NULL.
416**
417**	Returns:
418**		MI_SUCCESS/MI_FAILURE
419*/
420
421int
422#if SM_VA_STD
423smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
424#else /* SM_VA_STD */
425smfi_setmlreply(ctx, rcode, xcode, va_alist)
426	SMFICTX *ctx;
427	const char *rcode;
428	const char *xcode;
429	va_dcl
430#endif /* SM_VA_STD */
431{
432	size_t len;
433	size_t rlen;
434	int args;
435	char *buf, *txt;
436	const char *xc;
437	char repl[16];
438	SM_VA_LOCAL_DECL
439
440	if (rcode == NULL || ctx == NULL)
441		return MI_FAILURE;
442
443	/* ### <sp> */
444	len = strlen(rcode) + 1;
445	if (len != 4)
446		return MI_FAILURE;
447	if ((rcode[0] != '4' && rcode[0] != '5') ||
448	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
449	    !isascii(rcode[2]) || !isdigit(rcode[2]))
450		return MI_FAILURE;
451	if (xcode != NULL)
452	{
453		if (!myisenhsc(xcode, '\0'))
454			return MI_FAILURE;
455		xc = xcode;
456	}
457	else
458	{
459		if (rcode[0] == '4')
460			xc = "4.0.0";
461		else
462			xc = "5.0.0";
463	}
464
465	/* add trailing space */
466	len += strlen(xc) + 1;
467	rlen = len;
468	args = 0;
469	SM_VA_START(ap, xcode);
470	while ((txt = SM_VA_ARG(ap, char *)) != NULL)
471	{
472		size_t tl;
473
474		tl = strlen(txt);
475		if (tl > MAXREPLYLEN)
476			break;
477
478		/* this text, reply codes, \r\n */
479		len += tl + 2 + rlen;
480		if (++args > MAXREPLIES)
481			break;
482
483		/* XXX check also for unprintable chars? */
484		if (strpbrk(txt, "\r\n") != NULL)
485			break;
486	}
487	SM_VA_END(ap);
488	if (txt != NULL)
489		return MI_FAILURE;
490
491	/* trailing '\0' */
492	++len;
493	buf = malloc(len);
494	if (buf == NULL)
495		return MI_FAILURE;		/* oops */
496	(void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
497	(void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
498			   xc, " ");
499	SM_VA_START(ap, xcode);
500	txt = SM_VA_ARG(ap, char *);
501	if (txt != NULL)
502	{
503		(void) sm_strlcat2(buf, " ", txt, len);
504		while ((txt = SM_VA_ARG(ap, char *)) != NULL)
505		{
506			if (--args <= 1)
507				repl[3] = ' ';
508			(void) sm_strlcat2(buf, "\r\n", repl, len);
509			(void) sm_strlcat(buf, txt, len);
510		}
511	}
512	if (ctx->ctx_reply != NULL)
513		free(ctx->ctx_reply);
514	ctx->ctx_reply = buf;
515	SM_VA_END(ap);
516	return MI_SUCCESS;
517}
518
519/*
520**  SMFI_SETPRIV -- set private data
521**
522**	Parameters:
523**		ctx -- Opaque context structure
524**		privatedata -- pointer to private data
525**
526**	Returns:
527**		MI_SUCCESS/MI_FAILURE
528*/
529
530int
531smfi_setpriv(ctx, privatedata)
532	SMFICTX *ctx;
533	void *privatedata;
534{
535	if (ctx == NULL)
536		return MI_FAILURE;
537	ctx->ctx_privdata = privatedata;
538	return MI_SUCCESS;
539}
540
541/*
542**  SMFI_GETPRIV -- get private data
543**
544**	Parameters:
545**		ctx -- Opaque context structure
546**
547**	Returns:
548**		pointer to private data
549*/
550
551void *
552smfi_getpriv(ctx)
553	SMFICTX *ctx;
554{
555	if (ctx == NULL)
556		return NULL;
557	return ctx->ctx_privdata;
558}
559
560/*
561**  SMFI_GETSYMVAL -- get the value of a macro
562**
563**	See explanation in mfapi.h about layout of the structures.
564**
565**	Parameters:
566**		ctx -- Opaque context structure
567**		symname -- name of macro
568**
569**	Returns:
570**		value of macro (NULL in case of failure)
571*/
572
573char *
574smfi_getsymval(ctx, symname)
575	SMFICTX *ctx;
576	char *symname;
577{
578	int i;
579	char **s;
580	char one[2];
581	char braces[4];
582
583	if (ctx == NULL || symname == NULL || *symname == '\0')
584		return NULL;
585
586	if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
587	{
588		one[0] = symname[1];
589		one[1] = '\0';
590	}
591	else
592		one[0] = '\0';
593	if (strlen(symname) == 1)
594	{
595		braces[0] = '{';
596		braces[1] = *symname;
597		braces[2] = '}';
598		braces[3] = '\0';
599	}
600	else
601		braces[0] = '\0';
602
603	/* search backwards through the macro array */
604	for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
605	{
606		if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
607		    ctx->ctx_mac_buf[i] == NULL)
608			continue;
609		while (s != NULL && *s != NULL)
610		{
611			if (strcmp(*s, symname) == 0)
612				return *++s;
613			if (one[0] != '\0' && strcmp(*s, one) == 0)
614				return *++s;
615			if (braces[0] != '\0' && strcmp(*s, braces) == 0)
616				return *++s;
617			++s;	/* skip over macro value */
618			++s;	/* points to next macro name */
619		}
620	}
621	return NULL;
622}
623
624/*
625**  SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
626**		     timeouts during long milter-side operations
627**
628**	Parameters:
629**		ctx -- Opaque context structure
630**
631**	Return value:
632**		MI_SUCCESS/MI_FAILURE
633*/
634
635int
636smfi_progress(ctx)
637	SMFICTX *ctx;
638{
639	struct timeval timeout;
640
641	if (ctx == NULL)
642		return MI_FAILURE;
643
644	timeout.tv_sec = ctx->ctx_timeout;
645	timeout.tv_usec = 0;
646
647	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
648}
649