c2b0bb7e2bcf18f82aaff02324532dfb670959a3
[public/netxms.git] / src / libnxcl / comm.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Client Library
4 ** Copyright (C) 2004, 2005 Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License as published by
8 ** the Free Software Foundation; either version 2 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** $module: comm.cpp
21 **
22 **/
23
24 #include "libnxcl.h"
25
26
27 //
28 // Network receiver thread
29 //
30
31 THREAD_RESULT THREAD_CALL NetReceiver(NXCL_Session *pSession)
32 {
33 CSCPMessage *pMsg;
34 CSCP_MESSAGE *pRawMsg;
35 CSCP_BUFFER *pMsgBuffer;
36 BYTE *pDecryptionBuffer = NULL;
37 int i, iErr;
38 BOOL bMsgNotNeeded;
39 TCHAR szBuffer[128];
40
41 // Initialize raw message receiving function
42 pMsgBuffer = (CSCP_BUFFER *)malloc(sizeof(CSCP_BUFFER));
43 RecvCSCPMessage(0, NULL, pMsgBuffer, 0, NULL, NULL, 0);
44
45 // Allocate space for raw message
46 pRawMsg = (CSCP_MESSAGE *)malloc(pSession->m_dwReceiverBufferSize);
47 #ifdef _WITH_ENCRYPTION
48 pDecryptionBuffer = (BYTE *)malloc(pSession->m_dwReceiverBufferSize);
49 #endif
50
51 // Message receiving loop
52 while(1)
53 {
54 // Receive raw message
55 if ((iErr = RecvCSCPMessage(pSession->m_hSocket, pRawMsg,
56 pMsgBuffer, pSession->m_dwReceiverBufferSize,
57 &pSession->m_pCtx, pDecryptionBuffer, INFINITE)) <= 0)
58 break;
59
60 // Check if we get too large message
61 if (iErr == 1)
62 {
63 DebugPrintf(_T("Received too large message %s (%d bytes)"),
64 CSCPMessageCodeName(ntohs(pRawMsg->wCode), szBuffer),
65 ntohl(pRawMsg->dwSize));
66 continue;
67 }
68
69 // Check for decryption errors
70 if (iErr == 2)
71 {
72 DebugPrintf(_T("Message decryption error"));
73 continue;
74 }
75
76 // Check that actual received packet size is equal to encoded in packet
77 if ((int)ntohl(pRawMsg->dwSize) != iErr)
78 {
79 DebugPrintf(_T("RecvMsg: Bad packet length [dwSize=%d ActualSize=%d]"), ntohl(pRawMsg->dwSize), iErr);
80 continue; // Bad packet, wait for next
81 }
82
83 // Create message object from raw message
84 if (IsBinaryMsg(pRawMsg))
85 {
86 // Convert numeric fields to host byte order
87 pRawMsg->wCode = ntohs(pRawMsg->wCode);
88 pRawMsg->wFlags = ntohs(pRawMsg->wFlags);
89 pRawMsg->dwSize = ntohl(pRawMsg->dwSize);
90 pRawMsg->dwId = ntohl(pRawMsg->dwId);
91 pRawMsg->dwNumVars = ntohl(pRawMsg->dwNumVars);
92
93 DebugPrintf(_T("RecvRawMsg(\"%s\", id:%d)"), CSCPMessageCodeName(pRawMsg->wCode, szBuffer), pRawMsg->dwId);
94
95 // Process message
96 switch(pRawMsg->wCode)
97 {
98 case CMD_EVENT:
99 ProcessEvent(pSession, NULL, pRawMsg);
100 break;
101 case CMD_FILE_DATA:
102 MutexLock(pSession->m_mutexFileRq, INFINITE);
103 if ((pSession->m_hCurrFile != -1) && (pSession->m_dwFileRqId == pRawMsg->dwId))
104 {
105 if (write(pSession->m_hCurrFile, pRawMsg->df, pRawMsg->dwNumVars) == (int)pRawMsg->dwNumVars)
106 {
107 if (pRawMsg->wFlags & MF_END_OF_FILE)
108 {
109 close(pSession->m_hCurrFile);
110 pSession->m_dwFileRqCompletion = RCC_SUCCESS;
111 ConditionSet(pSession->m_condFileRq);
112 }
113 }
114 else
115 {
116 // I/O error
117 close(pSession->m_hCurrFile);
118 pSession->m_dwFileRqCompletion = RCC_FILE_IO_ERROR;
119 ConditionSet(pSession->m_condFileRq);
120 }
121 }
122 MutexUnlock(pSession->m_mutexFileRq);
123 break;
124 case CMD_ABORT_FILE_TRANSFER:
125 MutexLock(pSession->m_mutexFileRq, INFINITE);
126 if ((pSession->m_hCurrFile != -1) && (pSession->m_dwFileRqId == pRawMsg->dwId))
127 {
128 // I/O error
129 close(pSession->m_hCurrFile);
130 pSession->m_dwFileRqCompletion = RCC_FILE_IO_ERROR;
131 ConditionSet(pSession->m_condFileRq);
132 }
133 MutexUnlock(pSession->m_mutexFileRq);
134 break;
135 default: // Put unknown raw messages into the wait queue
136 pSession->m_msgWaitQueue.Put((CSCP_MESSAGE *)nx_memdup(pRawMsg, pRawMsg->dwSize));
137 break;
138 }
139 }
140 else
141 {
142 pMsg = new CSCPMessage(pRawMsg);
143 bMsgNotNeeded = TRUE;
144 DebugPrintf(_T("RecvMsg(\"%s\", id:%d)"), CSCPMessageCodeName(pMsg->GetCode(), szBuffer), pMsg->GetId());
145
146 // Process message
147 switch(pMsg->GetCode())
148 {
149 case CMD_KEEPALIVE: // Keepalive message, ignore it
150 pSession->SetTimeStamp(pMsg->GetVariableLong(VID_TIMESTAMP));
151 break;
152 case CMD_REQUEST_SESSION_KEY:
153 if (pSession->m_pCtx == NULL)
154 {
155 CSCPMessage *pResponse;
156
157 SetupEncryptionContext(pMsg, &pSession->m_pCtx, &pResponse, NULL);
158 pSession->SendMsg(pResponse);
159 delete pResponse;
160 }
161 case CMD_OBJECT: // Object information
162 case CMD_OBJECT_UPDATE:
163 case CMD_OBJECT_LIST_END:
164 pSession->ProcessObjectUpdate(pMsg);
165 break;
166 case CMD_EVENT_LIST_END:
167 ProcessEvent(pSession, pMsg, NULL);
168 break;
169 case CMD_SYSLOG_RECORDS:
170 ProcessSyslogRecords(pSession, pMsg);
171 break;
172 case CMD_TRAP_LOG_RECORDS:
173 ProcessTrapLogRecords(pSession, pMsg);
174 break;
175 case CMD_EVENT_DB_RECORD:
176 ProcessEventDBRecord(pSession, pMsg);
177 break;
178 case CMD_USER_DATA:
179 case CMD_GROUP_DATA:
180 case CMD_USER_DB_EOF:
181 pSession->ProcessUserDBRecord(pMsg);
182 break;
183 case CMD_USER_DB_UPDATE:
184 pSession->ProcessUserDBUpdate(pMsg);
185 break;
186 case CMD_NODE_DCI:
187 case CMD_NODE_DCI_LIST_END:
188 pSession->ProcessDCI(pMsg);
189 break;
190 case CMD_ALARM_UPDATE:
191 ProcessAlarmUpdate(pSession, pMsg);
192 break;
193 case CMD_ACTION_DB_UPDATE:
194 ProcessActionUpdate(pSession, pMsg);
195 break;
196 case CMD_NOTIFY:
197 pSession->CallEventHandler(NXC_EVENT_NOTIFICATION,
198 pMsg->GetVariableLong(VID_NOTIFICATION_CODE),
199 (void *)pMsg->GetVariableLong(VID_NOTIFICATION_DATA));
200 break;
201 default:
202 pSession->m_msgWaitQueue.Put(pMsg);
203 bMsgNotNeeded = FALSE;
204 break;
205 }
206 if (bMsgNotNeeded)
207 delete pMsg;
208 }
209 }
210
211 for(i = 0; i < SYNC_OP_COUNT; i++)
212 pSession->CompleteSync(i, RCC_COMM_FAILURE); // Abort active sync operation
213 DebugPrintf(_T("Network receiver thread stopped"));
214 free(pRawMsg);
215 free(pMsgBuffer);
216 #ifdef _WITH_ENCRYPTION
217 free(pDecryptionBuffer);
218 #endif
219
220 // Close socket
221 shutdown(pSession->m_hSocket, SHUT_WR);
222 {
223 char cTmp;
224 while(recv(pSession->m_hSocket, &cTmp, 1, 0) > 0);
225 }
226 shutdown(pSession->m_hSocket, SHUT_RD);
227 closesocket(pSession->m_hSocket);
228 return THREAD_OK;
229 }
230
231
232 //
233 // Connect to server
234 //
235
236 DWORD LIBNXCL_EXPORTABLE NXCConnect(TCHAR *pszServer, TCHAR *pszLogin,
237 TCHAR *pszPassword, NXC_SESSION *phSession,
238 TCHAR *pszClientInfo, BOOL bExactVersionMatch,
239 BOOL bEncrypt)
240 {
241 struct sockaddr_in servAddr;
242 CSCPMessage msg, *pResp;
243 BYTE szPasswordHash[SHA1_DIGEST_SIZE];
244 DWORD dwRetCode = RCC_COMM_FAILURE;
245 SOCKET hSocket;
246 THREAD hThread;
247 TCHAR *pszPort, szBuffer[64], szHostName[128];
248 WORD wPort = SERVER_LISTEN_PORT;
249
250 nx_strncpy(szHostName, pszServer, 128);
251
252 // Check if server given in form host:port
253 pszPort = _tcschr(szHostName, _T(':'));
254 if (pszPort != NULL)
255 {
256 TCHAR *pErr;
257 int nTemp;
258
259 *pszPort = 0;
260 pszPort++;
261 nTemp = _tcstol(pszPort, &pErr, 10);
262 if ((*pErr != 0) || (nTemp < 1) || (nTemp > 65535))
263 return RCC_INVALID_ARGUMENT;
264 wPort = (WORD)nTemp;
265 }
266
267 // Prepare address structure
268 memset(&servAddr, 0, sizeof(struct sockaddr_in));
269 servAddr.sin_family = AF_INET;
270 servAddr.sin_port = htons(wPort);
271 servAddr.sin_addr.s_addr = ResolveHostName(szHostName);;
272
273 if (servAddr.sin_addr.s_addr != INADDR_NONE)
274 {
275 // Create socket
276 if ((hSocket = socket(AF_INET, SOCK_STREAM, 0)) != -1)
277 {
278 // enable TCP_NODELAY
279 //int nVal = 1;
280 //setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, &nVal, sizeof(nVal));
281
282 // Connect to target
283 if (connect(hSocket, (struct sockaddr *)&servAddr, sizeof(struct sockaddr_in)) == 0)
284 {
285 NXCL_Session *pSession;
286
287 // Create new session and start receiver thread
288 pSession = new NXCL_Session;
289 pSession->Attach(hSocket);
290 hThread = ThreadCreateEx((THREAD_RESULT (THREAD_CALL *)(void *))NetReceiver, 0, pSession);
291 if (hThread != INVALID_THREAD_HANDLE)
292 pSession->SetRecvThread(hThread);
293
294 // Query server information
295 msg.SetId(pSession->CreateRqId());
296 msg.SetCode(CMD_GET_SERVER_INFO);
297 if (pSession->SendMsg(&msg))
298 {
299 // Receive response message
300 pResp = pSession->WaitForMessage(CMD_REQUEST_COMPLETED, msg.GetId());
301 if (pResp != NULL)
302 {
303 dwRetCode = pResp->GetVariableLong(VID_RCC);
304 if (dwRetCode == RCC_SUCCESS)
305 {
306 pResp->GetVariableBinary(VID_SERVER_ID, pSession->m_bsServerId, 8);
307 if (bExactVersionMatch)
308 {
309 TCHAR szServerVersion[64];
310
311 pResp->GetVariableStr(VID_SERVER_VERSION, szServerVersion, 64);
312 if (_tcsncmp(szServerVersion, NETXMS_VERSION_STRING, 64))
313 dwRetCode = RCC_VERSION_MISMATCH;
314 }
315 if (pResp->GetVariableLong(VID_PROTOCOL_VERSION) != CLIENT_PROTOCOL_VERSION)
316 dwRetCode = RCC_BAD_PROTOCOL;
317 }
318 delete pResp;
319
320 // Request encryption if needed
321 if ((dwRetCode == RCC_SUCCESS) && bEncrypt)
322 {
323 msg.DeleteAllVariables();
324 msg.SetId(pSession->CreateRqId());
325 msg.SetCode(CMD_REQUEST_ENCRYPTION);
326 if (pSession->SendMsg(&msg))
327 {
328 dwRetCode = pSession->WaitForRCC(msg.GetId());
329 }
330 else
331 {
332 dwRetCode = RCC_COMM_FAILURE;
333 }
334 }
335
336 if (dwRetCode == RCC_SUCCESS)
337 {
338 // Do login if we are requested to do so
339 // Login is not needed for web sessions
340 if (pszLogin != NULL)
341 {
342 // Prepare login message
343 msg.DeleteAllVariables();
344 msg.SetId(pSession->CreateRqId());
345 msg.SetCode(CMD_LOGIN);
346 msg.SetVariable(VID_LOGIN_NAME, pszLogin);
347 #ifdef UNICODE
348 char szMPasswd[64];
349
350 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
351 pszPassword, -1, szMPasswd, sizeof(szMPasswd), NULL, NULL);
352 CalculateSHA1Hash((BYTE *)szMPasswd, strlen(szMPasswd), szPasswordHash);
353 #else
354 CalculateSHA1Hash((BYTE *)pszPassword, strlen(pszPassword), szPasswordHash);
355 #endif
356 msg.SetVariable(VID_PASSWORD, szPasswordHash, SHA1_DIGEST_SIZE);
357 msg.SetVariable(VID_CLIENT_INFO, pszClientInfo);
358 msg.SetVariable(VID_LIBNXCL_VERSION, NETXMS_VERSION_STRING);
359 GetOSVersionString(szBuffer);
360 msg.SetVariable(VID_OS_INFO, szBuffer);
361 if (pSession->SendMsg(&msg))
362 {
363 // Receive response message
364 pResp = pSession->WaitForMessage(CMD_LOGIN_RESP, msg.GetId());
365 if (pResp != NULL)
366 {
367 dwRetCode = pResp->GetVariableLong(VID_RCC);
368 if (dwRetCode == RCC_SUCCESS)
369 pSession->ParseLoginMessage(pResp);
370 delete pResp;
371 }
372 else
373 {
374 // Connection is broken or timed out
375 dwRetCode = RCC_TIMEOUT;
376 }
377 }
378 else
379 {
380 dwRetCode = RCC_COMM_FAILURE;
381 }
382 }
383 }
384 }
385 else
386 {
387 // Connection is broken or timed out
388 dwRetCode = RCC_TIMEOUT;
389 }
390 }
391
392 if (dwRetCode == RCC_SUCCESS)
393 {
394 *phSession = pSession;
395 }
396 else
397 {
398 delete pSession;
399 }
400 }
401 else // connect() failed
402 {
403 closesocket(hSocket);
404 }
405 }
406 }
407
408 if (dwRetCode != RCC_SUCCESS)
409 *phSession = NULL;
410
411 return dwRetCode;
412 }
413
414
415 //
416 // Disconnect from server
417 //
418
419 void LIBNXCL_EXPORTABLE NXCDisconnect(NXC_SESSION hSession)
420 {
421 if (hSession != NULL)
422 {
423 // ((NXCL_Session *)hSession)->Disconnect();
424 delete ((NXCL_Session *)hSession);
425 }
426 }