Allow HTTP connectors to be reused without full restart, better X-Real-IP/X-Forwarded-For handling

This commit is contained in:
Thulinma 2021-10-02 22:21:58 +02:00
parent 1c539fbcd9
commit 9451ab5f11
3 changed files with 56 additions and 42 deletions

View file

@ -2368,6 +2368,8 @@ namespace Mist{
return capa["name"].asStringRef();
}
/// Writes data to statConn once per second, or more often if force==true.
/// Also handles push status updates
void Output::stats(bool force){
// cancel stats update if not initialized
if (!isInitialized){return;}

View file

@ -22,7 +22,9 @@ namespace Mist{
//General
idleInterval = 0;
idleLast = 0;
if (config->getString("ip").size()){myConn.setHost(config->getString("ip"));}
if (config->getString("ip").size()){
myConn.setHost(config->getString("ip"));
}
if (config->getString("prequest").size()){
myConn.Received().prepend(config->getString("prequest"));
}
@ -47,11 +49,11 @@ namespace Mist{
capa["forward"]["ip"]["help"] = "IP of forwarded connection.";
capa["forward"]["ip"]["type"] = "str";
capa["forward"]["ip"]["option"] = "--ip";
capa["forward"]["ip"]["name"] = "Previous request";
capa["forward"]["ip"]["help"] =
capa["forward"]["prequest"]["name"] = "Previous request";
capa["forward"]["prequest"]["help"] =
"Data to pretend arrived on the socket before parsing the socket.";
capa["forward"]["ip"]["type"] = "str";
capa["forward"]["ip"]["option"] = "--prequest";
capa["forward"]["prequest"]["type"] = "str";
capa["forward"]["prequest"]["option"] = "--prequest";
cfg->addOption("streamname", JSON::fromString("{\"arg\":\"string\",\"short\":\"s\",\"long\":"
"\"stream\",\"help\":\"The name of the stream "
"that this connector will transmit.\"}"));
@ -277,12 +279,15 @@ namespace Mist{
bool sawRequest = false;
while (H.Read(myConn)){
sawRequest = true;
//First, figure out which handler we need to use
std::string handler = getHandler();
if (handler != capa["name"].asStringRef() || H.GetVar("stream") != streamName){
INFO_MSG("Received request: %s => %s (%s)", H.getUrl().c_str(), handler.c_str(), H.GetVar("stream").c_str());
}else{
MEDIUM_MSG("Received request: %s => %s (%s)", H.getUrl().c_str(), handler.c_str(), H.GetVar("stream").c_str());
}
//None found? Abort the request with a 415 response status code.
if (!handler.size()){
H.Clean();
H.SetHeader("Server", APPIDENT);
@ -295,6 +300,32 @@ namespace Mist{
return;
}
// Check the real and/or forwarded IP address if the proxy is trusted
// Warns if proxy is not trusted and these headers are set
fwdHostStr.clear();
fwdHostBin.clear();
if (H.hasHeader("X-Real-IP") || H.hasHeader("X-Forwarded-For")){
if (H.hasHeader("X-Real-IP")){
fwdHostStr = H.GetHeader("X-Real-IP");
}
if (H.hasHeader("X-Forwarded-For")){
fwdHostStr = H.GetHeader("X-Forwarded-For");
if (fwdHostStr.find(',') != std::string::npos){fwdHostStr.erase(fwdHostStr.find(','));}
}
std::string trueHostStr = Output::getConnectedHost();
if (!fwdHostStr.size() || !isTrustedProxy(trueHostStr)){
if (fwdHostStr.size() && fwdHostStr != trueHostStr){
WARN_MSG("Host %s is attempting to act as a proxy for %s, but not trusted", trueHostStr.c_str(), fwdHostStr.c_str());
}
fwdHostStr.clear();
}else{
Socket::Connection tmp;
tmp.setHost(fwdHostStr);
fwdHostBin = tmp.getBinHost();
}
}
tkn.clear();
// Read the session token
if (Comms::tknMode & 0x01){
@ -327,13 +358,16 @@ namespace Mist{
if (handler != capa["name"].asStringRef() || H.GetVar("stream") != streamName || (statComm && (statComm.getHost() != getConnectedBinHost() || statComm.getTkn() != tkn))){
MEDIUM_MSG("Switching from %s (%s) to %s (%s)", capa["name"].asStringRef().c_str(),
streamName.c_str(), handler.c_str(), H.GetVar("stream").c_str());
//Prepare switch
disconnect();
streamName = H.GetVar("stream");
userSelect.clear();
if (statComm){statComm.setStatus(COMM_STATUS_DISCONNECT | statComm.getStatus());}
if (handler != capa["name"].asStringRef()){
reConnector(handler);
onFail("Server error - could not start connector", true);
return;
}
}
/*LTS-START*/
{
@ -408,6 +442,7 @@ namespace Mist{
preHTTP();
if (!myConn){return;}
onHTTP();
stats(true);
idleLast = Util::bootMS();
// Prevent the clean as well as the loop when we're in the middle of handling a request now
if (!wantRequest){return;}
@ -930,10 +965,10 @@ namespace Mist{
if (H.url.size()){tmpPrequest = H.BuildRequest();}
int argnum = 0;
argarr[argnum++] = (char *)tmparg.c_str();
std::string temphost = getConnectedHost();
std::string debuglevel = JSON::Value(Util::printDebugLevel).asString();
std::string trueHostStr = Output::getConnectedHost();
argarr[argnum++] = (char *)"--ip";
argarr[argnum++] = (char *)(temphost.c_str());
argarr[argnum++] = (char *)(trueHostStr.c_str());
argarr[argnum++] = (char *)"--stream";
argarr[argnum++] = (char *)(streamName.c_str());
argarr[argnum++] = (char *)"--prequest";
@ -950,40 +985,16 @@ namespace Mist{
execv(argarr[0], argarr);
}
/*LTS-START*/
std::string HTTPOutput::getConnectedHost(){
std::string host = Output::getConnectedHost();
std::string xRealIp = H.GetHeader("X-Real-IP");
if (fwdHostStr.size()){return fwdHostStr;}
return Output::getConnectedHost();
}
if (!xRealIp.size() || !isTrustedProxy(host)){
static bool msg = false;
if (xRealIp.size() && !msg && xRealIp != host){
WARN_MSG("Host %s is attempting to act as a proxy, but not trusted", host.c_str());
msg = true;
}
return host;
}
return xRealIp;
}
std::string HTTPOutput::getConnectedBinHost(){
// Do first check with connected host because of simplicity
std::string host = Output::getConnectedHost();
std::string xRealIp = H.GetHeader("X-Real-IP");
if (!xRealIp.size() || !isTrustedProxy(host)){
static bool msg = false;
if (xRealIp.size() && !msg && xRealIp != host){
WARN_MSG("Host %s is attempting to act as a proxy, but not trusted", host.c_str());
msg = true;
}
if (fwdHostBin.size()){return fwdHostBin;}
return Output::getConnectedBinHost();
}
Socket::Connection binConn;
binConn.setHost(xRealIp);
return binConn.getBinHost();
}
bool HTTPOutput::isTrustedProxy(const std::string &ip){
static std::set<std::string> trustedProxies;
if (!trustedProxies.size()){
@ -1009,7 +1020,6 @@ namespace Mist{
}
return false;
}
/*LTS-END*/
/// Parses a "Range: " header, setting byteStart and byteEnd.
/// Assumes byteStart and byteEnd are initialized to their minimum respectively maximum values

View file

@ -42,6 +42,8 @@ namespace Mist{
size_t prevVidTrack; ///< Previously selected main video track
bool stayLive; ///< Whether or not we're trying to stay on the live-most point, for live streams
std::string fwdHostStr; ///< Forwarded string IP, if non-empty
std::string fwdHostBin; ///< Forwarded binary IP, if non-empty
bool responded;
HTTP::Parser H;
HTTP::Websocket *webSock;