Commit eb31944f authored by cvs2svn's avatar cvs2svn
Browse files

This commit was manufactured by cvs2svn to create branch 'BRANCH_engine'.

parents 0dcf7fd5 ea4e4149
Loading
Loading
Loading
Loading

crypto/dso/dso_vms.c

0 → 100644
+364 −0
Original line number Diff line number Diff line
/* dso_vms.c */
/* Written by Richard Levitte (richard@levitte.org) for the OpenSSL
 * project 2000.
 */
/* ====================================================================
 * Copyright (c) 2000 The OpenSSL Project.  All rights reserved.
 *
 * 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 above 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 acknowledgment:
 *    "This product includes software developed by the OpenSSL Project
 *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
 *
 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    licensing@OpenSSL.org.
 *
 * 5. Products derived from this software may not be called "OpenSSL"
 *    nor may "OpenSSL" appear in their names without prior written
 *    permission of the OpenSSL Project.
 *
 * 6. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by the OpenSSL Project
 *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
 *
 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
 * EXPRESSED 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 OpenSSL PROJECT OR
 * ITS 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.
 * ====================================================================
 *
 * This product includes cryptographic software written by Eric Young
 * (eay@cryptsoft.com).  This product includes software written by Tim
 * Hudson (tjh@cryptsoft.com).
 *
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#ifdef VMS
#include <lib$routines.h>
#include <libfisdef.h>
#include <stsdef.h>
#include <descrip.h>
#include <starlet.h>
#endif
#include "cryptlib.h"
#include <openssl/dso.h>

#ifndef VMS
DSO_METHOD *DSO_METHOD_vms(void)
	{
	return NULL;
	}
#else
#pragma message disable DOLLARID

static int vms_load(DSO *dso, const char *filename);
static int vms_unload(DSO *dso);
static void *vms_bind_var(DSO *dso, const char *symname);
static DSO_FUNC_TYPE vms_bind_func(DSO *dso, const char *symname);
#if 0
static int vms_unbind_var(DSO *dso, char *symname, void *symptr);
static int vms_unbind_func(DSO *dso, char *symname, DSO_FUNC_TYPE symptr);
static int vms_init(DSO *dso);
static int vms_finish(DSO *dso);
#endif
static long vms_ctrl(DSO *dso, int cmd, long larg, void *parg);

static DSO_METHOD dso_meth_vms = {
	"OpenSSL 'VMS' shared library method",
	vms_load,
	NULL, /* unload */
	vms_bind_var,
	vms_bind_func,
/* For now, "unbind" doesn't exist */
#if 0
	NULL, /* unbind_var */
	NULL, /* unbind_func */
#endif
	vms_ctrl,
	NULL, /* init */
	NULL  /* finish */
	};

/* On VMS, the only "handle" is the file name.  LIB$FIND_IMAGE_SYMBOL depends
 * on the reference to the file name being the same for all calls regarding
 * one shared image, so we'll just store it in an instance of the following
 * structure and put a pointer to that instance in the meth_data stack.
 */
typedef struct dso_internal_st
	{
	/* This should contain the name only, no directory,
	 * no extension, nothing but a name. */
	struct dsc$descriptor_s filename_dsc;
	char filename[FILENAME_MAX+1];
	/* This contains whatever is not in filename, if needed.
	 * Normally not defined. */
	struct dsc$descriptor_s imagename_dsc;
	char imagename[FILENAME_MAX+1];
	} DSO_VMS_INTERNAL;


DSO_METHOD *DSO_METHOD_vms(void)
	{
	return(&dso_meth_vms);
	}

static int vms_load(DSO *dso, const char *filename)
	{
	DSO_VMS_INTERNAL *p;
	const char *sp1, *sp2;	/* Search result */

	/* A file specification may look like this:
	 *
	 *	node::dev:[dir-spec]name.type;ver
	 *
	 * or (for compatibility with TOPS-20):
	 *
	 *	node::dev:<dir-spec>name.type;ver
	 *
	 * and the dir-spec uses '.' as separator.  Also, a dir-spec
	 * may consist of several parts, with mixed use of [] and <>:
	 *
	 *	[dir1.]<dir2>
	 *
	 * We need to split the file specification into the name and
	 * the rest (both before and after the name itself).
	 */
	/* Start with trying to find the end of a dir-spec, and save the
	   position of the byte after in sp1 */
	sp1 = strrchr(filename, ']');
	sp2 = strrchr(filename, '>');
	if (sp1 == NULL) sp1 = sp2;
	if (sp2 != NULL && sp2 > sp1) sp1 = sp2;
	if (sp1 == NULL) sp1 = strrchr(filename, ':');
	if (sp1 == NULL)
		sp1 = filename;
	else
		sp1++;		/* The byte after the found character */
	/* Now, let's see if there's a type, and save the position in sp2 */
	sp2 = strchr(sp1, '.');
	/* If we found it, that's where we'll cut.  Otherwise, look for a
	   version number and save the position in sp2 */
	if (sp2 == NULL) sp2 = strchr(sp1, ';');
	/* If there was still nothing to find, set sp2 to point at the end of
	   the string */
	if (sp2 == NULL) sp2 = sp1 + strlen(sp1);

	/* Check that we won't get buffer overflows */
	if (sp2 - sp1 > FILENAME_MAX
		|| (sp1 - filename) + strlen(sp2) > FILENAME_MAX)
		{
		DSOerr(DSO_F_VMS_LOAD,DSO_R_FILENAME_TOO_BIG);
		return(0);
		}

	p = (DSO_VMS_INTERNAL *)OPENSSL_malloc(sizeof(DSO_VMS_INTERNAL));
	if(p == NULL)
		{
		DSOerr(DSO_F_VMS_LOAD,ERR_R_MALLOC_FAILURE);
		return(0);
		}

	strncpy(p->filename, sp1, sp2-sp1);
	p->filename[sp2-sp1] = '\0';

	strncpy(p->imagename, filename, sp1-filename);
	p->imagename[sp1-filename] = '\0';
	strcat(p->imagename, sp2);

	p->filename_dsc.dsc$w_length = strlen(p->filename);
	p->filename_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
	p->filename_dsc.dsc$b_class = DSC$K_CLASS_S;
	p->filename_dsc.dsc$a_pointer = p->filename;
	p->imagename_dsc.dsc$w_length = strlen(p->imagename);
	p->imagename_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
	p->imagename_dsc.dsc$b_class = DSC$K_CLASS_S;
	p->imagename_dsc.dsc$a_pointer = p->imagename;

	if(!sk_push(dso->meth_data, (char *)p))
		{
		DSOerr(DSO_F_VMS_LOAD,DSO_R_STACK_ERROR);
		OPENSSL_free(p);
		return(0);
		}
	return(1);
	}

/* Note that this doesn't actually unload the shared image, as there is no
 * such thing in VMS.  Next time it get loaded again, a new copy will
 * actually be loaded.
 */
static int vms_unload(DSO *dso)
	{
	DSO_VMS_INTERNAL *p;
	if(dso == NULL)
		{
		DSOerr(DSO_F_VMS_UNLOAD,ERR_R_PASSED_NULL_PARAMETER);
		return(0);
		}
	if(sk_num(dso->meth_data) < 1)
		return(1);
	p = (DSO_VMS_INTERNAL *)sk_pop(dso->meth_data);
	if(p == NULL)
		{
		DSOerr(DSO_F_VMS_UNLOAD,DSO_R_NULL_HANDLE);
		return(0);
		}
	/* Cleanup */
	OPENSSL_free(p);
	return(1);
	}

/* We must do this in a separate function because of the way the exception
   handler works (it makes this function return */
static int do_find_symbol(DSO_VMS_INTERNAL *ptr,
	struct dsc$descriptor_s *symname_dsc, void **sym,
	unsigned long flags)
	{
	/* Make sure that signals are caught and returned instead of
	   aborting the program.  The exception handler gets unestablished
	   automatically on return from this function.  */
	lib$establish(lib$sig_to_ret);

	if(ptr->imagename_dsc.dsc$w_length)
		return lib$find_image_symbol(&ptr->filename_dsc,
			symname_dsc, sym,
			&ptr->imagename_dsc, flags);
	else
		return lib$find_image_symbol(&ptr->filename_dsc,
			symname_dsc, sym,
			0, flags);
	}

static void *vms_bind_sym(DSO *dso, const char *symname)
	{
	DSO_VMS_INTERNAL *ptr;
	void *sym = 0;
	int status;
	int flags = LIB$M_FIS_MIXEDCASE;
	struct dsc$descriptor_s symname_dsc;

	symname_dsc.dsc$w_length = strlen(symname);
	symname_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
	symname_dsc.dsc$b_class = DSC$K_CLASS_S;
	symname_dsc.dsc$a_pointer = (char *)symname; /* The cast is needed */

	if((dso == NULL) || (symname == NULL))
		{
		DSOerr(DSO_F_VMS_BIND_VAR,ERR_R_PASSED_NULL_PARAMETER);
		return(NULL);
		}
	if(sk_num(dso->meth_data) < 1)
		{
		DSOerr(DSO_F_VMS_BIND_VAR,DSO_R_STACK_ERROR);
		return(NULL);
		}
	ptr = (DSO_VMS_INTERNAL *)sk_value(dso->meth_data,
		sk_num(dso->meth_data) - 1);
	if(ptr == NULL)
		{
		DSOerr(DSO_F_VMS_BIND_VAR,DSO_R_NULL_HANDLE);
		return(NULL);
		}

	if(dso->flags & DSO_FLAG_UPCASE_SYMBOL) flags = 0;

	status = do_find_symbol(ptr, &symname_dsc, &sym, flags);

	if(!$VMS_STATUS_SUCCESS(status))
		{
		unsigned short length;
		char errstring[257];
		struct dsc$descriptor_s errstring_dsc;

		errstring_dsc.dsc$w_length = sizeof(errstring);
		errstring_dsc.dsc$b_dtype = DSC$K_DTYPE_T;
		errstring_dsc.dsc$b_class = DSC$K_CLASS_S;
		errstring_dsc.dsc$a_pointer = errstring;

		status = sys$getmsg(status, &length, &errstring_dsc, 1, 0);

		if (!$VMS_STATUS_SUCCESS(status))
			lib$signal(status); /* This is really bad.  Abort!  */
		else
			{
			errstring[length] = '\0';

			DSOerr(DSO_F_VMS_BIND_VAR,DSO_R_SYM_FAILURE);
			if (ptr->imagename_dsc.dsc$w_length)
				ERR_add_error_data(9,
					"Symbol ", symname,
					" in ", ptr->filename,
					" (", ptr->imagename, ")",
					": ", errstring);
			else
				ERR_add_error_data(6,
					"Symbol ", symname,
					" in ", ptr->filename,
					": ", errstring);
			}
		return(NULL);
		}
	return(sym);
	}

static void *vms_bind_var(DSO *dso, const char *symname)
	{
	return vms_bind_sym(dso, symname);
	}

static DSO_FUNC_TYPE vms_bind_func(DSO *dso, const char *symname)
	{
	return (DSO_FUNC_TYPE)vms_bind_sym(dso, symname);
	}

static long vms_ctrl(DSO *dso, int cmd, long larg, void *parg)
        {
        if(dso == NULL)
                {
                DSOerr(DSO_F_VMS_CTRL,ERR_R_PASSED_NULL_PARAMETER);
                return(-1);
                }
        switch(cmd)
                {
        case DSO_CTRL_GET_FLAGS:
                return dso->flags;
        case DSO_CTRL_SET_FLAGS:
                dso->flags = (int)larg;
                return(0);
        case DSO_CTRL_OR_FLAGS:
                dso->flags |= (int)larg;
                return(0);
        default:
                break;
                }
        DSOerr(DSO_F_VMS_CTRL,DSO_R_UNKNOWN_COMMAND);
        return(-1);
        }

#endif /* VMS */
+38 −0
Original line number Diff line number Diff line
=pod

=head1 NAME

 BIO_ctrl_get_read_request - Find out how much bytes are were requested from the BIO

=head1 SYNOPSIS

 #include <openssl/bio.h>

 size_t BIO_ctrl_get_read_request(BIO *bio);

=head1 DESCRIPTION

BIO_ctrl_get_read_request() returns the number of bytes that were last
requested from B<bio> by a BIO_read() operation. This is useful e.g. for
BIO pairs, so that the application knows how much bytes to supply to B<bio>.

=head1 BUGS

When B<bio> is NULL, the OpenSSL library calls assert().

=head1 RETURN VALUES

The following return values can occur:

=over 4

=item E<gt>=0

The number of bytes requested.

=back

=head1 SEE ALSO

L<bio(3)|bio(3)>, L<BIO_s_mem(3)|BIO_s_mem(3)>,
L<BIO_new_bio_pair(3)|BIO_new_bio_pair(3)>
+36 −0
Original line number Diff line number Diff line
=pod

=head1 NAME

BIO_ctrl_pending - Find out how much bytes are buffered in a BIO

=head1 SYNOPSIS

 #include <openssl/bio.h>

 size_t BIO_ctrl_pending(BIO *bio);

=head1 DESCRIPTION

BIO_ctrl_pending() returns the number of bytes buffered in a BIO.

=head1 BUGS

When B<bio> is NULL, the OpenSSL library calls assert().

=head1 RETURN VALUES

The following return values can occur:

=over 4

=item E<gt>=0

The number of bytes pending the BIO.

=back

=head1 SEE ALSO

L<bio(3)|bio(3)>, L<BIO_s_mem(3)|BIO_s_mem(3)>,
L<BIO_new_bio_pair(3)|BIO_new_bio_pair(3)>
+102 −0
Original line number Diff line number Diff line
=pod

=head1 NAME

BIO_new_bio_pair - create a new BIO pair

=head1 SYNOPSIS

 #include <openssl/bio.h>

 int BIO_new_bio_pair(BIO **bio1, size_t writebuf1, BIO **bio2, size_t writebuf2);

=head1 DESCRIPTION

BIO_new_bio_pair() creates a buffering BIO pair. It has two endpoints between
data can be buffered. Its typical use is to connect one endpoint as underlying
input/output BIO to an SSL and access the other one controlled by the program
instead of accessing the network connection directly.

The two new BIOs B<bio1> and B<bio2> are symmetric with respect to their
functionality. The size of their buffers is determined by B<writebuf1> and
B<writebuf2>. If the size give is 0, the default size is used.

BIO_new_bio_pair() does not check whether B<bio1> or B<bio2> do point to
some other BIO, the values are overwritten, BIO_free() is not called.

The two BIOs, even though forming a BIO pair and must be BIO_free()'ed
seperately. This can be of importance, as some SSL-functions like SSL_set_bio()
or SSL_free() call BIO_free() implicitely, so that the peer-BIO is left
untouched and must also be BIO_free()'ed.

=head1 EXAMPLE

The BIO pair can be used to have full control over the network access of an
application. The application can call select() on the socket as required
without having to go through the SSL-interface.

 BIO *internal_bio, *network_bio;
 ...
 BIO_new_bio_pair(internal_bio, 0, network_bio, 0);
 SSL_set_bio(ssl, internal_bio);
 SSL_operations();
 ...

 application |   TLS-engine
    |        |
    +----------> SSL_operations()
             |     /\    ||
             |     ||    \/
             |   BIO-pair (internal_bio)
    +----------< BIO-pair (network_bio)
    |        |
  socket     |

  ...
  SSL_free(ssl);		/* implicitely frees internal_bio */
  BIO_free(network_bio);
  ...

As the BIO pair will only buffer the data and never directly access the
connection, it behaves non-blocking and will return as soon as the write
buffer is full or the read buffer is drained. Then the application has to
flush the write buffer and/or fill the read buffer.

Use the BIO_ctrl_pending(), to find out whether data is buffered in the BIO
and must be transfered to the network. Use BIO_ctrl_get_read_request() to
find out, how many bytes must be written into the buffer before the
SSL_operation() can successfully be continued.

=head1 IMPORTANT

As the data is buffered, SSL_operation() may return with a ERROR_SSL_WANT_READ
condition, but there is still data in the write buffer. An application must
not rely on the error value of SSL_operation() but must assure that the
write buffer is always flushed first. Otherwise a deadlock may occur as
the peer might be waiting for the data before being able to continue.

=head1 RETURN VALUES

The following return values can occur:

=over 4

=item 1

The BIO pair was created successfully. The new BIOs are available in
B<bio1> and B<bio2>.

=item 0

The operation failed. The NULL pointer is stored into the locations for
B<bio1> and B<bio2>. Check the error stack for more information.

=back

=head1 SEE ALSO

L<SSL_set_bio(3)|SSL_set_bio(3)>, L<ssl(3)|ssl(3)>, L<bio(3)|bio(3)>,
L<BIO_ctrl_pending(3)|BIO_ctrl_pending(3)>,
L<BIO_ctrl_get_read_request(3)|BIO_ctrl_get_read_request(3)>

=cut
+182 −0
Original line number Diff line number Diff line
=pod

=head1 NAME

BIO_s_accept, BIO_set_nbio, BIO_set_accept_port, BIO_get_accept_port,
BIO_set_nbio_accept, BIO_set_accept_bios, BIO_set_bind_mode,
BIO_get_bind_mode, BIO_do_accept - accept BIO

=head1 SYNOPSIS

 #include <openssl/bio.h>

 BIO_METHOD * BIO_s_accept(void);

 #define BIO_set_accept_port(b,name) BIO_ctrl(b,BIO_C_SET_ACCEPT,0,(char *)name)
 #define BIO_get_accept_port(b)	BIO_ptr_ctrl(b,BIO_C_GET_ACCEPT,0)

 BIO *BIO_new_accept(char *host_port);

 #define BIO_set_nbio_accept(b,n) BIO_ctrl(b,BIO_C_SET_ACCEPT,1,(n)?"a":NULL)
 #define BIO_set_accept_bios(b,bio) BIO_ctrl(b,BIO_C_SET_ACCEPT,2,(char *)bio)

 #define BIO_set_bind_mode(b,mode) BIO_ctrl(b,BIO_C_SET_BIND_MODE,mode,NULL)
 #define BIO_get_bind_mode(b,mode) BIO_ctrl(b,BIO_C_GET_BIND_MODE,0,NULL)

 #define BIO_BIND_NORMAL		0
 #define BIO_BIND_REUSEADDR_IF_UNUSED	1
 #define BIO_BIND_REUSEADDR		2

 #define BIO_do_accept(b)	BIO_do_handshake(b)

=head1 DESCRIPTION

BIO_s_accept() returns the accept BIO method. This is a wrapper
round the platform's TCP/IP socket accept routines.

Using accept BIOs TCP/IP connections can be accepted and data
transferred using only BIO routines. In this way any platform
specific operations are hidden by the BIO abstraction.

Read and write operations on an accept BIO will perform I/O
on the underlying connection. If no connection is established
and the port (see below) is set up properly then the BIO
waits for an incoming connection.

Accept BIOs support BIO_puts() but not BIO_gets().

If the close flag is set on an accept BIO then any active
connection on that chain is shutdown and the socket closed when
the BIO is freed.

Calling BIO_reset() on a accept BIO will close any active
connection and reset the BIO into a state where it awaits another
incoming connection.

BIO_get_fd() and BIO_set_fd() can be called to retrieve or set
the accept socket. See L<BIO_s_fd(3)|BIO_s_fd(3)>

BIO_set_accept_port() uses the string B<name> to set the accept
port. The port is represented as a string of the form "host:port",
where "host" is the interface to use and "port" is the port.
Either or both values can be "*" which is interpreted as meaning
any interface or port respectively. "port" has the same syntax
as the port specified in BIO_set_conn_port() for connect BIOs,
that is it can be a numerical port string or a string to lookup
using getservbyname() and a string table.

BIO_new_accept() combines BIO_new() and BIO_set_accept_port() into
a single call: that is it creates a new accept BIO with port
B<host_port>.

BIO_set_nbio_accept() sets the accept socket to blocking mode
(the default) if B<n> is 0 or non blocking mode if B<n> is 1.

BIO_set_accept_bios() can be used to set a chain of BIOs which
will be duplicated and prepended to the chain when an incoming
connection is received. This is useful if, for example, a 
buffering BIO is required for each connection.

BIO_set_bind_mode() and BIO_get_bind_mode() set and retrieve
the current bind mode. If BIO_BIND_NORMAL (the default) is set
then another socket cannot be bound to the same port. If
BIO_BIND_REUSEADDR is set then other sockets can bind to the
same port. If BIO_BIND_REUSEADDR_IF_UNUSED is set then and
attempt is first made to use BIO_BIN_NORMAL, if this fails
and the port is not in use then a second attempt is made
using BIO_BIND_REUSEADDR.

BIO_do_accept() serves two functions. When it is first
called, after the accept BIO has been setup, it will attempt
to create the accept socket and bind an address to it. Second
and subsequent calls to BIO_do_accept() will await an incoming
connection.

=head1 NOTES

When an accept BIO is at the end of a chain it will await an
incoming connection before processing I/O calls. When an accept
BIO is not at then end of a chain it passes I/O calls to the next
BIO in the chain.

When a connection is established a new socket BIO is created for
the conection and appended to the chain. That is the chain is now
accept->socket. This effectively means that attempting I/O on
an initial accept socket will await an incoming connection then
perform I/O on it.

If any additional BIOs have been set using BIO_set_accept_bios()
then they are placed between the socket and the accept BIO,
that is the chain will be accept->otherbios->socket.

If a server wishes to process multiple connections (as is normally
the case) then the accept BIO must be made available for further
incoming connections. This can be done by waiting for a connection and
then calling:

 connection = BIO_pop(accept);

After this call B<connection> will contain a BIO for the recently
established connection and B<accept> will now be a single BIO
again which can be used to await further incoming connections.
If no further connections will be accepted the B<accept> can
be freed using BIO_free().

If only a single connection will be processed it is possible to
perform I/O using the accept BIO itself. This is often undesirable
however because the accept BIO will still accept additional incoming
connections. This can be resolved by using BIO_pop() (see above)
and freeing up the accept BIO after the initial connection.

=head1 RETURN VALUES

TBA

=head1 EXAMPLE

This example accepts two connections on port 4444, sends messages
down each and finally closes both down.

 BIO *abio, *cbio, *cbio2;
 ERR_load_crypto_strings();
 abio = BIO_new_accept("4444");

 /* First call to BIO_accept() sets up accept BIO */
 if(BIO_do_accept(abio) <= 0) {
	fprintf(stderr, "Error setting up accept\n");
	ERR_print_errors_fp(stderr);
	exit(0);		
 }

 /* Wait for incoming connection */
 if(BIO_do_accept(abio) <= 0) {
	fprintf(stderr, "Error accepting connection\n");
	ERR_print_errors_fp(stderr);
	exit(0);		
 }
 fprintf(stderr, "Connection 1 established\n");
 /* Retrieve BIO for connection */
 cbio = BIO_pop(abio);
 BIO_puts(cbio, "Connection 1: Sending out Data on initial connection\n");
 fprintf(stderr, "Sent out data on connection 1\n");
 /* Wait for another connection */
 if(BIO_do_accept(abio) <= 0) {
	fprintf(stderr, "Error accepting connection\n");
	ERR_print_errors_fp(stderr);
	exit(0);		
 }
 fprintf(stderr, "Connection 2 established\n");
 /* Close accept BIO to refuse further connections */
 cbio2 = BIO_pop(abio);
 BIO_free(abio);
 BIO_puts(cbio2, "Connection 2: Sending out Data on second\n");
 fprintf(stderr, "Sent out data on connection 2\n");

 BIO_puts(cbio, "Connection 1: Second connection established\n");
 /* Close the two established connections */
 BIO_free(cbio);
 BIO_free(cbio2);

=head1 SEE ALSO

TBA
Loading