fixed long tap bug
[public/netxms.git] / src / server / libnxsrv / agent.cpp
CommitLineData
cc022855 1/*
5039dede
AK
2** NetXMS - Network Management System
3** Server Library
0f506caa 4** Copyright (C) 2003-2013 Victor Kirhenshtein
5039dede
AK
5**
6** This program is free software; you can redistribute it and/or modify
0702ed69
VK
7** it under the terms of the GNU Lesser General Public License as published by
8** the Free Software Foundation; either version 3 of the License, or
5039dede
AK
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**
0702ed69 16** You should have received a copy of the GNU Lesser General Public License
5039dede
AK
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: agent.cpp
21**
22**/
23
24#include "libnxsrv.h"
25#include <stdarg.h>
26
f00612ba
VK
27#ifdef _WIN32
28#define open _open
29#define close _close
30#define write _write
8c1befb6 31#else
ff198273 32#define _tell(f) lseek(f,0,SEEK_CUR)
f00612ba
VK
33#endif
34
a3050773
VK
35/**
36 * Constants
37 */
8f4f648d 38#define MAX_MSG_SIZE 8388608
5039dede 39
a3050773
VK
40/**
41 * Static data
42 */
5039dede
AK
43#ifdef _WITH_ENCRYPTION
44static int m_iDefaultEncryptionPolicy = ENCRYPTION_ALLOWED;
45#else
46static int m_iDefaultEncryptionPolicy = ENCRYPTION_DISABLED;
47#endif
48
a3050773
VK
49/**
50 * Set default encryption policy for agent communication
51 */
5039dede
AK
52void LIBNXSRV_EXPORTABLE SetAgentDEP(int iPolicy)
53{
54#ifdef _WITH_ENCRYPTION
55 m_iDefaultEncryptionPolicy = iPolicy;
56#endif
57}
58
a3050773
VK
59/**
60 * Receiver thread starter
61 */
62THREAD_RESULT THREAD_CALL AgentConnection::receiverThreadStarter(void *pArg)
5039dede 63{
a3050773 64 ((AgentConnection *)pArg)->receiverThread();
5039dede
AK
65 return THREAD_OK;
66}
67
a3050773
VK
68/**
69 * Constructor for AgentConnection
70 */
69c6604d 71AgentConnection::AgentConnection(UINT32 ipAddr, WORD port, int authMethod, const TCHAR *secret)
5039dede 72{
69c6604d
VK
73 m_dwAddr = ipAddr;
74 m_wPort = port;
75 m_iAuthMethod = authMethod;
76 if (secret != NULL)
5039dede
AK
77 {
78#ifdef UNICODE
69c6604d 79 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, secret, -1, m_szSecret, MAX_SECRET_LENGTH, NULL, NULL);
08b214c6 80 m_szSecret[MAX_SECRET_LENGTH - 1] = 0;
5039dede 81#else
69c6604d 82 nx_strncpy(m_szSecret, secret, MAX_SECRET_LENGTH);
5039dede
AK
83#endif
84 }
85 else
86 {
87 m_szSecret[0] = 0;
88 }
89 m_hSocket = -1;
90 m_tLastCommandTime = 0;
91 m_dwNumDataLines = 0;
92 m_ppDataLines = NULL;
93 m_pMsgWaitQueue = new MsgWaitQueue;
94 m_dwRequestId = 1;
7c521895 95 m_connectionTimeout = 30000; // 30 seconds
5039dede
AK
96 m_dwCommandTimeout = 10000; // Default timeout 10 seconds
97 m_bIsConnected = FALSE;
98 m_mutexDataLock = MutexCreate();
d3a7cf4c 99 m_mutexSocketWrite = MutexCreate();
5039dede
AK
100 m_hReceiverThread = INVALID_THREAD_HANDLE;
101 m_pCtx = NULL;
102 m_iEncryptionPolicy = m_iDefaultEncryptionPolicy;
103 m_bUseProxy = FALSE;
104 m_dwRecvTimeout = 420000; // 7 minutes
105 m_nProtocolVersion = NXCP_VERSION;
106 m_hCurrFile = -1;
76b4edb5 107 m_deleteFileOnDownloadFailure = true;
bb85e341 108 m_condFileDownload = ConditionCreate(TRUE);
4685a2ad 109 m_fileUploadInProgress = false;
9630fcd1 110 m_sendToClientMessageCallback = NULL;
5039dede
AK
111}
112
a3050773
VK
113/**
114 * Destructor
115 */
5039dede
AK
116AgentConnection::~AgentConnection()
117{
118 // Disconnect from peer
7c521895 119 disconnect();
5039dede
AK
120
121 // Wait for receiver thread termination
122 ThreadJoin(m_hReceiverThread);
cc022855 123
5039dede 124 // Close socket if active
a3050773 125 lock();
5039dede
AK
126 if (m_hSocket != -1)
127 {
128 closesocket(m_hSocket);
129 m_hSocket = -1;
130 }
a3050773 131 unlock();
5039dede 132
a3050773 133 lock();
7c521895 134 destroyResultData();
a3050773 135 unlock();
5039dede
AK
136
137 delete m_pMsgWaitQueue;
98abc9f1
VK
138 if (m_pCtx != NULL)
139 m_pCtx->decRefCount();
5039dede
AK
140
141 if (m_hCurrFile != -1)
9f6d453a 142 {
5039dede 143 close(m_hCurrFile);
f480bdd4 144 onFileDownload(FALSE);
9f6d453a 145 }
5039dede
AK
146
147 MutexDestroy(m_mutexDataLock);
d3a7cf4c 148 MutexDestroy(m_mutexSocketWrite);
bb85e341 149 ConditionDestroy(m_condFileDownload);
5039dede
AK
150}
151
a3050773
VK
152/**
153 * Print message. This method is virtual and can be overrided in
154 * derived classes. Default implementation will print message to stdout.
155 */
af21affe 156void AgentConnection::printMsg(const TCHAR *format, ...)
5039dede
AK
157{
158 va_list args;
159
af21affe
VK
160 va_start(args, format);
161 _vtprintf(format, args);
5039dede
AK
162 va_end(args);
163 _tprintf(_T("\n"));
164}
165
a3050773
VK
166/**
167 * Receiver thread
168 */
169void AgentConnection::receiverThread()
5039dede 170{
967893bb 171 UINT32 msgBufferSize = 1024;
5039dede
AK
172 CSCPMessage *pMsg;
173 CSCP_MESSAGE *pRawMsg;
174 CSCP_BUFFER *pMsgBuffer;
175 BYTE *pDecryptionBuffer = NULL;
4685a2ad 176 int error;
5039dede
AK
177 TCHAR szBuffer[128], szIpAddr[16];
178 SOCKET nSocket;
179
180 // Initialize raw message receiving function
181 pMsgBuffer = (CSCP_BUFFER *)malloc(sizeof(CSCP_BUFFER));
182 RecvNXCPMessage(0, NULL, pMsgBuffer, 0, NULL, NULL, 0);
183
184 // Allocate space for raw message
8f4f648d 185 pRawMsg = (CSCP_MESSAGE *)malloc(msgBufferSize);
5039dede 186#ifdef _WITH_ENCRYPTION
8f4f648d 187 pDecryptionBuffer = (BYTE *)malloc(msgBufferSize);
5039dede
AK
188#endif
189
190 // Message receiving loop
191 while(1)
192 {
8f4f648d
VK
193 // Shrink buffer after receiving large message
194 if (msgBufferSize > 131072)
195 {
196 msgBufferSize = 131072;
197 pRawMsg = (CSCP_MESSAGE *)realloc(pRawMsg, msgBufferSize);
198 if (pDecryptionBuffer != NULL)
199 pDecryptionBuffer = (BYTE *)realloc(pDecryptionBuffer, msgBufferSize);
200 }
201
5039dede 202 // Receive raw message
a3050773 203 lock();
5039dede 204 nSocket = m_hSocket;
a3050773 205 unlock();
8f4f648d
VK
206 if ((error = RecvNXCPMessageEx(nSocket, &pRawMsg, pMsgBuffer, &msgBufferSize,
207 &m_pCtx, (pDecryptionBuffer != NULL) ? &pDecryptionBuffer : NULL,
208 m_dwRecvTimeout, MAX_MSG_SIZE)) <= 0)
5039dede 209 {
a2069340
VK
210 if (WSAGetLastError() != WSAESHUTDOWN)
211 DbgPrintf(6, _T("AgentConnection::ReceiverThread(): RecvNXCPMessage() failed: error=%d, socket_error=%d"), error, WSAGetLastError());
5039dede
AK
212 break;
213 }
214
215 // Check if we get too large message
4685a2ad 216 if (error == 1)
5039dede 217 {
cc022855 218 printMsg(_T("Received too large message %s (%d bytes)"),
5039dede
AK
219 NXCPMessageCodeName(ntohs(pRawMsg->wCode), szBuffer),
220 ntohl(pRawMsg->dwSize));
221 continue;
222 }
223
224 // Check if we are unable to decrypt message
4685a2ad 225 if (error == 2)
5039dede 226 {
a3050773 227 printMsg(_T("Unable to decrypt received message"));
5039dede
AK
228 continue;
229 }
230
231 // Check for timeout
4685a2ad 232 if (error == 3)
5039dede 233 {
4685a2ad
VK
234 if (m_fileUploadInProgress)
235 continue; // Receive timeout may occur when uploading large files via slow links
a3050773 236 printMsg(_T("Timed out waiting for message"));
5039dede
AK
237 break;
238 }
239
240 // Check that actual received packet size is equal to encoded in packet
4685a2ad 241 if ((int)ntohl(pRawMsg->dwSize) != error)
5039dede 242 {
a3050773 243 printMsg(_T("RecvMsg: Bad packet length [dwSize=%d ActualSize=%d]"), ntohl(pRawMsg->dwSize), error);
5039dede
AK
244 continue; // Bad packet, wait for next
245 }
246
247 if (ntohs(pRawMsg->wFlags) & MF_BINARY)
248 {
249 // Convert message header to host format
250 pRawMsg->dwId = ntohl(pRawMsg->dwId);
251 pRawMsg->wCode = ntohs(pRawMsg->wCode);
252 pRawMsg->dwNumVars = ntohl(pRawMsg->dwNumVars);
08b214c6 253 DbgPrintf(6, _T("Received raw message %s from agent at %s"),
7c521895 254 NXCPMessageCodeName(pRawMsg->wCode, szBuffer), IpToStr(getIpAddr(), szIpAddr));
5039dede 255
76b4edb5 256 if ((pRawMsg->wCode == CMD_FILE_DATA) && (pRawMsg->dwId == m_dwDownloadRequestId))
5039dede 257 {
76b4edb5 258 if (m_sendToClientMessageCallback != NULL)
5039dede 259 {
76b4edb5 260 pRawMsg->wCode = ntohs(pRawMsg->wCode);
261 pRawMsg->dwNumVars = ntohl(pRawMsg->dwNumVars);
262 m_sendToClientMessageCallback(pRawMsg, m_downloadProgressCallbackArg);
263
5039dede
AK
264 if (ntohs(pRawMsg->wFlags) & MF_END_OF_FILE)
265 {
f480bdd4 266 onFileDownload(TRUE);
5039dede 267 }
76b4edb5 268 else
269 {
270 if (m_downloadProgressCallback != NULL)
271 {
272 m_downloadProgressCallback(pRawMsg->dwSize - (CSCP_HEADER_SIZE + 8), m_downloadProgressCallbackArg);
273 }
274 }
5039dede
AK
275 }
276 else
277 {
76b4edb5 278 if (m_hCurrFile != -1)
279 {
280 if (write(m_hCurrFile, pRawMsg->df, pRawMsg->dwNumVars) == (int)pRawMsg->dwNumVars)
281 {
282 if (ntohs(pRawMsg->wFlags) & MF_END_OF_FILE)
283 {
284 close(m_hCurrFile);
285 m_hCurrFile = -1;
286
287 onFileDownload(TRUE);
288 }
289 else
290 {
291 if (m_downloadProgressCallback != NULL)
292 {
293 m_downloadProgressCallback(_tell(m_hCurrFile), m_downloadProgressCallbackArg);
294 }
295 }
296 }
297 }
298 else
299 {
300 // I/O error
301 close(m_hCurrFile);
302 m_hCurrFile = -1;
cc022855 303
76b4edb5 304 onFileDownload(FALSE);
305 }
5039dede
AK
306 }
307 }
308 }
309 else
310 {
311 // Create message object from raw message
312 pMsg = new CSCPMessage(pRawMsg, m_nProtocolVersion);
f480bdd4 313 switch(pMsg->GetCode())
5039dede 314 {
f480bdd4
VK
315 case CMD_TRAP:
316 onTrap(pMsg);
317 delete pMsg;
318 break;
319 case CMD_PUSH_DCI_DATA:
320 onDataPush(pMsg);
321 delete pMsg;
322 break;
90284364 323 case CMD_REQUEST_COMPLETED:
c17f6cbc 324 m_pMsgWaitQueue->put(pMsg);
f480bdd4 325 break;
9fa031cd 326 case CMD_FILE_MONITORING:
327 onFileMonitoringData(pMsg);
328 delete pMsg;
329 break;
90284364
VK
330 default:
331 if (processCustomMessage(pMsg))
332 delete pMsg;
333 else
c17f6cbc 334 m_pMsgWaitQueue->put(pMsg);
90284364 335 break;
5039dede
AK
336 }
337 }
338 }
339
340 // Close socket and mark connection as disconnected
a3050773 341 lock();
f2665675
VK
342 if (m_hCurrFile != -1)
343 {
344 close(m_hCurrFile);
345 m_hCurrFile = -1;
f480bdd4 346 onFileDownload(FALSE);
f2665675 347 }
cc022855 348
4685a2ad 349 if (error == 0)
5039dede
AK
350 shutdown(m_hSocket, SHUT_RDWR);
351 closesocket(m_hSocket);
352 m_hSocket = -1;
98abc9f1
VK
353 if (m_pCtx != NULL)
354 {
355 m_pCtx->decRefCount();
356 m_pCtx = NULL;
357 }
5039dede 358 m_bIsConnected = FALSE;
a3050773 359 unlock();
5039dede
AK
360
361 free(pRawMsg);
362 free(pMsgBuffer);
363#ifdef _WITH_ENCRYPTION
364 free(pDecryptionBuffer);
365#endif
366}
367
a3050773
VK
368/**
369 * Connect to agent
370 */
967893bb 371BOOL AgentConnection::connect(RSA *pServerKey, BOOL bVerbose, UINT32 *pdwError, UINT32 *pdwSocketError)
5039dede
AK
372{
373 struct sockaddr_in sa;
374 TCHAR szBuffer[256];
375 BOOL bSuccess = FALSE, bForceEncryption = FALSE, bSecondPass = FALSE;
967893bb 376 UINT32 dwError = 0;
5039dede
AK
377
378 if (pdwError != NULL)
379 *pdwError = ERR_INTERNAL_ERROR;
380
c3acd0f6
VK
381 if (pdwSocketError != NULL)
382 *pdwSocketError = 0;
383
5039dede
AK
384 // Check if already connected
385 if (m_bIsConnected)
386 return FALSE;
387
388 // Wait for receiver thread from previous connection, if any
389 ThreadJoin(m_hReceiverThread);
390 m_hReceiverThread = INVALID_THREAD_HANDLE;
391
392 // Check if we need to close existing socket
393 if (m_hSocket != -1)
394 closesocket(m_hSocket);
395
396 // Create socket
397 m_hSocket = socket(AF_INET, SOCK_STREAM, 0);
4c0c75c7 398 if (m_hSocket == INVALID_SOCKET)
5039dede 399 {
a3050773 400 printMsg(_T("Call to socket() failed"));
5039dede
AK
401 goto connect_cleanup;
402 }
403
404 // Fill in address structure
405 memset(&sa, 0, sizeof(sa));
406 sa.sin_family = AF_INET;
407 if (m_bUseProxy)
408 {
409 sa.sin_addr.s_addr = m_dwProxyAddr;
410 sa.sin_port = htons(m_wProxyPort);
411 }
412 else
413 {
414 sa.sin_addr.s_addr = m_dwAddr;
415 sa.sin_port = htons(m_wPort);
416 }
417
418 // Connect to server
7c521895 419 if (ConnectEx(m_hSocket, (struct sockaddr *)&sa, sizeof(sa), m_connectionTimeout) == -1)
5039dede
AK
420 {
421 if (bVerbose)
a3050773 422 printMsg(_T("Cannot establish connection with agent %s"),
5039dede
AK
423 IpToStr(ntohl(m_bUseProxy ? m_dwProxyAddr : m_dwAddr), szBuffer));
424 dwError = ERR_CONNECT_FAILED;
425 goto connect_cleanup;
426 }
427
d3a7cf4c 428 if (!NXCPGetPeerProtocolVersion(m_hSocket, &m_nProtocolVersion, m_mutexSocketWrite))
5039dede
AK
429 {
430 dwError = ERR_INTERNAL_ERROR;
431 goto connect_cleanup;
432 }
433
434 // Start receiver thread
a3050773 435 m_hReceiverThread = ThreadCreateEx(receiverThreadStarter, 0, this);
5039dede
AK
436
437 // Setup encryption
438setup_encryption:
439 if ((m_iEncryptionPolicy == ENCRYPTION_PREFERRED) ||
440 (m_iEncryptionPolicy == ENCRYPTION_REQUIRED) ||
441 (bForceEncryption)) // Agent require encryption
442 {
443 if (pServerKey != NULL)
444 {
7c521895 445 dwError = setupEncryption(pServerKey);
5039dede
AK
446 if ((dwError != ERR_SUCCESS) &&
447 ((m_iEncryptionPolicy == ENCRYPTION_REQUIRED) || bForceEncryption))
448 goto connect_cleanup;
449 }
450 else
451 {
452 if ((m_iEncryptionPolicy == ENCRYPTION_REQUIRED) || bForceEncryption)
453 {
454 dwError = ERR_ENCRYPTION_REQUIRED;
455 goto connect_cleanup;
456 }
457 }
458 }
459
460 // Authenticate itself to agent
7c521895 461 if ((dwError = authenticate(m_bUseProxy && !bSecondPass)) != ERR_SUCCESS)
5039dede
AK
462 {
463 if ((dwError == ERR_ENCRYPTION_REQUIRED) &&
464 (m_iEncryptionPolicy != ENCRYPTION_DISABLED))
465 {
466 bForceEncryption = TRUE;
467 goto setup_encryption;
468 }
a3050773 469 printMsg(_T("Authentication to agent %s failed (%s)"), IpToStr(ntohl(m_dwAddr), szBuffer),
5039dede
AK
470 AgentErrorCodeToText(dwError));
471 goto connect_cleanup;
472 }
473
474 // Test connectivity
45d84f8a 475 if ((dwError = nop()) != ERR_SUCCESS)
5039dede
AK
476 {
477 if ((dwError == ERR_ENCRYPTION_REQUIRED) &&
478 (m_iEncryptionPolicy != ENCRYPTION_DISABLED))
479 {
480 bForceEncryption = TRUE;
481 goto setup_encryption;
482 }
a3050773 483 printMsg(_T("Communication with agent %s failed (%s)"), IpToStr(ntohl(m_dwAddr), szBuffer),
5039dede
AK
484 AgentErrorCodeToText(dwError));
485 goto connect_cleanup;
486 }
487
488 if (m_bUseProxy && !bSecondPass)
489 {
7c521895 490 dwError = setupProxyConnection();
5039dede
AK
491 if (dwError != ERR_SUCCESS)
492 goto connect_cleanup;
a3050773 493 lock();
98abc9f1
VK
494 if (m_pCtx != NULL)
495 {
496 m_pCtx->decRefCount();
497 m_pCtx = NULL;
498 }
a3050773 499 unlock();
5039dede
AK
500 bSecondPass = TRUE;
501 bForceEncryption = FALSE;
502 goto setup_encryption;
503 }
504
505 bSuccess = TRUE;
506 dwError = ERR_SUCCESS;
507
508connect_cleanup:
509 if (!bSuccess)
510 {
c3acd0f6 511 if (pdwSocketError != NULL)
967893bb 512 *pdwSocketError = (UINT32)WSAGetLastError();
c3acd0f6 513
a3050773 514 lock();
5039dede
AK
515 if (m_hSocket != -1)
516 shutdown(m_hSocket, SHUT_RDWR);
a3050773 517 unlock();
5039dede
AK
518 ThreadJoin(m_hReceiverThread);
519 m_hReceiverThread = INVALID_THREAD_HANDLE;
520
a3050773 521 lock();
5039dede
AK
522 if (m_hSocket != -1)
523 {
524 closesocket(m_hSocket);
525 m_hSocket = -1;
526 }
527
98abc9f1
VK
528 if (m_pCtx != NULL)
529 {
530 m_pCtx->decRefCount();
531 m_pCtx = NULL;
532 }
5039dede 533
a3050773 534 unlock();
5039dede
AK
535 }
536 m_bIsConnected = bSuccess;
537 if (pdwError != NULL)
538 *pdwError = dwError;
539 return bSuccess;
540}
541
af21affe
VK
542/**
543 * Disconnect from agent
544 */
7c521895 545void AgentConnection::disconnect()
5039dede 546{
a3050773 547 lock();
f2665675
VK
548 if (m_hCurrFile != -1)
549 {
550 close(m_hCurrFile);
551 m_hCurrFile = -1;
f480bdd4 552 onFileDownload(FALSE);
f2665675 553 }
9fa031cd 554
5039dede
AK
555 if (m_hSocket != -1)
556 {
557 shutdown(m_hSocket, SHUT_RDWR);
558 }
7c521895 559 destroyResultData();
5039dede 560 m_bIsConnected = FALSE;
a3050773 561 unlock();
5039dede
AK
562}
563
af21affe
VK
564/**
565 * Set authentication data
566 */
567void AgentConnection::setAuthData(int method, const TCHAR *secret)
cc022855 568{
af21affe
VK
569 m_iAuthMethod = method;
570#ifdef UNICODE
571 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, secret, -1, m_szSecret, MAX_SECRET_LENGTH, NULL, NULL);
572 m_szSecret[MAX_SECRET_LENGTH - 1] = 0;
573#else
574 nx_strncpy(m_szSecret, secret, MAX_SECRET_LENGTH);
575#endif
576}
5039dede 577
af21affe
VK
578/**
579 * Destroy command execuion results data
580 */
7c521895 581void AgentConnection::destroyResultData()
5039dede 582{
967893bb 583 UINT32 i;
5039dede
AK
584
585 if (m_ppDataLines != NULL)
586 {
587 for(i = 0; i < m_dwNumDataLines; i++)
588 if (m_ppDataLines[i] != NULL)
589 free(m_ppDataLines[i]);
590 free(m_ppDataLines);
591 m_ppDataLines = NULL;
592 }
593 m_dwNumDataLines = 0;
594}
595
d51f2182
VK
596/**
597 * Get interface list from agent
598 */
98762401 599InterfaceList *AgentConnection::getInterfaceList()
5039dede 600{
98762401 601 InterfaceList *pIfList = NULL;
36e44abe 602 NX_INTERFACE_INFO iface;
967893bb 603 UINT32 i, dwBits;
5039dede
AK
604 TCHAR *pChar, *pBuf;
605
4687826e 606 if (getList(_T("Net.InterfaceList")) == ERR_SUCCESS)
5039dede 607 {
98762401 608 pIfList = new InterfaceList(m_dwNumDataLines);
5039dede
AK
609
610 // Parse result set. Each line should have the following format:
611 // index ip_address/mask_bits iftype mac_address name
612 for(i = 0; i < m_dwNumDataLines; i++)
613 {
614 pBuf = m_ppDataLines[i];
36e44abe 615 memset(&iface, 0, sizeof(NX_INTERFACE_INFO));
5039dede
AK
616
617 // Index
618 pChar = _tcschr(pBuf, ' ');
619 if (pChar != NULL)
620 {
621 *pChar = 0;
98762401 622 iface.dwIndex = _tcstoul(pBuf, NULL, 10);
5039dede
AK
623 pBuf = pChar + 1;
624 }
625
626 // Address and mask
627 pChar = _tcschr(pBuf, _T(' '));
628 if (pChar != NULL)
629 {
630 TCHAR *pSlash;
631 static TCHAR defaultMask[] = _T("24");
632
633 *pChar = 0;
634 pSlash = _tcschr(pBuf, _T('/'));
635 if (pSlash != NULL)
636 {
637 *pSlash = 0;
638 pSlash++;
639 }
640 else // Just a paranoia protection, should'n happen if agent working correctly
641 {
642 pSlash = defaultMask;
643 }
98762401 644 iface.dwIpAddr = ntohl(_t_inet_addr(pBuf));
5039dede 645 dwBits = _tcstoul(pSlash, NULL, 10);
98762401 646 iface.dwIpNetMask = (dwBits == 32) ? 0xFFFFFFFF : (~(0xFFFFFFFF >> dwBits));
5039dede
AK
647 pBuf = pChar + 1;
648 }
649
650 // Interface type
651 pChar = _tcschr(pBuf, ' ');
652 if (pChar != NULL)
653 {
654 *pChar = 0;
98762401 655 iface.dwType = _tcstoul(pBuf, NULL, 10);
5039dede
AK
656 pBuf = pChar + 1;
657 }
658
659 // MAC address
660 pChar = _tcschr(pBuf, ' ');
661 if (pChar != NULL)
662 {
663 *pChar = 0;
98762401 664 StrToBin(pBuf, iface.bMacAddr, MAC_ADDR_LENGTH);
5039dede
AK
665 pBuf = pChar + 1;
666 }
667
478d4ff4
VK
668 // Name (set description to name)
669 nx_strncpy(iface.szName, pBuf, MAX_DB_STRING);
670 nx_strncpy(iface.szDescription, pBuf, MAX_DB_STRING);
98762401
VK
671
672 pIfList->add(&iface);
5039dede
AK
673 }
674
a3050773 675 lock();
7c521895 676 destroyResultData();
a3050773 677 unlock();
5039dede
AK
678 }
679
680 return pIfList;
681}
682
683
684//
685// Get parameter value
686//
687
967893bb 688UINT32 AgentConnection::getParameter(const TCHAR *pszParam, UINT32 dwBufSize, TCHAR *pszBuffer)
5039dede
AK
689{
690 CSCPMessage msg(m_nProtocolVersion), *pResponse;
967893bb 691 UINT32 dwRqId, dwRetCode;
5039dede
AK
692
693 if (m_bIsConnected)
694 {
695 dwRqId = m_dwRequestId++;
696 msg.SetCode(CMD_GET_PARAMETER);
697 msg.SetId(dwRqId);
698 msg.SetVariable(VID_PARAMETER, pszParam);
7c521895 699 if (sendMessage(&msg))
5039dede 700 {
7c521895 701 pResponse = waitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
5039dede
AK
702 if (pResponse != NULL)
703 {
704 dwRetCode = pResponse->GetVariableLong(VID_RCC);
705 if (dwRetCode == ERR_SUCCESS)
706 pResponse->GetVariableStr(VID_VALUE, pszBuffer, dwBufSize);
707 delete pResponse;
708 }
709 else
710 {
711 dwRetCode = ERR_REQUEST_TIMEOUT;
712 }
713 }
714 else
715 {
716 dwRetCode = ERR_CONNECTION_BROKEN;
717 }
718 }
719 else
720 {
721 dwRetCode = ERR_NOT_CONNECTED;
722 }
723
724 return dwRetCode;
725}
726
727
728//
729// Get ARP cache
730//
731
4687826e 732ARP_CACHE *AgentConnection::getArpCache()
5039dede
AK
733{
734 ARP_CACHE *pArpCache = NULL;
735 TCHAR szByte[4], *pBuf, *pChar;
967893bb 736 UINT32 i, j;
5039dede 737
4687826e 738 if (getList(_T("Net.ArpCache")) == ERR_SUCCESS)
5039dede
AK
739 {
740 // Create empty structure
741 pArpCache = (ARP_CACHE *)malloc(sizeof(ARP_CACHE));
742 pArpCache->dwNumEntries = m_dwNumDataLines;
743 pArpCache->pEntries = (ARP_ENTRY *)malloc(sizeof(ARP_ENTRY) * m_dwNumDataLines);
744 memset(pArpCache->pEntries, 0, sizeof(ARP_ENTRY) * m_dwNumDataLines);
745
746 szByte[2] = 0;
747
748 // Parse data lines
749 // Each line has form of XXXXXXXXXXXX a.b.c.d n
750 // where XXXXXXXXXXXX is a MAC address (12 hexadecimal digits)
751 // a.b.c.d is an IP address in decimal dotted notation
752 // n is an interface index
753 for(i = 0; i < m_dwNumDataLines; i++)
754 {
755 pBuf = m_ppDataLines[i];
756 if (_tcslen(pBuf) < 20) // Invalid line
757 continue;
758
759 // MAC address
760 for(j = 0; j < 6; j++)
761 {
762 memcpy(szByte, pBuf, sizeof(TCHAR) * 2);
763 pArpCache->pEntries[i].bMacAddr[j] = (BYTE)_tcstol(szByte, NULL, 16);
764 pBuf+=2;
765 }
766
767 // IP address
768 while(*pBuf == ' ')
769 pBuf++;
770 pChar = _tcschr(pBuf, _T(' '));
771 if (pChar != NULL)
772 *pChar = 0;
773 pArpCache->pEntries[i].dwIpAddr = ntohl(_t_inet_addr(pBuf));
774
775 // Interface index
776 if (pChar != NULL)
777 pArpCache->pEntries[i].dwIndex = _tcstoul(pChar + 1, NULL, 10);
778 }
779
a3050773 780 lock();
7c521895 781 destroyResultData();
a3050773 782 unlock();
5039dede
AK
783 }
784 return pArpCache;
785}
786
787
788//
789// Send dummy command to agent (can be used for keepalive)
790//
791
967893bb 792UINT32 AgentConnection::nop()
5039dede
AK
793{
794 CSCPMessage msg(m_nProtocolVersion);
967893bb 795 UINT32 dwRqId;
5039dede
AK
796
797 dwRqId = m_dwRequestId++;
798 msg.SetCode(CMD_KEEPALIVE);
799 msg.SetId(dwRqId);
7c521895
VK
800 if (sendMessage(&msg))
801 return waitForRCC(dwRqId, m_dwCommandTimeout);
5039dede
AK
802 else
803 return ERR_CONNECTION_BROKEN;
804}
805
806
807//
808// Wait for request completion code
809//
810
967893bb 811UINT32 AgentConnection::waitForRCC(UINT32 dwRqId, UINT32 dwTimeOut)
5039dede
AK
812{
813 CSCPMessage *pMsg;
967893bb 814 UINT32 dwRetCode;
5039dede 815
c17f6cbc 816 pMsg = m_pMsgWaitQueue->waitForMessage(CMD_REQUEST_COMPLETED, dwRqId, dwTimeOut);
5039dede
AK
817 if (pMsg != NULL)
818 {
819 dwRetCode = pMsg->GetVariableLong(VID_RCC);
820 delete pMsg;
821 }
822 else
823 {
824 dwRetCode = ERR_REQUEST_TIMEOUT;
825 }
826 return dwRetCode;
827}
828
5c44534b
VK
829/**
830 * Send message to agent
831 */
7c521895 832BOOL AgentConnection::sendMessage(CSCPMessage *pMsg)
5039dede 833{
5039dede
AK
834 BOOL bResult;
835
5c44534b 836 CSCP_MESSAGE *pRawMsg = pMsg->createMessage();
98abc9f1
VK
837 NXCPEncryptionContext *pCtx = acquireEncryptionContext();
838 if (pCtx != NULL)
5039dede 839 {
98abc9f1 840 CSCP_ENCRYPTED_MESSAGE *pEnMsg = CSCPEncryptMessage(pCtx, pRawMsg);
5039dede
AK
841 if (pEnMsg != NULL)
842 {
d3a7cf4c 843 bResult = (SendEx(m_hSocket, (char *)pEnMsg, ntohl(pEnMsg->dwSize), 0, m_mutexSocketWrite) == (int)ntohl(pEnMsg->dwSize));
5039dede
AK
844 free(pEnMsg);
845 }
846 else
847 {
848 bResult = FALSE;
849 }
98abc9f1 850 pCtx->decRefCount();
5039dede
AK
851 }
852 else
853 {
d3a7cf4c 854 bResult = (SendEx(m_hSocket, (char *)pRawMsg, ntohl(pRawMsg->dwSize), 0, m_mutexSocketWrite) == (int)ntohl(pRawMsg->dwSize));
5039dede
AK
855 }
856 free(pRawMsg);
857 return bResult;
858}
859
d51f2182
VK
860/**
861 * Trap handler. Should be overriden in derived classes to implement
862 * actual trap processing. Default implementation do nothing.
863 */
f480bdd4
VK
864void AgentConnection::onTrap(CSCPMessage *pMsg)
865{
866}
867
d51f2182
VK
868/**
869 * Data push handler. Should be overriden in derived classes to implement
870 * actual data push processing. Default implementation do nothing.
871 */
f480bdd4 872void AgentConnection::onDataPush(CSCPMessage *pMsg)
5039dede
AK
873{
874}
875
9fa031cd 876/**
877 * Monitoring data handler. Should be overriden in derived classes to implement
878 * actual monitoring data processing. Default implementation do nothing.
879 */
880void AgentConnection::onFileMonitoringData(CSCPMessage *pMsg)
881{
882}
883
d51f2182
VK
884/**
885 * Custom message handler
886 * If returns true, message considered as processed and will not be placed in wait queue
887 */
90284364
VK
888bool AgentConnection::processCustomMessage(CSCPMessage *pMsg)
889{
890 return false;
891}
892
d51f2182
VK
893/**
894 * Get list of values
895 */
967893bb 896UINT32 AgentConnection::getList(const TCHAR *pszParam)
5039dede
AK
897{
898 CSCPMessage msg(m_nProtocolVersion), *pResponse;
967893bb 899 UINT32 i, dwRqId, dwRetCode;
5039dede
AK
900
901 if (m_bIsConnected)
902 {
7c521895 903 destroyResultData();
5039dede
AK
904 dwRqId = m_dwRequestId++;
905 msg.SetCode(CMD_GET_LIST);
906 msg.SetId(dwRqId);
907 msg.SetVariable(VID_PARAMETER, pszParam);
7c521895 908 if (sendMessage(&msg))
5039dede 909 {
7c521895 910 pResponse = waitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
5039dede
AK
911 if (pResponse != NULL)
912 {
913 dwRetCode = pResponse->GetVariableLong(VID_RCC);
914 if (dwRetCode == ERR_SUCCESS)
915 {
916 m_dwNumDataLines = pResponse->GetVariableLong(VID_NUM_STRINGS);
917 m_ppDataLines = (TCHAR **)malloc(sizeof(TCHAR *) * m_dwNumDataLines);
918 for(i = 0; i < m_dwNumDataLines; i++)
919 m_ppDataLines[i] = pResponse->GetVariableStr(VID_ENUM_VALUE_BASE + i);
920 }
921 delete pResponse;
922 }
923 else
924 {
925 dwRetCode = ERR_REQUEST_TIMEOUT;
926 }
927 }
928 else
929 {
930 dwRetCode = ERR_CONNECTION_BROKEN;
931 }
932 }
933 else
934 {
935 dwRetCode = ERR_NOT_CONNECTED;
936 }
937
938 return dwRetCode;
939}
940
d51f2182
VK
941/**
942 * Get table
943 */
967893bb 944UINT32 AgentConnection::getTable(const TCHAR *pszParam, Table **table)
4687826e
VK
945{
946 CSCPMessage msg(m_nProtocolVersion), *pResponse;
967893bb 947 UINT32 dwRqId, dwRetCode;
4687826e
VK
948
949 *table = NULL;
950 if (m_bIsConnected)
951 {
952 dwRqId = m_dwRequestId++;
953 msg.SetCode(CMD_GET_TABLE);
954 msg.SetId(dwRqId);
955 msg.SetVariable(VID_PARAMETER, pszParam);
956 if (sendMessage(&msg))
957 {
958 pResponse = waitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
959 if (pResponse != NULL)
960 {
961 dwRetCode = pResponse->GetVariableLong(VID_RCC);
962 if (dwRetCode == ERR_SUCCESS)
963 {
964 *table = new Table(pResponse);
965 }
966 delete pResponse;
967 }
968 else
969 {
970 dwRetCode = ERR_REQUEST_TIMEOUT;
971 }
972 }
973 else
974 {
975 dwRetCode = ERR_CONNECTION_BROKEN;
976 }
977 }
978 else
979 {
980 dwRetCode = ERR_NOT_CONNECTED;
981 }
982
983 return dwRetCode;
984}
985
975f38fc
VK
986/**
987 * Authenticate to agent
988 */
967893bb 989UINT32 AgentConnection::authenticate(BOOL bProxyData)
5039dede
AK
990{
991 CSCPMessage msg(m_nProtocolVersion);
967893bb 992 UINT32 dwRqId;
5039dede
AK
993 BYTE hash[32];
994 int iAuthMethod = bProxyData ? m_iProxyAuth : m_iAuthMethod;
08b214c6 995 const char *pszSecret = bProxyData ? m_szProxySecret : m_szSecret;
5039dede
AK
996#ifdef UNICODE
997 WCHAR szBuffer[MAX_SECRET_LENGTH];
998#endif
999
1000 if (iAuthMethod == AUTH_NONE)
1001 return ERR_SUCCESS; // No authentication required
1002
1003 dwRqId = m_dwRequestId++;
1004 msg.SetCode(CMD_AUTHENTICATE);
1005 msg.SetId(dwRqId);
1006 msg.SetVariable(VID_AUTH_METHOD, (WORD)iAuthMethod);
1007 switch(iAuthMethod)
1008 {
1009 case AUTH_PLAINTEXT:
1010#ifdef UNICODE
1011 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pszSecret, -1, szBuffer, MAX_SECRET_LENGTH);
1012 msg.SetVariable(VID_SHARED_SECRET, szBuffer);
1013#else
1014 msg.SetVariable(VID_SHARED_SECRET, pszSecret);
1015#endif
1016 break;
1017 case AUTH_MD5_HASH:
1018 CalculateMD5Hash((BYTE *)pszSecret, (int)strlen(pszSecret), hash);
1019 msg.SetVariable(VID_SHARED_SECRET, hash, MD5_DIGEST_SIZE);
1020 break;
1021 case AUTH_SHA1_HASH:
1022 CalculateSHA1Hash((BYTE *)pszSecret, (int)strlen(pszSecret), hash);
1023 msg.SetVariable(VID_SHARED_SECRET, hash, SHA1_DIGEST_SIZE);
1024 break;
1025 default:
1026 break;
1027 }
7c521895
VK
1028 if (sendMessage(&msg))
1029 return waitForRCC(dwRqId, m_dwCommandTimeout);
5039dede
AK
1030 else
1031 return ERR_CONNECTION_BROKEN;
1032}
1033
1034
1035//
1036// Execute action on agent
1037//
1038
967893bb 1039UINT32 AgentConnection::execAction(const TCHAR *pszAction, int argc, TCHAR **argv)
5039dede
AK
1040{
1041 CSCPMessage msg(m_nProtocolVersion);
967893bb 1042 UINT32 dwRqId;
5039dede
AK
1043 int i;
1044
1045 if (!m_bIsConnected)
1046 return ERR_NOT_CONNECTED;
1047
1048 dwRqId = m_dwRequestId++;
1049 msg.SetCode(CMD_ACTION);
1050 msg.SetId(dwRqId);
1051 msg.SetVariable(VID_ACTION_NAME, pszAction);
967893bb 1052 msg.SetVariable(VID_NUM_ARGS, (UINT32)argc);
5039dede
AK
1053 for(i = 0; i < argc; i++)
1054 msg.SetVariable(VID_ACTION_ARG_BASE + i, argv[i]);
1055
7c521895
VK
1056 if (sendMessage(&msg))
1057 return waitForRCC(dwRqId, m_dwCommandTimeout);
5039dede
AK
1058 else
1059 return ERR_CONNECTION_BROKEN;
1060}
1061
1062
1063//
1064// Upload file to agent
1065//
1066
967893bb 1067UINT32 AgentConnection::uploadFile(const TCHAR *localFile, const TCHAR *destinationFile, void (* progressCallback)(INT64, void *), void *cbArg)
5039dede 1068{
967893bb 1069 UINT32 dwRqId, dwResult;
5039dede
AK
1070 CSCPMessage msg(m_nProtocolVersion);
1071 int i;
1072
1073 if (!m_bIsConnected)
1074 return ERR_NOT_CONNECTED;
1075
1076 dwRqId = m_dwRequestId++;
1077
1078 msg.SetCode(CMD_TRANSFER_FILE);
1079 msg.SetId(dwRqId);
cc022855 1080 for(i = (int)_tcslen(localFile) - 1;
619e5c9b
VK
1081 (i >= 0) && (localFile[i] != '\\') && (localFile[i] != '/'); i--);
1082 msg.SetVariable(VID_FILE_NAME, &localFile[i + 1]);
1083 if (destinationFile != NULL)
f0c1d2a4 1084 {
619e5c9b 1085 msg.SetVariable(VID_DESTINATION_FILE_NAME, destinationFile);
f0c1d2a4 1086 }
5039dede 1087
7c521895 1088 if (sendMessage(&msg))
5039dede 1089 {
7c521895 1090 dwResult = waitForRCC(dwRqId, m_dwCommandTimeout);
5039dede
AK
1091 }
1092 else
1093 {
1094 dwResult = ERR_CONNECTION_BROKEN;
1095 }
1096
1097 if (dwResult == ERR_SUCCESS)
1098 {
4685a2ad 1099 m_fileUploadInProgress = true;
98abc9f1 1100 NXCPEncryptionContext *ctx = acquireEncryptionContext();
cc022855 1101 if (SendFileOverNXCP(m_hSocket, dwRqId, localFile, ctx, 0, progressCallback, cbArg, m_mutexSocketWrite))
7c521895 1102 dwResult = waitForRCC(dwRqId, m_dwCommandTimeout);
5039dede
AK
1103 else
1104 dwResult = ERR_IO_FAILURE;
4685a2ad 1105 m_fileUploadInProgress = false;
5039dede
AK
1106 }
1107
1108 return dwResult;
1109}
1110
1111
1112//
1113// Send upgrade command
1114//
1115
967893bb 1116UINT32 AgentConnection::startUpgrade(const TCHAR *pszPkgName)
5039dede 1117{
967893bb 1118 UINT32 dwRqId, dwResult;
5039dede
AK
1119 CSCPMessage msg(m_nProtocolVersion);
1120 int i;
1121
1122 if (!m_bIsConnected)
1123 return ERR_NOT_CONNECTED;
1124
1125 dwRqId = m_dwRequestId++;
1126
1127 msg.SetCode(CMD_UPGRADE_AGENT);
1128 msg.SetId(dwRqId);
cc022855 1129 for(i = (int)_tcslen(pszPkgName) - 1;
5039dede
AK
1130 (i >= 0) && (pszPkgName[i] != '\\') && (pszPkgName[i] != '/'); i--);
1131 msg.SetVariable(VID_FILE_NAME, &pszPkgName[i + 1]);
1132
7c521895 1133 if (sendMessage(&msg))
5039dede 1134 {
7c521895 1135 dwResult = waitForRCC(dwRqId, m_dwCommandTimeout);
5039dede
AK
1136 }
1137 else
1138 {
1139 dwResult = ERR_CONNECTION_BROKEN;
1140 }
1141
1142 return dwResult;
1143}
1144
a0e181dc
VK
1145/**
1146 * Check status of network service via agent
1147 */
cc022855 1148UINT32 AgentConnection::checkNetworkService(UINT32 *pdwStatus, UINT32 dwIpAddr, int iServiceType,
1149 WORD wPort, WORD wProto,
5039dede
AK
1150 const TCHAR *pszRequest, const TCHAR *pszResponse)
1151{
967893bb 1152 UINT32 dwRqId, dwResult;
5039dede 1153 CSCPMessage msg(m_nProtocolVersion), *pResponse;
a0e181dc 1154 static WORD m_wDefaultPort[] = { 7, 22, 110, 25, 21, 80, 443, 23 };
5039dede
AK
1155
1156 if (!m_bIsConnected)
1157 return ERR_NOT_CONNECTED;
1158
1159 dwRqId = m_dwRequestId++;
1160
1161 msg.SetCode(CMD_CHECK_NETWORK_SERVICE);
1162 msg.SetId(dwRqId);
1163 msg.SetVariable(VID_IP_ADDRESS, dwIpAddr);
1164 msg.SetVariable(VID_SERVICE_TYPE, (WORD)iServiceType);
cc022855 1165 msg.SetVariable(VID_IP_PORT,
1166 (wPort != 0) ? wPort :
1167 m_wDefaultPort[((iServiceType >= NETSRV_CUSTOM) &&
a0e181dc 1168 (iServiceType <= NETSRV_TELNET)) ? iServiceType : 0]);
5039dede
AK
1169 msg.SetVariable(VID_IP_PROTO, (wProto != 0) ? wProto : (WORD)IPPROTO_TCP);
1170 msg.SetVariable(VID_SERVICE_REQUEST, pszRequest);
1171 msg.SetVariable(VID_SERVICE_RESPONSE, pszResponse);
1172
7c521895 1173 if (sendMessage(&msg))
5039dede
AK
1174 {
1175 // Wait up to 90 seconds for results
7c521895 1176 pResponse = waitForMessage(CMD_REQUEST_COMPLETED, dwRqId, 90000);
5039dede
AK
1177 if (pResponse != NULL)
1178 {
1179 dwResult = pResponse->GetVariableLong(VID_RCC);
1180 if (dwResult == ERR_SUCCESS)
1181 {
1182 *pdwStatus = pResponse->GetVariableLong(VID_SERVICE_STATUS);
1183 }
1184 delete pResponse;
1185 }
1186 else
1187 {
1188 dwResult = ERR_REQUEST_TIMEOUT;
1189 }
1190 }
1191 else
1192 {
1193 dwResult = ERR_CONNECTION_BROKEN;
1194 }
1195
1196 return dwResult;
1197}
1198
074498ac 1199/**
86c126f5 1200 * Get list of supported parameters from agent
074498ac 1201 */
967893bb 1202UINT32 AgentConnection::getSupportedParameters(ObjectArray<AgentParameterDefinition> **paramList, ObjectArray<AgentTableDefinition> **tableList)
5039dede 1203{
967893bb 1204 UINT32 dwRqId, dwResult;
5039dede
AK
1205 CSCPMessage msg(m_nProtocolVersion), *pResponse;
1206
cc8ce218
VK
1207 *paramList = NULL;
1208 *tableList = NULL;
5039dede
AK
1209
1210 if (!m_bIsConnected)
1211 return ERR_NOT_CONNECTED;
1212
1213 dwRqId = m_dwRequestId++;
1214
1215 msg.SetCode(CMD_GET_PARAMETER_LIST);
1216 msg.SetId(dwRqId);
1217
7c521895 1218 if (sendMessage(&msg))
5039dede 1219 {
7c521895 1220 pResponse = waitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
5039dede
AK
1221 if (pResponse != NULL)
1222 {
1223 dwResult = pResponse->GetVariableLong(VID_RCC);
cc8ce218 1224 DbgPrintf(6, _T("AgentConnection::getSupportedParameters(): RCC=%d"), dwResult);
5039dede
AK
1225 if (dwResult == ERR_SUCCESS)
1226 {
967893bb 1227 UINT32 count = pResponse->GetVariableLong(VID_NUM_PARAMETERS);
86c126f5 1228 ObjectArray<AgentParameterDefinition> *plist = new ObjectArray<AgentParameterDefinition>(count, 16, true);
967893bb 1229 for(UINT32 i = 0, dwId = VID_PARAM_LIST_BASE; i < count; i++)
5039dede 1230 {
86c126f5
VK
1231 plist->add(new AgentParameterDefinition(pResponse, dwId));
1232 dwId += 3;
5039dede 1233 }
86c126f5 1234 *paramList = plist;
cc8ce218
VK
1235 DbgPrintf(6, _T("AgentConnection::getSupportedParameters(): %d parameters received from agent"), count);
1236
1237 count = pResponse->GetVariableLong(VID_NUM_TABLES);
86c126f5 1238 ObjectArray<AgentTableDefinition> *tlist = new ObjectArray<AgentTableDefinition>(count, 16, true);
967893bb 1239 for(UINT32 i = 0, dwId = VID_TABLE_LIST_BASE; i < count; i++)
cc8ce218 1240 {
86c126f5
VK
1241 tlist->add(new AgentTableDefinition(pResponse, dwId));
1242 dwId += 3;
cc8ce218 1243 }
86c126f5 1244 *tableList = tlist;
cc8ce218
VK
1245 DbgPrintf(6, _T("AgentConnection::getSupportedParameters(): %d tables received from agent"), count);
1246 }
5039dede
AK
1247 delete pResponse;
1248 }
1249 else
1250 {
1251 dwResult = ERR_REQUEST_TIMEOUT;
1252 }
1253 }
1254 else
1255 {
1256 dwResult = ERR_CONNECTION_BROKEN;
1257 }
1258
1259 return dwResult;
1260}
1261
074498ac
VK
1262/**
1263 * Setup encryption
1264 */
967893bb 1265UINT32 AgentConnection::setupEncryption(RSA *pServerKey)
5039dede
AK
1266{
1267#ifdef _WITH_ENCRYPTION
1268 CSCPMessage msg(m_nProtocolVersion), *pResp;
967893bb 1269 UINT32 dwRqId, dwError, dwResult;
5039dede
AK
1270
1271 dwRqId = m_dwRequestId++;
1272
9e9d631e 1273 PrepareKeyRequestMsg(&msg, pServerKey, false);
5039dede 1274 msg.SetId(dwRqId);
7c521895 1275 if (sendMessage(&msg))
5039dede 1276 {
7c521895 1277 pResp = waitForMessage(CMD_SESSION_KEY, dwRqId, m_dwCommandTimeout);
5039dede
AK
1278 if (pResp != NULL)
1279 {
1280 dwResult = SetupEncryptionContext(pResp, &m_pCtx, NULL, pServerKey, m_nProtocolVersion);
1281 switch(dwResult)
1282 {
1283 case RCC_SUCCESS:
1284 dwError = ERR_SUCCESS;
1285 break;
1286 case RCC_NO_CIPHERS:
1287 dwError = ERR_NO_CIPHERS;
1288 break;
1289 case RCC_INVALID_PUBLIC_KEY:
1290 dwError = ERR_INVALID_PUBLIC_KEY;
1291 break;
1292 case RCC_INVALID_SESSION_KEY:
1293 dwError = ERR_INVALID_SESSION_KEY;
1294 break;
1295 default:
1296 dwError = ERR_INTERNAL_ERROR;
1297 break;
1298 }
1299 delete pResp;
1300 }
1301 else
1302 {
1303 dwError = ERR_REQUEST_TIMEOUT;
1304 }
1305 }
1306 else
1307 {
1308 dwError = ERR_CONNECTION_BROKEN;
1309 }
1310
1311 return dwError;
1312#else
1313 return ERR_NOT_IMPLEMENTED;
1314#endif
1315}
1316
1317
1318//
1319// Get configuration file from agent
1320//
1321
967893bb 1322UINT32 AgentConnection::getConfigFile(TCHAR **ppszConfig, UINT32 *pdwSize)
5039dede 1323{
967893bb 1324 UINT32 i, dwRqId, dwResult;
5039dede
AK
1325 CSCPMessage msg(m_nProtocolVersion), *pResponse;
1326#ifdef UNICODE
1327 BYTE *pBuffer;
1328#endif
1329
1330 *ppszConfig = NULL;
1331 *pdwSize = 0;
1332
1333 if (!m_bIsConnected)
1334 return ERR_NOT_CONNECTED;
1335
1336 dwRqId = m_dwRequestId++;
1337
1338 msg.SetCode(CMD_GET_AGENT_CONFIG);
1339 msg.SetId(dwRqId);
1340
7c521895 1341 if (sendMessage(&msg))
5039dede 1342 {
7c521895 1343 pResponse = waitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
5039dede
AK
1344 if (pResponse != NULL)
1345 {
1346 dwResult = pResponse->GetVariableLong(VID_RCC);
1347 if (dwResult == ERR_SUCCESS)
1348 {
1349 *pdwSize = pResponse->GetVariableBinary(VID_CONFIG_FILE, NULL, 0);
1350 *ppszConfig = (TCHAR *)malloc((*pdwSize + 1) * sizeof(TCHAR));
1351#ifdef UNICODE
1352 pBuffer = (BYTE *)malloc(*pdwSize + 1);
1353 pResponse->GetVariableBinary(VID_CONFIG_FILE, pBuffer, *pdwSize);
08b214c6 1354 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char *)pBuffer, *pdwSize, *ppszConfig, *pdwSize);
5039dede
AK
1355 free(pBuffer);
1356#else
1357 pResponse->GetVariableBinary(VID_CONFIG_FILE, (BYTE *)(*ppszConfig), *pdwSize);
1358#endif
1359 (*ppszConfig)[*pdwSize] = 0;
1360
1361 // We expect text file, so replace all non-printable characters with spaces
1362 for(i = 0; i < *pdwSize; i++)
cc022855 1363 if (((*ppszConfig)[i] < _T(' ')) &&
5039dede
AK
1364 ((*ppszConfig)[i] != _T('\t')) &&
1365 ((*ppszConfig)[i] != _T('\r')) &&
1366 ((*ppszConfig)[i] != _T('\n')))
1367 (*ppszConfig)[i] = _T(' ');
1368 }
1369 delete pResponse;
1370 }
1371 else
1372 {
1373 dwResult = ERR_REQUEST_TIMEOUT;
1374 }
1375 }
1376 else
1377 {
1378 dwResult = ERR_CONNECTION_BROKEN;
1379 }
1380
1381 return dwResult;
1382}
1383
1384
1385//
1386// Get configuration file from agent
1387//
1388
967893bb 1389UINT32 AgentConnection::updateConfigFile(const TCHAR *pszConfig)
5039dede 1390{
967893bb 1391 UINT32 dwRqId, dwResult;
5039dede
AK
1392 CSCPMessage msg(m_nProtocolVersion);
1393#ifdef UNICODE
1394 int nChars;
1395 BYTE *pBuffer;
1396#endif
1397
1398 if (!m_bIsConnected)
1399 return ERR_NOT_CONNECTED;
1400
1401 dwRqId = m_dwRequestId++;
1402
1403 msg.SetCode(CMD_UPDATE_AGENT_CONFIG);
1404 msg.SetId(dwRqId);
1405#ifdef UNICODE
465b3f2d 1406 nChars = (int)_tcslen(pszConfig);
5039dede
AK
1407 pBuffer = (BYTE *)malloc(nChars + 1);
1408 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
08b214c6 1409 pszConfig, nChars, (char *)pBuffer, nChars + 1, NULL, NULL);
5039dede
AK
1410 msg.SetVariable(VID_CONFIG_FILE, pBuffer, nChars);
1411 free(pBuffer);
1412#else
967893bb 1413 msg.SetVariable(VID_CONFIG_FILE, (BYTE *)pszConfig, (UINT32)strlen(pszConfig));
5039dede
AK
1414#endif
1415
7c521895 1416 if (sendMessage(&msg))
5039dede 1417 {
7c521895 1418 dwResult = waitForRCC(dwRqId, m_dwCommandTimeout);
5039dede
AK
1419 }
1420 else
1421 {
1422 dwResult = ERR_CONNECTION_BROKEN;
1423 }
1424
1425 return dwResult;
1426}
1427
1428
1429//
1430// Get routing table from agent
1431//
1432
4687826e 1433ROUTING_TABLE *AgentConnection::getRoutingTable()
5039dede
AK
1434{
1435 ROUTING_TABLE *pRT = NULL;
967893bb 1436 UINT32 i, dwBits;
5039dede
AK
1437 TCHAR *pChar, *pBuf;
1438
4687826e 1439 if (getList(_T("Net.IP.RoutingTable")) == ERR_SUCCESS)
5039dede
AK
1440 {
1441 pRT = (ROUTING_TABLE *)malloc(sizeof(ROUTING_TABLE));
1442 pRT->iNumEntries = m_dwNumDataLines;
1443 pRT->pRoutes = (ROUTE *)malloc(sizeof(ROUTE) * m_dwNumDataLines);
1444 memset(pRT->pRoutes, 0, sizeof(ROUTE) * m_dwNumDataLines);
1445 for(i = 0; i < m_dwNumDataLines; i++)
1446 {
1447 pBuf = m_ppDataLines[i];
1448
1449 // Destination address and mask
1450 pChar = _tcschr(pBuf, _T(' '));
1451 if (pChar != NULL)
1452 {
1453 TCHAR *pSlash;
1454 static TCHAR defaultMask[] = _T("24");
1455
1456 *pChar = 0;
1457 pSlash = _tcschr(pBuf, _T('/'));
1458 if (pSlash != NULL)
1459 {
1460 *pSlash = 0;
1461 pSlash++;
1462 }
1463 else // Just a paranoia protection, should'n happen if agent working correctly
1464 {
1465 pSlash = defaultMask;
1466 }
1467 pRT->pRoutes[i].dwDestAddr = ntohl(_t_inet_addr(pBuf));
1468 dwBits = _tcstoul(pSlash, NULL, 10);
1469 pRT->pRoutes[i].dwDestMask = (dwBits == 32) ? 0xFFFFFFFF : (~(0xFFFFFFFF >> dwBits));
1470 pBuf = pChar + 1;
1471 }
1472
1473 // Next hop address
1474 pChar = _tcschr(pBuf, _T(' '));
1475 if (pChar != NULL)
1476 {
1477 *pChar = 0;
1478 pRT->pRoutes[i].dwNextHop = ntohl(_t_inet_addr(pBuf));
1479 pBuf = pChar + 1;
1480 }
1481
1482 // Interface index
1483 pChar = _tcschr(pBuf, ' ');
1484 if (pChar != NULL)
1485 {
1486 *pChar = 0;
1487 pRT->pRoutes[i].dwIfIndex = _tcstoul(pBuf, NULL, 10);
1488 pBuf = pChar + 1;
1489 }
1490
1491 // Route type
1492 pRT->pRoutes[i].dwRouteType = _tcstoul(pBuf, NULL, 10);
1493 }
1494
a3050773 1495 lock();
7c521895 1496 destroyResultData();
a3050773 1497 unlock();
5039dede
AK
1498 }
1499
1500 return pRT;
1501}
1502
1503
1504//
1505// Set proxy information
1506//
1507
967893bb 1508void AgentConnection::setProxy(UINT32 dwAddr, WORD wPort, int iAuthMethod, const TCHAR *pszSecret)
5039dede
AK
1509{
1510 m_dwProxyAddr = dwAddr;
1511 m_wProxyPort = wPort;
1512 m_iProxyAuth = iAuthMethod;
1513 if (pszSecret != NULL)
1514 {
1515#ifdef UNICODE
cc022855 1516 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
5039dede
AK
1517 pszSecret, -1, m_szProxySecret, MAX_SECRET_LENGTH, NULL, NULL);
1518#else
1519 nx_strncpy(m_szProxySecret, pszSecret, MAX_SECRET_LENGTH);
1520#endif
1521 }
1522 else
1523 {
1524 m_szProxySecret[0] = 0;
1525 }
1526 m_bUseProxy = TRUE;
1527}
1528
1529
1530//
1531// Setup proxy connection
1532//
1533
967893bb 1534UINT32 AgentConnection::setupProxyConnection()
5039dede
AK
1535{
1536 CSCPMessage msg(m_nProtocolVersion);
967893bb 1537 UINT32 dwRqId;
5039dede
AK
1538
1539 dwRqId = m_dwRequestId++;
1540 msg.SetCode(CMD_SETUP_PROXY_CONNECTION);
1541 msg.SetId(dwRqId);
967893bb 1542 msg.SetVariable(VID_IP_ADDRESS, (UINT32)ntohl(m_dwAddr));
5039dede 1543 msg.SetVariable(VID_AGENT_PORT, m_wPort);
7c521895
VK
1544 if (sendMessage(&msg))
1545 return waitForRCC(dwRqId, 60000); // Wait 60 seconds for remote connect
5039dede
AK
1546 else
1547 return ERR_CONNECTION_BROKEN;
1548}
1549
ef8278f0
VK
1550/**
1551 * Enable trap receiving on connection
1552 */
967893bb 1553UINT32 AgentConnection::enableTraps()
5039dede
AK
1554{
1555 CSCPMessage msg(m_nProtocolVersion);
967893bb 1556 UINT32 dwRqId;
5039dede
AK
1557
1558 dwRqId = m_dwRequestId++;
1559 msg.SetCode(CMD_ENABLE_AGENT_TRAPS);
1560 msg.SetId(dwRqId);
7c521895
VK
1561 if (sendMessage(&msg))
1562 return waitForRCC(dwRqId, m_dwCommandTimeout);
5039dede
AK
1563 else
1564 return ERR_CONNECTION_BROKEN;
1565}
1566
cc022855 1567/**
1568 * Send custom request to agent
1569 */
76b4edb5 1570CSCPMessage *AgentConnection::customRequest(CSCPMessage *pRequest, const TCHAR *recvFile, bool append, void (*downloadProgressCallback)(size_t, void *),
1571 void (*fileResendCallback)(CSCP_MESSAGE*, void *), void *cbArg)
5039dede 1572{
967893bb 1573 UINT32 dwRqId, rcc;
5039dede
AK
1574 CSCPMessage *msg = NULL;
1575
1576 dwRqId = m_dwRequestId++;
1577 pRequest->SetId(dwRqId);
1578 if (recvFile != NULL)
1579 {
76b4edb5 1580 rcc = prepareFileDownload(recvFile, dwRqId, append, downloadProgressCallback, fileResendCallback,cbArg);
5039dede
AK
1581 if (rcc != ERR_SUCCESS)
1582 {
1583 // Create fake response message
1584 msg = new CSCPMessage;
1585 msg->SetCode(CMD_REQUEST_COMPLETED);
1586 msg->SetId(dwRqId);
1587 msg->SetVariable(VID_RCC, rcc);
1588 }
1589 }
1590
1591 if (msg == NULL)
1592 {
7c521895 1593 if (sendMessage(pRequest))
5039dede 1594 {
7c521895 1595 msg = waitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
5039dede
AK
1596 if ((msg != NULL) && (recvFile != NULL))
1597 {
1598 if (msg->GetVariableLong(VID_RCC) == ERR_SUCCESS)
1599 {
bb85e341 1600 if (ConditionWait(m_condFileDownload, 1800000)) // 30 min timeout
5039dede 1601 {
bb85e341 1602 if (!m_fileDownloadSucceeded)
5039dede
AK
1603 {
1604 msg->SetVariable(VID_RCC, ERR_IO_FAILURE);
901a5a9b 1605 if (m_deleteFileOnDownloadFailure)
08b214c6 1606 _tremove(recvFile);
5039dede
AK
1607 }
1608 }
1609 else
1610 {
1611 msg->SetVariable(VID_RCC, ERR_REQUEST_TIMEOUT);
1612 }
1613 }
1614 else
1615 {
ef8278f0 1616 if (fileResendCallback != NULL)
76b4edb5 1617 {
1618 close(m_hCurrFile);
1619 m_hCurrFile = -1;
1620 _tremove(recvFile);
1621 }
5039dede
AK
1622 }
1623 }
76b4edb5 1624
5039dede
AK
1625 }
1626 }
1627
1628 return msg;
1629}
1630
76b4edb5 1631/**
ef8278f0 1632 * Prepare for file download
76b4edb5 1633 */
76b4edb5 1634UINT32 AgentConnection::prepareFileDownload(const TCHAR *fileName, UINT32 rqId, bool append, void (*downloadProgressCallback)(size_t, void *),
ef8278f0 1635 void (*fileResendCallback)(CSCP_MESSAGE *, void *), void *cbArg)
5039dede 1636{
ef8278f0 1637 if (fileResendCallback == NULL)
76b4edb5 1638 {
1639 if (m_hCurrFile != -1)
1640 return ERR_RESOURCE_BUSY;
5039dede 1641
76b4edb5 1642 nx_strncpy(m_currentFileName, fileName, MAX_PATH);
1643 ConditionReset(m_condFileDownload);
1644 m_hCurrFile = _topen(fileName, (append ? 0 : (O_CREAT | O_TRUNC)) | O_RDWR | O_BINARY, S_IREAD | S_IWRITE);
1645 if (m_hCurrFile == -1)
1646 {
1647 DbgPrintf(4, _T("AgentConnection::PrepareFileDownload(): cannot open file %s (%s); append=%d rqId=%d"),
1648 fileName, _tcserror(errno), append, rqId);
1649 }
1650 else
1651 {
1652 if (append)
1653 lseek(m_hCurrFile, 0, SEEK_END);
1654 }
1655
1656 m_dwDownloadRequestId = rqId;
1657 m_downloadProgressCallback = downloadProgressCallback;
1658 m_downloadProgressCallbackArg = cbArg;
1659
9630fcd1
AK
1660 m_sendToClientMessageCallback = NULL;
1661
76b4edb5 1662 return (m_hCurrFile != -1) ? ERR_SUCCESS : ERR_FILE_OPEN_ERROR;
1663 }
1664 else
1665 {
1666 ConditionReset(m_condFileDownload);
76b4edb5 1667
1668 m_dwDownloadRequestId = rqId;
1669 m_downloadProgressCallback = downloadProgressCallback;
1670 m_downloadProgressCallbackArg = cbArg;
1671
9630fcd1
AK
1672 m_sendToClientMessageCallback = fileResendCallback;
1673
76b4edb5 1674 return ERR_SUCCESS;
1675 }
5039dede
AK
1676}
1677
76b4edb5 1678/**
1679 * File upload completion handler
1680 */
f480bdd4 1681void AgentConnection::onFileDownload(BOOL success)
5039dede 1682{
76b4edb5 1683 if (!success && m_deleteFileOnDownloadFailure)
901a5a9b 1684 _tremove(m_currentFileName);
bb85e341
VK
1685 m_fileDownloadSucceeded = success;
1686 ConditionSet(m_condFileDownload);
5039dede 1687}
1f385e47 1688
ef8278f0
VK
1689/**
1690 * Enable trap receiving on connection
1691 */
967893bb 1692UINT32 AgentConnection::getPolicyInventory(AgentPolicyInfo **info)
1f385e47
VK
1693{
1694 CSCPMessage msg(m_nProtocolVersion);
967893bb 1695 UINT32 dwRqId, rcc;
1f385e47
VK
1696
1697 *info = NULL;
1698 dwRqId = m_dwRequestId++;
1699 msg.SetCode(CMD_GET_POLICY_INVENTORY);
1700 msg.SetId(dwRqId);
1701 if (sendMessage(&msg))
1702 {
1703 CSCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
1704 if (response != NULL)
1705 {
1706 rcc = response->GetVariableLong(VID_RCC);
1707 if (rcc == ERR_SUCCESS)
1708 *info = new AgentPolicyInfo(response);
1709 delete response;
1710 }
1711 else
1712 {
1713 rcc = ERR_REQUEST_TIMEOUT;
1714 }
1715 }
1716 else
1717 {
1718 rcc = ERR_CONNECTION_BROKEN;
1719 }
1720 return rcc;
1721}
1722
ef8278f0
VK
1723/**
1724 * Uninstall policy by GUID
1725 */
967893bb 1726UINT32 AgentConnection::uninstallPolicy(uuid_t guid)
1f385e47 1727{
967893bb 1728 UINT32 rqId, rcc;
1f385e47
VK
1729 CSCPMessage msg(m_nProtocolVersion);
1730
1731 rqId = generateRequestId();
1732 msg.SetId(rqId);
1733 msg.SetCode(CMD_UNINSTALL_AGENT_POLICY);
1734 msg.SetVariable(VID_GUID, guid, UUID_LENGTH);
1735 if (sendMessage(&msg))
1736 {
1737 rcc = waitForRCC(rqId, m_dwCommandTimeout);
1738 }
1739 else
1740 {
1741 rcc = ERR_CONNECTION_BROKEN;
1742 }
1743 return rcc;
1744}
98abc9f1 1745
86c126f5
VK
1746/**
1747 * Acquire encryption context
1748 */
98abc9f1
VK
1749NXCPEncryptionContext *AgentConnection::acquireEncryptionContext()
1750{
a3050773 1751 lock();
98abc9f1
VK
1752 NXCPEncryptionContext *ctx = m_pCtx;
1753 if (ctx != NULL)
1754 ctx->incRefCount();
a3050773 1755 unlock();
98abc9f1
VK
1756 return ctx;
1757}
86c126f5
VK
1758
1759/**
1760 * Create new agent parameter definition from NXCP message
1761 */
967893bb 1762AgentParameterDefinition::AgentParameterDefinition(CSCPMessage *msg, UINT32 baseId)
86c126f5
VK
1763{
1764 m_name = msg->GetVariableStr(baseId);
1765 m_description = msg->GetVariableStr(baseId + 1);
1766 m_dataType = (int)msg->GetVariableShort(baseId + 2);
1767}
1768
1769/**
1770 * Create new agent parameter definition from another definition object
1771 */
1772AgentParameterDefinition::AgentParameterDefinition(AgentParameterDefinition *src)
1773{
1774 m_name = (src->m_name != NULL) ? _tcsdup(src->m_name) : NULL;
1775 m_description = (src->m_description != NULL) ? _tcsdup(src->m_description) : NULL;
1776 m_dataType = src->m_dataType;
1777}
1778
1779/**
1780 * Destructor for agent parameter definition
1781 */
1782AgentParameterDefinition::~AgentParameterDefinition()
1783{
1784 safe_free(m_name);
1785 safe_free(m_description);
1786}
1787
1788/**
1789 * Fill NXCP message
1790 */
967893bb 1791UINT32 AgentParameterDefinition::fillMessage(CSCPMessage *msg, UINT32 baseId)
86c126f5
VK
1792{
1793 msg->SetVariable(baseId, m_name);
1794 msg->SetVariable(baseId + 1, m_description);
1795 msg->SetVariable(baseId + 2, (WORD)m_dataType);
1796 return 3;
1797}
1798
1799/**
1800 * Create new agent table definition from NXCP message
1801 */
967893bb 1802AgentTableDefinition::AgentTableDefinition(CSCPMessage *msg, UINT32 baseId)
86c126f5
VK
1803{
1804 m_name = msg->GetVariableStr(baseId);
1805 m_description = msg->GetVariableStr(baseId + 2);
1806
1807 TCHAR *instanceColumns = msg->GetVariableStr(baseId + 1);
1808 if (instanceColumns != NULL)
1809 {
1810 m_instanceColumns = new StringList(instanceColumns, _T("|"));
1811 free(instanceColumns);
1812 }
1813 else
1814 {
1815 m_instanceColumns = new StringList;
1816 }
1817
1818 m_columns = new ObjectArray<AgentTableColumnDefinition>(16, 16, true);
1819}
1820
1821/**
1822 * Create new agent table definition from another definition object
1823 */
1824AgentTableDefinition::AgentTableDefinition(AgentTableDefinition *src)
1825{
1826 m_name = (src->m_name != NULL) ? _tcsdup(src->m_name) : NULL;
1827 m_description = (src->m_description != NULL) ? _tcsdup(src->m_description) : NULL;
1828 m_instanceColumns = new StringList(src->m_instanceColumns);
1829 m_columns = new ObjectArray<AgentTableColumnDefinition>(16, 16, true);
1830 for(int i = 0; i < src->m_columns->size(); i++)
1831 {
1832 m_columns->add(new AgentTableColumnDefinition(src->m_columns->get(i)));
1833 }
1834}
1835/**
1836 * Destructor for agent table definition
1837 */
1838AgentTableDefinition::~AgentTableDefinition()
1839{
1840 safe_free(m_name);
1841 safe_free(m_description);
1842 delete m_instanceColumns;
1843 delete m_columns;
1844}
1845
1846/**
1847 * Fill NXCP message
1848 */
967893bb 1849UINT32 AgentTableDefinition::fillMessage(CSCPMessage *msg, UINT32 baseId)
86c126f5
VK
1850{
1851 msg->SetVariable(baseId + 1, m_name);
1852 msg->SetVariable(baseId + 2, m_description);
1853
1854 TCHAR *instanceColumns = m_instanceColumns->join(_T("|"));
1855 msg->SetVariable(baseId + 3, instanceColumns);
1856 free(instanceColumns);
1857
967893bb 1858 UINT32 varId = baseId + 4;
86c126f5
VK
1859 for(int i = 0; i < m_columns->size(); i++)
1860 {
1861 msg->SetVariable(varId++, m_columns->get(i)->m_name);
1862 msg->SetVariable(varId++, (WORD)m_columns->get(i)->m_dataType);
1863 }
1864
1865 msg->SetVariable(baseId, varId - baseId);
1866 return varId - baseId;
1867}