819292f3defd10c094196ba1efccc94bc68c428a
[public/netxms.git] / src / server / core / client.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2016 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: client.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25 /**
26 * Static data
27 */
28 static ClientSession *m_pSessionList[MAX_CLIENT_SESSIONS];
29 static RWLOCK m_rwlockSessionListAccess;
30
31 /**
32 * Register new session in list
33 */
34 static BOOL RegisterClientSession(ClientSession *pSession)
35 {
36 UINT32 i;
37
38 RWLockWriteLock(m_rwlockSessionListAccess, INFINITE);
39 for(i = 0; i < MAX_CLIENT_SESSIONS; i++)
40 if (m_pSessionList[i] == NULL)
41 {
42 m_pSessionList[i] = pSession;
43 pSession->setId(i);
44 RWLockUnlock(m_rwlockSessionListAccess);
45 return TRUE;
46 }
47
48 RWLockUnlock(m_rwlockSessionListAccess);
49 nxlog_write(MSG_TOO_MANY_SESSIONS, EVENTLOG_WARNING_TYPE, NULL);
50 return FALSE;
51 }
52
53 /**
54 * Unregister session
55 */
56 void UnregisterClientSession(int id)
57 {
58 RWLockWriteLock(m_rwlockSessionListAccess, INFINITE);
59 m_pSessionList[id] = NULL;
60 RWLockUnlock(m_rwlockSessionListAccess);
61 }
62
63 /**
64 * Keep-alive thread
65 */
66 static THREAD_RESULT THREAD_CALL ClientKeepAliveThread(void *)
67 {
68 int i, iSleepTime;
69 NXCPMessage msg;
70
71 // Read configuration
72 iSleepTime = ConfigReadInt(_T("KeepAliveInterval"), 60);
73
74 // Prepare keepalive message
75 msg.setCode(CMD_KEEPALIVE);
76 msg.setId(0);
77
78 while(1)
79 {
80 if (SleepAndCheckForShutdown(iSleepTime))
81 break;
82
83 msg.setField(VID_TIMESTAMP, (UINT32)time(NULL));
84 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
85 for(i = 0; i < MAX_CLIENT_SESSIONS; i++)
86 if (m_pSessionList[i] != NULL)
87 if (m_pSessionList[i]->isAuthenticated())
88 m_pSessionList[i]->postMessage(&msg);
89 RWLockUnlock(m_rwlockSessionListAccess);
90 }
91
92 DbgPrintf(1, _T("Client keep-alive thread terminated"));
93 return THREAD_OK;
94 }
95
96 /**
97 * Initialize client listener(s)
98 */
99 void InitClientListeners()
100 {
101 // Create session list access rwlock
102 m_rwlockSessionListAccess = RWLockCreate();
103
104 // Start client keep-alive thread
105 ThreadCreate(ClientKeepAliveThread, 0, NULL);
106 }
107
108 /**
109 * Listener thread
110 */
111 THREAD_RESULT THREAD_CALL ClientListener(void *arg)
112 {
113 SOCKET sock, sockClient;
114 struct sockaddr_in servAddr;
115 int errorCount = 0;
116 socklen_t iSize;
117 WORD wListenPort;
118 ClientSession *pSession;
119
120 // Read configuration
121 wListenPort = (WORD)ConfigReadInt(_T("ClientListenerPort"), SERVER_LISTEN_PORT_FOR_CLIENTS);
122
123 // Create socket
124 if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
125 {
126 nxlog_write(MSG_SOCKET_FAILED, EVENTLOG_ERROR_TYPE, "s", _T("ClientListener"));
127 return THREAD_OK;
128 }
129
130 SetSocketExclusiveAddrUse(sock);
131 SetSocketReuseFlag(sock);
132 #ifndef _WIN32
133 fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC);
134 #endif
135
136 // Fill in local address structure
137 memset(&servAddr, 0, sizeof(struct sockaddr_in));
138 servAddr.sin_family = AF_INET;
139 servAddr.sin_addr.s_addr = !_tcscmp(g_szListenAddress, _T("*")) ? 0 : htonl(InetAddress::resolveHostName(g_szListenAddress, AF_INET).getAddressV4());
140 servAddr.sin_port = htons(wListenPort);
141
142 // Bind socket
143 if (bind(sock, (struct sockaddr *)&servAddr, sizeof(struct sockaddr_in)) != 0)
144 {
145 nxlog_write(MSG_BIND_ERROR, EVENTLOG_ERROR_TYPE, "dse", wListenPort, _T("ClientListener"), WSAGetLastError());
146 closesocket(sock);
147 /* TODO: we should initiate shutdown procedure here */
148 return THREAD_OK;
149 }
150
151 // Set up queue
152 listen(sock, SOMAXCONN);
153 nxlog_write(MSG_LISTENING_FOR_CLIENTS, EVENTLOG_INFORMATION_TYPE, "ad", ntohl(servAddr.sin_addr.s_addr), wListenPort);
154
155 // Wait for connection requests
156 while(!IsShutdownInProgress())
157 {
158 iSize = sizeof(struct sockaddr_in);
159 if ((sockClient = accept(sock, (struct sockaddr *)&servAddr, &iSize)) == -1)
160 {
161 int error;
162
163 #ifdef _WIN32
164 error = WSAGetLastError();
165 if (error != WSAEINTR)
166 #else
167 error = errno;
168 if (error != EINTR)
169 #endif
170 nxlog_write(MSG_ACCEPT_ERROR, NXLOG_ERROR, "e", error);
171 errorCount++;
172 if (errorCount > 1000)
173 {
174 nxlog_write(MSG_TOO_MANY_ACCEPT_ERRORS, NXLOG_WARNING, NULL);
175 errorCount = 0;
176 }
177 ThreadSleepMs(500);
178 continue;
179 }
180
181 errorCount = 0; // Reset consecutive errors counter
182 SetSocketNonBlocking(sockClient);
183
184 // Create new session structure and threads
185 pSession = new ClientSession(sockClient, (struct sockaddr *)&servAddr);
186 if (!RegisterClientSession(pSession))
187 {
188 delete pSession;
189 }
190 else
191 {
192 pSession->run();
193 }
194 }
195
196 closesocket(sock);
197 return THREAD_OK;
198 }
199
200 #ifdef WITH_IPV6
201
202 /**
203 * Listener thread - IPv6
204 */
205 THREAD_RESULT THREAD_CALL ClientListenerIPv6(void *arg)
206 {
207 SOCKET sock, sockClient;
208 struct sockaddr_in6 servAddr;
209 int errorCount = 0;
210 socklen_t iSize;
211 WORD wListenPort;
212 ClientSession *pSession;
213
214 // Read configuration
215 wListenPort = (WORD)ConfigReadInt(_T("ClientListenerPort"), SERVER_LISTEN_PORT_FOR_CLIENTS);
216
217 // Create socket
218 if ((sock = socket(AF_INET6, SOCK_STREAM, 0)) == INVALID_SOCKET)
219 {
220 nxlog_write(MSG_SOCKET_FAILED, EVENTLOG_ERROR_TYPE, "s", _T("ClientListenerIPv6"));
221 return THREAD_OK;
222 }
223
224 SetSocketExclusiveAddrUse(sock);
225 SetSocketReuseFlag(sock);
226 #ifdef IPV6_V6ONLY
227 int on = 1;
228 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, sizeof(int));
229 #endif
230 #ifndef _WIN32
231 fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC);
232 #endif
233
234 // Fill in local address structure
235 memset(&servAddr, 0, sizeof(struct sockaddr_in6));
236 servAddr.sin6_family = AF_INET6;
237 if (_tcscmp(g_szListenAddress, _T("*")))
238 memcpy(servAddr.sin6_addr.s6_addr, InetAddress::resolveHostName(g_szListenAddress, AF_INET6).getAddressV6(), 16);
239 servAddr.sin6_port = htons(wListenPort);
240
241 // Bind socket
242 if (bind(sock, (struct sockaddr *)&servAddr, sizeof(struct sockaddr_in6)) != 0)
243 {
244 nxlog_write(MSG_BIND_ERROR, EVENTLOG_ERROR_TYPE, "dse", wListenPort, _T("ClientListenerIPv6"), WSAGetLastError());
245 closesocket(sock);
246 /* TODO: we should initiate shutdown procedure here */
247 return THREAD_OK;
248 }
249
250 // Set up queue
251 listen(sock, SOMAXCONN);
252 nxlog_write(MSG_LISTENING_FOR_CLIENTS, EVENTLOG_INFORMATION_TYPE, "Hd", servAddr.sin6_addr.s6_addr, wListenPort);
253
254 // Wait for connection requests
255 while(!IsShutdownInProgress())
256 {
257 iSize = sizeof(struct sockaddr_in6);
258 if ((sockClient = accept(sock, (struct sockaddr *)&servAddr, &iSize)) == -1)
259 {
260 int error;
261
262 #ifdef _WIN32
263 error = WSAGetLastError();
264 if (error != WSAEINTR)
265 #else
266 error = errno;
267 if (error != EINTR)
268 #endif
269 nxlog_write(MSG_ACCEPT_ERROR, EVENTLOG_ERROR_TYPE, "e", error);
270 errorCount++;
271 if (errorCount > 1000)
272 {
273 nxlog_write(MSG_TOO_MANY_ACCEPT_ERRORS, EVENTLOG_WARNING_TYPE, NULL);
274 errorCount = 0;
275 }
276 ThreadSleepMs(500);
277 continue;
278 }
279
280 errorCount = 0; // Reset consecutive errors counter
281 SetSocketNonBlocking(sockClient);
282
283 // Create new session structure and threads
284 pSession = new ClientSession(sockClient, (struct sockaddr *)&servAddr);
285 if (!RegisterClientSession(pSession))
286 {
287 delete pSession;
288 }
289 else
290 {
291 pSession->run();
292 }
293 }
294
295 closesocket(sock);
296 return THREAD_OK;
297 }
298
299 #endif
300
301 /**
302 * Dump client sessions to screen
303 */
304 void DumpClientSessions(CONSOLE_CTX pCtx)
305 {
306 int i, iCount;
307 TCHAR szBuffer[256];
308 static const TCHAR *pszStateName[] = { _T("init"), _T("idle"), _T("processing") };
309 static const TCHAR *pszCipherName[] = { _T("NONE"), _T("AES-256"), _T("BF-256"), _T("IDEA"), _T("3DES"), _T("AES-128"), _T("BF-128") };
310 static const TCHAR *pszClientType[] = { _T("DESKTOP"), _T("WEB"), _T("MOBILE"), _T("TABLET"), _T("APP") };
311
312 ConsolePrintf(pCtx, _T("ID STATE CIPHER CLTYPE USER [CLIENT]\n"));
313 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
314 for(i = 0, iCount = 0; i < MAX_CLIENT_SESSIONS; i++)
315 if (m_pSessionList[i] != NULL)
316 {
317 TCHAR webServer[256] = _T("");
318 if (m_pSessionList[i]->getClientType() == CLIENT_TYPE_WEB)
319 {
320 _sntprintf(webServer, 256, _T(" (%s)"), m_pSessionList[i]->getWebServerAddress());
321 }
322 ConsolePrintf(pCtx, _T("%-3d %-24s %-8s %-7s %s%s [%s]\n"), i,
323 (m_pSessionList[i]->getState() != SESSION_STATE_PROCESSING) ?
324 pszStateName[m_pSessionList[i]->getState()] :
325 NXCPMessageCodeName(m_pSessionList[i]->getCurrentCmd(), szBuffer),
326 pszCipherName[m_pSessionList[i]->getCipher() + 1],
327 pszClientType[m_pSessionList[i]->getClientType()],
328 m_pSessionList[i]->getSessionName(), webServer,
329 m_pSessionList[i]->getClientInfo());
330 iCount++;
331 }
332 RWLockUnlock(m_rwlockSessionListAccess);
333 ConsolePrintf(pCtx, _T("\n%d active session%s\n\n"), iCount, iCount == 1 ? _T("") : _T("s"));
334 }
335
336 /**
337 * Kill client session
338 */
339 bool NXCORE_EXPORTABLE KillClientSession(int id)
340 {
341 bool success = false;
342 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
343 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
344 {
345 if ((m_pSessionList[i] != NULL) && (m_pSessionList[i]->getId() == id))
346 {
347 m_pSessionList[i]->kill();
348 success = true;
349 break;
350 }
351 }
352 RWLockUnlock(m_rwlockSessionListAccess);
353 return success;
354 }
355
356 /**
357 * Enumerate active sessions
358 */
359 void NXCORE_EXPORTABLE EnumerateClientSessions(void (*pHandler)(ClientSession *, void *), void *pArg)
360 {
361 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
362 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
363 {
364 if ((m_pSessionList[i] != NULL) && !m_pSessionList[i]->isTerminated())
365 {
366 pHandler(m_pSessionList[i], pArg);
367 }
368 }
369 RWLockUnlock(m_rwlockSessionListAccess);
370 }
371
372 /**
373 * Send user database update notification to all clients
374 */
375 void SendUserDBUpdate(int code, UINT32 id, UserDatabaseObject *object)
376 {
377 NXCPMessage msg;
378 msg.setCode(CMD_USER_DB_UPDATE);
379 msg.setId(0);
380 msg.setField(VID_UPDATE_TYPE, (WORD)code);
381 switch(code)
382 {
383 case USER_DB_CREATE:
384 case USER_DB_MODIFY:
385 object->fillMessage(&msg);
386 break;
387 default:
388 msg.setField(VID_USER_ID, id);
389 break;
390 }
391
392 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
393 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
394 if ((m_pSessionList[i] != NULL) &&
395 m_pSessionList[i]->isAuthenticated() &&
396 !m_pSessionList[i]->isTerminated() &&
397 m_pSessionList[i]->isSubscribedTo(NXC_CHANNEL_USERDB))
398 m_pSessionList[i]->postMessage(&msg);
399 RWLockUnlock(m_rwlockSessionListAccess);
400 }
401
402 /**
403 * Send graph update to all active sessions
404 */
405 void NXCORE_EXPORTABLE NotifyClientGraphUpdate(NXCPMessage *update, UINT32 graphId)
406 {
407 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
408 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
409 if ((m_pSessionList[i] != NULL) &&
410 m_pSessionList[i]->isAuthenticated() &&
411 !m_pSessionList[i]->isTerminated() &&
412 (GetGraphAccessCheckResult(graphId, m_pSessionList[i]->getUserId()) == RCC_SUCCESS))
413 m_pSessionList[i]->postMessage(update);
414 RWLockUnlock(m_rwlockSessionListAccess);
415 }
416
417 /**
418 * Send notification to all active user sessions
419 */
420 void NXCORE_EXPORTABLE NotifyClientSessions(UINT32 dwCode, UINT32 dwData)
421 {
422 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
423 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
424 {
425 if ((m_pSessionList[i] != NULL) &&
426 m_pSessionList[i]->isAuthenticated() &&
427 !m_pSessionList[i]->isTerminated())
428 {
429 m_pSessionList[i]->notify(dwCode, dwData);
430 }
431 }
432 RWLockUnlock(m_rwlockSessionListAccess);
433 }
434
435 /**
436 * Send notification to specified user session
437 */
438 void NXCORE_EXPORTABLE NotifyClientSession(UINT32 sessionId, UINT32 dwCode, UINT32 dwData)
439 {
440 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
441 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
442 if ((m_pSessionList[i] != NULL) && (m_pSessionList[i]->getId() == sessionId))
443 m_pSessionList[i]->notify(dwCode, dwData);
444 RWLockUnlock(m_rwlockSessionListAccess);
445 }
446
447 /**
448 * Get number of active user sessions
449 */
450 int GetSessionCount(bool includeSystemAccount)
451 {
452 int i, nCount;
453
454 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
455 for(i = 0, nCount = 0; i < MAX_CLIENT_SESSIONS; i++)
456 {
457 if ((m_pSessionList[i] != NULL) &&
458 (includeSystemAccount || (m_pSessionList[i]->getUserId() != 0)))
459 {
460 nCount++;
461 }
462 }
463 RWLockUnlock(m_rwlockSessionListAccess);
464 return nCount;
465 }
466
467 /**
468 * Check if given user is currenly logged in
469 */
470 bool IsLoggedIn(UINT32 dwUserId)
471 {
472 bool result = false;
473 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
474 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
475 if (m_pSessionList[i] != NULL && m_pSessionList[i]->getUserId() == dwUserId)
476 {
477 result = true;
478 break;
479 }
480 RWLockUnlock(m_rwlockSessionListAccess);
481 return result;
482 }
483
484 /**
485 * Close all user's sessions except given one
486 */
487 void CloseOtherSessions(UINT32 userId, UINT32 thisSession)
488 {
489 RWLockReadLock(m_rwlockSessionListAccess, INFINITE);
490 for(int i = 0; i < MAX_CLIENT_SESSIONS; i++)
491 {
492 if ((m_pSessionList[i] != NULL) &&
493 (m_pSessionList[i]->getUserId() == userId) &&
494 (m_pSessionList[i]->getId() != thisSession))
495 {
496 nxlog_debug(4, _T("CloseOtherSessions(%d,%d): disconnecting session %d"), userId, thisSession, m_pSessionList[i]->getId());
497 m_pSessionList[i]->kill();
498 }
499 }
500 RWLockUnlock(m_rwlockSessionListAccess);
501 }