Console compiles on UNIX with UNICODE
[public/netxms.git] / src / libnxcl / comm.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Client Library
4 ** Copyright (C) 2004, 2005, 2006, 2007 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 ** File: 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[256];
40
41 // Initialize raw message receiving function
42 pMsgBuffer = (CSCP_BUFFER *)malloc(sizeof(CSCP_BUFFER));
43 RecvNXCPMessage(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 = RecvNXCPMessage(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 NXCPMessageCodeName(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)"), NXCPMessageCodeName(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)"), NXCPMessageCodeName(pMsg->GetCode(), szBuffer), pMsg->GetId());
145
146 // Process message
147 switch(pMsg->GetCode())
148 {
149 case CMD_KEEPALIVE: // Keepalive message
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, NXCP_VERSION);
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->OnNotify(pMsg);
198 break;
199 default:
200 pSession->m_msgWaitQueue.Put(pMsg);
201 bMsgNotNeeded = FALSE;
202 break;
203 }
204 if (bMsgNotNeeded)
205 delete pMsg;
206 }
207 }
208
209 for(i = 0; i < SYNC_OP_COUNT; i++)
210 pSession->CompleteSync(i, RCC_COMM_FAILURE); // Abort active sync operation
211 DebugPrintf(_T("Network receiver thread stopped"));
212 free(pRawMsg);
213 free(pMsgBuffer);
214 #ifdef _WITH_ENCRYPTION
215 free(pDecryptionBuffer);
216 #endif
217
218 // Close socket
219 shutdown(pSession->m_hSocket, SHUT_WR);
220 {
221 char cTmp;
222 while(recv(pSession->m_hSocket, &cTmp, 1, 0) > 0);
223 }
224 shutdown(pSession->m_hSocket, SHUT_RD);
225 closesocket(pSession->m_hSocket);
226 return THREAD_OK;
227 }
228
229
230 //
231 // Connect to server
232 //
233
234 DWORD LIBNXCL_EXPORTABLE NXCConnect(DWORD dwFlags, const TCHAR *pszServer, const TCHAR *pszLogin,
235 const TCHAR *pszPassword, DWORD dwCertLen,
236 BOOL (* pfSign)(BYTE *, DWORD, BYTE *, DWORD *, void *),
237 void *pSignArg, NXC_SESSION *phSession, const TCHAR *pszClientInfo,
238 TCHAR **ppszUpgradeURL)
239 {
240 struct sockaddr_in servAddr;
241 CSCPMessage msg, *pResp;
242 DWORD dwRetCode = RCC_COMM_FAILURE;
243 SOCKET hSocket;
244 THREAD hThread;
245 TCHAR *pszPort, szBuffer[64], szHostName[128];
246 BYTE challenge[CLIENT_CHALLENGE_SIZE];
247 WORD wPort = SERVER_LISTEN_PORT;
248
249 nx_strncpy(szHostName, pszServer, 128);
250
251 if (ppszUpgradeURL != NULL)
252 *ppszUpgradeURL = NULL;
253
254 // Check if server given in form host:port
255 pszPort = _tcschr(szHostName, _T(':'));
256 if (pszPort != NULL)
257 {
258 TCHAR *pErr;
259 int nTemp;
260
261 *pszPort = 0;
262 pszPort++;
263 nTemp = _tcstol(pszPort, &pErr, 10);
264 if ((*pErr != 0) || (nTemp < 1) || (nTemp > 65535))
265 return RCC_INVALID_ARGUMENT;
266 wPort = (WORD)nTemp;
267 }
268
269 // Prepare address structure
270 memset(&servAddr, 0, sizeof(struct sockaddr_in));
271 servAddr.sin_family = AF_INET;
272 servAddr.sin_port = htons(wPort);
273 servAddr.sin_addr.s_addr = ResolveHostName(szHostName);
274
275 if (servAddr.sin_addr.s_addr != INADDR_NONE)
276 {
277 // Create socket
278 if ((hSocket = socket(AF_INET, SOCK_STREAM, 0)) != -1)
279 {
280 // enable TCP_NODELAY
281 //int nVal = 1;
282 //setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, &nVal, sizeof(nVal));
283
284 // Connect to target
285 if (connect(hSocket, (struct sockaddr *)&servAddr, sizeof(struct sockaddr_in)) == 0)
286 {
287 NXCL_Session *pSession;
288
289 // Create new session and start receiver thread
290 pSession = new NXCL_Session;
291 pSession->Attach(hSocket);
292 hThread = ThreadCreateEx((THREAD_RESULT (THREAD_CALL *)(void *))NetReceiver, 0, pSession);
293 if (hThread != INVALID_THREAD_HANDLE)
294 pSession->SetRecvThread(hThread);
295
296 // Query server information
297 msg.SetId(pSession->CreateRqId());
298 msg.SetCode(CMD_GET_SERVER_INFO);
299 if (pSession->SendMsg(&msg))
300 {
301 // Receive response message
302 pResp = pSession->WaitForMessage(CMD_REQUEST_COMPLETED, msg.GetId());
303 if (pResp != NULL)
304 {
305 dwRetCode = pResp->GetVariableLong(VID_RCC);
306 if (dwRetCode == RCC_SUCCESS)
307 {
308 pResp->GetVariableBinary(VID_SERVER_ID, pSession->m_bsServerId, 8);
309 if (dwFlags & NXCF_EXACT_VERSION_MATCH)
310 {
311 TCHAR szServerVersion[64];
312
313 pResp->GetVariableStr(VID_SERVER_VERSION, szServerVersion, 64);
314 if (_tcsncmp(szServerVersion, NETXMS_VERSION_STRING, 64))
315 dwRetCode = RCC_VERSION_MISMATCH;
316 }
317 if (pResp->GetVariableLong(VID_PROTOCOL_VERSION) != CLIENT_PROTOCOL_VERSION)
318 dwRetCode = RCC_BAD_PROTOCOL;
319 if (ppszUpgradeURL != NULL)
320 *ppszUpgradeURL = pResp->GetVariableStr(VID_CONSOLE_UPGRADE_URL);
321 pResp->GetVariableBinary(VID_CHALLENGE, challenge, CLIENT_CHALLENGE_SIZE);
322 }
323 delete pResp;
324
325 // Request encryption if needed
326 if ((dwRetCode == RCC_SUCCESS) && (dwFlags & NXCF_ENCRYPT))
327 {
328 msg.DeleteAllVariables();
329 msg.SetId(pSession->CreateRqId());
330 msg.SetCode(CMD_REQUEST_ENCRYPTION);
331 if (pSession->SendMsg(&msg))
332 {
333 dwRetCode = pSession->WaitForRCC(msg.GetId());
334 }
335 else
336 {
337 dwRetCode = RCC_COMM_FAILURE;
338 }
339 }
340
341 if (dwRetCode == RCC_SUCCESS)
342 {
343 // Prepare login message
344 msg.DeleteAllVariables();
345 msg.SetId(pSession->CreateRqId());
346 msg.SetCode(CMD_LOGIN);
347 msg.SetVariable(VID_LOGIN_NAME, pszLogin);
348 if (dwFlags & NXCF_USE_CERTIFICATE)
349 {
350 BYTE signature[256];
351 DWORD dwSigLen;
352
353 dwSigLen = 256;
354 if (!pfSign(challenge, CLIENT_CHALLENGE_SIZE, signature, &dwSigLen, pSignArg))
355 {
356 dwRetCode = RCC_LOCAL_CRYPTO_ERROR;
357 goto crypto_error;
358 }
359 msg.SetVariable(VID_SIGNATURE, signature, dwSigLen);
360 msg.SetVariable(VID_CERTIFICATE, (BYTE *)pszPassword, dwCertLen);
361 msg.SetVariable(VID_AUTH_TYPE, (WORD)NETXMS_AUTH_TYPE_CERTIFICATE);
362 }
363 else
364 {
365 msg.SetVariable(VID_PASSWORD, pszPassword);
366 msg.SetVariable(VID_AUTH_TYPE, (WORD)NETXMS_AUTH_TYPE_PASSWORD);
367 }
368 msg.SetVariable(VID_CLIENT_INFO, pszClientInfo);
369 msg.SetVariable(VID_LIBNXCL_VERSION, NETXMS_VERSION_STRING);
370 GetOSVersionString(szBuffer, 64);
371 msg.SetVariable(VID_OS_INFO, szBuffer);
372 if (pSession->SendMsg(&msg))
373 {
374 // Receive response message
375 pResp = pSession->WaitForMessage(CMD_LOGIN_RESP, msg.GetId());
376 if (pResp != NULL)
377 {
378 dwRetCode = pResp->GetVariableLong(VID_RCC);
379 if (dwRetCode == RCC_SUCCESS)
380 pSession->ParseLoginMessage(pResp);
381 delete pResp;
382 }
383 else
384 {
385 // Connection is broken or timed out
386 dwRetCode = RCC_TIMEOUT;
387 }
388 }
389 else
390 {
391 dwRetCode = RCC_COMM_FAILURE;
392 }
393 crypto_error:
394 ;
395 }
396 }
397 else
398 {
399 // Connection is broken or timed out
400 dwRetCode = RCC_TIMEOUT;
401 }
402 }
403
404 if (dwRetCode == RCC_SUCCESS)
405 {
406 *phSession = pSession;
407 }
408 else
409 {
410 delete pSession;
411 }
412 }
413 else // connect() failed
414 {
415 closesocket(hSocket);
416 }
417 }
418 }
419
420 if (dwRetCode != RCC_SUCCESS)
421 *phSession = NULL;
422
423 return dwRetCode;
424 }
425
426
427 //
428 // Disconnect from server
429 //
430
431 void LIBNXCL_EXPORTABLE NXCDisconnect(NXC_SESSION hSession)
432 {
433 if (hSession != NULL)
434 {
435 // ((NXCL_Session *)hSession)->Disconnect();
436 delete ((NXCL_Session *)hSession);
437 }
438 }