-
-#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,
- };
-
-BIO_METHOD *BIO_s_accept()
- {
- return(&methods_acceptp);
- }
-
-static int acpt_new(bi)
-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);
- }
-
-BIO_ACCEPT *BIO_ACCEPT_new()
- {
- BIO_ACCEPT *ret;
-
- if ((ret=(BIO_ACCEPT *)Malloc(sizeof(BIO_ACCEPT))) == NULL)
- return(NULL);
-
- memset(ret,0,sizeof(BIO_ACCEPT));
- ret->accept_sock=INVALID_SOCKET;
- return(ret);
- }
-
-void BIO_ACCEPT_free(a)
-BIO_ACCEPT *a;
- {
- if (a->param_addr != NULL) Free(a->param_addr);
- if (a->addr != NULL) Free(a->addr);
- if (a->bio_chain != NULL) BIO_free(a->bio_chain);
- Free(a);
- }
-
-static void acpt_close_socket(bio)
-BIO *bio;
- {
- BIO_ACCEPT *c;
-
- c=(BIO_ACCEPT *)bio->ptr;
- if (c->accept_sock != INVALID_SOCKET)
- {
- shutdown(c->accept_sock,2);
-# ifdef WINDOWS
- closesocket(c->accept_sock);
-# else
- close(c->accept_sock);
-# endif
- c->accept_sock=INVALID_SOCKET;
- bio->num=INVALID_SOCKET;
- }
- }
-
-static int acpt_free(a)
-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(b,c)
-BIO *b;
-BIO_ACCEPT *c;
- {
- BIO *bio=NULL,*dbio;
- unsigned long l=1;
- 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);
- if (s == INVALID_SOCKET)
- return(-1);
-
-#ifdef FIONBIO
- if (c->accept_nbio)
- {
- i=BIO_socket_ioctl(b->num,FIONBIO,&l);
- if (i < 0)
- {
-#ifdef WINDOWS
- closesocket(s);
-#else
- close(s);
-# endif
- BIOerr(BIO_F_ACPT_STATE,BIO_R_ERROR_SETTING_NBIO_ON_ACCEPT_SOCKET);
- return(-1);
- }
- }
-#endif
- 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;
- }
- i=BIO_accept(c->accept_sock,&(c->addr));
- 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));
-
-#ifdef FIONBIO
- if (c->nbio)
- {
- i=BIO_socket_ioctl(i,FIONBIO,&l);
- if (i < 0)
- {
- BIOerr(BIO_F_ACPT_STATE,BIO_R_ERROR_SETTING_NBIO_ON_ACCEPTED_SOCKET);
- goto err;
- }
- }
+ BIOerr(BIO_F_ACPT_STATE, BIO_R_UNAVAILABLE_IP_FAMILY);
+ goto exit_loop;
+ }
+ break;
+ case BIO_FAMILY_IPV4:
+ family = AF_INET;
+ break;
+ case BIO_FAMILY_IPANY:
+ family = AF_UNSPEC;
+ break;
+ default:
+ BIOerr(BIO_F_ACPT_STATE, BIO_R_UNSUPPORTED_IP_FAMILY);
+ goto exit_loop;
+ }
+ if (BIO_lookup(c->param_addr, c->param_serv, BIO_LOOKUP_SERVER,
+ family, SOCK_STREAM, &c->addr_first) == 0)
+ goto exit_loop;
+ }
+ if (c->addr_first == NULL) {
+ BIOerr(BIO_F_ACPT_STATE, BIO_R_LOOKUP_RETURNED_NOTHING);
+ goto exit_loop;
+ }
+ /* We're currently not iterating, but set this as preparation
+ * for possible future development in that regard
+ */
+ c->addr_iter = c->addr_first;
+ c->state = ACPT_S_CREATE_SOCKET;
+ break;
+
+ case ACPT_S_CREATE_SOCKET:
+ ret = BIO_socket(BIO_ADDRINFO_family(c->addr_iter),
+ BIO_ADDRINFO_socktype(c->addr_iter),
+ BIO_ADDRINFO_protocol(c->addr_iter), 0);
+ if (ret == (int)INVALID_SOCKET) {
+ SYSerr(SYS_F_SOCKET, get_last_socket_error());
+ ERR_add_error_data(4,
+ "hostname=", c->param_addr,
+ " service=", c->param_serv);
+ BIOerr(BIO_F_ACPT_STATE, BIO_R_UNABLE_TO_CREATE_SOCKET);
+ goto exit_loop;
+ }
+ c->accept_sock = ret;
+ b->num = ret;
+ c->state = ACPT_S_LISTEN;
+ break;
+
+ case ACPT_S_LISTEN:
+ {
+ if (!BIO_listen(c->accept_sock,
+ BIO_ADDRINFO_address(c->addr_iter),
+ c->bind_mode)) {
+ BIO_closesocket(c->accept_sock);
+ goto exit_loop;
+ }
+ }
+
+ {
+ union BIO_sock_info_u info;
+
+ info.addr = &c->cache_accepting_addr;
+ if (!BIO_sock_info(c->accept_sock, BIO_SOCK_INFO_ADDRESS,
+ &info)) {
+ BIO_closesocket(c->accept_sock);
+ goto exit_loop;
+ }
+ }
+
+ c->cache_accepting_name =
+ BIO_ADDR_hostname_string(&c->cache_accepting_addr, 1);
+ c->cache_accepting_serv =
+ BIO_ADDR_service_string(&c->cache_accepting_addr, 1);
+ c->state = ACPT_S_ACCEPT;
+ s = -1;
+ ret = 1;
+ goto end;
+
+ case ACPT_S_ACCEPT:
+ if (b->next_bio != NULL) {
+ c->state = ACPT_S_OK;
+ break;
+ }
+ BIO_clear_retry_flags(b);
+ b->retry_reason = 0;
+
+ s = BIO_accept_ex(c->accept_sock, &c->cache_peer_addr,
+ c->accepted_mode);
+
+ /* If the returned socket is invalid, this might still be
+ * retryable
+ */
+ if (s < 0) {
+ if (BIO_sock_should_retry(s)) {
+ BIO_set_retry_special(b);
+ b->retry_reason = BIO_RR_ACCEPT;
+ goto end;
+ }
+ }
+
+ /* If it wasn't retryable, we fail */
+ if (s < 0) {
+ ret = s;
+ goto exit_loop;
+ }
+
+ bio = BIO_new_socket(s, BIO_CLOSE);
+ if (bio == NULL)
+ goto exit_loop;
+
+ BIO_set_callback(bio, BIO_get_callback(b));
+ BIO_set_callback_arg(bio, BIO_get_callback_arg(b));
+
+ /*
+ * 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 exit_loop;
+ if (!BIO_push(dbio, bio))
+ goto exit_loop;
+ bio = dbio;
+ }
+ if (BIO_push(b, bio) == NULL)
+ goto exit_loop;
+
+ c->cache_peer_name =
+ BIO_ADDR_hostname_string(&c->cache_peer_addr, 1);
+ c->cache_peer_serv =
+ BIO_ADDR_service_string(&c->cache_peer_addr, 1);
+ c->state = ACPT_S_OK;
+ bio = NULL;
+ ret = 1;
+ goto end;
+
+ case ACPT_S_OK:
+ if (b->next_bio == NULL) {
+ c->state = ACPT_S_ACCEPT;
+ break;
+ }
+ ret = 1;
+ goto end;
+
+ default:
+ ret = 0;
+ goto end;
+ }
+ }
+
+ exit_loop:
+ if (bio != NULL)
+ BIO_free(bio);
+ else if (s >= 0)
+ BIO_closesocket(s);
+ end:
+ return ret;
+}
+
+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);
+ BIO_ADDRINFO_free(data->addr_first);
+ data->addr_first = NULL;
+ 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) {
+ char *hold_serv = data->param_serv;
+ /* We affect the hostname regardless. However, the input
+ * string might contain a host:service spec, so we must
+ * parse it, which might or might not affect the service
+ */
+ OPENSSL_free(data->param_addr);
+ data->param_addr = NULL;
+ ret = BIO_parse_hostserv(ptr,
+ &data->param_addr,
+ &data->param_serv,
+ BIO_PARSE_PRIO_SERV);
+ if (hold_serv != data->param_serv)
+ OPENSSL_free(hold_serv);
+ b->init = 1;
+ } else if (num == 1) {
+ OPENSSL_free(data->param_serv);
+ data->param_serv = BUF_strdup(ptr);
+ b->init = 1;
+ } else if (num == 2) {
+ data->bind_mode |= BIO_SOCK_NONBLOCK;
+ } else if (num == 3) {
+ BIO_free(data->bio_chain);
+ data->bio_chain = (BIO *)ptr;
+ } else if (num == 4) {
+ data->accept_family = *(int *)ptr;
+ }
+ } else {
+ if (num == 2) {
+ data->bind_mode &= ~BIO_SOCK_NONBLOCK;
+ }
+ }
+ break;
+ case BIO_C_SET_NBIO:
+ if (num != 0)
+ data->accepted_mode |= BIO_SOCK_NONBLOCK;
+ else
+ data->accepted_mode &= ~BIO_SOCK_NONBLOCK;
+ break;
+ case BIO_C_SET_FD:
+ b->init = 1;
+ b->num = *((int *)ptr);
+ data->accept_sock = b->num;
+ data->state = ACPT_S_ACCEPT;
+ 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 (num == 0 && ptr != NULL) {
+ pp = (char **)ptr;
+ *pp = data->cache_accepting_name;
+ } else if (num == 1 && ptr != NULL) {
+ pp = (char **)ptr;
+ *pp = data->cache_accepting_serv;
+ } else if (num == 2 && ptr != NULL) {
+ pp = (char **)ptr;
+ *pp = data->cache_peer_name;
+ } else if (num == 3 && ptr != NULL) {
+ pp = (char **)ptr;
+ *pp = data->cache_peer_serv;
+ } else if (num == 4) {
+ switch (BIO_ADDRINFO_family(data->addr_iter)) {
+#ifdef AF_INET6
+ case AF_INET6:
+ ret = BIO_FAMILY_IPV6;
+ break;