diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown
index 2c9814e59deb2b..bac0ace8a5ed63 100644
--- a/doc/api/tls.markdown
+++ b/doc/api/tls.markdown
@@ -494,6 +494,24 @@ Example:
If the peer does not provide a certificate, it returns `null` or an empty
object.
+### tlsSocket.getProtocol()
+
+Returns a string containing the negotiated SSL/TLS protocol version of the
+current connection. `'unknown'` will be returned for connected sockets that have
+not completed the handshaking process. `null` will be returned for server
+sockets or disconnected client sockets.
+
+Examples:
+```
+'SSLv3'
+'TLSv1'
+'TLSv1.1'
+'TLSv1.2'
+'unknown'
+```
+
+See https://www.openssl.org/docs/manmaster/ssl/SSL_get_version.html for more
+information.
### tlsSocket.getSession()
diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js
index dd293121cbc782..a888dc1554a495 100644
--- a/lib/_tls_wrap.js
+++ b/lib/_tls_wrap.js
@@ -649,6 +649,13 @@ TLSSocket.prototype.getEphemeralKeyInfo = function() {
return null;
};
+TLSSocket.prototype.getProtocol = function() {
+ if (this._handle)
+ return this._handle.getProtocol();
+
+ return null;
+};
+
// TODO: support anonymous (nocert) and PSK
diff --git a/src/node_crypto.cc b/src/node_crypto.cc
index c1a3d3f8610089..f6cf39bcb94b13 100644
--- a/src/node_crypto.cc
+++ b/src/node_crypto.cc
@@ -1192,6 +1192,7 @@ void SSLWrap::AddMethods(Environment* env, Local t) {
env->SetProtoMethod(t, "setOCSPResponse", SetOCSPResponse);
env->SetProtoMethod(t, "requestOCSP", RequestOCSP);
env->SetProtoMethod(t, "getEphemeralKeyInfo", GetEphemeralKeyInfo);
+ env->SetProtoMethod(t, "getProtocol", GetProtocol);
#ifdef SSL_set_max_send_fragment
env->SetProtoMethod(t, "setMaxSendFragment", SetMaxSendFragment);
@@ -1954,6 +1955,15 @@ void SSLWrap::GetCurrentCipher(const FunctionCallbackInfo& args) {
}
+template
+void SSLWrap::GetProtocol(const FunctionCallbackInfo& args) {
+ Base* w = Unwrap(args.Holder());
+
+ const char* tls_version = SSL_get_version(w->ssl_);
+ args.GetReturnValue().Set(OneByteString(args.GetIsolate(), tls_version));
+}
+
+
#ifdef OPENSSL_NPN_NEGOTIATED
template
int SSLWrap::AdvertiseNextProtoCallback(SSL* s,
diff --git a/src/node_crypto.h b/src/node_crypto.h
index aaadc904dd3980..8a6c66703720b0 100644
--- a/src/node_crypto.h
+++ b/src/node_crypto.h
@@ -248,6 +248,7 @@ class SSLWrap {
static void RequestOCSP(const v8::FunctionCallbackInfo& args);
static void GetEphemeralKeyInfo(
const v8::FunctionCallbackInfo& args);
+ static void GetProtocol(const v8::FunctionCallbackInfo& args);
#ifdef SSL_set_max_send_fragment
static void SetMaxSendFragment(
diff --git a/test/parallel/test-tls-getprotocol.js b/test/parallel/test-tls-getprotocol.js
new file mode 100644
index 00000000000000..67592143ee4875
--- /dev/null
+++ b/test/parallel/test-tls-getprotocol.js
@@ -0,0 +1,43 @@
+'use strict';
+const common = require('../common');
+const assert = require('assert');
+
+if (!common.hasCrypto) {
+ console.log('1..0 # Skipped: missing crypto');
+ return;
+}
+
+const tls = require('tls');
+const fs = require('fs');
+
+const clientConfigs = [
+ { secureProtocol: 'TLSv1_method', version: 'TLSv1' },
+ { secureProtocol: 'TLSv1_1_method', version: 'TLSv1.1' },
+ { secureProtocol: 'TLSv1_2_method', version: 'TLSv1.2' }
+];
+
+const serverConfig = {
+ key: fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem'),
+ cert: fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem')
+};
+
+const server = tls.createServer(serverConfig, common.mustCall(function() {
+
+}, clientConfigs.length)).listen(common.PORT, common.localhostIPv4, function() {
+ let connected = 0;
+ clientConfigs.forEach(function(v) {
+ tls.connect({
+ host: common.localhostIPv4,
+ port: common.PORT,
+ rejectUnauthorized: false,
+ secureProtocol: v.secureProtocol
+ }, common.mustCall(function() {
+ assert.strictEqual(this.getProtocol(), v.version);
+ this.on('end', common.mustCall(function() {
+ assert.strictEqual(this.getProtocol(), null);
+ })).end();
+ if (++connected === clientConfigs.length)
+ server.close();
+ }));
+ });
+});