Skip to content

Commit

Permalink
Better handle IPv6.
Browse files Browse the repository at this point in the history
  ∙ host:port parsing, where unavoidable, is now IPv6-friendly.
  ∙ |BIO_C_GET_CONNECT| is simply removed.
  ∙ bssl -accept now listens on both IPv6 and IPv4.

Change-Id: I1cbd8a79c0199bab3ced4c4fd79d2cc5240f250c
Reviewed-on: https://boringssl-review.googlesource.com/6214
Reviewed-by: Adam Langley <alangley@gmail.com>
  • Loading branch information
Matt Braithwaite authored and Adam Langley committed Oct 26, 2015
1 parent 301afaf commit 29d8adb
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 64 deletions.
122 changes: 64 additions & 58 deletions crypto/bio/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ typedef struct bio_connect_st {
char *param_port;
int nbio;

uint8_t ip[4];
unsigned short port;

struct sockaddr_storage them;
Expand All @@ -114,23 +113,59 @@ static int closesocket(int sock) {
}
#endif

/* maybe_copy_ipv4_address sets |*ipv4| to the IPv4 address from |ss| (in
* big-endian order), if |ss| contains an IPv4 socket address. */
static void maybe_copy_ipv4_address(uint8_t *ipv4,
const struct sockaddr_storage *ss) {
const struct sockaddr_in *sin;
/* split_host_and_port sets |*out_host| and |*out_port| to the host and port
* parsed from |name|. It returns one on success or zero on error. Even when
* successful, |*out_port| may be NULL on return if no port was specified. */
static int split_host_and_port(char **out_host, char **out_port, const char *name) {
const char *host, *port = NULL;
size_t host_len = 0;

if (ss->ss_family != AF_INET) {
return;
*out_host = NULL;
*out_port = NULL;

if (name[0] == '[') { /* bracketed IPv6 address */
const char *close = strchr(name, ']');
if (close == NULL) {
return 0;
}
host = name + 1;
host_len = close - host;
if (close[1] == ':') { /* [IP]:port */
port = close + 2;
} else if (close[1] != 0) {
return 0;
}
} else {
const char *colon = strchr(name, ':');
if (colon == NULL || strchr(colon + 1, ':') != NULL) { /* IPv6 address */
host = name;
host_len = strlen(name);
} else { /* host:port */
host = name;
host_len = colon - name;
port = colon + 1;
}
}

sin = (const struct sockaddr_in*) ss;
memcpy(ipv4, &sin->sin_addr, 4);
*out_host = BUF_strndup(host, host_len);
if (*out_host == NULL) {
return 0;
}
if (port == NULL) {
*out_port = NULL;
return 1;
}
*out_port = OPENSSL_strdup(port);
if (*out_port == NULL) {
OPENSSL_free(*out_host);
*out_host = NULL;
return 0;
}
return 1;
}

static int conn_state(BIO *bio, BIO_CONNECT *c) {
int ret = -1, i;
char *p, *q;
int (*cb)(const BIO *, int, int) = NULL;

if (c->info_callback != NULL) {
Expand All @@ -140,36 +175,30 @@ static int conn_state(BIO *bio, BIO_CONNECT *c) {
for (;;) {
switch (c->state) {
case BIO_CONN_S_BEFORE:
p = c->param_hostname;
if (p == NULL) {
/* If there's a hostname and a port, assume that both are
* exactly what they say. If there is only a hostname, try
* (just once) to split it into a hostname and port. */

if (c->param_hostname == NULL) {
OPENSSL_PUT_ERROR(BIO, BIO_R_NO_HOSTNAME_SPECIFIED);
goto exit_loop;
}
for (; *p != 0; p++) {
if (*p == ':' || *p == '/') {
break;
}
}

i = *p;
if (i == ':' || i == '/') {
*(p++) = 0;
if (i == ':') {
for (q = p; *q; q++) {
if (*q == '/') {
*q = 0;
break;
}
}
OPENSSL_free(c->param_port);
c->param_port = BUF_strdup(p);
if (c->param_port == NULL) {
char *host, *port;
if (!split_host_and_port(&host, &port, c->param_hostname) ||
port == NULL) {
OPENSSL_free(host);
OPENSSL_free(port);
OPENSSL_PUT_ERROR(BIO, BIO_R_NO_PORT_SPECIFIED);
ERR_add_error_data(2, "host=", c->param_hostname);
goto exit_loop;
}
}

if (c->param_port == NULL) {
OPENSSL_PUT_ERROR(BIO, BIO_R_NO_PORT_SPECIFIED);
ERR_add_error_data(2, "host=", c->param_hostname);
goto exit_loop;
OPENSSL_free(c->param_port);
c->param_port = port;
OPENSSL_free(c->param_hostname);
c->param_hostname = host;
}

if (!bio_ip_and_port_to_socket_and_addr(
Expand All @@ -180,9 +209,6 @@ static int conn_state(BIO *bio, BIO_CONNECT *c) {
goto exit_loop;
}

memset(c->ip, 0, 4);
maybe_copy_ipv4_address(c->ip, &c->them);

if (c->nbio) {
if (!bio_socket_nbio(bio->num, 1)) {
OPENSSL_PUT_ERROR(BIO, BIO_R_ERROR_SETTING_NBIO);
Expand Down Expand Up @@ -376,7 +402,6 @@ static int conn_write(BIO *bio, const char *in, int in_len) {

static long conn_ctrl(BIO *bio, int cmd, long num, void *ptr) {
int *ip;
const char **pptr;
long ret = 1;
BIO_CONNECT *data;

Expand All @@ -397,25 +422,6 @@ static long conn_ctrl(BIO *bio, int cmd, long num, void *ptr) {
ret = 1;
}
break;
case BIO_C_GET_CONNECT:
/* TODO(fork): can this be removed? (Or maybe this whole file). */
if (ptr != NULL) {
pptr = (const char **)ptr;
if (num == 0) {
*pptr = data->param_hostname;
} else if (num == 1) {
*pptr = data->param_port;
} else if (num == 2) {
*pptr = (char *) &data->ip[0];
} else if (num == 3) {
*((int *)ptr) = data->port;
}
if (!bio->init) {
*pptr = "not initialized";
}
ret = 1;
}
break;
case BIO_C_SET_CONNECT:
if (ptr != NULL) {
bio->init = 1;
Expand Down
1 change: 0 additions & 1 deletion include/openssl/bio.h
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,6 @@ struct bio_st {
#define BIO_C_GET_MD_CTX 120
#define BIO_C_GET_PROXY_PARAM 121
#define BIO_C_SET_BUFF_READ_DATA 122 /* data to read first */
#define BIO_C_GET_CONNECT 123
#define BIO_C_GET_ACCEPT 124
#define BIO_C_SET_SSL_RENEGOTIATE_BYTES 125
#define BIO_C_GET_SSL_NUM_RENEGOTIATES 126
Expand Down
10 changes: 5 additions & 5 deletions tool/transport_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -133,19 +133,19 @@ bool Connect(int *out_sock, const std::string &hostname_and_port) {
}

bool Accept(int *out_sock, const std::string &port) {
struct sockaddr_in addr, cli_addr;
struct sockaddr_in6 addr, cli_addr;
socklen_t cli_addr_len = sizeof(cli_addr);
memset(&addr, 0, sizeof(addr));

addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(atoi(port.c_str()));
addr.sin6_family = AF_INET6;
addr.sin6_addr = in6addr_any;
addr.sin6_port = htons(atoi(port.c_str()));

bool ok = false;
int server_sock = -1;

server_sock =
socket(addr.sin_family, SOCK_STREAM, 0);
socket(addr.sin6_family, SOCK_STREAM, 0);
if (server_sock < 0) {
perror("socket");
goto out;
Expand Down

0 comments on commit 29d8adb

Please sign in to comment.