/**************************************/
/* INCLUDES */

#include "tool.h"


/**************************************/
/* FUNCTIONS */

int establish_ip_connection(sockaddr_in * addr_ptr, int port,
				int *data_sock_ptr, int type)
	{
	// This function establish a connection of type 'type' to the host 
	// represented by addr_ptr at the port 'port'. The file descriptor 
	// will be put in data_sock. The sockaddr_in address is expected to
	// contain only the destination address (the field sin_addr must 
	// be set properly). All other fields of the structure will be 
	// assigned by this function. The function returns 1 on error, 0 
	// otherwise.
	// Arguments:
	// sockaddr_in address with sin_addr set properly.
	// Port.
	// Data socket.
	// Type (TCP_CONNECTION | UDP_CONNECTION).
	int error;
	
	// Setup structure fields.
	addr_ptr->sin_family = AF_INET;
	addr_ptr->sin_port = htons(port);
	memset(addr_ptr->sin_zero, 0, 8);
	
	error = open_ip_sock(type, data_sock_ptr);
	if(error)
		{
		return 1;
		}

	// Establish connection.
	error = connect(*data_sock_ptr, (struct sockaddr *) addr_ptr,
			sizeof(*addr_ptr));
	
	if(error == -1)
		{
		close(*data_sock_ptr);
		perror("Cannot connect to host");
		return 1;
		}
	
	return 0;
	}


/**************************************/
int open_ip_sock(int type, int *the_sock)
	{
	// This function opens a IP socket of type 'type'. The resulting socket
	// is put in the_sock. The function returns 1 on error, 0
	// otherwise.
	// Arguments:
	// Type of the socket (TCP_CONNECTION | UDP_CONNECTION).
	// Socket *.
	int conn_type;
	
	if(type == TCP_CONNECTION)
		{
		conn_type = SOCK_STREAM;
		}
	
	else if(type == UDP_CONNECTION)
		{
		conn_type = SOCK_DGRAM;
		}
	
	else
		{
		display_warning("Wrong connection type.");
		return 1;
		}
	
	*the_sock = socket(AF_INET, conn_type, 0);
	
	if(*the_sock == -1)
		{
		perror("Error creating socket");
		return 1;
		}
	
	return 0;
	}


/**************************************/
int bind_ip_sock(int *sock_ptr, int port)
	{
	// This function binds sock_ptr to port. It returns 1 on error,
	// 0 otherwise.
	int error;
	sockaddr_in local_addr;
	
	local_addr.sin_family = AF_INET;
	local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	local_addr.sin_port = htons(port);
	
	memset(local_addr.sin_zero, 0, 8);
	
	error = bind(*sock_ptr, (sockaddr *) &local_addr, 
		     sizeof(local_addr));
	
	if(error == -1)
		{
		perror("Error binding socket");
		return 1;
		}
	
	return 0;
	}

/*******************************************/
int get_port(char *arg, int *port)
	{
	// This function reads arg and convert it to port number. Returns 1 on
	// error, 0 otherwise.
	// Arguments:
	// String.
	// Port.
	char *end_ptr;
	
	errno = 0;
	*port = strtol(arg, &end_ptr, 10);
	if(*port == 0 && *end_ptr != '\0' || errno)
		{
		printf("Port parameter '%s' is invalid.\n", arg);
		return 1;
		}
	
	return 0;
	}


/*******************************************/
int get_ip_address(char *addr_str, sockaddr_in * addr_ptr)
	{
	// This functions reads the address string and converts it to a
	// sockaddr_in struct. Returns 1 on error, 0 otherwise.
	int error;
	
	// This doesn't validate right, but neither does the standard unix
	// function. Plus this convert hostnames to IP as well.
	addr tmp;
	error = addr_pton(addr_str, &tmp);
	if(error)
		{
		display_error("Cannot convert address: Bad format.");
		return 1;
		}
	
	error = addr_ntos(&tmp, (sockaddr *) addr_ptr);
	if(error)
		{
		display_error
		("Cannot convert address: addr_ntos failure.");
		return 1;
		}
	
	return 0;
	}


/*******************************************/
int shutdown_read_on_socket(int *sock_ptr)
	{
	// This function will make the socket refuses all incoming data.
	// It returns 1 on error, 0 otherwise.
	// Arguments:
	// Socket.
	int error;
	
	error = shutdown(*sock_ptr, SHUT_RD);
	
	if(error == -1)
		{
		perror("Cannot shutdown socket");
		return 1;
		}
	
	return 0;
	}
	

/**************************************/
void put_int(unsigned long the_int, unsigned char *buffer)
	{
	// This function puts an int into a char buffer, in network byte order.
	// Arguments:
	// Int.
	// Buffer.
	unsigned long long_nbo = htonl(the_int);
	
	buffer[0] = (char) ((long_nbo & 0xFF));
	buffer[1] = (char) ((long_nbo & 0xFF00) >> 8);
	buffer[2] = (char) ((long_nbo & 0xFF0000) >> 16);
	buffer[3] = (char) ((long_nbo & 0xFF000000) >> 24);
	}


/**************************************/
unsigned long get_int(unsigned char *buffer)
	{
	// This function returns an int from a char buffer, in host byte order.
	// Arguments:
	// Buffer.
	unsigned long long_nbo = 0;
	
	long_nbo |= buffer[3] << 24;
	long_nbo |= buffer[2] << 16;
	long_nbo |= buffer[1] << 8;
	long_nbo |= buffer[0];
	
	return ntohl(long_nbo);
	}
