Logo Search packages:      
Sourcecode: openssl version File versions

bss_conn.c

/* crypto/bio/bss_conn.c */
/* 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 <errno.h>
#define USE_SOCKETS
#include "cryptlib.h"
#include <openssl/bio.h>

#ifndef OPENSSL_NO_SOCK

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

#if (defined(OPENSSL_SYS_VMS) && __VMS_VER < 70000000)
/* FIONBIO used as a switch to enable ioctl, and that isn't in VMS < 7.0 */
#undef FIONBIO
#endif


00079 typedef struct bio_connect_st
      {
      int state;

      char *param_hostname;
      char *param_port;
      int nbio;

      unsigned char ip[4];
      unsigned short port;

      struct sockaddr_in them;

      /* int socket; this will be kept in bio->num so that it is
       * compatible with the bss_sock bio */ 

      /* called when the connection is initially made
       *  callback(BIO,state,ret);  The callback should return
       * 'ret'.  state is for compatibility with the ssl info_callback */
      int (*info_callback)(const BIO *bio,int state,int ret);
      } BIO_CONNECT;

static int conn_write(BIO *h, const char *buf, int num);
static int conn_read(BIO *h, char *buf, int size);
static int conn_puts(BIO *h, const char *str);
static long conn_ctrl(BIO *h, int cmd, long arg1, void *arg2);
static int conn_new(BIO *h);
static int conn_free(BIO *data);
static long conn_callback_ctrl(BIO *h, int cmd, bio_info_cb *);

static int conn_state(BIO *b, BIO_CONNECT *c);
static void conn_close_socket(BIO *data);
BIO_CONNECT *BIO_CONNECT_new(void );
void BIO_CONNECT_free(BIO_CONNECT *a);

static BIO_METHOD methods_connectp=
      {
      BIO_TYPE_CONNECT,
      "socket connect",
      conn_write,
      conn_read,
      conn_puts,
      NULL, /* connect_gets, */
      conn_ctrl,
      conn_new,
      conn_free,
      conn_callback_ctrl,
      };

static int conn_state(BIO *b, BIO_CONNECT *c)
      {
      int ret= -1,i;
      unsigned long l;
      char *p,*q;
      int (*cb)(const BIO *,int,int)=NULL;

      if (c->info_callback != NULL)
            cb=c->info_callback;

      for (;;)
            {
            switch (c->state)
                  {
            case BIO_CONN_S_BEFORE:
                  p=c->param_hostname;
                  if (p == NULL)
                        {
                        BIOerr(BIO_F_CONN_STATE,BIO_R_NO_HOSTNAME_SPECIFIED);
                        goto exit_loop;
                        }
                  for ( ; *p != '\0'; p++)
                        {
                        if ((*p == ':') || (*p == '/')) break;
                        }

                  i= *p;
                  if ((i == ':') || (i == '/'))
                        {

                        *(p++)='\0';
                        if (i == ':')
                              {
                              for (q=p; *q; q++)
                                    if (*q == '/')
                                          {
                                          *q='\0';
                                          break;
                                          }
                              if (c->param_port != NULL)
                                    OPENSSL_free(c->param_port);
                              c->param_port=BUF_strdup(p);
                              }
                        }

                  if (c->param_port == NULL)
                        {
                        BIOerr(BIO_F_CONN_STATE,BIO_R_NO_PORT_SPECIFIED);
                        ERR_add_error_data(2,"host=",c->param_hostname);
                        goto exit_loop;
                        }
                  c->state=BIO_CONN_S_GET_IP;
                  break;

            case BIO_CONN_S_GET_IP:
                  if (BIO_get_host_ip(c->param_hostname,&(c->ip[0])) <= 0)
                        goto exit_loop;
                  c->state=BIO_CONN_S_GET_PORT;
                  break;

            case BIO_CONN_S_GET_PORT:
                  if (c->param_port == NULL)
                        {
                        /* abort(); */
                        goto exit_loop;
                        }
                  else if (BIO_get_port(c->param_port,&c->port) <= 0)
                        goto exit_loop;
                  c->state=BIO_CONN_S_CREATE_SOCKET;
                  break;

            case BIO_CONN_S_CREATE_SOCKET:
                  /* now setup address */
                  memset((char *)&c->them,0,sizeof(c->them));
                  c->them.sin_family=AF_INET;
                  c->them.sin_port=htons((unsigned short)c->port);
                  l=(unsigned long)
                        ((unsigned long)c->ip[0]<<24L)|
                        ((unsigned long)c->ip[1]<<16L)|
                        ((unsigned long)c->ip[2]<< 8L)|
                        ((unsigned long)c->ip[3]);
                  c->them.sin_addr.s_addr=htonl(l);
                  c->state=BIO_CONN_S_CREATE_SOCKET;

                  ret=socket(AF_INET,SOCK_STREAM,SOCKET_PROTOCOL);
                  if (ret == INVALID_SOCKET)
                        {
                        SYSerr(SYS_F_SOCKET,get_last_socket_error());
                        ERR_add_error_data(4,"host=",c->param_hostname,
                              ":",c->param_port);
                        BIOerr(BIO_F_CONN_STATE,BIO_R_UNABLE_TO_CREATE_SOCKET);
                        goto exit_loop;
                        }
                  b->num=ret;
                  c->state=BIO_CONN_S_NBIO;
                  break;

            case BIO_CONN_S_NBIO:
                  if (c->nbio)
                        {
                        if (!BIO_socket_nbio(b->num,1))
                              {
                              BIOerr(BIO_F_CONN_STATE,BIO_R_ERROR_SETTING_NBIO);
                              ERR_add_error_data(4,"host=",
                                    c->param_hostname,
                                    ":",c->param_port);
                              goto exit_loop;
                              }
                        }
                  c->state=BIO_CONN_S_CONNECT;

#if defined(SO_KEEPALIVE) && !defined(OPENSSL_SYS_MPE)
                  i=1;
                  i=setsockopt(b->num,SOL_SOCKET,SO_KEEPALIVE,(char *)&i,sizeof(i));
                  if (i < 0)
                        {
                        SYSerr(SYS_F_SOCKET,get_last_socket_error());
                        ERR_add_error_data(4,"host=",c->param_hostname,
                              ":",c->param_port);
                        BIOerr(BIO_F_CONN_STATE,BIO_R_KEEPALIVE);
                        goto exit_loop;
                        }
#endif
                  break;

            case BIO_CONN_S_CONNECT:
                  BIO_clear_retry_flags(b);
                  ret=connect(b->num,
                        (struct sockaddr *)&c->them,
                        sizeof(c->them));
                  b->retry_reason=0;
                  if (ret < 0)
                        {
                        if (BIO_sock_should_retry(ret))
                              {
                              BIO_set_retry_special(b);
                              c->state=BIO_CONN_S_BLOCKED_CONNECT;
                              b->retry_reason=BIO_RR_CONNECT;
                              }
                        else
                              {
                              SYSerr(SYS_F_CONNECT,get_last_socket_error());
                              ERR_add_error_data(4,"host=",
                                    c->param_hostname,
                                    ":",c->param_port);
                              BIOerr(BIO_F_CONN_STATE,BIO_R_CONNECT_ERROR);
                              }
                        goto exit_loop;
                        }
                  else
                        c->state=BIO_CONN_S_OK;
                  break;

            case BIO_CONN_S_BLOCKED_CONNECT:
                  i=BIO_sock_error(b->num);
                  if (i)
                        {
                        BIO_clear_retry_flags(b);
                        SYSerr(SYS_F_CONNECT,i);
                        ERR_add_error_data(4,"host=",
                              c->param_hostname,
                              ":",c->param_port);
                        BIOerr(BIO_F_CONN_STATE,BIO_R_NBIO_CONNECT_ERROR);
                        ret=0;
                        goto exit_loop;
                        }
                  else
                        c->state=BIO_CONN_S_OK;
                  break;

            case BIO_CONN_S_OK:
                  ret=1;
                  goto exit_loop;
            default:
                  /* abort(); */
                  goto exit_loop;
                  }

            if (cb != NULL)
                  {
                  if (!(ret=cb((BIO *)b,c->state,ret)))
                        goto end;
                  }
            }

      /* Loop does not exit */
exit_loop:
      if (cb != NULL)
            ret=cb((BIO *)b,c->state,ret);
end:
      return(ret);
      }

BIO_CONNECT *BIO_CONNECT_new(void)
      {
      BIO_CONNECT *ret;

      if ((ret=(BIO_CONNECT *)OPENSSL_malloc(sizeof(BIO_CONNECT))) == NULL)
            return(NULL);
      ret->state=BIO_CONN_S_BEFORE;
      ret->param_hostname=NULL;
      ret->param_port=NULL;
      ret->info_callback=NULL;
      ret->nbio=0;
      ret->ip[0]=0;
      ret->ip[1]=0;
      ret->ip[2]=0;
      ret->ip[3]=0;
      ret->port=0;
      memset((char *)&ret->them,0,sizeof(ret->them));
      return(ret);
      }

void BIO_CONNECT_free(BIO_CONNECT *a)
      {
      if(a == NULL)
          return;

      if (a->param_hostname != NULL)
            OPENSSL_free(a->param_hostname);
      if (a->param_port != NULL)
            OPENSSL_free(a->param_port);
      OPENSSL_free(a);
      }

BIO_METHOD *BIO_s_connect(void)
      {
      return(&methods_connectp);
      }

static int conn_new(BIO *bi)
      {
      bi->init=0;
      bi->num=INVALID_SOCKET;
      bi->flags=0;
      if ((bi->ptr=(char *)BIO_CONNECT_new()) == NULL)
            return(0);
      else
            return(1);
      }

static void conn_close_socket(BIO *bio)
      {
      BIO_CONNECT *c;

      c=(BIO_CONNECT *)bio->ptr;
      if (bio->num != INVALID_SOCKET)
            {
            /* Only do a shutdown if things were established */
            if (c->state == BIO_CONN_S_OK)
                  shutdown(bio->num,2);
            closesocket(bio->num);
            bio->num=INVALID_SOCKET;
            }
      }

static int conn_free(BIO *a)
      {
      BIO_CONNECT *data;

      if (a == NULL) return(0);
      data=(BIO_CONNECT *)a->ptr;
       
      if (a->shutdown)
            {
            conn_close_socket(a);
            BIO_CONNECT_free(data);
            a->ptr=NULL;
            a->flags=0;
            a->init=0;
            }
      return(1);
      }
      
static int conn_read(BIO *b, char *out, int outl)
      {
      int ret=0;
      BIO_CONNECT *data;

      data=(BIO_CONNECT *)b->ptr;
      if (data->state != BIO_CONN_S_OK)
            {
            ret=conn_state(b,data);
            if (ret <= 0)
                        return(ret);
            }

      if (out != NULL)
            {
            clear_socket_error();
            ret=readsocket(b->num,out,outl);
            BIO_clear_retry_flags(b);
            if (ret <= 0)
                  {
                  if (BIO_sock_should_retry(ret))
                        BIO_set_retry_read(b);
                  }
            }
      return(ret);
      }

static int conn_write(BIO *b, const char *in, int inl)
      {
      int ret;
      BIO_CONNECT *data;

      data=(BIO_CONNECT *)b->ptr;
      if (data->state != BIO_CONN_S_OK)
            {
            ret=conn_state(b,data);
            if (ret <= 0) return(ret);
            }

      clear_socket_error();
      ret=writesocket(b->num,in,inl);
      BIO_clear_retry_flags(b);
      if (ret <= 0)
            {
            if (BIO_sock_should_retry(ret))
                  BIO_set_retry_write(b);
            }
      return(ret);
      }

static long conn_ctrl(BIO *b, int cmd, long num, void *ptr)
      {
      BIO *dbio;
      int *ip;
      const char **pptr;
      long ret=1;
      BIO_CONNECT *data;

      data=(BIO_CONNECT *)b->ptr;

      switch (cmd)
            {
      case BIO_CTRL_RESET:
            ret=0;
            data->state=BIO_CONN_S_BEFORE;
            conn_close_socket(b);
            b->flags=0;
            break;
      case BIO_C_DO_STATE_MACHINE:
            /* use this one to start the connection */
            if (data->state != BIO_CONN_S_OK)
                  ret=(long)conn_state(b,data);
            else
                  ret=1;
            break;
      case BIO_C_GET_CONNECT:
            if (ptr != NULL)
                  {
                  pptr=(const char **)ptr;
                  if (num == 0)
                        {
                        *pptr=data->param_hostname;

                        }
                  else if (num == 1)
                        {
                        *pptr=data->param_port;
                        }
                  else if (num == 2)
                        {
                        *pptr= (char *)&(data->ip[0]);
                        }
                  else if (num == 3)
                        {
                        *((int *)ptr)=data->port;
                        }
                  if ((!b->init) || (ptr == NULL))
                        *pptr="not initialized";
                  ret=1;
                  }
            break;
      case BIO_C_SET_CONNECT:
            if (ptr != NULL)
                  {
                  b->init=1;
                  if (num == 0)
                        {
                        if (data->param_hostname != NULL)
                              OPENSSL_free(data->param_hostname);
                        data->param_hostname=BUF_strdup(ptr);
                        }
                  else if (num == 1)
                        {
                        if (data->param_port != NULL)
                              OPENSSL_free(data->param_port);
                        data->param_port=BUF_strdup(ptr);
                        }
                  else if (num == 2)
                        {
                        char buf[16];
                        unsigned char *p = ptr;

                        BIO_snprintf(buf,sizeof buf,"%d.%d.%d.%d",
                                   p[0],p[1],p[2],p[3]);
                        if (data->param_hostname != NULL)
                              OPENSSL_free(data->param_hostname);
                        data->param_hostname=BUF_strdup(buf);
                        memcpy(&(data->ip[0]),ptr,4);
                        }
                  else if (num == 3)
                        {
                        char buf[DECIMAL_SIZE(int)+1];

                        BIO_snprintf(buf,sizeof buf,"%d",*(int *)ptr);
                        if (data->param_port != NULL)
                              OPENSSL_free(data->param_port);
                        data->param_port=BUF_strdup(buf);
                        data->port= *(int *)ptr;
                        }
                  }
            break;
      case BIO_C_SET_NBIO:
            data->nbio=(int)num;
            break;
      case BIO_C_GET_FD:
            if (b->init)
                  {
                  ip=(int *)ptr;
                  if (ip != NULL)
                        *ip=b->num;
                  ret=b->num;
                  }
            else
                  ret= -1;
            break;
      case BIO_CTRL_GET_CLOSE:
            ret=b->shutdown;
            break;
      case BIO_CTRL_SET_CLOSE:
            b->shutdown=(int)num;
            break;
      case BIO_CTRL_PENDING:
      case BIO_CTRL_WPENDING:
            ret=0;
            break;
      case BIO_CTRL_FLUSH:
            break;
      case BIO_CTRL_DUP:
            {
            dbio=(BIO *)ptr;
            if (data->param_port)
                  BIO_set_conn_port(dbio,data->param_port);
            if (data->param_hostname)
                  BIO_set_conn_hostname(dbio,data->param_hostname);
            BIO_set_nbio(dbio,data->nbio);
            /* FIXME: the cast of the function seems unlikely to be a good idea */
                (void)BIO_set_info_callback(dbio,(bio_info_cb *)data->info_callback);
            }
            break;
      case BIO_CTRL_SET_CALLBACK:
            {
#if 0 /* FIXME: Should this be used?  -- Richard Levitte */
            BIOerr(BIO_F_CONN_CTRL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
            ret = -1;
#else
            ret=0;
#endif
            }
            break;
      case BIO_CTRL_GET_CALLBACK:
            {
            int (**fptr)(const BIO *bio,int state,int xret);

            fptr=(int (**)(const BIO *bio,int state,int xret))ptr;
            *fptr=data->info_callback;
            }
            break;
      default:
            ret=0;
            break;
            }
      return(ret);
      }

static long conn_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
      {
      long ret=1;
      BIO_CONNECT *data;

      data=(BIO_CONNECT *)b->ptr;

      switch (cmd)
            {
      case BIO_CTRL_SET_CALLBACK:
            {
            data->info_callback=(int (*)(const struct bio_st *, int, int))fp;
            }
            break;
      default:
            ret=0;
            break;
            }
      return(ret);
      }

static int conn_puts(BIO *bp, const char *str)
      {
      int n,ret;

      n=strlen(str);
      ret=conn_write(bp,str,n);
      return(ret);
      }

BIO *BIO_new_connect(char *str)
      {
      BIO *ret;

      ret=BIO_new(BIO_s_connect());
      if (ret == NULL) return(NULL);
      if (BIO_set_conn_hostname(ret,str))
            return(ret);
      else
            {
            BIO_free(ret);
            return(NULL);
            }
      }

#endif


Generated by  Doxygen 1.6.0   Back to index