/*
* Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
*
* 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_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;
/*
* 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
*/
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);
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_CREATE_SOCKET 3
#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",
bwrite_conv,
conn_write,
bread_conv,
conn_read,
conn_puts,
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;
BIO_info_cb *cb = NULL;
if (c->info_callback != NULL)
cb = c->info_callback;
for (;;) {
switch (c->state) {
case BIO_CONN_S_BEFORE:
if (c->param_hostname == NULL && c->param_service == NULL) {
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;
break;
case BIO_CONN_S_GET_ADDR:
{
int family = AF_UNSPEC;
switch (c->connect_family) {
case BIO_FAMILY_IPV6:
if (1) { /* This is a trick we use to avoid bit rot.
* at least the "else" part will always be
* compiled.
*/
#if OPENSSL_USE_IPV6
family = AF_INET6;
} else {
#endif
ERR_raise(ERR_LIB_BIO, 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:
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, c->connect_sock_type,
&c->addr_first) == 0)
goto exit_loop;
}
if (c->addr_first == NULL