/*
- * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved.
*
- * Licensed under the OpenSSL license (the "License"). You may not use
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
#include <stdio.h>
#include <errno.h>
-#include "bio_lcl.h"
+#include "bio_local.h"
+#include "internal/bio_tfo.h"
+#include "internal/ktls.h"
#ifndef OPENSSL_NO_SOCK
typedef struct bio_connect_st {
int state;
int connect_family;
+ int connect_sock_type;
char *param_hostname;
char *param_service;
int connect_mode;
+# ifndef OPENSSL_NO_KTLS
+ unsigned char record_type;
+# endif
+ int tfo_first;
BIO_ADDRINFO *addr_first;
const BIO_ADDRINFO *addr_iter;
* ssl info_callback
*/
BIO_info_cb *info_callback;
+ /*
+ * Used when connect_sock_type is SOCK_DGRAM. Owned by us; we forward
+ * read/write(mmsg) calls to this if present.
+ */
+ BIO *dgram_bio;
} 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 int conn_gets(BIO *h, char *buf, int size);
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_sendmmsg(BIO *h, BIO_MSG *m, size_t s, size_t n,
+ uint64_t f, size_t *mp);
+static int conn_recvmmsg(BIO *h, BIO_MSG *m, size_t s, size_t n,
+ uint64_t f, size_t *mp);
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_CONNECT *BIO_CONNECT_new(void);
+static void BIO_CONNECT_free(BIO_CONNECT *a);
#define BIO_CONN_S_BEFORE 1
#define BIO_CONN_S_GET_ADDR 2
#define BIO_CONN_S_CONNECT 4
#define BIO_CONN_S_OK 5
#define BIO_CONN_S_BLOCKED_CONNECT 6
+#define BIO_CONN_S_CONNECT_ERROR 7
static const BIO_METHOD methods_connectp = {
BIO_TYPE_CONNECT,
"socket connect",
- /* TODO: Convert to new style write function */
bwrite_conv,
conn_write,
- /* TODO: Convert to new style read function */
bread_conv,
conn_read,
conn_puts,
- NULL, /* connect_gets, */
+ conn_gets,
conn_ctrl,
conn_new,
conn_free,
conn_callback_ctrl,
+ conn_sendmmsg,
+ conn_recvmmsg,
};
+static int conn_create_dgram_bio(BIO *b, BIO_CONNECT *c)
+{
+ if (c->connect_sock_type != SOCK_DGRAM)
+ return 1;
+
+#ifndef OPENSSL_NO_DGRAM
+ c->dgram_bio = BIO_new_dgram(b->num, 0);
+ if (c->dgram_bio == NULL)
+ goto err;
+
+ return 1;
+
+err:
+#endif
+ c->state = BIO_CONN_S_CONNECT_ERROR;
+ return 0;
+}
+
static int conn_state(BIO *b, BIO_CONNECT *c)
{
- int ret = -1, i;
+ int ret = -1, i, opts;
BIO_info_cb *cb = NULL;
if (c->info_callback != NULL)
switch (c->state) {
case BIO_CONN_S_BEFORE:
if (c->param_hostname == NULL && c->param_service == NULL) {
- BIOerr(BIO_F_CONN_STATE, BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED);
- ERR_add_error_data(4,
- "hostname=", c->param_hostname,
- " service=", c->param_service);
+ ERR_raise_data(ERR_LIB_BIO,
+ BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED,
+ "hostname=%s service=%s",
+ c->param_hostname, c->param_service);
goto exit_loop;
}
c->state = BIO_CONN_S_GET_ADDR;
* at least the "else" part will always be
* compiled.
*/
-#ifdef AF_INET6
+#if OPENSSL_USE_IPV6
family = AF_INET6;
} else {
#endif
- BIOerr(BIO_F_CONN_STATE, BIO_R_UNAVAILABLE_IP_FAMILY);
+ ERR_raise(ERR_LIB_BIO, BIO_R_UNAVAILABLE_IP_FAMILY);
goto exit_loop;
}
break;
family = AF_UNSPEC;
break;
default:
- BIOerr(BIO_F_CONN_STATE, BIO_R_UNSUPPORTED_IP_FAMILY);
+ ERR_raise(ERR_LIB_BIO, BIO_R_UNSUPPORTED_IP_FAMILY);
goto exit_loop;
}
if (BIO_lookup(c->param_hostname, c->param_service,
BIO_LOOKUP_CLIENT,
- family, SOCK_STREAM, &c->addr_first) == 0)
+ family, c->connect_sock_type,
+ &c->addr_first) == 0)
goto exit_loop;
}
if (c->addr_first == NULL) {
- BIOerr(BIO_F_CONN_STATE, BIO_R_LOOKUP_RETURNED_NOTHING);
+ ERR_raise(ERR_LIB_BIO, BIO_R_LOOKUP_RETURNED_NOTHING);
goto exit_loop;
}
c->addr_iter = c->addr_first;
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_hostname,
- " service=", c->param_service);
- BIOerr(BIO_F_CONN_STATE, BIO_R_UNABLE_TO_CREATE_SOCKET);
+ ERR_raise_data(ERR_LIB_SYS, get_last_socket_error(),
+ "calling socket(%s, %s)",
+ c->param_hostname, c->param_service);
+ ERR_raise(ERR_LIB_BIO, BIO_R_UNABLE_TO_CREATE_SOCKET);
goto exit_loop;
}
b->num = ret;
case BIO_CONN_S_CONNECT:
BIO_clear_retry_flags(b);
- ret = BIO_connect(b->num, BIO_ADDRINFO_address(c->addr_iter),
- BIO_SOCK_KEEPALIVE | c->connect_mode);
+ ERR_set_mark();
+
+ opts = c->connect_mode;
+ if (BIO_ADDRINFO_socktype(c->addr_iter) == SOCK_STREAM)
+ opts |= BIO_SOCK_KEEPALIVE;
+
+ ret = BIO_connect(b->num, BIO_ADDRINFO_address(c->addr_iter), opts);
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;
- ERR_clear_error();
+ ERR_pop_to_mark();
} else if ((c->addr_iter = BIO_ADDRINFO_next(c->addr_iter))
!= NULL) {
/*
*/
BIO_closesocket(b->num);
c->state = BIO_CONN_S_CREATE_SOCKET;
- ERR_clear_error();
+ ERR_pop_to_mark();
break;
} else {
- SYSerr(SYS_F_CONNECT, get_last_socket_error());
- ERR_add_error_data(4,
- "hostname=", c->param_hostname,
- " service=", c->param_service);
- BIOerr(BIO_F_CONN_STATE, BIO_R_CONNECT_ERROR);
+ ERR_clear_last_mark();
+ ERR_raise_data(ERR_LIB_SYS, get_last_socket_error(),
+ "calling connect(%s, %s)",
+ c->param_hostname, c->param_service);
+ c->state = BIO_CONN_S_CONNECT_ERROR;
+ break;
}
goto exit_loop;
} else {
+ ERR_clear_last_mark();
+ if (!conn_create_dgram_bio(b, c))
+ break;
c->state = BIO_CONN_S_OK;
}
break;
case BIO_CONN_S_BLOCKED_CONNECT:
+ /* wait for socket being writable, before querying BIO_sock_error */
+ if (BIO_socket_wait(b->num, 0, time(NULL)) == 0)
+ break;
i = BIO_sock_error(b->num);
- if (i) {
+ if (i != 0) {
BIO_clear_retry_flags(b);
- SYSerr(SYS_F_CONNECT, i);
- ERR_add_error_data(4,
- "hostname=", c->param_hostname,
- " service=", c->param_service);
- BIOerr(BIO_F_CONN_STATE, BIO_R_NBIO_CONNECT_ERROR);
+ if ((c->addr_iter = BIO_ADDRINFO_next(c->addr_iter)) != NULL) {
+ /*
+ * if there are more addresses to try, do that first
+ */
+ BIO_closesocket(b->num);
+ c->state = BIO_CONN_S_CREATE_SOCKET;
+ break;
+ }
+ ERR_raise_data(ERR_LIB_SYS, i,
+ "calling connect(%s, %s)",
+ c->param_hostname, c->param_service);
+ ERR_raise(ERR_LIB_BIO, BIO_R_NBIO_CONNECT_ERROR);
ret = 0;
goto exit_loop;
- } else
+ } else {
+ if (!conn_create_dgram_bio(b, c))
+ break;
c->state = BIO_CONN_S_OK;
+# ifndef OPENSSL_NO_KTLS
+ /*
+ * The new socket is created successfully regardless of ktls_enable.
+ * ktls_enable doesn't change any functionality of the socket, except
+ * changing the setsockopt to enable the processing of ktls_start.
+ * Thus, it is not a problem to call it for non-TLS sockets.
+ */
+ ktls_enable(b->num);
+# endif
+ }
break;
+ case BIO_CONN_S_CONNECT_ERROR:
+ ERR_raise(ERR_LIB_BIO, BIO_R_CONNECT_ERROR);
+ ret = 0;
+ goto exit_loop;
+
case BIO_CONN_S_OK:
ret = 1;
goto exit_loop;
return ret;
}
-BIO_CONNECT *BIO_CONNECT_new(void)
+static BIO_CONNECT *BIO_CONNECT_new(void)
{
BIO_CONNECT *ret;
return NULL;
ret->state = BIO_CONN_S_BEFORE;
ret->connect_family = BIO_FAMILY_IPANY;
+ ret->connect_sock_type = SOCK_STREAM;
return ret;
}
-void BIO_CONNECT_free(BIO_CONNECT *a)
+static void BIO_CONNECT_free(BIO_CONNECT *a)
{
if (a == NULL)
return;
-
OPENSSL_free(a->param_hostname);
OPENSSL_free(a->param_service);
BIO_ADDRINFO_free(a->addr_first);
return 0;
data = (BIO_CONNECT *)a->ptr;
+ BIO_free(data->dgram_bio);
+
if (a->shutdown) {
conn_close_socket(a);
BIO_CONNECT_free(data);
return ret;
}
+ if (data->dgram_bio != NULL) {
+ BIO_clear_retry_flags(b);
+ ret = BIO_read(data->dgram_bio, out, outl);
+ BIO_set_flags(b, BIO_get_retry_flags(data->dgram_bio));
+ return ret;
+ }
+
if (out != NULL) {
clear_socket_error();
- ret = readsocket(b->num, out, outl);
+# ifndef OPENSSL_NO_KTLS
+ if (BIO_get_ktls_recv(b))
+ ret = ktls_read_record(b->num, out, outl);
+ else
+# endif
+ 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);
+ else if (ret == 0)
+ b->flags |= BIO_FLAGS_IN_EOF;
}
}
return ret;
return ret;
}
+ if (data->dgram_bio != NULL) {
+ BIO_clear_retry_flags(b);
+ ret = BIO_write(data->dgram_bio, in, inl);
+ BIO_set_flags(b, BIO_get_retry_flags(data->dgram_bio));
+ return ret;
+ }
+
clear_socket_error();
- ret = writesocket(b->num, in, inl);
+# ifndef OPENSSL_NO_KTLS
+ if (BIO_should_ktls_ctrl_msg_flag(b)) {
+ ret = ktls_send_ctrl_message(b->num, data->record_type, in, inl);
+ if (ret >= 0) {
+ ret = inl;
+ BIO_clear_ktls_ctrl_msg_flag(b);
+ }
+ } else
+# endif
+# if defined(OSSL_TFO_SENDTO)
+ if (data->tfo_first) {
+ int peerlen = BIO_ADDRINFO_sockaddr_size(data->addr_iter);
+
+ ret = sendto(b->num, in, inl, OSSL_TFO_SENDTO,
+ BIO_ADDRINFO_sockaddr(data->addr_iter), peerlen);
+ data->tfo_first = 0;
+ } else
+# endif
+ ret = writesocket(b->num, in, inl);
BIO_clear_retry_flags(b);
if (ret <= 0) {
if (BIO_sock_should_retry(ret))
const char **pptr = NULL;
long ret = 1;
BIO_CONNECT *data;
+ const BIO_ADDR *dg_addr;
+# ifndef OPENSSL_NO_KTLS
+ ktls_crypto_info_t *crypto_info;
+# endif
data = (BIO_CONNECT *)b->ptr;
*pptr = (const char *)BIO_ADDRINFO_address(data->addr_iter);
} else if (num == 3) {
switch (BIO_ADDRINFO_family(data->addr_iter)) {
-# ifdef AF_INET6
+# if OPENSSL_USE_IPV6
case AF_INET6:
ret = BIO_FAMILY_IPV6;
break;
ret = -1;
break;
}
+ } else if (num == 4) {
+ ret = data->connect_mode;
} else {
ret = 0;
}
case BIO_C_SET_CONNECT:
if (ptr != NULL) {
b->init = 1;
- if (num == 0) {
+ if (num == 0) { /* BIO_set_conn_hostname */
char *hold_service = data->param_service;
/* 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_hostname);
data->param_hostname = NULL;
ret = BIO_parse_hostserv(ptr,
BIO_PARSE_PRIO_HOST);
if (hold_service != data->param_service)
OPENSSL_free(hold_service);
- } else if (num == 1) {
+ } else if (num == 1) { /* BIO_set_conn_port */
OPENSSL_free(data->param_service);
- data->param_service = BUF_strdup(ptr);
- } else if (num == 2) {
+ if ((data->param_service = OPENSSL_strdup(ptr)) == NULL)
+ ret = 0;
+ } else if (num == 2) { /* BIO_set_conn_address */
const BIO_ADDR *addr = (const BIO_ADDR *)ptr;
+ char *host = BIO_ADDR_hostname_string(addr, 1);
+ char *service = BIO_ADDR_service_string(addr, 1);
+
+ ret = host != NULL && service != NULL;
if (ret) {
- data->param_hostname = BIO_ADDR_hostname_string(addr, 1);
- data->param_service = BIO_ADDR_service_string(addr, 1);
+ OPENSSL_free(data->param_hostname);
+ data->param_hostname = host;
+ OPENSSL_free(data->param_service);
+ data->param_service = service;
BIO_ADDRINFO_free(data->addr_first);
data->addr_first = NULL;
data->addr_iter = NULL;
+ } else {
+ OPENSSL_free(host);
+ OPENSSL_free(service);
}
- } else if (num == 3) {
+ } else if (num == 3) { /* BIO_set_conn_ip_family */
data->connect_family = *(int *)ptr;
} else {
ret = 0;
}
}
break;
+ case BIO_C_SET_SOCK_TYPE:
+ if ((num != SOCK_STREAM && num != SOCK_DGRAM)
+ || data->state >= BIO_CONN_S_GET_ADDR) {
+ ret = 0;
+ break;
+ }
+
+ data->connect_sock_type = (int)num;
+ ret = 1;
+ break;
+ case BIO_C_GET_SOCK_TYPE:
+ ret = data->connect_sock_type;
+ break;
+ case BIO_C_GET_DGRAM_BIO:
+ if (data->dgram_bio != NULL) {
+ *(BIO **)ptr = data->dgram_bio;
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ break;
+ case BIO_CTRL_DGRAM_GET_PEER:
+ case BIO_CTRL_DGRAM_DETECT_PEER_ADDR:
+ if (data->state != BIO_CONN_S_OK)
+ conn_state(b, data); /* best effort */
+
+ if (data->state >= BIO_CONN_S_CREATE_SOCKET
+ && data->addr_iter != NULL
+ && (dg_addr = BIO_ADDRINFO_address(data->addr_iter)) != NULL) {
+
+ ret = BIO_ADDR_sockaddr_size(dg_addr);
+ if (num == 0 || num > ret)
+ num = ret;
+
+ memcpy(ptr, dg_addr, num);
+ ret = num;
+ } else {
+ ret = 0;
+ }
+
+ break;
+ case BIO_CTRL_GET_RPOLL_DESCRIPTOR:
+ case BIO_CTRL_GET_WPOLL_DESCRIPTOR:
+ {
+ BIO_POLL_DESCRIPTOR *pd = ptr;
+
+ if (data->state != BIO_CONN_S_OK)
+ conn_state(b, data); /* best effort */
+
+ if (data->state >= BIO_CONN_S_CREATE_SOCKET) {
+ pd->type = BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD;
+ pd->value.fd = b->num;
+ } else {
+ ret = 0;
+ }
+ }
+ break;
case BIO_C_SET_NBIO:
if (num != 0)
data->connect_mode |= BIO_SOCK_NONBLOCK;
else
data->connect_mode &= ~BIO_SOCK_NONBLOCK;
+
+ if (data->dgram_bio != NULL)
+ ret = BIO_set_nbio(data->dgram_bio, num);
+
break;
+#if defined(TCP_FASTOPEN) && !defined(OPENSSL_NO_TFO)
+ case BIO_C_SET_TFO:
+ if (num != 0) {
+ data->connect_mode |= BIO_SOCK_TFO;
+ data->tfo_first = 1;
+ } else {
+ data->connect_mode &= ~BIO_SOCK_TFO;
+ data->tfo_first = 0;
+ }
+ break;
+#endif
case BIO_C_SET_CONNECT_MODE:
data->connect_mode = (int)num;
+ if (num & BIO_SOCK_TFO)
+ data->tfo_first = 1;
+ else
+ data->tfo_first = 0;
break;
case BIO_C_GET_FD:
if (b->init) {
*fptr = data->info_callback;
}
break;
+ case BIO_CTRL_EOF:
+ ret = (b->flags & BIO_FLAGS_IN_EOF) != 0;
+ break;
+# ifndef OPENSSL_NO_KTLS
+ case BIO_CTRL_SET_KTLS:
+ crypto_info = (ktls_crypto_info_t *)ptr;
+ ret = ktls_start(b->num, crypto_info, num);
+ if (ret)
+ BIO_set_ktls_flag(b, num);
+ break;
+ case BIO_CTRL_GET_KTLS_SEND:
+ return BIO_should_ktls_flag(b, 1) != 0;
+ case BIO_CTRL_GET_KTLS_RECV:
+ return BIO_should_ktls_flag(b, 0) != 0;
+ case BIO_CTRL_SET_KTLS_TX_SEND_CTRL_MSG:
+ BIO_set_ktls_ctrl_msg_flag(b);
+ data->record_type = num;
+ ret = 0;
+ break;
+ case BIO_CTRL_CLEAR_KTLS_TX_CTRL_MSG:
+ BIO_clear_ktls_ctrl_msg_flag(b);
+ ret = 0;
+ break;
+ case BIO_CTRL_SET_KTLS_TX_ZEROCOPY_SENDFILE:
+ ret = ktls_enable_tx_zerocopy_sendfile(b->num);
+ if (ret)
+ BIO_set_ktls_zerocopy_sendfile_flag(b);
+ break;
+# endif
default:
ret = 0;
break;
return ret;
}
+int conn_gets(BIO *bio, char *buf, int size)
+{
+ BIO_CONNECT *data;
+ char *ptr = buf;
+ int ret = 0;
+
+ if (buf == NULL) {
+ ERR_raise(ERR_LIB_BIO, ERR_R_PASSED_NULL_PARAMETER);
+ return -1;
+ }
+ if (size <= 0) {
+ ERR_raise(ERR_LIB_BIO, BIO_R_INVALID_ARGUMENT);
+ return -1;
+ }
+ *buf = '\0';
+
+ if (bio == NULL || bio->ptr == NULL) {
+ ERR_raise(ERR_LIB_BIO, ERR_R_PASSED_NULL_PARAMETER);
+ return -1;
+ }
+ data = (BIO_CONNECT *)bio->ptr;
+ if (data->state != BIO_CONN_S_OK) {
+ ret = conn_state(bio, data);
+ if (ret <= 0)
+ return ret;
+ }
+
+ if (data->dgram_bio != NULL) {
+ ERR_raise(ERR_LIB_BIO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return -1;
+ }
+
+ clear_socket_error();
+ while (size-- > 1) {
+# ifndef OPENSSL_NO_KTLS
+ if (BIO_get_ktls_recv(bio))
+ ret = ktls_read_record(bio->num, ptr, 1);
+ else
+# endif
+ ret = readsocket(bio->num, ptr, 1);
+ BIO_clear_retry_flags(bio);
+ if (ret <= 0) {
+ if (BIO_sock_should_retry(ret))
+ BIO_set_retry_read(bio);
+ else if (ret == 0)
+ bio->flags |= BIO_FLAGS_IN_EOF;
+ break;
+ }
+ if (*ptr++ == '\n')
+ break;
+ }
+ *ptr = '\0';
+ return ret > 0 || (bio->flags & BIO_FLAGS_IN_EOF) != 0 ? ptr - buf : ret;
+}
+
+static int conn_sendmmsg(BIO *bio, BIO_MSG *msg, size_t stride, size_t num_msgs,
+ uint64_t flags, size_t *msgs_processed)
+{
+ int ret;
+ BIO_CONNECT *data;
+
+ if (bio == NULL) {
+ *msgs_processed = 0;
+ ERR_raise(ERR_LIB_BIO, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ data = (BIO_CONNECT *)bio->ptr;
+ if (data->state != BIO_CONN_S_OK) {
+ ret = conn_state(bio, data);
+ if (ret <= 0) {
+ *msgs_processed = 0;
+ return 0;
+ }
+ }
+
+ if (data->dgram_bio == NULL) {
+ *msgs_processed = 0;
+ ERR_raise(ERR_LIB_BIO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+
+ return BIO_sendmmsg(data->dgram_bio, msg, stride, num_msgs,
+ flags, msgs_processed);
+}
+
+static int conn_recvmmsg(BIO *bio, BIO_MSG *msg, size_t stride, size_t num_msgs,
+ uint64_t flags, size_t *msgs_processed)
+{
+ int ret;
+ BIO_CONNECT *data;
+
+ if (bio == NULL) {
+ *msgs_processed = 0;
+ ERR_raise(ERR_LIB_BIO, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ data = (BIO_CONNECT *)bio->ptr;
+ if (data->state != BIO_CONN_S_OK) {
+ ret = conn_state(bio, data);
+ if (ret <= 0) {
+ *msgs_processed = 0;
+ return 0;
+ }
+ }
+
+ if (data->dgram_bio == NULL) {
+ *msgs_processed = 0;
+ ERR_raise(ERR_LIB_BIO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+
+ return BIO_recvmmsg(data->dgram_bio, msg, stride, num_msgs,
+ flags, msgs_processed);
+}
+
BIO *BIO_new_connect(const char *str)
{
BIO *ret;