diff --git a/README.md b/README.md index 588c17c92..07a6cfe6d 100755 --- a/README.md +++ b/README.md @@ -148,6 +148,10 @@ For previous versions, please read: ## V3 changes +* v3.0, 2020-01-05, Always use string instance to avoid crash risk. 3.0.95 +* v3.0, 2020-01-05, For [#460][bug #460], fix ipv6 hostport parsing bug. 3.0.94 +* v3.0, 2020-01-05, For [#460][bug #460], fix ipv6 intranet address filter bug. 3.0.93 +* v3.0, 2020-01-05, For [#1543][bug #1543], use getpeername to retrieve client ip. 3.0.92 * v3.0, 2020-01-02, For [#1042][bug #1042], improve test coverage for config. 3.0.91 * v3.0, 2019-12-30, Fix mp4 security issue, check buffer when required size is variable. * v3.0, 2019-12-29, [3.0 alpha7(3.0.90)][r3.0a7] released. 116356 lines. @@ -1583,6 +1587,7 @@ Winlin [bug #1105]: https://github.com/ossrs/srs/issues/1105 [bug #1544]: https://github.com/ossrs/srs/issues/1544 [bug #1255]: https://github.com/ossrs/srs/issues/1255 +[bug #1543]: https://github.com/ossrs/srs/issues/1543 [bug #xxxxxxxxxxxxx]: https://github.com/ossrs/srs/issues/xxxxxxxxxxxxx [bug #1111]: https://github.com/ossrs/srs/issues/1111 diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 026ac8801..a0277f3c9 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -296,7 +296,7 @@ bool srs_config_apply_filter(SrsConfDirective* dvr_apply, SrsRequest* req) return false; } -string srs_config_bool2switch(const string& sbool) +string srs_config_bool2switch(string sbool) { return sbool == "true"? "on":"off"; } diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 6bc0b4cf2..c9c03e24f 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -123,7 +123,7 @@ extern bool srs_stream_caster_is_flv(std::string caster); extern bool srs_config_apply_filter(SrsConfDirective* dvr_apply, SrsRequest* req); // Convert bool in str to on/off -extern std::string srs_config_bool2switch(const std::string& sbool); +extern std::string srs_config_bool2switch(std::string sbool); // Parse loaded vhost directives to compatible mode. // For exmaple, SRS1/2 use the follow refer style: diff --git a/trunk/src/app/srs_app_st.cpp b/trunk/src/app/srs_app_st.cpp index 1e6ae4371..831a0613a 100755 --- a/trunk/src/app/srs_app_st.cpp +++ b/trunk/src/app/srs_app_st.cpp @@ -81,7 +81,7 @@ int SrsDummyCoroutine::cid() _ST_THREAD_CREATE_PFN _pfn_st_thread_create = (_ST_THREAD_CREATE_PFN)st_thread_create; -SrsSTCoroutine::SrsSTCoroutine(const string& n, ISrsCoroutineHandler* h, int cid) +SrsSTCoroutine::SrsSTCoroutine(string n, ISrsCoroutineHandler* h, int cid) { name = n; handler = h; diff --git a/trunk/src/app/srs_app_st.hpp b/trunk/src/app/srs_app_st.hpp index 131ec697c..956e00b42 100644 --- a/trunk/src/app/srs_app_st.hpp +++ b/trunk/src/app/srs_app_st.hpp @@ -132,7 +132,7 @@ private: public: // Create a thread with name n and handler h. // @remark User can specify a cid for thread to use, or we will allocate a new one. - SrsSTCoroutine(const std::string& n, ISrsCoroutineHandler* h, int cid = 0); + SrsSTCoroutine(std::string n, ISrsCoroutineHandler* h, int cid = 0); virtual ~SrsSTCoroutine(); public: // Start the thread. diff --git a/trunk/src/app/srs_app_utility.cpp b/trunk/src/app/srs_app_utility.cpp index 9374f4e4b..2dc149ddc 100644 --- a/trunk/src/app/srs_app_utility.cpp +++ b/trunk/src/app/srs_app_utility.cpp @@ -1142,7 +1142,7 @@ string srs_get_peer_ip(int fd) // discovery client information sockaddr_storage addr; socklen_t addrlen = sizeof(addr); - if (getsockname(fd, (sockaddr*)&addr, &addrlen) == -1) { + if (getpeername(fd, (sockaddr*)&addr, &addrlen) == -1) { return ""; } @@ -1157,7 +1157,7 @@ string srs_get_peer_ip(int fd) return std::string(saddr); } -bool srs_is_boolean(const string& str) +bool srs_is_boolean(string str) { return str == "true" || str == "false"; } diff --git a/trunk/src/app/srs_app_utility.hpp b/trunk/src/app/srs_app_utility.hpp index 26da0106d..be7cbced6 100644 --- a/trunk/src/app/srs_app_utility.hpp +++ b/trunk/src/app/srs_app_utility.hpp @@ -644,7 +644,7 @@ extern std::string srs_get_peer_ip(int fd); // is_bool("true") == true // is_bool("false") == true // otherwise, false. -extern bool srs_is_boolean(const std::string& str); +extern bool srs_is_boolean(std::string str); // Dump summaries for /api/v1/summaries. extern void srs_api_dump_summaries(SrsJsonObject* obj); diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 06f93f5cb..0eb763b92 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -27,7 +27,7 @@ // The version config. #define VERSION_MAJOR 3 #define VERSION_MINOR 0 -#define VERSION_REVISION 91 +#define VERSION_REVISION 95 // The macros generated by configure script. #include diff --git a/trunk/src/kernel/srs_kernel_mp4.cpp b/trunk/src/kernel/srs_kernel_mp4.cpp index 1845a87b2..5cfc5ac9f 100644 --- a/trunk/src/kernel/srs_kernel_mp4.cpp +++ b/trunk/src/kernel/srs_kernel_mp4.cpp @@ -110,12 +110,12 @@ void srs_mp4_delimiter_newline(stringstream& ss, SrsMp4DumpContext dc) srs_mp4_padding(ss, dc); } -int srs_mp4_string_length(const string& v) +int srs_mp4_string_length(string v) { return (int)v.length()+1; } -void srs_mp4_string_write(SrsBuffer* buf, const string& v) +void srs_mp4_string_write(SrsBuffer* buf, string v) { if (!v.empty()) { buf->write_bytes((char*)v.data(), (int)v.length()); diff --git a/trunk/src/kernel/srs_kernel_mp4.hpp b/trunk/src/kernel/srs_kernel_mp4.hpp index 6d06ea106..161f2c032 100644 --- a/trunk/src/kernel/srs_kernel_mp4.hpp +++ b/trunk/src/kernel/srs_kernel_mp4.hpp @@ -2169,7 +2169,7 @@ std::stringstream& srs_dumps_array(std::vector&arr, std::stringstream& ss, Sr pfn(elem, ss, dc); - if (i < limit - 1) { + if ((int)i < limit - 1) { delimiter(ss, dc); } } @@ -2192,7 +2192,7 @@ std::stringstream& srs_dumps_array(T* arr, int size, std::stringstream& ss, SrsM pfn(elem, ss, dc); - if (i < limit - 1) { + if ((int)i < limit - 1) { delimiter(ss, dc); } } diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index 1a38c7e1e..861be3610 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -170,44 +170,60 @@ string srs_dns_resolve(string host, int& family) { addrinfo hints; memset(&hints, 0, sizeof(hints)); - hints.ai_family = family; + hints.ai_family = family; addrinfo* r = NULL; SrsAutoFree(addrinfo, r); - - if(getaddrinfo(host.c_str(), NULL, NULL, &r)) { + if(getaddrinfo(host.c_str(), NULL, &hints, &r)) { return ""; } - char saddr[64]; - char* h = (char*)saddr; - socklen_t nbh = sizeof(saddr); - const int r0 = getnameinfo(r->ai_addr, r->ai_addrlen, h, nbh, NULL, 0, NI_NUMERICHOST); - - if(!r0) { - family = r->ai_family; - return string(saddr); + char shost[64]; + memset(shost, 0, sizeof(shost)); + if (getnameinfo(r->ai_addr, r->ai_addrlen, shost, sizeof(shost), NULL, 0, NI_NUMERICHOST)) { + return ""; } - return ""; + + family = r->ai_family; + return string(shost); } -void srs_parse_hostport(const string& hostport, string& host, int& port) +void srs_parse_hostport(string hostport, string& host, int& port) { - const size_t pos = hostport.rfind(":"); // Look for ":" from the end, to work with IPv6. - if (pos != std::string::npos) { - const string p = hostport.substr(pos + 1); - if ((pos >= 1) && - (hostport[0] == '[') && - (hostport[pos - 1] == ']')) { - // Handle IPv6 in RFC 2732 format, e.g. [3ffe:dead:beef::1]:1935 - host = hostport.substr(1, pos - 2); - } else { - // Handle IP address - host = hostport.substr(0, pos); - } - port = ::atoi(p.c_str()); - } else { + // No host or port. + if (hostport.empty()) { + return; + } + + size_t pos = string::npos; + + // Host only for ipv4. + if ((pos = hostport.rfind(":")) == string::npos) { host = hostport; + return; + } + + // For ipv4(only one colon), host:port. + if (hostport.find(":") == pos) { + host = hostport.substr(0, pos); + string p = hostport.substr(pos + 1); + if (!p.empty()) { + port = ::atoi(p.c_str()); + } + return; + } + + // Host only for ipv6. + if (hostport.at(0) != '[' || (pos = hostport.rfind("]:")) == string::npos) { + host = hostport; + return; + } + + // For ipv6, [host]:port. + host = hostport.substr(1, pos - 1); + string p = hostport.substr(pos + 2); + if (!p.empty()) { + port = ::atoi(p.c_str()); } } diff --git a/trunk/src/kernel/srs_kernel_utility.hpp b/trunk/src/kernel/srs_kernel_utility.hpp index 765a86546..04d386298 100644 --- a/trunk/src/kernel/srs_kernel_utility.hpp +++ b/trunk/src/kernel/srs_kernel_utility.hpp @@ -56,7 +56,7 @@ extern std::string srs_dns_resolve(std::string host, int& family); // Split the host:port to host and port. // @remark the hostport format in , where port is optional. -extern void srs_parse_hostport(const std::string& hostport, std::string& host, int& port); +extern void srs_parse_hostport(std::string hostport, std::string& host, int& port); // Parse the endpoint to ip and port. // @remark The hostport format in <[ip:]port>, where ip is default to "0.0.0.0". diff --git a/trunk/src/protocol/srs_protocol_json.cpp b/trunk/src/protocol/srs_protocol_json.cpp index 82f099114..c742bb158 100644 --- a/trunk/src/protocol/srs_protocol_json.cpp +++ b/trunk/src/protocol/srs_protocol_json.cpp @@ -1731,7 +1731,7 @@ SrsJsonAny* srs_json_parse_tree(json_value* node) } } -SrsJsonAny* SrsJsonAny::loads(const string& str) +SrsJsonAny* SrsJsonAny::loads(string str) { if (str.empty()) { return NULL; diff --git a/trunk/src/protocol/srs_protocol_json.hpp b/trunk/src/protocol/srs_protocol_json.hpp index cac07a8d5..273b237dc 100644 --- a/trunk/src/protocol/srs_protocol_json.hpp +++ b/trunk/src/protocol/srs_protocol_json.hpp @@ -107,7 +107,7 @@ public: public: // Read json tree from string. // @return json object. NULL if error. - static SrsJsonAny* loads(const std::string& str); + static SrsJsonAny* loads(std::string str); }; class SrsJsonObject : public SrsJsonAny diff --git a/trunk/src/service/srs_service_st.cpp b/trunk/src/service/srs_service_st.cpp index 512ec563c..6cd04ba93 100644 --- a/trunk/src/service/srs_service_st.cpp +++ b/trunk/src/service/srs_service_st.cpp @@ -157,7 +157,7 @@ srs_error_t srs_tcp_connect(string server, int port, srs_utime_t tm, srs_netfd_t addrinfo hints; memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; + hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; addrinfo* r = NULL; @@ -187,6 +187,43 @@ srs_error_t srs_tcp_connect(string server, int port, srs_utime_t tm, srs_netfd_t return srs_success; } +srs_error_t do_srs_tcp_listen(int fd, addrinfo* r, srs_netfd_t* pfd) +{ + srs_error_t err = srs_success; + + // Detect alive for TCP connection. + // @see https://github.com/ossrs/srs/issues/1044 + if ((err = srs_fd_keepalive(fd)) != srs_success) { + return srs_error_wrap(err, "set keepalive"); + } + + if ((err = srs_fd_closeexec(fd)) != srs_success) { + return srs_error_wrap(err, "set closeexec"); + } + + if ((err = srs_fd_reuseaddr(fd)) != srs_success) { + return srs_error_wrap(err, "set reuseaddr"); + } + + if ((err = srs_fd_reuseport(fd)) != srs_success) { + return srs_error_wrap(err, "set reuseport"); + } + + if (bind(fd, r->ai_addr, r->ai_addrlen) == -1) { + return srs_error_new(ERROR_SOCKET_BIND, "bind"); + } + + if (::listen(fd, SERVER_LISTEN_BACKLOG) == -1) { + return srs_error_new(ERROR_SOCKET_LISTEN, "listen"); + } + + if ((*pfd = srs_netfd_open_socket(fd)) == NULL){ + return srs_error_new(ERROR_ST_OPEN_SOCKET, "st open"); + } + + return err; +} + srs_error_t srs_tcp_listen(std::string ip, int port, srs_netfd_t* pfd) { srs_error_t err = srs_success; @@ -213,41 +250,36 @@ srs_error_t srs_tcp_listen(std::string ip, int port, srs_netfd_t* pfd) r->ai_family, r->ai_socktype, r->ai_protocol); } - // Detect alive for TCP connection. - // @see https://github.com/ossrs/srs/issues/1044 - if ((err = srs_fd_keepalive(fd)) != srs_success) { + if ((err = do_srs_tcp_listen(fd, r, pfd)) != srs_success) { ::close(fd); - return srs_error_wrap(err, "set keepalive fd=%d", fd); + return srs_error_wrap(err, "fd=%d", fd); } + return err; +} + +srs_error_t do_srs_udp_listen(int fd, addrinfo* r, srs_netfd_t* pfd) +{ + srs_error_t err = srs_success; + if ((err = srs_fd_closeexec(fd)) != srs_success) { - ::close(fd); - return srs_error_wrap(err, "set closeexec fd=%d", fd); + return srs_error_wrap(err, "set closeexec"); } if ((err = srs_fd_reuseaddr(fd)) != srs_success) { - ::close(fd); - return srs_error_wrap(err, "set reuseaddr fd=%d", fd); + return srs_error_wrap(err, "set reuseaddr"); } if ((err = srs_fd_reuseport(fd)) != srs_success) { - ::close(fd); - return srs_error_wrap(err, "set reuseport fd=%d", fd); + return srs_error_wrap(err, "set reuseport"); } if (bind(fd, r->ai_addr, r->ai_addrlen) == -1) { - ::close(fd); - return srs_error_new(ERROR_SOCKET_BIND, "bind fd=%d", fd); - } - - if (::listen(fd, SERVER_LISTEN_BACKLOG) == -1) { - ::close(fd); - return srs_error_new(ERROR_SOCKET_LISTEN, "listen fd=%d", fd); + return srs_error_new(ERROR_SOCKET_BIND, "bind"); } if ((*pfd = srs_netfd_open_socket(fd)) == NULL){ - ::close(fd); - return srs_error_new(ERROR_ST_OPEN_SOCKET, "st open fd=%d", fd); + return srs_error_new(ERROR_ST_OPEN_SOCKET, "st open"); } return err; @@ -279,29 +311,9 @@ srs_error_t srs_udp_listen(std::string ip, int port, srs_netfd_t* pfd) r->ai_family, r->ai_socktype, r->ai_protocol); } - if ((err = srs_fd_closeexec(fd)) != srs_success) { + if ((err = do_srs_udp_listen(fd, r, pfd)) != srs_success) { ::close(fd); - return srs_error_wrap(err, "set closeexec fd=%d", fd); - } - - if ((err = srs_fd_reuseaddr(fd)) != srs_success) { - ::close(fd); - return srs_error_wrap(err, "set reuseaddr fd=%d", fd); - } - - if ((err = srs_fd_reuseport(fd)) != srs_success) { - ::close(fd); - return srs_error_wrap(err, "set reuseport fd=%d", fd); - } - - if (bind(fd, r->ai_addr, r->ai_addrlen) == -1) { - ::close(fd); - return srs_error_new(ERROR_SOCKET_BIND, "bind fd=%d", fd); - } - - if ((*pfd = srs_netfd_open_socket(fd)) == NULL){ - ::close(fd); - return srs_error_new(ERROR_ST_OPEN_SOCKET, "st open fd=%d", fd); + return srs_error_wrap(err, "fd=%d", fd); } return err; diff --git a/trunk/src/service/srs_service_utility.cpp b/trunk/src/service/srs_service_utility.cpp index cc3eade17..108240bdb 100644 --- a/trunk/src/service/srs_service_utility.cpp +++ b/trunk/src/service/srs_service_utility.cpp @@ -51,7 +51,7 @@ bool srs_string_is_rtmp(string url) return srs_string_starts_with(url, "rtmp://"); } -bool srs_is_digit_number(const string& str) +bool srs_is_digit_number(string str) { if (str.empty()) { return false; @@ -115,8 +115,28 @@ bool srs_net_device_is_internet(const sockaddr* addr) } } else if(addr->sa_family == AF_INET6) { const sockaddr_in6* a6 = (const sockaddr_in6*)addr; - if ((IN6_IS_ADDR_LINKLOCAL(&a6->sin6_addr)) || - (IN6_IS_ADDR_SITELOCAL(&a6->sin6_addr))) { + + // IPv6 loopback is ::1 + if (IN6_IS_ADDR_LOOPBACK(&a6->sin6_addr)) { + return false; + } + + // IPv6 unspecified is :: + if (IN6_IS_ADDR_UNSPECIFIED(&a6->sin6_addr)) { + return false; + } + + // From IPv4, you might know APIPA (Automatic Private IP Addressing) or AutoNet. + // Whenever automatic IP configuration through DHCP fails. + // The prefix of a site-local address is FE80::/10. + if (IN6_IS_ADDR_LINKLOCAL(&a6->sin6_addr)) { + return false; + } + + // Site-local addresses are equivalent to private IP addresses in IPv4. + // The prefix of a site-local address is FEC0::/10. + // https://4sysops.com/archives/ipv6-tutorial-part-6-site-local-addresses-and-link-local-addresses/ + if (IN6_IS_ADDR_SITELOCAL(&a6->sin6_addr)) { return false; } } diff --git a/trunk/src/service/srs_service_utility.hpp b/trunk/src/service/srs_service_utility.hpp index c41a86215..ce1acc3ed 100644 --- a/trunk/src/service/srs_service_utility.hpp +++ b/trunk/src/service/srs_service_utility.hpp @@ -48,7 +48,7 @@ extern bool srs_string_is_rtmp(std::string url); // is_digit("10e3") === false // is_digit("!1234567890") === false // is_digit("") === false -extern bool srs_is_digit_number(const std::string& str); +extern bool srs_is_digit_number(std::string str); // Get local ip, fill to @param ips extern std::vector& srs_get_local_ips(); diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 8f62186cc..181bc58b1 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -4253,6 +4253,118 @@ VOID TEST(KernelUtilityTest, CoverTimeUtilityAll) _srs_system_time_us_cache -= 300*1000 * 1000 + 1; EXPECT_TRUE(srs_update_system_time() > 0); + + if (true) { + string host = "127.0.0.1:1935"; + int port = 0; + srs_parse_hostport(host, host, port); + EXPECT_EQ(1935, port); + EXPECT_STREQ("127.0.0.1", host.c_str()); + } + + if (true) { + string host; + int port = 8080; + srs_parse_hostport("::1", host, port); + EXPECT_EQ(8080, port); + EXPECT_STREQ("::1", host.c_str()); + } + + if (true) { + string host; + int port = 8080; + srs_parse_hostport("::", host, port); + EXPECT_EQ(8080, port); + EXPECT_STREQ("::", host.c_str()); + } + + if (true) { + string host; + int port = 0; + srs_parse_hostport("3ffe:dead:beef::1", host, port); + EXPECT_EQ(0, port); + EXPECT_STREQ("3ffe:dead:beef::1", host.c_str()); + } + + if (true) { + string host; + int port = 10; + srs_parse_hostport("2001:da8:6000:291:21f:d0ff:fed4:928c", host, port); + EXPECT_EQ(10, port); + EXPECT_STREQ("2001:da8:6000:291:21f:d0ff:fed4:928c", host.c_str()); + } + + if (true) { + string host; + int port = 0; + srs_parse_hostport("[2001:da8:6000:291:21f:d0ff:fed4:928c]:167", host, port); + EXPECT_EQ(167, port); + EXPECT_STREQ("2001:da8:6000:291:21f:d0ff:fed4:928c", host.c_str()); + } + + if (true) { + string host; + int port = 0; + srs_parse_hostport("[::A.B.C.D]:167", host, port); + EXPECT_EQ(167, port); + EXPECT_STREQ("::A.B.C.D", host.c_str()); + } + + if (true) { + string host; + int port = 0; + srs_parse_hostport("::A.B.C.D", host, port); + EXPECT_EQ(0, port); + EXPECT_STREQ("::A.B.C.D", host.c_str()); + } + + if (true) { + string host; + int port = 0; + srs_parse_hostport("[::FFFF:A.B.C.D]:167", host, port); + EXPECT_EQ(167, port); + EXPECT_STREQ("::FFFF:A.B.C.D", host.c_str()); + } + + if (true) { + string host; + int port = 0; + srs_parse_hostport("[ff00::]:167", host, port); + EXPECT_EQ(167, port); + EXPECT_STREQ("ff00::", host.c_str()); + } + + if (true) { + string host; + int port = 0; + srs_parse_hostport("[fe80::a00:27ff:fe84:be2%eth0]:167", host, port); + EXPECT_EQ(167, port); + EXPECT_STREQ("fe80::a00:27ff:fe84:be2%eth0", host.c_str()); + } + + if (true) { + string host; + int port = 0; + srs_parse_hostport("::FFFF:A.B.C.D", host, port); + EXPECT_EQ(0, port); + EXPECT_STREQ("::FFFF:A.B.C.D", host.c_str()); + } + + if (true) { + string host; + int port = 8080; + srs_parse_hostport("", host, port); + EXPECT_EQ(8080, port); + EXPECT_STREQ("", host.c_str()); + } + + if (true) { + string host; + int port = 8080; + srs_parse_hostport("3ffe:dead:beef::1", host, port); + EXPECT_EQ(8080, port); + EXPECT_STREQ("3ffe:dead:beef::1", host.c_str()); + } if (true) { string host; diff --git a/trunk/src/utest/srs_utest_service.cpp b/trunk/src/utest/srs_utest_service.cpp index 9307ab48f..e75f7285d 100644 --- a/trunk/src/utest/srs_utest_service.cpp +++ b/trunk/src/utest/srs_utest_service.cpp @@ -35,6 +35,9 @@ using namespace std; #include #include #include +#include +#include +#include class MockSrsConnection : public ISrsConnection { @@ -679,3 +682,400 @@ VOID TEST(TCPServerTest, MessageWritev) } } +VOID TEST(TCPServerTest, TCPListen) +{ + srs_error_t err; + + // Failed for invalid ip. + if (true) { + srs_netfd_t pfd = NULL; + HELPER_EXPECT_FAILED(srs_tcp_listen("10.0.0.abc", 1935, &pfd)); + srs_close_stfd(pfd); + } + + // If listen multiple times, should success for we already set the REUSEPORT. + if (true) { + srs_netfd_t pfd = NULL; + HELPER_ASSERT_SUCCESS(srs_tcp_listen("127.0.0.1", 1935, &pfd)); + + srs_netfd_t pfd2 = NULL; + srs_error_t err2 = srs_tcp_listen("127.0.0.1", 1935, &pfd2); + + srs_close_stfd(pfd); + srs_close_stfd(pfd2); + HELPER_EXPECT_SUCCESS(err2); + } + + // Typical listen. + if (true) { + srs_netfd_t pfd = NULL; + HELPER_ASSERT_SUCCESS(srs_tcp_listen("127.0.0.1", 1935, &pfd)); + srs_close_stfd(pfd); + } +} + +VOID TEST(TCPServerTest, UDPListen) +{ + srs_error_t err; + + // Failed for invalid ip. + if (true) { + srs_netfd_t pfd = NULL; + HELPER_EXPECT_FAILED(srs_udp_listen("10.0.0.abc", 1935, &pfd)); + srs_close_stfd(pfd); + } + + // If listen multiple times, should success for we already set the REUSEPORT. + if (true) { + srs_netfd_t pfd = NULL; + HELPER_ASSERT_SUCCESS(srs_udp_listen("127.0.0.1", 1935, &pfd)); + + srs_netfd_t pfd2 = NULL; + srs_error_t err2 = srs_udp_listen("127.0.0.1", 1935, &pfd2); + + srs_close_stfd(pfd); + srs_close_stfd(pfd2); + HELPER_EXPECT_SUCCESS(err2); + } + + // Typical listen. + if (true) { + srs_netfd_t pfd = NULL; + HELPER_ASSERT_SUCCESS(srs_udp_listen("127.0.0.1", 1935, &pfd)); + srs_close_stfd(pfd); + } +} + +class MockOnCycleThread : public ISrsCoroutineHandler +{ +public: + SrsSTCoroutine trd; + srs_cond_t cond; + MockOnCycleThread() : trd("mock", this, 0) { + cond = srs_cond_new(); + }; + virtual ~MockOnCycleThread() { + srs_cond_destroy(cond); + } + virtual srs_error_t cycle() { + srs_error_t err = srs_success; + + for (;;) { + srs_usleep(10 * SRS_UTIME_MILLISECONDS); + srs_cond_signal(cond); + // If no one waiting on the cond, directly return event signal more than one time. + // If someone waiting, signal them more than one time. + srs_cond_signal(cond); + + if ((err = trd.pull()) != srs_success) { + return err; + } + } + + return err; + } +}; + +VOID TEST(TCPServerTest, ThreadCondWait) +{ + MockOnCycleThread trd; + trd.trd.start(); + + srs_usleep(20 * SRS_UTIME_MILLISECONDS); + srs_cond_wait(trd.cond); + trd.trd.stop(); +} + +class MockOnCycleThread2 : public ISrsCoroutineHandler +{ +public: + SrsSTCoroutine trd; + srs_mutex_t lock; + MockOnCycleThread2() : trd("mock", this, 0) { + lock = srs_mutex_new(); + }; + virtual ~MockOnCycleThread2() { + srs_mutex_destroy(lock); + } + virtual srs_error_t cycle() { + srs_error_t err = srs_success; + + for (;;) { + srs_mutex_lock(lock); + srs_usleep(10 * SRS_UTIME_MILLISECONDS); + srs_mutex_unlock(lock); + + srs_error_t err = trd.pull(); + if (err != srs_success) { + return err; + } + } + + return err; + } +}; + +VOID TEST(TCPServerTest, ThreadMutexWait) +{ + MockOnCycleThread2 trd; + trd.trd.start(); + + srs_usleep(20 * SRS_UTIME_MILLISECONDS); + + srs_mutex_lock(trd.lock); + trd.trd.stop(); + srs_mutex_unlock(trd.lock); +} + +class MockOnCycleThread3 : public ISrsCoroutineHandler +{ +public: + SrsSTCoroutine trd; + srs_netfd_t fd; + MockOnCycleThread3() : trd("mock", this, 0) { + }; + virtual ~MockOnCycleThread3() { + trd.stop(); + srs_close_stfd(fd); + } + virtual srs_error_t start(string ip, int port) { + srs_error_t err = srs_success; + if ((err = srs_tcp_listen(ip, port, &fd)) != srs_success) { + return err; + } + + return trd.start(); + } + virtual srs_error_t do_cycle(srs_netfd_t cfd) { + srs_error_t err = srs_success; + + SrsStSocket skt; + if ((err = skt.initialize(cfd)) != srs_success) { + return err; + } + + skt.set_recv_timeout(1 * SRS_UTIME_SECONDS); + skt.set_send_timeout(1 * SRS_UTIME_SECONDS); + + while (true) { + if ((err = trd.pull()) != srs_success) { + return err; + } + + char buf[5]; + if ((err = skt.read_fully(buf, 5, NULL)) != srs_success) { + return err; + } + if ((err = skt.write(buf, 5, NULL)) != srs_success) { + return err; + } + } + + return err; + } + virtual srs_error_t cycle() { + srs_error_t err = srs_success; + + srs_netfd_t cfd = srs_accept(fd, NULL, NULL, SRS_UTIME_NO_TIMEOUT); + if (cfd == NULL) { + return err; + } + + err = do_cycle(cfd); + srs_close_stfd(cfd); + srs_freep(err); + + return err; + } +}; + +VOID TEST(TCPServerTest, TCPClientServer) +{ + srs_error_t err; + + MockOnCycleThread3 trd; + HELPER_ASSERT_SUCCESS(trd.start("127.0.0.1", 1935)); + + SrsTcpClient c("127.0.0.1", 1935, 1 * SRS_UTIME_SECONDS); + HELPER_ASSERT_SUCCESS(c.connect()); + + c.set_recv_timeout(1 * SRS_UTIME_SECONDS); + c.set_send_timeout(1 * SRS_UTIME_SECONDS); + + if (true) { + HELPER_ASSERT_SUCCESS(c.write((void*)"Hello", 5, NULL)); + + char buf[6]; HELPER_ARRAY_INIT(buf, 6, 0); + HELPER_ASSERT_SUCCESS(c.read(buf, 5, NULL)); + EXPECT_STREQ("Hello", buf); + } + + if (true) { + HELPER_ASSERT_SUCCESS(c.write((void*)"Hello", 5, NULL)); + + char buf[6]; HELPER_ARRAY_INIT(buf, 6, 0); + HELPER_ASSERT_SUCCESS(c.read_fully(buf, 5, NULL)); + EXPECT_STREQ("Hello", buf); + } + + if (true) { + HELPER_ASSERT_SUCCESS(c.write((void*)"Hello", 5, NULL)); + + char buf[6]; HELPER_ARRAY_INIT(buf, 6, 0); + ASSERT_EQ(5, srs_read(c.stfd, buf, 5, 1*SRS_UTIME_SECONDS)); + EXPECT_STREQ("Hello", buf); + } +} + +VOID TEST(TCPServerTest, CoverUtility) +{ + EXPECT_TRUE(srs_string_is_http("http://")); + EXPECT_TRUE(srs_string_is_http("https://")); + EXPECT_TRUE(srs_string_is_http("http://localhost")); + EXPECT_TRUE(srs_string_is_http("https://localhost")); + EXPECT_FALSE(srs_string_is_http("ftp://")); + EXPECT_FALSE(srs_string_is_http("ftps://")); + EXPECT_FALSE(srs_string_is_http("http:")); + EXPECT_FALSE(srs_string_is_http("https:")); + EXPECT_TRUE(srs_string_is_rtmp("rtmp://")); + EXPECT_TRUE(srs_string_is_rtmp("rtmp://localhost")); + EXPECT_FALSE(srs_string_is_rtmp("http://")); + EXPECT_FALSE(srs_string_is_rtmp("rtmp:")); + + // ipv4 loopback + if (true) { + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + + addrinfo* r = NULL; + SrsAutoFree(addrinfo, r); + ASSERT_TRUE(!getaddrinfo("127.0.0.1", NULL, &hints, &r)); + + EXPECT_FALSE(srs_net_device_is_internet((sockaddr*)r->ai_addr)); + } + + // ipv4 intranet + if (true) { + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + + addrinfo* r = NULL; + SrsAutoFree(addrinfo, r); + ASSERT_TRUE(!getaddrinfo("192.168.0.1", NULL, &hints, &r)); + + EXPECT_FALSE(srs_net_device_is_internet((sockaddr*)r->ai_addr)); + } + + EXPECT_FALSE(srs_net_device_is_internet("eth0")); + + if (true) { + sockaddr_in addr; + addr.sin_family = AF_INET; + + addr.sin_addr.s_addr = htonl(0x12000000); + EXPECT_TRUE(srs_net_device_is_internet((sockaddr*)&addr)); + + addr.sin_addr.s_addr = htonl(0x7f000000); + EXPECT_FALSE(srs_net_device_is_internet((sockaddr*)&addr)); + + addr.sin_addr.s_addr = htonl(0x7f000001); + EXPECT_FALSE(srs_net_device_is_internet((sockaddr*)&addr)); + + addr.sin_addr.s_addr = htonl(0x0a000000); + EXPECT_FALSE(srs_net_device_is_internet((sockaddr*)&addr)); + + addr.sin_addr.s_addr = htonl(0x0a000001); + EXPECT_FALSE(srs_net_device_is_internet((sockaddr*)&addr)); + + addr.sin_addr.s_addr = htonl(0x0affffff); + EXPECT_FALSE(srs_net_device_is_internet((sockaddr*)&addr)); + + addr.sin_addr.s_addr = htonl(0xc0a80000); + EXPECT_FALSE(srs_net_device_is_internet((sockaddr*)&addr)); + + addr.sin_addr.s_addr = htonl(0xc0a80001); + EXPECT_FALSE(srs_net_device_is_internet((sockaddr*)&addr)); + + addr.sin_addr.s_addr = htonl(0xc0a8ffff); + EXPECT_FALSE(srs_net_device_is_internet((sockaddr*)&addr)); + } + + // Normal ipv6 address. + if (true) { + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + + addrinfo* r = NULL; + SrsAutoFree(addrinfo, r); + ASSERT_TRUE(!getaddrinfo("2001:da8:6000:291:21f:d0ff:fed4:928c", NULL, &hints, &r)); + + EXPECT_TRUE(srs_net_device_is_internet((sockaddr*)r->ai_addr)); + } + if (true) { + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + + addrinfo* r = NULL; + SrsAutoFree(addrinfo, r); + ASSERT_TRUE(!getaddrinfo("3ffe:dead:beef::1", NULL, &hints, &r)); + + EXPECT_TRUE(srs_net_device_is_internet((sockaddr*)r->ai_addr)); + } + + // IN6_IS_ADDR_UNSPECIFIED + if (true) { + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + + addrinfo* r = NULL; + SrsAutoFree(addrinfo, r); + ASSERT_TRUE(!getaddrinfo("::", NULL, &hints, &r)); + + EXPECT_FALSE(srs_net_device_is_internet((sockaddr*)r->ai_addr)); + } + + // IN6_IS_ADDR_SITELOCAL + if (true) { + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + + addrinfo* r = NULL; + SrsAutoFree(addrinfo, r); + ASSERT_TRUE(!getaddrinfo("fec0::", NULL, &hints, &r)); + + EXPECT_FALSE(srs_net_device_is_internet((sockaddr*)r->ai_addr)); + } + + // IN6_IS_ADDR_LINKLOCAL + if (true) { + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + + addrinfo* r = NULL; + SrsAutoFree(addrinfo, r); + ASSERT_TRUE(!getaddrinfo("FE80::", NULL, &hints, &r)); + + EXPECT_FALSE(srs_net_device_is_internet((sockaddr*)r->ai_addr)); + } + + // IN6_IS_ADDR_LINKLOCAL + if (true) { + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + + addrinfo* r = NULL; + SrsAutoFree(addrinfo, r); + ASSERT_TRUE(!getaddrinfo("::1", NULL, &hints, &r)); + + EXPECT_FALSE(srs_net_device_is_internet((sockaddr*)r->ai_addr)); + } +} + diff --git a/trunk/src/utest/srs_utest_service.hpp b/trunk/src/utest/srs_utest_service.hpp index 819272dc6..995b75e83 100644 --- a/trunk/src/utest/srs_utest_service.hpp +++ b/trunk/src/utest/srs_utest_service.hpp @@ -29,5 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include +#include + #endif