Fixed bug with data type codes in Threshold class
[public/netxms.git] / src / server / libnxsrv / agent.cpp
CommitLineData
f77084bb
VK
1/*
2** NetXMS - Network Management System
3** Server Library
4** Copyright (C) 2003, 2004 Victor Kirhenshtein
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19**
20** $module: agent.cpp
21**
22**/
23
24#include "libnxsrv.h"
1ba9a162 25#include <stdarg.h>
f77084bb
VK
26
27
28//
29// Constants
30//
31
32#define RECEIVER_BUFFER_SIZE 262144
33
34
35//
36// Receiver thread starter
37//
38
1ba9a162 39void AgentConnection::ReceiverThreadStarter(void *pArg)
f77084bb
VK
40{
41 ((AgentConnection *)pArg)->ReceiverThread();
42}
43
44
45//
46// Default constructor for AgentConnection - normally shouldn't be used
47//
48
49AgentConnection::AgentConnection()
50{
51 m_dwAddr = inet_addr("127.0.0.1");
52 m_wPort = AGENT_LISTEN_PORT;
53 m_iAuthMethod = AUTH_NONE;
54 m_szSecret[0] = 0;
55 m_hSocket = -1;
56 m_tLastCommandTime = 0;
57 m_dwNumDataLines = 0;
58 m_ppDataLines = NULL;
59 m_pMsgWaitQueue = new MsgWaitQueue;
d1d0b3be 60 m_dwRequestId = 1;
f77084bb 61 m_dwCommandTimeout = 10000; // Default timeout 10 seconds
1c62be36
VK
62 m_bIsConnected = FALSE;
63 m_hMutex = MutexCreate();
f77084bb
VK
64}
65
66
67//
68// Normal constructor for AgentConnection
69//
70
71AgentConnection::AgentConnection(DWORD dwAddr, WORD wPort, int iAuthMethod, char *szSecret)
72{
73 m_dwAddr = dwAddr;
74 m_wPort = wPort;
75 m_iAuthMethod = iAuthMethod;
76 if (szSecret != NULL)
77 strncpy(m_szSecret, szSecret, MAX_SECRET_LENGTH);
78 else
79 m_szSecret[0] = 0;
80 m_hSocket = -1;
81 m_tLastCommandTime = 0;
82 m_dwNumDataLines = 0;
83 m_ppDataLines = NULL;
84 m_pMsgWaitQueue = new MsgWaitQueue;
d1d0b3be 85 m_dwRequestId = 1;
f77084bb 86 m_dwCommandTimeout = 10000; // Default timeout 10 seconds
1c62be36
VK
87 m_bIsConnected = FALSE;
88 m_hMutex = MutexCreate();
f77084bb
VK
89}
90
91
92//
93// Destructor
94//
95
96AgentConnection::~AgentConnection()
97{
98 if (m_hSocket != -1)
99 closesocket(m_hSocket);
100 DestroyResultData();
101 delete m_pMsgWaitQueue;
1c62be36 102 MutexDestroy(m_hMutex);
f77084bb
VK
103}
104
105
106//
059f6632
VK
107// Print message. This function is virtual and can be overrided in
108// derived classes. Default implementation will print message to stdout.
f77084bb
VK
109//
110
111void AgentConnection::PrintMsg(char *pszFormat, ...)
112{
113 va_list args;
114
115 va_start(args, pszFormat);
116 vprintf(pszFormat, args);
117 va_end(args);
118 printf("\n");
119}
120
121
122//
123// Receiver thread
124//
125
126void AgentConnection::ReceiverThread(void)
127{
128 CSCPMessage *pMsg;
129 CSCP_MESSAGE *pRawMsg;
130 CSCP_BUFFER *pMsgBuffer;
131 int iErr;
132 char szBuffer[128];
133
134 // Initialize raw message receiving function
135 pMsgBuffer = (CSCP_BUFFER *)MemAlloc(sizeof(CSCP_BUFFER));
136 RecvCSCPMessage(0, NULL, pMsgBuffer, 0);
137
138 // Allocate space for raw message
139 pRawMsg = (CSCP_MESSAGE *)MemAlloc(RECEIVER_BUFFER_SIZE);
140
141 // Message receiving loop
142 while(1)
143 {
144 // Receive raw message
145 if ((iErr = RecvCSCPMessage(m_hSocket, pRawMsg, pMsgBuffer, RECEIVER_BUFFER_SIZE)) <= 0)
146 break;
147
148 // Check if we get too large message
149 if (iErr == 1)
150 {
151 PrintMsg("Received too large message %s (%ld bytes)",
152 CSCPMessageCodeName(ntohs(pRawMsg->wCode), szBuffer),
153 ntohl(pRawMsg->dwSize));
154 continue;
155 }
156
157 // Check that actual received packet size is equal to encoded in packet
158 if ((int)ntohl(pRawMsg->dwSize) != iErr)
159 {
160 PrintMsg("RecvMsg: Bad packet length [dwSize=%d ActualSize=%d]", ntohl(pRawMsg->dwSize), iErr);
161 continue; // Bad packet, wait for next
162 }
163
164 // Create message object from raw message
165 pMsg = new CSCPMessage(pRawMsg);
166 if (pMsg->GetCode() == CMD_TRAP)
167 {
901c96c7 168 OnTrap(pMsg);
f77084bb
VK
169 delete pMsg;
170 }
171 else
172 {
173 m_pMsgWaitQueue->Put(pMsg);
174 }
175 }
176
1c62be36
VK
177 // Close socket and mark connection as disconnected
178 Disconnect();
179
f77084bb
VK
180 MemFree(pRawMsg);
181 MemFree(pMsgBuffer);
182}
183
184
185//
186// Connect to agent
187//
188
189BOOL AgentConnection::Connect(BOOL bVerbose)
190{
191 struct sockaddr_in sa;
192 char szBuffer[256];
193 BOOL bSuccess = FALSE;
d1d0b3be 194 DWORD dwError;
f77084bb 195
1c62be36
VK
196 // Check if already connected
197 if ((m_bIsConnected) || (m_hSocket != -1))
198 return FALSE;
199
f77084bb
VK
200 // Create socket
201 m_hSocket = socket(AF_INET, SOCK_STREAM, 0);
202 if (m_hSocket == -1)
203 {
204 PrintMsg("Call to socket() failed");
205 goto connect_cleanup;
206 }
207
208 // Fill in address structure
209 memset(&sa, 0, sizeof(sa));
210 sa.sin_addr.s_addr = m_dwAddr;
211 sa.sin_family = AF_INET;
212 sa.sin_port = htons(m_wPort);
213
214 // Connect to server
215 if (connect(m_hSocket, (struct sockaddr *)&sa, sizeof(sa)) == -1)
216 {
217 if (bVerbose)
218 PrintMsg("Cannot establish connection with agent %s", IpToStr(m_dwAddr, szBuffer));
219 goto connect_cleanup;
220 }
221
222 // Start receiver thread
1ba9a162 223 ThreadCreate(ReceiverThreadStarter, 0, this);
f77084bb
VK
224
225 // Authenticate itself to agent
d1d0b3be 226 if ((dwError = Authenticate()) != ERR_SUCCESS)
f77084bb 227 {
d1d0b3be
VK
228 PrintMsg("Authentication to agent %s failed (%s)", IpToStr(m_dwAddr, szBuffer),
229 AgentErrorCodeToText(dwError));
230 goto connect_cleanup;
f77084bb
VK
231 }
232
233 // Test connectivity
d1d0b3be 234 if ((dwError = Nop()) != ERR_SUCCESS)
f77084bb 235 {
d1d0b3be
VK
236 PrintMsg("Communication with agent %s failed (%s)", IpToStr(m_dwAddr, szBuffer),
237 AgentErrorCodeToText(dwError));
f77084bb
VK
238 goto connect_cleanup;
239 }
240
241 bSuccess = TRUE;
242
243connect_cleanup:
244 if (!bSuccess)
245 {
1c62be36 246 Lock();
f77084bb
VK
247 if (m_hSocket != -1)
248 {
249 shutdown(m_hSocket, 2);
250 closesocket(m_hSocket);
251 m_hSocket = -1;
252 }
1c62be36 253 Unlock();
f77084bb 254 }
1c62be36 255 m_bIsConnected = bSuccess;
f77084bb
VK
256 return bSuccess;
257}
258
259
260//
261// Disconnect from agent
262//
263
264void AgentConnection::Disconnect(void)
265{
1c62be36 266 Lock();
f77084bb
VK
267 if (m_hSocket != -1)
268 {
269 shutdown(m_hSocket, 2);
270 closesocket(m_hSocket);
271 m_hSocket = -1;
272 }
273 DestroyResultData();
1c62be36
VK
274 m_bIsConnected = FALSE;
275 Unlock();
f77084bb
VK
276}
277
278
279//
280// Destroy command execuion results data
281//
282
283void AgentConnection::DestroyResultData(void)
284{
285 DWORD i;
286
287 if (m_ppDataLines != NULL)
288 {
289 for(i = 0; i < m_dwNumDataLines; i++)
290 if (m_ppDataLines[i] != NULL)
901c96c7
VK
291 MemFree(m_ppDataLines[i]);
292 MemFree(m_ppDataLines);
f77084bb
VK
293 m_ppDataLines = NULL;
294 }
295 m_dwNumDataLines = 0;
296}
297
298
299//
300// Get interface list from agent
301//
302
303INTERFACE_LIST *AgentConnection::GetInterfaceList(void)
304{
1c62be36
VK
305 INTERFACE_LIST *pIfList = NULL;
306 DWORD i;
307 char *pChar, *pBuf;
308
309 if (GetList("InterfaceList") == ERR_SUCCESS)
310 {
fa7935ac 311 pIfList = (INTERFACE_LIST *)MemAlloc(sizeof(INTERFACE_LIST));
1c62be36 312 pIfList->iNumEntries = m_dwNumDataLines;
fa7935ac 313 pIfList->pInterfaces = (INTERFACE_INFO *)MemAlloc(sizeof(INTERFACE_INFO) * m_dwNumDataLines);
1c62be36
VK
314 memset(pIfList->pInterfaces, 0, sizeof(INTERFACE_INFO) * m_dwNumDataLines);
315 for(i = 0; i < m_dwNumDataLines; i++)
316 {
317 pBuf = m_ppDataLines[i];
318
319 // Index
320 pChar = strchr(pBuf, ' ');
321 if (pChar != NULL)
322 {
323 *pChar = 0;
324 pIfList->pInterfaces[i].dwIndex = strtoul(pBuf, NULL, 10);
325 pBuf = pChar + 1;
326 }
327
328 // Address and mask
329 pChar = strchr(pBuf, ' ');
330 if (pChar != NULL)
331 {
332 char *pSlash;
333
334 *pChar = 0;
335 pSlash = strchr(pBuf, '/');
336 if (pSlash != NULL)
337 {
338 *pSlash = 0;
339 pSlash++;
340 }
341 else // Just a paranoia protection, should'n happen if agent working correctly
342 {
343 pSlash = "24";
344 }
345 pIfList->pInterfaces[i].dwIpAddr = inet_addr(pBuf);
346 pIfList->pInterfaces[i].dwIpNetMask = htonl(~(0xFFFFFFFF >> strtoul(pSlash, NULL, 10)));
347 pBuf = pChar + 1;
348 }
349
350 // Interface type
351 pChar = strchr(pBuf, ' ');
352 if (pChar != NULL)
353 {
354 *pChar = 0;
355 pIfList->pInterfaces[i].dwIndex = strtoul(pBuf, NULL, 10);
356 pBuf = pChar + 1;
357 }
358
359 // Name
360 strncpy(pIfList->pInterfaces[i].szName, pBuf, MAX_OBJECT_NAME - 1);
361 }
362
363 DestroyResultData();
364 }
365
366 return pIfList;
f77084bb
VK
367}
368
369
370//
371// Get parameter value
372//
373
374DWORD AgentConnection::GetParameter(char *pszParam, DWORD dwBufSize, char *pszBuffer)
375{
376 CSCPMessage msg, *pResponce;
377 DWORD dwRqId, dwRetCode;
378
1c62be36 379 if (m_bIsConnected)
f77084bb 380 {
1c62be36
VK
381 dwRqId = m_dwRequestId++;
382 msg.SetCode(CMD_GET_PARAMETER);
383 msg.SetId(dwRqId);
384 msg.SetVariable(VID_PARAMETER, pszParam);
385 if (SendMessage(&msg))
f77084bb 386 {
1c62be36
VK
387 pResponce = WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
388 if (pResponce != NULL)
389 {
390 dwRetCode = pResponce->GetVariableLong(VID_RCC);
391 if (dwRetCode == ERR_SUCCESS)
392 pResponce->GetVariableStr(VID_VALUE, pszBuffer, dwBufSize);
393 delete pResponce;
394 }
395 else
396 {
397 dwRetCode = ERR_REQUEST_TIMEOUT;
398 }
f77084bb
VK
399 }
400 else
401 {
1c62be36 402 dwRetCode = ERR_CONNECTION_BROKEN;
f77084bb
VK
403 }
404 }
405 else
406 {
1c62be36 407 dwRetCode = ERR_NOT_CONNECTED;
f77084bb
VK
408 }
409
410 return dwRetCode;
411}
412
413
414//
415// Get ARP cache
416//
417
418ARP_CACHE *AgentConnection::GetArpCache(void)
419{
1c62be36
VK
420 ARP_CACHE *pArpCache = NULL;
421 char szByte[4], *pBuf, *pChar;
422 DWORD i, j;
423
424 if (GetList("ArpCache") == ERR_SUCCESS)
425 {
426 // Create empty structure
fa7935ac 427 pArpCache = (ARP_CACHE *)MemAlloc(sizeof(ARP_CACHE));
1c62be36 428 pArpCache->dwNumEntries = m_dwNumDataLines;
fa7935ac 429 pArpCache->pEntries = (ARP_ENTRY *)MemAlloc(sizeof(ARP_ENTRY) * m_dwNumDataLines);
1c62be36
VK
430 memset(pArpCache->pEntries, 0, sizeof(ARP_ENTRY) * m_dwNumDataLines);
431
432 szByte[2] = 0;
433
434 // Parse data lines
435 // Each line has form of XXXXXXXXXXXX a.b.c.d
436 // where XXXXXXXXXXXX is a MAC address (12 hexadecimal digits)
437 // and a.b.c.d is an IP address in decimal dotted notation
438 for(i = 0; i < m_dwNumDataLines; i++)
439 {
440 pBuf = m_ppDataLines[i];
441 if (strlen(pBuf) < 20) // Invalid line
442 continue;
443
444 // MAC address
445 for(j = 0; j < 6; j++)
446 {
447 memcpy(szByte, pBuf, 2);
448 pArpCache->pEntries[i].bMacAddr[j] = (BYTE)strtol(szByte, NULL, 16);
449 pBuf+=2;
450 }
451
452 // IP address
453 while(*pBuf == ' ')
454 pBuf++;
455 pChar = strchr(pBuf, ' ');
456 if (pChar != NULL)
457 *pChar = 0;
458 pArpCache->pEntries[i].dwIpAddr = inet_addr(pBuf);
459
460 // Interface index
461 if (pChar != NULL)
462 pArpCache->pEntries[i].dwIndex = strtoul(pChar + 1, NULL, 10);
463 }
464
465 DestroyResultData();
466 }
467 return pArpCache;
f77084bb
VK
468}
469
470
471//
472// Send dummy command to agent (can be used for keepalive)
473//
474
475DWORD AgentConnection::Nop(void)
476{
477 CSCPMessage msg;
478 DWORD dwRqId;
479
480 dwRqId = m_dwRequestId++;
481 msg.SetCode(CMD_KEEPALIVE);
482 msg.SetId(dwRqId);
483 if (SendMessage(&msg))
484 return WaitForRCC(dwRqId, m_dwCommandTimeout);
485 else
486 return ERR_CONNECTION_BROKEN;
487}
488
489
490//
491// Wait for request completion code
492//
493
494DWORD AgentConnection::WaitForRCC(DWORD dwRqId, DWORD dwTimeOut)
495{
496 CSCPMessage *pMsg;
497 DWORD dwRetCode;
498
499 pMsg = m_pMsgWaitQueue->WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId, dwTimeOut);
500 if (pMsg != NULL)
501 {
502 dwRetCode = pMsg->GetVariableLong(VID_RCC);
503 delete pMsg;
504 }
505 else
506 {
507 dwRetCode = ERR_REQUEST_TIMEOUT;
508 }
509 return dwRetCode;
510}
511
512
513//
514// Send message to agent
515//
516
517BOOL AgentConnection::SendMessage(CSCPMessage *pMsg)
518{
519 CSCP_MESSAGE *pRawMsg;
520 BOOL bResult;
521
522 pRawMsg = pMsg->CreateMessage();
523 bResult = (send(m_hSocket, (char *)pRawMsg, ntohl(pRawMsg->dwSize), 0) == (int)ntohl(pRawMsg->dwSize));
524 MemFree(pRawMsg);
525 return bResult;
526}
901c96c7
VK
527
528
529//
530// Trap handler. Should be overriden in derived classes to implement
531// actual trap processing. Default implementation do nothing.
532//
533
534void AgentConnection::OnTrap(CSCPMessage *pMsg)
535{
536}
537
538
539//
540// Get list of values
541//
542
543DWORD AgentConnection::GetList(char *pszParam)
544{
545 CSCPMessage msg, *pResponce;
546 DWORD i, dwRqId, dwRetCode;
547
1c62be36 548 if (m_bIsConnected)
901c96c7 549 {
1c62be36
VK
550 DestroyResultData();
551 dwRqId = m_dwRequestId++;
552 msg.SetCode(CMD_GET_LIST);
553 msg.SetId(dwRqId);
554 msg.SetVariable(VID_PARAMETER, pszParam);
555 if (SendMessage(&msg))
901c96c7 556 {
1c62be36
VK
557 pResponce = WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
558 if (pResponce != NULL)
559 {
560 dwRetCode = pResponce->GetVariableLong(VID_RCC);
561 if (dwRetCode == ERR_SUCCESS)
562 {
563 m_dwNumDataLines = pResponce->GetVariableLong(VID_NUM_STRINGS);
564 m_ppDataLines = (char **)MemAlloc(sizeof(char *) * m_dwNumDataLines);
565 for(i = 0; i < m_dwNumDataLines; i++)
566 m_ppDataLines[i] = pResponce->GetVariableStr(VID_ENUM_VALUE_BASE + i);
567 }
568 delete pResponce;
569 }
570 else
901c96c7 571 {
1c62be36 572 dwRetCode = ERR_REQUEST_TIMEOUT;
901c96c7 573 }
901c96c7
VK
574 }
575 else
576 {
1c62be36 577 dwRetCode = ERR_CONNECTION_BROKEN;
901c96c7
VK
578 }
579 }
580 else
581 {
1c62be36 582 dwRetCode = ERR_NOT_CONNECTED;
901c96c7
VK
583 }
584
585 return dwRetCode;
586}
d1d0b3be
VK
587
588
589//
590// Authenticate to agent
591//
592
593DWORD AgentConnection::Authenticate(void)
594{
595 CSCPMessage msg;
596 DWORD dwRqId;
597 BYTE hash[32];
598
599 if (m_iAuthMethod == AUTH_NONE)
600 return ERR_SUCCESS; // No authentication required
601
602 dwRqId = m_dwRequestId++;
603 msg.SetCode(CMD_AUTHENTICATE);
604 msg.SetId(dwRqId);
605 msg.SetVariable(VID_AUTH_METHOD, (WORD)m_iAuthMethod);
606 switch(m_iAuthMethod)
607 {
608 case AUTH_PLAINTEXT:
609 msg.SetVariable(VID_SHARED_SECRET, m_szSecret);
610 break;
611 case AUTH_MD5_HASH:
612 CalculateMD5Hash((BYTE *)m_szSecret, strlen(m_szSecret), hash);
613 msg.SetVariable(VID_SHARED_SECRET, hash, MD5_DIGEST_SIZE);
614 break;
615 case AUTH_SHA1_HASH:
616 CalculateSHA1Hash((BYTE *)m_szSecret, strlen(m_szSecret), hash);
617 msg.SetVariable(VID_SHARED_SECRET, hash, SHA1_DIGEST_SIZE);
618 break;
619 default:
620 break;
621 }
622 if (SendMessage(&msg))
623 return WaitForRCC(dwRqId, m_dwCommandTimeout);
624 else
625 return ERR_CONNECTION_BROKEN;
626}
3c774461
VK
627
628
629//
630// Execute action on agent
631//
632
633DWORD AgentConnection::ExecAction(char *pszAction, int argc, char **argv)
634{
635 CSCPMessage msg;
636 DWORD dwRqId;
637 int i;
638
1c62be36
VK
639 if (!m_bIsConnected)
640 return ERR_NOT_CONNECTED;
641
3c774461
VK
642 dwRqId = m_dwRequestId++;
643 msg.SetCode(CMD_ACTION);
644 msg.SetId(dwRqId);
645 msg.SetVariable(VID_ACTION_NAME, pszAction);
646 msg.SetVariable(VID_NUM_ARGS, (DWORD)argc);
647 for(i = 0; i < argc; i++)
648 msg.SetVariable(VID_ACTION_ARG_BASE + i, argv[i]);
649
650 if (SendMessage(&msg))
651 return WaitForRCC(dwRqId, m_dwCommandTimeout);
652 else
653 return ERR_CONNECTION_BROKEN;
654}