/*
 * Copyright (c) 2002, 2004, 2005 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 *
 * $Id: t-net-common.c,v 1.11 2006/07/16 02:07:39 ca Exp $
 */


extern char *optarg;
extern int optind;
extern int optopt;
extern int opterr;

static int Verbose;

#define SM_BUFSIZE	8192
#define SM_DEFPORT	2525

#ifndef SM_CHECK_NET_UNIX
# define SM_CHECK_NET_UNIX	0
#endif

static void
usage(const char *prg)
{
	fprintf(stderr, "usage: %s options\n", prg);
	fprintf(stderr, "-b n: set buffer size to n [default: %d]\n", SM_BUFSIZE);
	fprintf(stderr, "-c n: act as client, write n bytes\n");
	fprintf(stderr, "-s n: act as server, read n bytes\n");
	fprintf(stderr, "-d n: sleep n seconds before close()\n");
	fprintf(stderr, "-p port: port to use [default: %d]\n", SM_DEFPORT);
	fprintf(stderr, "-R n: read and write n times\n");
	fprintf(stderr, "-S: show some statistics\n");
	fprintf(stderr, "-T: show execution times\n");
#if SM_CHECK_NET_UNIX
	fprintf(stderr, "-u: use unix domain socket [%s]\n", nsocket);
	fprintf(stderr, "-U name: use name for unix domain socket\n");
#endif
	fprintf(stderr, "-V: increase verbosity\n");
	exit(0);
}

/*
**  PRTSTR -- print a message to stderr, then a string,
**		convert unprintable chars to hex
**
**	Parameters:
**		msg -- leading message
**		str --  string to print
**		n -- size of string.
**
**	Returns:
**		nothing.
*/

static void
prtstr(char *msg, char *str, int n)
{
	int l;

	if (msg != NULL)
		fprintf(stderr, "%s: ", msg);
	for (l = 0; l < n; l++)
	{
		if (isprint(str[l]))
			putc(str[l], stderr);
		else
			fprintf(stderr, " %02x ", str[l]);
	}
	fprintf(stderr, "\n");
}

/*
**  PRTERR -- print an error text to stderr
**
**	Parameters:
**		msg -- leading message
**		res -- error value to display
**
**	Returns:
**		nothing.
*/

static void
prterr(char *msg, sm_ret_T res)
{
	if (res != SM_IO_EOF)
	{
		fprintf(stderr, "%s: res=%x, type=%d, value=%d\n", msg,
			res, sm_error_type(res), sm_error_value(res));
#if HAVE_STRERROR
		fprintf(stderr, "%s: %s\n", msg, strerror(sm_error_value(res)));
#endif
	}
	else
		fprintf(stderr, "%s: EOF\n", msg);
}

#define sndc(i)	('A' + ((i) & 31))
#define rcvc(i)	('a' + ((i) & 31))

/*
**  WRITESOCK -- write wr characters (according to above macro) to fp,
**
**	Parameters:
**		fp -- file pointer
**		wr -- number of chars to send
**		j -- offset for calculation of character to send
**		srv -- currently acting for srv/clt
**
**	Returns:
**		usual error code
*/

static sm_ret_T
writesock(sm_file_T *fp, int wr, size_t *j, bool srv)
{
	sm_ret_T res;
	size_t n, l, i;
	ssize_t rr;
	char *t;
	uchar buf[SM_BUFSIZE];

	l = sizeof(buf);
	t = srv ? "srv" : "clt";
	do
	{
		n = SM_MIN(l, (size_t) wr);
		for (i = 0; i < n; i++)
		{
			buf[i] = srv ? sndc(*j) : rcvc(*j);
			(*j)++;
		}
		res = sm_io_write(fp, buf, n, &rr);
		if (sm_is_err(res))
		{
			prterr("writesock", res);
			if (Verbose > 2)
				fprintf(stderr, "%s: wrote '%s' [%d]\n",
					t, buf, (int) rr);
			else if (Verbose > 0)
				fprintf(stderr, "%s: wrote [%d]\n",
					t, (int) rr);
			return res;
		}
		else
			SM_TEST((size_t) rr == n);
		if (rr > 0)
		{
			wr -= rr;
			if (Verbose > 2)
				fprintf(stderr, "%s: wrote '%s' [%d]\n",
					t, buf, (int) rr);
			else if (Verbose > 0)
				fprintf(stderr, "%s: wrote [%d]\n",
					t, (int) rr);
		}
	} while (wr > 0);
	return SM_SUCCESS;
}

/*
**  READSOCK -- read rd characters from fp
**
**	Parameters:
**		fp -- file pointer
**		rd -- number of chars to read
**		j -- offset for checking received characters
**		srv -- currently acting for srv/clt
**
**	Returns:
**		usual error code
*/

static sm_ret_T
readsock(sm_file_T *fp, int rd, size_t *j, bool srv)
{
	sm_ret_T res;
	size_t n, l, i, o;
	ssize_t rr;
	char *t;
	uchar buf[SM_BUFSIZE];

	l = sizeof(buf);
	t = srv ? "srv" : "clt";
	do
	{
		n = SM_MIN(l, (size_t) rd);
		buf[0] = '\0';
		o = 0;
		do
		{
			res = sm_io_read(fp, buf + o, n - o, &rr);
			if (sm_is_err(res))
			{
				prterr("readsock", res);
				if (Verbose > 2)
				{
#if 0
					fprintf(stderr, "%s: got '", t);
					prtstr(NULL, buf, rr);
					fprintf(stderr, "' [%d]\n", rr);
#else
					fprintf(stderr, "%s: got '%s' [%d]\n",
						t, buf, (int) rr);
#endif
				}
				else if (Verbose > 0)
					fprintf(stderr, "%s: got [%d]\n",
						t, (int) rr);
				goto err;
			}
			if (Verbose > 2)
			{
				fprintf(stderr, "%s: got '%s' [%d/%d]\n",
					t, buf, (int) rr, (int) (n - o));
			}
			else if (Verbose > 0)
				fprintf(stderr, "%s: got [%d/%d]\n",
					t, (int) rr, (int) (n - o));
			if (rr > 0)
				o += rr;
		} while (o < n);
		SM_TEST((size_t) o == n);
		if (o > 0)
		{
			rd -= o;
			if (Verbose > 2)
			{
				fprintf(stderr, "%s: got '%s' [%d]\n",
					t, buf, (int) o);
				prtstr(t, (char *) buf, (int) o);
			}
			else if (Verbose > 0)
				fprintf(stderr, "%s: got [%d]\n", t, (int) o);
#if !SM_PERF_TEST
			for (i = 0; i < o; i++)
			{
				if (srv)
					SM_TEST(buf[i] == rcvc(*j));
				else
					SM_TEST(buf[i] == sndc(*j));
				(*j)++;
			}
#endif /* !SM_PERF_TEST */
		}
	} while (rd > 0);
  err:
	return res;
}

/*
**  WRITE_SOCK -- write wr characters to fd, potentially read too.
**
**	Parameters:
**		fd -- file descr
**		wr -- number of chars to send
**		delay -- sleep time before close
**		bsize -- buffer size to use
**		timeout -- timeout
**		both -- if >0: do write and read ("both" times)
**			sets double buffering
**
**	Returns:
**		usual error code
*/

static sm_ret_T
write_sock(int fd, int wr, int delay, int bsize, int timeout, int both)
{
	sm_ret_T res;
	size_t r, w;
	sm_file_T *fp;

	r = w = 0;
	res = sm_io_open(SmStStdiofd, &fd, SM_IO_RDWR, &fp, NULL);
	SM_TEST(res == SM_SUCCESS);
	SM_TEST(fp != NULL);
	if (sm_is_err(res))
		return res;
	res = sm_io_setinfo(fp, SM_IO_WHAT_TIMEOUT, &timeout);
	SM_TEST(sm_is_success(res));
	if (both > 0)
	{
		res = sm_io_setinfo(fp, SM_IO_DOUBLE, &both);
		SM_TEST(sm_is_success(res));
	}
	if (bsize > 0)
	{
		res = sm_io_setvbuf(fp, (uchar *) NULL, SM_IO_FBF,
				(size_t) bsize);
		SM_TEST(sm_is_success(res));
	}
	res = sm_fp_nonblock(fp, true);
	SM_TEST(sm_is_success(res));

	if (Verbose > 0)
		fprintf(stderr, "clt: new connection, wr=%d\n", wr);
	START();
	do
	{
		res = writesock(fp, wr, &w, false);
		SM_TEST(sm_is_success(res));
		if (!sm_is_success(res) && Verbose > 0)
			fprintf(stderr, "clt: writesock=%x\n", res);
		if (both > 0)
		{
			res = readsock(fp, wr, &r, false);
			SM_TEST(sm_is_success(res));
			if (!sm_is_success(res) && Verbose > 0)
				fprintf(stderr, "clt: readsock=%x\n", res);
		}
	} while (both-- > 0);
	END();
	res = sm_io_flush(fp);
	SM_TEST(sm_is_success(res));
	if (!sm_is_success(res) && Verbose > 0)
		fprintf(stderr, "clt: flush %x\n", res);
	if (delay > 0)
		sleep(delay);
	res = sm_io_close(fp, SM_IO_CF_NONE);
	SM_TEST(sm_is_success(res));
	if (!sm_is_success(res) && Verbose > 0)
		fprintf(stderr, "clt: close %x\n", res);
	DONE();
	return SM_SUCCESS;
}

/*
**  READ_SOCK -- read rd characters from fd, potentially write too.
**
**	Parameters:
**		fd -- file descr
**		rd -- number of chars to read
**		delay -- sleep time before close
**		bsize -- buffer size to use
**		timeout -- timeout
**		both -- if >0: do write and read ("both" times)
**			sets double buffering
**
**	Returns:
**		usual error code
*/

static sm_ret_T
read_sock(int fd, int rd, int delay, int bsize, int timeout, int both)
{
	sm_ret_T res, rescl;
	size_t r, w;
	sm_file_T *fp;

	r = w = 0;
	res = sm_io_open(SmStStdiofd, &fd, SM_IO_RDWR, &fp, NULL);
	SM_TEST(res == SM_SUCCESS);
	SM_TEST(fp != NULL);
	if (sm_is_err(res))
		return res;
	res = sm_io_setinfo(fp, SM_IO_WHAT_TIMEOUT, &timeout);
	SM_TEST(sm_is_success(res));
	res = sm_fp_nonblock(fp, true);
	SM_TEST(sm_is_success(res));
	if (both > 0)
	{
		res = sm_io_setinfo(fp, SM_IO_DOUBLE, &both);
		SM_TEST(sm_is_success(res));
	}
	if (bsize > 0)
	{
		res = sm_io_setvbuf(fp, (uchar *) NULL, SM_IO_FBF, (size_t) bsize);
		SM_TEST(sm_is_success(res));
	}

	if (Verbose > 0)
		fprintf(stderr, "srv: new connection, rd=%d\n", rd);
	START();
	do
	{
		res = readsock(fp, rd, &r, true);
		SM_TEST(sm_is_success(res));
		if (!sm_is_success(res) && Verbose > 0)
			fprintf(stderr, "srv: readsock=%x\n", res);
		if (both > 0)
		{
			res = writesock(fp, rd, &w, true);
			SM_TEST(sm_is_success(res));
			if (!sm_is_success(res) && Verbose > 0)
				fprintf(stderr, "srv: writesock=%x\n", res);
		}
	} while (both-- > 0);
	END();
	if (delay > 0)
		sleep(delay);
	rescl = sm_io_close(fp, SM_IO_CF_NONE);
	SM_TEST(sm_is_success(rescl));
	DONE();
	return res;
}


syntax highlighted by Code2HTML, v. 0.9.1