NPN/ALPN: allow disabling via command line

when using --http2 one can now selectively disable NPN or ALPN with
--no-alpn and --no-npn. for now honored with NSS only.

TODO: honor this option with GnuTLS and OpenSSL
This commit is contained in:
Fabian Frank 2014-02-09 23:38:55 -08:00 committed by Daniel Stenberg
parent 70bd9784de
commit 909a68c121
11 changed files with 87 additions and 19 deletions

View File

@ -133,6 +133,18 @@ version. (Added in 7.33.0)
.IP "--http2" .IP "--http2"
(HTTP) Tells curl to issue its requests using HTTP 2. This requires that the (HTTP) Tells curl to issue its requests using HTTP 2. This requires that the
underlying libcurl was built to support it. (Added in 7.33.0) underlying libcurl was built to support it. (Added in 7.33.0)
.IP "--no-npn"
Disable the NPN TLS extension. NPN is enabled by default if libcurl was built
with an SSL library that supports NPN. NPN is used by a libcurl that supports
HTTP 2 to negoatiate HTTP 2 support with the server during https sessions.
(Added in 7.36.0)
.IP "--no-alpn"
Disable the ALPN TLS extension. ALPN is enabled by default if libcurl was built
with an SSL library that supports ALPN. ALPN is used by a libcurl that supports
HTTP 2 to negoatiate HTTP 2 support with the server during https sessions.
(Added in 7.36.0)
.IP "-1, --tlsv1" .IP "-1, --tlsv1"
(SSL) (SSL)
Forces curl to use TLS version 1 when negotiating with a remote TLS server. Forces curl to use TLS version 1 when negotiating with a remote TLS server.

View File

@ -495,6 +495,8 @@ CURLOPT_SSLKEY 7.9.3
CURLOPT_SSLKEYPASSWD 7.9.3 7.17.0 CURLOPT_SSLKEYPASSWD 7.9.3 7.17.0
CURLOPT_SSLKEYTYPE 7.9.3 CURLOPT_SSLKEYTYPE 7.9.3
CURLOPT_SSLVERSION 7.1 CURLOPT_SSLVERSION 7.1
CURLOPT_SSL_ENABLE_ALPN 7.36.0
CURLOPT_SSL_ENABLE_NPN 7.36.0
CURLOPT_SSL_CIPHER_LIST 7.9 CURLOPT_SSL_CIPHER_LIST 7.9
CURLOPT_SSL_CTX_DATA 7.10.6 CURLOPT_SSL_CTX_DATA 7.10.6
CURLOPT_SSL_CTX_FUNCTION 7.10.6 CURLOPT_SSL_CTX_FUNCTION 7.10.6

View File

@ -1571,6 +1571,12 @@ typedef enum {
/* Set authentication options directly */ /* Set authentication options directly */
CINIT(LOGIN_OPTIONS, OBJECTPOINT, 224), CINIT(LOGIN_OPTIONS, OBJECTPOINT, 224),
/* Enable/disable TLS NPN extension (http2 over ssl might fail without) */
CINIT(SSL_ENABLE_NPN, LONG, 225),
/* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */
CINIT(SSL_ENABLE_ALPN, LONG, 226),
CURLOPT_LASTENTRY /* the last unused */ CURLOPT_LASTENTRY /* the last unused */
} CURLoption; } CURLoption;

View File

@ -563,6 +563,8 @@ CURLcode Curl_init_userdefined(struct UserDefined *set)
set->tcp_keepintvl = 60; set->tcp_keepintvl = 60;
set->tcp_keepidle = 60; set->tcp_keepidle = 60;
set->ssl_enable_npn = TRUE;
set->ssl_enable_alpn = TRUE;
return res; return res;
} }
@ -2478,6 +2480,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
case CURLOPT_TCP_KEEPINTVL: case CURLOPT_TCP_KEEPINTVL:
data->set.tcp_keepintvl = va_arg(param, long); data->set.tcp_keepintvl = va_arg(param, long);
break; break;
case CURLOPT_SSL_ENABLE_NPN:
data->set.ssl_enable_npn = (0 != va_arg(param, long))?TRUE:FALSE;
break;
case CURLOPT_SSL_ENABLE_ALPN:
data->set.ssl_enable_alpn = (0 != va_arg(param, long))?TRUE:FALSE;
break;
default: default:
/* unknown tag and its companion, just ignore: */ /* unknown tag and its companion, just ignore: */

View File

@ -1593,6 +1593,9 @@ struct UserDefined {
long tcp_keepintvl; /* seconds between TCP keepalive probes */ long tcp_keepintvl; /* seconds between TCP keepalive probes */
size_t maxconnects; /* Max idle connections in the connection cache */ size_t maxconnects; /* Max idle connections in the connection cache */
bool ssl_enable_npn; /* TLS NPN extension? */
bool ssl_enable_alpn; /* TLS ALPN extension? */
}; };
struct Names { struct Names {

View File

@ -616,15 +616,16 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg)
{ {
struct connectdata *conn = (struct connectdata*) arg; struct connectdata *conn = (struct connectdata*) arg;
#ifndef USE_NGHTTP2 #ifdef USE_NGHTTP2
(void)sock;
(void)conn;
#else
unsigned int buflenmax = 50; unsigned int buflenmax = 50;
unsigned char buf[50]; unsigned char buf[50];
unsigned int buflen; unsigned int buflen;
SSLNextProtoState state; SSLNextProtoState state;
if(!conn->data->set.ssl_enable_npn && !conn->data->set.ssl_enable_alpn) {
return;
}
if(SSL_GetNextProto(sock, &state, buf, &buflen, buflenmax) == SECSuccess) { if(SSL_GetNextProto(sock, &state, buf, &buflen, buflenmax) == SECSuccess) {
switch(state) { switch(state) {
@ -1311,6 +1312,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
#endif #endif
#endif #endif
if(connssl->state == ssl_connection_complete) if(connssl->state == ssl_connection_complete)
return CURLE_OK; return CURLE_OK;
@ -1485,32 +1487,45 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
} }
#ifdef USE_NGHTTP2 #ifdef USE_NGHTTP2
if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
#ifdef SSL_ENABLE_NPN #ifdef SSL_ENABLE_NPN
if(SSL_OptionSet(connssl->handle, SSL_ENABLE_NPN, PR_TRUE) != SECSuccess) if(data->set.ssl_enable_npn) {
goto error; if(SSL_OptionSet(connssl->handle, SSL_ENABLE_NPN, PR_TRUE) != SECSuccess)
goto error;
}
#endif #endif
#ifdef SSL_ENABLE_ALPN #ifdef SSL_ENABLE_ALPN
if(SSL_OptionSet(connssl->handle, SSL_ENABLE_ALPN, PR_TRUE) != SECSuccess) if(data->set.ssl_enable_alpn) {
goto error; if(SSL_OptionSet(connssl->handle, SSL_ENABLE_ALPN, PR_TRUE)
!= SECSuccess)
goto error;
}
#endif #endif
#if defined(SSL_ENABLE_NPN) || defined(SSL_ENABLE_ALPN) #if defined(SSL_ENABLE_NPN) || defined(SSL_ENABLE_ALPN)
alpn_protos[cur] = NGHTTP2_PROTO_VERSION_ID_LEN; if(data->set.ssl_enable_npn || data->set.ssl_enable_alpn) {
cur++; alpn_protos[cur] = NGHTTP2_PROTO_VERSION_ID_LEN;
memcpy(&alpn_protos[cur], NGHTTP2_PROTO_VERSION_ID, cur++;
NGHTTP2_PROTO_VERSION_ID_LEN); memcpy(&alpn_protos[cur], NGHTTP2_PROTO_VERSION_ID,
cur += NGHTTP2_PROTO_VERSION_ID_LEN; NGHTTP2_PROTO_VERSION_ID_LEN);
alpn_protos[cur] = ALPN_HTTP_1_1_LENGTH; cur += NGHTTP2_PROTO_VERSION_ID_LEN;
cur++; alpn_protos[cur] = ALPN_HTTP_1_1_LENGTH;
memcpy(&alpn_protos[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); cur++;
memcpy(&alpn_protos[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
if(SSL_SetNextProtoNego(connssl->handle, alpn_protos, alpn_protos_len) if(SSL_SetNextProtoNego(connssl->handle, alpn_protos, alpn_protos_len)
!= SECSuccess) != SECSuccess)
goto error; goto error;
}
else {
infof(data, "SSL, can't negotiate HTTP/2.0 with neither NPN nor ALPN\n");
}
#endif #endif
}
#endif #endif
/* Force handshake on next I/O */ /* Force handshake on next I/O */
SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE); SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE);

View File

@ -214,6 +214,8 @@ struct Configurable {
bool test_event_based; bool test_event_based;
#endif #endif
char *xoauth2_bearer; /* XOAUTH2 bearer token */ char *xoauth2_bearer; /* XOAUTH2 bearer token */
bool nonpn; /* enable/disable TLS NPN extension */
bool noalpn; /* enable/disable TLS ALPN extension */
struct Configurable* prev; struct Configurable* prev;
struct Configurable* next; /* Always last in the struct */ struct Configurable* next; /* Always last in the struct */

View File

@ -90,7 +90,9 @@ static const struct LongShort aliases[]= {
#endif #endif
{"*F", "dns-servers", TRUE}, {"*F", "dns-servers", TRUE},
{"*g", "trace", TRUE}, {"*g", "trace", TRUE},
{"*G", "npn", FALSE},
{"*h", "trace-ascii", TRUE}, {"*h", "trace-ascii", TRUE},
{"*H", "alpn", FALSE},
{"*i", "limit-rate", TRUE}, {"*i", "limit-rate", TRUE},
{"*j", "compressed", FALSE}, {"*j", "compressed", FALSE},
{"*J", "tr-encoding", FALSE}, {"*J", "tr-encoding", FALSE},
@ -554,6 +556,9 @@ ParameterError getparameter(char *flag, /* f or -long-flag */
warnf(config, "--trace overrides an earlier trace/verbose option\n"); warnf(config, "--trace overrides an earlier trace/verbose option\n");
config->tracetype = TRACE_BIN; config->tracetype = TRACE_BIN;
break; break;
case 'G': /* --npn */
config->nonpn = (!toggle)?TRUE:FALSE;
break;
case 'h': /* --trace-ascii */ case 'h': /* --trace-ascii */
GetStr(&config->trace_dump, nextarg); GetStr(&config->trace_dump, nextarg);
if(config->tracetype && (config->tracetype != TRACE_ASCII)) if(config->tracetype && (config->tracetype != TRACE_ASCII))
@ -561,6 +566,9 @@ ParameterError getparameter(char *flag, /* f or -long-flag */
"--trace-ascii overrides an earlier trace/verbose option\n"); "--trace-ascii overrides an earlier trace/verbose option\n");
config->tracetype = TRACE_ASCII; config->tracetype = TRACE_ASCII;
break; break;
case 'H': /* --alpn */
config->noalpn = (!toggle)?TRUE:FALSE;
break;
case 'i': /* --limit-rate */ case 'i': /* --limit-rate */
{ {
/* We support G, M, K too */ /* We support G, M, K too */

View File

@ -105,6 +105,8 @@ static const char *const helptext[] = {
" -0, --http1.0 Use HTTP 1.0 (H)", " -0, --http1.0 Use HTTP 1.0 (H)",
" --http1.1 Use HTTP 1.1 (H)", " --http1.1 Use HTTP 1.1 (H)",
" --http2 Use HTTP 2 (H)", " --http2 Use HTTP 2 (H)",
" --no-npn Disable the NPN TLS extension",
" --no-alpn Disable the ALPN TLS extension",
" --ignore-content-length Ignore the HTTP Content-Length header", " --ignore-content-length Ignore the HTTP Content-Length header",
" -i, --include Include protocol headers in the output (H/F)", " -i, --include Include protocol headers in the output (H/F)",
" -k, --insecure Allow connections to SSL sites without certs (H)", " -k, --insecure Allow connections to SSL sites without certs (H)",

View File

@ -1350,6 +1350,14 @@ static int operate_do(struct Configurable *config)
if(config->sasl_ir) if(config->sasl_ir)
my_setopt(curl, CURLOPT_SASL_IR, 1L); my_setopt(curl, CURLOPT_SASL_IR, 1L);
if(config->nonpn) {
my_setopt(curl, CURLOPT_SSL_ENABLE_NPN, 0L);
}
if(config->noalpn) {
my_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, 0L);
}
/* initialize retry vars for loop below */ /* initialize retry vars for loop below */
retry_sleep_default = (config->retry_delay) ? retry_sleep_default = (config->retry_delay) ?
config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */ config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */

View File

@ -145,6 +145,8 @@ const NameValue setopt_nv_CURLPROTO[] = {
static const NameValue setopt_nv_CURLNONZERODEFAULTS[] = { static const NameValue setopt_nv_CURLNONZERODEFAULTS[] = {
NV1(CURLOPT_SSL_VERIFYPEER, 1), NV1(CURLOPT_SSL_VERIFYPEER, 1),
NV1(CURLOPT_SSL_VERIFYHOST, 1), NV1(CURLOPT_SSL_VERIFYHOST, 1),
NV1(CURLOPT_SSL_ENABLE_NPN, 1),
NV1(CURLOPT_SSL_ENABLE_ALPN, 1),
NVEND NVEND
}; };