diff options
author | Matt Braithwaite <mab@google.com> | 2015-09-29 05:45:54 +0300 |
---|---|---|
committer | Adam Langley <alangley@gmail.com> | 2015-10-27 00:06:01 +0300 |
commit | 29d8adbdc6dbe8663e25c3bde68e3014d443454a (patch) | |
tree | b0c7abcee19611f6dea95c5ea4aa34dd4bb72b9a /crypto/bio | |
parent | 301afaf2230b5dfa028842296ab63a66f0d1b16c (diff) |
Better handle IPv6.
∙ 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>
Diffstat (limited to 'crypto/bio')
-rw-r--r-- | crypto/bio/connect.c | 122 |
1 files changed, 64 insertions, 58 deletions
diff --git a/crypto/bio/connect.c b/crypto/bio/connect.c index cc28ef87..0b34d7f7 100644 --- a/crypto/bio/connect.c +++ b/crypto/bio/connect.c @@ -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; @@ -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) { @@ -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( @@ -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); @@ -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; @@ -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; |