/* dumsockc - A command line for the dumb server 'dumsocks'.
   Rick Smereka, Copyright (C) 2000-2006.

   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

   Original Windows 32bit version Feb/2000, Rick Smereka
   
   Re-compiled after re-structuring the definition of the common
   send and repy codes. Apr/2000, Rick Smereka
   
   Re-compiled after adding the new 'clib' module 'ip.c'.
   Jul/2000, Rick Smereka

   Modified for use with both sockets and QNX message passing IPC.
   Removed manual socket interface. Re-coded to use 'ipclient'
   API. Ported to QNX V4.23a. Oct/2001, Rick Smereka

   Ported to Debian Linux. Jan/2003, Rick Smereka

   Modified for the updated 'socloc' API ('sloc.c').
   May/2003, Rick Smereka

   Added function 'term_app' to perform shutdown tasks.
   Added support for the commands 'DMCOM_TRANS_NUM',
   'DMCOM_CONNECT_NUM', 'DMCOM_SOCLOC_TRANS_NUM' and
   'DMCOM_SOCLOC_CONNECT_NUM'. Jun/2003, Rick Smereka

   Added includes 'appinit.h' and 'sliocode.h'.
   Added define 'APNAME'. Changed all logging calls to use 'logman'.
   Used 'appinit' to register and remove application name.
   May/2004, Rick Smereka

   Changed call in 'main' from 'gets' to 'get_rec' and changed
   command line buffer size to 'BUFSIZE' (defined in 'fio.h').
   Dec/2004, Rick Smereka

   Re-compile after changing the 'socloc' API.
   Jan/2005, Rick Smereka

   Re-compile after modifications to low level TCP socket communication
   module (ipcomm.c). Feb/2006, Rick Smereka */

#include "stdhead.h"
#include "flsocket.h"
#include "appinit.h"
#include "ipclient.h"                 /* IPC client routines */
#include "dumsocks.h"                 /* dumb socket server defines */
#ifdef IPC_TCP
#include "socloc.h"                   /* socket locate defines */
#include "sloc.h"                     /* 'socloc' client API */
#include "sconnect.h"                 /* parse connect string */
#include "sliocode.h"                 // 'socloc' error translation
#endif

#define VERSION "1.08.01-2006.02.23"  /* version ID */
// #define DEBUG 1

/* default private log file definition */

#define DUM_LOG_FILE "dumsockc.log"
#define APNAME "dumsockc"

/* global data */

#ifdef OS_WIN32
WSADATA wsaData;                      /* struct used by 'WSAStartup()' */
#endif

#ifdef IPC_TCP
int serverport = 0;                  /* TCP port that server is using */
int socloc_port;                     /* TCP port that 'socloc' is using */
char serverhost[256];                /* 'dumsocks' server host name */
#else
int connected_flag = FALSE;
#endif
char d_log_file[256];                /* name of client log file */

int main(void);
int d_initialize(void);
void d_term(char *);
void d_status(void);
void d_client_log_on(char *);
void d_client_log_off(void);
void d_client_log_status(void);
void d_server_log_on(char *);
void d_server_log_off(void);
void d_server_log_status(void);
void d_connect(char *);
void d_version(void);
void d_connect_num(void);
void d_trans_num(void);
void d_socloc_trans_num(void);
void d_socloc_connect_num(void);
void d_code_string(int, char *);
int get_token(char *);
void d_header(char *);
void d_out(char *,...);
int d_sr_code(int, char *);
int d_sr_int(int, char *, int *);
int d_sr_long(int, char *, long *);
int d_sr_char(int, char *, char *);
int is_connected(void);
void term_app(void);
#ifdef IPC_TCP
void d_socloc_log_on(char *);
void d_socloc_log_off(void);
void d_socloc_log_status(void);
void d_socloc_get_list(void);
void d_socloc_config_get_list(void);
void d_socloc_get(void);
void d_socloc_version(void);
int d_sr(int, char *, char *);
void d_failover(int);
#else
int d_sr(int, char *, char *, int);
void d_tcp_only(void);
#endif

int main(void)
{
   char *comm;
   int done = FALSE;

   /* allocate command line buffer */

   if ((comm = malloc(BUFSIZE + 1)) == NULL)
      {
      printf("dumsockc:insufficient memory to run program\n");
      return(0);
      }

   /* build logo string based on platform */

#ifndef OS_UNIX
   /* non-Unix */

   sprintf(comm, "dumsockc for %s Version %s", PLATFORM_STRING,
           VERSION);
#else
   /* Unix */

   sprintf(comm, "dumsockc for %s Version %s", SUB_PLATFORM_STRING,
           VERSION);
#endif

   printf("%s\n", comm);
   printf("By Rick Smereka, Copyright (c) 2000-2006\n");

   // register application name with 'appinit'

   if (!appinit_register_name(APNAME))
      {
      free(comm);
      printf("%s:fatal error registering app name with appinit\n", APNAME);
      return(0);
      }

#ifdef DEBUG
   if (logman_start(d_log_file, APNAME))
      {
      printf("error starting log file\n");
      free(comm);
      return(0);
      }
#endif

   /* initialize */

   if (!d_initialize())
      {
      free(comm);
      term_app();
      return(0);
      }

   printf("dumsockc comes with ABSOLUTELY NO WARRANTY\n");
   printf("This is free software, and you are welcome to redistribute it\n");
   printf("under certain conditions; see 'gpl.txt' for information.\n");

   /* interpret loop */

   while(!done)
      {
#ifdef IPC_TCP
      printf("dumsockc/%s[%d]> ", serverhost, serverport);
#else
      printf("dumsockc> ");
#endif
      get_rec(stdin, comm);

      if (!do_comm(comm))
         done = TRUE;
      }

   free(comm);
   term_app();
   printf("exit dumsockc\n");
   return(0);
}

int d_initialize(void)
{
   /* Start socket communication, locate a 'socloc' server
      and connect to a 'dumsocks' server. Function returns
      'TRUE' if the initialization was successful, 'FALSE'
      otherwise. */

   char mname[] = "d_initialize";
   char *thelist, mes[128];
   int ret;

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

   /* startup WinSock (if Windoze) */

#ifdef OS_WIN32
   if (WSAStartup(WINSOCK_VERSION, &wsaData))
      {
      d_out("%s:unable to start WinSock. "
            "Program abort", mname);
      return(FALSE);
      }
#endif

   /* attempt to connect to a 'socloc' server */

#ifdef IPC_TCP
   if ((ret = sloc_initialize()) != SL_OK)
      {
      sl_code_string(ret, mes);
      d_out("%s:bad rc[%s] from 'sloc_initialize'. "
            "Program abort", mname, mes);
      return(FALSE);
      }

   /* locate a 'dumsocks' server */

   if ((ret = sloc_find(DM_SERVICE_NAME, serverhost, &serverport,
        (char *)NULL)) != SL_OK)
      {
      sl_code_string(ret, mes);
      d_out("%s:bad rc[%s] from 'sloc_find'. Program abort", mname, mes);
      return(FALSE);
      }

   d_out("%s:found 'dumsocks' server on host '%s' port %d", mname,
         serverhost, serverport);

   /* get config list from 'socloc' server and update
      the client list with it */
      
   if ((thelist = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
      {
      d_out("%s:alloc fail[thelist]", mname);
      return(FALSE);
      }

   if ((ret = sloc_config_get_list(thelist)) != SL_OK)
      {
      free(thelist);
      sl_code_string(ret, mes);
      d_out("%s:bad rc[%s] from 'sloc_config_get_list'", mname, mes);
      return(FALSE);
      }
      
   if ((ret = sl_config_put_list(thelist)) != SL_OK)
      {
      sl_code_string(ret, mes);
      d_out("%s:bad rc[%s] from 'sl_config_put_list'",
                    mname, mes);
      free(thelist);
      return(FALSE);
      }

   /* load the port that the current 'socloc' server is
      using */
      
   sloc_get_active_socloc(thelist, &socloc_port);
   free(thelist);   

   // connect to the 'dumsocks' server

   if (!ipc_set_active(serverhost, serverport))
      {
      d_out("%s:bad rc[FALSE] from 'ipc_set_active'", mname);
      return(FALSE);
      }
#else
   if (!ipc_set_active(DM_SERVICE_NAME))
      {
      d_out("%s:bad rc[FALSE] from 'ipc_set_active'", mname);
      return(FALSE);
      }

   connected_flag = TRUE;
#endif

   strcpy(d_log_file, DUM_LOG_FILE);
   return(TRUE);
}

int do_comm(char *comm)
{
   /* Dispatch a 'dumsockc' command. Function always returns
      'TRUE' except when the 'off' command is detected. */

   char mname[] = "do_comm";
   char *keywrd;                 /* command keyword */
   int token;                    /* keyword token */
   int nwords;                   /* number of words */
   int len;                      /* length of command */

   logman("%s:enter", mname);
   len = strlen(comm);

   if (!len)
      {
      d_out("empty command");
      return(TRUE);
      }

   /* allocate to max as this string is re-used for the return
      status string */

   if ((keywrd = malloc(DM_MAXCOMMAND + 1)) == NULL)
      {
      d_out("%s:alloc fail[keywrd]", mname);
      return(TRUE);
      }

   if (!word(comm, keywrd, 1))
      {
      d_out("%s:error while retreving keyword, line skipped", mname);
      free(keywrd);
      return(TRUE);
      }

   nwords = words(comm);

   if ((token = get_token(keywrd)) == DMCOM_NOT_FOUND)
      {
      d_out("%s:unknown keyword[%s]", mname, keywrd);
      free(keywrd);
      return(TRUE);
      }

   /* process command */

   switch(token)
      {
      case DMCOM_TERM:
         d_term(comm);
         break;

      case DMCOM_STATUS:
         d_status();
         break;

      case DMCOM_CLIENT_LOG_ON:
         d_client_log_on(comm);
         break;
      
      case DMCOM_CLIENT_LOG_OFF:
         d_client_log_off();
         break;

      case DMCOM_CLIENT_LOG_STATUS:
         d_client_log_status();
         break;
         
      case DMCOM_SERVER_LOG_ON:
         d_server_log_on(comm);
         break;
      
      case DMCOM_SERVER_LOG_OFF:
         d_server_log_off();
         break;

      case DMCOM_SERVER_LOG_STATUS:
         d_server_log_status();
         break;
                  
      case DMCOM_CONNECT:
         d_connect(comm);
         break;
         
      case DMCOM_SOCLOC_GET_LIST:
#ifdef IPC_TCP
         d_socloc_get_list();
#else
         d_tcp_only();
#endif
         break;

      case DMCOM_SOCLOC_CONFIG_GET_LIST:
#ifdef IPC_TCP
         d_socloc_config_get_list();
#else
         d_tcp_only();
#endif
         break;
        
      case DMCOM_SOCLOC_GET:
#ifdef IPC_TCP
         d_socloc_get();
#else
         d_tcp_only();
#endif
         break;

      case DMCOM_SOCLOC_LOG_OFF:
#ifdef IPC_TCP
         d_socloc_log_off();
#else
         d_tcp_only();
#endif
         break;
         
      case DMCOM_SOCLOC_LOG_ON:
#ifdef IPC_TCP
         d_socloc_log_on(comm);
#else
         d_tcp_only();
#endif
         break;
         
      case DMCOM_SOCLOC_LOG_STATUS:
#ifdef IPC_TCP
         d_socloc_log_status();
#else
         d_tcp_only();
#endif
         break;

      case DMCOM_SOCLOC_VERSION:
#ifdef IPC_TCP
         d_socloc_version();
#else
         d_tcp_only();
#endif
         break;
         
      case DMCOM_VERSION:
         d_version();
         break;
                                     
      case DMCOM_TRANS_NUM:
         d_trans_num();
         break;

      case DMCOM_CONNECT_NUM:
         d_connect_num();
         break;

      case DMCOM_SOCLOC_TRANS_NUM:
#ifdef IPC_TCP
         d_socloc_trans_num();
#else
         d_tcp_only();
#endif
         break;

      case DMCOM_SOCLOC_CONNECT_NUM:
#ifdef IPC_TCP
         d_socloc_connect_num();
#else
         d_tcp_only();
#endif
         break;

      case DMCOM_OFF:
         free(keywrd);
         return(FALSE);
         break;

      default:
         d_out("%s:command not found", mname);
         free(keywrd);
         return(TRUE);
      };

   free(keywrd);         
   return(TRUE);
}

void d_term(char *comm)
{
   /* Terminate a 'dumsocks' server. Syntax:
   
         term passwd
         
      Where 'passwd' is the termination password. */

   char mname[] = "d_term";
   char passwd[128], parm[128], reply[25];
   int ret;

   d_header(mname);

   if (command_words(comm) < 2)
      {
      d_out("%s:no password,access denied", mname);
      return;
      }

   if (!command_word(comm, passwd, 2))
      {
      d_out("%s:error getting password", mname);
      return;
      }

   if (words(passwd) > 1)
      sprintf(parm, "'%s'", passwd);
   else
      strcpy(parm, passwd);
      
   ret = d_sr_code(DM_SEND_TERM, parm);

   /* if shutdown was successful, failover to
      next 'dumsocks' server */

   if (ret == DM_OK)
      {
      d_out("%s:'dumsocks' server is down", mname);
#ifdef IPC_TCP
      d_failover(FALSE);
#endif
      }

   if (logman_is_active())      
      logman("%s:normal exit rc[%d]", mname, ret);
   else
      {
      d_code_string(ret, parm);
      printf("%s\n", parm);
      }
}

void d_status(void)
{
   /* Get 'dumsocks' server status. Syntax:
   
         status */        

   char mname[] = "d_status";
   char mes[128];
   int ret;

   d_header(mname);
   ret = d_sr_code(DM_SEND_STATUS, (char *)NULL);

   if (logman_is_active())
      logman("%s:normal exit rc[%d]", mname, ret);
   else
      {
      d_code_string(ret, mes);
      printf("%s\n", mes);
      }
}

void d_client_log_on(char *comm)
{
   /* Turn client logging on with optional log file name.
      Syntax:
      
         client.log.on[ log.fname]
         
      Where 'log.fname' is the optional log file name. If no
      log file name is given 'DUM_LOG_FILE' will be used.
      It is an error if client logging is already on. */
      
   char mname[] = "d_client_log_on"; 

   d_header(mname);

   if (logman_is_active())
      {
      d_out("%s:logging already on", mname);
      return;
      }

   if (command_words(comm) > 1)
      {
      if (!command_word(comm, d_log_file, 2))
         {
         d_out("%s:error getting log file name", mname);
         return;
         }
       }
    else
       strcpy(d_log_file, DUM_LOG_FILE);
         
   if (logman_start(d_log_file, APNAME))
      {
      d_out("%s:error starting logging", mname);
      return;
      }
      
   if (logman_is_active())
      logman("%s:normal exit[0]", mname);
   else
      printf("ok\n");
}

void d_client_log_off(void)
{
   /* Turn client logging off. Syntax:
   
         client.log.off
         
      It is an error if client logging is already off. */
      
   char mname[] = "d_client_log_off";   

   d_header(mname);

   if (!logman_is_active())
      {
      d_out("%s:log already off", mname);
      return;
      }

   if (logman_is_active())
      logman("%s:normal exit[0]", mname);
   else
      printf("ok\n");
      
   logman_end();
}

void d_client_log_status(void)
{
   /* Display status of client logging. Syntax:
   
         client.log.status */         
      
   char mname[] = "d_client_log_status";
   
   d_header(mname);
   
   if (logman_is_active())
      logman("%s:client log is on", mname);
   else   
      printf("%s:client log is off\nok\n", mname);      
}
   
void d_server_log_on(char *comm)
{  
   /* Tell server to turn its logging on. Syntax:
   
         server.log.on[ log_fname]

      If no 'log_fname' is given, the default sever log file
      name will be used. It is an error if server logging is
      already on. */

   char mname[] = "d_server_log_on";
   char tmp[256], slog[256];
   int ret;

   d_header(mname);
   
   if (command_words(comm) > 1)
      {
      if (!command_word(comm, tmp, 2))
         {
         d_out("%s:error getting server log file name", mname);
         return;
         }
         
      if (words(tmp) > 1)
         sprintf(slog, "'%s'", tmp);
      else
         strcpy(slog, tmp);
         
      ret = d_sr_code(DM_SEND_LOG_ON, slog);
      }
   else
      ret = d_sr_code(DM_SEND_LOG_ON, (char *)NULL);

   if (logman_is_active())
      logman("%s:normal exit rc[%d]", mname, ret);
   else
      {
      d_code_string(ret, tmp);
      printf("%s\n", tmp);
      }
}

void d_server_log_off(void)
{
   /* Tell server to turn its logging off. Syntax:
   
         server.log.off
         
      It is an error if the server log is already off. */

   char mname[] = "d_server_log_off";
   char mes[128];
   int ret;

   d_header(mname);
   ret = d_sr_code(DM_SEND_LOG_OFF, (char *)NULL);

   if (logman_is_active())
      logman("%s:normal exit rc[%d]", mname, ret);
   else
      {
      d_code_string(ret, mes);
      printf("%s\n", mes);
      }
}

void d_server_log_status(void)
{
   /* Get status of server logging. Syntax:
   
         server.log.status */         
   
   char mname[] = "d_server_log_status";
   char mes[128];
   int ret;
   
   d_header(mname);
   ret = d_sr_char(DM_SEND_LOG_STATUS, (char *)NULL, mes);

    if (ret == DM_OK)
       d_out("%s:log status is '%s'", mname, mes);
 
   d_code_string(ret, mes);
   
   if (logman_is_active())
      logman("%s:normal exit,rc[%d]", mname, ret);
   else
      {
      d_code_string(ret, mes);
      printf("%s\n", mes);
      }
}

#ifdef IPC_TCP
void d_socloc_log_on(char *comm)
{  
   /* Tell 'socloc' server to turn its logging on. Syntax:
   
         socloc.log.on[ log_fname]

      If no 'log_fname' is given, the default sever log file
      name will be used. It is an error if server logging is
      already on. */

   char mname[] = "d_socloc_log_on";
   char tmp[256], slog[256];
   int ret;

   d_header(mname);
   
   if (command_words(comm) > 1)
      {
      if (!command_word(comm, tmp, 2))
         {
         d_out("%s:error getting server log file name", mname);
         return;
         }
         
      if (words(tmp) > 1)
         sprintf(slog, "'%s'", tmp);
      else
         strcpy(slog, tmp);
         
      ret = sloc_log_on(slog);
      }
   else
      ret = sloc_log_on((char *)NULL);
         
   if (logman_is_active())
      logman("%s:normal exit rc[%d]", mname, ret);
   else
      {
      sl_code_string(ret, tmp);
      printf("%s\n", tmp);
      }
}
#endif

#ifdef IPC_TCP
void d_socloc_log_off(void)
{
   /* Tell 'socloc' server to turn its logging off. Syntax:
   
         socloc.log.off
         
      It is an error if the server log is already off. */

   char mname[] = "d_socloc_log_off";
   char mes[128];
   int ret;

   d_header(mname);

   ret = sloc_log_off();
   
   if (logman_is_active())
      logman("%s:normal exit rc[%d]", mname, ret);
   else
      {
      sl_code_string(ret, mes);
      printf("%s\n", mes);
      }
}
#endif

#ifdef IPC_TCP
void d_socloc_log_status(void)
{
   /* Get status of 'socloc' server logging. Syntax:
   
         socloc.log.status */
   
   char mname[] = "d_socloc_log_status";
   char mes[128];
   int ret, lflag;
   
   d_header(mname);

   ret = sloc_log_status(&lflag, mes);
    
   if (ret != SL_OK)
      {
      if (logman_is_active())
         logman("%s:normal exit,rc[%d]", mname, ret);
      else
         {
         sl_code_string(ret, mes);
         printf("%s\n", mes);
         }
         
      return;
      }
      
   if (lflag)
      d_out("%s:socloc server log is active,log is %s", mname, mes);
   else
      d_out("%s:socloc server log is inactive,log is %s", mname, mes);
      
   if (logman_is_active())
      logman("%s:normal exit,rc[0]", mname);
}
#endif
   
void d_connect(char *comm)
{
   /* Connect to any 'dumsocks' server. TCP syntax is:
   
         connect [host host_name][ port port_num][ ip ip_ad]
         
      QNX message passing syntax is:

         connect

      All parameters except 'connect' keyword are optional (TCP oly). If no
      server details are supplied, a re-connect will be attempted
      to the first 'dumsocks' server. */
      
   char mname[] = "d_connect";
   char mes[128], *hname, *ip_ad;   
   int pos, ret, port, nwords;

   d_header(mname);
#ifdef IPC_TCP
   nwords = command_words(comm);

   if (nwords < 2)
      {
      /* if only 'connect' keyword, re-lookup first 'dumsocks' server */

      if ((ret = sloc_find(DM_SERVICE_NAME, serverhost, &serverport,
           (char *)NULL)) != SL_OK)
         {
         d_out("%s:bad rc[%d] from 'sloc_find'", mname, ret);
         return;
         }

      d_status();         
      return;
      }

   /* alloc details */

   if ((hname = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
      {
      d_out("%s:alloc fail[hname]", mname);
      return;
      } 

   if ((ip_ad = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
      {
      d_out("%s:alloc fail[ip_ad]", mname);
      free(hname);      
      return;
      }   

   /* get the position of the second command word */
   
   pos = command_indxword(comm, 2);
   
   /* if the first char in the second word is a quote,
      'command_indxword' will skip over this, we need to
      backup one position */
      
   if (comm[pos - 1] == '\"' || comm[pos - 1] == '\'')
      pos--;

   /* parse the connect string */
   
   if ((ret = sconnect_parse(&comm[pos], FALSE, (char *)NULL, hname,
                             &port, ip_ad)) != SP_OK)
      {
      sp_code_string(ret, mes);
      d_out("%s:bad rc[%d,%s] from 'sconnect_parse'", mname, ret, mes);         
      free(hname);
      free(ip_ad);      
      return;
      }
      
   /* locate the 'dumsocks' server based on supplied details */
   
   if ((ret = sloc_ll_find(DM_SERVICE_NAME, hname, &port, ip_ad)) != SL_OK)
      {
      sl_code_string(ret, mes);
      d_out("%s:bad rc[%d,%s] from 'sloc_ll_find'", mname, ret, mes);         
      free(hname);
      free(ip_ad);
      return;
      }

   strcpy(serverhost, hname);
   serverport = port;

   if (!ipc_set_active(serverhost, serverport))
      {
      d_out("%s:bad rc[FALSE] from 'ipc_set_active'", mname);
      free(hname);
      free(ip_ad);
      return;
      }
#else
   if (!ipc_set_active(DM_SERVICE_NAME))
      {
      d_out("%s:bad rc[FALSE] from 'ipc_set_active'", mname);
      return;
      }

   connected_flag = TRUE;
#endif

   d_status();

#ifdef IPC_TCP
   free(hname);
   free(ip_ad);
#endif

   if (logman_is_active())   
      logman("%s:normal exit rc[%d]", mname, ret);
}

#ifdef IPC_TCP
void d_socloc_get_list(void)
{
   /* Get the list of available 'dumsocks' servers from 'socloc'.
      Syntax:
      
         socloc.get.list */         
      
   char mname[] = "d_socloc_get_list";
   char *list_out, *entry, *hname, *port_char, *ip_ad;
   int ret, nentries, i;

   d_header(mname);
   
   if ((list_out = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
      {
      d_out("%s:alloc fail[list_out]", mname);
      return;
      } 

   ret = sloc_find_list(DM_SERVICE_NAME, list_out);
                           
   if (ret == DM_OK)
      {
      d_out("current 'dumsocks' servers");
      nentries = ll_words(list_out, SL_LIST_DELIM);
      
      if ((entry = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
         {
         d_out("%s:alloc fail[entry]", mname);
         free(list_out);
         return;
         }
         
      if ((hname = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
         {
         d_out("%s:alloc fail[hname]", mname);
         free(list_out);
         free(entry);
         return;
         }

      if ((port_char = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
         {
         d_out("%s:alloc fail[port_char]", mname);
         free(list_out);
         free(entry);
         free(hname);
         return;
         }

      if ((ip_ad = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
         {
         d_out("%s:alloc fail[ip_ad]", mname);
         free(list_out);
         free(entry);
         free(hname);
         free(port_char);
         return;
         }

      for(i = 1; i <= nentries; i++)
         {
         if (!ll_word(list_out, entry, i, SL_LIST_DELIM))
            {
            d_out("%s:error geting list entry[%d]", mname, i);
            free(list_out);
            free(entry);
            free(hname);
            free(port_char);
            free(ip_ad);
            return;
            }

         /* service name is first command word, we bypass this */
                     
         if (!command_word(entry, hname, 2))
            {
            d_out("%s:error getting host from entry[%d]", mname, i);
            free(list_out);
            free(entry);
            free(hname);
            free(port_char);
            free(ip_ad);
            return;
            }
            
         if (!command_word(entry, port_char, 3))
            {
            d_out("%s:error getting port from entry[%d]", mname, i);
            free(list_out);
            free(entry);
            free(hname);
            free(port_char);
            free(ip_ad);
            return;
            }
            
         if (command_words(entry) > 3)
            {
            if (!command_word(entry, ip_ad, 4))
               {
               d_out("%s:error getting ip from entry[%d]", mname, i);
               free(list_out);
               free(entry);
               free(hname);
               free(port_char);
               free(ip_ad);
               return;
               }
            }
         else
            ip_ad[0] = EOS;                        
         
         if (strlen(ip_ad))
            d_out("dumsocks[%d]:host=%s,port=%s,ip=%s", i, hname,
                  port_char, ip_ad);
         else
            d_out("dumsocks[%d]:host=%s,port=%s", i, hname, port_char);
         }
         
      free(entry);
      free(hname);
      free(port_char);
      free(ip_ad);
      }   
      
   if (logman_is_active())
      logman("%s:normal exit rc[%d]", mname, ret);
   else
      {
      sl_code_string(ret, list_out);
      printf("%s\n", list_out);
      }

   free(list_out);
}
#endif

#ifdef IPC_TCP 
void d_socloc_config_get_list(void)
{
   /* Get the list of available 'socloc' servers.
      Syntax:
      
         socloc.config.get.list */         
      
   char mname[] = "d_socloc_config_get_list";
   char *list_out, *entry, *hname, *port_char, *ip_ad;
   int ret, nentries, i;

   d_header(mname);
   
   if ((list_out = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
      {
      d_out("%s:alloc fail[list_out]", mname);
      return;
      } 

   ret = sloc_config_get_list(list_out);
                           
   if (ret == DM_OK)
      {
      d_out("current 'socloc' servers");
      nentries = ll_words(list_out, SL_LIST_DELIM);
      
      if ((entry = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
         {
         d_out("%s:alloc fail[entry]", mname);
         free(list_out);
         return;
         }
         
      if ((hname = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
         {
         d_out("%s:alloc fail[hname]", mname);
         free(list_out);
         free(entry);
         return;
         }

      if ((port_char = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
         {
         d_out("%s:alloc fail[port_char]", mname);
         free(list_out);
         free(entry);
         free(hname);
         return;
         }

      if ((ip_ad = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
         {
         d_out("%s:alloc fail[ip_ad]", mname);
         free(list_out);
         free(entry);
         free(hname);
         free(port_char);
         return;
         }

      for(i = 1; i <= nentries; i++)
         {
         if (!ll_word(list_out, entry, i, SL_LIST_DELIM))
            {
            d_out("%s:error geting list entry[%d]", mname, i);
            free(list_out);
            free(entry);
            free(hname);
            free(port_char);
            free(ip_ad);
            return;
            }
            
         if (!command_word(entry, hname, 1))
            {
            d_out("%s:error getting host from entry[%d]", mname, i);
            free(list_out);
            free(entry);
            free(hname);
            free(port_char);
            free(ip_ad);
            return;
            }
            
         if (!command_word(entry, port_char, 2))
            {
            d_out("%s:error getting port from entry[%d]", mname, i);
            free(list_out);
            free(entry);
            free(hname);
            free(port_char);
            free(ip_ad);
            return;
            }
            
         if (command_words(entry) > 2)
            {
            if (!command_word(entry, ip_ad, 3))
               {
               d_out("%s:error getting ip from entry[%d]", mname, i);
               free(list_out);
               free(entry);
               free(hname);
               free(port_char);
               free(ip_ad);
               return;
               }
            }
         else
            ip_ad[0] = EOS;
                        
         if (strlen(ip_ad))
            d_out("socloc[%d]:host=%s,port=%s,ip=%s", i, hname, 
                  port_char, ip_ad);
         else
            d_out("socloc[%d]:host=%s,port=%s", i, hname, port_char);
         }
         
      free(entry);
      free(hname);
      free(port_char);
      free(ip_ad);
      }   
      
   if (logman_is_active())
      logman("%s:normal exit rc[%d]", mname, ret);
   else
      {
      sl_code_string(ret, list_out);
      printf("%s\n", list_out);
      }

   free(list_out);
}
#endif

#ifdef IPC_TCP
void d_socloc_get(void)
{
   /* Get and display the current 'socloc' server. Syntax:
   
         socloc.get */         
      
   char mname[] = "d_socloc_get";
   char hname[256];
   int port;
   
   d_header(mname);
   hname[0] = EOS;
   port = 0;
   sloc_get_active_socloc(hname, &port);
   
   if (strlen(hname))
      d_out("connected to 'socloc' server '%s' on port %d", hname, port);
   else
      d_out("no current 'socloc' server");
      
   logman("%s:normal exit", mname);
}
#endif   

#ifdef IPC_TCP
void d_socloc_version(void)
{
   /* Get the version ID of the current 'socloc' server.
      Syntax:
      
         socloc.version */
         
   char mname[] = "d_socloc_version";
   char versionid[128];
   int ret;
   
   d_header(mname);
   ret = sloc_version(versionid);
   
   if (ret == SL_OK)
      d_out("'socloc' version ID is '%s'", versionid);
   else
      {
      sl_code_string(ret, versionid);
      d_out("%s", versionid);
      }
      
   logman("%s:normal exit,rc[%d]", mname, ret);
}
#endif

void d_version(void)
{
   /* Get the 'dumsocks' server version ID. Syntax:
   
         version */
         
   char mname[] = "d_version";
   char versionid[128];
   int ret;
   
   d_header(mname);
   ret = d_sr_char(DM_SEND_VERSION, (char *)NULL, versionid);
   
   if (ret == DM_OK)
      d_out("'dumsocks' version ID is '%s'", versionid);
   else
      {
      d_code_string(ret, versionid);
      d_out("%s", versionid);
      }
      
   logman("%s:normal exit,rc[%d]", mname, ret);
}

void d_trans_num(void)
{
   /* Get the 'dumsocks' server transaction count. Syntax:
   
         trans.num */
         
   char mname[] = "d_trans_num", mes[150];
   long trans_num;
   int ret;
   
   d_header(mname);
   ret = d_sr_long(DM_SEND_TRANS_NUM, (char *)NULL, &trans_num);
   
   if (ret == DM_OK)
      d_out("dumsocks transaction count is %ld", trans_num);
   else
      {
      d_code_string(ret, mes);
      d_out("%s", mes);
      }
      
   logman("%s:normal exit,rc[%d]", mname, ret);
}

void d_connect_num(void)
{
   /* Get the 'dumsocks' server connection count. Syntax:
   
         connect.num */
         
   char mname[] = "d_connect_num", mes[150];
   int ret, connect_num;
   
   d_header(mname);
   ret = d_sr_int(DM_SEND_CONNECT_NUM, (char *)NULL, &connect_num);
   
   if (ret == DM_OK)
      d_out("dumsocks connection count is %d", connect_num);
   else
      {
      d_code_string(ret, mes);
      d_out("%s", mes);
      }
      
   logman("%s:normal exit,rc[%d]", mname, ret);
}

#ifdef IPC_TCP
void d_socloc_trans_num(void)
{
   /* Get the current transaction count from the 'socloc' server. Syntax:
   
         socloc.trans.num */
   
   char mname[] = "d_socloc_trans_num", mes[128];
   long trans_num;
   int ret;
   
   d_header(mname);
   ret = sloc_trans_num(&trans_num);
    
   if (ret != SL_OK)
      {
      if (logman_is_active())
         logman("%s:normal exit,rc[%d]", mname, ret);
      else
         {
         sl_code_string(ret, mes);
         d_out("%s", mes);
         }
         
      return;
      }
      
   d_out("%s:current socloc transaction count is %ld", mname, trans_num);
      
   if (logman_is_active())
      logman("%s:normal exit,rc[0]", mname);
}

void d_socloc_connect_num(void)
{
   /* Get the current connection count from the 'socloc' server. Syntax:
   
         socloc.connect.num */
   
   char mname[] = "d_socloc_connect_num", mes[128];
   int ret, connect_num;
   
   d_header(mname);
   ret = sloc_connect_num(&connect_num);
    
   if (ret != SL_OK)
      {
      if (logman_is_active())
         logman("%s:normal exit,rc[%d]", mname, ret);
      else
         {
         sl_code_string(ret, mes);
         d_out("%s", mes);
         }
         
      return;
      }
      
   d_out("%s:current socloc connection count is %d", mname, connect_num);
      
   if (logman_is_active())
      logman("%s:normal exit,rc[0]", mname);
}
#endif

#ifdef IPC_QNX
void d_tcp_only(void)
{
   d_out("applies to TCP IPC method only");
}
#endif
   
int d_sr_code(int typ, char *parm)
{
   /* Send a message to the server which will return just a code. */

   char reply[50], mname[] = "d_sr_code";
   int ret;

   d_header(mname);

#ifdef IPC_TCP
   ret = d_sr(typ, parm, reply);
#else
   ret = d_sr(typ, parm, reply, 50);
#endif

   if (ret == DM_OK)
      if (!qatoi(reply, &ret))
         return(DM_INTERNAL_ERROR); 

   d_code_string(ret, reply);
   logman("%s:normal exit[%s]", mname, reply);
   return(ret);
}

int d_sr_int(int typ, char *parm, int *ival)
{
   /* Send a message to the server which will return an integer
      value as well as the 'dumsocks' code. */

   char reply[50], anint[50];
   int ret;

#ifdef IPC_TCP
   ret = d_sr(typ, parm, reply);
#else
   ret = d_sr(typ, parm, reply, 50);
#endif

   if (ret == DM_OK)
      {
      if (words(reply) < 2)
         return(DM_INTERNAL_ERROR);

      if (!word(reply, anint, 1))
         return(DM_INTERNAL_ERROR);

      if (!qatoi(anint, &ret))
         return(DM_INTERNAL_ERROR);

      if (!word(reply, anint, 2))
         return(DM_INTERNAL_ERROR);

      if (!qatoi(anint, ival))
         return(DM_INTERNAL_ERROR);
      }

   return(ret);
}

int d_sr_long(int typ, char *parm, long *lval)
{
   /* Send a message to the server which will return a long integer
      value as well as the 'dumsocks' code. */

   char reply[100], along[50];
   int ret;

#ifdef IPC_TCP
   ret = d_sr(typ, parm, reply);
#else
   ret = d_sr(typ, parm, reply, 100);
#endif

   if (ret == DM_OK)
      {
      if (words(reply) < 2)
         return(DM_INTERNAL_ERROR);

      if (!word(reply, along, 1))
         return(DM_INTERNAL_ERROR);

      if (!qatoi(along, &ret))
         return(DM_INTERNAL_ERROR);

      if (!word(reply, along, 2))
         return(DM_INTERNAL_ERROR);

      if (!qatol(along, lval))
         return(DM_INTERNAL_ERROR);
      }

   return(ret);
}

int d_sr_char(int typ, char *parm, char *reply)
{
   /* Send a message to the server which will return a string in
      addition to the 'dumsocks' code. */

   char *tmp, ival[25], mname[] = "d_sr_char";
   int ret;

   d_header(mname);

   if ((tmp = (char *)malloc(DM_MAXCOMMAND)) == NULL)
      {
      logman("%s:alloc fail[tmp]", mname);
      return(DM_MEMORY_FAIL);
      }

#ifdef IPC_TCP
   ret = d_sr(typ, parm, tmp);
#else
   ret = d_sr(typ, parm, tmp, DM_MAXCOMMAND); 
#endif 

   logman("%s:reply=%s,l=%d", mname, tmp, strlen(tmp));

   if (ret == DM_OK)
      {
      if (command_words(tmp) < 2)
         {
         free(tmp);
         return(DM_INTERNAL_ERROR);
         }

      if (!command_word(tmp, ival, 1))
         {
         free(tmp);
         return(DM_INTERNAL_ERROR);
         }

      if (!qatoi(ival, &ret))
         {
         free(tmp);
         return(DM_INTERNAL_ERROR);
         }

      if (!command_word(tmp, reply, 2))
         {
         free(tmp);
         return(DM_INTERNAL_ERROR);
         }
      }

   d_code_string(ret, tmp);
   logman("%s:normal exit[%s]", mname, tmp);
   free(tmp);
   return(ret);
}

#ifdef IPC_TCP
int d_sr(int typ, char *parm, char *reply)
#else
int d_sr(int typ, char *parm, char *reply, int size_reply)
#endif
{
   /* Send and receive a message to the server. Its ok if 'parm'
      is NULL or empty. Upon success, server reply load loaded
      into 'reply' which must be already allocated to
      sufficient size. Function returns a 'dumsocks' code. */

   char mname[] = "d_sr";
   char *mess;

   logman("%s:enter", mname);
   
   /* make sure we are connected */

   if (!is_connected())
      {
      logman("%s:not connected to any 'dumsocks' server", mname);
      return(DM_NO_SERVER); 
      } 

   if ((mess = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
      {
      logman("%s:alloc fail[mess]", mname);
      return(DM_MEMORY_FAIL);
      }

   memset(mess, 0, DM_MAXCOMMAND);

   // format send string

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

   logman("%s:send buf=%s,l=%d", mname, mess, strlen(mess));
   
#ifdef IPC_TCP
   if (!ipc_send_receive(mess, reply))
#else
   if (!ipc_send_receive(mess, reply, size_reply))
#endif
      {
      logman("%s:error communicating with server", mname);
      free(mess);
#ifdef IPC_TCP
      d_failover(TRUE);
#else
      connected_flag = FALSE;
#endif
      return(DM_VC_ERROR);
      }
         
   free(mess);
   logman("%s:normal exit,rc[ok],reply=%s,l=%d", mname,
                 reply, strlen(reply));
   return(DM_OK);
}

int is_connected(void)
{
   /* Determine whether a 'dumsocks' server is active.
      Function returns 'TRUE' if a server is active,
      'FALSE' otherwise. */

#ifdef IPC_TCP
   if (serverport > 0)
      return(TRUE);
#else
   if (connected_flag)
      return(TRUE);
#endif

   return(FALSE);
}

#ifdef IPC_TCP
void d_failover(int dflag)
{
   /* Failover to another 'dumsocks' server. The flag 'dflag'
      indicates whether we should get 'socloc' to delete the
      old entry. */
   
   char mname[] = "d_failover";
   char mes[138];
   int ret;

   d_header(mname);

   if (dflag)
      if ((ret = sloc_delete(serverport)) != SL_OK)
         {
         sl_code_string(ret, mes);
         d_out("%s:bad rc[%s] from 'sloc_delete'", mname, mes);
         }

   if ((ret = sloc_find(DM_SERVICE_NAME, serverhost, &serverport,
        (char *)NULL)) != SL_OK)
      {
      sl_code_string(ret, mes);
      d_out("%s:bad rc[%s] from 'sloc_find'", mname, mes);
      return;
      }

   if (!ipc_set_active(serverhost, serverport))
      {
      d_out("%s:bad rc[FALSE] from 'ipc_set_active'", mname);
      return;
      }

   if (logman_is_active())
      logman("%s:normal exit,failover ok,h=%s,p=%d", mname,
                    serverhost, serverport);
   else
      printf("failover to host '%s' on port %d successful\n",
             serverhost, serverport);
}
#endif

void d_code_string(int ret, char *mes)
{
   /* Translate a 'dumsocks' return code to English. 'mes' must
      already be allocated to sufficient size. */

   switch(ret)
      {
      case DM_OK:
         strcpy(mes, "ok");
         break;

      case DM_ACCESS_DENIED:
         strcpy(mes, "access denied");
         break;

      case DM_MEMORY_FAIL:
         strcpy(mes, "memory allocation error");
         break;

      case DM_INVALID_FUNCTION:
         strcpy(mes, "invalid function");
         break;

      case DM_INTERNAL_ERROR:
         strcpy(mes, "internal server error");
         break;

      case DM_INVALID_PARAMETER:
         strcpy(mes, "invalid parameter");
         break;

      case DM_VC_ERROR:
         strcpy(mes, "socket communication error");
         break;

      case DM_LOG_ALREADY_OFF:
         strcpy(mes, "log already off");
         break;

      case DM_LOG_ALREADY_ON:
         strcpy(mes, "log already on");
         break;

      case DM_NO_SERVER:
         strcpy(mes, "not connected to dumsocks server");
         break;

      case DM_NOT_FOUND:
         strcpy(mes, "not found");
         break;

      case DM_LOG_ERROR:
         strcpy(mes, "error writing to log file");
         break;
         
      case DM_NOT_IMPLEMENTED:
         strcpy(mes, "not implemented");
         break;

      default:
         strcpy(mes, "unknown code");
      };
}

int get_token(char *keywrd)
{
   /* Get keyword token. */

   char mname[] = "get_token";
   int i;
   int token = DMCOM_NOT_FOUND;

   /* static array of command words, notice the dummy zero position */

   static char *coms[] = { " ", "TERM", "STATUS", "CLIENT.LOG.OFF",
                           "CLIENT.LOG.ON", "CLIENT.LOG.STATUS",
                           "SERVER.LOG.OFF", "SERVER.LOG.ON",
                           "SERVER.LOG.STATUS", "CONNECT",
                           "SOCLOC.GET.LIST", "SOCLOC.CONFIG.GET.LIST",
                           "SOCLOC.GET", "SOCLOC.LOG.OFF",
                           "SOCLOC.LOG.ON", "SOCLOC.LOG.STATUS",
                           "SOCLOC.VERSION", "VERSION", "TRANS.NUM",
                           "CONNECT.NUM", "SOCLOC.TRANS.NUM",
                           "SOCLOC.CONNECT.NUM" };

   logman("%s:enter,keywrd='%s'", mname, keywrd);
   
   /* check for 'OFF' keyword separately */

   if (!stricmp(keywrd, "OFF"))
      {
      logman("%s:found 'OFF' keyword", mname);      
      return(DMCOM_OFF);
      }

   for(i = 1; i <= DMCOM_MAXCOM; i++)      
      if (!stricmp(keywrd, coms[i]))
         {
         token = i;        
         break;
         }

   logman("%s:normal exit,token=%d", mname, token);
   return(token);
}

void d_header(char *mname)
{
   /* Output the function enter header if logging. */
   
#ifdef IPC_TCP
   if (strlen(serverhost) && serverport != 0)
      logman("%s:enter,sh=%s,sp=%d", mname, serverhost, serverport);
   else
      logman("%s:enter,no connection", mname);
#else
   logman("%s:enter", mname);
#endif
}
      
void d_out(char *fmt,...)
{
   /* Output a message to the screen or use 'logman' to
      log the message. Output method depends on personal (client)
      logging status. Do not put a cr/lf at the end of the message.
      This will be automatically written to the end of the message. */

   char mes[MAXMES];
   va_list argptr;

   va_start(argptr, fmt);
   vsprintf(mes, fmt, argptr);
   
   if (logman_is_active())
      logman_nf(mes);
   else
      printf("%s\n", mes);
}

void term_app(void)
{
   // Shutdown all API's.

   if (logman_is_active())
      logman_end();

#ifdef IPC_TCP
   ipc_close();
   sloc_term_api();
#ifdef OS_WIN32
   WSACleanup();
#endif
#endif

   (void)appinit_remove_name();
}
