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 e98d2e745 do not serve rtmp on ServerOptions.internal_port (#3622)
e98d2e745 is described below

commit e98d2e7458f957d8d970826f7ed87d0b301d39fa
Author: sahvx655-wq <[email protected]>
AuthorDate: Sun Oct 25 15:18:35 2026 +0530

    do not serve rtmp on ServerOptions.internal_port (#3622)
    
    * do not serve rtmp on ServerOptions.internal_port
    
    * read the whole status prefix before comparing it in 
not_served_on_internal_port
    
    * require EOF after C0C1 in not_served_on_internal_port
    
    * write fully and retry EINTR in not_served_on_internal_port
---
 src/brpc/policy/rtmp_protocol.cpp |  7 ++++
 test/brpc_rtmp_unittest.cpp       | 84 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 91 insertions(+)

diff --git a/src/brpc/policy/rtmp_protocol.cpp 
b/src/brpc/policy/rtmp_protocol.cpp
index 41ed2422d..bf46922f7 100644
--- a/src/brpc/policy/rtmp_protocol.cpp
+++ b/src/brpc/policy/rtmp_protocol.cpp
@@ -3482,6 +3482,13 @@ ParseResult ParseRtmpMessage(butil::IOBuf* source, 
Socket *socket, bool read_eof
             // to avoid the confusion from root.
             return MakeParseError(PARSE_ERROR_TRY_OTHERS);
         }
+        if (IsInternalPort(*server, socket->local_side())) {
+            // ServerOptions.internal_port serves builtin and Tabbed services
+            // only and an RtmpService is neither. RTMP is handled right here
+            // without a Controller to reject the request with, so behave as
+            // if rtmp was not enabled on this port at all.
+            return MakeParseError(PARSE_ERROR_TRY_OTHERS);
+        }
         if (read_eof) {
             // No former RtmpContext when we read an EOF
             return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA);
diff --git a/test/brpc_rtmp_unittest.cpp b/test/brpc_rtmp_unittest.cpp
index 36e996b06..56737500b 100644
--- a/test/brpc_rtmp_unittest.cpp
+++ b/test/brpc_rtmp_unittest.cpp
@@ -30,6 +30,8 @@
 #include "butil/time.h"
 #include "butil/macros.h"
 #include "butil/fd_guard.h"
+#include "butil/file_util.h"
+#include "butil/posix/eintr_wrapper.h"
 #include "brpc/policy/rtmp_protocol.h"
 #include "brpc/socket.h"
 #include "brpc/acceptor.h"
@@ -1286,6 +1288,88 @@ TEST(RtmpTest, reject_unauthenticated_connection) {
     server.Join();
 }
 
+class RejectAllAuthenticator : public brpc::Authenticator {
+public:
+    int GenerateCredential(std::string*) const override { return 0; }
+    int VerifyCredential(const std::string&, const butil::EndPoint&,
+                         brpc::AuthContext*) const override { return -1; }
+};
+
+// Returns a port nothing is listening on, or -1.
+static int PickUnusedPort() {
+    butil::fd_guard sockfd(butil::tcp_listen(butil::EndPoint(butil::IP_ANY, 
0)));
+    if (sockfd < 0) {
+        return -1;
+    }
+    butil::EndPoint point;
+    if (butil::get_local_side(sockfd, &point) != 0) {
+        return -1;
+    }
+    return point.port;
+}
+
+TEST(RtmpTest, not_served_on_internal_port) {
+    PlayingDummyService rtmp_service;
+    RejectAllAuthenticator auth;
+    brpc::Server server;
+    brpc::ServerOptions server_opt;
+    server_opt.rtmp_service = &rtmp_service;
+    server_opt.auth = &auth;
+    bool started = false;
+    for (int i = 0; i < 10 && !started; ++i) {
+        server_opt.internal_port = PickUnusedPort();
+        started = (server_opt.internal_port >= 0 &&
+                   0 == server.Start("127.0.0.1:0", &server_opt));
+    }
+    ASSERT_TRUE(started);
+
+    butil::EndPoint internal_point;
+    ASSERT_EQ(0, butil::str2endpoint(
+        "127.0.0.1", server_opt.internal_port, &internal_point));
+    butil::fd_guard fd(butil::tcp_connect(internal_point, nullptr));
+    ASSERT_GE(fd, 0);
+    timeval tv = { 2, 0 };
+    ASSERT_EQ(0, setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)));
+
+    // Builtin services on internal_port pass authentication without any
+    // credential, which latches the connection as authenticated.
+    const std::string http_req =
+        "GET /version HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n";
+    ASSERT_EQ((int)http_req.size(), butil::WriteFileDescriptor(
+        fd, http_req.data(), http_req.size()));
+    // The reply may arrive in several segments, read until the status
+    // line prefix is complete.
+    const char status_prefix[] = "HTTP/1.1 200";
+    const size_t prefix_len = sizeof(status_prefix) - 1;
+    char resp[1024];
+    size_t nread = 0;
+    while (nread < prefix_len) {
+        const ssize_t n =
+            HANDLE_EINTR(read(fd, resp + nread, sizeof(resp) - nread));
+        ASSERT_GT(n, 0);
+        nread += n;
+    }
+    ASSERT_EQ(0, memcmp(resp, status_prefix, prefix_len));
+
+    // internal_port serves builtin and Tabbed services only, an RTMP
+    // handshake on the same connection must not reach rtmp_service. No
+    // protocol accepts the bytes there, so the server closes the connection
+    // and read() sees EOF. S0 (1 byte) means rtmp_service answered and -1
+    // is the receive timeout, i.e. the connection was left open; both fail.
+    char c0c1[1 + 1536];
+    memset(c0c1, 0, sizeof(c0c1));
+    c0c1[0] = 0x03;
+    ASSERT_EQ((int)sizeof(c0c1),
+              butil::WriteFileDescriptor(fd, c0c1, sizeof(c0c1)));
+    char s0 = 0;
+    const ssize_t n = HANDLE_EINTR(read(fd, &s0, 1));
+    const int saved_errno = errno;
+    ASSERT_EQ(0, n) << "n=" << n << " errno=" << saved_errno;
+
+    server.Stop(0);
+    server.Join();
+}
+
 TEST(RtmpTest, successfully_play_streams) {
     PlayingDummyService rtmp_service;
     brpc::Server server;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to