"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 }