diff --git a/conf/config.ini b/conf/config.ini index f3c51015..9d53457f 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -241,6 +241,8 @@ forbidCacheSuffix= forwarded_ip_header= #默认允许所有跨域请求 allow_cross_domains=1 +#允许访问http api和http文件索引的ip地址范围白名单,置空情况下不做限制 +allow_ip_range=127.0.0.1,172.16.0.0-172.31.255.255,192.168.0.0-192.168.255.255 [multicast] #rtp组播截止组播ip地址 diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 6742a957..fe56d404 100755 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -238,6 +238,11 @@ static inline void addHttpListener(){ //该api已被消费 consumed = true; + if (!HttpFileManager::isIPAllowed(sender.get_peer_ip())) { + invoker(403, HttpSession::KeyValue(), "Your ip is not allowed to access the service."); + return; + } + if(api_debug){ auto newInvoker = [invoker, parser](int code, const HttpSession::KeyValue &headerOut, const HttpBody::Ptr &body) { //body默认为空 diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 52c79091..d96c939f 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -165,6 +165,7 @@ const string kDirMenu = HTTP_FIELD "dirMenu"; const string kForbidCacheSuffix = HTTP_FIELD "forbidCacheSuffix"; const string kForwardedIpHeader = HTTP_FIELD "forwarded_ip_header"; const string kAllowCrossDomains = HTTP_FIELD "allow_cross_domains"; +const string kAllowIPRange = HTTP_FIELD "allow_ip_range"; static onceToken token([]() { mINI::Instance()[kSendBufSize] = 64 * 1024; @@ -193,6 +194,7 @@ static onceToken token([]() { mINI::Instance()[kForbidCacheSuffix] = ""; mINI::Instance()[kForwardedIpHeader] = ""; mINI::Instance()[kAllowCrossDomains] = 1; + mINI::Instance()[kAllowIPRange] = "127.0.0.1,172.16.0.0-172.31.255.255,192.168.0.0-192.168.255.255"; }); } // namespace Http diff --git a/src/Common/config.h b/src/Common/config.h index ec546a17..341f1d6c 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -254,6 +254,8 @@ extern const std::string kForbidCacheSuffix; extern const std::string kForwardedIpHeader; // 是否允许所有跨域请求 extern const std::string kAllowCrossDomains; +// 允许访问http api和http文件索引的ip地址范围白名单,置空情况下不做限制 +extern const std::string kAllowIPRange; } // namespace Http ////////////SHELL配置/////////// diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index 3b0fa544..a6d7b756 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -50,6 +50,58 @@ const string &HttpFileManager::getContentType(const char *name) { return HttpConst::getHttpContentType(name); } +#ifndef ntohll +static uint64_t ntohll(uint64_t val) { + return (((uint64_t)ntohl(val)) << 32) + ntohl(val >> 32); +} +#endif + +static uint64_t get_ip_uint64(const std::string &ip) { + try { + auto storage = SockUtil::make_sockaddr(ip.data(), 0); + if (storage.ss_family == AF_INET) { + return ntohl(reinterpret_cast(reinterpret_cast(storage).sin_addr)); + } + if (storage.ss_family == AF_INET6) { + return ntohll(reinterpret_cast(reinterpret_cast(storage).sin6_addr)); + } + } catch (std::exception &ex) { + WarnL << ex.what(); + } + return 0; +} + +bool HttpFileManager::isIPAllowed(const std::string &ip) { + using IPRangs = std::vector>; + GET_CONFIG_FUNC(IPRangs, allow_ip_range, Http::kAllowIPRange, [](const string &str) -> IPRangs { + IPRangs ret; + auto vec = split(str, ","); + for (auto &item : vec) { + auto range = split(item, "-"); + if (range.size() == 2) { + ret.emplace_back(get_ip_uint64(trim(range[0])), get_ip_uint64(trim(range[1]))); + } else if (range.size() == 1) { + auto ip = get_ip_uint64(trim(range[0])); + ret.emplace_back(ip, ip); + } else { + WarnL << "Invalid ip range: " << item; + } + } + return ret; + }); + + if (allow_ip_range.empty()) { + return true; + } + for (auto &range : allow_ip_range) { + auto ip_int = get_ip_uint64(ip); + if (ip_int >= range.first && ip_int <= range.second) { + return true; + } + } + return false; +} + static string searchIndexFile(const string &dir){ DIR *pDir; dirent *pDirent; @@ -321,10 +373,15 @@ static void canAccessPath(Session &sender, const Parser &parser, const MediaInfo return; } - //事件未被拦截,则认为是http下载请求 + if (!HttpFileManager::isIPAllowed(sender.get_peer_ip())) { + callback("Your ip is not allowed to access the service.", nullptr); + return; + } + + // 事件未被拦截,则认为是http下载请求 bool flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpAccess, parser, path, is_dir, accessPathInvoker, static_cast(sender)); if (!flag) { - //此事件无人监听,我们默认都有权限访问 + // 此事件无人监听,我们默认都有权限访问 callback("", nullptr); } } diff --git a/src/Http/HttpFileManager.h b/src/Http/HttpFileManager.h index 330b9fa1..a3c0c1f3 100644 --- a/src/Http/HttpFileManager.h +++ b/src/Http/HttpFileManager.h @@ -62,6 +62,13 @@ public: * @return mime值 */ static const std::string &getContentType(const char *name); + + /** + * 该ip是否再白名单中 + * @param ip 支持ipv4和ipv6 + */ + static bool isIPAllowed(const std::string &ip); + private: HttpFileManager() = delete; ~HttpFileManager() = delete;