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