2019-08-08 19:01:45 +08:00
|
|
|
|
/*
|
2020-04-04 20:30:09 +08:00
|
|
|
|
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
|
2019-06-11 09:25:54 +08:00
|
|
|
|
*
|
2021-01-17 18:31:50 +08:00
|
|
|
|
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
|
2019-06-11 09:25:54 +08:00
|
|
|
|
*
|
2020-04-04 20:30:09 +08:00
|
|
|
|
* Use of this source code is governed by MIT license that can be found in the
|
|
|
|
|
* LICENSE file in the root of the source tree. All contributing project authors
|
|
|
|
|
* may be found in the AUTHORS file in the root of the source tree.
|
2019-06-11 09:25:54 +08:00
|
|
|
|
*/
|
|
|
|
|
|
2019-05-20 11:22:59 +08:00
|
|
|
|
#include <sstream>
|
|
|
|
|
#include "Util/logger.h"
|
|
|
|
|
#include "Util/onceToken.h"
|
|
|
|
|
#include "Util/NoticeCenter.h"
|
|
|
|
|
#include "Common/config.h"
|
|
|
|
|
#include "Common/MediaSource.h"
|
|
|
|
|
#include "Http/HttpRequester.h"
|
|
|
|
|
#include "Network/TcpSession.h"
|
2019-05-20 16:26:04 +08:00
|
|
|
|
#include "Rtsp/RtspSession.h"
|
2019-06-12 17:53:48 +08:00
|
|
|
|
#include "Http/HttpSession.h"
|
2019-06-24 14:51:49 +08:00
|
|
|
|
#include "WebHook.h"
|
2021-04-08 17:34:13 +08:00
|
|
|
|
#include "WebApi.h"
|
2019-05-20 11:22:59 +08:00
|
|
|
|
|
|
|
|
|
using namespace toolkit;
|
|
|
|
|
using namespace mediakit;
|
|
|
|
|
|
|
|
|
|
namespace Hook {
|
|
|
|
|
#define HOOK_FIELD "hook."
|
|
|
|
|
|
2019-06-24 14:51:49 +08:00
|
|
|
|
const string kEnable = HOOK_FIELD"enable";
|
|
|
|
|
const string kTimeoutSec = HOOK_FIELD"timeoutSec";
|
|
|
|
|
const string kOnPublish = HOOK_FIELD"on_publish";
|
|
|
|
|
const string kOnPlay = HOOK_FIELD"on_play";
|
|
|
|
|
const string kOnFlowReport = HOOK_FIELD"on_flow_report";
|
|
|
|
|
const string kOnRtspRealm = HOOK_FIELD"on_rtsp_realm";
|
|
|
|
|
const string kOnRtspAuth = HOOK_FIELD"on_rtsp_auth";
|
|
|
|
|
const string kOnStreamChanged = HOOK_FIELD"on_stream_changed";
|
|
|
|
|
const string kOnStreamNotFound = HOOK_FIELD"on_stream_not_found";
|
|
|
|
|
const string kOnRecordMp4 = HOOK_FIELD"on_record_mp4";
|
2020-09-13 14:08:05 +08:00
|
|
|
|
const string kOnRecordTs = HOOK_FIELD"on_record_ts";
|
2019-06-24 14:51:49 +08:00
|
|
|
|
const string kOnShellLogin = HOOK_FIELD"on_shell_login";
|
|
|
|
|
const string kOnStreamNoneReader = HOOK_FIELD"on_stream_none_reader";
|
|
|
|
|
const string kOnHttpAccess = HOOK_FIELD"on_http_access";
|
2019-11-29 10:12:20 +08:00
|
|
|
|
const string kOnServerStarted = HOOK_FIELD"on_server_started";
|
2019-06-24 14:51:49 +08:00
|
|
|
|
const string kAdminParams = HOOK_FIELD"admin_params";
|
2019-05-20 11:22:59 +08:00
|
|
|
|
|
|
|
|
|
onceToken token([](){
|
2019-08-16 15:34:34 +08:00
|
|
|
|
mINI::Instance()[kEnable] = false;
|
2019-05-20 11:22:59 +08:00
|
|
|
|
mINI::Instance()[kTimeoutSec] = 10;
|
2020-11-08 09:28:46 +08:00
|
|
|
|
//默认hook地址设置为空,采用默认行为(例如不鉴权)
|
|
|
|
|
mINI::Instance()[kOnPublish] = "";
|
|
|
|
|
mINI::Instance()[kOnPlay] = "";
|
|
|
|
|
mINI::Instance()[kOnFlowReport] = "";
|
|
|
|
|
mINI::Instance()[kOnRtspRealm] = "";
|
|
|
|
|
mINI::Instance()[kOnRtspAuth] = "";
|
|
|
|
|
mINI::Instance()[kOnStreamChanged] = "";
|
|
|
|
|
mINI::Instance()[kOnStreamNotFound] = "";
|
|
|
|
|
mINI::Instance()[kOnRecordMp4] = "";
|
|
|
|
|
mINI::Instance()[kOnRecordTs] = "";
|
|
|
|
|
mINI::Instance()[kOnShellLogin] = "";
|
|
|
|
|
mINI::Instance()[kOnStreamNoneReader] = "";
|
|
|
|
|
mINI::Instance()[kOnHttpAccess] = "";
|
|
|
|
|
mINI::Instance()[kOnServerStarted] = "";
|
2019-05-20 17:12:00 +08:00
|
|
|
|
mINI::Instance()[kAdminParams] = "secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc";
|
2019-05-20 11:22:59 +08:00
|
|
|
|
},nullptr);
|
|
|
|
|
}//namespace Hook
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void parse_http_response(const SockException &ex,
|
|
|
|
|
const string &status,
|
|
|
|
|
const HttpClient::HttpHeader &header,
|
|
|
|
|
const string &strRecvBody,
|
|
|
|
|
const function<void(const Value &,const string &)> &fun){
|
|
|
|
|
if(ex){
|
|
|
|
|
auto errStr = StrPrinter << "[network err]:" << ex.what() << endl;
|
|
|
|
|
fun(Json::nullValue,errStr);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(status != "200"){
|
|
|
|
|
auto errStr = StrPrinter << "[bad http status code]:" << status << endl;
|
|
|
|
|
fun(Json::nullValue,errStr);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
stringstream ss(strRecvBody);
|
|
|
|
|
Value result;
|
|
|
|
|
ss >> result;
|
|
|
|
|
if(result["code"].asInt() != 0) {
|
|
|
|
|
auto errStr = StrPrinter << "[json code]:" << "code=" << result["code"] << ",msg=" << result["msg"] << endl;
|
|
|
|
|
fun(Json::nullValue,errStr);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
fun(result,"");
|
|
|
|
|
}catch (std::exception &ex){
|
|
|
|
|
auto errStr = StrPrinter << "[parse json failed]:" << ex.what() << endl;
|
|
|
|
|
fun(Json::nullValue,errStr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-20 16:26:04 +08:00
|
|
|
|
string to_string(const Value &value){
|
|
|
|
|
return value.toStyledString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string to_string(const HttpArgs &value){
|
|
|
|
|
return value.make();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *getContentType(const Value &value){
|
|
|
|
|
return "application/json";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *getContentType(const HttpArgs &value){
|
|
|
|
|
return "application/x-www-form-urlencoded";
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-22 09:53:31 +08:00
|
|
|
|
string getVhost(const Value &value) {
|
|
|
|
|
const char *key = VHOST_KEY;
|
|
|
|
|
auto val = value.find(key, key + sizeof(VHOST_KEY) - 1);
|
|
|
|
|
return val ? val->asString() : "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string getVhost(const HttpArgs &value) {
|
|
|
|
|
auto val = value.find(VHOST_KEY);
|
|
|
|
|
return val != value.end() ? val->second : "";
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-27 22:14:59 +08:00
|
|
|
|
void do_http_hook(const string &url,const ArgsType &body,const function<void(const Value &,const string &)> &func){
|
|
|
|
|
GET_CONFIG(string, mediaServerId, General::kMediaServerId);
|
|
|
|
|
GET_CONFIG(float, hook_timeoutSec, Hook::kTimeoutSec);
|
2020-10-01 14:37:52 +08:00
|
|
|
|
|
2020-12-27 22:14:59 +08:00
|
|
|
|
const_cast<ArgsType &>(body)["mediaServerId"] = mediaServerId;
|
2019-05-20 11:22:59 +08:00
|
|
|
|
HttpRequester::Ptr requester(new HttpRequester);
|
|
|
|
|
requester->setMethod("POST");
|
2019-05-20 16:26:04 +08:00
|
|
|
|
auto bodyStr = to_string(body);
|
|
|
|
|
requester->setBody(bodyStr);
|
2020-12-27 22:14:59 +08:00
|
|
|
|
requester->addHeader("Content-Type", getContentType(body));
|
2021-05-22 09:53:31 +08:00
|
|
|
|
auto vhost = getVhost(body);
|
|
|
|
|
if (!vhost.empty()) {
|
|
|
|
|
requester->addHeader("X-VHOST", vhost);
|
|
|
|
|
}
|
2019-05-20 11:22:59 +08:00
|
|
|
|
std::shared_ptr<Ticker> pTicker(new Ticker);
|
2020-12-27 22:14:59 +08:00
|
|
|
|
requester->startRequester(url, [url, func, bodyStr, requester, pTicker](const SockException &ex,
|
|
|
|
|
const string &status,
|
|
|
|
|
const HttpClient::HttpHeader &header,
|
2021-03-14 10:29:17 +08:00
|
|
|
|
const string &strRecvBody) mutable{
|
|
|
|
|
onceToken token(nullptr, [&]() mutable{
|
|
|
|
|
requester.reset();
|
2019-05-20 11:22:59 +08:00
|
|
|
|
});
|
|
|
|
|
parse_http_response(ex,status,header,strRecvBody,[&](const Value &obj,const string &err){
|
2020-12-27 22:14:59 +08:00
|
|
|
|
if (func) {
|
|
|
|
|
func(obj, err);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
}
|
2020-12-27 22:14:59 +08:00
|
|
|
|
if (!err.empty()) {
|
|
|
|
|
WarnL << "hook " << url << " " << pTicker->elapsedTime() << "ms,failed" << err << ":" << bodyStr;
|
|
|
|
|
} else if (pTicker->elapsedTime() > 500) {
|
|
|
|
|
DebugL << "hook " << url << " " << pTicker->elapsedTime() << "ms,success:" << bodyStr;
|
2019-05-20 11:22:59 +08:00
|
|
|
|
}
|
|
|
|
|
});
|
2020-12-27 22:14:59 +08:00
|
|
|
|
}, hook_timeoutSec);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-20 16:26:04 +08:00
|
|
|
|
static ArgsType make_json(const MediaInfo &args){
|
|
|
|
|
ArgsType body;
|
2019-05-20 11:22:59 +08:00
|
|
|
|
body["schema"] = args._schema;
|
2021-05-22 09:53:31 +08:00
|
|
|
|
body[VHOST_KEY] = args._vhost;
|
2019-05-20 11:22:59 +08:00
|
|
|
|
body["app"] = args._app;
|
|
|
|
|
body["stream"] = args._streamid;
|
|
|
|
|
body["params"] = args._param_strs;
|
2020-09-21 14:32:56 +08:00
|
|
|
|
return body;
|
2019-05-20 11:22:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-29 10:12:20 +08:00
|
|
|
|
static void reportServerStarted(){
|
|
|
|
|
GET_CONFIG(bool,hook_enable,Hook::kEnable);
|
|
|
|
|
GET_CONFIG(string,hook_server_started,Hook::kOnServerStarted);
|
|
|
|
|
if(!hook_enable || hook_server_started.empty()){
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArgsType body;
|
|
|
|
|
for (auto &pr : mINI::Instance()) {
|
|
|
|
|
body[pr.first] = (string &) pr.second;
|
|
|
|
|
}
|
|
|
|
|
//执行hook
|
|
|
|
|
do_http_hook(hook_server_started,body, nullptr);
|
|
|
|
|
}
|
2019-05-20 11:22:59 +08:00
|
|
|
|
|
|
|
|
|
void installWebHook(){
|
2019-05-28 17:14:36 +08:00
|
|
|
|
GET_CONFIG(bool,hook_enable,Hook::kEnable);
|
|
|
|
|
GET_CONFIG(string,hook_adminparams,Hook::kAdminParams);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
|
2019-05-20 16:26:04 +08:00
|
|
|
|
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaPublish,[](BroadcastMediaPublishArgs){
|
2020-11-08 09:28:46 +08:00
|
|
|
|
GET_CONFIG(string,hook_publish,Hook::kOnPublish);
|
2020-01-07 15:37:48 +08:00
|
|
|
|
GET_CONFIG(bool,toHls,General::kPublishToHls);
|
|
|
|
|
GET_CONFIG(bool,toMP4,General::kPublishToMP4);
|
2019-06-06 15:33:11 +08:00
|
|
|
|
if(!hook_enable || args._param_strs == hook_adminparams || hook_publish.empty() || sender.get_peer_ip() == "127.0.0.1"){
|
2020-09-12 19:09:56 +08:00
|
|
|
|
invoker("", toHls, toMP4);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
//异步执行该hook api,防止阻塞NoticeCenter
|
2019-05-20 16:26:04 +08:00
|
|
|
|
auto body = make_json(args);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
2019-05-20 17:34:39 +08:00
|
|
|
|
//执行hook
|
|
|
|
|
do_http_hook(hook_publish,body,[invoker](const Value &obj,const string &err){
|
2019-09-10 11:06:31 +08:00
|
|
|
|
if(err.empty()){
|
|
|
|
|
//推流鉴权成功
|
2020-01-07 15:38:35 +08:00
|
|
|
|
bool enableHls = toHls;
|
|
|
|
|
bool enableMP4 = toMP4;
|
|
|
|
|
|
2020-09-12 19:09:56 +08:00
|
|
|
|
//兼容用户不传递enableHls、enableMP4参数
|
|
|
|
|
if (obj.isMember("enableHls")) {
|
2020-01-07 15:38:35 +08:00
|
|
|
|
enableHls = obj["enableHls"].asBool();
|
2019-09-10 17:01:42 +08:00
|
|
|
|
}
|
2020-09-12 19:09:56 +08:00
|
|
|
|
if (obj.isMember("enableMP4")) {
|
2020-01-07 15:38:35 +08:00
|
|
|
|
enableMP4 = obj["enableMP4"].asBool();
|
2019-09-10 17:01:42 +08:00
|
|
|
|
}
|
2020-09-12 19:09:56 +08:00
|
|
|
|
invoker(err, enableHls, enableMP4);
|
|
|
|
|
} else {
|
2019-09-10 11:06:31 +08:00
|
|
|
|
//推流鉴权失败
|
2020-09-12 19:09:56 +08:00
|
|
|
|
invoker(err, false, false);
|
2019-09-10 11:06:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-20 11:22:59 +08:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaPlayed,[](BroadcastMediaPlayedArgs){
|
2020-11-08 09:28:46 +08:00
|
|
|
|
GET_CONFIG(string,hook_play,Hook::kOnPlay);
|
2019-06-06 15:33:11 +08:00
|
|
|
|
if(!hook_enable || args._param_strs == hook_adminparams || hook_play.empty() || sender.get_peer_ip() == "127.0.0.1"){
|
2019-05-20 11:22:59 +08:00
|
|
|
|
invoker("");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-20 16:26:04 +08:00
|
|
|
|
auto body = make_json(args);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
2019-05-20 17:34:39 +08:00
|
|
|
|
//执行hook
|
|
|
|
|
do_http_hook(hook_play,body,[invoker](const Value &obj,const string &err){
|
|
|
|
|
invoker(err);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastFlowReport,[](BroadcastFlowReportArgs){
|
2020-11-08 09:28:46 +08:00
|
|
|
|
GET_CONFIG(string,hook_flowreport,Hook::kOnFlowReport);
|
2020-04-23 22:04:59 +08:00
|
|
|
|
if(!hook_enable || args._param_strs == hook_adminparams || hook_flowreport.empty() || sender.get_peer_ip() == "127.0.0.1"){
|
2019-05-20 11:22:59 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-20 16:26:04 +08:00
|
|
|
|
auto body = make_json(args);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
body["totalBytes"] = (Json::UInt64)totalBytes;
|
|
|
|
|
body["duration"] = (Json::UInt64)totalDuration;
|
2019-05-20 17:46:06 +08:00
|
|
|
|
body["player"] = isPlayer;
|
2020-04-23 22:04:59 +08:00
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
2019-05-20 17:34:39 +08:00
|
|
|
|
//执行hook
|
|
|
|
|
do_http_hook(hook_flowreport,body, nullptr);
|
2019-05-20 11:22:59 +08:00
|
|
|
|
});
|
2019-05-20 16:26:04 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const string unAuthedRealm = "unAuthedRealm";
|
|
|
|
|
|
|
|
|
|
//监听kBroadcastOnGetRtspRealm事件决定rtsp链接是否需要鉴权(传统的rtsp鉴权方案)才能访问
|
|
|
|
|
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastOnGetRtspRealm,[](BroadcastOnGetRtspRealmArgs){
|
2020-11-08 09:28:46 +08:00
|
|
|
|
GET_CONFIG(string,hook_rtsp_realm,Hook::kOnRtspRealm);
|
2019-06-06 15:33:11 +08:00
|
|
|
|
if(!hook_enable || args._param_strs == hook_adminparams || hook_rtsp_realm.empty() || sender.get_peer_ip() == "127.0.0.1"){
|
2019-05-20 16:26:04 +08:00
|
|
|
|
//无需认证
|
|
|
|
|
invoker("");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto body = make_json(args);
|
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
2019-05-20 17:34:39 +08:00
|
|
|
|
//执行hook
|
|
|
|
|
do_http_hook(hook_rtsp_realm,body, [invoker](const Value &obj,const string &err){
|
|
|
|
|
if(!err.empty()){
|
|
|
|
|
//如果接口访问失败,那么该rtsp流认证失败
|
|
|
|
|
invoker(unAuthedRealm);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
invoker(obj["realm"].asString());
|
2019-05-20 16:26:04 +08:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//监听kBroadcastOnRtspAuth事件返回正确的rtsp鉴权用户密码
|
|
|
|
|
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastOnRtspAuth,[](BroadcastOnRtspAuthArgs){
|
2020-11-08 09:28:46 +08:00
|
|
|
|
GET_CONFIG(string,hook_rtsp_auth,Hook::kOnRtspAuth);
|
2019-05-20 16:26:04 +08:00
|
|
|
|
if(unAuthedRealm == realm || !hook_enable || hook_rtsp_auth.empty()){
|
|
|
|
|
//认证失败
|
|
|
|
|
invoker(false,makeRandStr(12));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto body = make_json(args);
|
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
|
|
|
|
body["user_name"] = user_name;
|
|
|
|
|
body["must_no_encrypt"] = must_no_encrypt;
|
|
|
|
|
body["realm"] = realm;
|
2019-05-20 17:34:39 +08:00
|
|
|
|
//执行hook
|
|
|
|
|
do_http_hook(hook_rtsp_auth,body, [invoker](const Value &obj,const string &err){
|
|
|
|
|
if(!err.empty()){
|
|
|
|
|
//认证失败
|
|
|
|
|
invoker(false,makeRandStr(12));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
invoker(obj["encrypted"].asBool(),obj["passwd"].asString());
|
2019-05-20 16:26:04 +08:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//监听rtsp、rtmp源注册或注销事件
|
|
|
|
|
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaChanged,[](BroadcastMediaChangedArgs){
|
2020-11-08 09:28:46 +08:00
|
|
|
|
GET_CONFIG(string,hook_stream_chaned,Hook::kOnStreamChanged);
|
2019-05-20 16:26:04 +08:00
|
|
|
|
if(!hook_enable || hook_stream_chaned.empty()){
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ArgsType body;
|
2021-04-08 17:34:13 +08:00
|
|
|
|
if (bRegist) {
|
|
|
|
|
body = makeMediaSourceJson(sender);
|
2021-04-11 11:39:49 +08:00
|
|
|
|
body["regist"] = bRegist;
|
2021-04-08 17:34:13 +08:00
|
|
|
|
} else {
|
|
|
|
|
body["schema"] = sender.getSchema();
|
2021-05-22 09:53:31 +08:00
|
|
|
|
body[VHOST_KEY] = sender.getVhost();
|
2021-04-08 17:34:13 +08:00
|
|
|
|
body["app"] = sender.getApp();
|
|
|
|
|
body["stream"] = sender.getId();
|
2021-04-11 11:39:49 +08:00
|
|
|
|
body["regist"] = bRegist;
|
2021-04-08 17:34:13 +08:00
|
|
|
|
}
|
2019-05-20 17:34:39 +08:00
|
|
|
|
//执行hook
|
|
|
|
|
do_http_hook(hook_stream_chaned,body, nullptr);
|
2019-05-20 16:26:04 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//监听播放失败(未找到特定的流)事件
|
|
|
|
|
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastNotFoundStream,[](BroadcastNotFoundStreamArgs){
|
2020-11-08 09:28:46 +08:00
|
|
|
|
GET_CONFIG(string,hook_stream_not_found,Hook::kOnStreamNotFound);
|
2019-05-20 16:26:04 +08:00
|
|
|
|
if(!hook_enable || hook_stream_not_found.empty()){
|
2020-11-08 09:28:46 +08:00
|
|
|
|
//如果确定这个流不存在,可以closePlayer()返回播放器流不存在
|
|
|
|
|
//closePlayer();
|
2019-05-20 16:26:04 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto body = make_json(args);
|
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
2019-05-20 17:34:39 +08:00
|
|
|
|
//执行hook
|
|
|
|
|
do_http_hook(hook_stream_not_found,body, nullptr);
|
2019-05-20 16:26:04 +08:00
|
|
|
|
});
|
|
|
|
|
|
2020-09-20 11:40:42 +08:00
|
|
|
|
static auto getRecordInfo = [](const RecordInfo &info) {
|
|
|
|
|
ArgsType body;
|
|
|
|
|
body["start_time"] = (Json::UInt64) info.start_time;
|
|
|
|
|
body["file_size"] = (Json::UInt64) info.file_size;
|
|
|
|
|
body["time_len"] = info.time_len;
|
|
|
|
|
body["file_path"] = info.file_path;
|
|
|
|
|
body["file_name"] = info.file_name;
|
|
|
|
|
body["folder"] = info.folder;
|
|
|
|
|
body["url"] = info.url;
|
|
|
|
|
body["app"] = info.app;
|
|
|
|
|
body["stream"] = info.stream;
|
2021-05-22 09:53:31 +08:00
|
|
|
|
body[VHOST_KEY] = info.vhost;
|
2020-09-20 11:40:42 +08:00
|
|
|
|
return body;
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-03 20:45:58 +08:00
|
|
|
|
#ifdef ENABLE_MP4
|
2019-05-20 16:26:04 +08:00
|
|
|
|
//录制mp4文件成功后广播
|
|
|
|
|
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastRecordMP4,[](BroadcastRecordMP4Args){
|
2020-11-08 09:28:46 +08:00
|
|
|
|
GET_CONFIG(string,hook_record_mp4,Hook::kOnRecordMp4);
|
2020-09-20 11:40:42 +08:00
|
|
|
|
if (!hook_enable || hook_record_mp4.empty()) {
|
2019-05-20 16:26:04 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-20 17:34:39 +08:00
|
|
|
|
//执行hook
|
2020-09-20 11:40:42 +08:00
|
|
|
|
do_http_hook(hook_record_mp4, getRecordInfo(info), nullptr);
|
2019-05-20 16:26:04 +08:00
|
|
|
|
});
|
2020-04-03 20:45:58 +08:00
|
|
|
|
#endif //ENABLE_MP4
|
2019-05-20 16:26:04 +08:00
|
|
|
|
|
2020-09-13 14:08:05 +08:00
|
|
|
|
NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastRecordTs, [](BroadcastRecordTsArgs) {
|
2020-11-08 09:28:46 +08:00
|
|
|
|
GET_CONFIG(string,hook_record_ts,Hook::kOnRecordTs);
|
2020-09-13 14:08:05 +08:00
|
|
|
|
if (!hook_enable || hook_record_ts.empty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 执行 hook
|
2020-09-20 11:40:42 +08:00
|
|
|
|
do_http_hook(hook_record_ts, getRecordInfo(info), nullptr);
|
2020-09-13 14:08:05 +08:00
|
|
|
|
});
|
|
|
|
|
|
2019-05-27 13:56:37 +08:00
|
|
|
|
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastShellLogin,[](BroadcastShellLoginArgs){
|
2020-11-08 09:28:46 +08:00
|
|
|
|
GET_CONFIG(string,hook_shell_login,Hook::kOnShellLogin);
|
2019-06-06 15:33:11 +08:00
|
|
|
|
if(!hook_enable || hook_shell_login.empty() || sender.get_peer_ip() == "127.0.0.1"){
|
|
|
|
|
invoker("");
|
2019-05-27 13:56:37 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ArgsType body;
|
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
|
|
|
|
body["user_name"] = user_name;
|
|
|
|
|
body["passwd"] = passwd;
|
|
|
|
|
|
|
|
|
|
//执行hook
|
|
|
|
|
do_http_hook(hook_shell_login,body, [invoker](const Value &,const string &err){
|
|
|
|
|
invoker(err);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2019-05-27 18:39:43 +08:00
|
|
|
|
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastStreamNoneReader,[](BroadcastStreamNoneReaderArgs){
|
2020-11-08 09:28:46 +08:00
|
|
|
|
GET_CONFIG(string,hook_stream_none_reader,Hook::kOnStreamNoneReader);
|
2019-05-27 18:39:43 +08:00
|
|
|
|
if(!hook_enable || hook_stream_none_reader.empty()){
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArgsType body;
|
|
|
|
|
body["schema"] = sender.getSchema();
|
2021-05-22 09:53:31 +08:00
|
|
|
|
body[VHOST_KEY] = sender.getVhost();
|
2019-05-27 18:39:43 +08:00
|
|
|
|
body["app"] = sender.getApp();
|
|
|
|
|
body["stream"] = sender.getId();
|
|
|
|
|
weak_ptr<MediaSource> weakSrc = sender.shared_from_this();
|
|
|
|
|
//执行hook
|
|
|
|
|
do_http_hook(hook_stream_none_reader,body, [weakSrc](const Value &obj,const string &err){
|
|
|
|
|
bool flag = obj["close"].asBool();
|
|
|
|
|
auto strongSrc = weakSrc.lock();
|
|
|
|
|
if(!flag || !err.empty() || !strongSrc){
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
strongSrc->close(false);
|
2021-08-12 10:54:57 +08:00
|
|
|
|
WarnL << "无人观看主动关闭流:"
|
|
|
|
|
<< strongSrc->getSchema() << "/"
|
|
|
|
|
<< strongSrc->getVhost() << "/"
|
|
|
|
|
<< strongSrc->getApp() << "/"
|
|
|
|
|
<< strongSrc->getId();
|
2019-05-27 18:39:43 +08:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2019-06-14 15:19:02 +08:00
|
|
|
|
/**
|
|
|
|
|
* kBroadcastHttpAccess事件触发机制
|
|
|
|
|
* 1、根据http请求头查找cookie,找到进入步骤3
|
2019-11-30 11:44:05 +08:00
|
|
|
|
* 2、根据http url参数查找cookie,如果还是未找到cookie则进入步骤5
|
2019-06-14 15:19:02 +08:00
|
|
|
|
* 3、cookie标记是否有权限访问文件,如果有权限,直接返回文件
|
|
|
|
|
* 4、cookie中记录的url参数是否跟本次url参数一致,如果一致直接返回客户端错误码
|
|
|
|
|
* 5、触发kBroadcastHttpAccess事件
|
|
|
|
|
*/
|
2019-06-13 13:02:30 +08:00
|
|
|
|
//开发者应该通过该事件判定http客户端是否有权限访问http服务器上的特定文件
|
2019-06-14 15:19:02 +08:00
|
|
|
|
//ZLMediaKit会记录本次鉴权的结果至cookie
|
|
|
|
|
//如果鉴权成功,在cookie有效期内,那么下次客户端再访问授权目录时,ZLMediaKit会直接返回文件
|
|
|
|
|
//如果鉴权失败,在cookie有效期内,如果http url参数不变(否则会立即再次触发鉴权事件),ZLMediaKit会直接返回错误码
|
|
|
|
|
//如果用户客户端不支持cookie,那么ZLMediaKit会根据url参数查找cookie并追踪用户,
|
|
|
|
|
//如果没有url参数,客户端又不支持cookie,那么会根据ip和端口追踪用户
|
|
|
|
|
//追踪用户的目的是为了缓存上次鉴权结果,减少鉴权次数,提高性能
|
2019-06-12 17:53:48 +08:00
|
|
|
|
NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastHttpAccess,[](BroadcastHttpAccessArgs){
|
2020-11-08 09:28:46 +08:00
|
|
|
|
GET_CONFIG(string,hook_http_access,Hook::kOnHttpAccess);
|
2020-01-09 17:03:29 +08:00
|
|
|
|
if(sender.get_peer_ip() == "127.0.0.1" || parser.Params() == hook_adminparams){
|
2019-06-13 13:02:30 +08:00
|
|
|
|
//如果是本机或超级管理员访问,那么不做访问鉴权;权限有效期1个小时
|
2019-06-14 18:42:09 +08:00
|
|
|
|
invoker("","",60 * 60);
|
2019-06-13 13:02:30 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(!hook_enable || hook_http_access.empty()){
|
|
|
|
|
//未开启http文件访问鉴权,那么允许访问,但是每次访问都要鉴权;
|
|
|
|
|
//因为后续随时都可能开启鉴权(重载配置文件后可能重新开启鉴权)
|
2019-06-14 18:42:09 +08:00
|
|
|
|
invoker("","",0);
|
2019-06-12 17:53:48 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ArgsType body;
|
|
|
|
|
body["ip"] = sender.get_peer_ip();
|
|
|
|
|
body["port"] = sender.get_peer_port();
|
|
|
|
|
body["id"] = sender.getIdentifier();
|
|
|
|
|
body["path"] = path;
|
|
|
|
|
body["is_dir"] = is_dir;
|
|
|
|
|
body["params"] = parser.Params();
|
2020-04-20 18:13:45 +08:00
|
|
|
|
for(auto &pr : parser.getHeader()){
|
2019-06-13 14:29:25 +08:00
|
|
|
|
body[string("header.") + pr.first] = pr.second;
|
2019-06-12 17:53:48 +08:00
|
|
|
|
}
|
|
|
|
|
//执行hook
|
|
|
|
|
do_http_hook(hook_http_access,body, [invoker](const Value &obj,const string &err){
|
|
|
|
|
if(!err.empty()){
|
2019-06-13 09:24:53 +08:00
|
|
|
|
//如果接口访问失败,那么仅限本次没有访问http服务器的权限
|
2019-06-14 18:42:09 +08:00
|
|
|
|
invoker(err,"",0);
|
2019-06-12 17:53:48 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2019-06-14 18:42:09 +08:00
|
|
|
|
//err参数代表不能访问的原因,空则代表可以访问
|
|
|
|
|
//path参数是该客户端能访问或被禁止的顶端目录,如果path为空字符串,则表述为当前目录
|
|
|
|
|
//second参数规定该cookie超时时间,如果second为0,本次鉴权结果不缓存
|
|
|
|
|
invoker(obj["err"].asString(),obj["path"].asString(),obj["second"].asInt());
|
2019-06-12 17:53:48 +08:00
|
|
|
|
});
|
|
|
|
|
});
|
2019-11-29 10:12:20 +08:00
|
|
|
|
|
|
|
|
|
//汇报服务器重新启动
|
|
|
|
|
reportServerStarted();
|
2019-05-20 17:03:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void unInstallWebHook(){
|
2019-05-20 16:26:04 +08:00
|
|
|
|
|
2019-06-13 18:39:57 +08:00
|
|
|
|
}
|