Logo Search packages:      
Sourcecode: openssl version File versions

s_socket.c

/* apps/s_socket.c -  socket-related functions used by s_client and s_server */
/* 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>

/* 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

#define USE_SOCKETS
#define NON_MAIN
#include "apps.h"
#undef USE_SOCKETS
#undef NON_MAIN
#include "s_apps.h"
#include <openssl/ssl.h>

#ifdef FLAT_INC
#include "e_os.h"
#else
#include "../e_os.h"
#endif

#ifndef OPENSSL_NO_SOCK

#if defined(OPENSSL_SYS_NETWARE) && defined(NETWARE_BSDSOCK)
#include "netdb.h"
#endif

static struct hostent *GetHostByName(char *name);
#if defined(OPENSSL_SYS_WINDOWS) || (defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK))
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, int type);
static int init_server(int *sock, int port, int type);
static int init_server_long(int *sock, int port,char *ip, int type);
static int do_accept(int acc_sock, int *sock, char **host);
static int host_ip(char *str, unsigned char ip[4]);

#ifdef OPENSSL_SYS_WIN16
#define SOCKET_PROTOCOL 0 /* more microsoft stupidity */
#else
#define SOCKET_PROTOCOL IPPROTO_TCP
#endif

#if defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK)
static int wsa_init_done=0;
#endif

#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();
            }
      }
#elif defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK)
static void sock_cleanup(void)
    {
    if (wsa_init_done)
        {
        wsa_init_done=0;
            WSACleanup();
            }
      }
#endif

static int ssl_sock_init(void)
      {
#ifdef WATT32
      extern int _watt_do_exit;
      _watt_do_exit = 0;
      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 */
            }
#elif defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK)
   WORD wVerReq;
   WSADATA wsaData;
   int err;

   if (!wsa_init_done)
      {
   
# ifdef SIGINT
      signal(SIGINT,(void (*)(int))sock_cleanup);
# endif

      wsa_init_done=1;
      wVerReq = MAKEWORD( 2, 0 );
      err = WSAStartup(wVerReq,&wsaData);
      if (err != 0)
         {
         BIO_printf(bio_err,"unable to start WINSOCK2, error code=%d\n",err);
         return(0);
         }
      }
#endif /* OPENSSL_SYS_WINDOWS */
      return(1);
      }

int init_client(int *sock, char *host, int port, int type)
      {
      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,type));
      }

static int init_client_ip(int *sock, unsigned char ip[4], int port, int type)
      {
      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);

      if (type == SOCK_STREAM)
            s=socket(AF_INET,SOCK_STREAM,SOCKET_PROTOCOL);
      else /* ( type == SOCK_DGRAM) */
            s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
                  
      if (s == INVALID_SOCKET) { perror("socket"); return(0); }

#ifndef OPENSSL_SYS_MPE
      if (type == SOCK_STREAM)
            {
            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); perror("connect"); return(0); }
      *sock=s;
      return(1);
      }

int do_server(int port, int type, int *ret, int (*cb)(char *hostname, int s, unsigned char *context), unsigned char *context)
      {
      int sock;
      char *name = NULL;
      int accept_socket;
      int i;

      if (!init_server(&accept_socket,port,type)) return(0);

      if (ret != NULL)
            {
            *ret=accept_socket;
            /* return(1);*/
            }
      for (;;)
            {
            if (type==SOCK_STREAM)
                  {
                  if (do_accept(accept_socket,&sock,&name) == 0)
                        {
                        SHUTDOWN(accept_socket);
                        return(0);
                        }
                  }
            else
                  sock = accept_socket;
            i=(*cb)(name,sock, context);
            if (name != NULL) OPENSSL_free(name);
            if (type==SOCK_STREAM)
                  SHUTDOWN2(sock);
            if (i < 0)
                  {
                  SHUTDOWN2(accept_socket);
                  return(i);
                  }
            }
      }

static int init_server_long(int *sock, int port, char *ip, int type)
      {
      int ret=0;
      struct sockaddr_in server;
      int s= -1,i;

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

      memset((char *)&server,0,sizeof(server));
      server.sin_family=AF_INET;
      server.sin_port=htons((unsigned short)port);
      if (ip == NULL)
            server.sin_addr.s_addr=INADDR_ANY;
      else
/* Added for T3E, address-of fails on bit field (beckman@acl.lanl.gov) */
#ifndef BIT_FIELD_LIMITS
            memcpy(&server.sin_addr.s_addr,ip,4);
#else
            memcpy(&server.sin_addr,ip,4);
#endif
      
            if (type == SOCK_STREAM)
                  s=socket(AF_INET,SOCK_STREAM,SOCKET_PROTOCOL);
            else /* type == SOCK_DGRAM */
                  s=socket(AF_INET, SOCK_DGRAM,IPPROTO_UDP);

      if (s == INVALID_SOCKET) goto err;
#if defined SOL_SOCKET && defined SO_REUSEADDR
            {
            int j = 1;
            setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
                     (void *) &j, sizeof j);
            }
#endif
      if (bind(s,(struct sockaddr *)&server,sizeof(server)) == -1)
            {
#ifndef OPENSSL_SYS_WINDOWS
            perror("bind");
#endif
            goto err;
            }
      /* Make it 128 for linux */
      if (type==SOCK_STREAM && listen(s,128) == -1) goto err;
      i=0;
      *sock=s;
      ret=1;
err:
      if ((ret == 0) && (s != -1))
            {
            SHUTDOWN(s);
            }
      return(ret);
      }

static int init_server(int *sock, int port, int type)
      {
      return(init_server_long(sock, port, NULL, type));
      }

static int do_accept(int acc_sock, int *sock, char **host)
      {
      int ret,i;
      struct hostent *h1,*h2;
      static struct sockaddr_in from;
      int len;
/*    struct linger ling; */

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

#ifndef OPENSSL_SYS_WINDOWS
redoit:
#endif

      memset((char *)&from,0,sizeof(from));
      len=sizeof(from);
      /* Note: under VMS with SOCKETSHR the fourth parameter is currently
       * of type (int *) whereas under other systems it is (void *) if
       * you don't have a cast it will choke the compiler: if you do
       * have a cast then you can either go for (int *) or (void *).
       */
      ret=accept(acc_sock,(struct sockaddr *)&from,(void *)&len);
      if (ret == INVALID_SOCKET)
            {
#if defined(OPENSSL_SYS_WINDOWS) || (defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK))
            i=WSAGetLastError();
            BIO_printf(bio_err,"accept error %d\n",i);
#else
            if (errno == EINTR)
                  {
                  /*check_timeout(); */
                  goto redoit;
                  }
            fprintf(stderr,"errno=%d ",errno);
            perror("accept");
#endif
            return(0);
            }

/*
      ling.l_onoff=1;
      ling.l_linger=0;
      i=setsockopt(ret,SOL_SOCKET,SO_LINGER,(char *)&ling,sizeof(ling));
      if (i < 0) { perror("linger"); return(0); }
      i=0;
      i=setsockopt(ret,SOL_SOCKET,SO_KEEPALIVE,(char *)&i,sizeof(i));
      if (i < 0) { perror("keepalive"); return(0); }
*/

      if (host == NULL) goto end;
#ifndef BIT_FIELD_LIMITS
      /* I should use WSAAsyncGetHostByName() under windows */
      h1=gethostbyaddr((char *)&from.sin_addr.s_addr,
            sizeof(from.sin_addr.s_addr),AF_INET);
#else
      h1=gethostbyaddr((char *)&from.sin_addr,
            sizeof(struct in_addr),AF_INET);
#endif
      if (h1 == NULL)
            {
            BIO_printf(bio_err,"bad gethostbyaddr\n");
            *host=NULL;
            /* return(0); */
            }
      else
            {
            if ((*host=(char *)OPENSSL_malloc(strlen(h1->h_name)+1)) == NULL)
                  {
                  perror("OPENSSL_malloc");
                  return(0);
                  }
            BUF_strlcpy(*host,h1->h_name,strlen(h1->h_name)+1);

            h2=GetHostByName(*host);
            if (h2 == NULL)
                  {
                  BIO_printf(bio_err,"gethostbyname failure\n");
                  return(0);
                  }
            i=0;
            if (h2->h_addrtype != AF_INET)
                  {
                  BIO_printf(bio_err,"gethostbyname addr is not AF_INET\n");
                  return(0);
                  }
            }
end:
      *sock=ret;
      return(1);
      }

int extract_host_port(char *str, char **host_ptr, unsigned char *ip,
           short *port_ptr)
      {
      char *h,*p;

      h=str;
      p=strchr(str,':');
      if (p == NULL)
            {
            BIO_printf(bio_err,"no port defined\n");
            return(0);
            }
      *(p++)='\0';

      if ((ip != NULL) && !host_ip(str,ip))
            goto err;
      if (host_ptr != NULL) *host_ptr=h;

      if (!extract_port(p,port_ptr))
            goto err;
      return(1);
err:
      return(0);
      }

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);
      }

int extract_port(char *str, short *port_ptr)
      {
      int i;
      struct servent *s;

      i=atoi(str);
      if (i != 0)
            *port_ptr=(unsigned short)i;
      else
            {
            s=getservbyname(str,"tcp");
            if (s == NULL)
                  {
                  BIO_printf(bio_err,"getservbyname failure for %s\n",str);
                  return(0);
                  }
            *port_ptr=ntohs((unsigned short)s->s_port);
            }
      return(1);
      }

#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);
            }
      }

#endif

Generated by  Doxygen 1.6.0   Back to index