#include <stdio.h>
#include <rpc/rpc.h>
#include <netdb.h>
#include <sys/socket.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "librpc01.h"

int program = 2000333;
int version = 10;
char *server = NULL;
char *file_name = NULL;
char host_name[100];
long host_address;

void do_compare(int, char *, struct data *, char *);
void usage_error(char *program_name);

int main(int argc, char *argv[])
{
	struct hostent *hp;
	struct data buffer, *return_buffer;
	int i, n, rc;
	FILE *fp;
	struct stat stat_buffer;
	char *p;
	CLIENT *clnt;
	struct sockaddr_in server_sin;
	int sock;
	struct timeval timeout;

	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-s")) {
			if (++i >= argc) {
				fprintf(stderr, "-s requires a host name\n");
				usage_error(argv[0]);
			}
			server = argv[i];
			continue;
		}

		if (!strcmp(argv[i], "-f")) {
			if (++i >= argc) {
				fprintf(stderr, "-h requires a file name\n");
				usage_error(argv[0]);
			}
			file_name = argv[i];
			continue;
		}

		if (!strcmp(argv[i], "-p")) {
			if (++i >= argc) {
				fprintf(stderr, "-p requires an argument\n");
				usage_error(argv[0]);
			}
			n = sscanf(argv[i], "%d", &program);
			if (n != 1) {
				fprintf(stderr,
					"-p requires an numeric argument\n");
				usage_error(argv[0]);
			}
			continue;
		}

		if (!strcmp(argv[i], "-v")) {
			if (++i >= argc) {
				fprintf(stderr, "-v requires an argument\n");
				usage_error(argv[0]);
			}
			n = sscanf(argv[i], "%d", &version);
			if (n != 1) {
				fprintf(stderr,
					"-v requires an numeric argument\n");
				usage_error(argv[0]);
			}
			continue;
		}
	}

	if (!server) {
		fprintf(stderr, "server not given\n");
		usage_error(argv[0]);
	}
	hp = gethostbyname(server);
	if (hp == (struct hostent *)NULL) {
		fprintf(stderr, "server %s unknown\n", server);
		usage_error(argv[0]);
	}
	memset(&server_sin, 0x00, sizeof(server_sin));
	server_sin.sin_family = AF_INET;
	memcpy(&server_sin.sin_addr, hp->h_addr, sizeof(hp->h_addr));

	if (!file_name) {
		fprintf(stderr, "file name not given\n");
		usage_error(argv[0]);
	}

	gethostname(host_name, 100);
	if ((hp = gethostbyname(host_name)) != NULL)
		host_address = (long)*((int *)hp->h_addr_list[0]);
	buffer.address = host_address;
	buffer.request_id = getpid();

	fp = fopen(file_name, "r");
	if (!fp) {
		fprintf(stderr, "Can not open %s\n", file_name);
		usage_error(argv[0]);
	}
	if (stat(file_name, &stat_buffer))
		fprintf(stderr, "stat() failed for %s, errno %d\n", file_name,
			errno);
	buffer.data_length = stat_buffer.st_size;
	if (buffer.data_length > 2187) {
		fprintf(stderr, "file too large (2187 max)\n");
		usage_error(argv[0]);
	}

	buffer.data = malloc(buffer.data_length);
	for (i = 0, p = buffer.data; i < buffer.data_length; i++, p++)
		*p = getc(fp);
	fclose(fp);

	rc = callrpc(server, program, version, 1, (xdrproc_t)xdr_send_data,
			(char *)&buffer, (xdrproc_t)xdr_receive_data,
			(char *)&return_buffer);
	do_compare(rc, "callrpc", &buffer, return_buffer->data);

	server_sin.sin_port = 0;
	sock = RPC_ANYSOCK;
	timeout.tv_usec = 0;
	timeout.tv_sec = 10;
	clnt = clntudp_create(&server_sin, program, version, timeout, &sock);
	if (clnt == (CLIENT *) NULL) {
		fprintf(stderr, "clntudp_create failed\n");
		exit(1);
	}
	timeout.tv_usec = 0;
	timeout.tv_sec = 30;
	rc = (int)clnt_call(clnt, 1, (xdrproc_t)xdr_send_data,
				(char *)&buffer, (xdrproc_t)xdr_receive_data,
				(char *)&return_buffer, timeout);
	clnt_destroy(clnt);
	do_compare(rc, "udp transport", &buffer, return_buffer->data);

	server_sin.sin_port = 0;
	sock = RPC_ANYSOCK;
	clnt = clnttcp_create(&server_sin, program, version, &sock, 0, 0);
	if (clnt == (CLIENT *) NULL) {
		fprintf(stderr, "clntudp_create failed\n");
		exit(1);
	}
	timeout.tv_usec = 0;
	timeout.tv_sec = 30;
	rc = (int)clnt_call(clnt, 1, (xdrproc_t)xdr_send_data,
				(char *)&buffer, (xdrproc_t)xdr_receive_data,
				(char *)&return_buffer, timeout);
	clnt_destroy(clnt);
	do_compare(rc, "tcp transport", &buffer, return_buffer->data);

	exit(0);
}

void do_compare(int rpc_rc, char *msg, struct data *buffer, char *ret_data)
{
	int rc;

	if (rpc_rc) {
		printf("RPC call with %s returned %d: ", msg, rpc_rc);
		clnt_perrno(rpc_rc);
		printf("\n");
		exit(1);
	}
	rc = memcmp(buffer->data, ret_data, buffer->data_length);
	if (rc) {
		printf("Data compare for %s returned %d\n", msg, rc);
		exit(1);
	}
}

void usage_error(char *program_name)
{
	fprintf(stderr,
		"Usage: %s -s server -f file [-p program-number] [-v version]\n",
		program_name);
	exit(2);
}
