Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions tool-openssl/s_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ static const argument_t kArguments[] = {
"Show protocol messages" },
{ "-servername", kOptionalArgument,
"Server name for SNI extension." },
{ "-noservername", kBooleanArgument,
"Do not send the server name (SNI) extension in the ClientHello" },
{ "", kOptionalArgument, "" },
};

Expand Down Expand Up @@ -77,5 +79,11 @@ bool SClientTool(const args_list_t &args) {
args_map.erase("-servername");
}

if (args_map.count("-noservername") && args_map.count("-server-name")) {
fprintf(stderr,
"s_client: Can't use -servername and -noservername together\n");
return false;
}

return DoClient(args_map, true);
}
95 changes: 95 additions & 0 deletions tool/client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,94 @@ OPENSSL_MSVC_PRAGMA(warning(pop))
#include "transport_common.h"


// Copyright 1995-2026 The OpenSSL Project Authors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Ported verbatim from OpenSSL 3.x apps/s_client.c (is_dNS_name).
/*
* Host dNS Name verifier: used for checking that the hostname is in dNS format
* before setting it as SNI
*/
static int is_dNS_name(const char *host)
{
const size_t MAX_LABEL_LENGTH = 63;
int isdnsname = 0;
size_t length = strlen(host);
Comment thread
WillChilds-Klein marked this conversation as resolved.
size_t label_length = 0;
int all_numeric = 1;

/*
* Deviation from strict DNS name syntax, also check names with '_'
* Check DNS name syntax, any '-' or '.' must be internal,
* and on either side of each '.' we can't have a '-' or '.'.
*
* If the name has just one label, we don't consider it a DNS name.
*/
for (size_t i = 0; i < length && label_length < MAX_LABEL_LENGTH; ++i) {
char c = host[i];

if ((c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| c == '_') {
label_length += 1;
all_numeric = 0;
continue;
}

if (c >= '0' && c <= '9') {
label_length += 1;
continue;
}

/* Dot and hyphen cannot be first or last. */
if (i > 0 && i < length - 1) {
if (c == '-') {
label_length += 1;
continue;
}
/*
* Next to a dot the preceding and following characters must not be
* another dot or a hyphen. Otherwise, record that the name is
* plausible, since it has two or more labels.
*/
if (c == '.'
&& host[i + 1] != '.'
&& host[i - 1] != '-'
&& host[i + 1] != '-') {
label_length = 0;
isdnsname = 1;
continue;
}
}
isdnsname = 0;
break;
}

/* dNS name must not be all numeric and labels must be shorter than 64 characters. */
isdnsname &= !all_numeric && !(label_length == MAX_LABEL_LENGTH);

return isdnsname;
}

static std::string DefaultSNIFromConnect(
const std::string &hostname_and_port) {
if (hostname_and_port.empty()) {
return std::string();
}
std::string host = hostname_and_port;
size_t colon = hostname_and_port.find_last_of(':');
if (colon != std::string::npos) {
host = hostname_and_port.substr(0, colon);
}
if (host.size() >= 2 && host.front() == '[' && host.back() == ']') {
return std::string();
}
if (!is_dNS_name(host.c_str())) {
return std::string();
}
return host;
}

static const argument_t kArguments[] = {
{
"-connect", kRequiredArgument,
Expand Down Expand Up @@ -446,6 +534,13 @@ static bool DoConnection(SSL_CTX *ctx,
args_map["-server-name"].c_str())) {
return false;
}
} else if (is_openssl_s_client && args_map.count("-noservername") == 0) {
// Default SNI to the -connect hostname, matching OpenSSL 1.1+ s_client.
std::string default_sni = DefaultSNIFromConnect(args_map["-connect"]);
if (!default_sni.empty() &&
!SSL_set_tlsext_host_name(ssl.get(), default_sni.c_str())) {
return false;
}
}

if (args_map.count("-ech-grease") != 0) {
Expand Down
Loading