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