xref: /freebsd/contrib/sendmail/libmilter/smfi.c (revision eacee0ff7ec955b32e09515246bd97b6edcd2b0f)
1 /*
2  *  Copyright (c) 1999-2001 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.57 2001/11/20 18:47:49 ca 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 **  SMFI_ADDRCPT -- send an additional recipient to the MTA
116 **
117 **	Parameters:
118 **		ctx -- Opaque context structure
119 **		rcpt -- recipient address
120 **
121 **	Returns:
122 **		MI_SUCCESS/MI_FAILURE
123 */
124 
125 int
126 smfi_addrcpt(ctx, rcpt)
127 	SMFICTX *ctx;
128 	char *rcpt;
129 {
130 	size_t len;
131 	struct timeval timeout;
132 
133 	if (rcpt == NULL || *rcpt == '\0')
134 		return MI_FAILURE;
135 	if (!mi_sendok(ctx, SMFIF_ADDRCPT))
136 		return MI_FAILURE;
137 	timeout.tv_sec = ctx->ctx_timeout;
138 	timeout.tv_usec = 0;
139 	len = strlen(rcpt) + 1;
140 	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
141 }
142 /*
143 **  SMFI_DELRCPT -- send a recipient to be removed to the MTA
144 **
145 **	Parameters:
146 **		ctx -- Opaque context structure
147 **		rcpt -- recipient address
148 **
149 **	Returns:
150 **		MI_SUCCESS/MI_FAILURE
151 */
152 
153 int
154 smfi_delrcpt(ctx, rcpt)
155 	SMFICTX *ctx;
156 	char *rcpt;
157 {
158 	size_t len;
159 	struct timeval timeout;
160 
161 	if (rcpt == NULL || *rcpt == '\0')
162 		return MI_FAILURE;
163 	if (!mi_sendok(ctx, SMFIF_DELRCPT))
164 		return MI_FAILURE;
165 	timeout.tv_sec = ctx->ctx_timeout;
166 	timeout.tv_usec = 0;
167 	len = strlen(rcpt) + 1;
168 	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
169 }
170 /*
171 **  SMFI_REPLACEBODY -- send a body chunk to the MTA
172 **
173 **	Parameters:
174 **		ctx -- Opaque context structure
175 **		bodyp -- body chunk
176 **		bodylen -- length of body chunk
177 **
178 **	Returns:
179 **		MI_SUCCESS/MI_FAILURE
180 */
181 
182 int
183 smfi_replacebody(ctx, bodyp, bodylen)
184 	SMFICTX *ctx;
185 	unsigned char *bodyp;
186 	int bodylen;
187 {
188 	int len, off, r;
189 	struct timeval timeout;
190 
191 	if (bodylen < 0 ||
192 	    (bodyp == NULL && bodylen > 0))
193 		return MI_FAILURE;
194 	if (!mi_sendok(ctx, SMFIF_CHGBODY))
195 		return MI_FAILURE;
196 	timeout.tv_sec = ctx->ctx_timeout;
197 	timeout.tv_usec = 0;
198 
199 	/* split body chunk if necessary */
200 	off = 0;
201 	while (bodylen > 0)
202 	{
203 		len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
204 						       bodylen;
205 		if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
206 				(char *) (bodyp + off), len)) != MI_SUCCESS)
207 			return r;
208 		off += len;
209 		bodylen -= len;
210 	}
211 	return MI_SUCCESS;
212 }
213 #if _FFR_QUARANTINE
214 /*
215 **  SMFI_QUARANTINE -- quarantine an envelope
216 **
217 **	Parameters:
218 **		ctx -- Opaque context structure
219 **		reason -- why?
220 **
221 **	Returns:
222 **		MI_SUCCESS/MI_FAILURE
223 */
224 
225 int
226 smfi_quarantine(ctx, reason)
227 	SMFICTX *ctx;
228 	char *reason;
229 {
230 	size_t len;
231 	int r;
232 	char *buf;
233 	struct timeval timeout;
234 
235 	if (reason == NULL || *reason == '\0')
236 		return MI_FAILURE;
237 	if (!mi_sendok(ctx, SMFIF_QUARANTINE))
238 		return MI_FAILURE;
239 	timeout.tv_sec = ctx->ctx_timeout;
240 	timeout.tv_usec = 0;
241 	len = strlen(reason) + 1;
242 	buf = malloc(len);
243 	if (buf == NULL)
244 		return MI_FAILURE;
245 	(void) memcpy(buf, reason, len);
246 	r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
247 	free(buf);
248 	return r;
249 }
250 #endif /* _FFR_QUARANTINE */
251 
252 /*
253 **  MYISENHSC -- check whether a string contains an enhanced status code
254 **
255 **	Parameters:
256 **		s -- string with possible enhanced status code.
257 **		delim -- delim for enhanced status code.
258 **
259 **	Returns:
260 **		0  -- no enhanced status code.
261 **		>4 -- length of enhanced status code.
262 **
263 **	Side Effects:
264 **		none.
265 */
266 static int
267 myisenhsc(s, delim)
268 	const char *s;
269 	int delim;
270 {
271 	int l, h;
272 
273 	if (s == NULL)
274 		return 0;
275 	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
276 		return 0;
277 	h = 0;
278 	l = 2;
279 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
280 		++h;
281 	if (h == 0 || s[l + h] != '.')
282 		return 0;
283 	l += h + 1;
284 	h = 0;
285 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
286 		++h;
287 	if (h == 0 || s[l + h] != delim)
288 		return 0;
289 	return l + h;
290 }
291 
292 /*
293 **  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
294 **
295 **	Parameters:
296 **		ctx -- Opaque context structure
297 **		rcode -- The three-digit (RFC 821) SMTP reply code.
298 **		xcode -- The extended (RFC 2034) reply code.
299 **		message -- The text part of the SMTP reply.
300 **
301 **	Returns:
302 **		MI_SUCCESS/MI_FAILURE
303 */
304 
305 int
306 smfi_setreply(ctx, rcode, xcode, message)
307 	SMFICTX *ctx;
308 	char *rcode;
309 	char *xcode;
310 	char *message;
311 {
312 	size_t len;
313 	char *buf;
314 
315 	if (rcode == NULL || ctx == NULL)
316 		return MI_FAILURE;
317 
318 	/* ### <sp> \0 */
319 	len = strlen(rcode) + 2;
320 	if (len != 5)
321 		return MI_FAILURE;
322 	if ((rcode[0] != '4' && rcode[0] != '5') ||
323 	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
324 	    !isascii(rcode[2]) || !isdigit(rcode[2]))
325 		return MI_FAILURE;
326 	if (xcode != NULL)
327 	{
328 		if (!myisenhsc(xcode, '\0'))
329 			return MI_FAILURE;
330 		len += strlen(xcode) + 1;
331 	}
332 	if (message != NULL)
333 	{
334 		size_t ml;
335 
336 		/* XXX check also for unprintable chars? */
337 		if (strpbrk(message, "\r\n") != NULL)
338 			return MI_FAILURE;
339 		ml = strlen(message);
340 		if (ml > MAXREPLYLEN)
341 			return MI_FAILURE;
342 		len += ml + 1;
343 	}
344 	buf = malloc(len);
345 	if (buf == NULL)
346 		return MI_FAILURE;		/* oops */
347 	(void) sm_strlcpy(buf, rcode, len);
348 	(void) sm_strlcat(buf, " ", len);
349 	if (xcode != NULL)
350 		(void) sm_strlcat(buf, xcode, len);
351 	if (message != NULL)
352 	{
353 		if (xcode != NULL)
354 			(void) sm_strlcat(buf, " ", len);
355 		(void) sm_strlcat(buf, message, len);
356 	}
357 	if (ctx->ctx_reply != NULL)
358 		free(ctx->ctx_reply);
359 	ctx->ctx_reply = buf;
360 	return MI_SUCCESS;
361 }
362 
363 #if _FFR_MULTILINE
364 /*
365 **  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
366 **
367 **	Parameters:
368 **		ctx -- Opaque context structure
369 **		rcode -- The three-digit (RFC 821) SMTP reply code.
370 **		xcode -- The extended (RFC 2034) reply code.
371 **		txt, ... -- The text part of the SMTP reply,
372 **			MUST be terminated with NULL.
373 **
374 **	Returns:
375 **		MI_SUCCESS/MI_FAILURE
376 */
377 
378 int
379 #if SM_VA_STD
380 smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
381 #else /* SM_VA_STD */
382 smfi_setmlreply(ctx, rcode, xcode, va_alist)
383 	SMFICTX *ctx;
384 	const char *rcode;
385 	const char *xcode;
386 	va_dcl
387 #endif /* SM_VA_STD */
388 {
389 	size_t len;
390 	size_t rlen;
391 	int args;
392 	char *buf, *txt;
393 	const char *xc;
394 	char repl[16];
395 	SM_VA_LOCAL_DECL
396 
397 	if (rcode == NULL || ctx == NULL)
398 		return MI_FAILURE;
399 
400 	/* ### <sp> */
401 	len = strlen(rcode) + 1;
402 	if (len != 4)
403 		return MI_FAILURE;
404 	if ((rcode[0] != '4' && rcode[0] != '5') ||
405 	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
406 	    !isascii(rcode[2]) || !isdigit(rcode[2]))
407 		return MI_FAILURE;
408 	if (xcode != NULL)
409 	{
410 		if (!myisenhsc(xcode, '\0'))
411 			return MI_FAILURE;
412 		xc = xcode;
413 	}
414 	else
415 	{
416 		if (rcode[0] == '4')
417 			xc = "4.0.0";
418 		else
419 			xc = "5.0.0";
420 	}
421 
422 	/* add trailing space */
423 	len += strlen(xc) + 1;
424 	rlen = len;
425 	args = 0;
426 	SM_VA_START(ap, xcode);
427 	while ((txt = SM_VA_ARG(ap, char *)) != NULL)
428 	{
429 		size_t tl;
430 
431 		tl = strlen(txt);
432 		if (tl > MAXREPLYLEN)
433 			return MI_FAILURE;
434 
435 		/* this text, reply codes, \r\n */
436 		len += tl + 2 + rlen;
437 		if (++args > MAXREPLIES)
438 			return MI_FAILURE;
439 
440 		/* XXX check also for unprintable chars? */
441 		if (strpbrk(txt, "\r\n") != NULL)
442 			return MI_FAILURE;
443 	}
444 	SM_VA_END(ap);
445 
446 	/* trailing '\0' */
447 	++len;
448 	buf = malloc(len);
449 	if (buf == NULL)
450 		return MI_FAILURE;		/* oops */
451 	(void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
452 	(void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
453 			   xc, " ");
454 	SM_VA_START(ap, xcode);
455 	txt = SM_VA_ARG(ap, char *);
456 	if (txt != NULL)
457 	{
458 		(void) sm_strlcat2(buf, " ", txt, len);
459 		while ((txt = SM_VA_ARG(ap, char *)) != NULL)
460 		{
461 			if (--args <= 1)
462 				repl[3] = ' ';
463 			(void) sm_strlcat2(buf, "\r\n", repl, len);
464 			(void) sm_strlcat(buf, txt, len);
465 		}
466 	}
467 	if (ctx->ctx_reply != NULL)
468 		free(ctx->ctx_reply);
469 	ctx->ctx_reply = buf;
470 	SM_VA_END(ap);
471 	return MI_SUCCESS;
472 }
473 #endif /* _FFR_MULTILINE */
474 
475 /*
476 **  SMFI_SETPRIV -- set private data
477 **
478 **	Parameters:
479 **		ctx -- Opaque context structure
480 **		privatedata -- pointer to private data
481 **
482 **	Returns:
483 **		MI_SUCCESS/MI_FAILURE
484 */
485 
486 int
487 smfi_setpriv(ctx, privatedata)
488 	SMFICTX *ctx;
489 	void *privatedata;
490 {
491 	if (ctx == NULL)
492 		return MI_FAILURE;
493 	ctx->ctx_privdata = privatedata;
494 	return MI_SUCCESS;
495 }
496 /*
497 **  SMFI_GETPRIV -- get private data
498 **
499 **	Parameters:
500 **		ctx -- Opaque context structure
501 **
502 **	Returns:
503 **		pointer to private data
504 */
505 
506 void *
507 smfi_getpriv(ctx)
508 	SMFICTX *ctx;
509 {
510 	if (ctx == NULL)
511 		return NULL;
512 	return ctx->ctx_privdata;
513 }
514 /*
515 **  SMFI_GETSYMVAL -- get the value of a macro
516 **
517 **	See explanation in mfapi.h about layout of the structures.
518 **
519 **	Parameters:
520 **		ctx -- Opaque context structure
521 **		symname -- name of macro
522 **
523 **	Returns:
524 **		value of macro (NULL in case of failure)
525 */
526 
527 char *
528 smfi_getsymval(ctx, symname)
529 	SMFICTX *ctx;
530 	char *symname;
531 {
532 	int i;
533 	char **s;
534 	char one[2];
535 	char braces[4];
536 
537 	if (ctx == NULL || symname == NULL || *symname == '\0')
538 		return NULL;
539 
540 	if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
541 	{
542 		one[0] = symname[1];
543 		one[1] = '\0';
544 	}
545 	else
546 		one[0] = '\0';
547 	if (strlen(symname) == 1)
548 	{
549 		braces[0] = '{';
550 		braces[1] = *symname;
551 		braces[2] = '}';
552 		braces[3] = '\0';
553 	}
554 	else
555 		braces[0] = '\0';
556 
557 	/* search backwards through the macro array */
558 	for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
559 	{
560 		if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
561 		    ctx->ctx_mac_buf[i] == NULL)
562 			continue;
563 		while (s != NULL && *s != NULL)
564 		{
565 			if (strcmp(*s, symname) == 0)
566 				return *++s;
567 			if (one[0] != '\0' && strcmp(*s, one) == 0)
568 				return *++s;
569 			if (braces[0] != '\0' && strcmp(*s, braces) == 0)
570 				return *++s;
571 			++s;	/* skip over macro value */
572 			++s;	/* points to next macro name */
573 		}
574 	}
575 	return NULL;
576 }
577