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