//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / pf
//
//File: udpsocket.cpp
//
//Version: $Revision: 1.3 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/10/19 08:46:03 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Author:
//      Juhana Räsänen
//
//Description:
//      See the corresponding header file
//
//Copyright:
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//
//Licence:
//
//
//History: 
//
//

#include "udpsocket.h"
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>	// strerror
#include <errno.h>	// errno
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include "pf/debug.h"

//
//Function: pfUDPsocket constructor
//
//Description:
//    UDP socket device constructor allocates buffers and copies the
//    given remote host name to local space so that we don't need to
//    worry about validity of the pointer when openDevice gets called.
//    Most of the device initializations are made in base class ctor.
//

pfUDPsocket :: pfUDPsocket(pfUlong bufferSize_)
    : pfDevice(bufferSize_),
      _isAddressLocked(0)
{
    // Allocate buffers
    assert(bufferSize_ > 0);
    _readBuffer = new char[_bufferSize];
    _writeBuffer = new char[_bufferSize];
    assert((_readBuffer != 0) && (_writeBuffer != 0));
    return;
}

//
//Function: pfUDPsocket destructor
//
//Description:
//    Closes the device and frees buffers, This must be here because
//    it cannot be done from the pfDevice destructor (see pf/device.cpp
//    comments for explanation).
//

pfUDPsocket :: ~pfUDPsocket (void)
{
    closeDevice();
    freeBuffers();
    return;
}

//
//Function: openDevice
//
//Description:
//    Opens, binds and connects a UDP socket. The remote end IP address
//    is looked up with gethostbyname() call, so name services or hosts
//    file must be intact. If something goes wrong in the procedure,
//    socket is not opened and device ERROR status bit is set.
//    Added by Timo Kokkkonen:
//    If remote_hostname_:s length is 0 then device opens with INADDR_ANY
//    options and now accept all connections in remote_port_.

bool pfUDPsocket :: openDevice(
    string remote_hostname_,
    int local_port_,
    int remote_port_)
{
    if (_fd != NO_FD)
    {
	closeDevice();
    }

    struct sockaddr_in addr;

    int descriptor = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (descriptor < 0)
    {
        setStatusError();
        debugString("pfUDPsocket: Error in openDevice : socket", strerror(errno));
        return 1;
    }
    setFD(descriptor);
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;

    if(remote_hostname_.length() != 0)
    {
        addr.sin_port = local_port_;
    
        if (::bind(_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
        {
            setStatusError();
            debugString("pfUDPsocket: Error in openDevice : bind", strerror(errno));
            closeDevice();
            return 1;
        }
        memset(&addr, 0, sizeof(addr));
        struct hostent *host = gethostbyname(remote_hostname_.c_str());
        if (host == 0)
        {
            setStatusError();
            debugString("pfUDPsocket: Error in openDevice: gethostbyname", strerror(errno));
            closeDevice();
            return 1;
        }
	memcpy(&(addr.sin_addr), host->h_addr_list[0], sizeof(addr.sin_addr));

        addr.sin_family = AF_INET;
        addr.sin_port = remote_port_;
        
        if (::connect(_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
        {
            setStatusError();
            debugString("pfUDPsocket: Error in openDevice : connect", strerror(errno));
            closeDevice();
            return 1;
        }

	setAddr(addr);
	_isAddressLocked = 1;
    }
    else // accept any connection
    {
        addr.sin_addr.s_addr=INADDR_ANY;
        addr.sin_port = remote_port_;

        int addrLen = sizeof(addr);
        if (bind(_fd,(struct sockaddr *) &addr, addrLen) < 0)
        {
            setStatusError();
            debugString("pfUDPsocket: Error in openDevice: bind", strerror(errno));
            closeDevice();
            return 1;
        }
    }

    return 0;
}

//
//Function: freeBuffers
//
//Description:
//    Method to free the buffers (allocated in the constructor).
//

void pfUDPsocket :: freeBuffers(void)
{
    delete [] _readBuffer;
    delete [] _writeBuffer;
    return;
}

//
//Function: readFromSocket  
//
//Description:
//    Reads data from socket to _readBuffer and returns readed data's length.
//
  
int pfUDPsocket :: readFromSocket(void)
{
    int length;
    unsigned int fromlen = sizeof(sockaddr);

    length = ::recvfrom(_fd, _readBuffer, _bufferSize,
                            0, (struct sockaddr *) &_addr, &fromlen);
    return length;
}

//
//Function: writeToSocket
//
//Description:
//    Writes data from _writeBuffer to socket and returns writed data's length.
//
 
int pfUDPsocket :: writeToSocket(char *start_)
{ 
    int nbytes;
    int tolen = sizeof(sockaddr);
    nbytes = ::sendto(_fd, start_, _writeBufferLength,
			  0, (struct sockaddr *) &_addr, tolen);
    return nbytes;
}

//
//Function: setAddr
//
//Description:
//
//
void pfUDPsocket :: setAddr(const struct sockaddr_in &addr_)
{
    if (_isAddressLocked == 0)
    {
        _addr = addr_;
    }
    return;
}

//
//Function: getAddr
//
//Description:
//
//
const struct sockaddr_in &pfUDPsocket :: getAddr(void) const
{
    return _addr;
}

