/*--------------------------------------------------------------------*/
/*       T c p E r r . c                                              */
/*                                                                    */
/*       Part of BinkD project                                        */
/*       WinSock error's                                              */
/*--------------------------------------------------------------------*/

/*--------------------------------------------------------------------*/
/*       Copyright (c) 1996 by Fydodor Ustinov                        */
/*                             FIDONet 2:5020/79                      */
/*                                                                    */
/*  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. See COPYING.  */
/*--------------------------------------------------------------------*/

/*--------------------------------------------------------------------*/
/*                          RCS Information                           */
/*--------------------------------------------------------------------*/

/*
 * $Id: TCPErr.c,v 2.9.2.4 2003/08/05 05:38:43 hbrew Exp $
 *
 * Revision history:
 * $Log: TCPErr.c,v $
 * Revision 2.9.2.4  2003/08/05 05:38:43  hbrew
 * 'static const char rcsid[]' removed
 *
 * Revision 2.9.2.3  2003/06/21 15:35:35  hbrew
 * Fix running on Win9x-systems.
 *
 * Revision 2.9.2.2  2003/06/21 07:59:05  gul
 * Fix typo in error report string
 *
 * Revision 2.9.2.1  2003/06/17 15:48:00  stas
 * Prevent service operations on incompatible OS (NT and 9x)
 *
 * Revision 2.9  2003/06/05 11:40:19  stas
 * Cosmetics
 *
 * Revision 2.8  2003/06/05 11:16:56  stas
 * Fix warning; More accuracy errors strings list maintnance
 *
 * Revision 2.7  2003/06/04 10:36:59  stas
 * Thread-safety tcperr() implementation on Win32
 *
 * Revision 2.6  2003/05/25 12:52:35  stas
 * Replace CR and LF with spaces in system error messages
 *
 * Revision 2.5  2003/05/23 17:54:08  stas
 * Display default text at unknown win32 error
 *
 * Revision 2.4  2003/05/23 09:11:13  stas
 * Improve diagnostic: print Win32API error message on unknown error
 *
 * Revision 2.3  2003/05/23 07:55:23  stas
 * Update error messages, add comments with messages published in MSDN
 *
 * Revision 2.2  2003/02/28 20:39:08  gul
 * Code cleanup:
 * change "()" to "(void)" in function declarations;
 * change C++-style comments to C-style
 *
 * Revision 2.1  2003/02/13 19:44:44  gul
 * Change \r\n -> \n
 *
 * Revision 2.0  2001/01/10 12:12:40  gul
 * Binkd is under CVS again
 *
 * Revision 0.02  1996/12/15  18:58:09  ufm
 *      Fixed bug with wrong IP Errors numeration.
 *      For sample, Error "Connection refused" (61) says as
 *      "Too many levels of symbolic links"  (62)
 *
 * Revision 0.01  1996/12/03  12:15:15  ufm
 *      First revision
 *
 */

/*--------------------------------------------------------------------*/
/*                        System include files                        */
/*--------------------------------------------------------------------*/

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

/*--------------------------------------------------------------------*/
/*                        Local include files                         */
/*--------------------------------------------------------------------*/

#include "../sem.h"

/*--------------------------------------------------------------------*/
/*                         Global definitions                         */
/*--------------------------------------------------------------------*/

/*--------------------------------------------------------------------*/
/*                          Global variables                          */
/*--------------------------------------------------------------------*/

/*--------------------------------------------------------------------*/
/*                           Local variables                          */
/*--------------------------------------------------------------------*/

#ifdef BINKDW9X
/* Windows version test
 * Parameter: Platform ID (VER_PLATFORM_WIN32_NT or VER_PLATFORM_WIN32_WINDOWS,
 *            see GetVersionEx() if MSDN)
 * Return 0 if match OS, not zero (usually -1) if do not match OS,
 * return 1 if can't retrieve OS version info.
 */
int W32_CheckOS(unsigned long PlatformId)
{ OSVERSIONINFO os_ver;

  os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  if( GetVersionEx(&os_ver) )
    return os_ver.dwPlatformId != PlatformId;
  return 1;
}
#endif

static char err_sem_init = 0;
static MUTEXSEM err_sem;

typedef struct s_StrErrList{
  int code;
  char *text;
  struct s_StrErrList *next;
} *pStrErrList;

static pStrErrList errlist;

/*--------------------------------------------------------------------*/
/*                    Local functions prototypes                      */
/*--------------------------------------------------------------------*/

/*--------------------------------------------------------------------*/
/*    int tcperr(void)                                                */
/*                                                                    */
/*    return string to winsock error.                                 */
/*--------------------------------------------------------------------*/

/* Return error string for win32 API error
 * return pointer to malloc'ed string or NULL
 */
char *W32APIstrerror(int errnum)
{ char *st, *cp=NULL;
  int len;

  FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        errnum,
        0, /* Default language */
        (LPTSTR) &cp,
        0,
        NULL
    );
  if( !cp || !(*cp) ){
    st = malloc(40);
    if(st) sprintf(st, "{%u} Unknown Win32API error", errnum);
  }else{
    len = strlen(cp)+15;
    st=malloc(len);
    if(st){
      len = sprintf(st, "{%u} ",errnum);
      AnsiToOem(cp,st+len);
      LocalFree(cp);  /* Win32 API free() implementation */
    }else st=cp;

    for(cp=st+len; *cp; cp++)
      switch(*cp){           /* Replace '\r' and '\n' with space */
        case '\r':
        case '\n':
          *cp=' ';
      }
  }

  return st;
}

/* Store new error message taken from FormatMessage() to errlist
 * Return message
 * Lock semaphore err_sem before call newerrortolist() please!
 */
const char *newerrortolist(int err){
  pStrErrList newe;

  if( !(newe = malloc(sizeof(*newe))) ){
/*    Log( 0, "Out of memory (malloc())" );*/
    return NULL;
  }
  newe->code = err;
  newe->text = W32APIstrerror(err);
  newe->next = errlist;
  errlist = newe;

  if( newe->text )
    return newe->text;

  return "Unknown error"; /* Don't return NULL! */
}

/* Release memory from errlist, release semaphore
 */
void ReleaseErrorList(void){
  pStrErrList el;

  if( err_sem_init ){
    if( errlist && !LockSem(&err_sem) ){
      while(errlist){
        if(errlist->text) free(errlist->text);
        el = errlist;
        errlist=errlist->next;
        free(el);
      }
      ReleaseSem(&err_sem);
    }
    err_sem_init=0;
    CleanSem(&err_sem);
  }
}

/* Return error message from errlist
 */
const char *errorfromlist(int err){
  pStrErrList el, sel;
  const char *errmess="";

  for( sel=el=errlist; el; el=el->next )
    if( el->code==err )
      return el->text;

  if(!err_sem_init){ err_sem_init=1; InitSem(&err_sem); }

  if( !LockSem(&err_sem) ){
    if(sel!=errlist)          /* List changed, check again */
      for( el=errlist; el && (el!=sel); el=el->next )
        if( el->code==err ){
          errmess = el->text;
          goto skip_newerrortolist;
        }
    errmess = newerrortolist(err);
skip_newerrortolist:
    ReleaseSem(&err_sem);
  }
  return errmess;
}

/* Return error message
 * (Massages taken from MSDN)
 */
const char *tcperr (int errnum) {
  const char *Str;

  switch( errnum ){
  case 10004:
     Str = "{10004} A blocking operation was interrupted by a call to WSACancelBlockingCall";
     break;
/* Not in MSDN, not return by FormatMessage
  case 10006:
     Str = "{10006} No such device or address";
     break; */
  case 10009:
/*     Str = "{10009} Bad file number";*/
     Str = "{10009} The file handle supplied is not valid";
     break;
  case 10013:
/*     Str = "{10013} Permission denied";*/
     Str = "{10013} An attempt was made to access a socket in a way forbidden by its access permissions";
     break;
  case 10014:
/*     Str = "{10014} Bad address";*/
     Str = "{10014} The system detected an invalid pointer address in attempting to use a pointer argument in a call";
     break;
  case 10022:
/*     Str = "{10022} Invalid argument";*/
     Str = "{10022} An invalid argument was supplied";
     break;
  case 10024:
     Str = "{10024} Too many open sockets";
     break;
/* Not in MSDN, not return by FormatMessage
  case 10032:
     Str = "{10032} Broken pipe";
     break;*/
  case 10035:
/*     Str = "{10035} Operation would block";*/
     Str = "{10035} A non-blocking socket operation could not be completed immediately";
     break;
  case 10036:
/*     Str = "{10036} Operation now in progress";*/
     Str = "{10036} A blocking operation is currently executing";
     break;
  case 10037:
/*     Str = "{10037} Operation already in progress";*/
     Str = "{10037} An operation was attempted on a non-blocking socket that already had an operation in progress";
     break;
  case 10038:
/*     Str = "{10038} Socket operation on non-socket";*/
     Str = "{10038} An operation was attempted on something that is not a socket";
     break;
  case 10039:
/*     Str = "{10039} Destination address required";*/
     Str = "{10039} A required address was omitted from an operation on a socket";
     break;
  case 10040:
/*     Str = "{10040} Message too long";*/
     Str = "{10040} A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself";
     break;
  case 10041:
/*     Str = "{10041} Protocol wrong type for socket";*/
     Str = "{10041} A protocol was specified in the socket function call that does not support the semantics of the socket type requested";
     break;
  case 10042:
/*     Str = "{10042} Protocol not available";*/
     Str = "{10042} An unknown, invalid, or unsupported option or level was specified in a getsockopt or setsockopt call";
     break;
  case 10043:
/*     Str = "{10043} Protocol not supported";*/
     Str = "{10043} The requested protocol has not been configured into the system, or no implementation for it exists";
     break;
  case 10044:
/*     Str = "{10044} Socket type not supported";*/
     Str = "{10044} The support for the specified socket type does not exist in this address family";
     break;
  case 10045:
/*     Str = "{10045} Operation not supported on socket";*/
     Str = "{10045} The attempted operation is not supported for the type of object referenced";
     break;
  case 10046:
/*     Str = "{10046} Protocol family not supported";*/
     Str = "{10046} The protocol family has not been configured into the system or no implementation for it exists";
     break;
  case 10047:
/*     Str = "{10047} Address family not supported by protocol family";*/
     Str = "{10047} An address incompatible with the requested protocol was used";
     break;
  case 10048:
/*     Str = "{10048} Address already in use";*/
     Str = "{10048} Only one usage of each socket address (protocol/network address/port) is normally permitted";
     break;
  case 10049:
/*     Str = "{10049} Can't assign requested address";*/
     Str = "{10049} The requested address is not valid in its context";
     break;
  case 10050:
/*     Str = "{10050} Network is down";*/
     Str = "{10050} A socket operation encountered a dead network";
     break;
  case 10051:
/*     Str = "{10052} Network is unreachable";*/
     Str = "{10052} A socket operation was attempted to an unreachable network";
     break;
  case 10052:
/*     Str = "{10052} Network dropped connection on reset";*/
     Str = "{10052} The connection has been broken due to keep-alive activity detecting a failure while the operation was in progress";
     break;
  case 10053:
/*     Str = "{10053} Software caused connection abort";*/
     Str = "{10053} An established connection was aborted by the software in your host machine";
     break;
  case 10054:
/*     Str = "{10054} Connection reset by peer";*/
     Str = "{10054} An existing connection was forcibly closed by the remote host";
     break;
  case 10055:
     Str = "{10055} No buffer space available";
/*     Str = "{10055} An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full";*/
     break;
  case 10056:
/*     Str = "{10056} Socket is already connected";*/
     Str = "{10056} A connect request was made on an already connected socket";
     break;
  case 10057:
     Str = "{10057} Socket is not connected";
/*     Str = "{10057} A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied";*/
     break;
  case 10058:
     Str = "{10058} Can't send after socket shutdown";
/*     Str = "{10058} A request to send or receive data was disallowed because the socket had already been shut down in that direction with a previous shutdown call";*/
     break;
  case 10059:
/*     Str = "{10059} Too many references: can't splice";*/
     Str = "{10059} Too many references to some kernel object";
     break;
  case 10060:
     Str = "{10060} Connection timed out";
/*     Str = "{10060} A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond"; */
     break;
  case 10061:
     Str = "{10061} Connection refused";
/*     Str = "{10061} No connection could be made because the target machine actively refused it";*/
     break;
  case 10062:
/*     Str = "{10062} Too many levels of symbolic links";*/
     Str = "{10062} Cannot translate name";
     break;
  case 10063:
/*     Str = "{10063} File name too long";*/
     Str = "{10063} Name component or name was too long";
     break;
  case 10064:
/*     Str = "{10064} Host is down";*/
     Str = "{10064} A socket operation failed because the destination host was down";
     break;
  case 10065:
/*     Str = "{10065} No route to host";*/
     Str = "{10065} A socket operation was attempted to an unreachable host";
     break;
  case 10066:
/*     Str = "{10066} Directory not empty";*/
     Str = "{10066} Cannot remove a directory that is not empty";
     break;
  case 10067:
/*     Str = "{10067} Applications limit reached";*/
     Str = "{10067} A Windows Sockets implementation may have a limit on the number of applications that may use it simultaneously.";
     break;
/*  case 10068:
     Str = "{10068} Ran out of quota.";
     break;*/
  case 10070:
     Str = "{10070} File handle reference is no longer available.";
     break;
  case 10071:
     Str = "{10071} Item is not available locally.";
     break;
  case 10091:
     Str = "{10091} WSAStartup cannot function at this time because the underlying system it uses to provide network services is currently unavailable.";
     break;
  case 10092:
     Str = "{10092} The Windows Sockets version requested is not supported.";
     break;
  case 10093:
     Str = "{10093} Either the application has not called WSAStartup, or WSAStartup failed.";
     break;
  case 10101:
     Str = "{10101} Returned by WSARecv or WSARecvFrom to indicate the remote party has initiated a graceful shutdown sequence.";
     break;
  case 11001:
     Str = "{11001} No such host is known.";
     break;
  case 11002:
     Str = "{11002} This is usually a temporary error during hostname resolution and means that the local server did not receive a response from an authoritative server.";
     break;
  default:
     Str = errorfromlist(errnum);
  }
   return Str;
}
