Skip to content

File wintcp.c

File List > connection > wintcp.c

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */


/*
 * wintcp.c - Open a TCP connection for Windows
 */

#ident "$Id$"

#include "config.h"

#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>

#include "dbtype.h"
#ifdef SERVER_MODE
#include "connection_error.h"
#include "connection_sr.h"
#else /* SERVER_MODE */
#include "connection_cl.h"
#endif /* SERVER_MODE */
#include "error_manager.h"
#include "error_code.h"
#include "connection_globals.h"
#include "wintcp.h"
#include "host_lookup.h"
#include "porting.h"
#include "system_parameter.h"
#include "client_support.h"

#if !defined(WINDOWS)
#error belong to WINDOWS platform
#endif

#define HOST_ID_ARRAY_SIZE 8

static const int css_Tcp_max_connect_tries = 3;
static const int css_Maximum_server_count = 1000;

/* containing the last WSA error */
static int css_Wsa_error = CSS_ER_WINSOCK_NOERROR;
static FARPROC old_hook = NULL;
static int max_socket_fds = _SYS_OPEN;

static unsigned int wsa_Init_count = 0;
static unsigned int css_fd_error (SOCKET fd);

#if defined (ENABLE_UNUSED_FUNCTION)
/*
 * css_get_wsa_error() - return the last WSA error
 *   return: the last WSA error
 *
 * Note: Must be exported so its visible through the DLL.
 */
int
css_get_wsa_error (void)
{
  return css_Wsa_error;
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * css_windows_blocking_hook() - blocking hook function
 *   return:
 */
bool
css_windows_blocking_hook (void)
{
  return false;
}

/*
 * css_windows_startup() -
 *   return:
 */
int
css_windows_startup (void)
{
  WORD wVersionRequested;
  WSADATA wsaData;
  int err;

  old_hook = NULL;
  css_Wsa_error = CSS_ER_WINSOCK_NOERROR;
  wVersionRequested = 0x101;
  err = WSAStartup (wVersionRequested, &wsaData);
  if (err != 0)
    {
      /* don't use WSAGetLastError since it has not been initialized. */
      css_Wsa_error = CSS_ER_WINSOCK_STARTUP;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_WINSOCK_STARTUP, 1, err);
      return -1;
    }

  max_socket_fds = wsaData.iMaxSockets;

#if 0
  /*
   * Establish a blocking "hook" function to prevent Windows messages
   * from being dispatched when we block on reads.
   */
  old_hook = WSASetBlockingHook ((FARPROC) css_windows_blocking_hook);
  if (old_hook == NULL)
    {
      /* couldn't set up our hook */
      css_Wsa_error = CSS_ER_WINSOCK_BLOCKING_HOOK;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_WINSOCK_STARTUP, 1, WSAGetLastError ());
      (void) WSACleanup ();
      return -1;
    }
#endif

  wsa_Init_count++;

  return 1;
}

/*
 * css_windows_shutdown() -
 *   return:
 */
void
css_windows_shutdown (void)
{
  int err;

#if 0
  if (old_hook != NULL)
    {
      (void) WSASetBlockingHook (old_hook);
    }
#endif
  if (wsa_Init_count > 0)
    {
      err = WSACleanup ();
      wsa_Init_count--;
    }
}

/*
 * css_tcp_client_open() -
 *   return:
 *   hostname(in):
 *   port(in):
 */
SOCKET
css_tcp_client_open (const char *host_name, int port)
{
  SOCKET fd;

  fd = css_tcp_client_open_with_retry (host_name, port, true);
  if (IS_INVALID_SOCKET (fd))
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_TCP_CANNOT_CONNECT_TO_MASTER, 1, host_name);
    }
  return fd;
}

/*
 * css_tcp_client_open_with_retry() -
 *   return:
 *   hostname(in):
 *   port(in):
 *   willretry(in):
 */
SOCKET
css_tcp_client_open_with_retry (const char *host_name, int port, bool will_retry)
{
  int bool_value;
  SOCKET s;
  int err;
  struct hostent *dest_host;
  unsigned int remote_ip;
  struct sockaddr_in addr;
  int success, numtries;

  /* Winsock must have been opened by now */

  /* first try the internet address format */
  remote_ip = inet_addr (host_name);
  if (remote_ip == INADDR_NONE)
    {
      /* then try a host name */
      dest_host = gethostbyname_uhost (host_name);
      if (dest_host == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_WINSOCK_HOSTNAME, 2, host_name, HOSTS_FILE);
      return INVALID_SOCKET;
    }
      remote_ip = *((unsigned int *) (dest_host->h_addr));
    }

  success = 0;
  if (will_retry)
    {
      numtries = 0;
    }
  else
    {
      numtries = css_Tcp_max_connect_tries - 1;
    }

  while (!success && numtries < css_Tcp_max_connect_tries)
    {
      s = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
      if (IS_INVALID_SOCKET (s))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_WINTCP_CANNOT_CREATE_STREAM, 1, WSAGetLastError ());
      return INVALID_SOCKET;
    }

      addr.sin_family = AF_INET;
      addr.sin_port = htons (port);
      addr.sin_addr.s_addr = remote_ip;

      /*
       * The master deliberately tries to connect to itself once to
       * prevent multiple masters from running.  In the "good" case,
       * the connect will fail.  Printing the message when that happens
       * makes starting the master on NT disturbing because the users sees
       * what they think is a bad message so don't print anything here.
       */
      if (connect (s, (struct sockaddr *) &addr, sizeof (addr)) != SOCKET_ERROR)
    {
      success = 1;
    }
      else
    {
      err = WSAGetLastError ();
      (void) closesocket (s);

      if (err != WSAECONNREFUSED && err != WSAETIMEDOUT)
        {
          /* this isn't an error we handle */
          return INVALID_SOCKET;
        }
      else
        {
          /*
           * See tcp.c for Unix platforms for more information.
           * retry the connection a few times in case the server is
           * overloaded at the moment.
           * Should be sleeping here but I can't find a Windows SDK function
           * to do that.
           */
          numtries++;
        }
    }
    }

  if (!success)
    {
      return INVALID_SOCKET;
    }

  bool_value = 1;
  /* ask for the "keep alive" option, ignore errors */
  if (prm_get_bool_value (PRM_ID_TCP_KEEPALIVE))
    {
      (void) setsockopt (s, SOL_SOCKET, SO_KEEPALIVE, (const char *) &bool_value, sizeof (int));
    }

  /* ask for NODELAY, this one is rather important */
  (void) setsockopt (s, IPPROTO_TCP, TCP_NODELAY, (const char *) &bool_value, sizeof (int));

  return s;
}

/*
 * css_shutdown_socket() -
 *   return:
 *   fd(in):
 */
void
css_shutdown_socket (SOCKET fd)
{
  if (!IS_INVALID_SOCKET (fd))
    {
      closesocket (fd);
    }
}

/*
 * css_fd_error() - Determine if a socket has any queued data
 *   return:
 *   fd(in):
 */
static unsigned int
css_fd_error (SOCKET fd)
{
  unsigned long count;
  long rc;

  rc = ioctlsocket (fd, FIONREAD, &count);
  if (rc == SOCKET_ERROR)
    {
      count = -1;
    }

  return (count);
}

#if defined (ENABLE_UNUSED_FUNCTION)
/*
 * css_fd_down() - Determine if a socket has been shut down for some reason
 *   return:
 *   fd(in):
 */
int
css_fd_down (SOCKET fd)
{
  int error_code = 0;
  int error_size = sizeof (int);

  if (getsockopt (fd, SOL_SOCKET, SO_ERROR, (char *) &error_code, &error_size) == SOCKET_ERROR || error_code != 0
      || css_fd_error (fd) <= 0)
    {
      return 1;
    }

  return 0;
}
#endif

/*
 * css_gethostname() - interface for the "gethostname" function
 *   return: 0 if success, or error
 *   name(out): buffer for name
 *   namelen(in): max buffer size
 */
int
css_gethostname (char *name, size_t namelen)
{
  const char *pc_name = "PC";
  char hostname[CUB_MAXHOSTNAMELEN];
  int err = 0;

#if !defined(SERVER_MODE)
  if (css_windows_startup () < 0)
    {
      return -1;
    }
#endif /* not SERVER_MODE */

  if (gethostname (hostname, CUB_MAXHOSTNAMELEN) != SOCKET_ERROR)
    {
      if (strlen (hostname))
    {
      pc_name = hostname;
    }
    }
  else
    {
      err = WSAGetLastError ();
    }

#if !defined(SERVER_MODE)
  css_windows_shutdown ();
#endif /* not SERVER_MODE */

  strncpy (name, pc_name, namelen);
  return err;
}

/*
 * css_gethostid() - returns the hex number that represents this hosts
 *                   internet address
 *   return: pseudo-hostid if succesful, 0 if not
 */
unsigned int
css_gethostid (void)
{
  struct hostent *hp;
  char hostname[CUB_MAXHOSTNAMELEN];
  unsigned int inaddr;
  unsigned int retval;

#if !defined(SERVER_MODE)
  if (css_windows_startup () < 0)
    {
      return 0;
    }
#endif /* not SERVER_MODE */

  retval = 0;
  if (gethostname (hostname, CUB_MAXHOSTNAMELEN) == SOCKET_ERROR)
    {
      css_Wsa_error = CSS_ER_WINSOCK_HOSTNAME;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_WINSOCK_HOSTNAME, 2, hostname, HOSTS_FILE);
    }
  else
    {
      inaddr = inet_addr (hostname);
      if (inaddr != INADDR_NONE)
    {
      retval = inaddr;
    }
      else
    {
      hp = gethostbyname_uhost (hostname);
      if (hp != NULL)
        {
          retval = (*(unsigned int *) hp->h_addr);
        }
      else
        {
          css_Wsa_error = CSS_ER_WINSOCK_HOSTID;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_WINSOCK_HOSTID, 2, hostname, HOSTS_FILE);
        }
    }
    }

#if !defined(SERVER_MODE)
  css_windows_shutdown ();
#endif /* not SERVER_MODE */

  return retval;
}

/*
 * css_tcp_setup_server_datagram() - datagram stubs
 *   return:
 *   pathname(in):
 *   sockfd(in):
 *
 * Note: The Windows platforms do not support this and will instead use the
 *       "new-style" multiple port-id connection interface
 */
bool
css_tcp_setup_server_datagram (char *pathname, SOCKET * sockfd)
{
  return false;
}

/*
 * css_tcp_listen_server_datagram() - datagram stubs
 *   return:
 *   sockfd(in):
 *   newfd(in):
 *
 * Note: The Windows platforms do not support this and will instead use the
 *       "new-style" multiple port-id connection interface
 */
bool
css_tcp_listen_server_datagram (SOCKET sockfd, SOCKET * newfd)
{
  return false;
}

/*
 * css_tcp_master_datagram() - datagram stubs
 *   return:
 *   pathname(in):
 *   sockfd(in):
 *
 * Note: The Windows platforms do not support this and will instead use the
 *       "new-style" multiple port-id connection interface
 */
bool
css_tcp_master_datagram (char *pathname, SOCKET * sockfd)
{
  return false;
}

/*
 * css_open_new_socket_from_master() - datagram stubs
 *   return:
 *   fd(in):
 *   rid(in):
 *
 * Note: The Windows platforms do not support this and will instead use the
 *       "new-style" multiple port-id connection interface
 */
SOCKET
css_open_new_socket_from_master (SOCKET fd, unsigned short *rid)
{
  return INVALID_SOCKET;
}

/*
 * css_transfer_fd() - datagram stubs
 *   return:
 *   server_fd(in):
 *   client_fd(in):
 *   rid(in):
 *
 * Note: The Windows platforms do not support this and will instead use the
 *       "new-style" multiple port-id connection interface
 */
bool
css_transfer_fd (SOCKET server_fd, SOCKET client_fd, unsigned short rid, CSS_SERVER_REQUEST request)
{
  return false;
}

/* These functions support the new-style connection protocol used by Windows. */

/*
 * css_tcp_master_open() - initialize for the master server internet
 *                         communication
 *   return:
 *   port(in):
 *   sockfd(in):
 */
int
css_tcp_master_open (int port, SOCKET * sockfd)
{
  struct sockaddr_in tcp_srv_addr;  /* server's internet socket addr */
  SOCKET sock;
  int retry_count = 0;
  int bool_value;

  /*
   * We have to create a socket ourselves and bind our well-known
   * address to it.
   */
  if (css_windows_startup () < 0)
    {
      return ER_CSS_WINSOCK_STARTUP;
    }

  memset ((void *) &tcp_srv_addr, 0, sizeof (tcp_srv_addr));
  tcp_srv_addr.sin_family = AF_INET;
  tcp_srv_addr.sin_addr.s_addr = htonl (INADDR_ANY);

  if (port > 0)
    {
      tcp_srv_addr.sin_port = htons (port);
    }
  else
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_WINTCP_PORT_ERROR, 0);
      return ERR_CSS_WINTCP_PORT_ERROR;
    }

  /*
   * Create the socket and Bind our local address so that any
   * client may send to us.
   */

retry:
  sock = socket (AF_INET, SOCK_STREAM, 0);
  if (IS_INVALID_SOCKET (sock))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_WINTCP_CANNOT_CREATE_STREAM, 1, WSAGetLastError ());
      *sockfd = sock;
      return ERR_CSS_WINTCP_CANNOT_CREATE_STREAM;
    }

  /*
   * Allow the new master to rebind the CUBRID port even if there are
   * clients with open connections from previous masters.
   */
  bool_value = 0;
  setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &bool_value, sizeof (int));

  bool_value = 1;
  setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (const char *) &bool_value, sizeof (int));

  if (bind (sock, (struct sockaddr *) &tcp_srv_addr, sizeof (tcp_srv_addr)) == SOCKET_ERROR)
    {
      if (WSAGetLastError () == WSAEADDRINUSE && retry_count <= 5)
    {
      retry_count++;
      sleep (1);
      css_shutdown_socket (sock);
      goto retry;
    }

      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_WINTCP_BIND_ABORT, 1, WSAGetLastError ());
      css_shutdown_socket (sock);
      *sockfd = sock;
      return ERR_CSS_WINTCP_BIND_ABORT;
    }

  /*
   * And set the listen parameter, telling the system that we're
   * ready to accept incoming connection requests.
   */
  listen (sock, css_Maximum_server_count);

  *sockfd = sock;
  return NO_ERROR;
}

/*
 * css_accept() - accept of a request from a client
 *   return:
 *   sockfd(in):
 */
static SOCKET
css_accept (SOCKET sockfd)
{
  struct sockaddr_in tcp_cli_addr;
  SOCKET newsockfd;
  int clilen, error;

  while (true)
    {
      clilen = sizeof (tcp_cli_addr);
      newsockfd = accept (sockfd, (struct sockaddr *) &tcp_cli_addr, &clilen);

      if (IS_INVALID_SOCKET (newsockfd))
    {
      error = WSAGetLastError ();
      if (error == WSAEINTR)
        {
          continue;
        }

      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_WINTCP_ACCEPT_ERROR, 1, error);
      return INVALID_SOCKET;
    }

      break;
    }

  return newsockfd;
}


/*
 * css_master_accept() - master accept of a request from a client
 *   return:
 *   sockfd(in):
 */
SOCKET
css_master_accept (SOCKET sockfd)
{
  return css_accept (sockfd);
}

/*
 * css_open_server_connection_socket() - open the socket used by the server
 *                                       for incoming client connection
 *                                       requests
 *   return: port id
 */
int
css_open_server_connection_socket (void)
{
  struct sockaddr_in tcp_srv_addr;  /* server's internet socket addr */
  SOCKET fd;
  int get_length;
  int bool_value;

#if !defined(SERVER_MODE)
  if (css_windows_startup () < 0)
    {
      return -1;
    }
#endif /* not SERVER_MODE */

  /* Create the socket */
  fd = socket (AF_INET, SOCK_STREAM, 0);
  if (IS_INVALID_SOCKET (fd))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_WINTCP_CANNOT_CREATE_STREAM, 1, WSAGetLastError ());
      return -1;
    }

  bool_value = 1;
  setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (const char *) &bool_value, sizeof (int));

  if (prm_get_bool_value (PRM_ID_TCP_KEEPALIVE))
    {
      setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, (const char *) &bool_value, sizeof (int));
    }

  /*
   * Set up an address asking for "any" (the local ?) IP addres
   * and set the port to zero to it will be automatically assigned.
   */
  memset ((void *) &tcp_srv_addr, 0, sizeof (tcp_srv_addr));
  tcp_srv_addr.sin_family = AF_INET;
  tcp_srv_addr.sin_addr.s_addr = htonl (INADDR_ANY);
  tcp_srv_addr.sin_port = 0;


  /* Bind the socket */
  if (bind (fd, (struct sockaddr *) &tcp_srv_addr, sizeof (tcp_srv_addr)) == SOCKET_ERROR)
    {
      goto error;
    }

  /* Determine which port_id the system has assigned. */
  get_length = sizeof (tcp_srv_addr);
  if (getsockname (fd, (struct sockaddr *) &tcp_srv_addr, &get_length) == SOCKET_ERROR)
    {
      goto error;
    }

  /*
   * Set it up to listen for incoming connections.  Note that under Winsock
   * (NetManage version at least), the backlog parameter is silently limited
   * to 5, regardless of what is requested.
   */
  if (listen (fd, css_Maximum_server_count) == SOCKET_ERROR)
    {
      goto error;
    }

  css_Server_connection_socket = fd;

  return (int) ntohs (tcp_srv_addr.sin_port);

error:
  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ERR_CSS_WINTCP_BIND_ABORT, 1, WSAGetLastError ());
  css_shutdown_socket (fd);
  return -1;
}

/*
 * css_close_server_connection_socket() - Close the socket that was opened by
 *                                        the server for incoming client
 *                                        requests
 *   return: void
 */
void
css_close_server_connection_socket (void)
{
  if (!IS_INVALID_SOCKET (css_Server_connection_socket))
    {
      closesocket (css_Server_connection_socket);
      css_Server_connection_socket = INVALID_SOCKET;
    }
}

/*
 * css_server_accept() - accept an incoming connection on the server's
 *                       connection socket
 *   return: the fd of the new connection
 *   sockfd(in):
 */
SOCKET
css_server_accept (SOCKET sockfd)
{
  return css_accept (sockfd);
}

int
css_get_max_socket_fds (void)
{
  return max_socket_fds;
}

/*
 * css_get_peer_name() - get the hostname of the peer socket
 *   return: 0 if success; otherwise errno
 *   hostname(in): buffer for hostname
 *   len(in): size of the hostname buffer
 */
int
css_get_peer_name (SOCKET sockfd, char *hostname, size_t len)
{
  struct sockaddr_in saddr_buf;
  struct sockaddr *saddr;
  int saddr_len;

  saddr = (struct sockaddr *) &saddr_buf;
  saddr_len = sizeof (saddr_buf);
  if (getpeername (sockfd, saddr, &saddr_len) != 0)
    {
      return WSAGetLastError ();
    }
  return getnameinfo_uhost (saddr, saddr_len, hostname, (DWORD) len, NULL, 0, NI_NOFQDN);
}

/*
 * css_get_sock_name() - get the hostname of the socket
 *   return: 0 if success; otherwise errno
 *   hostname(in): buffer for hostname
 *   len(in): size of the hostname buffer
 */
int
css_get_sock_name (SOCKET sockfd, char *hostname, size_t len)
{
  struct sockaddr_in saddr_buf;
  struct sockaddr *saddr;
  int saddr_len;

  saddr = (struct sockaddr *) &saddr_buf;
  saddr_len = sizeof (saddr_buf);
  if (getsockname (sockfd, saddr, &saddr_len) != 0)
    {
      return WSAGetLastError ();
    }
  return getnameinfo_uhost (saddr, saddr_len, hostname, (DWORD) len, NULL, 0, NI_NOFQDN);
}

/*
 * css_hostname_to_ip()
 *   return:
 *   host(in):
 *   ip_addr(out):
 */
int
css_hostname_to_ip (const char *host, unsigned char *ip_addr)
{
  unsigned int in_addr;
  int err = NO_ERROR;

#if !defined(SERVER_MODE)
  if (css_windows_startup () < 0)
    {
      return ER_CSS_WINSOCK_STARTUP;
    }
#endif /* not SERVER_MODE */

  /*
   * First try to convert to the host name as a dotted-decimal number.
   * Only if that fails do we call gethostbyname_uhost.
   */
  in_addr = inet_addr (host);
  if (in_addr != INADDR_NONE)
    {
      memcpy ((void *) ip_addr, (void *) &in_addr, sizeof (in_addr));
    }
  else
    {
      struct hostent *hp;

      hp = gethostbyname_uhost (host);
      if (hp == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CSS_WINSOCK_HOSTNAME, 2, host, HOSTS_FILE);
      err = ER_CSS_WINSOCK_HOSTNAME;
    }
      else
    {
      memcpy ((void *) ip_addr, (void *) hp->h_addr, hp->h_length);
    }
    }

#if !defined(SERVER_MODE)
  css_windows_shutdown ();
#endif /* not SERVER_MODE */

  return err;
}