/* src/s_socket.c -  socket-related functions */

/*
 *
 * Copyright (C) 2005, Ranch Networks, Inc.
 *
 * 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.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * Author : Srivatsa Chivukula <srivatsa@ranchnetworks.com>
 *
 */

/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
 * All rights reserved.
 *
 * This package is an SSL implementation written
 * by Eric Young (eay@cryptsoft.com).
 * The implementation was written so as to conform with Netscapes SSL.
 * 
 * This library is free for commercial and non-commercial use as long as
 * the following conditions are aheared to.  The following conditions
 * apply to all code found in this distribution, be it the RC4, RSA,
 * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
 * included with this distribution is covered by the same copyright terms
 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
 * 
 * Copyright remains Eric Young's, and as such any Copyright notices in
 * the code are not to be removed.
 * If this package is used in a product, Eric Young should be given attribution
 * as the author of the parts of the library used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    "This product includes cryptographic software written by
 *     Eric Young (eay@cryptsoft.com)"
 *    The word 'cryptographic' can be left out if the rouines from the library
 *    being used are not cryptographic related :-).
 * 4. If you include any Windows specific code (or a derivative thereof) from 
 *    the apps directory (application code) you must include an acknowledgement:
 *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
 * 
 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * 
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>

#ifndef NO_SSL
#define USE_SOCKETS
#include <openssl/e_os2.h>

/* With IPv6, it looks like Digital has mixed up the proper order of
   recursive header file inclusion, resulting in the compiler complaining
   that u_int isn't defined, but only if _POSIX_C_SOURCE is defined, which
   is needed to have fileno() declared correctly...  So let's define u_int */
#if defined(OPENSSL_SYS_VMS_DECC) && !defined(__U_INT)
#define __U_INT
typedef unsigned int u_int;
#endif

#undef USE_SOCKETS
#include <openssl/x509.h>
#undef NON_MAIN
#include "s_apps.h"
#define OPENSSL_NO_KRB5
#include <openssl/ssl.h>
#endif
#include "simco_core.h"

static struct hostent *GetHostByName(char *name);
#ifdef OPENSSL_SYS_WINDOWS
static void ssl_sock_cleanup(void);
#endif
static int ssl_sock_init(void);
static int init_client_ip(int *sock,unsigned char ip[4], int port);
static int host_ip(char *str, unsigned char ip[4]);

#define SOCKET_PROTOCOL IPPROTO_TCP

#ifdef OPENSSL_SYS_WINDOWS
static struct WSAData wsa_state;
static int wsa_init_done=0;

#ifdef OPENSSL_SYS_WIN16
static HWND topWnd=0;
static FARPROC lpTopWndProc=NULL;
static FARPROC lpTopHookProc=NULL;
extern HINSTANCE _hInstance;  /* nice global CRT provides */

static LONG FAR PASCAL topHookProc(HWND hwnd, UINT message, WPARAM wParam,
				   LPARAM lParam)
{
  if (hwnd == topWnd)
    {
      switch(message)
	{
	case WM_DESTROY:
	case WM_CLOSE:
	  SetWindowLong(topWnd,GWL_WNDPROC,(LONG)lpTopWndProc);
	  ssl_sock_cleanup();
	  break;
	}
    }
  return CallWindowProc(lpTopWndProc,hwnd,message,wParam,lParam);
}

static BOOL CALLBACK enumproc(HWND hwnd,LPARAM lParam)
{
  topWnd=hwnd;
  return(FALSE);
}

#endif /* OPENSSL_SYS_WIN32 */
#endif /* OPENSSL_SYS_WINDOWS */

#ifdef OPENSSL_SYS_WINDOWS
static void ssl_sock_cleanup(void)
{
  if (wsa_init_done)
    {
      wsa_init_done=0;
#ifndef OPENSSL_SYS_WINCE
      WSACancelBlockingCall();
#endif
      WSACleanup();
    }
}
#endif

static int ssl_sock_init(void)
{
#ifdef WATT32
  extern int _watt_do_exit;
  _watt_do_exit = 0;
  dbug_init();
  if (sock_init())
    return (0);
#elif defined(OPENSSL_SYS_WINDOWS)
  if (!wsa_init_done)
    {
      int err;
	  
#ifdef SIGINT
      signal(SIGINT,(void (*)(int))ssl_sock_cleanup);
#endif
      wsa_init_done=1;
      memset(&wsa_state,0,sizeof(wsa_state));
      if (WSAStartup(0x0101,&wsa_state)!=0)
	{
	  err=WSAGetLastError();
	  BIO_printf(bio_err,"unable to start WINSOCK, error code=%d\n",err);
	  return(0);
	}

#ifdef OPENSSL_SYS_WIN16
      EnumTaskWindows(GetCurrentTask(),enumproc,0L);
      lpTopWndProc=(FARPROC)GetWindowLong(topWnd,GWL_WNDPROC);
      lpTopHookProc=MakeProcInstance((FARPROC)topHookProc,_hInstance);

      SetWindowLong(topWnd,GWL_WNDPROC,(LONG)lpTopHookProc);
#endif /* OPENSSL_SYS_WIN16 */
    }
#endif /* OPENSSL_SYS_WINDOWS */
  return(1);
}

int init_client(int *sock, char *host, int port)
{
  unsigned char ip[4];
  short p=0;

  if (!host_ip(host,&(ip[0])))
    {
      return(0);
    }
  if (p != 0) port=p;
  return(init_client_ip(sock,ip,port));
}

static int init_client_ip(int *sock, unsigned char ip[4], int port)
{
  unsigned long addr;
  struct sockaddr_in them;
  int s,i;

  if (!ssl_sock_init()) return(0);

  memset((char *)&them,0,sizeof(them));
  them.sin_family=AF_INET;
  them.sin_port=htons((unsigned short)port);
  addr=(unsigned long)
    ((unsigned long)ip[0]<<24L)|
    ((unsigned long)ip[1]<<16L)|
    ((unsigned long)ip[2]<< 8L)|
    ((unsigned long)ip[3]);
  them.sin_addr.s_addr=htonl(addr);

  s=socket(AF_INET,SOCK_STREAM,SOCKET_PROTOCOL);
  if (s == -1) { perror("socket"); return(0); }

#ifndef OPENSSL_SYS_MPE
  i=0;
  i=setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(char *)&i,sizeof(i));
  if (i < 0) { perror("keepalive"); return(0); }
#endif
  if (connect(s,(struct sockaddr *)&them,sizeof(them)) == -1)
    { close(s); return(0); }
  *sock=s;
  return(1);
}

static int host_ip(char *str, unsigned char ip[4])
{
  unsigned int in[4]; 
  int i;

  if (sscanf(str,"%u.%u.%u.%u",&(in[0]),&(in[1]),&(in[2]),&(in[3])) == 4)
    {
      for (i=0; i<4; i++)
	if (in[i] > 255)
	  {
	    BIO_printf(bio_err,"invalid IP address\n");
	    goto err;
	  }
      ip[0]=in[0];
      ip[1]=in[1];
      ip[2]=in[2];
      ip[3]=in[3];
    }
  else
    { /* do a gethostbyname */
      struct hostent *he;

      if (!ssl_sock_init()) return(0);

      he=GetHostByName(str);
      if (he == NULL)
	{
	  BIO_printf(bio_err,"gethostbyname failure\n");
	  goto err;
	}
      /* cast to short because of win16 winsock definition */
      if ((short)he->h_addrtype != AF_INET)
	{
	  BIO_printf(bio_err,"gethostbyname addr is not AF_INET\n");
	  return(0);
	}
      ip[0]=he->h_addr_list[0][0];
      ip[1]=he->h_addr_list[0][1];
      ip[2]=he->h_addr_list[0][2];
      ip[3]=he->h_addr_list[0][3];
    }
  return(1);
 err:
  return(0);
}

#define GHBN_NUM	4
static struct ghbn_cache_st
{
  char name[128];
  struct hostent ent;
  unsigned long order;
} ghbn_cache[GHBN_NUM];

static unsigned long ghbn_hits=0L;
static unsigned long ghbn_miss=0L;

static struct hostent *GetHostByName(char *name)
{
  struct hostent *ret;
  int i,lowi=0;
  unsigned long low= (unsigned long)-1;

  for (i=0; i<GHBN_NUM; i++)
    {
      if (low > ghbn_cache[i].order)
	{
	  low=ghbn_cache[i].order;
	  lowi=i;
	}
      if (ghbn_cache[i].order > 0)
	{
	  if (strncmp(name,ghbn_cache[i].name,128) == 0)
	    break;
	}
    }
  if (i == GHBN_NUM) /* no hit*/
    {
      ghbn_miss++;
      ret=gethostbyname(name);
      if (ret == NULL) return(NULL);
      /* else add to cache */
      if(strlen(name) < sizeof ghbn_cache[0].name)
	{
	  strcpy(ghbn_cache[lowi].name,name);
	  memcpy((char *)&(ghbn_cache[lowi].ent),ret,sizeof(struct hostent));
	  ghbn_cache[lowi].order=ghbn_miss+ghbn_hits;
	}
      return(ret);
    }
  else
    {
      ghbn_hits++;
      ret= &(ghbn_cache[i].ent);
      ghbn_cache[i].order=ghbn_miss+ghbn_hits;
      return(ret);
    }
}
