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