diff --git a/trunk/conf/console.ipv46.conf b/trunk/conf/console.ipv46.conf index 9e97d9780..508a20457 100644 --- a/trunk/conf/console.ipv46.conf +++ b/trunk/conf/console.ipv46.conf @@ -7,15 +7,77 @@ srs_log_tank console; # RTMP server configuration rtmp { - listen 1935 [::]:1935; + listen 1935 [::]:1935; +} +rtmps { + enabled on; + listen 1443 [::]:1443; } http_api { - enabled on; - listen 1985; + enabled on; + listen 1985 [::]:1985; + https { + enabled on; + listen 1990 [::]:1990; + } } http_server { - enabled on; - listen 8080; + enabled on; + listen 8080 [::]:8080; + https { + enabled on; + listen 8088 [::]:8088; + } +} +rtc_server { + enabled on; + + # @see https://ossrs.io/lts/en-us/docs/v7/doc/webrtc#config-candidate + candidate $CANDIDATE; + use_auto_detect_network_ip on; + #ip_family ipv4; + #ip_family ipv6; + ip_family all; + api_as_candidates off; + + #protocol udp; + #protocol tcp; + protocol all; + + # UDP port - IPv4 and IPv6 + listen 8000 [::]:8000; + + tcp { + enabled on; + listen 8000 [::]:8000; + } +} +srt_server { + enabled on; + listen 10080 [::]:10080; +} +rtsp_server { + enabled on; + listen 8554 [::]:8554; } vhost __defaultVhost__ { + http_remux { + enabled on; + mount [vhost]/[app]/[stream].flv; + } + rtsp { + enabled on; + rtmp_to_rtsp on; + } + rtc { + enabled on; + # @see https://ossrs.io/lts/en-us/docs/v7/doc/webrtc#rtmp-to-rtc + rtmp_to_rtc on; + # @see https://ossrs.io/lts/en-us/docs/v7/doc/webrtc#rtc-to-rtmp + rtc_to_rtmp on; + } + srt { + enabled on; + srt_to_rtmp on; + } } diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index e45d823e7..0f3d3551f 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -168,9 +168,9 @@ tcmalloc_release_rate 0.8; ############################################################################################# # the rtmp server section rtmp { - # the rtmp listen ports, split by space, each listen entry is <[ip:]port> - # for example, 192.168.1.100:1935 10.10.10.100:1935 - # where the ip is optional, default to 0.0.0.0, that is 1935 equals to 0.0.0.0:1935 + # The rtmp listen endpoints, split by space, each listen entry is <[ip:]port>. The ip can be + # ipv4, ipv6, or both. For example: + # listen 1935 [::]:1935 192.168.1.100:1935 10.10.10.100:1935; # Overwrite by env SRS_LISTEN listen 1935; # the default chunk size is 128, max is 65536, @@ -188,7 +188,9 @@ rtmps { # Overwrite by env SRS_RTMPS_ENABLED # default: off enabled on; - # The rtmps listen port + # The rtmps listen endpoints, each with format as <[ip:]port>. The ip can be either ipv4 or ipv6, + # or both. For example: + # listen 1443 [::]:1443; # Overwrite by env SRS_RTMPS_LISTEN listen 1443; # The SSL private key file, generated by: @@ -410,10 +412,9 @@ http_api { # Overwrite by env SRS_HTTP_API_ENABLED # default: off enabled on; - # The http api listen entry is <[ip:]port>, For example, 192.168.1.100:8080, where the ip is optional, default to - # 0.0.0.0, that is 8080 equals to 0.0.0.0:8080. - # Note that you're able to use a dedicated port for HTTP API, such as 1985, to be different with HTTP server. In - # this situation, you you must also set another HTTPS API port. + # The http api listen endpoints, each is format as <[ip:]port>. The ip can be either ipv4 + # or ipv6, or both. For example: + # listen 1985 [::]:1985 192.168.1.100:1985 10.10.10.100:1985; # Overwrite by env SRS_HTTP_API_LISTEN # Default: 1985 listen 8080; @@ -458,9 +459,9 @@ http_api { # Overwrite by env SRS_HTTP_API_HTTPS_ENABLED # default: off enabled on; - # The listen endpoint for HTTPS API. - # Note that you're able to use a dedicated port for HTTPS API, such as 1990, and the HTTP API should not be - # the same of HTTP server(8080) neither. + # The listen endpoint for HTTPS API, each with format as <[ip:]port>. The ip can be either ipv4 or ipv6, + # or both. For example: + # listen 1990 [::]:1990 192.168.1.100:1990 10.10.10.100:1990; # Overwrite by env SRS_HTTP_API_HTTPS_LISTEN # Default: 1990 listen 8088; @@ -491,10 +492,9 @@ http_server { # Overwrite by env SRS_HTTP_SERVER_ENABLED # default: off enabled on; - # the http streaming listen entry is <[ip:]port> - # for example, 192.168.1.100:8080 - # where the ip is optional, default to 0.0.0.0, that is 8080 equals to 0.0.0.0:8080 - # @remark, if use lower port, for instance 80, user must start srs by root. + # The http streaming listen endpoints, each with format as <[ip:]port>. The ip can be either ipv4 or ipv6, + # or both. For example: + # listen 8080 [::]:8080 192.168.1.100:8080 10.10.10.100:8080; # Overwrite by env SRS_HTTP_SERVER_LISTEN # default: 8080 listen 8080; @@ -514,7 +514,9 @@ http_server { # Overwrite by env SRS_HTTP_SERVER_HTTPS_ENABLED # default: off enabled on; - # The listen endpoint for HTTPS Streaming. + # The listen endpoint for HTTPS Streaming, each with format as <[ip:]port>. The ip can be either ipv4 or ipv6, + # or both. For example: + # listen 8088 [::]:8088 192.168.1.100:8088 10.10.10.100:8088; # Overwrite by env SRS_HTTP_SERVER_HTTPS_LISTEN # default: 8088 listen 8088; @@ -653,7 +655,9 @@ srt_server { # Overwrite by env SRS_SRT_SERVER_ENABLED # default: off enabled on; - # The UDP listen port for SRT. + # The UDP listen endpoints for SRT, each with format as <[ip:]port>. The ip can be either ipv4 or ipv6, + # or both. For example: + # listen 10080 [::]:10080 192.168.1.100:10080 10.10.10.100:10080; # Overwrite by env SRS_SRT_SERVER_LISTEN listen 10080; # For detail parameters, please read wiki: @@ -754,7 +758,9 @@ rtc_server { # Overwrite by env SRS_RTC_SERVER_ENABLED # default: off enabled on; - # The udp listen port, we will reuse it for connections. + # The udp listen endpoints, we will reuse it for connections, each with format as <[ip:]port>. + # The ip can be either ipv4 or ipv6, or both. For example: + # listen 8000 [::]:8000 192.168.1.100:8000 10.10.10.100:8000; # Overwrite by env SRS_RTC_SERVER_LISTEN # default: 8000 listen 8000; @@ -765,8 +771,11 @@ rtc_server { # Overwrite by env SRS_RTC_SERVER_TCP_ENABLED # Default: off enabled off; - # The TCP listen port for WebRTC. Highly recommend is some normally used ports, such as TCP/80, TCP/443, - # TCP/8000, TCP/8080 etc. However SRS default to TCP/8000 corresponding to UDP/8000. + # The TCP listen endpoints for WebRTC, each with format as <[ip:]port>. The ip can be either ipv4 or ipv6, + # or both. For example: + # listen 8000 [::]:8000 192.168.1.100:8000 10.10.10.100:8000; + # Highly recommend is some normally used ports, such as TCP/80, TCP/443, TCP/8000, TCP/8080 etc. + # However SRS default to TCP/8000 corresponding to UDP/8000. # Overwrite by env SRS_RTC_SERVER_TCP_LISTEN # Default: 8000 listen 8000; @@ -1544,7 +1553,9 @@ rtsp_server { # Overwrite by env SRS_RTSP_SERVER_ENABLED # default: off enabled on; - # The listen port for RTSP server. + # The listen endpoints for RTSP server, each with format as <[ip:]port>. The ip can be + # either ipv4 or ipv6, or both. For example: + # listen 554 [::]:554 192.168.1.100:554 10.10.10.100:554; # Overwrite by env SRS_RTSP_SERVER_LISTEN # default: 554 listen 8554; diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index 5d919bf42..873295cdc 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -7,6 +7,7 @@ The changelog for SRS. ## SRS 7.0 Changelog +* v7.0, 2025-08-29, Merge [#4457](https://github.com/ossrs/srs/pull/4457): Support IPv6 for all protocols: RTMP, HTTP/HTTPS, WebRTC, SRT, RTSP. v7.0.67 (#4457) * v7.0, 2025-08-28, Merge [#4456](https://github.com/ossrs/srs/pull/4456): AI: Remove cloud CLS and APM. v7.0.66 (#4456) * v7.0, 2025-08-27, Merge [#4455](https://github.com/ossrs/srs/pull/4455): Gather utility functions to kernel or protocol. v7.0.65 (#4455) * v7.0, 2025-08-27, Merge [#4454](https://github.com/ossrs/srs/pull/4454): AI: Config: Move RTMP configs to rtmp{} section. v7.0.64 (#4454) diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 67750cd90..c7def53b6 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -2107,7 +2107,7 @@ srs_error_t SrsConfig::raw_to_json(SrsJsonObject *obj) obj->set("http_api", sobj); sobj->set("enabled", SrsJsonAny::boolean(get_http_api_enabled())); - sobj->set("listen", SrsJsonAny::str(get_http_api_listen().c_str())); + sobj->set("listen", SrsJsonAny::str(get_http_api_listens().at(0).c_str())); sobj->set("crossdomain", SrsJsonAny::boolean(get_http_api_crossdomain())); SrsJsonObject *ssobj = SrsJsonAny::object(); @@ -2477,18 +2477,8 @@ srs_error_t SrsConfig::check_normal_config() return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "listen requires params"); } for (int i = 0; i < (int)listens.size(); i++) { - int port; - string ip; - srs_net_split_for_listener(listens[i], ip, port); - - // check ip - if (!srs_net_is_valid_ip(ip)) { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "listen.ip=%s is invalid", ip.c_str()); - } - - // check port - if (port <= 0) { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "listen.port=%d is invalid", port); + if (!srs_net_is_valid_endpoint(listens[i])) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "rtmp.listen=%s is invalid", listens[i].c_str()); } } } @@ -2536,22 +2526,39 @@ srs_error_t SrsConfig::check_normal_config() // Check HTTP API and server. //////////////////////////////////////////////////////////////////////// if (true) { - string api = get_http_api_listen(); - string server = get_http_stream_listen(); - if (api.empty()) { + vector apis = get_http_api_listens(); + if (apis.empty()) { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "http_api.listen requires params"); } - if (server.empty()) { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "http_server.listen requires params"); + for (int i = 0; i < (int)apis.size(); i++) { + if (!srs_net_is_valid_endpoint(apis[i])) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "http_api.listen=%s is invalid", apis[i].c_str()); + } } - string apis = get_https_api_listen(); - string servers = get_https_stream_listen(); - if (api == server && apis != servers) { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "for same http, https api(%s) != server(%s)", apis.c_str(), servers.c_str()); + vector servers = get_http_stream_listens(); + if (servers.empty()) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "http_server.listen requires params"); } - if (apis == servers && api != server) { - return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "for same https, http api(%s) != server(%s)", api.c_str(), server.c_str()); + for (int i = 0; i < (int)servers.size(); i++) { + if (!srs_net_is_valid_endpoint(servers[i])) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "http_server.listen=%s is invalid", servers[i].c_str()); + } + } + + vector sapis = get_https_api_listens(); + for (int i = 0; i < (int)sapis.size(); i++) { + if (!srs_net_is_valid_endpoint(sapis[i])) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "http_api.https.listen=%s is invalid", sapis[i].c_str()); + } + } + + vector sservers = get_https_stream_listens(); + if (srs_strings_equal(apis, servers) && !srs_strings_equal(sapis, sservers)) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "for same http, https api(%s) != server(%s)", srs_strings_join(sapis, ",").c_str(), srs_strings_join(sservers, ",").c_str()); + } + if (srs_strings_equal(sapis, sservers) && !srs_strings_equal(apis, servers)) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "for same https, http api(%s) != server(%s)", srs_strings_join(apis, ",").c_str(), srs_strings_join(servers, ",").c_str()); } if (get_https_api_enabled() && !get_http_api_enabled()) { @@ -2560,6 +2567,65 @@ srs_error_t SrsConfig::check_normal_config() if (get_https_stream_enabled() && !get_http_stream_enabled()) { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "https stream depends on http"); } + + // Validate HTTPS stream listen addresses + if (get_https_stream_enabled()) { + vector https_listens = get_https_stream_listens(); + for (int i = 0; i < (int)https_listens.size(); i++) { + if (!srs_net_is_valid_endpoint(https_listens[i])) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "https_server.listen=%s is invalid", https_listens[i].c_str()); + } + } + } + } + + //////////////////////////////////////////////////////////////////////// + // Check RTSP server. + //////////////////////////////////////////////////////////////////////// + if (get_rtsp_server_enabled()) { + // Validate RTSP server listen addresses + vector rtsp_listens = get_rtsp_server_listens(); + for (int i = 0; i < (int)rtsp_listens.size(); i++) { + if (!srs_net_is_valid_endpoint(rtsp_listens[i])) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "rtsp_server.listen=%s is invalid", rtsp_listens[i].c_str()); + } + } + } + + //////////////////////////////////////////////////////////////////////// + // Check RTC server. + //////////////////////////////////////////////////////////////////////// + if (get_rtc_server_enabled()) { + // Validate RTC server listen addresses + vector rtc_listens = get_rtc_server_listens(); + for (int i = 0; i < (int)rtc_listens.size(); i++) { + if (!srs_net_is_valid_endpoint(rtc_listens[i])) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "rtc_server.listen=%s is invalid", rtc_listens[i].c_str()); + } + } + + // Validate RTC server TCP listen addresses + if (get_rtc_server_tcp_enabled()) { + vector rtc_tcp_listens = get_rtc_server_tcp_listens(); + for (int i = 0; i < (int)rtc_tcp_listens.size(); i++) { + if (!srs_net_is_valid_endpoint(rtc_tcp_listens[i])) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "rtc_server.tcp.listen=%s is invalid", rtc_tcp_listens[i].c_str()); + } + } + } + } + + //////////////////////////////////////////////////////////////////////// + // Check SRT server. + //////////////////////////////////////////////////////////////////////// + if (get_srt_enabled()) { + // Validate SRT server listen addresses + vector srt_listens = get_srt_listens(); + for (int i = 0; i < (int)srt_listens.size(); i++) { + if (!srs_net_is_valid_endpoint(srt_listens[i])) { + return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "srt_server.listen=%s is invalid", srt_listens[i].c_str()); + } + } } //////////////////////////////////////////////////////////////////////// @@ -3735,24 +3801,38 @@ bool SrsConfig::get_rtsp_server_enabled(SrsConfDirective *conf) return SRS_CONF_PREFER_FALSE(conf->arg0()); } -int SrsConfig::get_rtsp_server_listen() +vector SrsConfig::get_rtsp_server_listens() { - SRS_OVERWRITE_BY_ENV_INT("srs.rtsp_server.listen"); // SRS_RTSP_SERVER_LISTEN + std::vector listens; + + if (!srs_getenv("srs.rtsp_server.listen").empty()) { // SRS_RTSP_SERVER_LISTEN + return srs_strings_split(srs_getenv("srs.rtsp_server.listen"), " "); + } + + static string DEFAULT = "554"; SrsConfDirective *conf = root->get("rtsp_server"); - - static int DEFAULT = 554; - if (!conf) { - return DEFAULT; + listens.push_back(DEFAULT); + return listens; } conf = conf->get("listen"); - if (!conf || conf->arg0().empty()) { - return DEFAULT; + if (!conf) { + listens.push_back(DEFAULT); + return listens; } - return ::atoi(conf->arg0().c_str()); + for (int i = 0; i < (int)conf->args.size(); i++) { + listens.push_back(conf->args.at(i)); + } + + // If no arguments, use default + if (listens.empty()) { + listens.push_back(DEFAULT); + } + + return listens; } SrsConfDirective *SrsConfig::get_rtsp(string vhost) @@ -3825,23 +3905,36 @@ bool SrsConfig::get_rtc_server_enabled(SrsConfDirective *conf) return SRS_CONF_PREFER_FALSE(conf->arg0()); } -int SrsConfig::get_rtc_server_listen() +vector SrsConfig::get_rtc_server_listens() { - SRS_OVERWRITE_BY_ENV_INT("srs.rtc_server.listen"); // SRS_RTC_SERVER_LISTEN + std::vector listens; - static int DEFAULT = 8000; + if (!srs_getenv("srs.rtc_server.listen").empty()) { // SRS_RTC_SERVER_LISTEN + return srs_strings_split(srs_getenv("srs.rtc_server.listen"), " "); + } SrsConfDirective *conf = root->get("rtc_server"); if (!conf) { - return DEFAULT; + listens.push_back("8000"); + return listens; } conf = conf->get("listen"); - if (!conf || conf->arg0().empty()) { - return DEFAULT; + if (!conf) { + listens.push_back("8000"); + return listens; } - return ::atoi(conf->arg0().c_str()); + for (int i = 0; i < (int)conf->args.size(); i++) { + listens.push_back(conf->args.at(i)); + } + + // If no arguments, use default + if (listens.empty()) { + listens.push_back("8000"); + } + + return listens; } std::string SrsConfig::get_rtc_server_candidates() @@ -3979,28 +4072,44 @@ bool SrsConfig::get_rtc_server_tcp_enabled() return SRS_CONF_PREFER_FALSE(conf->arg0()); } -int SrsConfig::get_rtc_server_tcp_listen() +vector SrsConfig::get_rtc_server_tcp_listens() { - SRS_OVERWRITE_BY_ENV_INT("srs.rtc_server.tcp.listen"); // SRS_RTC_SERVER_TCP_LISTEN + std::vector listens; - static int DEFAULT = 8000; + if (!srs_getenv("srs.rtc_server.tcp.listen").empty()) { // SRS_RTC_SERVER_TCP_LISTEN + return srs_strings_split(srs_getenv("srs.rtc_server.tcp.listen"), " "); + } + + static string DEFAULT = "8000"; SrsConfDirective *conf = root->get("rtc_server"); if (!conf) { - return DEFAULT; + listens.push_back(DEFAULT); + return listens; } conf = conf->get("tcp"); if (!conf) { - return DEFAULT; + listens.push_back(DEFAULT); + return listens; } conf = conf->get("listen"); - if (!conf || conf->arg0().empty()) { - return DEFAULT; + if (!conf) { + listens.push_back(DEFAULT); + return listens; } - return ::atoi(conf->arg0().c_str()); + for (int i = 0; i < (int)conf->args.size(); i++) { + listens.push_back(conf->args.at(i)); + } + + // If no arguments, use default + if (listens.empty()) { + listens.push_back(DEFAULT); + } + + return listens; } std::string SrsConfig::get_rtc_server_protocol() @@ -7464,24 +7573,38 @@ bool SrsConfig::get_http_api_enabled(SrsConfDirective *conf) return SRS_CONF_PREFER_FALSE(conf->arg0()); } -string SrsConfig::get_http_api_listen() +vector SrsConfig::get_http_api_listens() { - SRS_OVERWRITE_BY_ENV_STRING("srs.http_api.listen"); // SRS_HTTP_API_LISTEN + std::vector listens; + + if (!srs_getenv("srs.http_api.listen").empty()) { // SRS_HTTP_API_LISTEN + return srs_strings_split(srs_getenv("srs.http_api.listen"), " "); + } static string DEFAULT = "1985"; SrsConfDirective *conf = root->get("http_api"); - if (!conf) { - return DEFAULT; + listens.push_back(DEFAULT); + return listens; } conf = conf->get("listen"); - if (!conf || conf->arg0().empty()) { - return DEFAULT; + if (!conf) { + listens.push_back(DEFAULT); + return listens; } - return conf->arg0(); + for (int i = 0; i < (int)conf->args.size(); i++) { + listens.push_back(conf->args.at(i)); + } + + // If no arguments, use default + if (listens.empty()) { + listens.push_back(DEFAULT); + } + + return listens; } bool SrsConfig::get_http_api_crossdomain() @@ -7664,28 +7787,48 @@ bool SrsConfig::get_https_api_enabled() return SRS_CONF_PREFER_FALSE(conf->arg0()); } -string SrsConfig::get_https_api_listen() +vector SrsConfig::get_https_api_listens() { - SRS_OVERWRITE_BY_ENV_STRING("srs.http_api.https.listen"); // SRS_HTTP_API_HTTPS_LISTEN + std::vector listens; - // We should not use static default, because we need to reset for different use scenarios. - string DEFAULT = "1990"; - // Follow the HTTPS server if config HTTP API as the same of HTTP server. - if (get_http_api_listen() == get_http_stream_listen()) { - DEFAULT = get_https_stream_listen(); + if (!srs_getenv("srs.http_api.https.listen").empty()) { // SRS_HTTP_API_HTTPS_LISTEN + return srs_strings_split(srs_getenv("srs.http_api.https.listen"), " "); } + // If HTTP API uses the same port to HTTP server, then HTTPS API should + // always default to the same port to HTTPS server. + if (true) { + vector apis = get_http_api_listens(); + vector servers = get_http_stream_listens(); + if (srs_strings_equal(apis, servers)) { + return get_https_stream_listens(); + } + } + + static string DEFAULT = "1990"; + SrsConfDirective *conf = get_https_api(); if (!conf) { - return DEFAULT; + listens.push_back(DEFAULT); + return listens; } conf = conf->get("listen"); if (!conf) { - return DEFAULT; + listens.push_back(DEFAULT); + return listens; } - return conf->arg0(); + for (int i = 0; i < (int)conf->args.size(); i++) { + listens.push_back(conf->args.at(i)); + } + + // If no arguments, use default + if (listens.empty()) { + listens.push_back(DEFAULT); + } + + return listens; } string SrsConfig::get_https_api_ssl_key() @@ -7745,21 +7888,38 @@ bool SrsConfig::get_srt_enabled() return SRS_CONF_PREFER_FALSE(conf->arg0()); } -unsigned short SrsConfig::get_srt_listen_port() +vector SrsConfig::get_srt_listens() { - SRS_OVERWRITE_BY_ENV_INT("srs.srt_server.listen"); // SRS_SRT_SERVER_LISTEN + std::vector listens; + + if (!srs_getenv("srs.srt_server.listen").empty()) { // SRS_SRT_SERVER_LISTEN + return srs_strings_split(srs_getenv("srs.srt_server.listen"), " "); + } + + static string DEFAULT = "10080"; - static unsigned short DEFAULT = 10080; SrsConfDirective *conf = root->get("srt_server"); if (!conf) { - return DEFAULT; + listens.push_back(DEFAULT); + return listens; } conf = conf->get("listen"); - if (!conf || conf->arg0().empty()) { - return DEFAULT; + if (!conf) { + listens.push_back(DEFAULT); + return listens; } - return (unsigned short)atoi(conf->arg0().c_str()); + + for (int i = 0; i < (int)conf->args.size(); i++) { + listens.push_back(conf->args.at(i)); + } + + // If no arguments, use default + if (listens.empty()) { + listens.push_back(DEFAULT); + } + + return listens; } int64_t SrsConfig::get_srto_maxbw() @@ -8104,23 +8264,38 @@ bool SrsConfig::get_http_stream_enabled(SrsConfDirective *conf) return SRS_CONF_PREFER_FALSE(conf->arg0()); } -string SrsConfig::get_http_stream_listen() +vector SrsConfig::get_http_stream_listens() { - SRS_OVERWRITE_BY_ENV_STRING("srs.http_server.listen"); // SRS_HTTP_SERVER_LISTEN + std::vector listens; + + if (!srs_getenv("srs.http_server.listen").empty()) { // SRS_HTTP_SERVER_LISTEN + return srs_strings_split(srs_getenv("srs.http_server.listen"), " "); + } static string DEFAULT = "8080"; SrsConfDirective *conf = root->get("http_server"); if (!conf) { - return DEFAULT; + listens.push_back(DEFAULT); + return listens; } conf = conf->get("listen"); - if (!conf || conf->arg0().empty()) { - return DEFAULT; + if (!conf) { + listens.push_back(DEFAULT); + return listens; } - return conf->arg0(); + for (int i = 0; i < (int)conf->args.size(); i++) { + listens.push_back(conf->args.at(i)); + } + + // If no arguments, use default + if (listens.empty()) { + listens.push_back(DEFAULT); + } + + return listens; } string SrsConfig::get_http_stream_dir() @@ -8190,23 +8365,38 @@ bool SrsConfig::get_https_stream_enabled() return SRS_CONF_PREFER_FALSE(conf->arg0()); } -string SrsConfig::get_https_stream_listen() +vector SrsConfig::get_https_stream_listens() { - SRS_OVERWRITE_BY_ENV_STRING("srs.http_server.https.listen"); // SRS_HTTP_SERVER_HTTPS_LISTEN + std::vector listens; + + if (!srs_getenv("srs.http_server.https.listen").empty()) { // SRS_HTTP_SERVER_HTTPS_LISTEN + return srs_strings_split(srs_getenv("srs.http_server.https.listen"), " "); + } static string DEFAULT = "8088"; SrsConfDirective *conf = get_https_stream(); if (!conf) { - return DEFAULT; + listens.push_back(DEFAULT); + return listens; } conf = conf->get("listen"); if (!conf) { - return DEFAULT; + listens.push_back(DEFAULT); + return listens; } - return conf->arg0(); + for (int i = 0; i < (int)conf->args.size(); i++) { + listens.push_back(conf->args.at(i)); + } + + // If no arguments, use default + if (listens.empty()) { + listens.push_back(DEFAULT); + } + + return listens; } string SrsConfig::get_https_stream_ssl_key() diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 5c8a0c4a1..4b2a42c9f 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -513,7 +513,8 @@ public: public: virtual bool get_rtsp_server_enabled(); virtual bool get_rtsp_server_enabled(SrsConfDirective *conf); - virtual int get_rtsp_server_listen(); + // Get the rtsp server listen addresses, support IPv4 and IPv6. + virtual std::vector get_rtsp_server_listens(); public: SrsConfDirective *get_rtsp(std::string vhost); @@ -524,14 +525,16 @@ public: public: virtual bool get_rtc_server_enabled(); virtual bool get_rtc_server_enabled(SrsConfDirective *conf); - virtual int get_rtc_server_listen(); + // Get the rtc server listen addresses, support IPv4 and IPv6. + virtual std::vector get_rtc_server_listens(); virtual std::string get_rtc_server_candidates(); virtual bool get_api_as_candidates(); virtual bool get_resolve_api_domain(); virtual bool get_keep_api_domain(); virtual bool get_use_auto_detect_network_ip(); virtual bool get_rtc_server_tcp_enabled(); - virtual int get_rtc_server_tcp_listen(); + // Get the rtc server tcp listen addresses, support IPv4 and IPv6. + virtual std::vector get_rtc_server_tcp_listens(); virtual std::string get_rtc_server_protocol(); virtual std::string get_rtc_server_ip_family(); virtual bool get_rtc_server_ecdsa(); @@ -689,8 +692,8 @@ public: public: // Whether the srt sevice enabled virtual bool get_srt_enabled(); - // Get the srt service listen port - virtual unsigned short get_srt_listen_port(); + // Get the srt service listen addresses, support IPv4 and IPv6. + virtual std::vector get_srt_listens(); // Get the srt SRTO_MAXBW, max bandwith, default is -1. virtual int64_t get_srto_maxbw(); // Get the srt SRTO_MSS, Maximum Segment Size, default is 1500. @@ -1060,8 +1063,8 @@ private: public: // Whether http api enabled. virtual bool get_http_api_enabled(); - // Get the http api listen port. - virtual std::string get_http_api_listen(); + // Get the http api listen addresses, support IPv4 and IPv6. + virtual std::vector get_http_api_listens(); // Whether enable crossdomain for http api. virtual bool get_http_api_crossdomain(); // Whether enable the HTTP RAW API. @@ -1084,7 +1087,7 @@ private: public: virtual bool get_https_api_enabled(); - virtual std::string get_https_api_listen(); + virtual std::vector get_https_api_listens(); virtual std::string get_https_api_ssl_key(); virtual std::string get_https_api_ssl_cert(); // http stream section @@ -1096,8 +1099,8 @@ public: // Whether http stream enabled. // TODO: FIXME: rename to http_static. virtual bool get_http_stream_enabled(); - // Get the http stream listen port. - virtual std::string get_http_stream_listen(); + // Get the http stream listen addresses, support IPv4 and IPv6. + virtual std::vector get_http_stream_listens(); // Get the http stream root dir. virtual std::string get_http_stream_dir(); // Whether enable crossdomain for http static and stream server. @@ -1108,7 +1111,8 @@ private: public: virtual bool get_https_stream_enabled(); - virtual std::string get_https_stream_listen(); + // Get the https stream listen addresses, support IPv4 and IPv6. + virtual std::vector get_https_stream_listens(); virtual std::string get_https_stream_ssl_key(); virtual std::string get_https_stream_ssl_cert(); // rtmps section diff --git a/trunk/src/app/srs_app_coworkers.cpp b/trunk/src/app/srs_app_coworkers.cpp index be78576c1..3b4501c42 100644 --- a/trunk/src/app/srs_app_coworkers.cpp +++ b/trunk/src/app/srs_app_coworkers.cpp @@ -84,7 +84,7 @@ SrsJsonAny *SrsCoWorkers::dumps(string vhost, string coworker, string app, strin } // The backend API endpoint. - string backend = _srs_config->get_http_api_listen(); + string backend = _srs_config->get_http_api_listens().at(0); if (backend.find(":") == string::npos) { backend = service_ip + ":" + backend; } diff --git a/trunk/src/app/srs_app_heartbeat.cpp b/trunk/src/app/srs_app_heartbeat.cpp index d8024971b..5d2da3b2e 100644 --- a/trunk/src/app/srs_app_heartbeat.cpp +++ b/trunk/src/app/srs_app_heartbeat.cpp @@ -97,8 +97,10 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat() SrsJsonArray *o = SrsJsonAny::array(); obj->set("http", o); - string endpoint = _srs_config->get_http_stream_listen(); - o->append(SrsJsonAny::str(endpoint.c_str())); + vector endpoints = _srs_config->get_http_stream_listens(); + for (int i = 0; i < (int)endpoints.size(); i++) { + o->append(SrsJsonAny::str(endpoints.at(i).c_str())); + } } // For HTTP API listen endpoints. @@ -106,8 +108,10 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat() SrsJsonArray *o = SrsJsonAny::array(); obj->set("api", o); - string endpoint = _srs_config->get_http_api_listen(); - o->append(SrsJsonAny::str(endpoint.c_str())); + vector endpoints = _srs_config->get_http_api_listens(); + for (int i = 0; i < (int)endpoints.size(); i++) { + o->append(SrsJsonAny::str(endpoints.at(i).c_str())); + } } // For SRT listen endpoints. @@ -115,8 +119,10 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat() SrsJsonArray *o = SrsJsonAny::array(); obj->set("srt", o); - uint16_t endpoint = _srs_config->get_srt_listen_port(); - o->append(SrsJsonAny::str(srs_fmt_sprintf("udp://0.0.0.0:%d", endpoint).c_str())); + vector endpoints = _srs_config->get_srt_listens(); + for (int i = 0; i < (int)endpoints.size(); i++) { + o->append(SrsJsonAny::str(endpoints.at(i).c_str())); + } } // For RTSP listen endpoints. @@ -124,8 +130,10 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat() SrsJsonArray *o = SrsJsonAny::array(); obj->set("rtsp", o); - int endpoint = _srs_config->get_rtsp_server_listen(); - o->append(SrsJsonAny::str(srs_fmt_sprintf("rtsp://0.0.0.0:%d", endpoint).c_str())); + vector endpoints = _srs_config->get_rtsp_server_listens(); + for (int i = 0; i < (int)endpoints.size(); i++) { + o->append(SrsJsonAny::str(endpoints.at(i).c_str())); + } } // For WebRTC listen endpoints. @@ -133,12 +141,18 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat() SrsJsonArray *o = SrsJsonAny::array(); obj->set("rtc", o); - int endpoint = _srs_config->get_rtc_server_listen(); - o->append(SrsJsonAny::str(srs_fmt_sprintf("udp://0.0.0.0:%d", endpoint).c_str())); + vector endpoints = _srs_config->get_rtc_server_listens(); + for (int i = 0; i < (int)endpoints.size(); i++) { + string endpoint = srs_fmt_sprintf("udp://%s", endpoints.at(i).c_str()); + o->append(SrsJsonAny::str(endpoint.c_str())); + } if (_srs_config->get_rtc_server_tcp_enabled()) { - endpoint = _srs_config->get_rtc_server_tcp_listen(); - o->append(SrsJsonAny::str(srs_fmt_sprintf("tcp://0.0.0.0:%d", endpoint).c_str())); + vector endpoints = _srs_config->get_rtc_server_tcp_listens(); + for (int i = 0; i < (int)endpoints.size(); i++) { + string endpoint = srs_fmt_sprintf("tcp://%s", endpoints.at(i).c_str()); + o->append(SrsJsonAny::str(endpoint.c_str())); + } } } } diff --git a/trunk/src/app/srs_app_rtc_server.cpp b/trunk/src/app/srs_app_rtc_server.cpp index 5b6dfc034..1a4d86792 100644 --- a/trunk/src/app/srs_app_rtc_server.cpp +++ b/trunk/src/app/srs_app_rtc_server.cpp @@ -366,25 +366,43 @@ srs_error_t SrsRtcServer::listen_udp() return err; } - int port = _srs_config->get_rtc_server_listen(); - if (port <= 0) { - return srs_error_new(ERROR_RTC_PORT, "invalid port=%d", port); + // Check protocol setting - only create UDP listeners if protocol allows UDP + string protocol = _srs_config->get_rtc_server_protocol(); + if (protocol == "tcp") { + // When protocol is "tcp", don't create UDP listeners + return err; } - string ip = srs_net_address_any(); + vector rtc_listens = _srs_config->get_rtc_server_listens(); + if (rtc_listens.empty()) { + return srs_error_new(ERROR_RTC_PORT, "empty rtc listen"); + } + + // There should be no listeners before listening. srs_assert(listeners.empty()); + // For each listen address, create multiple listeners based on reuseport setting int nn_listeners = _srs_config->get_rtc_server_reuseport(); - for (int i = 0; i < nn_listeners; i++) { - SrsUdpMuxListener *listener = new SrsUdpMuxListener(this, ip, port); + for (int j = 0; j < (int)rtc_listens.size(); j++) { + string ip; + int port; + srs_net_split_for_listener(rtc_listens[j], ip, port); - if ((err = listener->listen()) != srs_success) { - srs_freep(listener); - return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port); + if (port <= 0) { + return srs_error_new(ERROR_RTC_PORT, "invalid port=%d", port); } - srs_trace("rtc listen at udp://%s:%d, fd=%d", ip.c_str(), port, listener->fd()); - listeners.push_back(listener); + for (int i = 0; i < nn_listeners; i++) { + SrsUdpMuxListener *listener = new SrsUdpMuxListener(this, ip, port); + + if ((err = listener->listen()) != srs_success) { + srs_freep(listener); + return srs_error_wrap(err, "listen %s:%d", ip.c_str(), port); + } + + srs_trace("WebRTC listen at udp://%s:%d, fd=%d", ip.c_str(), port, listener->fd()); + listeners.push_back(listener); + } } return err; @@ -589,8 +607,21 @@ srs_error_t SrsRtcServer::do_create_session(SrsRtcUserConfig *ruc, SrsSdp &local // We allows to mock the eip of server. if (true) { - int udp_port = _srs_config->get_rtc_server_listen(); - int tcp_port = _srs_config->get_rtc_server_tcp_listen(); + // TODO: Support multiple listen ports. + int udp_port = 0; + if (true) { + string udp_host; + string udp_hostport = _srs_config->get_rtc_server_listens().at(0); + srs_net_split_for_listener(udp_hostport, udp_host, udp_port); + } + + int tcp_port = 0; + if (true) { + string tcp_host; + string tcp_hostport = _srs_config->get_rtc_server_tcp_listens().at(0); + srs_net_split_for_listener(tcp_hostport, tcp_host, tcp_port); + } + string protocol = _srs_config->get_rtc_server_protocol(); set candidates = discover_candidates(ruc); @@ -612,7 +643,8 @@ srs_error_t SrsRtcServer::do_create_session(SrsRtcUserConfig *ruc, SrsSdp &local } vector v = vector(candidates.begin(), candidates.end()); - srs_trace("RTC: Use candidates %s, protocol=%s", srs_strings_join(v, ", ").c_str(), protocol.c_str()); + srs_trace("RTC: Use candidates %s, protocol=%s, tcp_port=%d, udp_port=%d", + srs_strings_join(v, ", ").c_str(), protocol.c_str(), tcp_port, udp_port); } // Setup the negotiate DTLS by config. diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index eacd7ae92..89edba798 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -340,13 +340,13 @@ SrsServer::SrsServer() rtmp_listener_ = new SrsMultipleTcpListeners(this); rtmps_listener_ = new SrsMultipleTcpListeners(this); - api_listener_ = new SrsTcpListener(this); - apis_listener_ = new SrsTcpListener(this); - http_listener_ = new SrsTcpListener(this); - https_listener_ = new SrsTcpListener(this); - webrtc_listener_ = new SrsTcpListener(this); + api_listener_ = new SrsMultipleTcpListeners(this); + apis_listener_ = new SrsMultipleTcpListeners(this); + http_listener_ = new SrsMultipleTcpListeners(this); + https_listener_ = new SrsMultipleTcpListeners(this); + webrtc_listener_ = new SrsMultipleTcpListeners(this); #ifdef SRS_RTSP - rtsp_listener_ = new SrsTcpListener(this); + rtsp_listener_ = new SrsMultipleTcpListeners(this); #endif stream_caster_flv_listener_ = new SrsHttpFlvListener(); stream_caster_mpegts_ = new SrsUdpCasterListener(); @@ -510,28 +510,28 @@ srs_error_t SrsServer::initialize() _srs_config->subscribe(this); bool stream = _srs_config->get_http_stream_enabled(); - string http_listen = _srs_config->get_http_stream_listen(); - string https_listen = _srs_config->get_https_stream_listen(); + vector http_listens = _srs_config->get_http_stream_listens(); + vector https_listens = _srs_config->get_https_stream_listens(); bool rtc = _srs_config->get_rtc_server_enabled(); bool rtc_tcp = _srs_config->get_rtc_server_tcp_enabled(); - string rtc_listen = srs_strconv_format_int(_srs_config->get_rtc_server_tcp_listen()); + vector rtc_listens = _srs_config->get_rtc_server_tcp_listens(); // If enabled and listen is the same value, resue port for WebRTC over TCP. - if (stream && rtc && rtc_tcp && http_listen == rtc_listen) { - srs_trace("WebRTC tcp=%s reuses http=%s server", rtc_listen.c_str(), http_listen.c_str()); + if (stream && rtc && rtc_tcp && srs_strings_equal(http_listens, rtc_listens)) { + srs_trace("WebRTC tcp=%s reuses http=%s server", srs_strings_join(rtc_listens, ",").c_str(), srs_strings_join(http_listens, ",").c_str()); reuse_rtc_over_server_ = true; } - if (stream && rtc && rtc_tcp && https_listen == rtc_listen) { - srs_trace("WebRTC tcp=%s reuses https=%s server", rtc_listen.c_str(), https_listen.c_str()); + if (stream && rtc && rtc_tcp && srs_strings_equal(https_listens, rtc_listens)) { + srs_trace("WebRTC tcp=%s reuses https=%s server", srs_strings_join(rtc_listens, ",").c_str(), srs_strings_join(https_listens, ",").c_str()); reuse_rtc_over_server_ = true; } // If enabled and the listen is the same value, reuse port. bool api = _srs_config->get_http_api_enabled(); - string api_listen = _srs_config->get_http_api_listen(); - string apis_listen = _srs_config->get_https_api_listen(); - if (stream && api && api_listen == http_listen && apis_listen == https_listen) { - srs_trace("API reuses http=%s and https=%s server", http_listen.c_str(), https_listen.c_str()); + vector api_listens = _srs_config->get_http_api_listens(); + vector apis_listens = _srs_config->get_https_api_listens(); + if (stream && api && srs_strings_equal(api_listens, http_listens) && srs_strings_equal(apis_listens, https_listens)) { + srs_trace("API reuses http=%s and https=%s server", srs_strings_join(http_listens, ",").c_str(), srs_strings_join(https_listens, ",").c_str()); reuse_api_over_server_ = true; } @@ -608,9 +608,10 @@ srs_error_t SrsServer::listen() // Create HTTP API listener. if (_srs_config->get_http_api_enabled()) { if (reuse_api_over_server_) { - srs_trace("HTTP-API: Reuse listen to http server %s", _srs_config->get_http_stream_listen().c_str()); + vector listens = _srs_config->get_http_stream_listens(); + srs_trace("HTTP-API: Reuse listen to http server %s", srs_strings_join(listens, ",").c_str()); } else { - api_listener_->set_endpoint(_srs_config->get_http_api_listen())->set_label("HTTP-API"); + api_listener_->add(_srs_config->get_http_api_listens())->set_label("HTTP-API"); if ((err = api_listener_->listen()) != srs_success) { return srs_error_wrap(err, "http api listen"); } @@ -620,9 +621,10 @@ srs_error_t SrsServer::listen() // Create HTTPS API listener. if (_srs_config->get_https_api_enabled()) { if (reuse_api_over_server_) { - srs_trace("HTTPS-API: Reuse listen to http server %s", _srs_config->get_https_stream_listen().c_str()); + vector listens = _srs_config->get_https_stream_listens(); + srs_trace("HTTPS-API: Reuse listen to http server %s", srs_strings_join(listens, ",").c_str()); } else { - apis_listener_->set_endpoint(_srs_config->get_https_api_listen())->set_label("HTTPS-API"); + apis_listener_->add(_srs_config->get_https_api_listens())->set_label("HTTPS-API"); if ((err = apis_listener_->listen()) != srs_success) { return srs_error_wrap(err, "https api listen"); } @@ -631,7 +633,7 @@ srs_error_t SrsServer::listen() // Create HTTP server listener. if (_srs_config->get_http_stream_enabled()) { - http_listener_->set_endpoint(_srs_config->get_http_stream_listen())->set_label("HTTP-Server"); + http_listener_->add(_srs_config->get_http_stream_listens())->set_label("HTTP-Server"); if ((err = http_listener_->listen()) != srs_success) { return srs_error_wrap(err, "http server listen"); } @@ -639,15 +641,16 @@ srs_error_t SrsServer::listen() // Create HTTPS server listener. if (_srs_config->get_https_stream_enabled()) { - https_listener_->set_endpoint(_srs_config->get_https_stream_listen())->set_label("HTTPS-Server"); + https_listener_->add(_srs_config->get_https_stream_listens())->set_label("HTTPS-Server"); if ((err = https_listener_->listen()) != srs_success) { return srs_error_wrap(err, "https server listen"); } } // Start WebRTC over TCP listener. - if (!reuse_rtc_over_server_ && _srs_config->get_rtc_server_tcp_enabled()) { - webrtc_listener_->set_endpoint(srs_strconv_format_int(_srs_config->get_rtc_server_tcp_listen()))->set_label("WebRTC"); + string protocol = _srs_config->get_rtc_server_protocol(); + if (!reuse_rtc_over_server_ && protocol != "udp" && _srs_config->get_rtc_server_tcp_enabled()) { + webrtc_listener_->add(_srs_config->get_rtc_server_tcp_listens())->set_label("WebRTC"); if ((err = webrtc_listener_->listen()) != srs_success) { return srs_error_wrap(err, "webrtc tcp listen"); } @@ -656,7 +659,7 @@ srs_error_t SrsServer::listen() #ifdef SRS_RTSP // Start RTSP listener. RTC is a critical dependency. if (_srs_config->get_rtsp_server_enabled()) { - rtsp_listener_->set_endpoint(srs_strconv_format_int(_srs_config->get_rtsp_server_listen()))->set_label("RTSP"); + rtsp_listener_->add(_srs_config->get_rtsp_server_listens())->set_label("RTSP"); if ((err = rtsp_listener_->listen()) != srs_success) { return srs_error_wrap(err, "rtsp listen"); } diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index 97e47c1e0..1d4c63514 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -131,20 +131,20 @@ private: // RTMPS stream listeners, over TCP. SrsMultipleTcpListeners *rtmps_listener_; // HTTP API listener, over TCP. Please note that it might reuse with stream listener. - SrsTcpListener *api_listener_; + SrsMultipleTcpListeners *api_listener_; // HTTPS API listener, over TCP. Please note that it might reuse with stream listener. - SrsTcpListener *apis_listener_; + SrsMultipleTcpListeners *apis_listener_; // HTTP server listener, over TCP. Please note that request of both HTTP static and stream are served by this // listener, and it might be reused by HTTP API and WebRTC TCP. - SrsTcpListener *http_listener_; + SrsMultipleTcpListeners *http_listener_; // HTTPS server listener, over TCP. Please note that request of both HTTP static and stream are served by this // listener, and it might be reused by HTTP API and WebRTC TCP. - SrsTcpListener *https_listener_; + SrsMultipleTcpListeners *https_listener_; // WebRTC over TCP listener. Please note that there is always a UDP listener by RTC server. - SrsTcpListener *webrtc_listener_; + SrsMultipleTcpListeners *webrtc_listener_; #ifdef SRS_RTSP // RTSP listener, over TCP. - SrsTcpListener *rtsp_listener_; + SrsMultipleTcpListeners *rtsp_listener_; #endif // Stream Caster for push over HTTP-FLV. SrsHttpFlvListener *stream_caster_flv_listener_; diff --git a/trunk/src/app/srs_app_srt_server.cpp b/trunk/src/app/srs_app_srt_server.cpp index 070b5c673..5913625d2 100644 --- a/trunk/src/app/srs_app_srt_server.cpp +++ b/trunk/src/app/srs_app_srt_server.cpp @@ -195,16 +195,28 @@ srs_error_t SrsSrtServer::listen_srt_mpegts() // Close all listener for SRT if exists. close_listeners(); - // Start a listener for SRT, we might need multiple listeners in the future. - SrsSrtAcceptor *acceptor = new SrsSrtAcceptor(this); - acceptors_.push_back(acceptor); + // Start listeners for SRT, support multiple addresses including IPv6. + vector srt_listens = _srs_config->get_srt_listens(); + for (int i = 0; i < (int)srt_listens.size(); i++) { + SrsSrtAcceptor *acceptor = new SrsSrtAcceptor(this); - int port; - string ip; - srs_net_split_for_listener(srs_strconv_format_int(_srs_config->get_srt_listen_port()), ip, port); + int port; + string ip; + srs_net_split_for_listener(srt_listens[i], ip, port); - if ((err = acceptor->listen(ip, port)) != srs_success) { - return srs_error_wrap(err, "srt listen %s:%d", ip.c_str(), port); + if ((err = acceptor->listen(ip, port)) != srs_success) { + srs_freep(acceptor); + srs_warn("srt listen %s:%d failed, err=%s", ip.c_str(), port, srs_error_desc(err).c_str()); + srs_error_reset(err); + continue; + } + + acceptors_.push_back(acceptor); + } + + // Check if at least one listener succeeded + if (acceptors_.empty()) { + return srs_error_new(ERROR_SOCKET_LISTEN, "no srt listeners available"); } return err; diff --git a/trunk/src/core/srs_core_version7.hpp b/trunk/src/core/srs_core_version7.hpp index 238874436..3d3c38b41 100644 --- a/trunk/src/core/srs_core_version7.hpp +++ b/trunk/src/core/srs_core_version7.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 7 #define VERSION_MINOR 0 -#define VERSION_REVISION 66 +#define VERSION_REVISION 67 #endif \ No newline at end of file diff --git a/trunk/src/kernel/srs_kernel_utility.cpp b/trunk/src/kernel/srs_kernel_utility.cpp index 24447bd37..1653b9e01 100644 --- a/trunk/src/kernel/srs_kernel_utility.cpp +++ b/trunk/src/kernel/srs_kernel_utility.cpp @@ -855,6 +855,27 @@ bool srs_net_is_valid_ip(string ip) return false; } +bool srs_net_is_valid_endpoint(std::string endpoint) +{ + if (endpoint.empty()) { + return false; + } + + string host; + int port = 0; + srs_net_split_for_listener(endpoint, host, port); + + if (!srs_net_is_valid_ip(host)) { + return false; + } + + if (port <= 0) { + return false; + } + + return true; +} + bool srs_net_is_ipv4(string domain) { for (int i = 0; i < (int)domain.length(); i++) { diff --git a/trunk/src/kernel/srs_kernel_utility.hpp b/trunk/src/kernel/srs_kernel_utility.hpp index 88788f902..df8cc4976 100644 --- a/trunk/src/kernel/srs_kernel_utility.hpp +++ b/trunk/src/kernel/srs_kernel_utility.hpp @@ -105,6 +105,23 @@ std::string srs_strings_join(std::vector &vs, std::string separator) return ss.str(); } +// Compare two vector with string. +template +bool srs_strings_equal(std::vector &a, std::vector &b) +{ + if (a.size() != b.size()) { + return false; + } + + for (int i = 0; i < (int)a.size(); i++) { + if (a.at(i) != b.at(i)) { + return false; + } + } + + return true; +} + // Compare the memory in bytes. // @return true if completely equal; otherwise, false. extern bool srs_bytes_equal(void *pa, void *pb, int size); @@ -174,6 +191,9 @@ extern std::string srs_net_address_any(); // Check whether the ip is valid. extern bool srs_net_is_valid_ip(std::string ip); +// Check whether the endpoint is valid. +extern bool srs_net_is_valid_endpoint(std::string endpoint); + // Whether domain is an IPv4 address. extern bool srs_net_is_ipv4(std::string domain); diff --git a/trunk/src/protocol/srs_protocol_srt.cpp b/trunk/src/protocol/srs_protocol_srt.cpp index 0acc38439..399078932 100644 --- a/trunk/src/protocol/srs_protocol_srt.cpp +++ b/trunk/src/protocol/srs_protocol_srt.cpp @@ -55,6 +55,15 @@ static srs_error_t do_srs_srt_listen(srs_srt_t srt_fd, addrinfo *r) return srs_error_wrap(err, "nonblock"); } + // Set IPv6 options for IPv6 addresses + if (r->ai_family == AF_INET6) { + // Set SRTO_IPV6ONLY to 1 for IPv6-only behavior to avoid conflicts with IPv4 wildcard binding + int ipv6only = 1; // IPv6-only to avoid conflicts + if (srt_setsockflag(srt_fd, SRTO_IPV6ONLY, &ipv6only, sizeof(ipv6only)) == SRT_ERROR) { + return srs_error_new(ERROR_SOCKET_BIND, "srt set SRTO_IPV6ONLY=%d failed, err=%s", ipv6only, srt_getlasterror_str()); + } + } + if (srt_bind(srt_fd, r->ai_addr, r->ai_addrlen) == -1) { return srs_error_new(ERROR_SOCKET_BIND, "bind"); } @@ -204,7 +213,7 @@ srs_error_t srs_srt_listen(srs_srt_t srt_fd, std::string ip, int port) addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; + hints.ai_socktype = SOCK_DGRAM; // SRT is UDP-based hints.ai_flags = AI_NUMERICHOST; addrinfo *r_raw = NULL; @@ -764,7 +773,7 @@ srs_error_t SrsSrtSocket::accept(srs_srt_t *client_srt_fd) srs_error_t err = srs_success; while (true) { - sockaddr_in inaddr; + sockaddr_storage inaddr; // Use sockaddr_storage to support both IPv4 and IPv6 int addrlen = sizeof(inaddr); // @see https://github.com/Haivision/srt/blob/master/docs/API/API-functions.md#srt_accept srs_srt_t srt_fd = srt_accept(srt_fd_, (sockaddr *)&inaddr, &addrlen); diff --git a/trunk/src/protocol/srs_protocol_st.cpp b/trunk/src/protocol/srs_protocol_st.cpp index ada71e57a..6d741ea06 100644 --- a/trunk/src/protocol/srs_protocol_st.cpp +++ b/trunk/src/protocol/srs_protocol_st.cpp @@ -123,6 +123,13 @@ void srs_close_stfd(srs_netfd_t &stfd) } } +void srs_close_stfd_ptr(srs_netfd_t *stfd) +{ + if (stfd && *stfd) { + srs_close_stfd(*stfd); + } +} + srs_error_t srs_fd_closeexec(int fd) { int flags = fcntl(fd, F_GETFD); diff --git a/trunk/src/protocol/srs_protocol_st.hpp b/trunk/src/protocol/srs_protocol_st.hpp index bab3b40d5..c9367b32e 100644 --- a/trunk/src/protocol/srs_protocol_st.hpp +++ b/trunk/src/protocol/srs_protocol_st.hpp @@ -33,6 +33,7 @@ extern void srs_st_destroy(void); // Close the netfd, and close the underlayer fd. // @remark when close, user must ensure io completed. extern void srs_close_stfd(srs_netfd_t &stfd); +extern void srs_close_stfd_ptr(srs_netfd_t *stfd); // Set the FD_CLOEXEC of FD. extern srs_error_t srs_fd_closeexec(int fd); diff --git a/trunk/src/utest/srs_utest_config.cpp b/trunk/src/utest/srs_utest_config.cpp index cbbf1eae4..d98a7cd85 100644 --- a/trunk/src/utest/srs_utest_config.cpp +++ b/trunk/src/utest/srs_utest_config.cpp @@ -3729,43 +3729,57 @@ VOID TEST(ConfigMainTest, CheckHttpListen) if (true) { MockSrsConfig conf; - HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen xxx;}http_server{enabled on;listen xxx;}")); + HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 1234;}http_server{enabled on;listen 2345;}")); EXPECT_TRUE(conf.get_http_stream_enabled()); - EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); - EXPECT_STREQ("xxx", conf.get_http_api_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("2345", conf.get_http_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("1234", conf.get_http_api_listens().at(0).c_str()); EXPECT_TRUE(conf.get_http_stream_crossdomain()); } if (true) { MockSrsConfig conf; - HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen xxx;}http_server{enabled on;listen xxx;}")); + HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 1234;}http_server{enabled on;listen 2345;}")); EXPECT_TRUE(conf.get_http_stream_enabled()); - EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); - EXPECT_STREQ("8088", conf.get_https_stream_listen().c_str()); - EXPECT_STREQ("xxx", conf.get_http_api_listen().c_str()); - EXPECT_STREQ("8088", conf.get_https_api_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("2345", conf.get_http_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_https_stream_listens().size()); + EXPECT_STREQ("8088", conf.get_https_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("1234", conf.get_http_api_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_https_api_listens().size()); + EXPECT_STREQ("1990", conf.get_https_api_listens().at(0).c_str()); EXPECT_TRUE(conf.get_http_stream_crossdomain()); } if (true) { MockSrsConfig conf; - HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen mmm;https{enabled on;listen zzz;}}http_server{enabled on;listen xxx;https{enabled on;listen yyy;}}")); + HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 1234;https{enabled on;listen 2345;}}http_server{enabled on;listen 3456;https{enabled on;listen 4567;}}")); EXPECT_TRUE(conf.get_http_stream_enabled()); - EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); - EXPECT_STREQ("yyy", conf.get_https_stream_listen().c_str()); - EXPECT_STREQ("mmm", conf.get_http_api_listen().c_str()); - EXPECT_STREQ("zzz", conf.get_https_api_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("3456", conf.get_http_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_https_stream_listens().size()); + EXPECT_STREQ("4567", conf.get_https_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("1234", conf.get_http_api_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_https_api_listens().size()); + EXPECT_STREQ("2345", conf.get_https_api_listens().at(0).c_str()); EXPECT_TRUE(conf.get_http_stream_crossdomain()); } if (true) { MockSrsConfig conf; - HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen xxx;https{enabled on;listen yyy;}}http_server{enabled on;listen xxx;https{enabled on;listen yyy;}}")); + HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 1234;https{enabled on;listen 2345;}}http_server{enabled on;listen 3456;https{enabled on;listen 5678;}}")); EXPECT_TRUE(conf.get_http_stream_enabled()); - EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); - EXPECT_STREQ("yyy", conf.get_https_stream_listen().c_str()); - EXPECT_STREQ("xxx", conf.get_http_api_listen().c_str()); - EXPECT_STREQ("yyy", conf.get_https_api_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("3456", conf.get_http_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_https_stream_listens().size()); + EXPECT_STREQ("5678", conf.get_https_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("1234", conf.get_http_api_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_https_api_listens().size()); + EXPECT_STREQ("2345", conf.get_https_api_listens().at(0).c_str()); EXPECT_TRUE(conf.get_http_stream_crossdomain()); } @@ -3882,9 +3896,10 @@ VOID TEST(ConfigMainTest, CheckVhostConfig5) if (true) { MockSrsConfig conf; - HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen xxx;crossdomain off;auth {enabled on;username admin;password 123456;}raw_api {enabled on;allow_reload on;allow_query on;allow_update on;}}")); + HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 1234;crossdomain off;auth {enabled on;username admin;password 123456;}raw_api {enabled on;allow_reload on;allow_query on;allow_update on;}}")); EXPECT_TRUE(conf.get_http_api_enabled()); - EXPECT_STREQ("xxx", conf.get_http_api_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("1234", conf.get_http_api_listens().at(0).c_str()); EXPECT_FALSE(conf.get_http_api_crossdomain()); EXPECT_TRUE(conf.get_raw_api()); EXPECT_TRUE(conf.get_raw_api_allow_reload()); @@ -3897,9 +3912,10 @@ VOID TEST(ConfigMainTest, CheckVhostConfig5) if (true) { MockSrsConfig conf; - HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_server{enabled on;listen xxx;dir xxx2;crossdomain on;}")); + HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_server{enabled on;listen 1234;dir xxx2;crossdomain on;}")); EXPECT_TRUE(conf.get_http_stream_enabled()); - EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str()); EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str()); EXPECT_TRUE(conf.get_http_stream_crossdomain()); } @@ -3955,7 +3971,7 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig) MockSrsConfig conf; conf.mock_include("./conf/include_test/include.conf", "rtmp{listen 1935;}include ./conf/include_test/include_1.conf;"); - conf.mock_include("./conf/include_test/include_1.conf", "max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen xxx;dir xxx2;}vhost ossrs.net {hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}}"); + conf.mock_include("./conf/include_test/include_1.conf", "max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen 1234;dir xxx2;}vhost ossrs.net {hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}}"); HELPER_ASSERT_SUCCESS(conf.parse("include ./conf/include_test/include.conf;")); @@ -3966,7 +3982,8 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig) EXPECT_FALSE(conf.get_log_tank_file()); EXPECT_TRUE(conf.get_http_stream_enabled()); - EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str()); EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str()); EXPECT_TRUE(conf.get_hls_enabled("ossrs.net")); @@ -3980,7 +3997,7 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig) if (true) { MockSrsConfig conf; - conf.mock_include("./conf/include_test/include_1.conf", "max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen xxx;dir xxx2;}vhost ossrs.net {hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}}"); + conf.mock_include("./conf/include_test/include_1.conf", "max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen 1234;dir xxx2;}vhost ossrs.net {hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}}"); HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "include ./conf/include_test/include_1.conf;")); @@ -3991,7 +4008,8 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig) EXPECT_FALSE(conf.get_log_tank_file()); EXPECT_TRUE(conf.get_http_stream_enabled()); - EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str()); EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str()); EXPECT_TRUE(conf.get_hls_enabled("ossrs.net")); @@ -4005,7 +4023,7 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig) if (true) { MockSrsConfig conf; - conf.mock_include("./conf/include_test/include_2.conf", "listen 1935;max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen xxx;dir xxx2;}vhost ossrs.net {include ./conf/include_test/include_3.conf;}"); + conf.mock_include("./conf/include_test/include_2.conf", "listen 1935;max_connections 1000;daemon off;srs_log_tank console;http_server {enabled on;listen 1234;dir xxx2;}vhost ossrs.net {include ./conf/include_test/include_3.conf;}"); conf.mock_include("./conf/include_test/include_3.conf", "hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}"); HELPER_ASSERT_SUCCESS(conf.parse("include ./conf/include_test/include_2.conf;")); @@ -4017,7 +4035,8 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig) EXPECT_FALSE(conf.get_log_tank_file()); EXPECT_TRUE(conf.get_http_stream_enabled()); - EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str()); EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str()); EXPECT_TRUE(conf.get_hls_enabled("ossrs.net")); @@ -4045,7 +4064,8 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig) EXPECT_FALSE(conf.get_log_tank_file()); EXPECT_TRUE(conf.get_http_stream_enabled()); - EXPECT_STREQ("8080", conf.get_http_stream_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("8080", conf.get_http_stream_listens().at(0).c_str()); EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str()); EXPECT_TRUE(conf.get_hls_enabled("ossrs.net")); @@ -4056,7 +4076,8 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig) EXPECT_EQ(60 * SRS_UTIME_SECONDS, conf.get_hls_window("ossrs.net")); EXPECT_TRUE(conf.get_http_api_enabled()); - EXPECT_STREQ("1985", conf.get_http_api_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("1985", conf.get_http_api_listens().at(0).c_str()); } if (true) { @@ -4064,8 +4085,8 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig) conf.mock_include("./conf/include_test/include_3.conf", "hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}"); conf.mock_include("./conf/include_test/include_4.conf", "listen 1935;max_connections 1000;daemon off;srs_log_tank console;include ./conf/include_test/include_5.conf ./conf/include_test/include_6.conf;vhost ossrs.net {include ./conf/include_test/include_3.conf;}"); - conf.mock_include("./conf/include_test/include_5.conf", "http_server {enabled on;listen xxx;dir xxx2;}"); - conf.mock_include("./conf/include_test/include_6.conf", "http_api {enabled on;listen yyy;}"); + conf.mock_include("./conf/include_test/include_5.conf", "http_server {enabled on;listen 1234;dir xxx2;}"); + conf.mock_include("./conf/include_test/include_6.conf", "http_api {enabled on;listen 2345;}"); HELPER_ASSERT_SUCCESS(conf.parse("include ./conf/include_test/include_4.conf;")); @@ -4076,11 +4097,13 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig) EXPECT_FALSE(conf.get_log_tank_file()); EXPECT_TRUE(conf.get_http_stream_enabled()); - EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str()); EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str()); EXPECT_TRUE(conf.get_http_api_enabled()); - EXPECT_STREQ("yyy", conf.get_http_api_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("2345", conf.get_http_api_listens().at(0).c_str()); EXPECT_TRUE(conf.get_hls_enabled("ossrs.net")); EXPECT_STREQ("xxx", conf.get_hls_path("ossrs.net").c_str()); @@ -4095,8 +4118,8 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig) conf.mock_include("./conf/include_test/include_3.conf", "hls {enabled on;hls_path xxx;hls_m3u8_file xxx1;hls_ts_file xxx2;hls_fragment 10;hls_window 60;}"); conf.mock_include("./conf/include_test/include_4.conf", "listen 1935;max_connections 1000;daemon off;srs_log_tank console;include ./conf/include_test/include_5.conf ./conf/include_test/include_6.conf;vhost ossrs.net {include ./conf/include_test/include_3.conf ./conf/include_test/include_7.conf;}"); - conf.mock_include("./conf/include_test/include_5.conf", "http_server {enabled on;listen xxx;dir xxx2;}"); - conf.mock_include("./conf/include_test/include_6.conf", "http_api {enabled on;listen yyy;}"); + conf.mock_include("./conf/include_test/include_5.conf", "http_server {enabled on;listen 1234;dir xxx2;}"); + conf.mock_include("./conf/include_test/include_6.conf", "http_api {enabled on;listen 2345;}"); conf.mock_include("./conf/include_test/include_7.conf", "dash{enabled on;dash_fragment 10;dash_update_period 10;dash_timeshift 10;dash_path xxx;dash_mpd_file xxx2;}"); HELPER_ASSERT_SUCCESS(conf.parse("include ./conf/include_test/include_4.conf;")); @@ -4108,11 +4131,13 @@ VOID TEST(ConfigMainTest, CheckIncludeConfig) EXPECT_FALSE(conf.get_log_tank_file()); EXPECT_TRUE(conf.get_http_stream_enabled()); - EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str()); EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str()); EXPECT_TRUE(conf.get_http_api_enabled()); - EXPECT_STREQ("yyy", conf.get_http_api_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("2345", conf.get_http_api_listens().at(0).c_str()); EXPECT_TRUE(conf.get_hls_enabled("ossrs.net")); EXPECT_STREQ("xxx", conf.get_hls_path("ossrs.net").c_str()); @@ -4344,7 +4369,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesHttpApi) EXPECT_TRUE(conf.get_http_api_enabled()); SrsSetEnvConfig(conf, http_api_listen, "SRS_HTTP_API_LISTEN", "xxx"); - EXPECT_STREQ("xxx", conf.get_http_api_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("xxx", conf.get_http_api_listens().at(0).c_str()); SrsSetEnvConfig(conf, http_api_crossdomain, "SRS_HTTP_API_CROSSDOMAIN", "off"); EXPECT_FALSE(conf.get_http_api_crossdomain()); @@ -4376,7 +4402,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesHttpApi) EXPECT_TRUE(conf.get_https_api_enabled()); SrsSetEnvConfig(conf, https_api_listen, "SRS_HTTP_API_HTTPS_LISTEN", "xxx"); - EXPECT_STREQ("xxx", conf.get_https_api_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_https_api_listens().size()); + EXPECT_STREQ("xxx", conf.get_https_api_listens().at(0).c_str()); SrsSetEnvConfig(conf, https_api_ssl_key, "SRS_HTTP_API_HTTPS_KEY", "xxx2"); EXPECT_STREQ("xxx2", conf.get_https_api_ssl_key().c_str()); @@ -4395,7 +4422,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesHttpServer) EXPECT_TRUE(conf.get_http_stream_enabled()); SrsSetEnvConfig(conf, http_stream_listen, "SRS_HTTP_SERVER_LISTEN", "xxx"); - EXPECT_STREQ("xxx", conf.get_http_stream_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("xxx", conf.get_http_stream_listens().at(0).c_str()); SrsSetEnvConfig(conf, http_stream_dir, "SRS_HTTP_SERVER_DIR", "xxx2"); EXPECT_STREQ("xxx2", conf.get_http_stream_dir().c_str()); @@ -4411,7 +4439,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesHttpServer) EXPECT_TRUE(conf.get_https_stream_enabled()); SrsSetEnvConfig(conf, https_stream_listen, "SRS_HTTP_SERVER_HTTPS_LISTEN", "xxx"); - EXPECT_STREQ("xxx", conf.get_https_stream_listen().c_str()); + EXPECT_EQ(1, (int)conf.get_https_stream_listens().size()); + EXPECT_STREQ("xxx", conf.get_https_stream_listens().at(0).c_str()); SrsSetEnvConfig(conf, https_stream_ssl_key, "SRS_HTTP_SERVER_HTTPS_KEY", "xxx2"); EXPECT_STREQ("xxx2", conf.get_https_stream_ssl_key().c_str()); @@ -4430,7 +4459,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesSrtServer) EXPECT_TRUE(conf.get_srt_enabled()); SrsSetEnvConfig(conf, srt_listen_port, "SRS_SRT_SERVER_LISTEN", "10000"); - EXPECT_EQ(10000, conf.get_srt_listen_port()); + EXPECT_EQ(1, (int)conf.get_srt_listens().size()); + EXPECT_STREQ("10000", conf.get_srt_listens().at(0).c_str()); SrsSetEnvConfig(conf, srto_maxbw, "SRS_SRT_SERVER_MAXBW", "1000000000"); EXPECT_EQ(1000000000, conf.get_srto_maxbw()); @@ -4501,7 +4531,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesRtspServer) EXPECT_TRUE(conf.get_rtsp_server_enabled()); SrsSetEnvConfig(conf, rtsp_server_listen, "SRS_RTSP_SERVER_LISTEN", "554"); - EXPECT_EQ(554, conf.get_rtsp_server_listen()); + EXPECT_EQ(1, (int)conf.get_rtsp_server_listens().size()); + EXPECT_STREQ("554", conf.get_rtsp_server_listens().at(0).c_str()); } } @@ -4514,7 +4545,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesRtcServer) EXPECT_TRUE(conf.get_rtc_server_enabled()); SrsSetEnvConfig(conf, rtc_server_listen, "SRS_RTC_SERVER_LISTEN", "8080"); - EXPECT_EQ(8080, conf.get_rtc_server_listen()); + EXPECT_EQ(1, (int)conf.get_rtc_server_listens().size()); + EXPECT_STREQ("8080", conf.get_rtc_server_listens().at(0).c_str()); SrsSetEnvConfig(conf, rtc_server_protocol, "SRS_RTC_SERVER_PROTOCOL", "xxx"); EXPECT_STREQ("xxx", conf.get_rtc_server_protocol().c_str()); @@ -4557,7 +4589,8 @@ VOID TEST(ConfigEnvTest, CheckEnvValuesRtcServer) EXPECT_TRUE(conf.get_rtc_server_tcp_enabled()); SrsSetEnvConfig(conf, get_rtc_server_tcp_listen, "SRS_RTC_SERVER_TCP_LISTEN", "8080"); - EXPECT_EQ(8080, conf.get_rtc_server_tcp_listen()); + EXPECT_EQ(1, (int)conf.get_rtc_server_tcp_listens().size()); + EXPECT_STREQ("8080", conf.get_rtc_server_tcp_listens().at(0).c_str()); } if (true) { diff --git a/trunk/src/utest/srs_utest_config2.cpp b/trunk/src/utest/srs_utest_config2.cpp index 6a88d4dea..c803e1345 100644 --- a/trunk/src/utest/srs_utest_config2.cpp +++ b/trunk/src/utest/srs_utest_config2.cpp @@ -44,3 +44,122 @@ VOID TEST(ConfigMainTest, CheckIncludeEmptyConfig) EXPECT_EQ(1, (int)conf.get_listens().size()); } } + +VOID TEST(ConfigMainTest, CheckHttpListenFollow) +{ + srs_error_t err; + + if (true) { + MockSrsConfig conf; + HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;https{enabled on;}}http_server{enabled on;listen 1985;https{enabled on;listen 4567;}}")); + EXPECT_TRUE(conf.get_http_stream_enabled()); + + // If http API use same port to HTTP server. + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("1985", conf.get_http_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("1985", conf.get_http_api_listens().at(0).c_str()); + + // Then HTTPS API should use the same port to HTTPS server. + EXPECT_EQ(1, (int)conf.get_https_stream_listens().size()); + EXPECT_STREQ("4567", conf.get_https_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_https_api_listens().size()); + EXPECT_STREQ("4567", conf.get_https_api_listens().at(0).c_str()); + EXPECT_TRUE(conf.get_http_stream_crossdomain()); + } + + if (true) { + MockSrsConfig conf; + HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 8080;https{enabled on;}}http_server{enabled on;https{enabled on;listen 4567;}}")); + EXPECT_TRUE(conf.get_http_stream_enabled()); + + // If http API use same port to HTTP server. + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("8080", conf.get_http_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("8080", conf.get_http_api_listens().at(0).c_str()); + + // Then HTTPS API should use the same port to HTTPS server. + EXPECT_EQ(1, (int)conf.get_https_stream_listens().size()); + EXPECT_STREQ("4567", conf.get_https_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_https_api_listens().size()); + EXPECT_STREQ("4567", conf.get_https_api_listens().at(0).c_str()); + EXPECT_TRUE(conf.get_http_stream_crossdomain()); + } + + if (true) { + MockSrsConfig conf; + HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 8080;https{enabled on;}}http_server{enabled on;https{enabled on;}}")); + EXPECT_TRUE(conf.get_http_stream_enabled()); + + // If http API use same port to HTTP server. + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("8080", conf.get_http_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("8080", conf.get_http_api_listens().at(0).c_str()); + + // Then HTTPS API should use the same port to HTTPS server. + EXPECT_EQ(1, (int)conf.get_https_stream_listens().size()); + EXPECT_STREQ("8088", conf.get_https_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_https_api_listens().size()); + EXPECT_STREQ("8088", conf.get_https_api_listens().at(0).c_str()); + EXPECT_TRUE(conf.get_http_stream_crossdomain()); + } + + if (true) { + MockSrsConfig conf; + HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;https{enabled on;}}http_server{enabled on;listen 1985;https{enabled on;}}")); + EXPECT_TRUE(conf.get_http_stream_enabled()); + + // If http API use same port to HTTP server. + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("1985", conf.get_http_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("1985", conf.get_http_api_listens().at(0).c_str()); + + // Then HTTPS API should use the same port to HTTPS server. + EXPECT_EQ(1, (int)conf.get_https_stream_listens().size()); + EXPECT_STREQ("8088", conf.get_https_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_https_api_listens().size()); + EXPECT_STREQ("8088", conf.get_https_api_listens().at(0).c_str()); + EXPECT_TRUE(conf.get_http_stream_crossdomain()); + } + + if (true) { + MockSrsConfig conf; + HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 1234;https{enabled on;}}http_server{enabled on;listen 1234;https{enabled on;}}")); + EXPECT_TRUE(conf.get_http_stream_enabled()); + + // If http API use same port to HTTP server. + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("1234", conf.get_http_api_listens().at(0).c_str()); + + // Then HTTPS API should use the same port to HTTPS server. + EXPECT_EQ(1, (int)conf.get_https_stream_listens().size()); + EXPECT_STREQ("8088", conf.get_https_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_https_api_listens().size()); + EXPECT_STREQ("8088", conf.get_https_api_listens().at(0).c_str()); + EXPECT_TRUE(conf.get_http_stream_crossdomain()); + } + + if (true) { + MockSrsConfig conf; + HELPER_ASSERT_SUCCESS(conf.parse(_MIN_OK_CONF "http_api{enabled on;listen 1234;https{enabled on;}}http_server{enabled on;listen 1234;https{enabled on;listen 4567;}}")); + EXPECT_TRUE(conf.get_http_stream_enabled()); + + // If http API use same port to HTTP server. + EXPECT_EQ(1, (int)conf.get_http_stream_listens().size()); + EXPECT_STREQ("1234", conf.get_http_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_http_api_listens().size()); + EXPECT_STREQ("1234", conf.get_http_api_listens().at(0).c_str()); + + // Then HTTPS API should use the same port to HTTPS server. + EXPECT_EQ(1, (int)conf.get_https_stream_listens().size()); + EXPECT_STREQ("4567", conf.get_https_stream_listens().at(0).c_str()); + EXPECT_EQ(1, (int)conf.get_https_api_listens().size()); + EXPECT_STREQ("4567", conf.get_https_api_listens().at(0).c_str()); + EXPECT_TRUE(conf.get_http_stream_crossdomain()); + } +} diff --git a/trunk/src/utest/srs_utest_st.cpp b/trunk/src/utest/srs_utest_st.cpp index 1ddf55a21..f0428f21c 100644 --- a/trunk/src/utest/srs_utest_st.cpp +++ b/trunk/src/utest/srs_utest_st.cpp @@ -3,6 +3,10 @@ // // SPDX-License-Identifier: MIT // +#include +#include +#include +#include #include #include #include @@ -108,3 +112,497 @@ VOID TEST(StTest, StUtimePerformance) EXPECT_LT(gettimeofday_elapsed_time > st_utime_elapsed_time ? gettimeofday_elapsed_time - st_utime_elapsed_time : st_utime_elapsed_time - gettimeofday_elapsed_time, 30); } } + +// Test IPv4 and IPv6 socket functions +VOID TEST(StSocketTest, IPv4TcpListen) +{ + srs_error_t err; + + srs_netfd_t fd = NULL; + HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", 0, &fd)); + EXPECT_TRUE(fd != NULL); + + // Get the actual port that was assigned + int actual_fd = srs_netfd_fileno(fd); + EXPECT_GT(actual_fd, 0); + + srs_close_stfd(fd); +} + +VOID TEST(StSocketTest, IPv4UdpListen) +{ + srs_error_t err; + + srs_netfd_t fd = NULL; + HELPER_EXPECT_SUCCESS(srs_udp_listen("127.0.0.1", 0, &fd)); + EXPECT_TRUE(fd != NULL); + + // Get the actual port that was assigned + int actual_fd = srs_netfd_fileno(fd); + EXPECT_GT(actual_fd, 0); + + srs_close_stfd(fd); +} + +VOID TEST(StSocketTest, IPv6TcpListen) +{ + srs_error_t err; + + srs_netfd_t fd = NULL; + HELPER_EXPECT_SUCCESS(srs_udp_listen("::1", 0, &fd)); + + int actual_fd = srs_netfd_fileno(fd); + EXPECT_GT(actual_fd, 0); + + srs_close_stfd(fd); +} + +VOID TEST(StSocketTest, IPv6UdpListen) +{ + srs_error_t err; + + srs_netfd_t fd = NULL; + HELPER_EXPECT_SUCCESS(srs_udp_listen("::1", 0, &fd)); + + int actual_fd = srs_netfd_fileno(fd); + EXPECT_GT(actual_fd, 0); + + srs_close_stfd(fd); +} + +VOID TEST(StSocketTest, IPv6AnyAddressTcpListen) +{ + srs_error_t err; + + srs_netfd_t fd = NULL; + HELPER_EXPECT_SUCCESS(srs_tcp_listen("::", 0, &fd)); + + int actual_fd = srs_netfd_fileno(fd); + EXPECT_GT(actual_fd, 0); + + srs_close_stfd(fd); +} + +VOID TEST(StSocketTest, IPv6AnyAddressUdpListen) +{ + srs_error_t err; + + srs_netfd_t fd = NULL; + HELPER_EXPECT_SUCCESS(srs_udp_listen("::", 0, &fd)); + + int actual_fd = srs_netfd_fileno(fd); + EXPECT_GT(actual_fd, 0); + + srs_close_stfd(fd); +} + +VOID TEST(StSocketTest, IPv4AnyAddressTcpListen) +{ + srs_error_t err; + + srs_netfd_t fd = NULL; + HELPER_EXPECT_SUCCESS(srs_tcp_listen("0.0.0.0", 0, &fd)); + EXPECT_TRUE(fd != NULL); + + int actual_fd = srs_netfd_fileno(fd); + EXPECT_GT(actual_fd, 0); + + srs_close_stfd(fd); +} + +VOID TEST(StSocketTest, IPv4AnyAddressUdpListen) +{ + srs_error_t err; + + srs_netfd_t fd = NULL; + HELPER_EXPECT_SUCCESS(srs_udp_listen("0.0.0.0", 0, &fd)); + EXPECT_TRUE(fd != NULL); + + int actual_fd = srs_netfd_fileno(fd); + EXPECT_GT(actual_fd, 0); + + srs_close_stfd(fd); +} + +VOID TEST(StSocketTest, InvalidAddressTcpListen) +{ + srs_error_t err; + + srs_netfd_t fd = NULL; + HELPER_EXPECT_FAILED(srs_tcp_listen("999.999.999.999", 8080, &fd)); + EXPECT_TRUE(fd == NULL); + + HELPER_EXPECT_FAILED(srs_tcp_listen("::gggg", 8080, &fd)); + EXPECT_TRUE(fd == NULL); +} + +VOID TEST(StSocketTest, InvalidAddressUdpListen) +{ + srs_error_t err; + + srs_netfd_t fd = NULL; + HELPER_EXPECT_FAILED(srs_udp_listen("999.999.999.999", 8080, &fd)); + EXPECT_TRUE(fd == NULL); + + HELPER_EXPECT_FAILED(srs_udp_listen("::gggg", 8080, &fd)); + EXPECT_TRUE(fd == NULL); +} + +VOID TEST(StSocketTest, PortRangeTcpListen) +{ + srs_error_t err; + + // Test valid port ranges + srs_netfd_t fd = NULL; + HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", 1024, &fd)); + EXPECT_TRUE(fd != NULL); + srs_close_stfd(fd); + + fd = NULL; + HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", 65535, &fd)); + EXPECT_TRUE(fd != NULL); + srs_close_stfd(fd); + + // Test port 0 (system assigns available port) + fd = NULL; + HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", 0, &fd)); + EXPECT_TRUE(fd != NULL); + srs_close_stfd(fd); +} + +VOID TEST(StSocketTest, PortRangeUdpListen) +{ + srs_error_t err; + + // Test valid port ranges + srs_netfd_t fd = NULL; + HELPER_EXPECT_SUCCESS(srs_udp_listen("127.0.0.1", 1024, &fd)); + EXPECT_TRUE(fd != NULL); + srs_close_stfd(fd); + + fd = NULL; + HELPER_EXPECT_SUCCESS(srs_udp_listen("127.0.0.1", 65535, &fd)); + EXPECT_TRUE(fd != NULL); + srs_close_stfd(fd); + + // Test port 0 (system assigns available port) + fd = NULL; + HELPER_EXPECT_SUCCESS(srs_udp_listen("127.0.0.1", 0, &fd)); + EXPECT_TRUE(fd != NULL); + srs_close_stfd(fd); +} + +VOID TEST(StSocketTest, TcpConnectIPv4) +{ + srs_error_t err; + + // Test connecting to a well-known service (should fail but test address resolution) + srs_netfd_t fd = NULL; + HELPER_EXPECT_FAILED(srs_tcp_connect("127.0.0.1", 1, SRS_UTIME_SECONDS, &fd)); + EXPECT_TRUE(fd == NULL); +} + +VOID TEST(StSocketTest, TcpConnectIPv6) +{ + srs_error_t err; + + // Test connecting to IPv6 loopback (should fail but test address resolution) + srs_netfd_t fd = NULL; + HELPER_EXPECT_FAILED(srs_tcp_connect("::1", 1, SRS_UTIME_SECONDS, &fd)); + EXPECT_TRUE(fd == NULL); +} + +VOID TEST(StSocketTest, TcpConnectInvalidAddress) +{ + srs_error_t err; + + // Test connecting to invalid address + srs_netfd_t fd = NULL; + HELPER_EXPECT_FAILED(srs_tcp_connect("999.999.999.999", 80, SRS_UTIME_SECONDS, &fd)); + EXPECT_TRUE(fd == NULL); + + // Test connecting to invalid IPv6 address + fd = NULL; + HELPER_EXPECT_FAILED(srs_tcp_connect("::gggg", 80, SRS_UTIME_SECONDS, &fd)); + EXPECT_TRUE(fd == NULL); +} + +// Test address utility functions +VOID TEST(StSocketTest, AddressUtilityFunctions) +{ + // Test srs_net_address_any() function + string any_addr = srs_net_address_any(); + EXPECT_TRUE(any_addr == "0.0.0.0" || any_addr == "::"); + + // Test srs_net_is_valid_ip() function + EXPECT_TRUE(srs_net_is_valid_ip("127.0.0.1")); + EXPECT_TRUE(srs_net_is_valid_ip("0.0.0.0")); + EXPECT_TRUE(srs_net_is_valid_ip("192.168.1.1")); + + // Test IPv6 addresses if supported + EXPECT_TRUE(srs_net_is_valid_ip("::1")); + EXPECT_TRUE(srs_net_is_valid_ip("::")); + EXPECT_TRUE(srs_net_is_valid_ip("2001:db8::1")); + + // Test invalid addresses + EXPECT_FALSE(srs_net_is_valid_ip("999.999.999.999")); + EXPECT_FALSE(srs_net_is_valid_ip("::gggg")); + EXPECT_FALSE(srs_net_is_valid_ip("invalid")); + EXPECT_FALSE(srs_net_is_valid_ip("")); +} + +VOID TEST(StSocketTest, AddressParsingForListener) +{ + string ip; + int port; + + // Test IPv4 address:port parsing + srs_net_split_for_listener("192.168.1.1:8080", ip, port); + EXPECT_EQ(ip, "192.168.1.1"); + EXPECT_EQ(port, 8080); + + // Test IPv4 address without port (should use any address) + srs_net_split_for_listener("8080", ip, port); + EXPECT_TRUE(ip == "0.0.0.0" || ip == "::"); // Should be any address + EXPECT_EQ(port, 8080); + + // Test IPv6 address in RFC 2732 format [address]:port + srs_net_split_for_listener("[::1]:8080", ip, port); + EXPECT_EQ(ip, "::1"); + EXPECT_EQ(port, 8080); + + // Test IPv6 address in RFC 2732 format [address]:port with full address + srs_net_split_for_listener("[2001:db8::1]:1935", ip, port); + EXPECT_EQ(ip, "2001:db8::1"); + EXPECT_EQ(port, 1935); + + // Test IPv6 any address + srs_net_split_for_listener("[::]:8080", ip, port); + EXPECT_EQ(ip, "::"); + EXPECT_EQ(port, 8080); + + // Test localhost + srs_net_split_for_listener("localhost:8080", ip, port); + EXPECT_EQ(ip, "localhost"); + EXPECT_EQ(port, 8080); +} + +VOID TEST(StSocketTest, IPv4Detection) +{ + // Test srs_net_is_ipv4() function + EXPECT_TRUE(srs_net_is_ipv4("127.0.0.1")); + EXPECT_TRUE(srs_net_is_ipv4("0.0.0.0")); + EXPECT_TRUE(srs_net_is_ipv4("192.168.1.1")); + EXPECT_TRUE(srs_net_is_ipv4("255.255.255.255")); + + // Test non-IPv4 addresses + EXPECT_FALSE(srs_net_is_ipv4("::1")); + EXPECT_FALSE(srs_net_is_ipv4("2001:db8::1")); + EXPECT_FALSE(srs_net_is_ipv4("localhost")); + EXPECT_FALSE(srs_net_is_ipv4("example.com")); + + // Test empty string - the function might treat it as IPv4 format, so check actual behavior + bool empty_result = srs_net_is_ipv4(""); + // Just verify the function doesn't crash, behavior may vary + EXPECT_TRUE(empty_result == true || empty_result == false); + + // Test invalid IPv4 (but still numeric format) + EXPECT_TRUE(srs_net_is_ipv4("999.999.999.999")); // Invalid but IPv4 format +} + +VOID TEST(StSocketTest, DualStackBehavior) +{ + srs_error_t err; + + // Test that we can create both IPv4 and IPv6 sockets on the same port + // This tests the underlying dual-stack behavior + + srs_netfd_t ipv4_fd = NULL; + srs_netfd_t ipv6_fd = NULL; + + // Create IPv4 socket first + HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", 0, &ipv4_fd)); + EXPECT_TRUE(ipv4_fd != NULL); + srs_close_stfd(ipv4_fd); + + // Try to create IPv6 socket (may fail if IPv6 not supported) + HELPER_EXPECT_SUCCESS(srs_tcp_listen("::1", 0, &ipv6_fd)); + EXPECT_TRUE(ipv6_fd != NULL); + srs_close_stfd(ipv6_fd); +} + +VOID TEST(StSocketTest, SocketFileDescriptorValidation) +{ + srs_error_t err; + + // Test that created sockets have valid file descriptors + srs_netfd_t tcp_fd = NULL; + HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", 0, &tcp_fd)); + EXPECT_TRUE(tcp_fd != NULL); + + int raw_fd = srs_netfd_fileno(tcp_fd); + EXPECT_GT(raw_fd, 0); // Valid file descriptor should be > 0 + + srs_close_stfd(tcp_fd); + + // Test UDP socket + srs_netfd_t udp_fd = NULL; + HELPER_EXPECT_SUCCESS(srs_udp_listen("127.0.0.1", 0, &udp_fd)); + EXPECT_TRUE(udp_fd != NULL); + + raw_fd = srs_netfd_fileno(udp_fd); + EXPECT_GT(raw_fd, 0); // Valid file descriptor should be > 0 + + srs_close_stfd(udp_fd); +} + +VOID TEST(StSocketTest, MultipleSocketsOnDifferentPorts) +{ + srs_error_t err; + + // Test creating multiple sockets on different ports + vector sockets; + + for (int i = 0; i < 5; i++) { + srs_netfd_t fd = NULL; + HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", 0, &fd)); // Port 0 = system assigns + EXPECT_TRUE(fd != NULL); + + int raw_fd = srs_netfd_fileno(fd); + EXPECT_GT(raw_fd, 0); + + sockets.push_back(fd); + } + + // Clean up all sockets + for (size_t i = 0; i < sockets.size(); i++) { + srs_close_stfd(sockets[i]); + } +} + +VOID TEST(StSocketTest, RandomPortTcpListenAndConnect) +{ + srs_error_t err; + + // Generate random port in range [30000, 60000] + int random_port = 30000 + (srs_rand_integer() % (60000 - 30000 + 1)); + EXPECT_GE(random_port, 30000); + EXPECT_LE(random_port, 60000); + + // Create TCP listener on the random port + srs_netfd_t listen_fd = NULL; + HELPER_EXPECT_SUCCESS(srs_tcp_listen("127.0.0.1", random_port, &listen_fd)); + EXPECT_TRUE(listen_fd != NULL); + + // Use SrsUniquePtr to automatically close the socket when it goes out of scope. + SrsUniquePtr listen_fd_ptr(&listen_fd, srs_close_stfd_ptr); + + int actual_fd = srs_netfd_fileno(listen_fd); + EXPECT_GT(actual_fd, 0); + + // Try to connect to the listening port + srs_netfd_t connect_fd = NULL; + HELPER_EXPECT_SUCCESS(srs_tcp_connect("127.0.0.1", random_port, SRS_UTIME_SECONDS, &connect_fd)); + SrsUniquePtr connect_fd_ptr(&connect_fd, srs_close_stfd_ptr); + + int connect_actual_fd = srs_netfd_fileno(connect_fd); + EXPECT_GT(connect_actual_fd, 0); + + // Verify they are different file descriptors + EXPECT_NE(actual_fd, connect_actual_fd); +} + +VOID TEST(StSocketTest, RandomPortTcpListenAndConnectIPv6) +{ + srs_error_t err; + + // Generate random port in range [30000, 60000] + int random_port = 30000 + (srs_rand_integer() % (60000 - 30000 + 1)); + EXPECT_GE(random_port, 30000); + EXPECT_LE(random_port, 60000); + + // Create TCP listener on IPv6 loopback with the random port + srs_netfd_t listen_fd = NULL; + HELPER_EXPECT_SUCCESS(srs_tcp_listen("::1", random_port, &listen_fd)); + EXPECT_TRUE(listen_fd != NULL); + SrsUniquePtr listen_fd_ptr(&listen_fd, srs_close_stfd_ptr); + + int actual_fd = srs_netfd_fileno(listen_fd); + EXPECT_GT(actual_fd, 0); + + // Try to connect to the listening port + srs_netfd_t connect_fd = NULL; + HELPER_EXPECT_SUCCESS(srs_tcp_connect("::1", random_port, SRS_UTIME_SECONDS, &connect_fd)); + EXPECT_TRUE(connect_fd != NULL); + SrsUniquePtr connect_fd_ptr(&connect_fd, srs_close_stfd_ptr); + + int connect_actual_fd = srs_netfd_fileno(connect_fd); + EXPECT_GT(connect_actual_fd, 0); + + // Verify they are different file descriptors + EXPECT_NE(actual_fd, connect_actual_fd); +} + +VOID TEST(StSocketTest, RandomPortUdpListenIPv4) +{ + srs_error_t err; + + // Generate random port in range [30000, 60000] + int random_port = 30000 + (srs_rand_integer() % (60000 - 30000 + 1)); + EXPECT_GE(random_port, 30000); + EXPECT_LE(random_port, 60000); + + // Create UDP listener on IPv4 loopback with the random port + srs_netfd_t listen_fd = NULL; + HELPER_EXPECT_SUCCESS(srs_udp_listen("127.0.0.1", random_port, &listen_fd)); + EXPECT_TRUE(listen_fd != NULL); + SrsUniquePtr listen_fd_ptr(&listen_fd, srs_close_stfd_ptr); + + int actual_fd = srs_netfd_fileno(listen_fd); + EXPECT_GT(actual_fd, 0); + + // For UDP, we can create another socket and bind to a different port + // to simulate client behavior (UDP is connectionless) + srs_netfd_t client_fd = NULL; + HELPER_EXPECT_SUCCESS(srs_udp_listen("127.0.0.1", 0, &client_fd)); // Port 0 = system assigns + EXPECT_TRUE(client_fd != NULL); + SrsUniquePtr client_fd_ptr(&client_fd, srs_close_stfd_ptr); + + int client_actual_fd = srs_netfd_fileno(client_fd); + EXPECT_GT(client_actual_fd, 0); + + // Verify they are different file descriptors + EXPECT_NE(actual_fd, client_actual_fd); +} + +VOID TEST(StSocketTest, RandomPortUdpListenIPv6) +{ + srs_error_t err; + + // Generate random port in range [30000, 60000] + int random_port = 30000 + (srs_rand_integer() % (60000 - 30000 + 1)); + EXPECT_GE(random_port, 30000); + EXPECT_LE(random_port, 60000); + + // Create UDP listener on IPv6 loopback with the random port + srs_netfd_t listen_fd = NULL; + HELPER_EXPECT_SUCCESS(srs_udp_listen("::1", random_port, &listen_fd)); + EXPECT_TRUE(listen_fd != NULL); + SrsUniquePtr listen_fd_ptr(&listen_fd, srs_close_stfd_ptr); + + int actual_fd = srs_netfd_fileno(listen_fd); + EXPECT_GT(actual_fd, 0); + + // For UDP, we can create another socket and bind to a different port + // to simulate client behavior (UDP is connectionless) + srs_netfd_t client_fd = NULL; + HELPER_EXPECT_SUCCESS(srs_udp_listen("::1", 0, &client_fd)); + EXPECT_TRUE(client_fd != NULL); + SrsUniquePtr client_fd_ptr(&client_fd, srs_close_stfd_ptr); + + int client_actual_fd = srs_netfd_fileno(client_fd); + EXPECT_GT(client_actual_fd, 0); + + // Verify they are different file descriptors + EXPECT_NE(actual_fd, client_actual_fd); +}