You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@brpc.apache.org by ww...@apache.org on 2023/12/25 03:52:53 UTC

(brpc) branch master updated: Add client ALPN support (#2251)

This is an automated email from the ASF dual-hosted git repository.

wwbmmm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new 4b149518 Add client ALPN support (#2251)
4b149518 is described below

commit 4b1495186268a07eb9cae5db86176bb1dd9a0fe4
Author: Menci <hu...@gmail.com>
AuthorDate: Mon Dec 25 11:52:48 2023 +0800

    Add client ALPN support (#2251)
    
    * Add client ALPN support
    
    * Fix build error
    
    * Remove ALPN code from mesalink_ssl_helper
    
    * Add alpn_protocol.size() to error message
    
    * Add docs
---
 docs/cn/client.md               |  3 +++
 docs/en/client.md               |  4 ++++
 src/brpc/channel.cpp            |  1 +
 src/brpc/details/ssl_helper.cpp | 38 ++++++++++++++++++++++++++++++++++++++
 src/brpc/details/ssl_helper.h   |  7 +++++++
 src/brpc/socket.cpp             | 27 +++++++++++++++++++++++++++
 src/brpc/socket.h               |  5 +++--
 src/brpc/ssl_options.h          |  4 ++++
 8 files changed, 87 insertions(+), 2 deletions(-)

diff --git a/docs/cn/client.md b/docs/cn/client.md
index ef714ed8..27f1fa70 100755
--- a/docs/cn/client.md
+++ b/docs/cn/client.md
@@ -831,6 +831,9 @@ options.mutable_ssl_options();
 // 开启客户端SSL并定制选项。
 options.mutable_ssl_options()->ciphers_name = "...";
 options.mutable_ssl_options()->sni_name = "...";
+
+// 设置 ALPN 的协议优先级(默认不启用 ALPN)。
+options.mutable_ssl_options()->alpn_protocols = {"h2", "http/1.1"};
 ```
 - 连接单点和集群的Channel均可以开启SSL访问(初始实现曾不支持集群)。
 - 开启后,该Channel上任何协议的请求,都会被SSL加密后发送。如果希望某些请求不加密,需要额外再创建一个Channel。
diff --git a/docs/en/client.md b/docs/en/client.md
index 358bf2f2..f199fc6b 100644
--- a/docs/en/client.md
+++ b/docs/en/client.md
@@ -755,6 +755,10 @@ options.mutable_ssl_options();
 // Enable client-side SSL and customize values.
 options.mutable_ssl_options()->ciphers_name = "...";
 options.mutable_ssl_options()->sni_name = "...";
+
+// Set the protocol preference of ALPN.
+// (By default ALPN is disabled.)
+options.mutable_ssl_options()->alpn_protocols = {"h2", "http/1.1"};
 ```
 
 - Channels connecting to a single server or a cluster both support SSL (the initial implementation does not support cluster)
diff --git a/src/brpc/channel.cpp b/src/brpc/channel.cpp
index a94c09b5..67ab4969 100644
--- a/src/brpc/channel.cpp
+++ b/src/brpc/channel.cpp
@@ -303,6 +303,7 @@ static int CreateSocketSSLContext(const ChannelOptions& options,
         *ssl_ctx = std::make_shared<SocketSSLContext>();
         (*ssl_ctx)->raw_ctx = raw_ctx;
         (*ssl_ctx)->sni_name = options.ssl_options().sni_name;
+        (*ssl_ctx)->alpn_protocols = options.ssl_options().alpn_protocols;
     } else {
         (*ssl_ctx) = NULL;
     }
diff --git a/src/brpc/details/ssl_helper.cpp b/src/brpc/details/ssl_helper.cpp
index a0275261..d33d0ee7 100644
--- a/src/brpc/details/ssl_helper.cpp
+++ b/src/brpc/details/ssl_helper.cpp
@@ -498,6 +498,14 @@ SSL_CTX* CreateClientSSLContext(const ChannelSSLOptions& options) {
         return NULL;
     }
 
+    if (!options.alpn_protocols.empty()) {
+        std::vector<unsigned char> alpn_list;
+        if (!BuildALPNProtocolList(options.alpn_protocols, alpn_list)) {
+            return NULL;
+        }
+        SSL_CTX_set_alpn_protos(ssl_ctx.get(), alpn_list.data(), alpn_list.size());
+    }
+
     SSL_CTX_set_session_cache_mode(ssl_ctx.get(), SSL_SESS_CACHE_CLIENT);
     return ssl_ctx.release();
 }
@@ -896,6 +904,36 @@ std::string ALPNProtocolToString(const AdaptiveProtocolType& protocol) {
     return std::string(&length, 1) + name.data(); 
 }
 
+bool BuildALPNProtocolList(
+    const std::vector<std::string>& alpn_protocols,
+    std::vector<unsigned char>& result
+) {
+    size_t alpn_list_length = 0;
+    for (const auto& alpn_protocol : alpn_protocols) {
+        if (alpn_protocol.size() > UCHAR_MAX) {
+            LOG(ERROR) << "Fail to build ALPN procotol list: "
+                       << "protocol name length " << alpn_protocol.size() << " too long, "
+                       << "max 255 supported.";
+            return false;
+        }
+        alpn_list_length += alpn_protocol.size() + 1;
+    }
+
+    result.resize(alpn_list_length);
+    for (size_t curr = 0, i = 0; i < alpn_protocols.size(); i++) {
+        result[curr++] = static_cast<unsigned char>(
+            alpn_protocols[i].size()
+        );
+        std::copy(
+            alpn_protocols[i].begin(),
+            alpn_protocols[i].end(),
+            result.begin() + curr
+        );
+        curr += alpn_protocols[i].size();
+    }
+    return true;
+}
+
 } // namespace brpc
 
 #endif // USE_MESALINK
diff --git a/src/brpc/details/ssl_helper.h b/src/brpc/details/ssl_helper.h
index 4f1e55b2..da126b39 100644
--- a/src/brpc/details/ssl_helper.h
+++ b/src/brpc/details/ssl_helper.h
@@ -113,6 +113,13 @@ void Print(std::ostream& os, X509* cert, const char* sep);
 
 std::string ALPNProtocolToString(const AdaptiveProtocolType& protocol);
 
+// Build a binary formatted ALPN protocol list that OpenSSL's
+// `SSL_CTX_set_alpn_protos` accepts from a C++ string vector.
+bool BuildALPNProtocolList(
+    const std::vector<std::string>& alpn_protocols,
+    std::vector<unsigned char>& result
+);
+
 } // namespace brpc
 
 #endif // BRPC_SSL_HELPER_H
diff --git a/src/brpc/socket.cpp b/src/brpc/socket.cpp
index acd1b54d..2a391f35 100644
--- a/src/brpc/socket.cpp
+++ b/src/brpc/socket.cpp
@@ -1958,6 +1958,33 @@ int Socket::SSLHandshake(int fd, bool server_mode) {
         ERR_clear_error();
         int rc = SSL_do_handshake(_ssl_session);
         if (rc == 1) {
+            // In client, check if server returned ALPN selection is acceptable.
+            if (!server_mode && !_ssl_ctx->alpn_protocols.empty()) {
+                const unsigned char *alpn_proto;
+                unsigned int alpn_proto_length;
+                SSL_get0_alpn_selected(_ssl_session, &alpn_proto, &alpn_proto_length);
+                if (!alpn_proto) {
+                    LOG(ERROR) << "Server returned no ALPN protocol";
+                    return -1;
+                }
+
+                std::string alpn_protocol(
+                    reinterpret_cast<char const *>(alpn_proto),
+                    alpn_proto_length
+                );
+                if (
+                    std::find(
+                        _ssl_ctx->alpn_protocols.begin(),
+                        _ssl_ctx->alpn_protocols.end(),
+                        alpn_protocol
+                    ) == _ssl_ctx->alpn_protocols.end()
+                ) {
+                    LOG(ERROR) << "Server returned unacceptable ALPN protocol: "
+                               << alpn_protocol;
+                    return -1;
+                }
+            }
+
             _ssl_state = SSL_CONNECTED;
             AddBIOBuffer(_ssl_session, fd, FLAGS_ssl_bio_buffer_size);
             return 0;
diff --git a/src/brpc/socket.h b/src/brpc/socket.h
index 44ea0b02..9d85aafa 100644
--- a/src/brpc/socket.h
+++ b/src/brpc/socket.h
@@ -170,8 +170,9 @@ struct SocketSSLContext {
     SocketSSLContext();
     ~SocketSSLContext();
 
-    SSL_CTX* raw_ctx;           // owned
-    std::string sni_name;       // useful for clients
+    SSL_CTX* raw_ctx;                        // owned
+    std::string sni_name;                    // useful for clients
+    std::vector<std::string> alpn_protocols; // useful for clients
 };
 
 struct SocketKeepaliveOptions {
diff --git a/src/brpc/ssl_options.h b/src/brpc/ssl_options.h
index c7caa1dd..bbe9ccf1 100644
--- a/src/brpc/ssl_options.h
+++ b/src/brpc/ssl_options.h
@@ -84,6 +84,10 @@ struct ChannelSSLOptions {
     // Default: see above
     VerifyOptions verify;
 
+    // Set the protocol preference of ALPN (Application-Layer Protocol Negotiation)
+    // Default: unset
+    std::vector<std::string> alpn_protocols;
+
     // TODO: Support CRL
 };
 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@brpc.apache.org
For additional commands, e-mail: dev-help@brpc.apache.org