/* system independent sockets (basically for unix and Win)
   (C)2000,1 Simon Urbanek
   
   conditional defines: 

   USE_SNPRINTF 
     emulate snprintf on Win platforms (you will
     lose the security which is provided under unix of course)

   SOCK_ERRORS
     include error code handling and checking functions
*/

#ifndef __SISOCKS_H__
#define __SISOCKS_H__

#if defined __GNUC__ && !defined unix && !defined Win32 /* MacOS X hack (gcc on any platform should behave as unix - except for Win32, where we need to keep using winsock) */
#define unix
#endif

#if defined SOCK_ERRORS || defined USE_SNPRINTF
#include <stdio.h>
#endif
#include <string.h>
#include <stdlib.h>

#ifdef unix
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>

#define sockerrno errno

#define SOCKET int
#define INVALID_SOCKET (-1)
#define closesocket(A) close(A)

#else

#define windows
#include <windows.h>
#include <winsock.h>
#include <string.h>
#define inet_aton(A,B) (0, B.s_addr=inet_addr(A))

#define sockerrno WSAGetLastError()

/* more recent WinGW has those defined, olders don't */
#ifndef ECONNREFUSED
#define ECONNREFUSED WSAECONNREFUSED
#define EADDRINUSE WSAEADDRINUSE
#define ENOTSOCK WSAENOTSOCK
#define EISCONN WSAEISCONN
#define ETIMEDOUT WSAETIMEDOUT
#define ENETUNREACH WSAENETUNREACH
#define EINPROGRESS WSAEINPROGRESS
#define EALREADY WSAEALREADY
#define EAFNOSUPPORT WSAEAFNOSUPPORT
#define EBADF WSAEBADF
#define EINVAL WSAEINVAL
#define EOPNOTSUPP WSAEOPNOTSUPP
#define EFAULT WSAEFAULT
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EACCES WSAEACCES
#endif

#ifdef USE_SNPRINTF
int snprintf(char *buf, int len, char *fmt, ...)
{
   va_list argptr;
   int cnt;

   va_start(argptr, fmt);
   cnt = vsprintf(buf, fmt, argptr);
   va_end(argptr);

   return(cnt);
}

#endif

#endif

#define SA struct sockaddr
#define SAIN struct sockaddr_in

#ifdef windows

#ifdef NEED_INITSOCKS

static int initsocks(void)
{
  WSADATA dt;
  /* initialize WinSock 1.1 */
  return (WSAStartup(0x0101,&dt))?-1:0;
};

#define donesocks() WSACleanup()
#endif

#else
 
/* no stupid stuff necessary for unix */
#define initsocks()
#define donesocks()

#ifndef INVALID_SOCKET
#define INVALID_SOCKET -1
#endif

#endif

#ifdef SOCK_ERRORS

int suppmode=0;
int socklasterr;
FILE *sockerrlog=0;

/* copy error description to buf or set *buf=0 if none */
int sockerrorchecks(char *buf, int blen, int res) {
  *buf=0;
  if (res==-1) {
    switch(sockerrno) {
    case EBADF: strncpy(buf,"bad descriptor",blen); break;
    case EINVAL: strncpy(buf,"already in use",blen); break;
    case EACCES: strncpy(buf,"access denied",blen); break;
    case ENOTSOCK: strncpy(buf,"descriptor is not a socket",blen); break;
    case EOPNOTSUPP: strncpy(buf,"operation not supported",blen); break;
    case EFAULT: strncpy(buf,"fault",blen); break;
    case EWOULDBLOCK: strncpy(buf,"operation would block",blen); break;
    case EISCONN: strncpy(buf,"is already connected",blen); break;
    case ECONNREFUSED: strncpy(buf,"connection refused",blen); break;
    case ETIMEDOUT: strncpy(buf,"operation timed out",blen); break;
    case ENETUNREACH: strncpy(buf,"network is unreachable",blen); break;
    case EADDRINUSE: strncpy(buf,"address already in use",blen); break;
    case EINPROGRESS: strncpy(buf,"in progress",blen); break;
    case EALREADY: strncpy(buf,"previous connect request not completed yet",blen); break;
#ifdef unix
    default: snprintf(buf,blen,"unknown socket error %d",sockerrno);
#else
    default: sprintf(buf,"unknown socket error %d",sockerrno);
#endif
    };
  };
  return res;
};

/* check socket error and add to log file if necessary */
int sockerrorcheck(char *sn, int rtb, int res) {
  if (!sockerrlog) sockerrlog=stderr;
  if ((signed int)res==-1) {
    if (socklasterr==sockerrno) {
      suppmode++;
    } else {
      if (suppmode>0) {
        fprintf(sockerrlog,"##> REP: (last error has been repeated %d times.)\n",suppmode);
        suppmode=0;
      };
      fprintf(sockerrlog,"##> SOCK_ERROR: %s error #%d",sn,sockerrno);
      switch(sockerrno) {
      case EBADF: fprintf(sockerrlog,"(bad descriptor)"); break;
      case EINVAL: fprintf(sockerrlog,"(already in use)"); break;
      case EACCES: fprintf(sockerrlog,"(access denied)"); break;
      case ENOTSOCK: fprintf(sockerrlog,"(descriptor is not a socket)"); break;
      case EOPNOTSUPP: fprintf(sockerrlog,"(operation not supported)"); break;
      case EFAULT: fprintf(sockerrlog,"(fault)"); break;
      case EWOULDBLOCK: fprintf(sockerrlog,"(operation would block)"); break;
      case EISCONN: fprintf(sockerrlog,"(is already connected)"); break;
      case ECONNREFUSED: fprintf(sockerrlog,"(connection refused)"); break;
      case ETIMEDOUT: fprintf(sockerrlog,"(operation timed out)"); break;
      case ENETUNREACH: fprintf(sockerrlog,"(network is unreachable)"); break;
      case EADDRINUSE: fprintf(sockerrlog,"(address already in use)"); break;
      case EINPROGRESS: fprintf(sockerrlog,"(in progress)"); break;
      case EALREADY: fprintf(sockerrlog,"(previous connect request not completed yet)"); break;
      default: fprintf(sockerrlog,"(?)");
      };
      fprintf(sockerrlog,"\n"); fflush(sockerrlog);
      socklasterr=sockerrno;
    };
    if (rtb) exit(1);
  };
  return res;
};

#define FCF(X,F) sockerrorcheck(X,1,F)
#define CF(X,F) sockerrorcheck(X,0,F)

#endif

#ifdef BUILD_SIN
static struct sockaddr *build_sin(struct sockaddr_in *sa,char *ip,int port) {
  memset(sa,0,sizeof(struct sockaddr_in));
  sa->sin_family=AF_INET;
  sa->sin_port=htons(port);
  sa->sin_addr.s_addr=(ip)?inet_addr(ip):htonl(INADDR_ANY);
  return (struct sockaddr*)sa;
};
#endif

#endif /* __SISOCKS_H__ */
