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