/*
 * Copyright (c) 2002 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: clt2.c,v 1.3 2006/02/16 19:19:40 ca Exp $
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "st.h"

#if !HAVE_SNPRINTF
# define snprintf sm_snprintf
# include "sm/string.h"
#endif /* !HAVE_SNPRINTF */

#define IOBUFSIZE (16*1024)

#define REPS	10

#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif

#define SEC2USEC(s) ((s)*1000000LL)
#define REQUEST_TIMEOUT SEC2USEC(3)

static char *prog;                     /* Program name   */
static struct sockaddr_in rmt_addr;    /* Remote address */
static int debug = 0;
static st_netfd_t rmt_nfd = NULL;

static void
print_sys_error(const char *msg)
{
	fprintf(stderr, "%s: %s: %s\n", prog, msg, strerror(errno));
}

static void
read_address(const char *str, struct sockaddr_in *sin)
{
	char host[128], *p;
	struct hostent *hp;
	short port;

	strlcpy(host, str, sizeof(host));
	if ((p = strchr(host, ':')) == NULL)
	{
		fprintf(stderr, "%s: invalid address: %s\n", prog, host);
		exit(1);
	}
	*p++ = '\0';
	port = (short) atoi(p);
	if (port < 1)
	{
		fprintf(stderr, "%s: invalid port: %s\n", prog, p);
		exit(1);
	}

	memset(sin, 0, sizeof(struct sockaddr_in));
	sin->sin_family = AF_INET;
	sin->sin_port = htons(port);
	if (host[0] == '\0')
	{
		sin->sin_addr.s_addr = INADDR_ANY;
		return;
	}
	sin->sin_addr.s_addr = inet_addr(host);
	if (sin->sin_addr.s_addr == INADDR_NONE)
	{
		/* not dotted-decimal */
		if ((hp = gethostbyname(host)) == NULL)
		{
			fprintf(stderr, "%s: can't resolve address: %s\n", prog, host);
			exit(1);
		}
		memcpy(&sin->sin_addr, hp->h_addr, hp->h_length);
	}
}

static int
con2srv(void)
{
	int sock;
	char buf[IOBUFSIZE];

	/* Connect to remote host */
	if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
	{
		snprintf(buf, sizeof(buf), "socket=%d", sock);
		print_sys_error(buf);
		goto done;
	}
	if ((rmt_nfd = st_netfd_open_socket(sock)) == NULL)
	{
		snprintf(buf, sizeof(buf), "st_netfd_open_socket");
		print_sys_error(buf);
		close(sock);
		goto done;
	}
	if (st_connect(rmt_nfd, (struct sockaddr *)&rmt_addr,
		 sizeof(rmt_addr), REQUEST_TIMEOUT) < 0)
	{
		snprintf(buf, sizeof(buf), "connect");
		print_sys_error(buf);
		st_netfd_close(rmt_nfd);
		goto done;
	}

	if (debug > 2)
		fprintf(stderr, "client: connected\n");
	return 1;
done:
	return -1;
}

static void *
wrsrv(void *arg)
{
	int n, r, l;
	char buf[IOBUFSIZE];

	for (n = 0; n < REPS; n++)
	{
		snprintf(buf, sizeof(buf), "CMD %d\n", n);
		l = strlen(buf);
		r = st_write(rmt_nfd, buf, l, REQUEST_TIMEOUT);
		if (r != l)
		{
			fprintf(stderr,
				"error write s='%s' n=%d, l=%d, r=%d, errno=%d\n",
				buf, n, l, r, errno);
			return NULL;
		}
		st_usleep(1);
	}
	return NULL;
}


static void *
rdsrv(void *arg)
{
	int n;
	char buf[IOBUFSIZE];

	while (1)
	{
		n = st_read(rmt_nfd, buf, IOBUFSIZE, REQUEST_TIMEOUT);
		if (n <= 0)
		{
			fprintf(stderr,
				"error read n=%d, errno=%d\n", n, errno);
			goto fail;
		}
		if (debug > 3)
		{
			fprintf(stderr, "rcvd: \"");
			write(STDERR_FILENO, buf, n);
			fprintf(stderr, "\"\n");
		}
		while (n > 0)
			if (buf[--n] == 'Q')
				return NULL;
		st_usleep(1);
	}

fail:
	return NULL;
}

int
main(int argc, char *argv[])
{
	st_thread_t thr1;
	extern char *optarg;
	int opt, r, raddr, *rv;

	prog = argv[0];
	raddr = 0;

	/* Parse arguments */
	while((opt = getopt(argc, argv, "c:d:hr:t:S")) != -1)
	{
		switch (opt)
		{
		  case 'd':
			debug = atoi(optarg);
			if (debug < 0)
			{
				fprintf(stderr, "%s: invalid number for debug: %s\n", prog, optarg);
				exit(1);
			}
			break;
		  case 'r':
			read_address(optarg, &rmt_addr);
			if (rmt_addr.sin_addr.s_addr == INADDR_ANY)
			{
				fprintf(stderr, "%s: invalid remote address: %s\n", prog, optarg);
				exit(1);
			}
			raddr = 1;
			break;
		  case 'h':
		  case '?':
			fprintf(stderr, "Usage: %s -l <[host]:port> -r <host:port> "
	      			"[-S]\n", prog);
			exit(1);
		}
	}
	if (!raddr)
	{
		fprintf(stderr, "%s: remote address required\n", prog);
		exit(1);
	}

	if (debug)
		fprintf(stderr, "%s: starting client\n", prog);

	/* Initialize the ST library */
	if (st_init() < 0)
	{
		print_sys_error("st_init");
		exit(1);
	}

	if (con2srv() < 0)
		exit(1);

	thr1 = st_thread_create(wrsrv, (void *) 1, 1, 0);
	if (NULL == thr1)
	{
		print_sys_error("st_thread_create");
		exit(1);
	}

	rdsrv((void *) 2);

	r = st_thread_join(thr1, (void **) &rv);
	if (rmt_nfd != NULL)
	{
		st_netfd_close(rmt_nfd);
		rmt_nfd = NULL;
	}
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1