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