/*
* 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