/* crypto/bio/bss_acpt.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 typedef struct bio_accept_st { int state; char *param_addr; int accept_sock; int accept_nbio; char *addr; int nbio; /* If 0, it means normal, if 1, do a connect on bind failure, * and if there is no-one listening, bind with SO_REUSEADDR. * If 2, always use SO_REUSEADDR. */ int bind_mode; BIO *bio_chain; } BIO_ACCEPT; static int acpt_write(BIO *h, const char *buf, int num); static int acpt_read(BIO *h, char *buf, int size); static int acpt_puts(BIO *h, const char *str); static long acpt_ctrl(BIO *h, int cmd, long arg1, void *arg2); static int acpt_new(BIO *h); static int acpt_free(BIO *data); static int acpt_state(BIO *b, BIO_ACCEPT *c); static void acpt_close_socket(BIO *data); static BIO_ACCEPT *BIO_ACCEPT_new(void ); static void BIO_ACCEPT_free(BIO_ACCEPT *a); #define ACPT_S_BEFORE 1 #define ACPT_S_GET_ACCEPT_SOCKET 2 #define ACPT_S_OK 3 static BIO_METHOD methods_acceptp= { BIO_TYPE_ACCEPT, "socket accept", acpt_write, acpt_read, acpt_puts, NULL, /* connect_gets, */ acpt_ctrl, acpt_new, acpt_free, NULL, }; BIO_METHOD *BIO_s_accept(void) { return(&methods_acceptp); } static int acpt_new(BIO *bi) { BIO_ACCEPT *ba; bi->init=0; bi->num=INVALID_SOCKET; bi->flags=0; if ((ba=BIO_ACCEPT_new()) == NULL) return(0); bi->ptr=(char *)ba; ba->state=ACPT_S_BEFORE; bi->shutdown=1; return(1); } static BIO_ACCEPT *BIO_ACCEPT_new(void) { BIO_ACCEPT *ret; if ((ret=(BIO_ACCEPT *)OPENSSL_malloc(sizeof(BIO_ACCEPT))) == NULL) return(NULL); memset(ret,0,sizeof(BIO_ACCEPT)); ret->accept_sock=INVALID_SOCKET; ret->bind_mode=BIO_BIND_NORMAL; return(ret); } static void BIO_ACCEPT_free(BIO_ACCEPT *a) { if(a == NULL) return; if (a->param_addr != NULL) OPENSSL_free(a->param_addr); if (a->addr != NULL) OPENSSL_free(a->addr); if (a->bio_chain != NULL) BIO_free(a->bio_chain); OPENSSL_free(a); } static void acpt_close_socket(BIO *bio) { BIO_ACCEPT *c; c=(BIO_ACCEPT *)bio->ptr; if (c->accept_sock != INVALID_SOCKET) { shutdown(c->accept_sock,2); closesocket(c->accept_sock); c->accept_sock=INVALID_SOCKET; bio->num=INVALID_SOCKET; } } static int acpt_free(BIO *a) { BIO_ACCEPT *data; if (a == NULL) return(0); data=(BIO_ACCEPT *)a->ptr; if (a->shutdown) { acpt_close_socket(a); BIO_ACCEPT_free(data); a->ptr=NULL; a->flags=0; a->init=0; } return(1); } static int acpt_state(BIO *b, BIO_ACCEPT *c) { BIO *bio=NULL,*dbio; int s= -1; int i; again: switch (c->state) { case ACPT_S_BEFORE: if (c->param_addr == NULL) { BIOerr(BIO_F_ACPT_STATE,BIO_R_NO_ACCEPT_PORT_SPECIFIED); return(-1); } s=BIO_get_accept_socket(c->param_addr,c->bind_mode); if (s == INVALID_SOCKET) return(-1); if (c->accept_nbio) { if (!BIO_socket_nbio(s,1)) { closesocket(s); BIOerr(BIO_F_ACPT_STATE,BIO_R_ERROR_SETTING_NBIO_ON_ACCEPT_SOCKET); return(-1); } } c->accept_sock=s; b->num=s; c->state=ACPT_S_GET_ACCEPT_SOCKET; return(1); /* break; */ case ACPT_S_GET_ACCEPT_SOCKET: if (b->next_bio != NULL) { c->state=ACPT_S_OK; goto again; } BIO_clear_retry_flags(b); b->retry_reason=0; i=BIO_accept(c->accept_sock,&(c->addr)); /* -2 return means we should retry */ if(i == -2) { BIO_set_retry_special(b); b->retry_reason=BIO_RR_ACCEPT; return -1; } if (i < 0) return(i); bio=BIO_new_socket(i,BIO_CLOSE); if (bio == NULL) goto err; BIO_set_callback(bio,BIO_get_callback(b)); BIO_set_callback_arg(bio,BIO_get_callback_arg(b)); if (c->nbio) { if (!BIO_socket_nbio(i,1)) { BIOerr(BIO_F_ACPT_STATE,BIO_R_ERROR_SETTING_NBIO_ON_ACCEPTED_SOCKET); goto err; } } /* If the accept BIO has an bio_chain, we dup it and * put the new socket at the end. */ if (c->bio_chain != NULL) { if ((dbio=BIO_dup_chain(c->bio_chain)) == NULL) goto err; if (!BIO_push(dbio,bio)) goto err; bio=dbio; } if (BIO_push(b,bio) == NULL) goto err; c->state=ACPT_S_OK; return(1); err: if (bio != NULL) BIO_free(bio); else if (s >= 0) closesocket(s); return(0); /* break; */ case ACPT_S_OK: if (b->next_bio == NULL) { c->state=ACPT_S_GET_ACCEPT_SOCKET; goto again; } return(1); /* break; */ default: return(0); /* break; */ } } static int acpt_read(BIO *b, char *out, int outl) { int ret=0; BIO_ACCEPT *data; BIO_clear_retry_flags(b); data=(BIO_ACCEPT *)b->ptr; while (b->next_bio == NULL) { ret=acpt_state(b,data); if (ret <= 0) return(ret); } ret=BIO_read(b->next_bio,out,outl); BIO_copy_next_retry(b); return(ret); } static int acpt_write(BIO *b, const char *in, int inl) { int ret; BIO_ACCEPT *data; BIO_clear_retry_flags(b); data=(BIO_ACCEPT *)b->ptr; while (b->next_bio == NULL) { ret=acpt_state(b,data); if (ret <= 0) return(ret); } ret=BIO_write(b->next_bio,in,inl); BIO_copy_next_retry(b); return(ret); } static long acpt_ctrl(BIO *b, int cmd, long num, void *ptr) { int *ip; long ret=1; BIO_ACCEPT *data; char **pp; data=(BIO_ACCEPT *)b->ptr; switch (cmd) { case BIO_CTRL_RESET: ret=0; data->state=ACPT_S_BEFORE; acpt_close_socket(b); b->flags=0; break; case BIO_C_DO_STATE_MACHINE: /* use this one to start the connection */ ret=(long)acpt_state(b,data); break; case BIO_C_SET_ACCEPT: if (ptr != NULL) { if (num == 0) { b->init=1; if (data->param_addr != NULL) OPENSSL_free(data->param_addr); data->param_addr=BUF_strdup(ptr); } else if (num == 1) { data->accept_nbio=(ptr != NULL); } else if (num == 2) { if (data->bio_chain != NULL) BIO_free(data->bio_chain); data->bio_chain=(BIO *)ptr; } } break; case BIO_C_SET_NBIO: data->nbio=(int)num; break; case BIO_C_SET_FD: b->init=1; b->num= *((int *)ptr); data->accept_sock=b->num; data->state=ACPT_S_GET_ACCEPT_SOCKET; b->shutdown=(int)num; b->init=1; break; case BIO_C_GET_FD: if (b->init) { ip=(int *)ptr; if (ip != NULL) *ip=data->accept_sock; ret=data->accept_sock; } else ret= -1; break; case BIO_C_GET_ACCEPT: if (b->init) { if (ptr != NULL) { pp=(char **)ptr; *pp=data->param_addr; } else ret= -1; } 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_C_SET_BIND_MODE: data->bind_mode=(int)num; break; case BIO_C_GET_BIND_MODE: ret=(long)data->bind_mode; break; case BIO_CTRL_DUP: /* dbio=(BIO *)ptr; if (data->param_port) EAY EAY BIO_set_port(dbio,data->param_port); if (data->param_hostname) BIO_set_hostname(dbio,data->param_hostname); BIO_set_nbio(dbio,data->nbio); */ break; default: ret=0; break; } return(ret); } static int acpt_puts(BIO *bp, const char *str) { int n,ret; n=strlen(str); ret=acpt_write(bp,str,n); return(ret); } BIO *BIO_new_accept(char *str) { BIO *ret; ret=BIO_new(BIO_s_accept()); if (ret == NULL) return(NULL); if (BIO_set_accept_port(ret,str)) return(ret); else { BIO_free(ret); return(NULL); } } #endif