xref: /freebsd/contrib/sendmail/libmilter/smfi.c (revision ee2ea5ceafed78a5bd9810beb9e3ca927180c226)
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 1.1.1.3 2002/04/10 03:05:00 gshapiro 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 static int
271 myisenhsc(s, delim)
272 	const char *s;
273 	int delim;
274 {
275 	int l, h;
276 
277 	if (s == NULL)
278 		return 0;
279 	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
280 		return 0;
281 	h = 0;
282 	l = 2;
283 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
284 		++h;
285 	if (h == 0 || s[l + h] != '.')
286 		return 0;
287 	l += h + 1;
288 	h = 0;
289 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
290 		++h;
291 	if (h == 0 || s[l + h] != delim)
292 		return 0;
293 	return l + h;
294 }
295 
296 /*
297 **  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
298 **
299 **	Parameters:
300 **		ctx -- Opaque context structure
301 **		rcode -- The three-digit (RFC 821) SMTP reply code.
302 **		xcode -- The extended (RFC 2034) reply code.
303 **		message -- The text part of the SMTP reply.
304 **
305 **	Returns:
306 **		MI_SUCCESS/MI_FAILURE
307 */
308 
309 int
310 smfi_setreply(ctx, rcode, xcode, message)
311 	SMFICTX *ctx;
312 	char *rcode;
313 	char *xcode;
314 	char *message;
315 {
316 	size_t len;
317 	char *buf;
318 
319 	if (rcode == NULL || ctx == NULL)
320 		return MI_FAILURE;
321 
322 	/* ### <sp> \0 */
323 	len = strlen(rcode) + 2;
324 	if (len != 5)
325 		return MI_FAILURE;
326 	if ((rcode[0] != '4' && rcode[0] != '5') ||
327 	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
328 	    !isascii(rcode[2]) || !isdigit(rcode[2]))
329 		return MI_FAILURE;
330 	if (xcode != NULL)
331 	{
332 		if (!myisenhsc(xcode, '\0'))
333 			return MI_FAILURE;
334 		len += strlen(xcode) + 1;
335 	}
336 	if (message != NULL)
337 	{
338 		size_t ml;
339 
340 		/* XXX check also for unprintable chars? */
341 		if (strpbrk(message, "\r\n") != NULL)
342 			return MI_FAILURE;
343 		ml = strlen(message);
344 		if (ml > MAXREPLYLEN)
345 			return MI_FAILURE;
346 		len += ml + 1;
347 	}
348 	buf = malloc(len);
349 	if (buf == NULL)
350 		return MI_FAILURE;		/* oops */
351 	(void) sm_strlcpy(buf, rcode, len);
352 	(void) sm_strlcat(buf, " ", len);
353 	if (xcode != NULL)
354 		(void) sm_strlcat(buf, xcode, len);
355 	if (message != NULL)
356 	{
357 		if (xcode != NULL)
358 			(void) sm_strlcat(buf, " ", len);
359 		(void) sm_strlcat(buf, message, len);
360 	}
361 	if (ctx->ctx_reply != NULL)
362 		free(ctx->ctx_reply);
363 	ctx->ctx_reply = buf;
364 	return MI_SUCCESS;
365 }
366 
367 #if _FFR_MULTILINE
368 /*
369 **  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
370 **
371 **	Parameters:
372 **		ctx -- Opaque context structure
373 **		rcode -- The three-digit (RFC 821) SMTP reply code.
374 **		xcode -- The extended (RFC 2034) reply code.
375 **		txt, ... -- The text part of the SMTP reply,
376 **			MUST be terminated with NULL.
377 **
378 **	Returns:
379 **		MI_SUCCESS/MI_FAILURE
380 */
381 
382 int
383 #if SM_VA_STD
384 smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
385 #else /* SM_VA_STD */
386 smfi_setmlreply(ctx, rcode, xcode, va_alist)
387 	SMFICTX *ctx;
388 	const char *rcode;
389 	const char *xcode;
390 	va_dcl
391 #endif /* SM_VA_STD */
392 {
393 	size_t len;
394 	size_t rlen;
395 	int args;
396 	char *buf, *txt;
397 	const char *xc;
398 	char repl[16];
399 	SM_VA_LOCAL_DECL
400 
401 	if (rcode == NULL || ctx == NULL)
402 		return MI_FAILURE;
403 
404 	/* ### <sp> */
405 	len = strlen(rcode) + 1;
406 	if (len != 4)
407 		return MI_FAILURE;
408 	if ((rcode[0] != '4' && rcode[0] != '5') ||
409 	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
410 	    !isascii(rcode[2]) || !isdigit(rcode[2]))
411 		return MI_FAILURE;
412 	if (xcode != NULL)
413 	{
414 		if (!myisenhsc(xcode, '\0'))
415 			return MI_FAILURE;
416 		xc = xcode;
417 	}
418 	else
419 	{
420 		if (rcode[0] == '4')
421 			xc = "4.0.0";
422 		else
423 			xc = "5.0.0";
424 	}
425 
426 	/* add trailing space */
427 	len += strlen(xc) + 1;
428 	rlen = len;
429 	args = 0;
430 	SM_VA_START(ap, xcode);
431 	while ((txt = SM_VA_ARG(ap, char *)) != NULL)
432 	{
433 		size_t tl;
434 
435 		tl = strlen(txt);
436 		if (tl > MAXREPLYLEN)
437 			break;
438 
439 		/* this text, reply codes, \r\n */
440 		len += tl + 2 + rlen;
441 		if (++args > MAXREPLIES)
442 			break;
443 
444 		/* XXX check also for unprintable chars? */
445 		if (strpbrk(txt, "\r\n") != NULL)
446 			break;
447 	}
448 	SM_VA_END(ap);
449 	if (txt != NULL)
450 		return MI_FAILURE;
451 
452 	/* trailing '\0' */
453 	++len;
454 	buf = malloc(len);
455 	if (buf == NULL)
456 		return MI_FAILURE;		/* oops */
457 	(void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
458 	(void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
459 			   xc, " ");
460 	SM_VA_START(ap, xcode);
461 	txt = SM_VA_ARG(ap, char *);
462 	if (txt != NULL)
463 	{
464 		(void) sm_strlcat2(buf, " ", txt, len);
465 		while ((txt = SM_VA_ARG(ap, char *)) != NULL)
466 		{
467 			if (--args <= 1)
468 				repl[3] = ' ';
469 			(void) sm_strlcat2(buf, "\r\n", repl, len);
470 			(void) sm_strlcat(buf, txt, len);
471 		}
472 	}
473 	if (ctx->ctx_reply != NULL)
474 		free(ctx->ctx_reply);
475 	ctx->ctx_reply = buf;
476 	SM_VA_END(ap);
477 	return MI_SUCCESS;
478 }
479 #endif /* _FFR_MULTILINE */
480 
481 /*
482 **  SMFI_SETPRIV -- set private data
483 **
484 **	Parameters:
485 **		ctx -- Opaque context structure
486 **		privatedata -- pointer to private data
487 **
488 **	Returns:
489 **		MI_SUCCESS/MI_FAILURE
490 */
491 
492 int
493 smfi_setpriv(ctx, privatedata)
494 	SMFICTX *ctx;
495 	void *privatedata;
496 {
497 	if (ctx == NULL)
498 		return MI_FAILURE;
499 	ctx->ctx_privdata = privatedata;
500 	return MI_SUCCESS;
501 }
502 
503 /*
504 **  SMFI_GETPRIV -- get private data
505 **
506 **	Parameters:
507 **		ctx -- Opaque context structure
508 **
509 **	Returns:
510 **		pointer to private data
511 */
512 
513 void *
514 smfi_getpriv(ctx)
515 	SMFICTX *ctx;
516 {
517 	if (ctx == NULL)
518 		return NULL;
519 	return ctx->ctx_privdata;
520 }
521 
522 /*
523 **  SMFI_GETSYMVAL -- get the value of a macro
524 **
525 **	See explanation in mfapi.h about layout of the structures.
526 **
527 **	Parameters:
528 **		ctx -- Opaque context structure
529 **		symname -- name of macro
530 **
531 **	Returns:
532 **		value of macro (NULL in case of failure)
533 */
534 
535 char *
536 smfi_getsymval(ctx, symname)
537 	SMFICTX *ctx;
538 	char *symname;
539 {
540 	int i;
541 	char **s;
542 	char one[2];
543 	char braces[4];
544 
545 	if (ctx == NULL || symname == NULL || *symname == '\0')
546 		return NULL;
547 
548 	if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
549 	{
550 		one[0] = symname[1];
551 		one[1] = '\0';
552 	}
553 	else
554 		one[0] = '\0';
555 	if (strlen(symname) == 1)
556 	{
557 		braces[0] = '{';
558 		braces[1] = *symname;
559 		braces[2] = '}';
560 		braces[3] = '\0';
561 	}
562 	else
563 		braces[0] = '\0';
564 
565 	/* search backwards through the macro array */
566 	for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
567 	{
568 		if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
569 		    ctx->ctx_mac_buf[i] == NULL)
570 			continue;
571 		while (s != NULL && *s != NULL)
572 		{
573 			if (strcmp(*s, symname) == 0)
574 				return *++s;
575 			if (one[0] != '\0' && strcmp(*s, one) == 0)
576 				return *++s;
577 			if (braces[0] != '\0' && strcmp(*s, braces) == 0)
578 				return *++s;
579 			++s;	/* skip over macro value */
580 			++s;	/* points to next macro name */
581 		}
582 	}
583 	return NULL;
584 }
585 
586 #if _FFR_SMFI_PROGRESS
587 /*
588 **  SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
589 **		     timeouts during long milter-side operations
590 **
591 **	Parameters:
592 **		ctx -- Opaque context structure
593 **
594 **	Return value:
595 **		MI_SUCCESS/MI_FAILURE
596 */
597 
598 int
599 smfi_progress(ctx)
600 	SMFICTX *ctx;
601 {
602 	struct timeval timeout;
603 
604 	if (ctx == NULL)
605 		return MI_FAILURE;
606 
607 	timeout.tv_sec = ctx->ctx_timeout;
608 	timeout.tv_usec = 0;
609 
610 	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
611 }
612 #endif /* _FFR_SMFI_PROGRESS */
613