1 #include "stdafx.h"
2 #include <iostream>
3 #include "TCPServer.h"
4 #include "TCPClient.h"
5 #include "../main/RFXNames.h"
6 #include "../main/RFXtrx.h"
7 #include "../main/Helper.h"
8 #include "../main/Logger.h"
9 #include "../hardware/DomoticzTCP.h"
10 #include "../main/mainworker.h"
11 #include "../main/localtime_r.h"
12 #include <boost/asio.hpp>
13 #include <algorithm>
14 #include <boost/bind.hpp>
15 
16 namespace tcp {
17 namespace server {
18 
CTCPServerIntBase(CTCPServer * pRoot)19 CTCPServerIntBase::CTCPServerIntBase(CTCPServer *pRoot)
20 {
21 	m_pRoot=pRoot;
22 }
23 
24 
~CTCPServerIntBase(void)25 CTCPServerIntBase::~CTCPServerIntBase(void)
26 {
27 //	stopAllClients();
28 }
29 
start()30 void CTCPServerInt::start()
31 {
32 	// The io_service::run() call will block until all asynchronous operations
33 	// have finished. While the server is running, there is always at least one
34 	// asynchronous operation outstanding: the asynchronous accept call waiting
35 	// for new incoming connections.
36 	io_service_.run();
37 }
38 
stop()39 void CTCPServerInt::stop()
40 {
41 	// Post a call to the stop function so that server::stop() is safe to call
42 	// from any thread.
43 	io_service_.post(boost::bind(&CTCPServerInt::handle_stop, this));
44 	m_incoming_domoticz_history.clear();
45 }
46 
handle_stop()47 void CTCPServerInt::handle_stop()
48 {
49 	// The server is stopped by cancelling all outstanding asynchronous
50 	// operations. Once all operations have finished the io_service::run() call
51 	// will exit.
52 	acceptor_.close();
53 	stopAllClients();
54 }
55 
IsUserHereFirstTime(const std::string & ip_string)56 bool CTCPServerInt::IsUserHereFirstTime(const std::string &ip_string)
57 {
58 	//
59 	//	Log same IP-address first time and then once per day
60 	//
61 	time_t now = mytime(NULL);
62 
63 	std::vector<_tTCPLogInfo>::iterator itt = m_incoming_domoticz_history.begin();
64 	while (itt!= m_incoming_domoticz_history.end())
65 	{
66 		if (difftime(now,itt->time) > SECONDS_PER_DAY)
67 		{
68 			itt = m_incoming_domoticz_history.erase(itt);
69 		}
70 		else
71 		{
72 			if (ip_string.compare(itt->string) == 0)
73 			{
74 				//already logged this
75 				return false;
76 			}
77 			++itt;
78 		}
79 	}
80 	if (m_incoming_domoticz_history.size() > 100)
81 		return false; //just to be safe
82 
83 	_tTCPLogInfo li;
84 	li.time = now;
85 	li.string = ip_string;
86 	m_incoming_domoticz_history.push_back(li);
87 	return true;
88 }
89 
handleAccept(const boost::system::error_code & error)90 void CTCPServerInt::handleAccept(const boost::system::error_code& error)
91 {
92 	if (error)
93 		return;
94 	std::lock_guard<std::mutex> l(connectionMutex);
95 	std::string s = new_connection_->socket()->remote_endpoint().address().to_string();
96 
97 	if (s.substr(0, 7) == "::ffff:") {
98 		s = s.substr(7);
99 	}
100 
101 	new_connection_->m_endpoint=s;
102 
103 	if (IsUserHereFirstTime(s))
104 	{
105 		_log.Log(LOG_STATUS, "Incoming Domoticz connection from: %s", s.c_str());
106 	}
107 	else
108 	{
109 		_log.Debug(DEBUG_NORM, "Incoming Domoticz connection from: %s", s.c_str());
110 	}
111 
112 	connections_.insert(new_connection_);
113 	new_connection_->start();
114 
115 	new_connection_.reset(new CTCPClient(io_service_, this));
116 
117 	acceptor_.async_accept(
118 		*(new_connection_->socket()),
119 		boost::bind(&CTCPServerInt::handleAccept, this,
120 		boost::asio::placeholders::error));
121 }
122 
FindUser(const std::string & username)123 _tRemoteShareUser* CTCPServerIntBase::FindUser(const std::string &username)
124 {
125 	std::vector<_tRemoteShareUser>::iterator itt;
126 	int ii=0;
127 	for (itt=m_users.begin(); itt!=m_users.end(); ++itt)
128 	{
129 		if (itt->Username==username)
130 			return &m_users[ii];
131 		ii++;
132 	}
133 	return NULL;
134 }
135 
HandleAuthentication(CTCPClient_ptr c,const std::string & username,const std::string & password)136 bool CTCPServerIntBase::HandleAuthentication(CTCPClient_ptr c, const std::string &username, const std::string &password)
137 {
138 	_tRemoteShareUser *pUser=FindUser(username);
139 	if (pUser==NULL)
140 		return false;
141 
142 	return ((pUser->Username==username)&&(pUser->Password==password));
143 }
144 
DoDecodeMessage(const CTCPClientBase * pClient,const unsigned char * pRXCommand)145 void CTCPServerIntBase::DoDecodeMessage(const CTCPClientBase *pClient, const unsigned char *pRXCommand)
146 {
147 	m_pRoot->DoDecodeMessage(pClient,pRXCommand);
148 }
149 
stopClient(CTCPClient_ptr c)150 void CTCPServerInt::stopClient(CTCPClient_ptr c)
151 {
152 	std::lock_guard<std::mutex> l(connectionMutex);
153 	connections_.erase(c);
154 	c->stop();
155 }
156 
stopAllClients()157 void CTCPServerIntBase::stopAllClients()
158 {
159 	std::lock_guard<std::mutex> l(connectionMutex);
160 	if (connections_.empty())
161 		return;
162 	std::set<CTCPClient_ptr>::const_iterator itt;
163 	for (itt=connections_.begin(); itt!=connections_.end(); ++itt)
164 	{
165 		CTCPClientBase *pClient=itt->get();
166 		if (pClient)
167 			pClient->stop();
168 	}
169 	connections_.clear();
170 }
171 
GetRemoteUsers()172 std::vector<_tRemoteShareUser> CTCPServerIntBase::GetRemoteUsers()
173 {
174 	return m_users;
175 }
176 
SetRemoteUsers(const std::vector<_tRemoteShareUser> & users)177 void CTCPServerIntBase::SetRemoteUsers(const std::vector<_tRemoteShareUser> &users)
178 {
179 	std::lock_guard<std::mutex> l(connectionMutex);
180 	m_users=users;
181 }
182 
GetUserDevicesCount(const std::string & username)183 unsigned int CTCPServerIntBase::GetUserDevicesCount(const std::string &username)
184 {
185 	_tRemoteShareUser *pUser=FindUser(username);
186 	if (pUser==NULL)
187 		return 0;
188 	return (unsigned int) pUser->Devices.size();
189 }
190 
SendToAll(const int,const uint64_t DeviceRowID,const char * pData,size_t Length,const CTCPClientBase * pClient2Ignore)191 void CTCPServerIntBase::SendToAll(const int /*HardwareID*/, const uint64_t DeviceRowID, const char *pData, size_t Length, const CTCPClientBase* pClient2Ignore)
192 {
193 	std::lock_guard<std::mutex> l(connectionMutex);
194 
195 	//do not share Interface Messages
196 	if (
197 		(pData[1]==pTypeInterfaceMessage)||
198 		(pData[1]==pTypeRecXmitMessage)
199 		)
200 		return;
201 
202 	std::set<CTCPClient_ptr>::const_iterator itt;
203 	for (itt=connections_.begin(); itt!=connections_.end(); ++itt)
204 	{
205 		CTCPClientBase *pClient=itt->get();
206 		if (pClient==pClient2Ignore)
207 			continue;
208 
209 		if (pClient)
210 		{
211 			_tRemoteShareUser *pUser=FindUser(pClient->m_username);
212 			if (pUser!=NULL)
213 			{
214 				//check if we are allowed to get this device
215 				bool bOk2Send=false;
216 				if (pUser->Devices.size()==0)
217 					bOk2Send=true;
218 				else
219 				{
220 					int tdevices=pUser->Devices.size();
221 					for (int ii=0; ii<tdevices; ii++)
222 					{
223 						if (pUser->Devices[ii]==DeviceRowID)
224 						{
225 							bOk2Send=true;
226 							break;
227 						}
228 					}
229 				}
230 				if (bOk2Send)
231 					pClient->write(pData,Length);
232 			}
233 		}
234 	}
235 }
236 
CTCPServerInt(const std::string & address,const std::string & port,CTCPServer * pRoot)237 CTCPServerInt::CTCPServerInt(const std::string& address, const std::string& port, CTCPServer *pRoot) :
238 	CTCPServerIntBase(pRoot),
239 	io_service_(),
240 	acceptor_(io_service_)
241 {
242 	// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
243 	boost::asio::ip::tcp::resolver resolver(io_service_);
244 	boost::asio::ip::tcp::resolver::query query(address, port);
245 	boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
246 	acceptor_.open(endpoint.protocol());
247 	acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
248 	acceptor_.bind(endpoint);
249 	acceptor_.listen();
250 
251 	new_connection_ = std::shared_ptr<CTCPClient>(new CTCPClient(io_service_, this));
252 
253 	acceptor_.async_accept(
254 		*(new_connection_->socket()),
255 		boost::bind(&CTCPServerInt::handleAccept, this,
256 			boost::asio::placeholders::error));
257 }
258 
~CTCPServerInt(void)259 CTCPServerInt::~CTCPServerInt(void)
260 {
261 
262 }
263 
264 #ifndef NOCLOUD
265 // our proxied server
CTCPServerProxied(CTCPServer * pRoot,http::server::CProxyClient * proxy)266 CTCPServerProxied::CTCPServerProxied(CTCPServer *pRoot, http::server::CProxyClient *proxy) : CTCPServerIntBase(pRoot)
267 {
268 	m_pProxyClient = proxy;
269 }
270 
~CTCPServerProxied(void)271 CTCPServerProxied::~CTCPServerProxied(void)
272 {
273 }
274 
start()275 void CTCPServerProxied::start()
276 {
277 }
278 
stop()279 void CTCPServerProxied::stop()
280 {
281 	stopAllClients();
282 }
283 
284 /// Stop the specified connection.
stopClient(CTCPClient_ptr c)285 void CTCPServerProxied::stopClient(CTCPClient_ptr c)
286 {
287 	std::lock_guard<std::mutex> l(connectionMutex);
288 	c->stop();
289 	connections_.erase(c);
290 }
291 
OnDisconnect(const std::string & token)292 bool CTCPServerProxied::OnDisconnect(const std::string &token)
293 {
294 	std::set<CTCPClient_ptr>::const_iterator itt;
295 	for (itt = connections_.begin(); itt != connections_.end(); ++itt) {
296 		CSharedClient *pClient = dynamic_cast<CSharedClient *>(itt->get());
297 		if (pClient && pClient->CompareToken(token)) {
298 			pClient->stop();
299 			connections_.erase(itt);
300 			return true;
301 		}
302 	}
303 	return false;
304 }
305 
OnNewConnection(const std::string & token,const std::string & username,const std::string & password)306 bool CTCPServerProxied::OnNewConnection(const std::string &token, const std::string &username, const std::string &password)
307 {
308 	CSharedClient *new_client = new CSharedClient(this, m_pProxyClient, token, username);
309 	CTCPClient_ptr new_connection_ = std::shared_ptr<CSharedClient>(new_client);
310 	if (!HandleAuthentication(new_connection_, username, password)) {
311 		new_connection_.reset(); // deletes new_client
312 		return false;
313 	}
314 	_log.Log(LOG_STATUS, "Incoming Domoticz connection via Proxy accepted for user %s.", username.c_str());
315 	connections_.insert(new_connection_);
316 	new_connection_->start();
317 	new_connection_.reset(); // invalidate dangling pointer
318 	return true;
319 }
320 
OnIncomingData(const std::string & token,const unsigned char * data,size_t bytes_transferred)321 bool CTCPServerProxied::OnIncomingData(const std::string &token, const unsigned char *data, size_t bytes_transferred)
322 {
323 	CSharedClient *client = FindClient(token);
324 	if (client == NULL) {
325 		return false;
326 	}
327 	client->OnIncomingData(data, bytes_transferred);
328 	return true;
329 }
330 
FindClient(const std::string & token)331 CSharedClient *CTCPServerProxied::FindClient(const std::string &token)
332 {
333 	std::set<CTCPClient_ptr>::const_iterator itt;
334 	for (itt = connections_.begin(); itt != connections_.end(); ++itt) {
335 		CSharedClient *pClient = dynamic_cast<CSharedClient *>(itt->get());
336 		if (pClient && pClient->CompareToken(token)) {
337 			return pClient;
338 		}
339 	}
340 	return NULL;
341 }
342 #endif
343 
344 //Out main (wrapper) server
CTCPServer()345 CTCPServer::CTCPServer()
346 {
347 	m_pTCPServer = NULL;
348 #ifndef NOCLOUD
349 	m_pProxyServer = NULL;
350 #endif
351 }
352 
CTCPServer(const int)353 CTCPServer::CTCPServer(const int /*ID*/)
354 {
355 	m_pTCPServer = NULL;
356 #ifndef NOCLOUD
357 	m_pProxyServer = NULL;
358 #endif
359 }
360 
~CTCPServer()361 CTCPServer::~CTCPServer()
362 {
363 	StopServer();
364 #ifndef NOCLOUD
365 	if (m_pProxyServer != NULL) {
366 		m_pProxyServer->stop();
367 		delete m_pProxyServer;
368 		m_pProxyServer = NULL;
369 	}
370 #endif
371 }
372 
StartServer(const std::string & address,const std::string & port)373 bool CTCPServer::StartServer(const std::string &address, const std::string &port)
374 {
375 	int tries = 0;
376 	bool exception = false;
377 	std::string listen_address = address;
378 
379 	do {
380 		try
381 		{
382 			exception = false;
383 			StopServer();
384 			if (m_pTCPServer != NULL) {
385 				_log.Log(LOG_ERROR, "Stopping TCPServer should delete resources !");
386 			}
387 			m_pTCPServer = new CTCPServerInt(listen_address, port, this);
388 		}
389 		catch (std::exception& e)
390 		{
391 			exception = true;
392 			switch (tries) {
393 			case 0:
394 				listen_address = "::";
395 				break;
396 			case 1:
397 				listen_address = "0.0.0.0";
398 				break;
399 			case 2:
400 				_log.Log(LOG_ERROR, "Exception starting shared server: %s", e.what());
401 				return false;
402 			}
403 			tries++;
404 		}
405 	} while (exception);
406 	_log.Log(LOG_NORM, "Starting shared server on: %s:%s", listen_address.c_str(), port.c_str());
407 	//Start worker thread
408 	m_thread = std::make_shared<std::thread>(&CTCPServer::Do_Work, this);
409 	SetThreadName(m_thread->native_handle(), "TCPServer");
410 	return (m_thread != nullptr);
411 }
412 
413 #ifndef NOCLOUD
StartServer(http::server::CProxyClient * proxy)414 bool CTCPServer::StartServer(http::server::CProxyClient *proxy)
415 {
416 	_log.Log(LOG_NORM, "Accepting shared server connections via MyDomotiz (see settings menu).");
417 	m_pProxyServer = new CTCPServerProxied(this, proxy);
418 	// we load the remote users at this point, because this server was not started yet when
419 	// LoadSharedUsers() was called at startup.
420 	if (m_pTCPServer) {
421 		m_pProxyServer->SetRemoteUsers(m_pTCPServer->GetRemoteUsers());
422 	}
423 	else {
424 		m_mainworker.LoadSharedUsers();
425 	}
426 	return true;
427 }
428 #endif
429 
StopServer()430 void CTCPServer::StopServer()
431 {
432 	std::lock_guard<std::mutex> l(m_server_mutex);
433 	if (m_pTCPServer) {
434 		m_pTCPServer->stop();
435 	}
436 	if (m_thread)
437 	{
438 		m_thread->join();
439 		m_thread.reset();
440 	}
441 	// This is the only time to delete it
442 	if (m_pTCPServer) {
443 		delete m_pTCPServer;
444 		m_pTCPServer = NULL;
445 		_log.Log(LOG_STATUS, "TCPServer: shared server stopped");
446 	}
447 #ifndef NOCLOUD
448 	if (m_pProxyServer) {
449 		m_pProxyServer->stop();
450 	}
451 #endif
452 }
453 
Do_Work()454 void CTCPServer::Do_Work()
455 {
456 	if (m_pTCPServer) {
457 		_log.Log(LOG_STATUS, "TCPServer: shared server started...");
458 		m_pTCPServer->start();
459 	}
460 }
461 
SendToAll(const int HardwareID,const uint64_t DeviceRowID,const char * pData,size_t Length,const CTCPClientBase * pClient2Ignore)462 void CTCPServer::SendToAll(const int HardwareID, const uint64_t DeviceRowID, const char *pData, size_t Length, const CTCPClientBase* pClient2Ignore)
463 {
464 	std::lock_guard<std::mutex> l(m_server_mutex);
465 	if (m_pTCPServer)
466 		m_pTCPServer->SendToAll(HardwareID, DeviceRowID, pData, Length, pClient2Ignore);
467 #ifndef NOCLOUD
468 	if (m_pProxyServer)
469 		m_pProxyServer->SendToAll(HardwareID, DeviceRowID, pData, Length, pClient2Ignore);
470 #endif
471 }
472 
SetRemoteUsers(const std::vector<_tRemoteShareUser> & users)473 void CTCPServer::SetRemoteUsers(const std::vector<_tRemoteShareUser> &users)
474 {
475 	std::lock_guard<std::mutex> l(m_server_mutex);
476 	if (m_pTCPServer)
477 		m_pTCPServer->SetRemoteUsers(users);
478 #ifndef NOCLOUD
479 	if (m_pProxyServer)
480 		m_pProxyServer->SetRemoteUsers(users);
481 #endif
482 }
483 
GetUserDevicesCount(const std::string & username)484 unsigned int CTCPServer::GetUserDevicesCount(const std::string &username)
485 {
486 	std::lock_guard<std::mutex> l(m_server_mutex);
487 	if (m_pTCPServer) {
488 		return m_pTCPServer->GetUserDevicesCount(username);
489 	}
490 #ifndef NOCLOUD
491 	else if (m_pProxyServer) {
492 		return m_pProxyServer->GetUserDevicesCount(username);
493 	}
494 #endif
495 	return 0;
496 }
497 
stopAllClients()498 void CTCPServer::stopAllClients()
499 {
500 	if (m_pTCPServer)
501 		m_pTCPServer->stopAllClients();
502 #ifndef NOCLOUD
503 	if (m_pProxyServer)
504 		m_pProxyServer->stopAllClients();
505 #endif
506 }
507 
DoDecodeMessage(const CTCPClientBase * pClient,const unsigned char * pRXCommand)508 void CTCPServer::DoDecodeMessage(const CTCPClientBase *pClient, const unsigned char *pRXCommand)
509 {
510 	HwdType = HTYPE_Domoticz;
511 	m_HwdID=8765;
512 	m_Name="DomoticzFromMaster";
513 	m_SeqNr=1;
514 	m_pUserData=(void*)pClient;
515 	sDecodeRXMessage(this, pRXCommand, NULL, -1);
516 }
517 
518 #ifndef NOCLOUD
GetProxiedServer()519 CTCPServerProxied *CTCPServer::GetProxiedServer()
520 {
521 	return m_pProxyServer;
522 }
523 #endif
524 
525 } // namespace server
526 } // namespace tcp
527