comm.c revision 90792
164562Sgshapiro/*
290792Sgshapiro *  Copyright (c) 1999-2001 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
464562Sgshapiro *
564562Sgshapiro * By using this file, you agree to the terms and conditions set
664562Sgshapiro * forth in the LICENSE file which can be found at the top level of
764562Sgshapiro * the sendmail distribution.
864562Sgshapiro *
964562Sgshapiro */
1064562Sgshapiro
1190792Sgshapiro#include <sm/gen.h>
1290792SgshapiroSM_RCSID("@(#)$Id: comm.c,v 8.48 2001/11/07 17:43:04 ca Exp $")
1364562Sgshapiro
1464562Sgshapiro#include "libmilter.h"
1590792Sgshapiro#include <sm/errstring.h>
1664562Sgshapiro
1790792Sgshapiro#define FD_Z	FD_ZERO(&readset);			\
1890792Sgshapiro		FD_SET((unsigned int) sd, &readset);	\
1990792Sgshapiro		FD_ZERO(&excset);			\
2090792Sgshapiro		FD_SET((unsigned int) sd, &excset)
2164562Sgshapiro
2264562Sgshapiro/*
2364562Sgshapiro**  MI_RD_CMD -- read a command
2464562Sgshapiro**
2564562Sgshapiro**	Parameters:
2664562Sgshapiro**		sd -- socket descriptor
2764562Sgshapiro**		timeout -- maximum time to wait
2864562Sgshapiro**		cmd -- single character command read from sd
2964562Sgshapiro**		rlen -- pointer to length of result
3064562Sgshapiro**		name -- name of milter
3164562Sgshapiro**
3264562Sgshapiro**	Returns:
3364562Sgshapiro**		buffer with rest of command
3464562Sgshapiro**		(malloc()ed here, should be free()d)
3564562Sgshapiro**		hack: encode error in cmd
3664562Sgshapiro*/
3764562Sgshapiro
3864562Sgshapirochar *
3964562Sgshapiromi_rd_cmd(sd, timeout, cmd, rlen, name)
4064562Sgshapiro	socket_t sd;
4164562Sgshapiro	struct timeval *timeout;
4264562Sgshapiro	char *cmd;
4364562Sgshapiro	size_t *rlen;
4464562Sgshapiro	char *name;
4564562Sgshapiro{
4664562Sgshapiro	ssize_t len;
4764562Sgshapiro	mi_int32 expl;
4864562Sgshapiro	ssize_t i;
4964562Sgshapiro	fd_set readset, excset;
5064562Sgshapiro	int ret;
5164562Sgshapiro	int save_errno;
5264562Sgshapiro	char *buf;
5364562Sgshapiro	char data[MILTER_LEN_BYTES + 1];
5464562Sgshapiro
5564562Sgshapiro	*cmd = '\0';
5664562Sgshapiro	*rlen = 0;
5771345Sgshapiro
5864562Sgshapiro	if (sd >= FD_SETSIZE)
5964562Sgshapiro	{
6064562Sgshapiro		smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
6164562Sgshapiro			name, sd, FD_SETSIZE);
6264562Sgshapiro		*cmd = SMFIC_SELECT;
6364562Sgshapiro		return NULL;
6464562Sgshapiro	}
6571345Sgshapiro
6664562Sgshapiro	FD_Z;
6764562Sgshapiro	i = 0;
6864562Sgshapiro	while ((ret = select(sd + 1, &readset, NULL, &excset, timeout)) >= 1)
6964562Sgshapiro	{
7064562Sgshapiro		if (FD_ISSET(sd, &excset))
7164562Sgshapiro		{
7264562Sgshapiro			*cmd = SMFIC_SELECT;
7364562Sgshapiro			return NULL;
7464562Sgshapiro		}
7590792Sgshapiro
7690792Sgshapiro		len = MI_SOCK_READ(sd, data + i, sizeof data - i);
7790792Sgshapiro		if (MI_SOCK_READ_FAIL(len))
7864562Sgshapiro		{
7964562Sgshapiro			smi_log(SMI_LOG_ERR,
8064562Sgshapiro				"%s, mi_rd_cmd: read returned %d: %s",
8190792Sgshapiro				name, len, sm_errstring(errno));
8264562Sgshapiro			*cmd = SMFIC_RECVERR;
8364562Sgshapiro			return NULL;
8464562Sgshapiro		}
8564562Sgshapiro		if (len == 0)
8664562Sgshapiro		{
8764562Sgshapiro			*cmd = SMFIC_EOF;
8864562Sgshapiro			return NULL;
8964562Sgshapiro		}
9064562Sgshapiro		if (len >= (ssize_t) sizeof data - i)
9164562Sgshapiro			break;
9264562Sgshapiro		i += len;
9364562Sgshapiro		FD_Z;
9464562Sgshapiro	}
9564562Sgshapiro	if (ret == 0)
9664562Sgshapiro	{
9764562Sgshapiro		*cmd = SMFIC_TIMEOUT;
9864562Sgshapiro		return NULL;
9964562Sgshapiro	}
10064562Sgshapiro	else if (ret < 0)
10164562Sgshapiro	{
10264562Sgshapiro		smi_log(SMI_LOG_ERR,
10364562Sgshapiro			"%s: mi_rd_cmd: select returned %d: %s",
10490792Sgshapiro			name, ret, sm_errstring(errno));
10564562Sgshapiro		*cmd = SMFIC_RECVERR;
10664562Sgshapiro		return NULL;
10764562Sgshapiro	}
10864562Sgshapiro
10964562Sgshapiro	*cmd = data[MILTER_LEN_BYTES];
11064562Sgshapiro	data[MILTER_LEN_BYTES] = '\0';
11164562Sgshapiro	(void) memcpy((void *) &expl, (void *) &(data[0]), MILTER_LEN_BYTES);
11264562Sgshapiro	expl = ntohl(expl) - 1;
11364562Sgshapiro	if (expl <= 0)
11464562Sgshapiro		return NULL;
11564562Sgshapiro	if (expl > MILTER_CHUNK_SIZE)
11664562Sgshapiro	{
11764562Sgshapiro		*cmd = SMFIC_TOOBIG;
11864562Sgshapiro		return NULL;
11964562Sgshapiro	}
12090792Sgshapiro#if _FFR_ADD_NULL
12190792Sgshapiro	buf = malloc(expl + 1);
12290792Sgshapiro#else /* _FFR_ADD_NULL */
12364562Sgshapiro	buf = malloc(expl);
12490792Sgshapiro#endif /* _FFR_ADD_NULL */
12564562Sgshapiro	if (buf == NULL)
12664562Sgshapiro	{
12764562Sgshapiro		*cmd = SMFIC_MALLOC;
12864562Sgshapiro		return NULL;
12964562Sgshapiro	}
13064562Sgshapiro
13164562Sgshapiro	i = 0;
13264562Sgshapiro	FD_Z;
13364562Sgshapiro	while ((ret = select(sd + 1, &readset, NULL, &excset, timeout)) == 1)
13464562Sgshapiro	{
13564562Sgshapiro		if (FD_ISSET(sd, &excset))
13664562Sgshapiro		{
13764562Sgshapiro			*cmd = SMFIC_SELECT;
13864562Sgshapiro			free(buf);
13964562Sgshapiro			return NULL;
14064562Sgshapiro		}
14190792Sgshapiro		len = MI_SOCK_READ(sd, buf + i, expl - i);
14290792Sgshapiro		if (MI_SOCK_READ_FAIL(len))
14364562Sgshapiro		{
14464562Sgshapiro			smi_log(SMI_LOG_ERR,
14564562Sgshapiro				"%s: mi_rd_cmd: read returned %d: %s",
14690792Sgshapiro				name, len, sm_errstring(errno));
14764562Sgshapiro			ret = -1;
14864562Sgshapiro			break;
14964562Sgshapiro		}
15064562Sgshapiro		if (len == 0)
15164562Sgshapiro		{
15264562Sgshapiro			*cmd = SMFIC_EOF;
15364562Sgshapiro			free(buf);
15464562Sgshapiro			return NULL;
15564562Sgshapiro		}
15664562Sgshapiro		if (len > expl - i)
15764562Sgshapiro		{
15864562Sgshapiro			*cmd = SMFIC_RECVERR;
15964562Sgshapiro			free(buf);
16064562Sgshapiro			return NULL;
16164562Sgshapiro		}
16264562Sgshapiro		if (len >= expl - i)
16364562Sgshapiro		{
16464562Sgshapiro			*rlen = expl;
16590792Sgshapiro#if _FFR_ADD_NULL
16690792Sgshapiro			/* makes life simpler for common string routines */
16790792Sgshapiro			buf[expl] = '\0';
16890792Sgshapiro#endif /* _FFR_ADD_NULL */
16964562Sgshapiro			return buf;
17064562Sgshapiro		}
17164562Sgshapiro		i += len;
17264562Sgshapiro		FD_Z;
17364562Sgshapiro	}
17464562Sgshapiro
17564562Sgshapiro	save_errno = errno;
17664562Sgshapiro	free(buf);
17764562Sgshapiro
17864562Sgshapiro	/* select returned 0 (timeout) or < 0 (error) */
17964562Sgshapiro	if (ret == 0)
18064562Sgshapiro	{
18164562Sgshapiro		*cmd = SMFIC_TIMEOUT;
18264562Sgshapiro		return NULL;
18364562Sgshapiro	}
18464562Sgshapiro	if (ret < 0)
18564562Sgshapiro	{
18664562Sgshapiro		smi_log(SMI_LOG_ERR,
18764562Sgshapiro			"%s: mi_rd_cmd: select returned %d: %s",
18890792Sgshapiro			name, ret, sm_errstring(save_errno));
18964562Sgshapiro		*cmd = SMFIC_RECVERR;
19064562Sgshapiro		return NULL;
19164562Sgshapiro	}
19264562Sgshapiro	*cmd = SMFIC_UNKNERR;
19364562Sgshapiro	return NULL;
19464562Sgshapiro}
19590792Sgshapiro/*
19664562Sgshapiro**  MI_WR_CMD -- write a cmd to sd
19764562Sgshapiro**
19864562Sgshapiro**	Parameters:
19964562Sgshapiro**		sd -- socket descriptor
20064562Sgshapiro**		timeout -- maximum time to wait (currently unused)
20164562Sgshapiro**		cmd -- single character command to write
20264562Sgshapiro**		buf -- buffer with further data
20364562Sgshapiro**		len -- length of buffer (without cmd!)
20464562Sgshapiro**
20564562Sgshapiro**	Returns:
20664562Sgshapiro**		MI_SUCCESS/MI_FAILURE
20764562Sgshapiro*/
20864562Sgshapiro
20964562Sgshapiroint
21064562Sgshapiromi_wr_cmd(sd, timeout, cmd, buf, len)
21164562Sgshapiro	socket_t sd;
21264562Sgshapiro	struct timeval *timeout;
21364562Sgshapiro	int cmd;
21464562Sgshapiro	char *buf;
21564562Sgshapiro	size_t len;
21664562Sgshapiro{
21764562Sgshapiro	size_t sl, i;
21864562Sgshapiro	ssize_t l;
21964562Sgshapiro	mi_int32 nl;
22064562Sgshapiro	int ret;
22164562Sgshapiro	fd_set wrtset;
22264562Sgshapiro	char data[MILTER_LEN_BYTES + 1];
22364562Sgshapiro
22464562Sgshapiro	if (len > MILTER_CHUNK_SIZE)
22564562Sgshapiro		return MI_FAILURE;
22664562Sgshapiro	nl = htonl(len + 1);	/* add 1 for the cmd char */
22764562Sgshapiro	(void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES);
22864562Sgshapiro	data[MILTER_LEN_BYTES] = (char) cmd;
22964562Sgshapiro	i = 0;
23064562Sgshapiro	sl = MILTER_LEN_BYTES + 1;
23164562Sgshapiro
23266494Sgshapiro	do
23366494Sgshapiro	{
23464562Sgshapiro		FD_ZERO(&wrtset);
23590792Sgshapiro		FD_SET((unsigned int) sd, &wrtset);
23664562Sgshapiro		if ((ret = select(sd + 1, NULL, &wrtset, NULL, timeout)) == 0)
23764562Sgshapiro			return MI_FAILURE;
23864562Sgshapiro	} while (ret < 0 && errno == EINTR);
23964562Sgshapiro	if (ret < 0)
24064562Sgshapiro		return MI_FAILURE;
24164562Sgshapiro
24264562Sgshapiro	/* use writev() instead to send the whole stuff at once? */
24366494Sgshapiro	while ((l = MI_SOCK_WRITE(sd, (void *) (data + i),
24466494Sgshapiro				  sl - i)) < (ssize_t) sl)
24564562Sgshapiro	{
24664562Sgshapiro		if (l < 0)
24764562Sgshapiro			return MI_FAILURE;
24864562Sgshapiro		i += l;
24964562Sgshapiro		sl -= l;
25064562Sgshapiro	}
25164562Sgshapiro
25264562Sgshapiro	if (len > 0 && buf == NULL)
25364562Sgshapiro		return MI_FAILURE;
25464562Sgshapiro	if (len == 0 || buf == NULL)
25564562Sgshapiro		return MI_SUCCESS;
25664562Sgshapiro	i = 0;
25764562Sgshapiro	sl = len;
25866494Sgshapiro	do
25966494Sgshapiro	{
26064562Sgshapiro		FD_ZERO(&wrtset);
26190792Sgshapiro		FD_SET((unsigned int) sd, &wrtset);
26264562Sgshapiro		if ((ret = select(sd + 1, NULL, &wrtset, NULL, timeout)) == 0)
26364562Sgshapiro			return MI_FAILURE;
26464562Sgshapiro	} while (ret < 0 && errno == EINTR);
26564562Sgshapiro	if (ret < 0)
26664562Sgshapiro		return MI_FAILURE;
26766494Sgshapiro	while ((l = MI_SOCK_WRITE(sd, (void *) (buf + i),
26866494Sgshapiro				  sl - i)) < (ssize_t) sl)
26964562Sgshapiro	{
27064562Sgshapiro		if (l < 0)
27164562Sgshapiro			return MI_FAILURE;
27264562Sgshapiro		i += l;
27364562Sgshapiro		sl -= l;
27464562Sgshapiro	}
27564562Sgshapiro	return MI_SUCCESS;
27664562Sgshapiro}
277