/* #define OS_WINDOWS */

#include <stdio.h>
#ifdef OS_WINDOWS
#include <io.h>
#include <winsock2.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#endif
#include <errno.h>

extern int errno;

#define BUF_LEN 80

#ifdef OS_WINDOWS
void net_init()
{
	WORD ver_requested;
	WSADATA ver_data;

	ver_requested = MAKEWORD(2,2);
	if(WSAStartup(ver_requested, &ver_data) != 0) {
		fprintf(stderr, "Winsock DDL not found or version problem");
		exit(1);
	}
	// One could/should examine ver_data.wVersion here,
	// if we really need Version 2.2, LOBYTE(ver_data.wVersion)
	// and HIBYTE(ver_data.wVersion) must both be 2.
}
#else
void net_init()
{
	// Not needed under UNIX
}
#endif

#ifdef OS_WINDOWS
void net_perror(const char *prefix)
{
	int err;
	char *msg;

	err = WSAGetLastError();
	switch(err) {
	case WSANOTINITIALISED:
		msg = "Not initialized";
		break;
	case WSAENETDOWN:
		msg = "Network subsystem has failed";
		break;
	case WSAENOTCONN:
		msg = "The socket is not connected.";
		break;
	case WSAEINTR:
		msg = "The blocking call was cancelled.";
		break;
	case WSAEINPROGRESS:
		msg = "Busy";
		break;
	case WSAENETRESET:
		msg = "Connection broken";
		break;
	case WSAETIMEDOUT:
		msg = "Timed out";
		break;
	case WSAECONNREFUSED:
		msg = "Connection refused";
		break;
	/* There are more, but I have no more time now. */
	default:
		msg = 0;
		break;
	}
	if(msg)
		fprintf(stderr, "%s: %s.\n", prefix, msg);
	else
		fprintf(stderr, "%s: Error %d.\n", prefix, err);

	WSASetLastError(0);
}
#else
void net_perror(const char *prefix)
{
	perror(prefix);
}
#endif

#ifdef OS_WINDOWS
void net_close()
{
	if(WSACleanup() != 0) {
		net_perror("Error during Winsock cleanup");
		exit(1);
	}
}
#else
void net_close()
{
	// Not needed under UNIX
}
#endif



int main(int argc, char *argv[])
{
	const char *host; /* Host name, e.g. thor.informatik.uni-giessen.de */
	int port; /* port number, by default 13 (daytime server) */
	int s; /* Socket Descriptor */
	struct hostent *h; /* Host information */
	struct sockaddr_in addr; /* Internet address */
		/* struct sockaddr_in is defined in <netinet/in.h>,
		 * which is included via <netdb.h>.
		 * It must be compatible with / will be casted to
		 * struct sockaddr, defined in <sys/socket.h>.
		 */
	char buf[BUF_LEN];
	int len;

	/* Parse arguments: */
	if(argc == 1) {
		/* host = "thor.informatik.uni-giessen.de"; */
		host = "time.nist.gov";
		port = 13;
	}
	else if(argc == 2) {
		host = argv[1];
		port = 13;
	}
	else if(argc == 3) {
		host = argv[1];
		if(sscanf(argv[2], "%d", &port) != 1) {
			fprintf(stderr, "Invalid port number: `%s'.\n",
				argv[2]);
			exit(1);
		}
	}
	else {
		fprintf(stderr, "Invalid number of arguments (%d)!\n", argc);
		fprintf(stderr, "Must be called: dt_client [host [port]]\n");
		exit(1);
	}

	/* Initialize network rountines (under Windows): */
	net_init();

	/* Get internet address: */
	h = gethostbyname(host);
	/* Note: unsafe with multithreaded programs. Use gethostbyname_r */
	if(h == NULL) {
		fprintf(stderr, "Error looking up host: `%s'.\n", host);
		net_perror("System error message");
		exit(1);
	}

	/* Print information about host: */
	if(h->h_addrtype != AF_INET) {
		fprintf(stderr, "Not an internet address!\n");
		exit(1);
	}
	if(h->h_length != sizeof(struct in_addr)) {
		fprintf(stderr, "Bad network address size!\n");
		exit(1);
	}
	printf("Official name: %s\n", h->h_name);
	printf("Length of address: %d\n", h->h_length);
	printf("Address: %d.%d.%d.%d\n",
		(unsigned char) h->h_addr[0], (unsigned char) h->h_addr[1],
		(unsigned char) h->h_addr[2], (unsigned char) h->h_addr[3]);

	/* Set socket address: */
	memset(&addr, 0, sizeof(addr));
		/* Initialize structure to zero */
		/* Alternative: Field sin_zero (8 bytes) at the end */
	addr.sin_family = AF_INET;
	addr.sin_port = htons((short)port);
		/* htons: host to network byte order - short */
	memcpy(&(addr.sin_addr), h->h_addr, h->h_length);

	/* Allocate a Socket: */
	s = socket(AF_INET, SOCK_STREAM, 0);
	if(s == -1) {
		net_perror("Error creating socket");
		exit(1);
	}

	/* Connect to the server: */
	if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
		net_perror("Error connecting to server");
		exit(1);
	}

	/* Get data from server and print it: */
	while( (len = recv(s, buf, BUF_LEN, 0)) > 0) {
		if(write(fileno(stdout), buf, len) != len) {
			perror("Error writing output");
		}
	}

	/* Program ends: */
	return(0);
}


