"Fossies" - the Fresh Open Source Software Archive 
Member "netxms-3.8.166/src/server/libnxsrv/agent.cpp" (23 Feb 2021, 80590 Bytes) of package /linux/misc/netxms-3.8.166.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "agent.cpp" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
3.8.120_vs_3.8.166.
1 /*
2 ** NetXMS - Network Management System
3 ** Server Library
4 ** Copyright (C) 2003-2021 Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
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
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 Lesser 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: agent.cpp
21 **
22 **/
23
24 #include "libnxsrv.h"
25 #include <stdarg.h>
26 #include <nxstat.h>
27
28 #ifndef _WIN32
29 #define _tell(f) lseek((f),0,SEEK_CUR)
30 #endif
31
32 #define DEBUG_TAG _T("agent.conn")
33
34 /**
35 * Constants
36 */
37 #define MAX_MSG_SIZE 268435456
38
39 /**
40 * Agent connection thread pool
41 */
42 LIBNXSRV_EXPORTABLE_VAR(ThreadPool *g_agentConnectionThreadPool) = nullptr;
43
44 /**
45 * Unique connection ID
46 */
47 static VolatileCounter s_connectionId = 0;
48
49 /**
50 * Static data
51 */
52 #ifdef _WITH_ENCRYPTION
53 static int m_iDefaultEncryptionPolicy = ENCRYPTION_ALLOWED;
54 #else
55 static int m_iDefaultEncryptionPolicy = ENCRYPTION_DISABLED;
56 #endif
57 static ObjectArray<BackgroundSocketPollerHandle> s_pollers(64, 64, Ownership::True);
58 static Mutex s_pollerListLock(true);
59 static bool s_shutdownMode = false;
60 static uint32_t s_maxConnectionsPerPoller = std::min(256, SOCKET_POLLER_MAX_SOCKETS - 1);
61
62 /**
63 * Set default encryption policy for agent communication
64 */
65 void LIBNXSRV_EXPORTABLE SetAgentDEP(int iPolicy)
66 {
67 #ifdef _WITH_ENCRYPTION
68 m_iDefaultEncryptionPolicy = iPolicy;
69 #endif
70 }
71
72 /**
73 * Set shutdown mode for agent connections
74 */
75 void LIBNXSRV_EXPORTABLE DisableAgentConnections()
76 {
77 s_pollerListLock.lock();
78 s_shutdownMode = true;
79 for(int i = 0; i < s_pollers.size(); i++)
80 s_pollers.get(i)->poller.shutdown();
81 s_pollerListLock.unlock();
82 }
83
84 /**
85 * Agent connection receiver
86 */
87 class AgentConnectionReceiver
88 {
89 private:
90 weak_ptr<AgentConnection> m_connection;
91 weak_ptr<AgentConnectionReceiver> m_self;
92 uint32_t m_debugId;
93 uint32_t m_recvTimeout;
94 shared_ptr<AbstractCommChannel> m_channel;
95 TCHAR m_threadPoolKey[16];
96 bool m_attached;
97
98 void debugPrintf(int level, const TCHAR *format, ...);
99
100 static void channelPollerCallback(BackgroundSocketPollResult pollResult, AbstractCommChannel *channel, const shared_ptr<AgentConnectionReceiver>& receiver);
101
102 bool readChannel();
103 MessageReceiverResult readMessage(bool allowChannelRead);
104 void finalize();
105
106 public:
107 shared_ptr<NXCPEncryptionContext> m_encryptionContext;
108 CommChannelMessageReceiver *m_messageReceiver;
109
110 static shared_ptr<AgentConnectionReceiver> create(const shared_ptr<AgentConnection>& connection)
111 {
112 auto receiver = make_shared<AgentConnectionReceiver>(connection);
113 receiver->m_self = receiver;
114 return receiver;
115 }
116
117 AgentConnectionReceiver(const shared_ptr<AgentConnection>& connection) : m_connection(connection), m_channel(connection->m_channel)
118 {
119 m_debugId = connection->m_debugId;
120 m_messageReceiver = new CommChannelMessageReceiver(m_channel, 4096, MAX_MSG_SIZE);
121 m_recvTimeout = connection->m_recvTimeout; // 7 minutes
122 _sntprintf(m_threadPoolKey, 16, _T("RECV-%u"), m_debugId);
123 m_attached = true;
124 }
125
126 ~AgentConnectionReceiver()
127 {
128 debugPrintf(7, _T("AgentConnectionReceiver destructor called (this=%p)"), this);
129 delete m_messageReceiver;
130 }
131
132 void start();
133
134 void detach()
135 {
136 m_attached = false;
137 m_connection.reset();
138 }
139 };
140
141 /**
142 * Write debug output in receiver
143 */
144 void AgentConnectionReceiver::debugPrintf(int level, const TCHAR *format, ...)
145 {
146 va_list args;
147 va_start(args, format);
148 nxlog_debug_tag_object2(DEBUG_TAG, m_debugId, level, format, args);
149 va_end(args);
150 }
151
152 /**
153 * Callback for channel poller
154 */
155 void AgentConnectionReceiver::channelPollerCallback(BackgroundSocketPollResult pollResult, AbstractCommChannel *channel, const shared_ptr<AgentConnectionReceiver>& receiver)
156 {
157 if (pollResult == BackgroundSocketPollResult::SUCCESS)
158 {
159 if (!s_shutdownMode && receiver->m_attached && receiver->readChannel())
160 {
161 channel->backgroundPoll(receiver->m_recvTimeout, channelPollerCallback, receiver);
162 return;
163 }
164 }
165 else
166 {
167 receiver->debugPrintf(5, _T("Channel poll error (%d)"), static_cast<int>(pollResult));
168 }
169 if (g_agentConnectionThreadPool != nullptr)
170 ThreadPoolExecuteSerialized(g_agentConnectionThreadPool, receiver->m_threadPoolKey, receiver, &AgentConnectionReceiver::finalize);
171 else
172 receiver->finalize();
173 }
174
175 /**
176 * Start receiver
177 */
178 void AgentConnectionReceiver::start()
179 {
180 m_channel->backgroundPoll(m_recvTimeout, channelPollerCallback, m_self.lock());
181 }
182
183 /**
184 * Read messages from channel
185 */
186 bool AgentConnectionReceiver::readChannel()
187 {
188 MessageReceiverResult result = readMessage(true);
189 while(result == MSGRECV_SUCCESS)
190 result = readMessage(false);
191 return (result == MSGRECV_WANT_READ) || (result == MSGRECV_WANT_WRITE);
192 }
193
194 /**
195 * Read single message from channel
196 */
197 MessageReceiverResult AgentConnectionReceiver::readMessage(bool allowChannelRead)
198 {
199 // Receive raw message
200 MessageReceiverResult result;
201 NXCPMessage *msg = m_messageReceiver->readMessage(0, &result, allowChannelRead);
202 if ((result == MSGRECV_WANT_READ) || (result == MSGRECV_WANT_WRITE))
203 return result;
204
205 // Check for decryption error
206 if (result == MSGRECV_DECRYPTION_FAILURE)
207 {
208 debugPrintf(6, _T("Unable to decrypt received message"));
209 return MSGRECV_SUCCESS; // continue reading
210 }
211
212 shared_ptr<AgentConnection> connection = m_connection.lock();
213 if (connection == nullptr)
214 {
215 delete msg;
216 return MSGRECV_COMM_FAILURE; // Parent connection was destroyed
217 }
218
219 // Check for timeout
220 if (result == MSGRECV_TIMEOUT)
221 {
222 if (connection->m_fileUploadInProgress)
223 return MSGRECV_WANT_READ; // Receive timeout may occur when uploading large files via slow links
224 debugPrintf(6, _T("Timed out waiting for message"));
225 return MSGRECV_TIMEOUT;
226 }
227
228 // Receive error
229 if (msg == nullptr)
230 {
231 if (result == MSGRECV_CLOSED)
232 debugPrintf(6, _T("Communication channel shutdown"));
233 else
234 debugPrintf(6, _T("Message receiving error (%s)"), AbstractMessageReceiver::resultToText(result));
235 return result;
236 }
237
238 if (IsShutdownInProgress())
239 {
240 debugPrintf(6, _T("Process shutdown"));
241 delete msg;
242 return MSGRECV_COMM_FAILURE;
243 }
244
245 if (msg->isBinary())
246 {
247 if (nxlog_get_debug_level_tag_object(DEBUG_TAG, m_debugId) >= 6)
248 {
249 TCHAR buffer[64];
250 debugPrintf(6, _T("Received raw message %s (%d) from agent at %s"),
251 NXCPMessageCodeName(msg->getCode(), buffer), msg->getId(), connection->m_addr.toString().cstr());
252 }
253
254 if ((msg->getCode() == CMD_FILE_DATA) && (msg->getId() == connection->m_downloadRequestId))
255 {
256 if (g_agentConnectionThreadPool != nullptr)
257 {
258 TCHAR key[64];
259 _sntprintf(key, 64, _T("FileTransfer_%p"), this);
260 ThreadPoolExecuteSerialized(g_agentConnectionThreadPool, key, connection, &AgentConnection::processFileData, msg);
261 }
262 else
263 {
264 connection->processFileData(msg);
265 }
266 msg = nullptr; // Prevent delete
267 }
268 else if ((msg->getCode() == CMD_ABORT_FILE_TRANSFER) && (msg->getId() == connection->m_downloadRequestId))
269 {
270 if (g_agentConnectionThreadPool != nullptr)
271 {
272 TCHAR key[64];
273 _sntprintf(key, 64, _T("FileTransfer_%p"), this);
274 ThreadPoolExecuteSerialized(g_agentConnectionThreadPool, key, connection, &AgentConnection::processFileTransferAbort, msg);
275 }
276 else
277 {
278 connection->processFileTransferAbort(msg);
279 }
280 msg = nullptr; // Prevent delete
281 }
282 else if (msg->getCode() == CMD_TCP_PROXY_DATA)
283 {
284 connection->processTcpProxyData(msg->getId(), msg->getBinaryData(), msg->getBinaryDataSize());
285 }
286 delete msg;
287 }
288 else if (msg->isControl())
289 {
290 if (nxlog_get_debug_level_tag_object(DEBUG_TAG, m_debugId) >= 6)
291 {
292 TCHAR buffer[64];
293 debugPrintf(6, _T("Received control message %s from agent at %s"),
294 NXCPMessageCodeName(msg->getCode(), buffer), connection->m_addr.toString().cstr());
295 }
296 connection->m_pMsgWaitQueue->put(msg);
297 }
298 else
299 {
300 if (nxlog_get_debug_level_tag_object(DEBUG_TAG, m_debugId) >= 6)
301 {
302 TCHAR buffer[64];
303 debugPrintf(6, _T("Received message %s (%d) from agent at %s"),
304 NXCPMessageCodeName(msg->getCode(), buffer), msg->getId(), (const TCHAR *)connection->m_addr.toString());
305 }
306 switch(msg->getCode())
307 {
308 case CMD_REQUEST_COMPLETED:
309 case CMD_SESSION_KEY:
310 connection->m_pMsgWaitQueue->put(msg);
311 break;
312 case CMD_TRAP:
313 if (g_agentConnectionThreadPool != nullptr)
314 {
315 TCHAR key[64];
316 _sntprintf(key, 64, _T("EventProc_%p"), this);
317 ThreadPoolExecuteSerialized(g_agentConnectionThreadPool, key, connection, &AgentConnection::onTrapCallback, msg);
318 }
319 else
320 {
321 delete msg;
322 }
323 break;
324 case CMD_SYSLOG_RECORDS:
325 if (g_agentConnectionThreadPool != nullptr)
326 {
327 TCHAR key[64];
328 _sntprintf(key, 64, _T("Syslog_%p"), this);
329 ThreadPoolExecuteSerialized(g_agentConnectionThreadPool, key, connection, &AgentConnection::onSyslogMessageCallback, msg);
330 }
331 else
332 {
333 delete msg;
334 }
335 break;
336 case CMD_WINDOWS_EVENT:
337 if (g_agentConnectionThreadPool != nullptr)
338 {
339 TCHAR key[64];
340 _sntprintf(key, 64, _T("WinEvent_%p"), this);
341 ThreadPoolExecuteSerialized(g_agentConnectionThreadPool, key, connection, &AgentConnection::onWindowsEventCallback, msg);
342 }
343 else
344 {
345 delete msg;
346 }
347 break;
348 case CMD_PUSH_DCI_DATA:
349 if (g_agentConnectionThreadPool != nullptr)
350 {
351 ThreadPoolExecute(g_agentConnectionThreadPool, connection, &AgentConnection::onDataPushCallback, msg);
352 }
353 else
354 {
355 delete msg;
356 }
357 break;
358 case CMD_DCI_DATA:
359 if (g_agentConnectionThreadPool != nullptr)
360 {
361 ThreadPoolExecute(g_agentConnectionThreadPool, connection, &AgentConnection::processCollectedDataCallback, msg);
362 }
363 else
364 {
365 NXCPMessage response(CMD_REQUEST_COMPLETED, msg->getId(), connection->m_nProtocolVersion);
366 response.setField(VID_RCC, ERR_INTERNAL_ERROR);
367 connection->sendMessage(&response);
368 delete msg;
369 }
370 break;
371 case CMD_GET_SSH_KEYS:
372 if (g_agentConnectionThreadPool != nullptr)
373 {
374 ThreadPoolExecute(g_agentConnectionThreadPool, connection, &AgentConnection::getSshKeysCallback, msg);
375 }
376 else
377 {
378 NXCPMessage response(CMD_REQUEST_COMPLETED, msg->getId(), connection->m_nProtocolVersion);
379 response.setField(VID_RCC, ERR_INTERNAL_ERROR);
380 connection->sendMessage(&response);
381 delete msg;
382 }
383 break;
384 case CMD_FILE_MONITORING:
385 connection->onFileMonitoringData(msg);
386 delete msg;
387 break;
388 case CMD_SNMP_TRAP:
389 if (g_agentConnectionThreadPool != nullptr)
390 {
391 TCHAR key[64];
392 _sntprintf(key, 64, _T("SNMPTrap_%p"), this);
393 ThreadPoolExecuteSerialized(g_agentConnectionThreadPool, key, connection, &AgentConnection::onSnmpTrapCallback, msg);
394 }
395 else
396 {
397 delete msg;
398 }
399 break;
400 case CMD_CLOSE_TCP_PROXY:
401 connection->processTcpProxyData(msg->getFieldAsUInt32(VID_CHANNEL_ID), nullptr, 0);
402 delete msg;
403 break;
404 default:
405 if (connection->processCustomMessage(msg))
406 delete msg;
407 else
408 connection->m_pMsgWaitQueue->put(msg);
409 break;
410 }
411 }
412 return MSGRECV_SUCCESS;
413 }
414
415 /**
416 * Finalize receiver cleanup
417 */
418 void AgentConnectionReceiver::finalize()
419 {
420 debugPrintf(6, _T("Receiver loop terminated"));
421
422 // Close socket and mark connection as disconnected
423 m_channel->close();
424
425 shared_ptr<AgentConnection> connection = m_connection.lock();
426 if (connection != nullptr)
427 {
428 connection->lock();
429 if (connection->m_hCurrFile != -1)
430 {
431 _close(connection->m_hCurrFile);
432 connection->m_hCurrFile = -1;
433 connection->onFileDownload(false);
434 }
435 else if (connection->m_sendToClientMessageCallback != nullptr)
436 {
437 connection->m_sendToClientMessageCallback = nullptr;
438 connection->onFileDownload(false);
439 }
440
441 debugPrintf(6, _T("Closing communication channel"));
442 connection->m_isConnected = false;
443 connection->unlock();
444
445 connection->onDisconnect();
446 }
447
448 debugPrintf(6, _T("Receiver cleanup completed"));
449 }
450
451 /**
452 * Constructor for AgentConnection
453 */
454 AgentConnection::AgentConnection(const InetAddress& addr, uint16_t port, const TCHAR *secret, bool allowCompression)
455 {
456 #ifdef _WIN32
457 m_self = new weak_ptr<AgentConnection>();
458 #endif
459 m_debugId = InterlockedIncrement(&s_connectionId);
460 m_addr = addr;
461 m_port = port;
462 if ((secret != nullptr) && (*secret != 0))
463 {
464 #ifdef UNICODE
465 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, secret, -1, m_secret, MAX_SECRET_LENGTH, nullptr, nullptr);
466 m_secret[MAX_SECRET_LENGTH - 1] = 0;
467 #else
468 strlcpy(m_secret, secret, MAX_SECRET_LENGTH);
469 #endif
470 DecryptPasswordA("netxms", m_secret, m_secret, MAX_SECRET_LENGTH);
471 }
472 else
473 {
474 m_secret[0] = 0;
475 }
476 m_allowCompression = allowCompression;
477 m_tLastCommandTime = 0;
478 m_pMsgWaitQueue = new MsgWaitQueue;
479 m_requestId = 0;
480 m_connectionTimeout = 5000; // 5 seconds
481 m_commandTimeout = 5000; // Default timeout 5 seconds
482 m_recvTimeout = 420000; // 7 minutes
483 m_isConnected = false;
484 m_mutexDataLock = MutexCreate();
485 m_mutexSocketWrite = MutexCreate();
486 m_encryptionPolicy = m_iDefaultEncryptionPolicy;
487 m_useProxy = false;
488 m_proxyPort = 4700;
489 m_proxySecret[0] = 0;
490 m_nProtocolVersion = NXCP_VERSION;
491 m_hCurrFile = -1;
492 m_deleteFileOnDownloadFailure = true;
493 m_condFileDownload = ConditionCreate(true);
494 m_fileDownloadSucceeded = false;
495 m_fileUploadInProgress = false;
496 m_fileUpdateConnection = false;
497 m_sendToClientMessageCallback = nullptr;
498 m_downloadRequestId = 0;
499 m_downloadProgressCallback = nullptr;
500 m_downloadProgressCallbackArg = nullptr;
501 m_bulkDataProcessing = 0;
502 m_controlServer = false;
503 m_masterServer = false;
504 }
505
506 /**
507 * Destructor
508 */
509 AgentConnection::~AgentConnection()
510 {
511 debugPrintf(7, _T("AgentConnection destructor called (this=%p)"), this);
512
513 if (m_receiver != nullptr)
514 m_receiver->detach();
515
516 delete m_pMsgWaitQueue;
517
518 if (m_hCurrFile != -1)
519 {
520 _close(m_hCurrFile);
521 onFileDownload(false);
522 }
523 else if (m_sendToClientMessageCallback != nullptr)
524 {
525 onFileDownload(false);
526 }
527
528 if (m_channel != nullptr)
529 m_channel->shutdown();
530
531 MutexDestroy(m_mutexDataLock);
532 MutexDestroy(m_mutexSocketWrite);
533 ConditionDestroy(m_condFileDownload);
534 }
535
536 /**
537 * Write debug output
538 */
539 void AgentConnection::debugPrintf(int level, const TCHAR *format, ...)
540 {
541 va_list args;
542 va_start(args, format);
543 nxlog_debug_tag_object2(DEBUG_TAG, m_debugId, level, format, args);
544 va_end(args);
545 }
546
547 /**
548 * Create channel. Default implementation creates socket channel.
549 */
550 shared_ptr<AbstractCommChannel> AgentConnection::createChannel()
551 {
552 if (s_shutdownMode)
553 return shared_ptr<AbstractCommChannel>();
554
555 SOCKET s = m_useProxy ?
556 ConnectToHost(m_proxyAddr, m_proxyPort, m_connectionTimeout) :
557 ConnectToHost(m_addr, m_port, m_connectionTimeout);
558
559 // Connect to server
560 if (s == INVALID_SOCKET)
561 {
562 TCHAR buffer[64];
563 debugPrintf(5, _T("Cannot establish connection with agent at %s:%d"),
564 m_useProxy ? m_proxyAddr.toString(buffer) : m_addr.toString(buffer),
565 (int)(m_useProxy ? m_proxyPort : m_port));
566 return shared_ptr<AbstractCommChannel>();
567 }
568
569 // Select socket poller
570 BackgroundSocketPollerHandle *sp = nullptr;
571 s_pollerListLock.lock();
572 if (s_shutdownMode)
573 {
574 shutdown(s, SHUT_RDWR);
575 closesocket(s);
576 s_pollerListLock.unlock();
577 return shared_ptr<AbstractCommChannel>();
578 }
579 for(int i = 0; i < s_pollers.size(); i++)
580 {
581 BackgroundSocketPollerHandle *p = s_pollers.get(i);
582 if (static_cast<uint32_t>(InterlockedIncrement(&p->usageCount)) < s_maxConnectionsPerPoller)
583 {
584 sp = p;
585 break;
586 }
587 InterlockedDecrement(&p->usageCount);
588 }
589 if (sp == nullptr)
590 {
591 sp = new BackgroundSocketPollerHandle();
592 sp->usageCount = 1;
593 s_pollers.add(sp);
594 }
595 s_pollerListLock.unlock();
596
597 return make_shared<SocketCommChannel>(s, sp);
598 }
599
600 /**
601 * Acquire communication channel. Caller must call decRefCount to release channel.
602 */
603 shared_ptr<AbstractCommChannel> AgentConnection::acquireChannel()
604 {
605 lock();
606 shared_ptr<AbstractCommChannel> channel(m_channel);
607 unlock();
608 return channel;
609 }
610
611 /**
612 * Acquire encryption context
613 */
614 shared_ptr<NXCPEncryptionContext> AgentConnection::acquireEncryptionContext()
615 {
616 lock();
617 shared_ptr<NXCPEncryptionContext> ctx = (m_receiver != nullptr) ? m_receiver->m_encryptionContext : shared_ptr<NXCPEncryptionContext>();
618 unlock();
619 return ctx;
620 }
621
622 /**
623 * Connect to agent
624 */
625 bool AgentConnection::connect(RSA *serverKey, uint32_t *error, uint32_t *socketError, uint64_t serverId)
626 {
627 TCHAR szBuffer[256];
628 bool success = false;
629 bool forceEncryption = false;
630 bool secondPass = false;
631 uint32_t dwError = 0;
632
633 if (error != nullptr)
634 *error = ERR_INTERNAL_ERROR;
635
636 if (socketError != nullptr)
637 *socketError = 0;
638
639 if (s_shutdownMode)
640 return false;
641
642 lock();
643
644 // Check if already connected
645 if (m_isConnected)
646 {
647 unlock();
648 return false;
649 }
650
651 // Wait for receiver thread from previous connection, if any
652 if (m_receiver != nullptr)
653 {
654 m_receiver->detach();
655 m_receiver.reset();
656 }
657
658 // Detach from existing channel if any
659 m_channel.reset();
660
661 unlock();
662
663 auto channel = createChannel();
664 if (channel == nullptr)
665 {
666 debugPrintf(6, _T("Cannot create communication channel"));
667 dwError = ERR_CONNECT_FAILED;
668 goto connect_cleanup;
669 }
670
671 lock();
672 m_channel = channel;
673 unlock();
674
675 if (!NXCPGetPeerProtocolVersion(m_channel, &m_nProtocolVersion, m_mutexSocketWrite))
676 {
677 debugPrintf(6, _T("Protocol version negotiation failed"));
678 dwError = ERR_INTERNAL_ERROR;
679 goto connect_cleanup;
680 }
681 debugPrintf(6, _T("Using NXCP version %d"), m_nProtocolVersion);
682
683 // Start receiver thread
684 lock();
685 m_receiver = AgentConnectionReceiver::create(self());
686 m_receiver->start();
687 unlock();
688
689 // Setup encryption
690 setup_encryption:
691 if ((m_encryptionPolicy == ENCRYPTION_PREFERRED) ||
692 (m_encryptionPolicy == ENCRYPTION_REQUIRED) ||
693 forceEncryption) // Agent require encryption
694 {
695 if (serverKey != nullptr)
696 {
697 dwError = setupEncryption(serverKey);
698 if ((dwError != ERR_SUCCESS) &&
699 ((m_encryptionPolicy == ENCRYPTION_REQUIRED) || forceEncryption))
700 goto connect_cleanup;
701 }
702 else
703 {
704 if ((m_encryptionPolicy == ENCRYPTION_REQUIRED) || forceEncryption)
705 {
706 dwError = ERR_ENCRYPTION_REQUIRED;
707 goto connect_cleanup;
708 }
709 }
710 }
711
712 // Authenticate itself to agent
713 if ((dwError = authenticate(m_useProxy && !secondPass)) != ERR_SUCCESS)
714 {
715 if ((dwError == ERR_ENCRYPTION_REQUIRED) &&
716 (m_encryptionPolicy != ENCRYPTION_DISABLED))
717 {
718 forceEncryption = true;
719 goto setup_encryption;
720 }
721 debugPrintf(5, _T("Authentication to agent %s failed (%s)"), m_addr.toString(szBuffer),
722 AgentErrorCodeToText(dwError));
723 goto connect_cleanup;
724 }
725
726 // Test connectivity and inform agent about server capabilities
727 if ((dwError = setServerCapabilities()) != ERR_SUCCESS)
728 {
729 if ((dwError == ERR_ENCRYPTION_REQUIRED) &&
730 (m_encryptionPolicy != ENCRYPTION_DISABLED))
731 {
732 forceEncryption = true;
733 goto setup_encryption;
734 }
735 if (dwError != ERR_UNKNOWN_COMMAND) // Older agents may not support enable IPv6 command
736 {
737 debugPrintf(5, _T("Communication with agent %s failed (%s)"), m_addr.toString(szBuffer), AgentErrorCodeToText(dwError));
738 goto connect_cleanup;
739 }
740 }
741
742 if (m_useProxy && !secondPass)
743 {
744 dwError = setupProxyConnection();
745 if (dwError != ERR_SUCCESS)
746 goto connect_cleanup;
747 lock();
748 m_receiver->m_encryptionContext.reset();
749 unlock();
750
751 debugPrintf(6, _T("Proxy connection established"));
752
753 // Renegotiate NXCP version with actual target agent
754 NXCP_MESSAGE msg;
755 msg.id = 0;
756 msg.numFields = 0;
757 msg.size = htonl(NXCP_HEADER_SIZE);
758 msg.code = htons(CMD_GET_NXCP_CAPS);
759 msg.flags = htons(MF_CONTROL | MF_NXCP_VERSION(NXCP_VERSION));
760 if (m_channel->send(&msg, NXCP_HEADER_SIZE, m_mutexSocketWrite) == NXCP_HEADER_SIZE)
761 {
762 NXCPMessage *rsp = m_pMsgWaitQueue->waitForMessage(CMD_NXCP_CAPS, 0, m_commandTimeout);
763 if (rsp != nullptr)
764 {
765 if (rsp->isControl())
766 m_nProtocolVersion = rsp->getControlData() >> 24;
767 else
768 m_nProtocolVersion = 1; // assume that peer doesn't understand CMD_GET_NXCP_CAPS message
769 delete rsp;
770 }
771 else
772 {
773 // assume that peer doesn't understand CMD_GET_NXCP_CAPS message
774 // and set version number to 1
775 m_nProtocolVersion = 1;
776 }
777 debugPrintf(6, _T("Using NXCP version %d after re-negotioation"), m_nProtocolVersion);
778 }
779 else
780 {
781 debugPrintf(6, _T("Protocol version re-negotiation failed - cannot send CMD_GET_NXCP_CAPS message"));
782 dwError = ERR_CONNECTION_BROKEN;
783 goto connect_cleanup;
784 }
785
786 secondPass = true;
787 forceEncryption = false;
788 goto setup_encryption;
789 }
790
791 if (serverId != 0)
792 setServerId(serverId);
793
794 success = true;
795 dwError = ERR_SUCCESS;
796
797 connect_cleanup:
798 if (!success)
799 {
800 if (socketError != nullptr)
801 *socketError = (UINT32)WSAGetLastError();
802
803 lock();
804
805 if (m_receiver != nullptr)
806 {
807 m_receiver->detach();
808 m_receiver.reset();
809 }
810
811 if (m_channel != nullptr)
812 {
813 m_channel->shutdown();
814 m_channel->close();
815 m_channel.reset();
816 }
817
818 unlock();
819 }
820 m_isConnected = success;
821 if (error != nullptr)
822 *error = dwError;
823 return success;
824 }
825
826 /**
827 * Disconnect from agent
828 */
829 void AgentConnection::disconnect()
830 {
831 debugPrintf(6, _T("disconnect() called"));
832 lock();
833 if (m_hCurrFile != -1)
834 {
835 _close(m_hCurrFile);
836 m_hCurrFile = -1;
837 onFileDownload(false);
838 }
839 else if (m_sendToClientMessageCallback != nullptr)
840 {
841 m_sendToClientMessageCallback = nullptr;
842 onFileDownload(false);
843 }
844
845 if (m_channel != nullptr)
846 {
847 m_channel->shutdown();
848 m_channel.reset();
849 }
850 m_isConnected = false;
851 unlock();
852 debugPrintf(6, _T("Disconnect completed"));
853 }
854
855 /**
856 * Disconnect handler. Default implementation does nothing.
857 */
858 void AgentConnection::onDisconnect()
859 {
860 }
861
862 /**
863 * Set shared secret for authentication (nullptr will disable authentication)
864 */
865 void AgentConnection::setSharedSecret(const TCHAR *secret)
866 {
867 if ((secret != nullptr) && (*secret != 0))
868 {
869 #ifdef UNICODE
870 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, secret, -1, m_secret, MAX_SECRET_LENGTH, nullptr, nullptr);
871 m_secret[MAX_SECRET_LENGTH - 1] = 0;
872 #else
873 strlcpy(m_secret, secret, MAX_SECRET_LENGTH);
874 #endif
875 DecryptPasswordA("netxms", m_secret, m_secret, MAX_SECRET_LENGTH);
876 }
877 else
878 {
879 m_secret[0] = 0;
880 }
881 }
882
883 /**
884 * Get interface list from agent
885 */
886 InterfaceList *AgentConnection::getInterfaceList()
887 {
888 StringList *data;
889 if (getList(_T("Net.InterfaceList"), &data) != ERR_SUCCESS)
890 return nullptr;
891
892 InterfaceList *pIfList = new InterfaceList(data->size());
893
894 // Parse result set. Each line should have the following format:
895 // index ip_address/mask_bits iftype mac_address name
896 // index ip_address/mask_bits iftype(mtu) mac_address name
897 for(int i = 0; i < data->size(); i++)
898 {
899 TCHAR *line = MemCopyString(data->get(i));
900 TCHAR *pBuf = line;
901 UINT32 ifIndex = 0;
902
903 // Index
904 TCHAR *pChar = _tcschr(pBuf, ' ');
905 if (pChar != nullptr)
906 {
907 *pChar = 0;
908 ifIndex = _tcstoul(pBuf, nullptr, 10);
909 pBuf = pChar + 1;
910 }
911
912 bool newInterface = false;
913 InterfaceInfo *iface = pIfList->findByIfIndex(ifIndex);
914 if (iface == nullptr)
915 {
916 iface = new InterfaceInfo(ifIndex);
917 newInterface = true;
918 }
919
920 // Address and mask
921 pChar = _tcschr(pBuf, _T(' '));
922 if (pChar != nullptr)
923 {
924 TCHAR *pSlash;
925 static TCHAR defaultMask[] = _T("24");
926
927 *pChar = 0;
928 pSlash = _tcschr(pBuf, _T('/'));
929 if (pSlash != nullptr)
930 {
931 *pSlash = 0;
932 pSlash++;
933 }
934 else // Just a paranoia protection, should'n happen if agent working correctly
935 {
936 pSlash = defaultMask;
937 }
938 InetAddress addr = InetAddress::parse(pBuf);
939 if (addr.isValid())
940 {
941 addr.setMaskBits(_tcstol(pSlash, nullptr, 10));
942 // Agent may return 0.0.0.0/0 for interfaces without IP address
943 if ((addr.getFamily() != AF_INET) || (addr.getAddressV4() != 0))
944 iface->ipAddrList.add(addr);
945 }
946 pBuf = pChar + 1;
947 }
948
949 if (newInterface)
950 {
951 // Interface type
952 pChar = _tcschr(pBuf, ' ');
953 if (pChar != nullptr)
954 {
955 *pChar = 0;
956
957 TCHAR *eptr;
958 iface->type = _tcstoul(pBuf, &eptr, 10);
959
960 // newer agents can return if_type(mtu)
961 if (*eptr == _T('('))
962 {
963 pBuf = eptr + 1;
964 eptr = _tcschr(pBuf, _T(')'));
965 if (eptr != nullptr)
966 {
967 *eptr = 0;
968 iface->mtu = _tcstol(pBuf, nullptr, 10);
969 }
970 }
971
972 pBuf = pChar + 1;
973 }
974
975 // MAC address
976 pChar = _tcschr(pBuf, ' ');
977 if (pChar != nullptr)
978 {
979 *pChar = 0;
980 StrToBin(pBuf, iface->macAddr, MAC_ADDR_LENGTH);
981 pBuf = pChar + 1;
982 }
983
984 // Name (set description to name)
985 _tcslcpy(iface->name, pBuf, MAX_DB_STRING);
986 _tcslcpy(iface->description, pBuf, MAX_DB_STRING);
987
988 pIfList->add(iface);
989 }
990 MemFree(line);
991 }
992
993 delete data;
994 return pIfList;
995 }
996
997 /**
998 * Get parameter value
999 */
1000 uint32_t AgentConnection::getParameter(const TCHAR *param, TCHAR *buffer, size_t size)
1001 {
1002 if (!m_isConnected)
1003 return ERR_NOT_CONNECTED;
1004
1005 NXCPMessage msg(m_nProtocolVersion);
1006 uint32_t requestId = generateRequestId();
1007 msg.setCode(CMD_GET_PARAMETER);
1008 msg.setId(requestId);
1009 msg.setField(VID_PARAMETER, param);
1010
1011 uint32_t rcc;
1012 if (sendMessage(&msg))
1013 {
1014 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, requestId, m_commandTimeout);
1015 if (response != nullptr)
1016 {
1017 rcc = response->getFieldAsUInt32(VID_RCC);
1018 if (rcc == ERR_SUCCESS)
1019 {
1020 if (response->isFieldExist(VID_VALUE))
1021 {
1022 response->getFieldAsString(VID_VALUE, buffer, size);
1023 }
1024 else
1025 {
1026 rcc = ERR_MALFORMED_RESPONSE;
1027 debugPrintf(3, _T("Malformed response to CMD_GET_PARAMETER"));
1028 }
1029 }
1030 delete response;
1031 }
1032 else
1033 {
1034 rcc = ERR_REQUEST_TIMEOUT;
1035 }
1036 }
1037 else
1038 {
1039 rcc = ERR_CONNECTION_BROKEN;
1040 }
1041 return rcc;
1042 }
1043
1044 /**
1045 * Query web service. Request type determines if parameter or list mode will be used.
1046 * Only first element of "pathList" will be used for list request.
1047 * "results" argument should point to StringMap for parameters request and to StringList for list request.
1048 * For list first element form parameters list will be used. If parameters list is empty
1049 * "/" will be used for XML and JSON types and "(*)" will be used for text type.
1050 */
1051 uint32_t AgentConnection::queryWebService(WebServiceRequestType requestType, const TCHAR *url, uint32_t requestTimeout,
1052 uint32_t retentionTime, const TCHAR *login, const TCHAR *password, WebServiceAuthType authType, const StringMap& headers,
1053 const StringList& pathList, bool verifyCert, bool verifyHost, bool forcePlainTextParser, void *results)
1054 {
1055 if (!m_isConnected)
1056 return ERR_NOT_CONNECTED;
1057
1058 NXCPMessage msg(m_nProtocolVersion);
1059 uint32_t requestId = generateRequestId();
1060 msg.setCode(CMD_QUERY_WEB_SERVICE);
1061 msg.setId(requestId);
1062 msg.setField(VID_URL, url);
1063 msg.setField(VID_TIMEOUT, requestTimeout);
1064 msg.setField(VID_RETENTION_TIME, retentionTime);
1065 msg.setField(VID_LOGIN_NAME, login);
1066 msg.setField(VID_PASSWORD, password);
1067 msg.setField(VID_AUTH_TYPE, static_cast<uint16_t>(authType));
1068 msg.setField(VID_VERIFY_CERT, verifyCert);
1069 msg.setField(VID_VERIFY_HOST, verifyHost);
1070 msg.setField(VID_FORCE_PLAIN_TEXT_PARSER, forcePlainTextParser);
1071 headers.fillMessage(&msg, VID_NUM_HEADERS, VID_HEADERS_BASE);
1072 msg.setField(VID_REQUEST_TYPE, static_cast<uint16_t>(requestType));
1073 pathList.fillMessage(&msg, VID_PARAM_LIST_BASE, VID_NUM_PARAMETERS);
1074
1075 uint32_t rcc;
1076 if (sendMessage(&msg))
1077 {
1078 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, requestId, m_commandTimeout);
1079 if (response != nullptr)
1080 {
1081 rcc = response->getFieldAsUInt32(VID_RCC);
1082 if (rcc == ERR_SUCCESS)
1083 {
1084 if ((requestType == WebServiceRequestType::PARAMETER) && response->isFieldExist(VID_NUM_PARAMETERS))
1085 {
1086 static_cast<StringMap*>(results)->loadMessage(response, VID_NUM_PARAMETERS, VID_PARAM_LIST_BASE);
1087 }
1088 else if ((requestType == WebServiceRequestType::LIST) && response->isFieldExist(VID_NUM_ELEMENTS))
1089 {
1090 static_cast<StringList*>(results)->loadMessage(response, VID_ELEMENT_LIST_BASE, VID_NUM_ELEMENTS);
1091 }
1092 else
1093 {
1094 rcc = ERR_MALFORMED_RESPONSE;
1095 debugPrintf(4, _T("Malformed response to CMD_QUERY_WEB_SERVICE"));
1096 }
1097 }
1098 delete response;
1099 }
1100 else
1101 {
1102 rcc = ERR_REQUEST_TIMEOUT;
1103 }
1104 }
1105 else
1106 {
1107 rcc = ERR_CONNECTION_BROKEN;
1108 }
1109 return rcc;
1110 }
1111
1112 /**
1113 * Method is used for both - to get parameters and to get list.
1114 * For list first element form parameters list will be used. If parameters list is empty:
1115 * "/" will be used for XML and JSOn types and "(*)" will be used for text type.
1116 */
1117 uint32_t AgentConnection::queryWebServiceList(const TCHAR *url, uint32_t requestTimeout, uint32_t retentionTime, const TCHAR *login, const TCHAR *password,
1118 WebServiceAuthType authType, const StringMap& headers, const TCHAR *path, bool verifyCert, bool verifyHost, bool forcePlainTextParser, StringList *results)
1119 {
1120 StringList pathList;
1121 pathList.add(path);
1122 return queryWebService(WebServiceRequestType::LIST, url, requestTimeout, retentionTime, login, password, authType, headers,
1123 pathList, verifyCert, verifyHost, forcePlainTextParser, results);
1124 }
1125
1126 /**
1127 * Query web service for parameters
1128 */
1129 uint32_t AgentConnection::queryWebServiceParameters(const TCHAR *url, uint32_t requestTimeout, uint32_t retentionTime, const TCHAR *login, const TCHAR *password,
1130 WebServiceAuthType authType, const StringMap& headers, const StringList& pathList, bool verifyCert, bool verifyHost, bool forcePlainTextParser, StringMap *results)
1131 {
1132 return queryWebService(WebServiceRequestType::PARAMETER, url, requestTimeout, retentionTime, login, password, authType, headers,
1133 pathList, verifyCert, verifyHost, forcePlainTextParser, results);
1134 }
1135
1136 /**
1137 * Get ARP cache
1138 */
1139 ArpCache *AgentConnection::getArpCache()
1140 {
1141 StringList *data;
1142 if (getList(_T("Net.ArpCache"), &data) != ERR_SUCCESS)
1143 return nullptr;
1144
1145 // Create empty structure
1146 ArpCache *arpCache = new ArpCache();
1147
1148 TCHAR szByte[4], *pBuf, *pChar;
1149 szByte[2] = 0;
1150
1151 // Parse data lines
1152 // Each line has form of XXXXXXXXXXXX a.b.c.d n
1153 // where XXXXXXXXXXXX is a MAC address (12 hexadecimal digits)
1154 // a.b.c.d is an IP address in decimal dotted notation
1155 // n is an interface index
1156 for(int i = 0; i < data->size(); i++)
1157 {
1158 TCHAR *line = MemCopyString(data->get(i));
1159 pBuf = line;
1160 if (_tcslen(pBuf) < 20) // Invalid line
1161 {
1162 debugPrintf(7, _T("AgentConnection::getArpCache(): invalid line received from agent (\"%s\")"), line);
1163 free(line);
1164 continue;
1165 }
1166
1167 // MAC address
1168 BYTE macAddr[6];
1169 for(int j = 0; j < 6; j++)
1170 {
1171 memcpy(szByte, pBuf, sizeof(TCHAR) * 2);
1172 macAddr[j] = (BYTE)_tcstol(szByte, nullptr, 16);
1173 pBuf += 2;
1174 }
1175
1176 // IP address
1177 while(*pBuf == ' ')
1178 pBuf++;
1179 pChar = _tcschr(pBuf, _T(' '));
1180 if (pChar != nullptr)
1181 *pChar = 0;
1182 InetAddress ipAddr = InetAddress::parse(pBuf);
1183
1184 // Interface index
1185 UINT32 ifIndex = (pChar != nullptr) ? _tcstoul(pChar + 1, nullptr, 10) : 0;
1186
1187 arpCache->addEntry(ipAddr, MacAddress(macAddr, 6), ifIndex);
1188
1189 free(line);
1190 }
1191
1192 delete data;
1193 return arpCache;
1194 }
1195
1196 /**
1197 * Send dummy command to agent (can be used for keepalive)
1198 */
1199 uint32_t AgentConnection::nop()
1200 {
1201 if (!m_isConnected)
1202 return ERR_CONNECTION_BROKEN;
1203
1204 NXCPMessage msg(m_nProtocolVersion);
1205 uint32_t requestId = generateRequestId();
1206 msg.setCode(CMD_KEEPALIVE);
1207 msg.setId(requestId);
1208 if (sendMessage(&msg))
1209 return waitForRCC(requestId, m_commandTimeout);
1210 else
1211 return ERR_CONNECTION_BROKEN;
1212 }
1213
1214 /**
1215 * inform agent about server capabilities
1216 */
1217 uint32_t AgentConnection::setServerCapabilities()
1218 {
1219 NXCPMessage msg(m_nProtocolVersion);
1220 uint32_t requestId = generateRequestId();
1221 msg.setCode(CMD_SET_SERVER_CAPABILITIES);
1222 msg.setField(VID_ENABLED, (INT16)1); // Enables IPv6 on pre-2.0 agents
1223 msg.setField(VID_IPV6_SUPPORT, (INT16)1);
1224 msg.setField(VID_BULK_RECONCILIATION, (INT16)1);
1225 msg.setField(VID_ENABLE_COMPRESSION, (INT16)(m_allowCompression ? 1 : 0));
1226 msg.setId(requestId);
1227 if (!sendMessage(&msg))
1228 return ERR_CONNECTION_BROKEN;
1229
1230 NXCPMessage *response = m_pMsgWaitQueue->waitForMessage(CMD_REQUEST_COMPLETED, requestId, m_commandTimeout);
1231 if (response == nullptr)
1232 return ERR_REQUEST_TIMEOUT;
1233
1234 uint32_t rcc = response->getFieldAsUInt32(VID_RCC);
1235 if (rcc == ERR_SUCCESS)
1236 {
1237 if (response->isFieldExist(VID_FLAGS))
1238 {
1239 uint16_t flags = response->getFieldAsUInt16(VID_FLAGS);
1240 if (flags & 0x01)
1241 m_controlServer = true;
1242 if (flags & 0x02)
1243 m_masterServer = true;
1244 }
1245 else
1246 {
1247 // Agents before 2.2.13 do not return access flags, assume this server has full access
1248 m_controlServer = true;
1249 m_masterServer = true;
1250 }
1251 }
1252 delete response;
1253 return rcc;
1254 }
1255
1256 /**
1257 * Set server ID
1258 */
1259 uint32_t AgentConnection::setServerId(uint64_t serverId)
1260 {
1261 NXCPMessage msg(m_nProtocolVersion);
1262 uint32_t requestId = generateRequestId();
1263 msg.setCode(CMD_SET_SERVER_ID);
1264 msg.setField(VID_SERVER_ID, serverId);
1265 msg.setId(requestId);
1266 if (sendMessage(&msg))
1267 return waitForRCC(requestId, m_commandTimeout);
1268 else
1269 return ERR_CONNECTION_BROKEN;
1270 }
1271
1272 /**
1273 * Wait for request completion code
1274 */
1275 uint32_t AgentConnection::waitForRCC(uint32_t requestId, uint32_t timeout)
1276 {
1277 uint32_t rcc;
1278 NXCPMessage *response = m_pMsgWaitQueue->waitForMessage(CMD_REQUEST_COMPLETED, requestId, timeout);
1279 if (response != nullptr)
1280 {
1281 rcc = response->getFieldAsUInt32(VID_RCC);
1282 delete response;
1283 }
1284 else
1285 {
1286 rcc = ERR_REQUEST_TIMEOUT;
1287 }
1288 return rcc;
1289 }
1290
1291 /**
1292 * Send message to agent
1293 */
1294 bool AgentConnection::sendMessage(NXCPMessage *pMsg)
1295 {
1296 shared_ptr<AbstractCommChannel> channel = acquireChannel();
1297 if (channel == nullptr)
1298 return false;
1299
1300 if (nxlog_get_debug_level_tag_object(DEBUG_TAG, m_debugId) >= 6)
1301 {
1302 TCHAR codeName[64], ipAddrText[64];
1303 debugPrintf(6, _T("Sending message %s (%d) to agent at %s"),
1304 NXCPMessageCodeName(pMsg->getCode(), codeName), pMsg->getId(), m_addr.toString(ipAddrText));
1305 }
1306
1307 bool success;
1308 NXCP_MESSAGE *rawMsg = pMsg->serialize(m_allowCompression);
1309 shared_ptr<NXCPEncryptionContext> encryptionContext = acquireEncryptionContext();
1310 if (encryptionContext != nullptr)
1311 {
1312 NXCP_ENCRYPTED_MESSAGE *encryptedMsg = encryptionContext->encryptMessage(rawMsg);
1313 if (encryptedMsg != nullptr)
1314 {
1315 success = (channel->send(encryptedMsg, ntohl(encryptedMsg->size), m_mutexSocketWrite) == (int)ntohl(encryptedMsg->size));
1316 MemFree(encryptedMsg);
1317 }
1318 else
1319 {
1320 success = false;
1321 }
1322 }
1323 else
1324 {
1325 success = (channel->send(rawMsg, ntohl(rawMsg->size), m_mutexSocketWrite) == (int)ntohl(rawMsg->size));
1326 }
1327 MemFree(rawMsg);
1328 return success;
1329 }
1330
1331 /**
1332 * Callback for sending NXCP message in background
1333 */
1334 void AgentConnection::postMessageCallback(NXCPMessage *msg)
1335 {
1336 sendMessage(msg);
1337 delete msg;
1338 }
1339
1340 /**
1341 * Send NXCP message in background. Provided message will be destroyed after sending.
1342 */
1343 void AgentConnection::postMessage(NXCPMessage *msg)
1344 {
1345 TCHAR key[64];
1346 _sntprintf(key, 64, _T("PostMessage_%p"), this);
1347 ThreadPoolExecuteSerialized(g_agentConnectionThreadPool, key, self(), &AgentConnection::postMessageCallback, msg);
1348 }
1349
1350 /**
1351 * Send raw message to agent
1352 */
1353 bool AgentConnection::sendRawMessage(NXCP_MESSAGE *pMsg)
1354 {
1355 shared_ptr<AbstractCommChannel> channel = acquireChannel();
1356 if (channel == nullptr)
1357 return false;
1358
1359 if (nxlog_get_debug_level_tag_object(DEBUG_TAG, m_debugId) >= 6)
1360 {
1361 TCHAR codeName[64], ipAddrText[64];
1362 debugPrintf(6, _T("Sending raw message %s (%d) to agent at %s"),
1363 NXCPMessageCodeName(ntohs(pMsg->code), codeName), ntohl(pMsg->id), m_addr.toString(ipAddrText));
1364 }
1365
1366 bool success;
1367 NXCP_MESSAGE *rawMsg = pMsg;
1368 shared_ptr<NXCPEncryptionContext> encryptionContext = acquireEncryptionContext();
1369 if (encryptionContext != nullptr)
1370 {
1371 NXCP_ENCRYPTED_MESSAGE *pEnMsg = encryptionContext->encryptMessage(rawMsg);
1372 if (pEnMsg != nullptr)
1373 {
1374 success = (channel->send(pEnMsg, ntohl(pEnMsg->size), m_mutexSocketWrite) == (int)ntohl(pEnMsg->size));
1375 free(pEnMsg);
1376 }
1377 else
1378 {
1379 success = false;
1380 }
1381 }
1382 else
1383 {
1384 success = (channel->send(rawMsg, ntohl(rawMsg->size), m_mutexSocketWrite) == (int)ntohl(rawMsg->size));
1385 }
1386 return success;
1387 }
1388
1389 /**
1390 * Callback for sending raw NXCP message in background
1391 */
1392 void AgentConnection::postRawMessageCallback(NXCP_MESSAGE *msg)
1393 {
1394 sendRawMessage(msg);
1395 MemFree(msg);
1396 }
1397
1398 /**
1399 * Send raw NXCP message in background. Provided message will be destroyed after sending.
1400 */
1401 void AgentConnection::postRawMessage(NXCP_MESSAGE *msg)
1402 {
1403 TCHAR key[64];
1404 _sntprintf(key, 64, _T("PostMessage_%p"), this);
1405 ThreadPoolExecuteSerialized(g_agentConnectionThreadPool, key, self(), &AgentConnection::postRawMessageCallback, msg);
1406 }
1407
1408 /**
1409 * Callback for processing incoming event on separate thread
1410 */
1411 void AgentConnection::onTrapCallback(NXCPMessage *msg)
1412 {
1413 onTrap(msg);
1414 delete msg;
1415 }
1416
1417 /**
1418 * Trap handler. Should be overriden in derived classes to implement
1419 * actual trap processing. Default implementation do nothing.
1420 */
1421 void AgentConnection::onTrap(NXCPMessage *pMsg)
1422 {
1423 }
1424
1425 /**
1426 * Callback for processing incoming syslog message on separate thread
1427 */
1428 void AgentConnection::onSyslogMessageCallback(NXCPMessage *msg)
1429 {
1430 onSyslogMessage(*msg);
1431 delete msg;
1432 }
1433
1434 /**
1435 * Syslog message handler. Should be overriden in derived classes to implement
1436 * actual message processing. Default implementation do nothing.
1437 */
1438 void AgentConnection::onSyslogMessage(const NXCPMessage& msg)
1439 {
1440 }
1441
1442 /**
1443 * Callback for processing incoming windows evens on separate thread
1444 */
1445 void AgentConnection::onWindowsEventCallback(NXCPMessage *msg)
1446 {
1447 onWindowsEvent(*msg);
1448 delete msg;
1449 }
1450
1451 /**
1452 * Windows event handler. Should be overriden in derived classes to implement
1453 * actual event processing. Default implementation do nothing.
1454 */
1455 void AgentConnection::onWindowsEvent(const NXCPMessage& msg)
1456 {
1457 }
1458
1459 /**
1460 * Callback for processing data push on separate thread
1461 */
1462 void AgentConnection::onDataPushCallback(NXCPMessage *msg)
1463 {
1464 onDataPush(msg);
1465 delete msg;
1466 }
1467
1468 /**
1469 * Data push handler. Should be overriden in derived classes to implement
1470 * actual data push processing. Default implementation do nothing.
1471 */
1472 void AgentConnection::onDataPush(NXCPMessage *pMsg)
1473 {
1474 }
1475
1476 /**
1477 * Monitoring data handler. Should be overriden in derived classes to implement
1478 * actual monitoring data processing. Default implementation do nothing.
1479 */
1480 void AgentConnection::onFileMonitoringData(NXCPMessage *pMsg)
1481 {
1482 }
1483
1484 /**
1485 * Callback for processing data push on separate thread
1486 */
1487 void AgentConnection::onSnmpTrapCallback(NXCPMessage *msg)
1488 {
1489 onSnmpTrap(msg);
1490 delete msg;
1491 }
1492
1493 /**
1494 * SNMP trap handler. Should be overriden in derived classes to implement
1495 * actual SNMP trap processing. Default implementation do nothing.
1496 */
1497 void AgentConnection::onSnmpTrap(NXCPMessage *pMsg)
1498 {
1499 }
1500
1501 /**
1502 * Custom message handler
1503 * If returns true, message considered as processed and will not be placed in wait queue
1504 */
1505 bool AgentConnection::processCustomMessage(NXCPMessage *pMsg)
1506 {
1507 return false;
1508 }
1509
1510 /**
1511 * Get list of values
1512 */
1513 uint32_t AgentConnection::getList(const TCHAR *param, StringList **list)
1514 {
1515 uint32_t rcc;
1516 *list = nullptr;
1517 if (m_isConnected)
1518 {
1519 NXCPMessage msg(CMD_GET_LIST, generateRequestId(), m_nProtocolVersion);
1520 msg.setField(VID_PARAMETER, param);
1521 if (sendMessage(&msg))
1522 {
1523 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, msg.getId(), m_commandTimeout);
1524 if (response != nullptr)
1525 {
1526 rcc = response->getFieldAsUInt32(VID_RCC);
1527 if (rcc == ERR_SUCCESS)
1528 {
1529 *list = new StringList();
1530 int count = response->getFieldAsInt32(VID_NUM_STRINGS);
1531 for(int i = 0; i < count; i++)
1532 (*list)->addPreallocated(response->getFieldAsString(VID_ENUM_VALUE_BASE + i));
1533 }
1534 delete response;
1535 }
1536 else
1537 {
1538 rcc = ERR_REQUEST_TIMEOUT;
1539 }
1540 }
1541 else
1542 {
1543 rcc = ERR_CONNECTION_BROKEN;
1544 }
1545 }
1546 else
1547 {
1548 rcc = ERR_NOT_CONNECTED;
1549 }
1550
1551 return rcc;
1552 }
1553
1554 /**
1555 * Get table
1556 */
1557 uint32_t AgentConnection::getTable(const TCHAR *pszParam, Table **table)
1558 {
1559 NXCPMessage msg(m_nProtocolVersion), *pResponse;
1560 uint32_t dwRqId, dwRetCode;
1561
1562 *table = nullptr;
1563 if (m_isConnected)
1564 {
1565 dwRqId = generateRequestId();
1566 msg.setCode(CMD_GET_TABLE);
1567 msg.setId(dwRqId);
1568 msg.setField(VID_PARAMETER, pszParam);
1569 if (sendMessage(&msg))
1570 {
1571 pResponse = waitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_commandTimeout);
1572 if (pResponse != nullptr)
1573 {
1574 dwRetCode = pResponse->getFieldAsUInt32(VID_RCC);
1575 if (dwRetCode == ERR_SUCCESS)
1576 {
1577 *table = new Table(pResponse);
1578 }
1579 delete pResponse;
1580 }
1581 else
1582 {
1583 dwRetCode = ERR_REQUEST_TIMEOUT;
1584 }
1585 }
1586 else
1587 {
1588 dwRetCode = ERR_CONNECTION_BROKEN;
1589 }
1590 }
1591 else
1592 {
1593 dwRetCode = ERR_NOT_CONNECTED;
1594 }
1595
1596 return dwRetCode;
1597 }
1598
1599 /**
1600 * Authenticate to agent
1601 */
1602 uint32_t AgentConnection::authenticate(BOOL bProxyData)
1603 {
1604 const char *secret = bProxyData ? m_proxySecret : m_secret;
1605 if (*secret == 0)
1606 return ERR_SUCCESS; // No authentication required
1607
1608 NXCPMessage msg(m_nProtocolVersion);
1609 msg.setCode(CMD_AUTHENTICATE);
1610 uint32_t requestId = generateRequestId();
1611 msg.setId(requestId);
1612 msg.setField(VID_AUTH_METHOD, (WORD)AUTH_SHA1_HASH); // For compatibility with agents before 3.3
1613 BYTE hash[SHA1_DIGEST_SIZE];
1614 CalculateSHA1Hash(reinterpret_cast<const BYTE*>(secret), strlen(secret), hash);
1615 msg.setField(VID_SHARED_SECRET, hash, SHA1_DIGEST_SIZE);
1616 if (sendMessage(&msg))
1617 return waitForRCC(requestId, m_commandTimeout);
1618 else
1619 return ERR_CONNECTION_BROKEN;
1620 }
1621
1622 /**
1623 * Execute command on agent
1624 */
1625 uint32_t AgentConnection::executeCommand(const TCHAR *command, const StringList &args,
1626 bool withOutput, void (*outputCallback)(ActionCallbackEvent, const TCHAR*, void*), void *cbData)
1627 {
1628 if (!m_isConnected)
1629 return ERR_NOT_CONNECTED;
1630
1631 NXCPMessage request(CMD_ACTION, generateRequestId(), m_nProtocolVersion);
1632 request.setField(VID_ACTION_NAME, command);
1633 request.setField(VID_RECEIVE_OUTPUT, withOutput);
1634 args.fillMessage(&request, VID_ACTION_ARG_BASE, VID_NUM_ARGS);
1635
1636 if (sendMessage(&request))
1637 {
1638 if (withOutput)
1639 {
1640 uint32_t rcc = waitForRCC(request.getId(), m_commandTimeout);
1641 if (rcc == ERR_SUCCESS)
1642 {
1643 outputCallback(ACE_CONNECTED, nullptr, cbData); // Indicate successful start
1644 bool eos = false;
1645 while(!eos)
1646 {
1647 NXCPMessage *response = waitForMessage(CMD_COMMAND_OUTPUT, request.getId(), m_commandTimeout * 10);
1648 if (response != nullptr)
1649 {
1650 eos = response->isEndOfSequence();
1651 if (response->isFieldExist(VID_MESSAGE))
1652 {
1653 TCHAR line[4096];
1654 response->getFieldAsString(VID_MESSAGE, line, 4096);
1655 outputCallback(ACE_DATA, line, cbData);
1656 }
1657 delete response;
1658 }
1659 else
1660 {
1661 return ERR_REQUEST_TIMEOUT;
1662 }
1663 }
1664 outputCallback(ACE_DISCONNECTED, nullptr, cbData);
1665 return ERR_SUCCESS;
1666 }
1667 else
1668 {
1669 return rcc;
1670 }
1671 }
1672 else
1673 {
1674 return waitForRCC(request.getId(), m_commandTimeout);
1675 }
1676 }
1677 else
1678 {
1679 return ERR_CONNECTION_BROKEN;
1680 }
1681 }
1682
1683 /**
1684 * Upload file to agent
1685 */
1686 UINT32 AgentConnection::uploadFile(const TCHAR *localFile, const TCHAR *destinationFile, bool allowPathExpansion,
1687 void (* progressCallback)(INT64, void *), void *cbArg, NXCPStreamCompressionMethod compMethod)
1688 {
1689 UINT32 dwRqId, dwResult;
1690 NXCPMessage msg(m_nProtocolVersion);
1691
1692 // Disable compression if it is disabled on connection level or if agent do not support it
1693 if (!m_allowCompression || (m_nProtocolVersion < 4))
1694 compMethod = NXCP_STREAM_COMPRESSION_NONE;
1695
1696 if (!m_isConnected)
1697 return ERR_NOT_CONNECTED;
1698
1699 dwRqId = generateRequestId();
1700 msg.setId(dwRqId);
1701
1702 time_t lastModTime = 0;
1703 NX_STAT_STRUCT st;
1704 if (CALL_STAT(localFile, &st) == 0)
1705 {
1706 lastModTime = st.st_mtime;
1707 }
1708
1709 // Use core agent if destination file name is not set and file manager subagent otherwise
1710 if ((destinationFile == nullptr) || (*destinationFile == 0))
1711 {
1712 msg.setCode(CMD_TRANSFER_FILE);
1713 int i;
1714 for(i = (int)_tcslen(localFile) - 1;
1715 (i >= 0) && (localFile[i] != '\\') && (localFile[i] != '/'); i--);
1716 msg.setField(VID_FILE_NAME, &localFile[i + 1]);
1717 }
1718 else
1719 {
1720 msg.setCode(CMD_FILEMGR_UPLOAD);
1721 msg.setField(VID_OVERWRITE, true);
1722 msg.setField(VID_FILE_NAME, destinationFile);
1723 msg.setField(VID_ALLOW_PATH_EXPANSION, allowPathExpansion);
1724 }
1725 msg.setFieldFromTime(VID_MODIFICATION_TIME, lastModTime);
1726
1727 if (sendMessage(&msg))
1728 {
1729 dwResult = waitForRCC(dwRqId, m_commandTimeout);
1730 }
1731 else
1732 {
1733 dwResult = ERR_CONNECTION_BROKEN;
1734 }
1735
1736 if (dwResult == ERR_SUCCESS)
1737 {
1738 shared_ptr<AbstractCommChannel> channel = acquireChannel();
1739 if (channel != nullptr)
1740 {
1741 debugPrintf(5, _T("Sending file \"%s\" to agent %s compression"),
1742 localFile, (compMethod == NXCP_STREAM_COMPRESSION_NONE) ? _T("without") : _T("with"));
1743 m_fileUploadInProgress = true;
1744 shared_ptr<NXCPEncryptionContext> ctx = acquireEncryptionContext();
1745 if (SendFileOverNXCP(channel.get(), dwRqId, localFile, ctx.get(), 0, progressCallback, cbArg, m_mutexSocketWrite, compMethod))
1746 dwResult = waitForRCC(dwRqId, m_commandTimeout);
1747 else
1748 dwResult = ERR_IO_FAILURE;
1749 m_fileUploadInProgress = false;
1750 }
1751 else
1752 {
1753 dwResult = ERR_CONNECTION_BROKEN;
1754 }
1755 }
1756
1757 return dwResult;
1758 }
1759
1760 /**
1761 * Send upgrade command
1762 */
1763 UINT32 AgentConnection::startUpgrade(const TCHAR *pkgName)
1764 {
1765 if (!m_isConnected)
1766 return ERR_NOT_CONNECTED;
1767
1768 uint32_t requestId = generateRequestId();
1769 NXCPMessage msg(CMD_UPGRADE_AGENT, requestId, m_nProtocolVersion);
1770 int i;
1771 for(i = (int)_tcslen(pkgName) - 1;
1772 (i >= 0) && (pkgName[i] != '\\') && (pkgName[i] != '/'); i--);
1773 msg.setField(VID_FILE_NAME, &pkgName[i + 1]);
1774
1775 uint32_t rcc;
1776 if (sendMessage(&msg))
1777 {
1778 rcc = waitForRCC(requestId, m_commandTimeout);
1779 }
1780 else
1781 {
1782 rcc = ERR_CONNECTION_BROKEN;
1783 }
1784 return rcc;
1785 }
1786
1787 /**
1788 * Check status of network service via agent
1789 */
1790 UINT32 AgentConnection::checkNetworkService(UINT32 *pdwStatus, const InetAddress& addr, int iServiceType,
1791 WORD wPort, WORD wProto, const TCHAR *pszRequest,
1792 const TCHAR *pszResponse, UINT32 *responseTime)
1793 {
1794 UINT32 dwRqId, dwResult;
1795 NXCPMessage msg(m_nProtocolVersion), *pResponse;
1796 static WORD m_wDefaultPort[] = { 7, 22, 110, 25, 21, 80, 443, 23 };
1797
1798 if (!m_isConnected)
1799 return ERR_NOT_CONNECTED;
1800
1801 dwRqId = generateRequestId();
1802
1803 msg.setCode(CMD_CHECK_NETWORK_SERVICE);
1804 msg.setId(dwRqId);
1805 msg.setField(VID_IP_ADDRESS, addr);
1806 msg.setField(VID_SERVICE_TYPE, (WORD)iServiceType);
1807 msg.setField(VID_IP_PORT,
1808 (wPort != 0) ? wPort :
1809 m_wDefaultPort[((iServiceType >= NETSRV_CUSTOM) &&
1810 (iServiceType <= NETSRV_TELNET)) ? iServiceType : 0]);
1811 msg.setField(VID_IP_PROTO, (wProto != 0) ? wProto : (WORD)IPPROTO_TCP);
1812 msg.setField(VID_SERVICE_REQUEST, pszRequest);
1813 msg.setField(VID_SERVICE_RESPONSE, pszResponse);
1814
1815 if (sendMessage(&msg))
1816 {
1817 // Wait up to 90 seconds for results
1818 pResponse = waitForMessage(CMD_REQUEST_COMPLETED, dwRqId, 90000);
1819 if (pResponse != nullptr)
1820 {
1821 dwResult = pResponse->getFieldAsUInt32(VID_RCC);
1822 if (dwResult == ERR_SUCCESS)
1823 {
1824 *pdwStatus = pResponse->getFieldAsUInt32(VID_SERVICE_STATUS);
1825 if (responseTime != nullptr)
1826 {
1827 *responseTime = pResponse->getFieldAsUInt32(VID_RESPONSE_TIME);
1828 }
1829 }
1830 delete pResponse;
1831 }
1832 else
1833 {
1834 dwResult = ERR_REQUEST_TIMEOUT;
1835 }
1836 }
1837 else
1838 {
1839 dwResult = ERR_CONNECTION_BROKEN;
1840 }
1841
1842 return dwResult;
1843 }
1844
1845 /**
1846 * Get list of supported parameters from agent
1847 */
1848 UINT32 AgentConnection::getSupportedParameters(ObjectArray<AgentParameterDefinition> **paramList, ObjectArray<AgentTableDefinition> **tableList)
1849 {
1850 UINT32 dwRqId, dwResult;
1851 NXCPMessage msg(m_nProtocolVersion), *pResponse;
1852
1853 *paramList = nullptr;
1854 *tableList = nullptr;
1855
1856 if (!m_isConnected)
1857 return ERR_NOT_CONNECTED;
1858
1859 dwRqId = generateRequestId();
1860
1861 msg.setCode(CMD_GET_PARAMETER_LIST);
1862 msg.setId(dwRqId);
1863
1864 if (sendMessage(&msg))
1865 {
1866 pResponse = waitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_commandTimeout);
1867 if (pResponse != nullptr)
1868 {
1869 dwResult = pResponse->getFieldAsUInt32(VID_RCC);
1870 DbgPrintf(6, _T("AgentConnection::getSupportedParameters(): RCC=%d"), dwResult);
1871 if (dwResult == ERR_SUCCESS)
1872 {
1873 uint32_t count = pResponse->getFieldAsUInt32(VID_NUM_PARAMETERS);
1874 ObjectArray<AgentParameterDefinition> *plist = new ObjectArray<AgentParameterDefinition>(count, 16, Ownership::True);
1875 for(uint32_t i = 0, id = VID_PARAM_LIST_BASE; i < count; i++)
1876 {
1877 plist->add(new AgentParameterDefinition(pResponse, id));
1878 id += 3;
1879 }
1880 *paramList = plist;
1881 DbgPrintf(6, _T("AgentConnection::getSupportedParameters(): %d parameters received from agent"), count);
1882
1883 count = pResponse->getFieldAsUInt32(VID_NUM_TABLES);
1884 ObjectArray<AgentTableDefinition> *tlist = new ObjectArray<AgentTableDefinition>(count, 16, Ownership::True);
1885 for(uint32_t i = 0, id = VID_TABLE_LIST_BASE; i < count; i++)
1886 {
1887 tlist->add(new AgentTableDefinition(pResponse, id));
1888 id += 3;
1889 }
1890 *tableList = tlist;
1891 DbgPrintf(6, _T("AgentConnection::getSupportedParameters(): %d tables received from agent"), count);
1892 }
1893 delete pResponse;
1894 }
1895 else
1896 {
1897 dwResult = ERR_REQUEST_TIMEOUT;
1898 }
1899 }
1900 else
1901 {
1902 dwResult = ERR_CONNECTION_BROKEN;
1903 }
1904
1905 return dwResult;
1906 }
1907
1908 /**
1909 * Setup encryption
1910 */
1911 uint32_t AgentConnection::setupEncryption(RSA *pServerKey)
1912 {
1913 #ifdef _WITH_ENCRYPTION
1914 uint32_t requestId = generateRequestId();
1915 NXCPMessage msg(m_nProtocolVersion);
1916 msg.setId(requestId);
1917 PrepareKeyRequestMsg(&msg, pServerKey, false);
1918
1919 uint32_t result;
1920 if (sendMessage(&msg))
1921 {
1922 NXCPMessage *response = waitForMessage(CMD_SESSION_KEY, requestId, m_commandTimeout);
1923 if (response != nullptr)
1924 {
1925 NXCPEncryptionContext *encryptionContext = nullptr;
1926 uint32_t rcc = SetupEncryptionContext(response, &encryptionContext, nullptr, pServerKey, m_nProtocolVersion);
1927 switch(rcc)
1928 {
1929 case RCC_SUCCESS:
1930 m_receiver->m_encryptionContext = shared_ptr<NXCPEncryptionContext>(encryptionContext);
1931 m_receiver->m_messageReceiver->setEncryptionContext(m_receiver->m_encryptionContext);
1932 result = ERR_SUCCESS;
1933 break;
1934 case RCC_NO_CIPHERS:
1935 result = ERR_NO_CIPHERS;
1936 break;
1937 case RCC_INVALID_PUBLIC_KEY:
1938 result = ERR_INVALID_PUBLIC_KEY;
1939 break;
1940 case RCC_INVALID_SESSION_KEY:
1941 result = ERR_INVALID_SESSION_KEY;
1942 break;
1943 default:
1944 result = ERR_INTERNAL_ERROR;
1945 break;
1946 }
1947 delete response;
1948 }
1949 else
1950 {
1951 result = ERR_REQUEST_TIMEOUT;
1952 }
1953 }
1954 else
1955 {
1956 result = ERR_CONNECTION_BROKEN;
1957 }
1958
1959 return result;
1960 #else
1961 return ERR_NOT_IMPLEMENTED;
1962 #endif
1963 }
1964
1965 /**
1966 * Get configuration file from agent
1967 */
1968 uint32_t AgentConnection::readConfigFile(TCHAR **content, size_t *sizeptr)
1969 {
1970 *content = nullptr;
1971 *sizeptr = 0;
1972
1973 if (!m_isConnected)
1974 return ERR_NOT_CONNECTED;
1975
1976 uint32_t rcc;
1977 uint32_t requestId = generateRequestId();
1978
1979 NXCPMessage msg(m_nProtocolVersion);
1980 msg.setCode(CMD_READ_AGENT_CONFIG_FILE);
1981 msg.setId(requestId);
1982
1983 if (sendMessage(&msg))
1984 {
1985 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, requestId, m_commandTimeout);
1986 if (response != nullptr)
1987 {
1988 rcc = response->getFieldAsUInt32(VID_RCC);
1989 if (rcc == ERR_SUCCESS)
1990 {
1991 size_t size = response->getFieldAsBinary(VID_CONFIG_FILE, nullptr, 0);
1992 BYTE *utf8Text = (BYTE *)malloc(size + 1);
1993 response->getFieldAsBinary(VID_CONFIG_FILE, (BYTE *)utf8Text, size);
1994
1995 // We expect text file, so replace all non-printable characters with spaces
1996 for(size_t i = 0; i < size; i++)
1997 if ((utf8Text[i] < ' ') &&
1998 (utf8Text[i] != '\t') &&
1999 (utf8Text[i] != '\r') &&
2000 (utf8Text[i] != '\n'))
2001 utf8Text[i] = ' ';
2002 utf8Text[size] = 0;
2003
2004 #ifdef UNICODE
2005 *content = WideStringFromUTF8String((char *)utf8Text);
2006 #else
2007 *content = MBStringFromUTF8String((char *)utf8Text);
2008 #endif
2009 MemFree(utf8Text);
2010 *sizeptr = _tcslen(*content);
2011 }
2012 delete response;
2013 }
2014 else
2015 {
2016 rcc = ERR_REQUEST_TIMEOUT;
2017 }
2018 }
2019 else
2020 {
2021 rcc = ERR_CONNECTION_BROKEN;
2022 }
2023
2024 return rcc;
2025 }
2026
2027 /**
2028 * Update configuration file on agent
2029 */
2030 uint32_t AgentConnection::writeConfigFile(const TCHAR *content)
2031 {
2032 NXCPMessage msg(m_nProtocolVersion);
2033
2034 if (!m_isConnected)
2035 return ERR_NOT_CONNECTED;
2036
2037 uint32_t requestId = generateRequestId();
2038
2039 msg.setCode(CMD_WRITE_AGENT_CONFIG_FILE);
2040 msg.setId(requestId);
2041 #ifdef UNICODE
2042 char *utf8content = UTF8StringFromWideString(content);
2043 msg.setField(VID_CONFIG_FILE, (BYTE *)utf8content, strlen(utf8content));
2044 MemFree(utf8content);
2045 #else
2046 msg.setField(VID_CONFIG_FILE, (const BYTE *)content, strlen(content));
2047 #endif
2048
2049 uint32_t rcc;
2050 if (sendMessage(&msg))
2051 {
2052 rcc = waitForRCC(requestId, m_commandTimeout);
2053 }
2054 else
2055 {
2056 rcc = ERR_CONNECTION_BROKEN;
2057 }
2058
2059 return rcc;
2060 }
2061
2062 /**
2063 * Get routing table from agent
2064 */
2065 RoutingTable *AgentConnection::getRoutingTable()
2066 {
2067 StringList *data;
2068 if (getList(_T("Net.IP.RoutingTable"), &data) != ERR_SUCCESS)
2069 return nullptr;
2070
2071 auto routingTable = new RoutingTable(data->size(), 64);
2072 for(int i = 0; i < data->size(); i++)
2073 {
2074 TCHAR *line = MemCopyString(data->get(i));
2075 TCHAR *pBuf = line;
2076
2077 ROUTE route;
2078 memset(&route, 0, sizeof(route));
2079
2080 // Destination address and mask
2081 TCHAR *pChar = _tcschr(pBuf, _T(' '));
2082 if (pChar != nullptr)
2083 {
2084 TCHAR *pSlash;
2085 static TCHAR defaultMask[] = _T("24");
2086
2087 *pChar = 0;
2088 pSlash = _tcschr(pBuf, _T('/'));
2089 if (pSlash != nullptr)
2090 {
2091 *pSlash = 0;
2092 pSlash++;
2093 }
2094 else // Just a paranoia protection, should'n happen if agent working correctly
2095 {
2096 pSlash = defaultMask;
2097 }
2098 route.dwDestAddr = InetAddress::parse(pBuf).getAddressV4();
2099 uint32_t bits = _tcstoul(pSlash, nullptr, 10);
2100 route.dwDestMask = (bits == 32) ? 0xFFFFFFFF : (~(0xFFFFFFFF >> bits));
2101 pBuf = pChar + 1;
2102 }
2103
2104 // Next hop address
2105 pChar = _tcschr(pBuf, _T(' '));
2106 if (pChar != nullptr)
2107 {
2108 *pChar = 0;
2109 route.dwNextHop = InetAddress::parse(pBuf).getAddressV4();
2110 pBuf = pChar + 1;
2111 }
2112
2113 // Interface index
2114 pChar = _tcschr(pBuf, ' ');
2115 if (pChar != nullptr)
2116 {
2117 *pChar = 0;
2118 route.dwIfIndex = _tcstoul(pBuf, nullptr, 10);
2119 pBuf = pChar + 1;
2120 }
2121
2122 // Route type
2123 route.dwRouteType = _tcstoul(pBuf, nullptr, 10);
2124
2125 routingTable->add(&route);
2126 MemFree(line);
2127 }
2128
2129 delete data;
2130 return routingTable;
2131 }
2132
2133 /**
2134 * Set proxy information
2135 */
2136 void AgentConnection::setProxy(const InetAddress& addr, uint16_t port, const TCHAR *secret)
2137 {
2138 m_proxyAddr = addr;
2139 m_proxyPort = port;
2140 if ((secret != nullptr) && (*secret != 0))
2141 {
2142 #ifdef UNICODE
2143 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR, secret, -1, m_proxySecret, MAX_SECRET_LENGTH, nullptr, nullptr);
2144 m_proxySecret[MAX_SECRET_LENGTH - 1] = 0;
2145 #else
2146 strlcpy(m_proxySecret, secret, MAX_SECRET_LENGTH);
2147 #endif
2148 DecryptPasswordA("netxms", m_proxySecret, m_proxySecret, MAX_SECRET_LENGTH);
2149 }
2150 else
2151 {
2152 m_proxySecret[0] = 0;
2153 }
2154 m_useProxy = true;
2155 }
2156
2157 /**
2158 * Setup proxy connection
2159 */
2160 uint32_t AgentConnection::setupProxyConnection()
2161 {
2162 NXCPMessage msg(m_nProtocolVersion);
2163 uint32_t requestId = generateRequestId();
2164 msg.setCode(CMD_SETUP_PROXY_CONNECTION);
2165 msg.setId(requestId);
2166 msg.setField(VID_IP_ADDRESS, m_addr.getAddressV4()); // For compatibility with agents < 2.2.7
2167 msg.setField(VID_DESTINATION_ADDRESS, m_addr);
2168 msg.setField(VID_AGENT_PORT, m_port);
2169 if (sendMessage(&msg))
2170 return waitForRCC(requestId, 60000); // Wait 60 seconds for remote connect
2171 else
2172 return ERR_CONNECTION_BROKEN;
2173 }
2174
2175 /**
2176 * Enable trap receiving on connection
2177 */
2178 UINT32 AgentConnection::enableTraps()
2179 {
2180 NXCPMessage msg(m_nProtocolVersion);
2181 UINT32 dwRqId;
2182
2183 dwRqId = generateRequestId();
2184 msg.setCode(CMD_ENABLE_AGENT_TRAPS);
2185 msg.setId(dwRqId);
2186 if (sendMessage(&msg))
2187 return waitForRCC(dwRqId, m_commandTimeout);
2188 else
2189 return ERR_CONNECTION_BROKEN;
2190 }
2191
2192 /**
2193 * Enable trap receiving on connection
2194 */
2195 uint32_t AgentConnection::enableFileUpdates()
2196 {
2197 NXCPMessage msg(m_nProtocolVersion);
2198 UINT32 dwRqId;
2199
2200 dwRqId = generateRequestId();
2201 msg.setCode(CMD_ENABLE_FILE_UPDATES);
2202 msg.setId(dwRqId);
2203 if (!sendMessage(&msg))
2204 return ERR_CONNECTION_BROKEN;
2205
2206 uint32_t rcc = waitForRCC(dwRqId, m_commandTimeout);
2207 if (rcc == ERR_SUCCESS)
2208 m_fileUpdateConnection = true;
2209 return rcc;
2210 }
2211
2212 /**
2213 * Take screenshot from remote system
2214 */
2215 UINT32 AgentConnection::takeScreenshot(const TCHAR *sessionName, BYTE **data, size_t *size)
2216 {
2217 NXCPMessage msg(m_nProtocolVersion);
2218 UINT32 dwRqId;
2219
2220 dwRqId = generateRequestId();
2221 msg.setCode(CMD_TAKE_SCREENSHOT);
2222 msg.setId(dwRqId);
2223 msg.setField(VID_NAME, sessionName);
2224 if (sendMessage(&msg))
2225 {
2226 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_commandTimeout);
2227 if (response != nullptr)
2228 {
2229 UINT32 rcc = response->getFieldAsUInt32(VID_RCC);
2230 if (rcc == ERR_SUCCESS)
2231 {
2232 const BYTE *p = response->getBinaryFieldPtr(VID_FILE_DATA, size);
2233 if (p != nullptr)
2234 {
2235 *data = (BYTE *)malloc(*size);
2236 memcpy(*data, p, *size);
2237 }
2238 else
2239 {
2240 *data = nullptr;
2241 }
2242 }
2243 delete response;
2244 return rcc;
2245 }
2246 else
2247 {
2248 return ERR_REQUEST_TIMEOUT;
2249 }
2250 }
2251 else
2252 {
2253 return ERR_CONNECTION_BROKEN;
2254 }
2255 }
2256
2257 /**
2258 * Resolve hostname by IP address in local network
2259 */
2260 TCHAR *AgentConnection::getHostByAddr(const InetAddress& ipAddr, TCHAR *buffer, size_t bufLen)
2261 {
2262 NXCPMessage msg(m_nProtocolVersion);
2263 UINT32 dwRqId = generateRequestId();
2264 msg.setCode(CMD_GET_HOSTNAME_BY_IPADDR);
2265 msg.setId(dwRqId);
2266 msg.setField(VID_IP_ADDRESS, ipAddr);
2267 TCHAR *result = nullptr;
2268 if (sendMessage(&msg))
2269 {
2270 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_commandTimeout);
2271 if (response != nullptr)
2272 {
2273 if (response->getFieldAsUInt32(VID_RCC) == ERR_SUCCESS)
2274 {
2275 result = response->getFieldAsString(VID_NAME, buffer, bufLen);
2276 if ((result != nullptr) && (*result == 0))
2277 {
2278 // Agents before 2.2.16 can return empty string instead of error if IP cannot be resolved
2279 if (buffer == nullptr)
2280 MemFree(result);
2281 result = nullptr;
2282 }
2283 }
2284 delete response;
2285 }
2286 }
2287 return result;
2288 }
2289
2290 /**
2291 * Send custom request to agent
2292 */
2293 NXCPMessage *AgentConnection::customRequest(NXCPMessage *request, const TCHAR *recvFile, bool append,
2294 void (*downloadProgressCallback)(size_t, void*), void (*fileResendCallback)(NXCPMessage*, void*), void *cbArg)
2295 {
2296 NXCPMessage *msg = nullptr;
2297
2298 uint32_t requestId = generateRequestId();
2299 request->setId(requestId);
2300 if (recvFile != nullptr)
2301 {
2302 uint32_t rcc = prepareFileDownload(recvFile, requestId, append, downloadProgressCallback, fileResendCallback, cbArg);
2303 if (rcc != ERR_SUCCESS)
2304 {
2305 // Create fake response message
2306 msg = new NXCPMessage;
2307 msg->setCode(CMD_REQUEST_COMPLETED);
2308 msg->setId(requestId);
2309 msg->setField(VID_RCC, rcc);
2310 }
2311 }
2312
2313 if ((msg == nullptr) && sendMessage(request))
2314 {
2315 msg = waitForMessage(CMD_REQUEST_COMPLETED, requestId, m_commandTimeout);
2316 if ((msg != nullptr) && (recvFile != nullptr))
2317 {
2318 if (msg->getFieldAsUInt32(VID_RCC) == ERR_SUCCESS)
2319 {
2320 if (ConditionWait(m_condFileDownload, 1800000)) // 30 min timeout
2321 {
2322 if (!m_fileDownloadSucceeded)
2323 {
2324 msg->setField(VID_RCC, ERR_IO_FAILURE);
2325 if (m_deleteFileOnDownloadFailure)
2326 _tremove(recvFile);
2327 }
2328 }
2329 else
2330 {
2331 msg->setField(VID_RCC, ERR_REQUEST_TIMEOUT);
2332 }
2333 }
2334 else
2335 {
2336 if (fileResendCallback != nullptr)
2337 {
2338 _close(m_hCurrFile);
2339 m_hCurrFile = -1;
2340 _tremove(recvFile);
2341 }
2342 }
2343 }
2344
2345 }
2346
2347 return msg;
2348 }
2349
2350 /**
2351 * Cancel file download
2352 */
2353 uint32_t AgentConnection::cancelFileDownload()
2354 {
2355 NXCPMessage msg(CMD_CANCEL_FILE_DOWNLOAD, generateRequestId(), getProtocolVersion());
2356 msg.setField(VID_REQUEST_ID, m_downloadRequestId);
2357
2358 uint32_t rcc;
2359 if (sendMessage(&msg))
2360 {
2361 NXCPMessage *result = waitForMessage(CMD_REQUEST_COMPLETED, msg.getId(), m_commandTimeout);
2362 if (result != nullptr)
2363 {
2364 rcc = result->getFieldAsUInt32(VID_RCC);
2365 delete result;
2366 }
2367 else
2368 {
2369 rcc = ERR_REQUEST_TIMEOUT;
2370 }
2371 }
2372 else
2373 {
2374 rcc = ERR_CONNECTION_BROKEN;
2375 }
2376 return rcc;
2377 }
2378
2379 /**
2380 * Prepare for file download
2381 */
2382 uint32_t AgentConnection::prepareFileDownload(const TCHAR *fileName, uint32_t rqId, bool append,
2383 void (*downloadProgressCallback)(size_t, void*), void (*fileResendCallback)(NXCPMessage*, void*), void *cbArg)
2384 {
2385 if (fileResendCallback == nullptr)
2386 {
2387 if (m_hCurrFile != -1)
2388 return ERR_RESOURCE_BUSY;
2389
2390 nx_strncpy(m_currentFileName, fileName, MAX_PATH);
2391 ConditionReset(m_condFileDownload);
2392 m_hCurrFile = _topen(fileName, (append ? 0 : (O_CREAT | O_TRUNC)) | O_RDWR | O_BINARY, S_IREAD | S_IWRITE);
2393 if (m_hCurrFile == -1)
2394 {
2395 DbgPrintf(4, _T("AgentConnection::PrepareFileDownload(): cannot open file %s (%s); append=%d rqId=%d"),
2396 fileName, _tcserror(errno), append, rqId);
2397 }
2398 else
2399 {
2400 if (append)
2401 _lseek(m_hCurrFile, 0, SEEK_END);
2402 }
2403
2404 m_downloadRequestId = rqId;
2405 m_downloadProgressCallback = downloadProgressCallback;
2406 m_downloadProgressCallbackArg = cbArg;
2407
2408 m_sendToClientMessageCallback = nullptr;
2409
2410 return (m_hCurrFile != -1) ? ERR_SUCCESS : ERR_FILE_OPEN_ERROR;
2411 }
2412 else
2413 {
2414 ConditionReset(m_condFileDownload);
2415
2416 m_downloadRequestId = rqId;
2417 m_downloadProgressCallback = downloadProgressCallback;
2418 m_downloadProgressCallbackArg = cbArg;
2419
2420 m_sendToClientMessageCallback = fileResendCallback;
2421
2422 return ERR_SUCCESS;
2423 }
2424 }
2425
2426 /**
2427 * Process incoming file data
2428 */
2429 void AgentConnection::processFileData(NXCPMessage *msg)
2430 {
2431 if (m_sendToClientMessageCallback != nullptr)
2432 {
2433 m_sendToClientMessageCallback(msg, m_downloadProgressCallbackArg);
2434 if (msg->isEndOfFile())
2435 {
2436 m_sendToClientMessageCallback = nullptr;
2437 onFileDownload(true);
2438 }
2439 else
2440 {
2441 if (m_downloadProgressCallback != nullptr)
2442 {
2443 m_downloadProgressCallback(msg->getBinaryDataSize(), m_downloadProgressCallbackArg);
2444 }
2445 }
2446 }
2447 else
2448 {
2449 if (m_hCurrFile != -1)
2450 {
2451 if (_write(m_hCurrFile, msg->getBinaryData(), static_cast<int>(msg->getBinaryDataSize())) == static_cast<int>(msg->getBinaryDataSize()))
2452 {
2453 if (msg->isEndOfFile())
2454 {
2455 _close(m_hCurrFile);
2456 m_hCurrFile = -1;
2457 onFileDownload(true);
2458 }
2459 else if (m_downloadProgressCallback != nullptr)
2460 {
2461 m_downloadProgressCallback(_tell(m_hCurrFile), m_downloadProgressCallbackArg);
2462 }
2463 }
2464 }
2465 else
2466 {
2467 // I/O error
2468 _close(m_hCurrFile);
2469 m_hCurrFile = -1;
2470 onFileDownload(false);
2471 }
2472 }
2473 delete msg;
2474 }
2475
2476 /**
2477 * Process file transfer abort request
2478 */
2479 void AgentConnection::processFileTransferAbort(NXCPMessage *msg)
2480 {
2481 if (m_sendToClientMessageCallback != nullptr)
2482 {
2483 m_sendToClientMessageCallback(msg, m_downloadProgressCallbackArg);
2484 m_sendToClientMessageCallback = nullptr;
2485 }
2486 else
2487 {
2488 _close(m_hCurrFile);
2489 m_hCurrFile = -1;
2490 }
2491 onFileDownload(false);
2492 delete msg;
2493 }
2494
2495 /**
2496 * File upload completion handler
2497 */
2498 void AgentConnection::onFileDownload(bool success)
2499 {
2500 if (!success && m_deleteFileOnDownloadFailure)
2501 _tremove(m_currentFileName);
2502 m_fileDownloadSucceeded = success;
2503 ConditionSet(m_condFileDownload);
2504 }
2505
2506 /**
2507 * Enable trap receiving on connection
2508 */
2509 UINT32 AgentConnection::getPolicyInventory(AgentPolicyInfo **info)
2510 {
2511 NXCPMessage msg(m_nProtocolVersion);
2512
2513 *info = nullptr;
2514 uint32_t requestId = generateRequestId();
2515 msg.setCode(CMD_GET_POLICY_INVENTORY);
2516 msg.setId(requestId);
2517 uint32_t rcc;
2518 if (sendMessage(&msg))
2519 {
2520 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, requestId, m_commandTimeout);
2521 if (response != nullptr)
2522 {
2523 rcc = response->getFieldAsUInt32(VID_RCC);
2524 if (rcc == ERR_SUCCESS)
2525 *info = new AgentPolicyInfo(response);
2526 delete response;
2527 }
2528 else
2529 {
2530 rcc = ERR_REQUEST_TIMEOUT;
2531 }
2532 }
2533 else
2534 {
2535 rcc = ERR_CONNECTION_BROKEN;
2536 }
2537 return rcc;
2538 }
2539
2540 /**
2541 * Uninstall policy by GUID
2542 */
2543 UINT32 AgentConnection::uninstallPolicy(const uuid& guid)
2544 {
2545 UINT32 rqId, rcc;
2546 NXCPMessage msg(m_nProtocolVersion);
2547
2548 rqId = generateRequestId();
2549 msg.setId(rqId);
2550 msg.setCode(CMD_UNINSTALL_AGENT_POLICY);
2551 msg.setField(VID_GUID, guid);
2552 if (sendMessage(&msg))
2553 {
2554 rcc = waitForRCC(rqId, m_commandTimeout);
2555 }
2556 else
2557 {
2558 rcc = ERR_CONNECTION_BROKEN;
2559 }
2560 return rcc;
2561 }
2562
2563 /**
2564 * Callback for processing collected data on separate thread
2565 */
2566 void AgentConnection::processCollectedDataCallback(NXCPMessage *msg)
2567 {
2568 NXCPMessage response(CMD_REQUEST_COMPLETED, msg->getId(), m_nProtocolVersion);
2569
2570 if (msg->getFieldAsBoolean(VID_BULK_RECONCILIATION))
2571 {
2572 // Check that only one bulk data processor is running
2573 if (InterlockedIncrement(&m_bulkDataProcessing) == 1)
2574 {
2575 response.setField(VID_RCC, processBulkCollectedData(msg, &response));
2576 }
2577 else
2578 {
2579 response.setField(VID_RCC, ERR_RESOURCE_BUSY);
2580 }
2581 InterlockedDecrement(&m_bulkDataProcessing);
2582 }
2583 else
2584 {
2585 uint32_t rcc = processCollectedData(msg);
2586 response.setField(VID_RCC, rcc);
2587 }
2588
2589 sendMessage(&response);
2590 delete msg;
2591 }
2592
2593 /**
2594 * Process collected data information (for DCI with agent-side cache)
2595 */
2596 UINT32 AgentConnection::processCollectedData(NXCPMessage *msg)
2597 {
2598 return ERR_NOT_IMPLEMENTED;
2599 }
2600
2601 /**
2602 * Process collected data information in bulk mode (for DCI with agent-side cache)
2603 */
2604 UINT32 AgentConnection::processBulkCollectedData(NXCPMessage *request, NXCPMessage *response)
2605 {
2606 return ERR_NOT_IMPLEMENTED;
2607 }
2608
2609 /**
2610 * Callback for getting SSH keys by id
2611 */
2612 void AgentConnection::getSshKeysCallback(NXCPMessage *msg)
2613 {
2614 NXCPMessage response(CMD_REQUEST_COMPLETED, msg->getId(), m_nProtocolVersion);
2615 getSshKeys(msg, &response);
2616 sendMessage(&response);
2617 delete msg;
2618 }
2619
2620 /**
2621 * Get SSH key function
2622 */
2623 void AgentConnection::getSshKeys(NXCPMessage *request, NXCPMessage *response)
2624 {
2625 response->setField(VID_RCC, ERR_NOT_IMPLEMENTED);
2626 }
2627
2628 /**
2629 * Setup TCP proxy
2630 */
2631 UINT32 AgentConnection::setupTcpProxy(const InetAddress& ipAddr, UINT16 port, UINT32 *channelId)
2632 {
2633 UINT32 requestId = generateRequestId();
2634 NXCPMessage msg(CMD_SETUP_TCP_PROXY, requestId, m_nProtocolVersion);
2635 msg.setField(VID_IP_ADDRESS, ipAddr);
2636 msg.setField(VID_PORT, port);
2637
2638 UINT32 rcc;
2639 if (sendMessage(&msg))
2640 {
2641 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, requestId, m_commandTimeout);
2642 if (response != nullptr)
2643 {
2644 rcc = response->getFieldAsUInt32(VID_RCC);
2645 if (rcc == ERR_SUCCESS)
2646 {
2647 *channelId = response->getFieldAsUInt32(VID_CHANNEL_ID);
2648 }
2649 delete response;
2650 }
2651 else
2652 {
2653 rcc = ERR_REQUEST_TIMEOUT;
2654 }
2655 }
2656 else
2657 {
2658 rcc = ERR_CONNECTION_BROKEN;
2659 }
2660 return rcc;
2661 }
2662
2663 /**
2664 * Close TCP proxy
2665 */
2666 UINT32 AgentConnection::closeTcpProxy(UINT32 channelId)
2667 {
2668 UINT32 requestId = generateRequestId();
2669 NXCPMessage msg(CMD_CLOSE_TCP_PROXY, requestId, m_nProtocolVersion);
2670 msg.setField(VID_CHANNEL_ID, channelId);
2671 UINT32 rcc;
2672 if (sendMessage(&msg))
2673 {
2674 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, requestId, m_commandTimeout);
2675 if (response != nullptr)
2676 {
2677 rcc = response->getFieldAsUInt32(VID_RCC);
2678 delete response;
2679 }
2680 else
2681 {
2682 rcc = ERR_REQUEST_TIMEOUT;
2683 }
2684 }
2685 else
2686 {
2687 rcc = ERR_CONNECTION_BROKEN;
2688 }
2689 return rcc;
2690 }
2691
2692 /**
2693 * Process data received from TCP proxy
2694 */
2695 void AgentConnection::processTcpProxyData(uint32_t channelId, const void *data, size_t size)
2696 {
2697 }
2698
2699 /**
2700 * Get file set information
2701 */
2702 uint32_t AgentConnection::getFileSetInfo(const StringList &fileSet, bool allowPathExpansion, ObjectArray<RemoteFileInfo> **fileSetInfo)
2703 {
2704 *fileSetInfo = nullptr;
2705 uint32_t requestId = generateRequestId();
2706 NXCPMessage msg(CMD_GET_FILE_SET_DETAILS, requestId, m_nProtocolVersion);
2707 msg.setField(VID_ALLOW_PATH_EXPANSION, allowPathExpansion);
2708 fileSet.fillMessage(&msg, VID_ELEMENT_LIST_BASE, VID_NUM_ELEMENTS);
2709 uint32_t rcc;
2710 if (sendMessage(&msg))
2711 {
2712 NXCPMessage *response = waitForMessage(CMD_REQUEST_COMPLETED, requestId, m_commandTimeout);
2713 if (response != nullptr)
2714 {
2715 rcc = response->getFieldAsUInt32(VID_RCC);
2716 if (rcc == ERR_SUCCESS)
2717 {
2718 int count = response->getFieldAsInt32(VID_NUM_ELEMENTS);
2719 if (count == fileSet.size())
2720 {
2721 auto info = new ObjectArray<RemoteFileInfo>(count, 16, Ownership::True);
2722 UINT32 fieldId = VID_ELEMENT_LIST_BASE;
2723 for(int i = 0; i < count; i++)
2724 {
2725 info->add(new RemoteFileInfo(response, fieldId, fileSet.get(i)));
2726 fieldId += 10;
2727 }
2728 *fileSetInfo = info;
2729 }
2730 else
2731 {
2732 rcc = ERR_INTERNAL_ERROR;
2733 }
2734 }
2735 delete response;
2736 }
2737 else
2738 {
2739 rcc = ERR_REQUEST_TIMEOUT;
2740 }
2741 }
2742 else
2743 {
2744 rcc = ERR_CONNECTION_BROKEN;
2745 }
2746 return rcc;
2747 }
2748
2749 /**
2750 * Create new agent parameter definition from NXCP message
2751 */
2752 AgentParameterDefinition::AgentParameterDefinition(const NXCPMessage *msg, uint32_t baseId)
2753 {
2754 m_name = msg->getFieldAsString(baseId);
2755 m_description = msg->getFieldAsString(baseId + 1);
2756 m_dataType = (int)msg->getFieldAsUInt16(baseId + 2);
2757 }
2758
2759 /**
2760 * Create new agent parameter definition from another definition object
2761 */
2762 AgentParameterDefinition::AgentParameterDefinition(const AgentParameterDefinition *src)
2763 {
2764 m_name = MemCopyString(src->m_name);
2765 m_description = MemCopyString(src->m_description);
2766 m_dataType = src->m_dataType;
2767 }
2768
2769 /**
2770 * Create new agent parameter definition from scratch
2771 */
2772 AgentParameterDefinition::AgentParameterDefinition(const TCHAR *name, const TCHAR *description, int dataType)
2773 {
2774 m_name = MemCopyString(name);
2775 m_description = MemCopyString(description);
2776 m_dataType = dataType;
2777 }
2778
2779 /**
2780 * Destructor for agent parameter definition
2781 */
2782 AgentParameterDefinition::~AgentParameterDefinition()
2783 {
2784 MemFree(m_name);
2785 MemFree(m_description);
2786 }
2787
2788 /**
2789 * Fill NXCP message
2790 */
2791 uint32_t AgentParameterDefinition::fillMessage(NXCPMessage *msg, uint32_t baseId) const
2792 {
2793 msg->setField(baseId, m_name);
2794 msg->setField(baseId + 1, m_description);
2795 msg->setField(baseId + 2, static_cast<uint16_t>(m_dataType));
2796 return 3;
2797 }
2798
2799 /**
2800 * Create new agent table definition from NXCP message
2801 */
2802 AgentTableDefinition::AgentTableDefinition(const NXCPMessage *msg, uint32_t baseId)
2803 {
2804 m_name = msg->getFieldAsString(baseId);
2805 m_description = msg->getFieldAsString(baseId + 2);
2806
2807 TCHAR *instanceColumns = msg->getFieldAsString(baseId + 1);
2808 if (instanceColumns != nullptr)
2809 {
2810 m_instanceColumns = new StringList(instanceColumns, _T("|"));
2811 MemFree(instanceColumns);
2812 }
2813 else
2814 {
2815 m_instanceColumns = new StringList;
2816 }
2817
2818 m_columns = new ObjectArray<AgentTableColumnDefinition>(16, 16, Ownership::True);
2819 }
2820
2821 /**
2822 * Create new agent table definition from another definition object
2823 */
2824 AgentTableDefinition::AgentTableDefinition(const AgentTableDefinition *src)
2825 {
2826 m_name = MemCopyString(src->m_name);
2827 m_description = MemCopyString(src->m_description);
2828 m_instanceColumns = new StringList(src->m_instanceColumns);
2829 m_columns = new ObjectArray<AgentTableColumnDefinition>(16, 16, Ownership::True);
2830 for(int i = 0; i < src->m_columns->size(); i++)
2831 {
2832 m_columns->add(new AgentTableColumnDefinition(src->m_columns->get(i)));
2833 }
2834 }
2835 /**
2836 * Destructor for agent table definition
2837 */
2838 AgentTableDefinition::~AgentTableDefinition()
2839 {
2840 MemFree(m_name);
2841 MemFree(m_description);
2842 delete m_instanceColumns;
2843 delete m_columns;
2844 }
2845
2846 /**
2847 * Fill NXCP message
2848 */
2849 uint32_t AgentTableDefinition::fillMessage(NXCPMessage *msg, uint32_t baseId) const
2850 {
2851 msg->setField(baseId + 1, m_name);
2852 msg->setField(baseId + 2, m_description);
2853
2854 TCHAR *instanceColumns = m_instanceColumns->join(_T("|"));
2855 msg->setField(baseId + 3, instanceColumns);
2856 free(instanceColumns);
2857
2858 uint32_t fieldId = baseId + 4;
2859 for(int i = 0; i < m_columns->size(); i++)
2860 {
2861 msg->setField(fieldId++, m_columns->get(i)->m_name);
2862 msg->setField(fieldId++, (WORD)m_columns->get(i)->m_dataType);
2863 }
2864
2865 msg->setField(baseId, fieldId - baseId);
2866 return fieldId - baseId;
2867 }
2868
2869 /**
2870 * Create remote file info object
2871 */
2872 RemoteFileInfo::RemoteFileInfo(NXCPMessage *msg, uint32_t baseId, const TCHAR *name)
2873 {
2874 m_name = MemCopyString(name);
2875 m_status = msg->getFieldAsUInt32(baseId);
2876 if (m_status == ERR_SUCCESS)
2877 {
2878 m_size = msg->getFieldAsUInt64(baseId + 1);
2879 m_mtime = msg->getFieldAsTime(baseId + 2);
2880 msg->getFieldAsBinary(baseId + 3, m_hash, MD5_DIGEST_SIZE);
2881 }
2882 else
2883 {
2884 m_size = 0;
2885 m_mtime = 0;
2886 memset(m_hash, 0, MD5_DIGEST_SIZE);
2887 }
2888 }
2889
2890 /**
2891 * Destroy remote file info object
2892 */
2893 RemoteFileInfo::~RemoteFileInfo()
2894 {
2895 MemFree(m_name);
2896 }