extern "C" RENDERDOC_API TargetControl *RENDERDOC_CC RENDERDOC_CreateTargetControl( const char *host, uint32_t ident, const char *clientName, bool32 forceConnection) { string s = "localhost"; if(host != NULL && host[0] != '\0') s = host; bool android = false; if(host != NULL && !strncmp(host, "adb:", 4)) { android = true; s = "127.0.0.1"; // could parse out an (optional) device name from host+4 here. } Network::Socket *sock = Network::CreateClientSocket(s.c_str(), ident & 0xffff, 750); if(sock == NULL) return NULL; bool localhost = !android && (Network::GetIPOctet(sock->GetRemoteIP(), 0) == 127); TargetControl *remote = new TargetControl(sock, clientName, forceConnection != 0, localhost); if(remote->Connected()) return remote; delete remote; return NULL; }
void RenderDoc::BecomeReplayHost(volatile bool32 &killReplay) { Network::Socket *sock = Network::CreateServerSocket("0.0.0.0", RenderDoc_ReplayNetworkPort, 1); if(sock == NULL) return; Serialiser ser("", Serialiser::WRITING, false); bool newlyReady = true; while(!killReplay) { if(newlyReady) { RDCLOG("Replay host ready for requests."); newlyReady = false; } Network::Socket *client = sock->AcceptClient(false); if(client == NULL) { if(!sock->Connected()) { RDCERR("Error in accept - shutting down server"); SAFE_DELETE(sock); return; } Threading::Sleep(5); continue; } newlyReady = true; RDCLOG("Connection received."); map<RDCDriver,string> drivers = RenderDoc::Inst().GetRemoteDrivers(); uint32_t count = (uint32_t)drivers.size(); ser.Serialise("", count); for(auto it=drivers.begin(); it != drivers.end(); ++it) { RDCDriver driver = it->first; ser.Serialise("", driver); ser.Serialise("", (*it).second); } if(!SendPacket(client, ePacket_RemoteDriverList, ser)) { RDCERR("Network error sending supported driver list"); SAFE_DELETE(client); continue; } Threading::Sleep(4); // don't care about the result, just want to check that the socket hasn't been gracefully shut down client->IsRecvDataWaiting(); if(!client->Connected()) { RDCLOG("Connection closed after sending remote driver list"); SAFE_DELETE(client); continue; } string cap_file; string dummy, dummy2; FileIO::GetDefaultFiles("remotecopy", cap_file, dummy, dummy2); Serialiser *fileRecv = NULL; if(!RecvChunkedFile(client, ePacket_CopyCapture, cap_file.c_str(), fileRecv, NULL)) { FileIO::Delete(cap_file.c_str()); RDCERR("Network error receiving file"); SAFE_DELETE(fileRecv); SAFE_DELETE(client); continue; } RDCLOG("File received."); SAFE_DELETE(fileRecv); RDCDriver driverType = RDC_Unknown; string driverName = ""; RenderDoc::Inst().FillInitParams(cap_file.c_str(), driverType, driverName, NULL); if(RenderDoc::Inst().HasRemoteDriver(driverType)) { ProgressLoopData data; data.sock = client; data.killsignal = false; data.progress = 0.0f; RenderDoc::Inst().SetProgressPtr(&data.progress); Threading::ThreadHandle ticker = Threading::CreateThread(ProgressTicker, &data); IRemoteDriver *driver = NULL; auto status = RenderDoc::Inst().CreateRemoteDriver(driverType, cap_file.c_str(), &driver); if(status != eReplayCreate_Success || driver == NULL) { RDCERR("Failed to create remote driver for driver type %d name %s", driverType, driverName.c_str()); SAFE_DELETE(client); continue; } driver->ReadLogInitialisation(); RenderDoc::Inst().SetProgressPtr(NULL); data.killsignal = true; Threading::JoinThread(ticker); Threading::CloseThread(ticker); FileIO::Delete(cap_file.c_str()); SendPacket(client, ePacket_LogReady); ProxySerialiser *proxy = new ProxySerialiser(client, driver); while(client) { if(!proxy->Tick() || killReplay) { SAFE_DELETE(client); } } driver->Shutdown(); RDCLOG("Closing replay connection"); SAFE_DELETE(proxy); SAFE_DELETE(client); } else { RDCERR("File needs driver for %s which isn't supported!", driverName.c_str()); FileIO::Delete(cap_file.c_str()); } SAFE_DELETE(client); } }
void RenderDoc::RemoteAccessClientThread(void *s) { Threading::KeepModuleAlive(); Network::Socket *client = (Network::Socket *)s; Serialiser ser("", Serialiser::WRITING, false); string api = ""; RDCDriver driver; RenderDoc::Inst().GetCurrentDriver(driver, api); ser.Rewind(); string target = RenderDoc::Inst().GetCurrentTarget(); ser.Serialise("", target); ser.Serialise("", api); uint32_t mypid = Process::GetCurrentPID(); ser.Serialise("", mypid); if(!SendPacket(client, ePacket_Handshake, ser)) { SAFE_DELETE(client); { SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock); RenderDoc::Inst().m_SingleClientName = ""; } Threading::ReleaseModuleExitThread(); return; } const int pingtime = 1000; // ping every 1000ms const int ticktime = 10; // tick every 10ms int curtime = 0; vector<CaptureData> captures; vector<pair<uint32_t, uint32_t> > children; while(client) { if(RenderDoc::Inst().m_RemoteClientThreadShutdown || (client && !client->Connected())) { SAFE_DELETE(client); break; } ser.Rewind(); Threading::Sleep(ticktime); curtime += ticktime; PacketType packetType = ePacket_Noop; string curapi; RenderDoc::Inst().GetCurrentDriver(driver, curapi); vector<CaptureData> caps = RenderDoc::Inst().GetCaptures(); vector<pair<uint32_t, uint32_t> > childprocs = RenderDoc::Inst().GetChildProcesses(); if(curapi != api) { api = curapi; ser.Serialise("", api); packetType = ePacket_RegisterAPI; } else if(caps.size() != captures.size()) { uint32_t idx = (uint32_t)captures.size(); captures.push_back(caps[idx]); packetType = ePacket_NewCapture; std::string path = FileIO::GetFullPathname(captures.back().path); ser.Serialise("", idx); ser.Serialise("", captures.back().timestamp); ser.Serialise("", path); uint32_t len = 0; RENDERDOC_GetThumbnail(captures.back().path.c_str(), NULL, len); byte *thumb = new byte[len]; RENDERDOC_GetThumbnail(captures.back().path.c_str(), thumb, len); size_t l = len; ser.Serialise("", len); ser.SerialiseBuffer("", thumb, l); delete[] thumb; } else if(childprocs.size() != children.size()) { uint32_t idx = (uint32_t)children.size(); children.push_back(childprocs[idx]); packetType = ePacket_NewChild; ser.Serialise("", children.back().first); ser.Serialise("", children.back().second); } if(curtime < pingtime && packetType == ePacket_Noop) { if(client->IsRecvDataWaiting()) { PacketType type; Serialiser *recvser = NULL; if(!RecvPacket(client, type, &recvser)) SAFE_DELETE(client); if(client == NULL) { SAFE_DELETE(recvser); continue; } else if(type == ePacket_TriggerCapture) { RenderDoc::Inst().TriggerCapture(); } else if(type == ePacket_QueueCapture) { uint32_t frameNum = 0; recvser->Serialise("", frameNum); RenderDoc::Inst().QueueCapture(frameNum); } else if(type == ePacket_CopyCapture) { caps = RenderDoc::Inst().GetCaptures(); uint32_t id = 0; recvser->Serialise("", id); if(id < caps.size()) { ser.Serialise("", id); if(!SendPacket(client, ePacket_CopyCapture, ser)) { SAFE_DELETE(client); continue; } ser.Rewind(); if(!SendChunkedFile(client, ePacket_CopyCapture, caps[id].path.c_str(), ser, NULL)) { SAFE_DELETE(client); continue; } RenderDoc::Inst().MarkCaptureRetrieved(id); } } SAFE_DELETE(recvser); } continue; } curtime = 0; if(!SendPacket(client, packetType, ser)) { SAFE_DELETE(client); continue; } } // give up our connection { SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock); RenderDoc::Inst().m_SingleClientName = ""; } Threading::ReleaseModuleExitThread(); }
void RenderDoc::RemoteAccessServerThread(void *s) { Threading::KeepModuleAlive(); Network::Socket *sock = (Network::Socket *)s; RenderDoc::Inst().m_SingleClientName = ""; Threading::ThreadHandle clientThread = 0; RenderDoc::Inst().m_RemoteClientThreadShutdown = false; while(!RenderDoc::Inst().m_RemoteServerThreadShutdown) { Network::Socket *client = sock->AcceptClient(false); if(client == NULL) { if(!sock->Connected()) { RDCERR("Error in accept - shutting down server"); SAFE_DELETE(sock); Threading::ReleaseModuleExitThread(); return; } Threading::Sleep(5); continue; } string existingClient; string newClient; bool kick = false; // receive handshake from client and get its name { PacketType type; Serialiser *ser = NULL; if(!RecvPacket(client, type, &ser)) { SAFE_DELETE(ser); SAFE_DELETE(client); continue; } if(type != ePacket_Handshake) { SAFE_DELETE(ser); SAFE_DELETE(client); continue; } ser->SerialiseString("", newClient); ser->Serialise("", kick); SAFE_DELETE(ser); if(newClient.empty()) { SAFE_DELETE(client); continue; } } // see if we have a client { SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock); existingClient = RenderDoc::Inst().m_SingleClientName; } if(!existingClient.empty() && kick) { // forcibly close communication thread which will kill the connection RenderDoc::Inst().m_RemoteClientThreadShutdown = true; Threading::JoinThread(clientThread); Threading::CloseThread(clientThread); clientThread = 0; RenderDoc::Inst().m_RemoteClientThreadShutdown = false; existingClient = ""; } if(existingClient.empty()) { SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock); RenderDoc::Inst().m_SingleClientName = newClient; } // if we've claimed client status, spawn a thread to communicate if(existingClient.empty() || kick) { clientThread = Threading::CreateThread(RemoteAccessClientThread, client); continue; } else { // if we've been asked to kick the existing connection off // reject this connection and tell them who is busy Serialiser ser("", Serialiser::WRITING, false); string api = ""; RDCDriver driver; RenderDoc::Inst().GetCurrentDriver(driver, api); string target = RenderDoc::Inst().GetCurrentTarget(); ser.Serialise("", target); ser.Serialise("", api); ser.SerialiseString("", RenderDoc::Inst().m_SingleClientName); // don't care about errors, we're going to close the connection either way SendPacket(client, ePacket_Busy, ser); SAFE_DELETE(client); } } RenderDoc::Inst().m_RemoteClientThreadShutdown = true; // don't join, just close the thread, as we can't wait while in the middle of module unloading Threading::CloseThread(clientThread); clientThread = 0; Threading::ReleaseModuleExitThread(); }
void RenderDoc::BecomeRemoteServer(const char *listenhost, uint16_t port, volatile bool32 &killReplay) { Network::Socket *sock = Network::CreateServerSocket(listenhost, port, 1); if(sock == NULL) return; bool newlyReady = true; std::vector<std::pair<uint32_t, uint32_t> > listenRanges; bool allowExecution = true; FILE *f = FileIO::fopen(FileIO::GetAppFolderFilename("remoteserver.conf").c_str(), "r"); while(f && !FileIO::feof(f)) { string line = trim(FileIO::getline(f)); if(line == "") continue; // skip comments if(line[0] == '#') continue; if(line.substr(0, sizeof("whitelist") - 1) == "whitelist") { uint32_t ip = 0, mask = 0; // CIDR notation bool found = Network::ParseIPRangeCIDR(line.c_str() + sizeof("whitelist"), ip, mask); if(found) { listenRanges.push_back(std::make_pair(ip, mask)); continue; } else { RDCLOG("Couldn't parse IP range from: %s", line.c_str() + sizeof("whitelist")); } continue; } else if(line.substr(0, sizeof("noexec") - 1) == "noexec") { allowExecution = false; continue; } RDCLOG("Malformed line '%s'. See documentation for file format.", line.c_str()); } if(f) FileIO::fclose(f); if(listenRanges.empty()) { RDCLOG("No whitelist IP ranges configured - using default private IP ranges."); RDCLOG( "Create a config file remoteserver.conf in ~/.renderdoc or %%APPDATA%%/renderdoc to narrow " "this down or accept connections from more ranges."); listenRanges.push_back(std::make_pair(Network::MakeIP(10, 0, 0, 0), 0xff000000)); listenRanges.push_back(std::make_pair(Network::MakeIP(172, 16, 0, 0), 0xfff00000)); listenRanges.push_back(std::make_pair(Network::MakeIP(192, 168, 0, 0), 0xffff0000)); } RDCLOG("Allowing connections from:"); for(size_t i = 0; i < listenRanges.size(); i++) { uint32_t ip = listenRanges[i].first; uint32_t mask = listenRanges[i].second; RDCLOG("%u.%u.%u.%u / %u.%u.%u.%u", Network::GetIPOctet(ip, 0), Network::GetIPOctet(ip, 1), Network::GetIPOctet(ip, 2), Network::GetIPOctet(ip, 3), Network::GetIPOctet(mask, 0), Network::GetIPOctet(mask, 1), Network::GetIPOctet(mask, 2), Network::GetIPOctet(mask, 3)); } if(allowExecution) RDCLOG("Allowing execution commands"); else RDCLOG("Blocking execution commands"); while(!killReplay) { if(newlyReady) { RDCLOG("Replay host ready for requests."); newlyReady = false; } Network::Socket *client = sock->AcceptClient(false); if(client == NULL) { if(!sock->Connected()) { RDCERR("Error in accept - shutting down server"); SAFE_DELETE(sock); return; } Threading::Sleep(5); continue; } uint32_t ip = client->GetRemoteIP(); RDCLOG("Connection received from %u.%u.%u.%u.", Network::GetIPOctet(ip, 0), Network::GetIPOctet(ip, 1), Network::GetIPOctet(ip, 2), Network::GetIPOctet(ip, 3)); bool valid = false; for(size_t i = 0; i < listenRanges.size(); i++) { if(Network::MatchIPMask(ip, listenRanges[i].first, listenRanges[i].second)) { valid = true; break; } } if(!valid) { RDCLOG("Doesn't match any listen range, closing connection."); SAFE_DELETE(client); continue; } Serialiser ser("", Serialiser::WRITING, false); newlyReady = true; map<RDCDriver, string> drivers = RenderDoc::Inst().GetRemoteDrivers(); uint32_t count = (uint32_t)drivers.size(); ser.Serialise("", count); for(auto it = drivers.begin(); it != drivers.end(); ++it) { RDCDriver driver = it->first; ser.Serialise("", driver); ser.Serialise("", (*it).second); } if(!SendPacket(client, ePacket_RemoteDriverList, ser)) { RDCERR("Network error sending supported driver list"); SAFE_DELETE(client); continue; } Threading::Sleep(4); // don't care about the result, just want to check that the socket hasn't been gracefully shut // down client->IsRecvDataWaiting(); if(!client->Connected()) { RDCLOG("Connection closed after sending remote driver list"); SAFE_DELETE(client); continue; } string cap_file; string dummy, dummy2; FileIO::GetDefaultFiles("remotecopy", cap_file, dummy, dummy2); Serialiser *fileRecv = NULL; if(!RecvChunkedFile(client, ePacket_CopyCapture, cap_file.c_str(), fileRecv, NULL)) { FileIO::Delete(cap_file.c_str()); RDCERR("Network error receiving file"); SAFE_DELETE(fileRecv); SAFE_DELETE(client); continue; } RDCLOG("File received."); SAFE_DELETE(fileRecv); RDCDriver driverType = RDC_Unknown; string driverName = ""; RenderDoc::Inst().FillInitParams(cap_file.c_str(), driverType, driverName, NULL); if(RenderDoc::Inst().HasRemoteDriver(driverType)) { ProgressLoopData data; data.sock = client; data.killsignal = false; data.progress = 0.0f; RenderDoc::Inst().SetProgressPtr(&data.progress); Threading::ThreadHandle ticker = Threading::CreateThread(ProgressTicker, &data); IRemoteDriver *driver = NULL; auto status = RenderDoc::Inst().CreateRemoteDriver(driverType, cap_file.c_str(), &driver); if(status != eReplayCreate_Success || driver == NULL) { RDCERR("Failed to create remote driver for driver type %d name %s", driverType, driverName.c_str()); SAFE_DELETE(client); continue; } driver->ReadLogInitialisation(); RenderDoc::Inst().SetProgressPtr(NULL); data.killsignal = true; Threading::JoinThread(ticker); Threading::CloseThread(ticker); FileIO::Delete(cap_file.c_str()); SendPacket(client, ePacket_LogReady); ProxySerialiser *proxy = new ProxySerialiser(client, driver); while(client) { if(!proxy->Tick() || killReplay) { SAFE_DELETE(client); } } driver->Shutdown(); RDCLOG("Closing replay connection"); SAFE_DELETE(proxy); SAFE_DELETE(client); } else { RDCERR("File needs driver for %s which isn't supported!", driverName.c_str()); FileIO::Delete(cap_file.c_str()); } SAFE_DELETE(client); } SAFE_DELETE(sock); }
void RenderDoc::RemoteAccessClientThread(void *s) { Network::Socket *client = (Network::Socket *)s; Serialiser ser(L"", Serialiser::WRITING, false); wstring api = L""; RDCDriver driver; RenderDoc::Inst().GetCurrentDriver(driver, api); ser.Rewind(); wstring target = RenderDoc::Inst().GetCurrentTarget(); ser.Serialise("", target); ser.Serialise("", api); if(!SendPacket(client, ePacket_Handshake, ser)) { SAFE_DELETE(client); { SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock); RenderDoc::Inst().m_SingleClientName = L""; } return; } const int pingtime = 1000; // ping every 1000ms const int ticktime = 10; // tick every 10ms int curtime = 0; vector<wstring> captures; while(client) { if(RenderDoc::Inst().m_RemoteClientThreadShutdown || (client && !client->Connected())) { SAFE_DELETE(client); break; } ser.Rewind(); Threading::Sleep(ticktime); curtime += ticktime; PacketType packetType = ePacket_Noop; wstring curapi; RenderDoc::Inst().GetCurrentDriver(driver, curapi); if(curapi != api) { api = curapi; ser.Serialise("", api); packetType = ePacket_RegisterAPI; } else { vector<wstring> caps = RenderDoc::Inst().GetCaptures(); if(caps.size() != captures.size()) { uint32_t idx = (uint32_t)captures.size(); captures.push_back(caps[idx]); packetType = ePacket_NewCapture; uint64_t timestamp = FileIO::GetModifiedTimestamp(captures.back().c_str()); ser.Serialise("", idx); ser.Serialise("", timestamp); ser.Serialise("", captures.back()); uint32_t len = 128*1024; byte *thumb = new byte[len]; RENDERDOC_GetThumbnail(captures.back().c_str(), thumb, len); size_t l = len; ser.Serialise("", len); ser.SerialiseBuffer("", thumb, l); delete[] thumb; } } if(curtime < pingtime && packetType == ePacket_Noop) { if(client->IsRecvDataWaiting()) { PacketType type; Serialiser *recvser = NULL; if(!RecvPacket(client, type, &recvser)) SAFE_DELETE(client); if(client == NULL) { SAFE_DELETE(recvser); continue; } else if(type == ePacket_TriggerCapture) { RenderDoc::Inst().TriggerCapture(); } else if(type == ePacket_QueueCapture) { uint32_t frameNum = 0; recvser->Serialise("", frameNum); RenderDoc::Inst().QueueCapture(frameNum); } else if(type == ePacket_CopyCapture) { vector<wstring> caps = RenderDoc::Inst().GetCaptures(); uint32_t id = 0; recvser->Serialise("", id); if(id < caps.size()) { ser.Serialise("", id); if(!SendPacket(client, ePacket_CopyCapture, ser)) { SAFE_DELETE(client); continue; } ser.Rewind(); if(!SendChunkedFile(client, ePacket_CopyCapture, caps[id].c_str(), ser, NULL)) { SAFE_DELETE(client); continue; } RenderDoc::Inst().MarkCaptureRetrieved(id); } } SAFE_DELETE(recvser); } continue; } curtime = 0; if(!SendPacket(client, packetType, ser)) { SAFE_DELETE(client); continue; } } // give up our connection { SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock); RenderDoc::Inst().m_SingleClientName = L""; } }