Update: UltraVNC 1.4.3.6 and UltraVNC SC 1.4.3.6: viewtopic.php?t=37885
Important: Please update to latest version before to create a reply, a topic or an issue: viewtopic.php?t=37864

Join us on social networks and share our announcements:
- Website: https://uvnc.com/
- GitHub: https://github.com/ultravnc
- Mastodon: https://mastodon.social/@ultravnc
- Facebook: https://www.facebook.com/ultravnc1
- X/Twitter: https://twitter.com/ultravnc1
- Reddit community: https://www.reddit.com/r/ultravnc
- OpenHub: https://openhub.net/p/ultravnc

Control of VNC Server via Telnet Control Port - WITH PATCH!

Any features you would like to see in UltraVNC? Propose it here
Post Reply
hsmeier
Posts: 3
Joined: 2014-03-25 08:33

Control of VNC Server via Telnet Control Port - WITH PATCH!

Post by hsmeier »

Hi Guys,

I need to control a running VNC server via socket with telnet on localhost. This is meant to provide an easy to automate interface for other processes starting and then manipulating a VNC server locally.
Because I definitely need to have such an interface, I won't just ask for anybody to implement it but rather provide a patch here (see below) that does exactly this.
Question to the developers: Would you like to integrate this patch into UltraVNC code? Do you need additional modifications prior to integration?

Manipulation options are
- listing of active clients
- switching on/of keybord and/or mouse and/or file transfer permissions for each client separately
- switching on/off keybord and/or mouse locally (only if there are active clients ...)
- setting of password,
- adding clients via repeater
- killing clients
- shutting down server
- and more

The existence and the port number of the telnet control port is configurable in ultravnc.ini. The telnet control port in release build is only bound to loopback device.

Here a short how to try (you need gnu patch):

- Take snapshot of today (revision 860)
- cd "...\ultravnc\UltraVNC Project Root\UltraVNC"
- patch -p 1 < ultravnc-with-telnet-ctrl.diff
- build winvnc with vs 10 (tested, with vc 9, 11 or 12 you will still have to add vncTelnetCtrl.* to project winvnc)
- add the following two lines to ultravnc.ini, section [admin]:
TelnetCtrlPort=12345
EnableDriver=1
- run winvnc.exe
- open a cmd.exe, type telnet localhost 12345
- type help and see a usage message explaining what exactly you can do
- play with it ...

Thanks for trying!
Comments?

Cheers

Hans

Code: Select all

diff -Naur UltraVNC-orig/winvnc/winvnc/vncclient.cpp UltraVNC/winvnc/winvnc/vncclient.cpp
--- UltraVNC-orig/winvnc/winvnc/vncclient.cpp	2013-11-02 00:00:46.000000000 +0100
+++ UltraVNC/winvnc/winvnc/vncclient.cpp	2014-03-25 09:02:47.439091300 +0100
@@ -3125,7 +3125,12 @@
 				fUserOk = m_client->DoFTUserImpersonation();
 			}
 
-		    if (!m_client->m_keyboardenabled || !m_client->m_pointerenabled) fUserOk = false; //PGM
+			switch(m_client->m_ftEnableMode)
+			{
+				case vncClient::FT_DISABLED: fUserOk = false; break;
+				case vncClient::FT_AUTOMATIC: if (!m_client->m_keyboardenabled || !m_client->m_pointerenabled) fUserOk = false; /*PGM*/ break;
+				case vncClient::FT_ENABLED: fUserOk = true; break;
+			}
 
 			omni_mutex_lock l(m_client->GetUpdateLock(),88);
 
@@ -3587,7 +3592,7 @@
 								else
 									ft.contentType = rfbFileTransferAccess; // Viewer with old FT protocole
 
-								if (!bOldFTProtocole && m_server->FileTransferEnabled() && m_client->m_server->RemoteInputsEnabled() && fUserOk)
+								if (!bOldFTProtocole && m_server->FileTransferEnabled() && fUserOk)
 								   ft.size = Swap32IfLE(1);
 								else
 								   ft.size = Swap32IfLE(-1); 
@@ -4141,6 +4146,8 @@
 
 	m_socket = NULL;
 	m_client_name = 0;
+	m_client_port = 0;
+	m_ftEnableMode = FT_AUTOMATIC;
 
 	// Initialise mouse fields
 	m_mousemoved = FALSE;
@@ -4367,6 +4374,8 @@
 	else
 		m_client_name = _strdup("<unknown>");
 
+   m_client_port = m_socket->GetPeerPort();
+
 	// Save the client id
 	m_id = newid;
 
@@ -4624,6 +4633,13 @@
 	return m_client_name;
 }
 
+// Functions used to set and retrieve the client settings
+int
+vncClient::GetClientPort()
+{
+	return m_client_port;
+}
+
 // Enabling and disabling clipboard/GFX updates
 void
 vncClient::DisableProtocol()
@@ -6215,3 +6231,4 @@
 	m_socket->SendExact((char *)&msg, sz_rfbNotifyPluginStreamingMsg, rfbNotifyPluginStreaming);
 	m_socket->SetPluginStreamingOut();
 }
+
diff -Naur UltraVNC-orig/winvnc/winvnc/vncclient.h UltraVNC/winvnc/winvnc/vncclient.h
--- UltraVNC-orig/winvnc/winvnc/vncclient.h	2013-03-27 21:22:24.000000000 +0100
+++ UltraVNC/winvnc/winvnc/vncclient.h	2014-03-25 09:02:47.454716200 +0100
@@ -90,6 +90,8 @@
 class vncClient
 {
 public:
+	enum FtEnableMode {FT_DISABLED, FT_AUTOMATIC, FT_ENABLED};
+
 	// Constructor/destructor
 	vncClient();
 	virtual ~vncClient();
@@ -156,12 +158,17 @@
 
 	// Functions for setting & getting the client settings
 	virtual void EnableKeyboard(BOOL enable) {m_keyboardenabled = enable;};
+	virtual BOOL IsKeyboardEnabled(){return m_keyboardenabled;};
 	virtual void EnablePointer(BOOL enable) {m_pointerenabled = enable;};
+	virtual BOOL IsPointerEnabled(){return m_pointerenabled;};
+	virtual void SetFileTransferMode(FtEnableMode ftEnableMode) {m_ftEnableMode = ftEnableMode;}
+	virtual FtEnableMode GetFileTransferEnableMode(){return m_ftEnableMode;}
 	virtual void EnableJap(bool enable) {m_jap = enable;};
 	virtual void SetCapability(int capability) {m_capability = capability;};
 
 	virtual int GetCapability() {return m_capability;};
 	virtual const char *GetClientName();
+	virtual int GetClientPort();
 	virtual vncClientId GetClientId() {return m_id;};
 
 	// Disable/enable protocol messages to the client
@@ -396,6 +403,7 @@
 	BOOL			m_IsLoopback;
 	BOOL			m_keyboardenabled;
 	BOOL			m_pointerenabled;
+	FtEnableMode	m_ftEnableMode;
 	bool			m_jap;
 	int				m_capability;
 	vncClientId		m_id;
@@ -410,6 +418,7 @@
 	// The socket
 	VSocket			*m_socket;
 	char			*m_client_name;
+   int         m_client_port;
 
 	// The client thread
 	omni_thread		*m_thread;
diff -Naur UltraVNC-orig/winvnc/winvnc/vncproperties.cpp UltraVNC/winvnc/winvnc/vncproperties.cpp
--- UltraVNC-orig/winvnc/winvnc/vncproperties.cpp	2013-11-08 19:55:46.000000000 +0100
+++ UltraVNC/winvnc/winvnc/vncproperties.cpp	2014-03-25 09:02:47.454716200 +0100
@@ -2263,6 +2263,10 @@
 		m_server->SetAuthHosts(0);
 	}
 
+   bool enableTelnetCtrl = myIniFile.ReadInt("admin", "EnableTelnetCtrl", false)?true:false;
+   int port = myIniFile.ReadInt("admin", "TelnetCtrlPort", 59000);
+   m_server->EnableTelnetCtrl(enableTelnetCtrl, port);
+
 	vnclog.Print(LL_INTINFO, VNCLOG("***** DBG - Load User Preferences\n"));
 
 	// Set the default user prefs
diff -Naur UltraVNC-orig/winvnc/winvnc/vncserver.cpp UltraVNC/winvnc/winvnc/vncserver.cpp
--- UltraVNC-orig/winvnc/winvnc/vncserver.cpp	2013-11-04 18:14:34.000000000 +0100
+++ UltraVNC/winvnc/winvnc/vncserver.cpp	2014-03-25 09:02:47.470341100 +0100
@@ -46,6 +46,8 @@
 
 #include "vncmenu.h"
 
+#include "vncTelnetCtrl.h"
+
 #include "Localization.h" // ACT : Add localization on messages
 bool g_Server_running;
 extern bool g_Desktop_running;
@@ -241,12 +243,20 @@
 	//adzm 2010-05-12 - dsmplugin config
 	m_szDSMPluginConfig[0] = '\0';
 	OS_Shutdown=false;
+
+	m_telnetCtrl = NULL;
 }
 
 vncServer::~vncServer()
 {
 	vnclog.Print(LL_STATE, VNCLOG("shutting down server object1\n"));
 
+	if(m_telnetCtrl != NULL)
+	{
+		delete m_telnetCtrl;
+		m_telnetCtrl = NULL;
+	}
+
 	// We don't want to retry when we are shutting down...
 	m_fAutoReconnect = FALSE;
 	m_fIdReconnect = FALSE;
@@ -341,6 +351,33 @@
 	g_Server_running=false;
 }
 
+
+void
+vncServer::EnableTelnetCtrl(bool enable, int port)
+{
+   if(enable)
+   {
+      if(m_telnetCtrl && m_telnetCtrl->GetPort() == port)
+      {
+         // nothing to do
+      }
+      else 
+      {
+         if(m_telnetCtrl)
+         {
+            delete m_telnetCtrl; //runs on wrong port, need new
+         }
+         m_telnetCtrl = new vncTelnetCtrl(this, &fShutdownOrdered, GetCurrentThreadId(), port);
+      }
+   }
+   else
+   {
+      delete m_telnetCtrl;
+      m_telnetCtrl = NULL;
+   }
+}
+
+
 void
 vncServer::ShutdownServer()
 {
diff -Naur UltraVNC-orig/winvnc/winvnc/vncserver.h UltraVNC/winvnc/winvnc/vncserver.h
--- UltraVNC-orig/winvnc/winvnc/vncserver.h	2013-11-04 18:14:34.000000000 +0100
+++ UltraVNC/winvnc/winvnc/vncserver.h	2014-03-25 09:02:47.470341100 +0100
@@ -38,6 +38,7 @@
 // and via the ORB interface
 extern bool			fShutdownOrdered;
 class vncServer;
+class vncTelnetCtrl;
 
 #if (!defined(_WINVNC_VNCSERVER))
 #define _WINVNC_VNCSERVER
@@ -163,6 +164,8 @@
 	UINT				m_port;
 	UINT				m_port_http; // TightVNC 1.2.7
 
+   virtual void EnableTelnetCtrl(bool enable, int port);
+
 	virtual void ShutdownServer();
 
 protected:
@@ -604,6 +607,8 @@
 	// adzm 2010-08
 	int m_socketKeepAliveTimeout;
 	bool clearconsole;
+
+	vncTelnetCtrl* m_telnetCtrl;
 };
 
 #endif
diff -Naur UltraVNC-orig/winvnc/winvnc/vncTelnetCtrl.cpp UltraVNC/winvnc/winvnc/vncTelnetCtrl.cpp
--- UltraVNC-orig/winvnc/winvnc/vncTelnetCtrl.cpp	1970-01-01 01:00:00.000000000 +0100
+++ UltraVNC/winvnc/winvnc/vncTelnetCtrl.cpp	2014-03-25 09:02:47.470341100 +0100
@@ -0,0 +1,660 @@
+#include "vncTelnetCtrl.h"
+#include "vncserver.h"
+
+
+
+vncTelnetCtrl::vncTelnetCtrl(vncServer* pServer, bool* pOrderShutdown, DWORD threadId, int port)
+	:m_pServer(pServer),
+	m_threadHandle(0),
+	m_sock(0),
+	m_threadId(threadId),
+	m_pOrderShutdown(pOrderShutdown),
+	m_port(port)
+{
+	bool wsaInitailized = false; 
+	try
+	{
+		WORD wVersionRequested = MAKEWORD(2, 0);
+		WSADATA wsaData;
+		if (WSAStartup(wVersionRequested, &wsaData))throw -1;
+		wsaInitailized = true;
+		
+		if ((m_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)throw -1;
+		struct sockaddr_in addr;
+		addr.sin_family = AF_INET;
+		addr.sin_port = htons(m_port);
+#ifdef _DEBUG
+		addr.sin_addr.s_addr = htonl(INADDR_ANY);
+#else
+		addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+#endif
+		if (bind(m_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) throw -1;
+		if (listen(m_sock, 5) < 0) throw -1;
+
+		DWORD dwTId;
+		m_threadHandle = CreateThread(NULL, 0, &threadProc, this, 0, &dwTId);
+		if(!m_threadHandle)throw -1;
+	}
+	catch(...)
+	{
+		if(m_sock)
+		{
+			shutdown(m_sock, SD_BOTH);
+			closesocket(m_sock);
+		}
+		if(m_threadHandle)
+		{
+			WaitForSingleObject( m_threadHandle, INFINITE );
+			CloseHandle(m_threadHandle);
+		}
+		if(wsaInitailized)
+		{
+			WSACleanup();
+		}
+	}
+}
+
+vncTelnetCtrl::~vncTelnetCtrl(void)
+{
+	shutdown(m_sock, SD_BOTH);
+	closesocket(m_sock);
+	WaitForSingleObject( m_threadHandle, INFINITE );
+	CloseHandle(m_threadHandle);
+	WSACleanup();
+}
+
+DWORD WINAPI vncTelnetCtrl::threadProc(LPVOID pThis)
+{
+	return static_cast<vncTelnetCtrl*>(pThis)->Listen();
+}
+
+DWORD vncTelnetCtrl::Listen()
+{
+	SOCKET accSock = 0;
+	while ((accSock = accept(m_sock, NULL, 0)) != SOCKET_ERROR)
+	{
+		DWORD dwTId;
+		accInfo* pai = new accInfo;
+		pai->m_Sock=accSock;
+		pai->m_ThrHandle = CreateThread(NULL, 0, &accProc, pai, 0, &dwTId);
+		pai->m_pThis = this;
+		pai->m_ShutdownOnDie = false;
+		if(pai->m_ThrHandle)
+		{
+			m_accList.push_back(pai);
+		}
+		else
+		{
+			shutdown(pai->m_Sock, SD_BOTH);
+			closesocket(pai->m_Sock);
+			delete pai;
+		}
+		joinThreads(false);
+	}
+	joinThreads(true);
+	return 0;
+}
+
+void vncTelnetCtrl::joinThreads(bool terminateThreads)
+{
+	bool removedOne=false;
+	do
+	{
+		removedOne=false;
+		for(std::list<accInfo*>::iterator i = m_accList.begin(); i != m_accList.end(); i++)
+		{
+			if((terminateThreads) && ((*i)->m_Sock != 0))
+			{
+				shutdown((*i)->m_Sock, SD_BOTH);
+				closesocket((*i)->m_Sock);
+				(*i)->m_Sock = 0;
+			}
+			if((*i)->m_Sock == 0)
+			{
+				WaitForSingleObject((*i)->m_ThrHandle, INFINITE);
+				CloseHandle((*i)->m_ThrHandle);
+				delete *i;
+				m_accList.remove(*i);
+				removedOne = true;
+				break;
+			}
+		}
+	}
+	while(removedOne);		
+}
+
+DWORD WINAPI vncTelnetCtrl::accProc(LPVOID pAccInfo)
+{
+	accInfo* pai = static_cast<accInfo*>(pAccInfo);
+	int bytes = 0;
+	char buf[1];
+	char cmd[1024];
+	char* pCmd = cmd;
+	send(pai->m_Sock, "> ", 2, 0);
+	while((bytes=recv(pai->m_Sock, buf, sizeof(buf), 0))>0)
+	{
+		// echo
+		//send(pai->m_Sock, buf, 1, 0);
+
+		if(*buf == '\r')
+		{
+			//ignore otherwise
+		}
+		else 
+		if(*buf == '\n')
+		{
+			*pCmd=0;
+			pCmd=cmd;
+			const char* pRes = NULL;
+			int ResLen = 0;
+			bool exitNow = !pai->m_pThis->DoCommand(cmd, pRes, ResLen, pai);
+			send(pai->m_Sock, pRes, ResLen, 0);
+			delete[] pRes;
+			if(exitNow)
+			{
+				shutdown(pai->m_Sock, SD_BOTH);
+				closesocket(pai->m_Sock);
+			}
+			else
+			{
+				send(pai->m_Sock, "> ", 2, 0);
+			}
+		}
+		else
+		{
+			if(pCmd<(cmd+sizeof(cmd)-1))
+			{
+				*pCmd++=*buf;
+			}
+			else
+			{
+				send(pai->m_Sock, "bye: command too long\r\n", 23, 0);
+				shutdown(pai->m_Sock, SD_BOTH);
+				closesocket(pai->m_Sock);
+			}
+		}
+	}
+	closesocket(pai->m_Sock);
+	pai->m_Sock = 0;
+	if(pai->m_ShutdownOnDie)
+	{
+		*(pai->m_pThis->m_pOrderShutdown) = true;
+	}
+	return 0;
+}
+
+
+bool vncTelnetCtrl::DoCommand(const char* pCmdRaw, const char*& pRes, int& ResLen, accInfo* pAi)
+{
+	bool more = true;
+	bool understood = false;
+	
+	// evaluate backspace
+	char cmd[1024];
+	char* pCmd = cmd;
+	while(*pCmdRaw)
+	{
+		if(*pCmdRaw != 0x08)
+		{
+			*pCmd = *pCmdRaw;
+			pCmd++;
+		}
+		else
+		{
+			if(pCmd > cmd)
+			{
+				pCmd--;
+			}
+		}
+		pCmdRaw++;
+	}
+	*pCmd = 0;
+	pCmd = cmd;
+	// --
+
+	if(strcmp(pCmd, "help") == 0)
+	{
+		pRes = allocateResponse(
+			"\r\n"
+			"Usage:\r\n"
+			"\r\n"
+			"Commands:\r\n"
+			"\r\n"
+			"   help     Shows this help text.\r\n"
+			"\r\n"
+			"   list     Returns a list of clients that are logged in.\r\n"
+			"            Values listed:\r\n"
+			"            <clientid>       ID of client (to be used with other commands)\r\n"
+			"            <PeerAddress>:<PeerPort> identifying the connected client\r\n"
+			"            k:<on|off>       keyboard input state\r\n"
+			"            m:<on|off>       mouse input state\r\n"
+			"            f:<enabled|automaticdisabled>\r\n"
+			"                             file transfer enable state, values:\r\n"
+			"                             enabled   file transfer enabled\r\n"
+			"                             automatic file transfer enabled if all input on\r\n"
+			"                             disabled  file transfer disabled\r\n"
+			"\r\n"
+			"   passwd <password> Sets a new password.\r\n"
+			"\r\n"
+			"   addclient <address>:<port> <id>\r\n"
+			"            Opens a client connection to the repeater at the given address\r\n"
+			"            and listening at the given port under the given id.\r\n"
+			"            The ID gets automatically prefixed by 'ID:'.\r\n"
+			"            Any ID the repeater can handle is applicable.\r\n"
+			"\r\n"
+			"   input <on|off> <<clientid>|local>\r\n"
+			"            Switches keyboard and mouse input on or off for the given\r\n"
+			"            client ID or for the local desktop. See also 'list'.\r\n"
+			"\r\n"
+			"   mouse <on|off> <<clientid>|local>\r\n"
+			"            Switches mouse input on or off for the given\r\n"
+			"            client ID or for the local desktop. See also 'list'.\r\n"
+			"\r\n"
+			"   keyboard <on|off> <<clientid>|local>\r\n"
+			"            Switches keyboard on or off for the given\r\n"
+			"            client ID or for the local desktop. See also 'list'.\r\n"
+			"\r\n"
+			"   files <enable|automatic|disable> <clientid>\r\n"
+			"            Switches file transfer enable state as given.\r\n"
+			"            For explanation of the modes see 'list'.\r\n"
+			"\r\n"
+			"   kill <clientid>\r\n"
+			"            Kills the given client ID. See also 'list'.\r\n"
+			"\r\n"
+			"   quit     Exits from telnet interface.\r\n"
+			"\r\n"
+			"   exit     Exits from telnet interface.\r\n"
+			"\r\n"
+			"   shutdown\r\n"
+			"            Immediately shuts down the vnc server.\r\n"
+			"\r\n"
+			"   shutdownondie\r\n"
+			"            Sets a mode that shuts down the vnc server immediately\r\n"
+			"            if telnet connection has an unexpected connection loss.\r\n"
+			"            Useful if telnet port is used for control by another tool.\r\n"
+			"            Quitting normally by exit or quit doesn't issue a shutdown \r\n"
+			"            but instead resets the shutdownondie mode\r\n"
+			"ok\r\n",
+			ResLen);
+		understood = true;
+	}
+	else
+	if(strcmp(pCmd, "quit") == 0)
+	{
+		pAi->m_ShutdownOnDie = false;
+		pRes = allocateResponse("bye\r\n", ResLen);
+		more = false;
+		understood = true;
+	}
+	else
+	if(strcmp(pCmd, "exit") == 0)
+	{
+		pAi->m_ShutdownOnDie = false;
+		pRes = allocateResponse("bye\r\n", ResLen);
+		more = false;
+		understood = true;
+	}
+	else
+	if(strcmp(pCmd, "list") == 0)
+	{
+		std::string res;
+		{
+			omni_mutex_lock lock(m_pServer->m_clientsLock,10001);
+			vncClientList l = m_pServer->ClientList();
+			for(std::list<vncClientId>::iterator i = l.begin(); i != l.end(); i++)
+			{
+				vncClientId ClientId = *i;
+				vncClient* pClient = m_pServer->GetClient(ClientId);
+				const char* pPeerName = pClient->GetClientName();
+				int peerPort = pClient->GetClientPort();
+				bool keyboardEnabled = pClient->IsKeyboardEnabled()?true:false;
+				bool pointerEnabled = pClient->IsPointerEnabled()?true:false;
+				vncClient::FtEnableMode ftMode = pClient->GetFileTransferEnableMode();
+				const char* pFtMode = "";
+				switch(ftMode)
+				{
+					case vncClient::FT_DISABLED: pFtMode="f:disabled"; break;
+					case vncClient::FT_AUTOMATIC: pFtMode="f:automatic"; break;
+					case vncClient::FT_ENABLED: pFtMode="f:enabled"; break;
+				}
+				char line[1024];
+				sprintf(line,"%d %s:%d %s %s %s\r\n", static_cast<int>(ClientId), pPeerName, peerPort, (keyboardEnabled?"k:on":"k:off"), (pointerEnabled?"m:on":"m:off"), pFtMode);
+				res.append(line);
+			}
+		}
+		pRes = allocateResponse(res.c_str(), ResLen);
+		understood = true;
+	}
+	else
+	if(strncmp(pCmd, "passwd ", 7) == 0)
+	{
+		pCmd+=7;
+		char encpasswd[MAXPWLEN+1];
+		encpasswd[MAXPWLEN]=0;
+		vncEncryptPasswd(pCmd, encpasswd);
+		m_pServer->SetPassword(encpasswd);
+		pRes = allocateResponse("ok\r\n", ResLen);
+		understood = true;
+	}
+	else
+	if(strncmp(pCmd, "addclient ", 9) == 0)
+	{
+		pCmd+=9;
+		
+		// eat extra white space
+		while((*pCmd == ' ') || (*pCmd == '\t'))
+		{
+			pCmd++;
+		}
+
+		char* pAddress = new char[strlen(pCmd)+1];
+		char* pAddressChar=pAddress;
+		while((*pCmd != ':') && (*pCmd != 0))
+		{
+			*pAddressChar = *pCmd;
+			pAddressChar++;
+			pCmd++;
+		}
+		*pAddressChar = 0;
+
+		bool good = true;
+		if(*pCmd != ':')
+		{
+			good = false;
+		}
+
+		int port = 0;
+		if(good)
+		{
+			pCmd++;
+			char* pEnd = NULL;
+			port = strtoul(pCmd, &pEnd, 10);
+			if(port <= 0xffff)
+			{
+				if((*pEnd == ' ') || (*pEnd == '\t'))
+				{
+					pCmd = pEnd+1;
+				}
+				else
+				{
+					good = false;
+				}
+			}
+			else
+			{
+				good = false;
+			}
+		}
+
+		if(good)
+		{
+			// eat extra white space
+			while((*pCmd == ' ') || (*pCmd == '\t'))
+			{
+				pCmd++;
+			}
+		}
+
+		//unsigned id = 0;
+		//if(good)
+		//{
+		//	char* pEnd = NULL;
+		//	id = strtoul(pCmd, &pEnd, 10);
+		//	if(pCmd == pEnd)
+		//	{
+		//		good = false;
+		//	}
+		//	pCmd = pEnd;
+		//}
+		char* pId = NULL;
+		if(good)
+		{
+			pId = new char[strlen(pCmd)+1];
+			char* pIdChar=pId;
+			while((*pCmd != ' ') && (*pCmd != '\t') && (*pCmd != 0))
+			{
+				*pIdChar = *pCmd;
+				pIdChar++;
+				pCmd++;
+			}
+			*pIdChar = 0;
+			if(pIdChar == pId)
+			{
+				good = false;
+			}
+		}
+
+		if(good)
+		{
+			// eat extra white space
+			while((*pCmd == ' ') || (*pCmd == '\t'))
+			{
+				pCmd++;
+			}
+		}
+
+		if(good)
+		{
+			if(*pCmd != 0)
+			{
+				good = false;
+			}
+		}
+
+		if(good)
+		{
+			VSocket *tmpsock = new VSocket;
+			tmpsock->Create();
+			if (tmpsock->Connect(pAddress, port))
+			{
+				char finalidcode[_MAX_PATH];
+				ZeroMemory(finalidcode, sizeof(finalidcode));
+				strcpy(finalidcode, "ID:");
+				//sprintf(finalidcode+3, "%d", id);
+				strcpy(finalidcode+3, pId); 
+				tmpsock->Send(finalidcode,50);
+				tmpsock->SetTimeout(0);
+				m_pServer->AddClient(tmpsock, FALSE, TRUE, 0, NULL, finalidcode, pAddress, port);
+				pRes = allocateResponse("ok\r\n", ResLen);
+			}
+			else
+			{
+				pRes = allocateResponse("nok: Failed to connect.\r\n", ResLen);
+				delete tmpsock;
+			}
+			understood = true;
+		}
+
+		delete pAddress;
+		delete pId;
+	}
+	else
+	if((strncmp(pCmd, "input ", 6) == 0) || (strncmp(pCmd, "mouse ", 6) == 0) || (strncmp(pCmd, "keyboard ", 9) == 0)) 
+	{
+		bool mouse = false;
+		bool kbd = false;
+		switch(*pCmd)
+		{
+		case 'm': pCmd+=6; mouse = true; break; 
+		case 'k': pCmd+=9; kbd = true; break; 
+		case 'i': pCmd+=6; mouse = true; kbd = true; break; 
+		}
+		bool onoffok=false;
+		bool on=false;
+		if(strncmp(pCmd, "on ", 3) == 0)
+		{
+			onoffok=true;
+			on=true;
+			pCmd+=3;
+		}
+		else if(strncmp(pCmd, "off ", 4) == 0)
+		{
+			onoffok=true;
+			on=false;
+			pCmd+=4;
+		}
+
+		int id = 0;
+		int idok = false;
+		if(onoffok)
+		{
+			char* pBehind = NULL;
+			id = strtol(pCmd, &pBehind, 10);
+			if((pBehind != pCmd) && (*pBehind == 0))
+			{
+				idok=true;
+			}
+		}
+
+		if(idok)
+		{
+			vncClientId ClientId = id;
+			{
+				omni_mutex_lock lock(m_pServer->m_clientsLock,10002);
+				vncClient* pClient = m_pServer->GetClient(ClientId);
+				if(pClient)
+				{
+					if(kbd)pClient->EnableKeyboard(on);
+					if(mouse)pClient->EnablePointer(on);
+					pRes = allocateResponse("ok\r\n", ResLen);
+				}
+				else
+				{
+					pRes = allocateResponse("nok: Client not found.\r\n", ResLen);
+				}
+			}
+			understood = true;
+		}
+		else
+		if(strcmp(pCmd, "local") == 0)
+		{
+			if(kbd==mouse)
+			{
+				if(on || (m_pServer->AuthClientCount() > 0))
+				{
+					m_pServer->DisableLocalInputs(!on);
+					pRes = allocateResponse("ok\r\n", ResLen);
+				}
+				else
+				{
+					pRes = allocateResponse("nok: Ignoring 'input off local' as no authenticated clients are connected.\r\n", ResLen);
+				}
+			}
+			else
+			{
+				pRes = allocateResponse("nok: Cannot enable/disable local mouse and keyboard input seperately.\r\n", ResLen);
+			}
+			understood = true;
+		}
+	}
+	else
+	if((strncmp(pCmd, "files ", 6) == 0)) 
+	{
+		pCmd+=6;
+		vncClient::FtEnableMode mode = vncClient::FT_AUTOMATIC;
+		bool modeok=false;
+		if(strncmp(pCmd, "disable ", 8) == 0)
+		{
+			mode=vncClient::FT_DISABLED;
+			modeok=true;
+			pCmd+=8;
+		}
+		else 
+		if(strncmp(pCmd, "automatic ", 10) == 0)
+		{
+			mode=vncClient::FT_AUTOMATIC;
+			modeok=true;
+			pCmd+=10;
+		}
+		else
+		if(strncmp(pCmd, "enable ", 7) == 0)
+		{
+			mode=vncClient::FT_ENABLED;
+			modeok=true;
+			pCmd+=7;
+		}
+
+		int id = 0;
+		int idok = false;
+		if(modeok)
+		{
+			char* pBehind = NULL;
+			id = strtol(pCmd, &pBehind, 10);
+			if((pBehind != pCmd) && (*pBehind == 0))
+			{
+				idok=true;
+			}
+		}
+
+		if(idok)
+		{
+			vncClientId ClientId = id;
+			{
+				omni_mutex_lock lock(m_pServer->m_clientsLock,10002);
+				vncClient* pClient = m_pServer->GetClient(ClientId);
+				if(pClient)
+				{
+					pClient->SetFileTransferMode(mode);
+					pRes = allocateResponse("ok\r\n", ResLen);
+				}
+				else
+				{
+					pRes = allocateResponse("nok: Client not found.\r\n", ResLen);
+				}
+			}
+			understood = true;
+		}
+	}
+	else
+	if(strncmp(pCmd, "kill ", 5) == 0)
+	{
+		pCmd+=5;
+		int id = 0;
+		int idok = false;
+		id = atoi(pCmd);
+		if(id)
+		{
+			idok=true;
+		}
+
+		if(idok)
+		{
+			vncClientId ClientId = id;
+			m_pServer->KillClient(ClientId);
+			pRes = allocateResponse("ok\r\n", ResLen);
+			understood = true;
+		}
+	}
+	else
+	if(strcmp(pCmd, "shutdown") == 0)
+	{
+		pAi->m_ShutdownOnDie = false;
+		*m_pOrderShutdown = true;
+		PostThreadMessage(m_threadId, WM_QUIT, 0,0);
+		pRes = allocateResponse("bye\r\n", ResLen);
+		more = false;
+		understood = true;
+	}
+	else
+	if(strcmp(pCmd, "shutdownondie") == 0)
+	{
+		pRes = allocateResponse("ok\r\n", ResLen);
+		pAi->m_ShutdownOnDie = true;
+		understood = true;
+	}
+
+	if(!understood)
+	{
+		pRes = allocateResponse("nok: Unknown command or bad arguments. Type help for usage.\r\n", ResLen);
+	}
+
+	return more;
+}
+
+const char* vncTelnetCtrl::allocateResponse(const char* pRes, int& ResLen)
+{
+	ResLen = strlen(pRes);
+	char* pBuf = new char[ResLen+1];
+	strcpy(pBuf, pRes);
+	return pBuf;
+}
\ Kein Zeilenumbruch am Dateiende.
diff -Naur UltraVNC-orig/winvnc/winvnc/vncTelnetCtrl.h UltraVNC/winvnc/winvnc/vncTelnetCtrl.h
--- UltraVNC-orig/winvnc/winvnc/vncTelnetCtrl.h	1970-01-01 01:00:00.000000000 +0100
+++ UltraVNC/winvnc/winvnc/vncTelnetCtrl.h	2014-03-25 09:02:47.485966000 +0100
@@ -0,0 +1,33 @@
+#pragma once
+#include "stdhdrs.h"
+#include <list>
+class vncServer;
+class vncTelnetCtrl
+{
+public:
+	vncTelnetCtrl(vncServer* pServer, bool* pOrderShutdown, DWORD ThreadId, int port);
+	virtual ~vncTelnetCtrl();
+	int GetPort() { return m_port; }
+private:
+	struct accInfo
+	{
+		SOCKET m_Sock;
+		HANDLE m_ThrHandle;
+		vncTelnetCtrl* m_pThis;
+		bool m_ShutdownOnDie;
+	};
+	static DWORD WINAPI threadProc(LPVOID pThis);
+	static DWORD WINAPI accProc(LPVOID pAccInfo);
+	static const char* allocateResponse(const char* pRes, int& ResLen);
+	DWORD Listen();
+	void joinThreads(bool terminateThreads);
+	bool DoCommand(const char* pCmdRaw, const char*& pRes, int& ResLen, accInfo* pAi);
+	vncServer* m_pServer;
+	HANDLE m_threadHandle;
+	SOCKET m_sock;
+	std::list<accInfo*> m_accList;
+	DWORD m_threadId;
+	bool* m_pOrderShutdown;
+	int m_port;
+};
+
diff -Naur UltraVNC-orig/winvnc/winvnc/vsocket.cpp UltraVNC/winvnc/winvnc/vsocket.cpp
--- UltraVNC-orig/winvnc/winvnc/vsocket.cpp	2013-03-27 21:22:24.000000000 +0100
+++ UltraVNC/winvnc/winvnc/vsocket.cpp	2014-03-25 09:02:47.485966000 +0100
@@ -405,6 +405,21 @@
 }
 
 ////////////////////////////
+////////////////////////////
+
+VCard
+VSocket::GetPeerPort()
+{
+	struct sockaddr_in sockinfo;
+	int sockinfosize = sizeof(sockinfo);
+
+	// Get the peer address for the client socket
+	getpeername(sock, (struct sockaddr *)&sockinfo, &sockinfosize);
+
+   return ntohs(sockinfo.sin_port);
+}
+
+////////////////////////////
 
 VString
 VSocket::GetSockName()
diff -Naur UltraVNC-orig/winvnc/winvnc/vsocket.h UltraVNC/winvnc/winvnc/vsocket.h
--- UltraVNC-orig/winvnc/winvnc/vsocket.h	2012-11-07 09:19:46.000000000 +0100
+++ UltraVNC/winvnc/winvnc/vsocket.h	2014-03-25 09:02:47.485966000 +0100
@@ -116,6 +116,12 @@
   //        This string MUST be copied before the next socket call...
   VString GetPeerName();
 
+  // GetPeerPort
+  //        If the socket is connected then this returns the name
+  //        of the machine to which it is connected.
+  //        This string MUST be copied before the next socket call...
+  VCard GetPeerPort();
+
   // GetSockName
   //		If the socket exists then the name of the local machine
   //		is returned.  This string MUST be copied before the next
diff -Naur UltraVNC-orig/winvnc/winvnc/winvnc.cpp UltraVNC/winvnc/winvnc/winvnc.cpp
--- UltraVNC-orig/winvnc/winvnc/winvnc.cpp	2013-11-04 18:14:34.000000000 +0100
+++ UltraVNC/winvnc/winvnc/winvnc.cpp	2014-03-25 09:02:47.485966000 +0100
@@ -40,6 +40,7 @@
 #include "winvnc.h"
 
 #include "vncserver.h"
+#include "vncTelnetCtrl.h"
 #include "vncmenu.h"
 #include "vncinsthandler.h"
 #include "vncservice.h"
@@ -1027,6 +1028,7 @@
 	//vnclog.Print(LL_INTINFO, VNCLOG("***** DBG - Previous instance checked - Trying to create server\n"));
 	// CREATE SERVER
 	vncServer server;
+   DWORD dwTId;
 
 	// Set the name and port number
 	server.SetName(szAppName);
@@ -1051,7 +1053,6 @@
 		//vnclog.Print(LL_STATE, VNCLOG("################## Creating Imp Thread : %d \n"), nn);
 
 		HANDLE threadHandle;
-		DWORD dwTId;
 		threadHandle = CreateThread(NULL, 0, imp_desktop_thread, &server, 0, &dwTId);
 
 		if (threadHandle)
diff -Naur UltraVNC-orig/winvnc/winvnc/winvnc.vcxproj UltraVNC/winvnc/winvnc/winvnc.vcxproj
--- UltraVNC-orig/winvnc/winvnc/winvnc.vcxproj	2013-12-05 21:11:22.000000000 +0100
+++ UltraVNC/winvnc/winvnc/winvnc.vcxproj	2014-03-25 09:02:47.501590900 +0100
@@ -1221,6 +1221,7 @@
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
+    <ClCompile Include="vncTelnetCtrl.cpp" />
     <ClCompile Include="vnctimedmsgbox.cpp">
       <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions>
@@ -1325,6 +1326,7 @@
     <ClInclude Include="vncservice.h" />
     <ClInclude Include="vncsetauth.h" />
     <ClInclude Include="vncsockconnect.h" />
+    <ClInclude Include="vncTelnetCtrl.h" />
     <ClInclude Include="vnctimedmsgbox.h" />
     <ClInclude Include="vsocket.h" />
     <ClInclude Include="vtypes.h" />
User avatar
Rudi De Vos
Admin & Developer
Admin & Developer
Posts: 6832
Joined: 2004-04-23 10:21
Contact:

Re: Control of VNC Server via Telnet Control Port - WITH PAT

Post by Rudi De Vos »

It looks a nice addon...and could be usefull to other.

I have NOT tested it, just a fast security remark.

Now, ultravnc.ini is protected by the OS, only admins can make changes
Using telnet (even in loopback mode) any local guest can telnet to it...

For listing this isn't a direct issue but thoose two options look unsecure.
- setting of password,
- adding clients via repeater

Possible some extra telnet authentication is needed to protect the access.
Bonji
100
100
Posts: 339
Joined: 2008-05-13 14:54

Re: Control of VNC Server via Telnet Control Port - WITH PAT

Post by Bonji »

This looks really cool, and I know people have inquired in the past about using uVNC as a file transfer tool and I see this having the potential of providing a command line interface to transferring files. Is there any chance file transfers could be added to this?
-Ben
hsmeier
Posts: 3
Joined: 2014-03-25 08:33

Re: Control of VNC Server via Telnet Control Port - WITH PAT

Post by hsmeier »

@Rudi

Yes, you are certainly right.
For me here this isn't an issue because this stuff runs on a machine the "normal" user hasn't access to Windows, only to the GUI Application.
But for making it fit for others - yes.
The question is, shall the user type the telnet access password directly on the telnet prompt? It thus appears on the screen and it could be sniffed on loopback (not so easy but possible). For use with an automated client we could instead offer challenge/response.
My Suggestion: let's make it configurable - no passwd|clear text passwd|challenge/response. To get the obscured password written to the config file, we could add a command "telnetpasswd". So the admin could start with config "no password", set the password (server writes the obsured password to config file) and then modify the config file to "clear text password" or "challenge/response" (options in config file would be 0|1|2).
If this sounds good to you I'm going to implement it - at least the first two options for now.

@Bonji

If this is meant to trigger file transfers from the server side that might be an option ...
goergc
Posts: 5
Joined: 2013-09-30 06:51

Re: Control of VNC Server via Telnet Control Port - WITH PAT

Post by goergc »

Is this feature already built into a released version of UltraVNC? This interface looks like the solution for a current task I am challenging.

Best regards
Christian
Post Reply