Fixed synchronization bug in AgentConnection class destructor
[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
VK
149 // Initialize raw message receiving function
150 pMsgBuffer = (CSCP_BUFFER *)MemAlloc(sizeof(CSCP_BUFFER));
151 RecvCSCPMessage(0, NULL, pMsgBuffer, 0);
152
153 // Allocate space for raw message
154 pRawMsg = (CSCP_MESSAGE *)MemAlloc(RECEIVER_BUFFER_SIZE);
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
f77084bb
VK
195 MemFree(pRawMsg);
196 MemFree(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)
901c96c7
VK
308 MemFree(m_ppDataLines[i]);
309 MemFree(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
326 if (GetList("InterfaceList") == ERR_SUCCESS)
327 {
fa7935ac 328 pIfList = (INTERFACE_LIST *)MemAlloc(sizeof(INTERFACE_LIST));
1c62be36 329 pIfList->iNumEntries = m_dwNumDataLines;
fa7935ac 330 pIfList->pInterfaces = (INTERFACE_INFO *)MemAlloc(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
380 DestroyResultData();
381 }
382
383 return pIfList;
f77084bb
VK
384}
385
386
387//
388// Get parameter value
389//
390
391DWORD AgentConnection::GetParameter(char *pszParam, DWORD dwBufSize, char *pszBuffer)
392{
393 CSCPMessage msg, *pResponce;
394 DWORD dwRqId, dwRetCode;
395
1c62be36 396 if (m_bIsConnected)
f77084bb 397 {
1c62be36
VK
398 dwRqId = m_dwRequestId++;
399 msg.SetCode(CMD_GET_PARAMETER);
400 msg.SetId(dwRqId);
401 msg.SetVariable(VID_PARAMETER, pszParam);
402 if (SendMessage(&msg))
f77084bb 403 {
1c62be36
VK
404 pResponce = WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
405 if (pResponce != NULL)
406 {
407 dwRetCode = pResponce->GetVariableLong(VID_RCC);
408 if (dwRetCode == ERR_SUCCESS)
409 pResponce->GetVariableStr(VID_VALUE, pszBuffer, dwBufSize);
410 delete pResponce;
411 }
412 else
413 {
414 dwRetCode = ERR_REQUEST_TIMEOUT;
415 }
f77084bb
VK
416 }
417 else
418 {
1c62be36 419 dwRetCode = ERR_CONNECTION_BROKEN;
f77084bb
VK
420 }
421 }
422 else
423 {
1c62be36 424 dwRetCode = ERR_NOT_CONNECTED;
f77084bb
VK
425 }
426
427 return dwRetCode;
428}
429
430
431//
432// Get ARP cache
433//
434
435ARP_CACHE *AgentConnection::GetArpCache(void)
436{
1c62be36
VK
437 ARP_CACHE *pArpCache = NULL;
438 char szByte[4], *pBuf, *pChar;
439 DWORD i, j;
440
441 if (GetList("ArpCache") == ERR_SUCCESS)
442 {
443 // Create empty structure
fa7935ac 444 pArpCache = (ARP_CACHE *)MemAlloc(sizeof(ARP_CACHE));
1c62be36 445 pArpCache->dwNumEntries = m_dwNumDataLines;
fa7935ac 446 pArpCache->pEntries = (ARP_ENTRY *)MemAlloc(sizeof(ARP_ENTRY) * m_dwNumDataLines);
1c62be36
VK
447 memset(pArpCache->pEntries, 0, sizeof(ARP_ENTRY) * m_dwNumDataLines);
448
449 szByte[2] = 0;
450
451 // Parse data lines
452 // Each line has form of XXXXXXXXXXXX a.b.c.d
453 // where XXXXXXXXXXXX is a MAC address (12 hexadecimal digits)
454 // and a.b.c.d is an IP address in decimal dotted notation
455 for(i = 0; i < m_dwNumDataLines; i++)
456 {
457 pBuf = m_ppDataLines[i];
458 if (strlen(pBuf) < 20) // Invalid line
459 continue;
460
461 // MAC address
462 for(j = 0; j < 6; j++)
463 {
464 memcpy(szByte, pBuf, 2);
465 pArpCache->pEntries[i].bMacAddr[j] = (BYTE)strtol(szByte, NULL, 16);
466 pBuf+=2;
467 }
468
469 // IP address
470 while(*pBuf == ' ')
471 pBuf++;
472 pChar = strchr(pBuf, ' ');
473 if (pChar != NULL)
474 *pChar = 0;
475 pArpCache->pEntries[i].dwIpAddr = inet_addr(pBuf);
476
477 // Interface index
478 if (pChar != NULL)
479 pArpCache->pEntries[i].dwIndex = strtoul(pChar + 1, NULL, 10);
480 }
481
482 DestroyResultData();
483 }
484 return pArpCache;
f77084bb
VK
485}
486
487
488//
489// Send dummy command to agent (can be used for keepalive)
490//
491
492DWORD AgentConnection::Nop(void)
493{
494 CSCPMessage msg;
495 DWORD dwRqId;
496
497 dwRqId = m_dwRequestId++;
498 msg.SetCode(CMD_KEEPALIVE);
499 msg.SetId(dwRqId);
500 if (SendMessage(&msg))
501 return WaitForRCC(dwRqId, m_dwCommandTimeout);
502 else
503 return ERR_CONNECTION_BROKEN;
504}
505
506
507//
508// Wait for request completion code
509//
510
511DWORD AgentConnection::WaitForRCC(DWORD dwRqId, DWORD dwTimeOut)
512{
513 CSCPMessage *pMsg;
514 DWORD dwRetCode;
515
516 pMsg = m_pMsgWaitQueue->WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId, dwTimeOut);
517 if (pMsg != NULL)
518 {
519 dwRetCode = pMsg->GetVariableLong(VID_RCC);
520 delete pMsg;
521 }
522 else
523 {
524 dwRetCode = ERR_REQUEST_TIMEOUT;
525 }
526 return dwRetCode;
527}
528
529
530//
531// Send message to agent
532//
533
534BOOL AgentConnection::SendMessage(CSCPMessage *pMsg)
535{
536 CSCP_MESSAGE *pRawMsg;
537 BOOL bResult;
538
539 pRawMsg = pMsg->CreateMessage();
540 bResult = (send(m_hSocket, (char *)pRawMsg, ntohl(pRawMsg->dwSize), 0) == (int)ntohl(pRawMsg->dwSize));
541 MemFree(pRawMsg);
542 return bResult;
543}
901c96c7
VK
544
545
546//
547// Trap handler. Should be overriden in derived classes to implement
548// actual trap processing. Default implementation do nothing.
549//
550
551void AgentConnection::OnTrap(CSCPMessage *pMsg)
552{
553}
554
555
556//
557// Get list of values
558//
559
560DWORD AgentConnection::GetList(char *pszParam)
561{
562 CSCPMessage msg, *pResponce;
563 DWORD i, dwRqId, dwRetCode;
564
1c62be36 565 if (m_bIsConnected)
901c96c7 566 {
1c62be36
VK
567 DestroyResultData();
568 dwRqId = m_dwRequestId++;
569 msg.SetCode(CMD_GET_LIST);
570 msg.SetId(dwRqId);
571 msg.SetVariable(VID_PARAMETER, pszParam);
572 if (SendMessage(&msg))
901c96c7 573 {
1c62be36
VK
574 pResponce = WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
575 if (pResponce != NULL)
576 {
577 dwRetCode = pResponce->GetVariableLong(VID_RCC);
578 if (dwRetCode == ERR_SUCCESS)
579 {
580 m_dwNumDataLines = pResponce->GetVariableLong(VID_NUM_STRINGS);
581 m_ppDataLines = (char **)MemAlloc(sizeof(char *) * m_dwNumDataLines);
582 for(i = 0; i < m_dwNumDataLines; i++)
583 m_ppDataLines[i] = pResponce->GetVariableStr(VID_ENUM_VALUE_BASE + i);
584 }
585 delete pResponce;
586 }
587 else
901c96c7 588 {
1c62be36 589 dwRetCode = ERR_REQUEST_TIMEOUT;
901c96c7 590 }
901c96c7
VK
591 }
592 else
593 {
1c62be36 594 dwRetCode = ERR_CONNECTION_BROKEN;
901c96c7
VK
595 }
596 }
597 else
598 {
1c62be36 599 dwRetCode = ERR_NOT_CONNECTED;
901c96c7
VK
600 }
601
602 return dwRetCode;
603}
d1d0b3be
VK
604
605
606//
607// Authenticate to agent
608//
609
610DWORD AgentConnection::Authenticate(void)
611{
612 CSCPMessage msg;
613 DWORD dwRqId;
614 BYTE hash[32];
615
616 if (m_iAuthMethod == AUTH_NONE)
617 return ERR_SUCCESS; // No authentication required
618
619 dwRqId = m_dwRequestId++;
620 msg.SetCode(CMD_AUTHENTICATE);
621 msg.SetId(dwRqId);
622 msg.SetVariable(VID_AUTH_METHOD, (WORD)m_iAuthMethod);
623 switch(m_iAuthMethod)
624 {
625 case AUTH_PLAINTEXT:
626 msg.SetVariable(VID_SHARED_SECRET, m_szSecret);
627 break;
628 case AUTH_MD5_HASH:
629 CalculateMD5Hash((BYTE *)m_szSecret, strlen(m_szSecret), hash);
630 msg.SetVariable(VID_SHARED_SECRET, hash, MD5_DIGEST_SIZE);
631 break;
632 case AUTH_SHA1_HASH:
633 CalculateSHA1Hash((BYTE *)m_szSecret, strlen(m_szSecret), hash);
634 msg.SetVariable(VID_SHARED_SECRET, hash, SHA1_DIGEST_SIZE);
635 break;
636 default:
637 break;
638 }
639 if (SendMessage(&msg))
640 return WaitForRCC(dwRqId, m_dwCommandTimeout);
641 else
642 return ERR_CONNECTION_BROKEN;
643}
3c774461
VK
644
645
646//
647// Execute action on agent
648//
649
650DWORD AgentConnection::ExecAction(char *pszAction, int argc, char **argv)
651{
652 CSCPMessage msg;
653 DWORD dwRqId;
654 int i;
655
1c62be36
VK
656 if (!m_bIsConnected)
657 return ERR_NOT_CONNECTED;
658
3c774461
VK
659 dwRqId = m_dwRequestId++;
660 msg.SetCode(CMD_ACTION);
661 msg.SetId(dwRqId);
662 msg.SetVariable(VID_ACTION_NAME, pszAction);
663 msg.SetVariable(VID_NUM_ARGS, (DWORD)argc);
664 for(i = 0; i < argc; i++)
665 msg.SetVariable(VID_ACTION_ARG_BASE + i, argv[i]);
666
667 if (SendMessage(&msg))
668 return WaitForRCC(dwRqId, m_dwCommandTimeout);
669 else
670 return ERR_CONNECTION_BROKEN;
671}