/* OpenVAS-Client
 *
 * Description: Socket wrappers for W32.
 *
 * Authors:
 * Marcus Brinkmann <marcus.brinkmann@ruhr-uni-bochum.de>
 *
 * Copyright:
 * Copyright (C) 2009 Intevation GmbH
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * or, at your option, any later version as published by the Free
 * Software Foundation
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * In addition, as a special exception, you have
 * permission to link the code of this program with the OpenSSL
 * library (or with modified versions of OpenSSL that use the same
 * license as OpenSSL), and distribute linked combinations including
 * the two. You must obey the GNU General Public License in all
 * respects for all of the code used other than OpenSSL. If you
 * modify this file, you may extend this exception to your version
 * of the file, but you are not obligated to do so. If you do not
 * wish to do so, delete this exception statement from your version.
 */

#include <config.h>

#ifdef _WIN32
#define HAVE_W32_SYSTEM 1
#endif

#include <stdio.h>
#include <stdlib.h>

#ifdef HAVE_W32_SYSTEM
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincrypt.h>
#include <io.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#endif

#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>

#include "openvas-socket.h"

#ifdef HAVE_W32_SYSTEM
#ifndef S_IRGRP
# define S_IRGRP 0
# define S_IWGRP 0
#endif
#endif

#if HAVE_W32_SYSTEM
#define SOCKET2HANDLE(s) ((int)(s))
#define HANDLE2SOCKET(h) ((SOCKET)(h))
#else
#define SOCKET2HANDLE(s) (s)
#define HANDLE2SOCKET(h) (h)
#endif


/**
 * @brief Same as perror(), but prefixes the data by pid.
 */
static int
openvas_perror (const char* error)
{
  fprintf(stderr, "[%d] %s: %s\n", getpid(), error, strerror(errno));
  return 0;
}


#ifdef HAVE_W32_SYSTEM
int
_openvas_sock_wsa2errno (int err)
{
  switch (err)
    {
    case WSAENOTSOCK:
      return EINVAL;
    case WSAEWOULDBLOCK:
    case WSAEINPROGRESS:
      return EAGAIN;
    case ERROR_BROKEN_PIPE:
      return EPIPE;
    case WSANOTINITIALISED:
      return ENOSYS;
    default:
      return EIO;
    }
}
#endif /* HAVE_W32_SYSTEM */

int
_openvas_close (int fd)
{
#ifdef HAVE_W32_SYSTEM
  int rc = closesocket (HANDLE2SOCKET(fd));
  if (rc)
    errno = _openvas_sock_wsa2errno (WSAGetLastError ());
  if (rc && WSAGetLastError () == WSAENOTSOCK)
    {
      rc = CloseHandle (fd);
      if (rc)
    /* FIXME. */
    errno = EIO;
    }
  return rc;
#else
  return close (fd);
#endif
}


/* Return a new socket.  Note that under W32 we consider a socket the
   same as an System Handle; all functions using such a handle know
   about this dual use and act accordingly. */ 
int
_openvas_sock_new (int domain, int type, int proto)
{
#ifdef HAVE_W32_SYSTEM
  int res;
  res = SOCKET2HANDLE(socket (domain, type, proto));
  if (res == -1)
    errno = _openvas_sock_wsa2errno (WSAGetLastError ());
  return res;
#else
  return socket (domain, type, proto);
#endif
}


int
_openvas_sock_connect (int sockfd, struct sockaddr *addr, int addrlen)
{
#ifdef HAVE_W32_SYSTEM
  int res;
  res = connect (HANDLE2SOCKET (sockfd), addr, addrlen);
  if (res < 0)
    errno = _openvas_sock_wsa2errno (WSAGetLastError ());
  return res;
#else
  return connect (sockfd, addr, addrlen);
#endif
}


int
_openvas_sock_bind (int sockfd, struct sockaddr *addr, int addrlen)
{
#ifdef HAVE_W32_SYSTEM
  int res = bind (HANDLE2SOCKET(sockfd), addr, addrlen);
  if (res < 0)
    errno = _openvas_sock_wsa2errno (WSAGetLastError ());
  return res;
#else
  return bind (sockfd, addr, addrlen);
#endif
}


int
_openvas_sock_unblock (int soc)
{
#ifdef HAVE_W32_SYSTEM
  unsigned long arg = 1;
  int res = ioctlsocket (HANDLE2SOCKET (soc), FIONBIO, &arg);
  if (res < 0)
    errno = _openvas_sock_wsa2errno (WSAGetLastError ());
  return res;
#else
  int flags =  fcntl(soc, F_GETFL, 0);

  if (flags < 0)
    {
      openvas_perror("fcntl(F_GETFL)");
      return -1;
    }
  if (fcntl(soc, F_SETFL, O_NONBLOCK | flags) < 0)
    {
      openvas_perror("fcntl(F_SETFL,O_NONBLOCK)");
      return -1;
    }
  return 0;
#endif
}


static int
_openvas_sock_block (int soc)
{
#ifdef HAVE_W32_SYSTEM
  unsigned long arg = 0;
  int res = ioctlsocket (HANDLE2SOCKET (soc), FIONBIO, &arg);
  if (res < 0)
    errno = _openvas_sock_wsa2errno (WSAGetLastError ());
  return res;
#else
  int flags =  fcntl(soc, F_GETFL, 0);
  if (flags < 0)
    {
      openvas_perror("fcntl(F_GETFL)");
      return -1;
    }
  if (fcntl(soc, F_SETFL, (~O_NONBLOCK) & flags) < 0)
    {
      openvas_perror("fcntl(F_SETFL,~O_NONBLOCK)");
      return -1;
    }
  return 0;
#endif
}


/* Public API.  */
int
openvas_sock_close (int fd)
{
  return _openvas_close (fd);
}

int 
openvas_sock_new (int domain, int type, int proto)
{
  return _openvas_sock_new (domain, type, proto);
}

int
openvas_sock_connect (int sockfd, struct sockaddr *addr, int addrlen)
{
  return _openvas_sock_connect (sockfd, addr, addrlen);
}

int
openvas_sock_bind (int sockfd, struct sockaddr *addr, int addrlen)
{
  return _openvas_sock_bind (sockfd, addr, addrlen);
}

int
openvas_sock_block (int soc)
{
  return _openvas_sock_block (soc);
}

int
openvas_sock_unblock (int soc)
{
  return _openvas_sock_unblock (soc);
}
