Minir changes and fixes
[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
VK
61 m_dwCommandTimeout = 10000; // Default timeout 10 seconds
62}
63
64
65//
66// Normal constructor for AgentConnection
67//
68
69AgentConnection::AgentConnection(DWORD dwAddr, WORD wPort, int iAuthMethod, char *szSecret)
70{
71 m_dwAddr = dwAddr;
72 m_wPort = wPort;
73 m_iAuthMethod = iAuthMethod;
74 if (szSecret != NULL)
75 strncpy(m_szSecret, szSecret, MAX_SECRET_LENGTH);
76 else
77 m_szSecret[0] = 0;
78 m_hSocket = -1;
79 m_tLastCommandTime = 0;
80 m_dwNumDataLines = 0;
81 m_ppDataLines = NULL;
82 m_pMsgWaitQueue = new MsgWaitQueue;
d1d0b3be 83 m_dwRequestId = 1;
f77084bb
VK
84 m_dwCommandTimeout = 10000; // Default timeout 10 seconds
85}
86
87
88//
89// Destructor
90//
91
92AgentConnection::~AgentConnection()
93{
94 if (m_hSocket != -1)
95 closesocket(m_hSocket);
96 DestroyResultData();
97 delete m_pMsgWaitQueue;
98}
99
100
101//
059f6632
VK
102// Print message. This function is virtual and can be overrided in
103// derived classes. Default implementation will print message to stdout.
f77084bb
VK
104//
105
106void AgentConnection::PrintMsg(char *pszFormat, ...)
107{
108 va_list args;
109
110 va_start(args, pszFormat);
111 vprintf(pszFormat, args);
112 va_end(args);
113 printf("\n");
114}
115
116
117//
118// Receiver thread
119//
120
121void AgentConnection::ReceiverThread(void)
122{
123 CSCPMessage *pMsg;
124 CSCP_MESSAGE *pRawMsg;
125 CSCP_BUFFER *pMsgBuffer;
126 int iErr;
127 char szBuffer[128];
128
129 // Initialize raw message receiving function
130 pMsgBuffer = (CSCP_BUFFER *)MemAlloc(sizeof(CSCP_BUFFER));
131 RecvCSCPMessage(0, NULL, pMsgBuffer, 0);
132
133 // Allocate space for raw message
134 pRawMsg = (CSCP_MESSAGE *)MemAlloc(RECEIVER_BUFFER_SIZE);
135
136 // Message receiving loop
137 while(1)
138 {
139 // Receive raw message
140 if ((iErr = RecvCSCPMessage(m_hSocket, pRawMsg, pMsgBuffer, RECEIVER_BUFFER_SIZE)) <= 0)
141 break;
142
143 // Check if we get too large message
144 if (iErr == 1)
145 {
146 PrintMsg("Received too large message %s (%ld bytes)",
147 CSCPMessageCodeName(ntohs(pRawMsg->wCode), szBuffer),
148 ntohl(pRawMsg->dwSize));
149 continue;
150 }
151
152 // Check that actual received packet size is equal to encoded in packet
153 if ((int)ntohl(pRawMsg->dwSize) != iErr)
154 {
155 PrintMsg("RecvMsg: Bad packet length [dwSize=%d ActualSize=%d]", ntohl(pRawMsg->dwSize), iErr);
156 continue; // Bad packet, wait for next
157 }
158
159 // Create message object from raw message
160 pMsg = new CSCPMessage(pRawMsg);
161 if (pMsg->GetCode() == CMD_TRAP)
162 {
901c96c7 163 OnTrap(pMsg);
f77084bb
VK
164 delete pMsg;
165 }
166 else
167 {
168 m_pMsgWaitQueue->Put(pMsg);
169 }
170 }
171
172 MemFree(pRawMsg);
173 MemFree(pMsgBuffer);
174}
175
176
177//
178// Connect to agent
179//
180
181BOOL AgentConnection::Connect(BOOL bVerbose)
182{
183 struct sockaddr_in sa;
184 char szBuffer[256];
185 BOOL bSuccess = FALSE;
d1d0b3be 186 DWORD dwError;
f77084bb
VK
187
188 // Create socket
189 m_hSocket = socket(AF_INET, SOCK_STREAM, 0);
190 if (m_hSocket == -1)
191 {
192 PrintMsg("Call to socket() failed");
193 goto connect_cleanup;
194 }
195
196 // Fill in address structure
197 memset(&sa, 0, sizeof(sa));
198 sa.sin_addr.s_addr = m_dwAddr;
199 sa.sin_family = AF_INET;
200 sa.sin_port = htons(m_wPort);
201
202 // Connect to server
203 if (connect(m_hSocket, (struct sockaddr *)&sa, sizeof(sa)) == -1)
204 {
205 if (bVerbose)
206 PrintMsg("Cannot establish connection with agent %s", IpToStr(m_dwAddr, szBuffer));
207 goto connect_cleanup;
208 }
209
210 // Start receiver thread
1ba9a162 211 ThreadCreate(ReceiverThreadStarter, 0, this);
f77084bb
VK
212
213 // Authenticate itself to agent
d1d0b3be 214 if ((dwError = Authenticate()) != ERR_SUCCESS)
f77084bb 215 {
d1d0b3be
VK
216 PrintMsg("Authentication to agent %s failed (%s)", IpToStr(m_dwAddr, szBuffer),
217 AgentErrorCodeToText(dwError));
218 goto connect_cleanup;
f77084bb
VK
219 }
220
221 // Test connectivity
d1d0b3be 222 if ((dwError = Nop()) != ERR_SUCCESS)
f77084bb 223 {
d1d0b3be
VK
224 PrintMsg("Communication with agent %s failed (%s)", IpToStr(m_dwAddr, szBuffer),
225 AgentErrorCodeToText(dwError));
f77084bb
VK
226 goto connect_cleanup;
227 }
228
229 bSuccess = TRUE;
230
231connect_cleanup:
232 if (!bSuccess)
233 {
234 if (m_hSocket != -1)
235 {
236 shutdown(m_hSocket, 2);
237 closesocket(m_hSocket);
238 m_hSocket = -1;
239 }
240 }
241 return bSuccess;
242}
243
244
245//
246// Disconnect from agent
247//
248
249void AgentConnection::Disconnect(void)
250{
251 if (m_hSocket != -1)
252 {
253 shutdown(m_hSocket, 2);
254 closesocket(m_hSocket);
255 m_hSocket = -1;
256 }
257 DestroyResultData();
258}
259
260
261//
262// Destroy command execuion results data
263//
264
265void AgentConnection::DestroyResultData(void)
266{
267 DWORD i;
268
269 if (m_ppDataLines != NULL)
270 {
271 for(i = 0; i < m_dwNumDataLines; i++)
272 if (m_ppDataLines[i] != NULL)
901c96c7
VK
273 MemFree(m_ppDataLines[i]);
274 MemFree(m_ppDataLines);
f77084bb
VK
275 m_ppDataLines = NULL;
276 }
277 m_dwNumDataLines = 0;
278}
279
280
281//
282// Get interface list from agent
283//
284
285INTERFACE_LIST *AgentConnection::GetInterfaceList(void)
286{
287 return NULL;
288}
289
290
291//
292// Get parameter value
293//
294
295DWORD AgentConnection::GetParameter(char *pszParam, DWORD dwBufSize, char *pszBuffer)
296{
297 CSCPMessage msg, *pResponce;
298 DWORD dwRqId, dwRetCode;
299
300 dwRqId = m_dwRequestId++;
301 msg.SetCode(CMD_GET_PARAMETER);
302 msg.SetId(dwRqId);
303 msg.SetVariable(VID_PARAMETER, pszParam);
304 if (SendMessage(&msg))
305 {
306 pResponce = WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
307 if (pResponce != NULL)
308 {
309 dwRetCode = pResponce->GetVariableLong(VID_RCC);
310 if (dwRetCode == ERR_SUCCESS)
311 pResponce->GetVariableStr(VID_VALUE, pszBuffer, dwBufSize);
312 delete pResponce;
313 }
314 else
315 {
316 dwRetCode = ERR_REQUEST_TIMEOUT;
317 }
318 }
319 else
320 {
321 dwRetCode = ERR_CONNECTION_BROKEN;
322 }
323
324 return dwRetCode;
325}
326
327
328//
329// Get ARP cache
330//
331
332ARP_CACHE *AgentConnection::GetArpCache(void)
333{
334 return NULL;
335}
336
337
338//
339// Send dummy command to agent (can be used for keepalive)
340//
341
342DWORD AgentConnection::Nop(void)
343{
344 CSCPMessage msg;
345 DWORD dwRqId;
346
347 dwRqId = m_dwRequestId++;
348 msg.SetCode(CMD_KEEPALIVE);
349 msg.SetId(dwRqId);
350 if (SendMessage(&msg))
351 return WaitForRCC(dwRqId, m_dwCommandTimeout);
352 else
353 return ERR_CONNECTION_BROKEN;
354}
355
356
357//
358// Wait for request completion code
359//
360
361DWORD AgentConnection::WaitForRCC(DWORD dwRqId, DWORD dwTimeOut)
362{
363 CSCPMessage *pMsg;
364 DWORD dwRetCode;
365
366 pMsg = m_pMsgWaitQueue->WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId, dwTimeOut);
367 if (pMsg != NULL)
368 {
369 dwRetCode = pMsg->GetVariableLong(VID_RCC);
370 delete pMsg;
371 }
372 else
373 {
374 dwRetCode = ERR_REQUEST_TIMEOUT;
375 }
376 return dwRetCode;
377}
378
379
380//
381// Send message to agent
382//
383
384BOOL AgentConnection::SendMessage(CSCPMessage *pMsg)
385{
386 CSCP_MESSAGE *pRawMsg;
387 BOOL bResult;
388
389 pRawMsg = pMsg->CreateMessage();
390 bResult = (send(m_hSocket, (char *)pRawMsg, ntohl(pRawMsg->dwSize), 0) == (int)ntohl(pRawMsg->dwSize));
391 MemFree(pRawMsg);
392 return bResult;
393}
901c96c7
VK
394
395
396//
397// Trap handler. Should be overriden in derived classes to implement
398// actual trap processing. Default implementation do nothing.
399//
400
401void AgentConnection::OnTrap(CSCPMessage *pMsg)
402{
403}
404
405
406//
407// Get list of values
408//
409
410DWORD AgentConnection::GetList(char *pszParam)
411{
412 CSCPMessage msg, *pResponce;
413 DWORD i, dwRqId, dwRetCode;
414
415 DestroyResultData();
416 dwRqId = m_dwRequestId++;
417 msg.SetCode(CMD_GET_LIST);
418 msg.SetId(dwRqId);
419 msg.SetVariable(VID_PARAMETER, pszParam);
420 if (SendMessage(&msg))
421 {
422 pResponce = WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId, m_dwCommandTimeout);
423 if (pResponce != NULL)
424 {
425 dwRetCode = pResponce->GetVariableLong(VID_RCC);
426 if (dwRetCode == ERR_SUCCESS)
427 {
428 m_dwNumDataLines = pResponce->GetVariableLong(VID_NUM_STRINGS);
429 m_ppDataLines = (char **)MemAlloc(sizeof(char *) * m_dwNumDataLines);
430 for(i = 0; i < m_dwNumDataLines; i++)
431 m_ppDataLines[i] = pResponce->GetVariableStr(VID_ENUM_VALUE_BASE + i);
432 }
433 delete pResponce;
434 }
435 else
436 {
437 dwRetCode = ERR_REQUEST_TIMEOUT;
438 }
439 }
440 else
441 {
442 dwRetCode = ERR_CONNECTION_BROKEN;
443 }
444
445 return dwRetCode;
446}
d1d0b3be
VK
447
448
449//
450// Authenticate to agent
451//
452
453DWORD AgentConnection::Authenticate(void)
454{
455 CSCPMessage msg;
456 DWORD dwRqId;
457 BYTE hash[32];
458
459 if (m_iAuthMethod == AUTH_NONE)
460 return ERR_SUCCESS; // No authentication required
461
462 dwRqId = m_dwRequestId++;
463 msg.SetCode(CMD_AUTHENTICATE);
464 msg.SetId(dwRqId);
465 msg.SetVariable(VID_AUTH_METHOD, (WORD)m_iAuthMethod);
466 switch(m_iAuthMethod)
467 {
468 case AUTH_PLAINTEXT:
469 msg.SetVariable(VID_SHARED_SECRET, m_szSecret);
470 break;
471 case AUTH_MD5_HASH:
472 CalculateMD5Hash((BYTE *)m_szSecret, strlen(m_szSecret), hash);
473 msg.SetVariable(VID_SHARED_SECRET, hash, MD5_DIGEST_SIZE);
474 break;
475 case AUTH_SHA1_HASH:
476 CalculateSHA1Hash((BYTE *)m_szSecret, strlen(m_szSecret), hash);
477 msg.SetVariable(VID_SHARED_SECRET, hash, SHA1_DIGEST_SIZE);
478 break;
479 default:
480 break;
481 }
482 if (SendMessage(&msg))
483 return WaitForRCC(dwRqId, m_dwCommandTimeout);
484 else
485 return ERR_CONNECTION_BROKEN;
486}
3c774461
VK
487
488
489//
490// Execute action on agent
491//
492
493DWORD AgentConnection::ExecAction(char *pszAction, int argc, char **argv)
494{
495 CSCPMessage msg;
496 DWORD dwRqId;
497 int i;
498
499 dwRqId = m_dwRequestId++;
500 msg.SetCode(CMD_ACTION);
501 msg.SetId(dwRqId);
502 msg.SetVariable(VID_ACTION_NAME, pszAction);
503 msg.SetVariable(VID_NUM_ARGS, (DWORD)argc);
504 for(i = 0; i < argc; i++)
505 msg.SetVariable(VID_ACTION_ARG_BASE + i, argv[i]);
506
507 if (SendMessage(&msg))
508 return WaitForRCC(dwRqId, m_dwCommandTimeout);
509 else
510 return ERR_CONNECTION_BROKEN;
511}