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