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