/* A communications module for the socket locate service server.
   This module is used only by 'socloc' servers to talk to
   other 'socloc' servers. Applies to TCP socket IPC method only.
   Rick Smereka, Copyright (C) 2003.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   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, get a copy via the Internet at
   http://gnu.org/copyleft/gpl.html or write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
   MA 02111-1307 USA

   You can contact the author via email at rsmereka@future-lab.com

   This module was constructed to enable a 'socloc' server to communicate
   with other 'socloc' and socket servers without using the standard 'socloc'
   client API. It was found that having both client applications and
   'socloc' servers using the standard 'socloc' API ('sloc.c') was
   causing too many interactions. Since a specific 'socloc' server
   may talk to a number of other 'socloc' servers along with any
   number of socket servers, this API does not follow the model of
   other Future Lab socket API's. Instead of connecting to another
   server and leaving the socket open, this API performs a 'connect',
   send, receive and a disconnect once per request.

   Original Linux version. May/2003, Rick Smereka

   Ported to 32-bit Windows, Jun/2003, Rick Smereka */

#include "stdhead.h"          // standard include
#include "flsocket.h"         // standard socket defines
#include "ipcomm.h"           // low level socket send/receive
#include "slocsrv.h"          // this module
#include "socloc.h"           // global socket locator defines
#include "slconfig.h"         // socket locate config management
#include "ip.h"               // IP library routines

// global (to module) data

#ifdef OS_WIN32
SOCKET slclientSocket;        // client socket
SOCKADDR_IN slsockClientAddr; // client address structure
LPHOSTENT lpHostEnt;          // host info structure
#endif

#ifdef OS_UNIX
int slclientSocket;
struct sockaddr_in slsockClientAddr;
struct hostent *lpHostEnt;
#endif

char sloc_hostname[128];      // host name of 'socloc' server
int sloc_port = 0;            // port 'socloc' is using

// private functions

static int sloc_ll_status(void);
static int sloc_sr_code(int, char *);
static int sloc_sr_char(int, char *, char *);
static int sloc_client_connect(void);

int sloc_custom_status(char *hname, int port)
{
   /* Query the status of any socket server on any port.
      Socket server must understand the status command
      code (see 'flsocket.h'). Function returns
      'SL_OK' if the server is responding, a 'socloc'
      code otherwise. */

   char mname[] = "sloc_custom_status";
   char thost[128];
   int ret, tport;

   log_file_date("%s:enter", mname);

   if (hname == (char *)NULL || !strlen(hname))
      {
      log_file_date("%s:null or empty[hname]", mname);
      return(SL_INVALID_PARAMETER);
      }

   if (port <= 0)
      {
      log_file_date("%s:out of range[port]", mname);
      return(SL_INVALID_PARAMETER);
      }

   /* save current host name and port */

   strcpy(thost, sloc_hostname);
   tport = sloc_port;

   /* load new values */

   strcpy(sloc_hostname, hname);
   sloc_port = port;

   /* call low level status */

   if ((ret = sloc_ll_status()) != SL_OK)
      {
      log_file_date("%s:bad rc[%d] from sloc_ll_status", mname, ret);
      strcpy(sloc_hostname, thost);
      sloc_port = tport;
      return(ret);
      }

   log_file_date("%s:normal exit[%d]", mname, ret);
   strcpy(sloc_hostname, thost);
   sloc_port = tport;
   return(ret);
}

int sloc_get_list(char *list_out)
{
   /* Get the 'socloc' list. The delimited list is
      returned in 'list_out' which must already be allocated
      by the caller to sufficient size. Function returns
      'SL_OK' upon success, a 'socloc' code otherwise. */

   char mname[] = "sloc_get_list";
   int ret;

   log_file_date("%s:enter", mname);

   if (list_out == (char *)NULL)
      {
      log_file_date("%s:null[list_out]", mname);
      return(SL_INVALID_PARAMETER);
      }

   ret = sloc_sr_char(SL_SEND_GET_LIST, (char *)NULL, list_out);
   log_file_date("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_config_get_list(char *list_out)
{
   /* Get the 'socloc' config list. The delimited list is
      returned in 'list_out' which must already be allocated
      by the caller to sufficient size. Function returns
      'SL_OK' upon success, a 'socloc' code otherwise. */

   char mname[] = "sloc_config_get_list";
   int ret;

   log_file_date("%s:enter", mname);

   if (list_out == (char *)NULL)
      {
      log_file_date("%s:null[list_out]", mname);
      return(SL_INVALID_PARAMETER);
      }

   ret = sloc_sr_char(SL_SEND_CONFIG_GET_LIST, (char *)NULL, list_out);
   log_file_date("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_bcast_add(char *sname, char *hname, int port, char *ip_ad)
{
   /* Send an add command to each known 'socloc' server.
      This function should only be used by a 'socloc' server.
      Note that it is not considered an error if a 'socloc'
      server does not respond or responds with an error.
      Function returns a 'socloc' code. */

   char mname[] = "sloc_bcast_add";
   char thost[128], *aentry;
   int tport, nentries, i, ret;

   log_file_date("%s:enter", mname);

   if (sname == (char *)NULL || !strlen(sname))
      {
      log_file_date("%s:null or empty[sname]", mname);
      return(SL_INVALID_PARAMETER);
      }

   if (hname == (char *)NULL || !strlen(hname))
      {
      log_file_date("%s:null or empty[hname]", mname);
      return(SL_INVALID_PARAMETER);
      }

   if (port <= 0)
      {
      log_file_date("%s:out of range[port]", mname);
      return(SL_INVALID_PARAMETER);
      }

   /* save current server host name and port */

   strcpy(thost, sloc_hostname);
   tport = sloc_port;

   if ((ret = sl_config_get_nentries(&nentries)) != SL_OK)
      {
      log_file_date("%s:bad rc[%d] from sl_config_get_nentries", mname, ret);
      return(ret);
      }

   log_file_date("%s:number of config entries is %d", mname, nentries);

   if ((aentry = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      log_file_date("%s:alloc fail[aentry]", mname);
      return(SL_MEMORY_FAIL);
      }

   if (ip_ad != (char *)NULL && strlen(ip_ad))
      sprintf(aentry, "0 '%s' '%s' %d %s", sname, hname, port, ip_ad);
   else
      sprintf(aentry, "0 '%s' '%s' %d", sname, hname, port);

   /* go through list of servers, sending an add command to each
      with the 'orig_flag' set to a server (0) */

   for(i = 1; i <= nentries; i++)
      {
      if ((ret = sl_config_get_nth(i, sloc_hostname, &sloc_port, (char *)NULL))
          == SL_OK)
         {
         ret = sloc_sr_code(SL_SEND_ADD, aentry);
         log_file_date("%s[%s,%d]rc=%d", mname, sloc_hostname, sloc_port, ret);
         }
      }

   /* put back current entry */

   strcpy(sloc_hostname, thost);
   sloc_port = tport;
   free(aentry);
   log_file_date("%s:normal exit[0]", mname);
   return(SL_OK);
}

int sloc_bcast_delete(int port)
{
   /* Send a delete command to each known 'socloc' server.
      This function should only be used by a 'socloc' server.
      Note that it is not considered an error if a 'socloc'
      server does not respond or responds with an error.
      Function returns a 'socloc' code. */

   char mname[] = "sloc_bcast_delete";
   char thost[128], *aentry;
   int tport, nentries, i, ret;

   log_file_date("%s:enter,port=%d", mname, port);

   if (port <= 0)
      {
      log_file_date("%s:out of range[port]", mname);
      return(SL_INVALID_PARAMETER);
      }

   /* save current server host name and port */

   strcpy(thost, sloc_hostname);
   tport = sloc_port;

   if ((ret = sl_config_get_nentries(&nentries)) != SL_OK)
      {
      log_file_date("%s:bad rc[%d] from sl_config_get_nentries", mname, ret);
      return(ret);
      }

   log_file_date("%s:number of config entries is %d", mname, nentries);

   if ((aentry = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      log_file_date("%s:alloc fail[aentry]", mname);
      return(SL_MEMORY_FAIL);
      }

   /* indicate a server send with the initial 'orig_flag' set to zero */

   sprintf(aentry, "0 %d", port);

   /* go through list of servers, sending a config add command to each */

   for(i = 1; i <= nentries; i++)
      {
      if ((ret = sl_config_get_nth(i, sloc_hostname, &sloc_port, (char *)NULL))
          == SL_OK)
         {
         ret = sloc_sr_code(SL_SEND_DELETE, aentry);
         log_file_date("%s[%s,%d]rc=%d", mname, sloc_hostname, sloc_port, ret);
         }
      }

   /* put back current entry */

   strcpy(sloc_hostname, thost);
   sloc_port = tport;
   free(aentry);
   log_file_date("%s:normal exit[0]", mname);
   return(SL_OK);
}

int sloc_bcast_config_add(char *hname, int port, char *ip_ad)
{
   /* Send a config add command to each known 'socloc' server.
      This function should only be used by a 'socloc' server.
      Note that it is not considered an error if a 'socloc'
      server does not respond or responds with an error.
      Function returns a 'socloc' code. */

   char mname[] = "sloc_bcast_config_add";
   char thost[128], *aentry;
   int tport, nentries, i, ret;

   log_file_date("%s:enter", mname);

   if (hname == (char *)NULL || !strlen(hname))
      {
      log_file_date("%s:null or empty[hname]", mname);
      return(SL_INVALID_PARAMETER);
      }

   if (port <= 0)
      {
      log_file_date("%s:out of range[port]", mname);
      return(SL_INVALID_PARAMETER);
      }

   /* save current server host name and port */

   strcpy(thost, sloc_hostname);
   tport = sloc_port;

   if ((ret = sl_config_get_nentries(&nentries)) != SL_OK)
      {
      log_file_date("%s:bad rc[%d] from sl_config_get_nentries", mname, ret);
      return(ret);
      }

   log_file_date("%s:number of config entries is %d", mname, nentries);

   if ((aentry = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      log_file_date("%s:alloc fail[aentry]", mname);
      return(SL_MEMORY_FAIL);
      }

   /* indicate a server send with the initial 'orig_flag' set to zero */

   if (ip_ad != (char *)NULL && strlen(ip_ad))
      sprintf(aentry, "0 '%s' %d %s", hname, port, ip_ad);
   else
      sprintf(aentry, "0 '%s' %d", hname, port);

   /* go through list of servers, sending a config add command to each */

   for(i = 1; i <= nentries; i++)
      {
      if ((ret = sl_config_get_nth(i, sloc_hostname, &sloc_port, (char *)NULL))
          == SL_OK)
         {
         ret = sloc_sr_code(SL_SEND_CONFIG_ADD, aentry);
         log_file_date("%s[%s,%d]rc=%d", mname, sloc_hostname, sloc_port, ret);
         }
      }

   /* put back current entry */

   strcpy(sloc_hostname, thost);
   sloc_port = tport;
   free(aentry);
   log_file_date("%s:normal exit[0]", mname);
   return(SL_OK);
}

int sloc_bcast_config_delete(int port)
{
   /* Send a config delete command to each known 'socloc' server.
      This function should only be used by a 'socloc' server.
      Note that it is not considered an error if a 'socloc'
      server does not respond or responds with an error.
      Function returns a 'socloc' code. */

   char mname[] = "sloc_bcast_config_delete";
   char thost[128], *aentry;
   int tport, nentries, i, ret;

   log_file_date("%s:enter", mname);

   if (port <= 0)
      {
      log_file_date("%s:out of range[port]", mname);
      return(SL_INVALID_PARAMETER);
      }

   /* save current server host name and port */

   strcpy(thost, sloc_hostname);
   tport = sloc_port;

   if ((ret = sl_config_get_nentries(&nentries)) != SL_OK)
      {
      log_file_date("%s:bad rc[%d] from sl_config_get_nentries", mname, ret);
      return(ret);
      }

   log_file_date("%s:number of config entries is %d", mname, nentries);

   if ((aentry = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      log_file_date("%s:alloc fail[aentry]", mname);
      return(SL_MEMORY_FAIL);
      }

   sprintf(aentry, "0 %d", port);

   /* go through list of servers, sending a config add command to each */

   for(i = 1; i <= nentries; i++)
      {
      if ((ret = sl_config_get_nth(i, sloc_hostname, &sloc_port, (char *)NULL))
          == SL_OK)
         {
         ret = sloc_sr_code(SL_SEND_CONFIG_DELETE, aentry);
         log_file_date("%s[%s,%d]rc=%d", mname, sloc_hostname, sloc_port, ret);
         }
      }

   /* put back current entry */

   strcpy(sloc_hostname, thost);
   sloc_port = tport;
   free(aentry);
   log_file_date("%s:normal exit[0]", mname);
   return(SL_OK);
}

void sloc_get_active_socloc(char *hname, int *port)
{
   /* Get and return the active 'socloc' server host name
      and port number. 'hname' must be allocated by the
      caller to sufficient size. */

   char mname[] = "sloc_get_active_socloc";

   if (hname == (char *)NULL || port == (int *)NULL)
      {
      log_file_date("%s:null[hname or port]", mname);
      return;
      }

   hname[0] = EOS;
   *port = 0;

   if (!strlen(sloc_hostname) || sloc_port == 0)
      {
      log_file_date("%s:no active server", mname);
      return;
      }

   strcpy(hname, sloc_hostname);
   *port = sloc_port;
   log_file_date("%s:normal exit", mname);
}

int sloc_find_first_active(void)
{
   /* Traverse through the list of socket locate servers and
      find the first one active. The active server host name
      and port number will be loaded into the globals
      'sloc_hostname' and 'sloc_port' upon success. Any entry
      in the list not found to be active will be deleted. Function
      returns 'SL_OK' upon success, a socket locate code
      otherwise. */

   char mname[] = "sloc_find_first_active";
   int nentries;
   int ret;

   log_file_date("%s:enter", mname);

   while(1)
      {
      if ((ret = sl_config_get_nentries(&nentries)) != SL_OK)
         {
         log_file_date("%s:bad rc[%d] from sl_config_get_nentries", mname, ret);
         return(ret);
         }

      log_file_date("%s:number of config entries is %d", mname, nentries);

      /* load host name and port number for this entry */

      if ((ret = sl_config_get_first(sloc_hostname, &sloc_port,
                                   (char *)NULL)) != SL_OK)
         {
         log_file_date("%s:bad rc[%d] from sl_config_get_first", mname, ret);
         return(ret);
         }

      log_file_date("%s:config entry:sloc_hostname=%s,sloc_port=%d",
                    mname, sloc_hostname, sloc_port);

      /* get status of server */

      if ((ret = sloc_ll_status()) == SL_OK)
         {
         log_file_date("%s:exit:server %s responding", mname, sloc_hostname);
         return(ret);
         }

      log_file_date("%s:server %s not responding", mname, sloc_hostname);

      /* if not responding delete entry */

      if ((ret = sl_config_delete(sloc_port)) != SL_OK)
         {
         log_file_date("%s:bad rc[%d] from sl_config_delete", mname, ret);
         return(ret);
         }
      }

   /* we should never get here but just in case */

   sloc_hostname[0] = EOS;
   sloc_port = 0;
   log_file_date("%s:unanticipated wacky exit,rc=%d", mname,
                 SL_NO_SUCH_SOCLOC);
   return(SL_NO_SUCH_SOCLOC);
}

int sloc_is_init(void)
{
   /* Determine whether the 'socloc' API has been initialized.
      The API is considered initialized when there is an active
      host name in 'sloc_hostname' and a positive port number in
      'sloc_port'. Function returns 'SL_OK' if the API has been
      initialized, 'SL_NO_INIT' otherwise. */

   char mname[] = "sloc_is_init";
   int ret;

   log_file_date("%s:enter", mname);

   if (!sloc_port || sloc_hostname == (char *)NULL || !strlen(sloc_hostname))
      ret = SL_NO_INIT;
   else
      ret = SL_OK;

   log_file_date("%s:normal exit[%d]", mname, ret);
   return(ret);
}

/* private functions */

static int sloc_ll_status(void)
{
   /* Get the status from the current 'socloc' server.
      Function returns 'SL_OK' upon success, a 'socloc' 
      code otherwise. */

   char mname[] = "sloc_ll_status";
   int ret;

   log_file_date("%s:enter:host=%s,port=%d", mname, sloc_hostname,
                 sloc_port);
   ret = sloc_sr_code(SL_SEND_STATUS, (char *)NULL);
   log_file_date("%s:normal exit[%d]", mname, ret);
   return(ret);
}

static int sloc_sr_code(int typ, char *parm)
{
   /* Send and recveive a message to the 'socloc' server that returns
      only a return code. Its ok if 'parm' is NULL or empty.
      Function returns a 'socloc' code. */

   char mname[] = "sloc_sr_code", *mess;
   int len, full_len, ret;

   /* if init has not been called, exit error */

   if ((ret = sloc_is_init()) != SL_OK)
      {
      log_file_date("%s:no socloc server details", mname);
      return(ret);
      }

   // connect to defined server

   if ((ret = sloc_client_connect()) != SL_OK)
      {
      log_file_date("%s:bad rc[%d] from sloc_client_connect", mname, ret);
      return(ret);
      }

   // estimate length and alloc send/receive buffer

   len = (parm == (char *)NULL) ? 0 : strlen(parm);
   full_len = len + 5;

   if ((mess = (char *)malloc(full_len)) == (char *)NULL)
      {
#ifdef OS_WIN32
      (void)closesocket(slclientSocket);
#else
      close(slclientSocket);
#endif

      log_file_date("%s:alloc fail[mess]", mname);
      return(SL_MEMORY_FAIL);
      }

   memset(mess, 0, full_len);

   // format send string

   if (parm == (char *)NULL || !len)
      sprintf(mess, "%d", typ);
   else
      sprintf(mess, "%d %s", typ, parm);

   // send message

   if (!ipc_send(slclientSocket, mess))
      {
#ifdef OS_WIN32
      (void)closesocket(slclientSocket);
#else
      close(slclientSocket);
#endif

      log_file_date("%s:bad rc[FALSE] from ipc_send", mname);
      free(mess);
      return(SL_VC_ERROR);
      }

   memset(mess, 0, full_len);

   // receive return code

   if (!ipc_recv(slclientSocket, mess))
      {
#ifdef OS_WIN32
      (void)closesocket(slclientSocket);
#else
      close(slclientSocket);
#endif

      log_file_date("%s:bad rc[FALSE] from ipc_recv", mname);
      free(mess);
      return(SL_VC_ERROR);
      }

   // make sure its a number

   if (!qatoi(mess, &ret))
      {
#ifdef OS_WIN32
      (void)closesocket(slclientSocket);
#else
      close(slclientSocket);
#endif

      log_file_date("%s:bad rc[FALSE] from qatoi[wrd 1]", mname);
      free(mess);
      return(SL_INTERNAL_ERROR);
      }

   free(mess);

#ifdef OS_WIN32
   (void)closesocket(slclientSocket);
#else
   close(slclientSocket);
#endif

   return(ret);
}

static int sloc_sr_char(int typ, char *parm, char *char_out)
{
   /* Send and recveive a message to the 'socloc' server that returns a
      string as well as a return code. 'char_out' must already
      be allocated to sufficient size for the receiving string.
      Its ok if 'parm' is NULL or empty. Function returns a 'socloc' code. */

   char mname[] = "sloc_sr_char", *mess, tmp[50];
   int len, full_len, nwords, pos, ret;

   /* if init has not been called, exit error */

   if ((ret = sloc_is_init()) != SL_OK)
      {
      log_file_date("%s:no socloc server details", mname);
      return(ret);
      }

   char_out[0] = EOS;

   // connect to defined server

   if ((ret = sloc_client_connect()) != SL_OK)
      {
      log_file_date("%s:bad rc[%d] from sloc_client_connect", mname, ret);
      return(ret);
      }

   // estimate length and alloc send/receive buffer

   len = (parm == (char *)NULL) ? 0 : strlen(parm);
   full_len = len + 5;

   if ((mess = (char *)malloc(full_len)) == (char *)NULL)
      {
      log_file_date("%s:alloc fail[mess]", mname);
      return(SL_MEMORY_FAIL);
      }

   memset(mess, 0, full_len);

   // format send string

   if (parm == (char *)NULL || !len)
      sprintf(mess, "%d", typ);
   else
      sprintf(mess, "%d %s", typ, parm);

   // send message

   if (!ipc_send(slclientSocket, mess))
      {
#ifdef OS_WIN32
      (void)closesocket(slclientSocket);
#else
      close(slclientSocket);
#endif

      log_file_date("%s:bad rc[FALSE] from ipc_send", mname);
      free(mess);
      return(SL_VC_ERROR);
      }

   free(mess);

   // re-allocate 'mess' for receive buffer (max size)

   if ((mess = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
#ifdef OS_WIN32
      (void)closesocket(slclientSocket);
#else
      close(slclientSocket);
#endif

      log_file_date("%s:alloc fail[mess]", mname);
      return(SL_MEMORY_FAIL);
      }

   memset(mess, 0, SL_MAXCOMMAND);

   // receive reply

   if (!ipc_recv(slclientSocket, mess))
      {
#ifdef OS_WIN32
      (void)closesocket(slclientSocket);
#else
      close(slclientSocket);
#endif

      log_file_date("%s:bad rc[FALSE] from ipc_recv", mname);
      free(mess);
      return(SL_VC_ERROR);
      }

   // s/b two words in reply

   nwords = command_words(mess);

   // s/b reply code in word one

   if (!command_word(mess, tmp, 1))
      {
#ifdef OS_WIN32
      (void)closesocket(slclientSocket);
#else
      close(slclientSocket);
#endif

      log_file_date("%s:bad rc[FALSE] from command_word[1]", mname);
      free(mess);
      return(SL_INTERNAL_ERROR);
      }

   // make sure its a number

   if (!qatoi(tmp, &ret))
      {
#ifdef OS_WIN32
      (void)closesocket(slclientSocket);
#else
      close(slclientSocket);
#endif

      log_file_date("%s:bad rc[FALSE] from qatoi[wrd 1]", mname);
      free(mess);
      return(SL_INTERNAL_ERROR);
      }

   // if reply is 'ok', load 'char_out'

   if (ret == SL_OK)
      {
      if (nwords < 2)
         {
#ifdef OS_WIN32
         (void)closesocket(slclientSocket);
#else
         close(slclientSocket);
#endif

         log_file_date("%s:expecting at least two words in reply", mname);
         free(mess);
         return(SL_INTERNAL_ERROR);
         }

      // 'char_value' is from the end of the first word

      if ((pos = command_indxword(mess, 2)) == -1)
         {
#ifdef OS_WIN32
         (void)closesocket(slclientSocket);
#else
         close(slclientSocket);
#endif

         log_file_date("%s:bad rc[-1] from command_indxword[2]", mname);
         free(mess);
         return(SL_INTERNAL_ERROR);
         }

      /* if second command word starts with quote backup one */

      if (mess[pos - 1] == '\'' || mess[pos - 1] == '"')
         pos -= 1;

      len = strlen(mess) - pos;
      strncpy(char_out, &mess[pos], len);
      char_out[len] = EOS;
      }

   free(mess);

#ifdef OS_WIN32
   (void)closesocket(slclientSocket);
#else
   close(slclientSocket);
#endif

   return(ret);
}

static int sloc_client_connect(void)
{
   /* Connect to the 'socloc' server. An attempt to open the TCP
      socket is made. The host name 'sloc_hostname' and the
      TCP port 'sloc_port' are assumed to be already loaded
      Function returns 'SL_OK' if a successful connection was 
      made, a 'socloc' code otherwise. Private function. */

   char mname[] = "sloc_client_connect";
   int ret;

   // make sure host name and port are loaded

   if ((ret = sloc_is_init()) != SL_OK)
      {
      log_file_date("%s:bad rc[%d] from sloc_is_init", mname, ret);
      return(ret);
      }

   // resolve server host name

   lpHostEnt = gethostbyname(sloc_hostname);

   if (!lpHostEnt)
      {
      log_file_date("%s:bad rc[0] from gethostbyname", mname);
      return(SL_VC_ERROR);
      }

   // create the socket

#ifdef OS_WIN32
   slclientSocket = socket(PF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
#endif

#ifdef OS_UNIX
   slclientSocket = socket(AF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
#endif

   if (slclientSocket == INVALID_SOCKET)
      {
      log_file_date("%s:bad rc[INVALID_SOCKET] from socket", mname);
      return(SL_VC_ERROR);
      }

   // load client address data

   memset(&slsockClientAddr, 0, sizeof(slsockClientAddr));
   slsockClientAddr.sin_family = AF_INET;
   slsockClientAddr.sin_port = htons(sloc_port);

#ifdef OS_WIN32
   slsockClientAddr.sin_addr = *((LPIN_ADDR)*lpHostEnt->h_addr_list);
#endif

#ifdef OS_UNIX
   slsockClientAddr.sin_addr.s_addr = ((struct in_addr *)(lpHostEnt->h_addr))->s_addr;
#endif

   // connect to server

#ifdef OS_WIN32
   if (connect(slclientSocket, (LPSOCKADDR)&slsockClientAddr,
               sizeof(slsockClientAddr)))
#endif

#ifdef OS_UNIX
   if (connect(slclientSocket, (SA *)&slsockClientAddr,
               sizeof(slsockClientAddr)) == SOCKET_ERROR)
#endif
      {
      log_file_date("%s:bad rc[SOCKET_ERROR] from connect", mname);
      return(SL_VC_ERROR);
      }

   return(SL_OK);
}
