- Unneded webapi commands removed
[public/netxms.git] / src / agent / core / session.cpp
CommitLineData
e9580fef
VK
1/*
2** NetXMS multiplatform core agent
3** Copyright (C) 2003, 2004 Victor Kirhenshtein
4**
5** This program is free software; you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation; either version 2 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program; if not, write to the Free Software
17** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18**
19** $module: session.cpp
20**
21**/
22
23#include "nxagentd.h"
24
25
ccdbbb52
VK
26//
27// Externals
28//
29
30void UnregisterSession(DWORD dwIndex);
31
32
0a147f4b
VK
33//
34// Constants
35//
36
37#define RAW_MSG_SIZE 262144
38
39
ccdbbb52
VK
40//
41// Client communication read thread
42//
43
44THREAD_RESULT THREAD_CALL CommSession::ReadThreadStarter(void *pArg)
45{
46 ((CommSession *)pArg)->ReadThread();
47
48 // When CommSession::ReadThread exits, all other session
49 // threads are already stopped, so we can safely destroy
50 // session object
51 UnregisterSession(((CommSession *)pArg)->GetIndex());
52 delete (CommSession *)pArg;
53 return THREAD_OK;
54}
55
56
57//
58// Client communication write thread
59//
60
61THREAD_RESULT THREAD_CALL CommSession::WriteThreadStarter(void *pArg)
62{
63 ((CommSession *)pArg)->WriteThread();
64 return THREAD_OK;
65}
66
67
68//
69// Received message processing thread
70//
71
72THREAD_RESULT THREAD_CALL CommSession::ProcessingThreadStarter(void *pArg)
73{
74 ((CommSession *)pArg)->ProcessingThread();
75 return THREAD_OK;
76}
77
78
e9580fef
VK
79//
80// Client session class constructor
81//
82
83CommSession::CommSession(SOCKET hSocket, DWORD dwHostAddr)
84{
85 m_pSendQueue = new Queue;
86 m_pMessageQueue = new Queue;
87 m_hSocket = hSocket;
88 m_dwIndex = INVALID_INDEX;
89 m_pMsgBuffer = (CSCP_BUFFER *)malloc(sizeof(CSCP_BUFFER));
ccdbbb52
VK
90 m_hWriteThread = INVALID_THREAD_HANDLE;
91 m_hProcessingThread = INVALID_THREAD_HANDLE;
e9580fef 92 m_dwHostAddr = dwHostAddr;
7c205c0c 93 m_bIsAuthenticated = (g_dwFlags & AF_REQUIRE_AUTH) ? FALSE : TRUE;
e9580fef
VK
94}
95
96
97//
98// Destructor
99//
100
101CommSession::~CommSession()
102{
103 shutdown(m_hSocket, 2);
104 closesocket(m_hSocket);
105 delete m_pSendQueue;
106 delete m_pMessageQueue;
107 safe_free(m_pMsgBuffer);
ccdbbb52
VK
108}
109
110
111//
112// Start all threads
113//
114
115void CommSession::Run(void)
116{
117 m_hWriteThread = ThreadCreateEx(WriteThreadStarter, 0, this);
118 m_hProcessingThread = ThreadCreateEx(ProcessingThreadStarter, 0, this);
119 ThreadCreate(ReadThreadStarter, 0, this);
e9580fef 120}
0a147f4b
VK
121
122
123//
124// Reading thread
125//
126
127void CommSession::ReadThread(void)
128{
129 CSCP_MESSAGE *pRawMsg;
130 CSCPMessage *pMsg;
131 int iErr;
7c205c0c 132 char szBuffer[32];
cb7ec554 133 WORD wFlags;
0a147f4b
VK
134
135 // Initialize raw message receiving function
136 RecvCSCPMessage(0, NULL, m_pMsgBuffer, 0);
137
138 pRawMsg = (CSCP_MESSAGE *)malloc(RAW_MSG_SIZE);
139 while(1)
140 {
141 if ((iErr = RecvCSCPMessage(m_hSocket, pRawMsg, m_pMsgBuffer, RAW_MSG_SIZE)) <= 0)
142 break;
143
144 // Check if message is too large
145 if (iErr == 1)
146 continue;
147
148 // Check that actual received packet size is equal to encoded in packet
149 if ((int)ntohl(pRawMsg->dwSize) != iErr)
150 {
901c96c7 151 DebugPrintf("Actual message size doesn't match wSize value (%d,%d)", iErr, ntohl(pRawMsg->dwSize));
0a147f4b
VK
152 continue; // Bad packet, wait for next
153 }
154
cb7ec554
VK
155 wFlags = ntohs(pRawMsg->wFlags);
156 if (wFlags & MF_BINARY)
157 {
158 // Convert message header to host format
159 pRawMsg->dwId = ntohl(pRawMsg->dwId);
160 pRawMsg->wCode = ntohs(pRawMsg->wCode);
161 pRawMsg->dwNumVars = ntohl(pRawMsg->dwNumVars);
162 if (pRawMsg->wCode == CMD_FILE_DATA)
163 {
164 }
165 }
166 else
167 {
168 // Create message object from raw message
169 pMsg = new CSCPMessage(pRawMsg);
170 m_pMessageQueue->Put(pMsg);
171 }
0a147f4b
VK
172 }
173 if (iErr < 0)
174 WriteLog(MSG_SESSION_BROKEN, EVENTLOG_WARNING_TYPE, "e", WSAGetLastError());
175 free(pRawMsg);
176
177 // Notify other threads to exit
178 m_pSendQueue->Put(INVALID_POINTER_VALUE);
179 m_pMessageQueue->Put(INVALID_POINTER_VALUE);
180
181 // Wait for other threads to finish
ccdbbb52
VK
182 ThreadJoin(m_hWriteThread);
183 ThreadJoin(m_hProcessingThread);
7c205c0c
VK
184
185 DebugPrintf("Session with %s closed", IpToStr(m_dwHostAddr, szBuffer));
0a147f4b
VK
186}
187
188
189//
190// Writing thread
191//
192
193void CommSession::WriteThread(void)
194{
195 CSCP_MESSAGE *pMsg;
196 char szBuffer[128];
197
198 while(1)
199 {
200 pMsg = (CSCP_MESSAGE *)m_pSendQueue->GetOrBlock();
201 if (pMsg == INVALID_POINTER_VALUE) // Session termination indicator
202 break;
203
901c96c7 204 DebugPrintf("Sending message %s", CSCPMessageCodeName(ntohs(pMsg->wCode), szBuffer));
0a147f4b
VK
205 if (send(m_hSocket, (const char *)pMsg, ntohl(pMsg->dwSize), 0) <= 0)
206 {
9d72bde1 207 free(pMsg);
0a147f4b
VK
208 break;
209 }
9d72bde1 210 free(pMsg);
0a147f4b 211 }
0a147f4b
VK
212}
213
214
215//
216// Message processing thread
217//
218
219void CommSession::ProcessingThread(void)
220{
221 CSCPMessage *pMsg;
222 char szBuffer[128];
7c205c0c 223 CSCPMessage msg;
0a147f4b
VK
224
225 while(1)
226 {
227 pMsg = (CSCPMessage *)m_pMessageQueue->GetOrBlock();
228 if (pMsg == INVALID_POINTER_VALUE) // Session termination indicator
229 break;
901c96c7 230 DebugPrintf("Received message %s", CSCPMessageCodeName(pMsg->GetCode(), szBuffer));
7c205c0c
VK
231
232 // Prepare responce message
233 msg.SetCode(CMD_REQUEST_COMPLETED);
234 msg.SetId(pMsg->GetId());
235
236 // Check if authentication required
237 if ((!m_bIsAuthenticated) && (pMsg->GetCode() != CMD_AUTHENTICATE))
238 {
239 msg.SetVariable(VID_RCC, ERR_AUTH_REQUIRED);
240 }
241 else
242 {
243 switch(pMsg->GetCode())
244 {
245 case CMD_AUTHENTICATE:
246 Authenticate(pMsg, &msg);
247 break;
248 case CMD_GET_PARAMETER:
249 GetParameter(pMsg, &msg);
250 break;
251 case CMD_GET_LIST:
901c96c7 252 GetList(pMsg, &msg);
7c205c0c 253 break;
6c426e3a
VK
254 case CMD_KEEPALIVE:
255 msg.SetVariable(VID_RCC, ERR_SUCCESS);
256 break;
3c774461
VK
257 case CMD_ACTION:
258 Action(pMsg, &msg);
259 break;
7c205c0c
VK
260 default:
261 msg.SetVariable(VID_RCC, ERR_UNKNOWN_COMMAND);
262 break;
263 }
264 }
0a147f4b 265 delete pMsg;
7c205c0c
VK
266
267 // Send responce
268 SendMessage(&msg);
269 msg.DeleteAllVariables();
0a147f4b 270 }
0a147f4b 271}
7c205c0c
VK
272
273
274//
275// Authenticate peer
276//
277
278void CommSession::Authenticate(CSCPMessage *pRequest, CSCPMessage *pMsg)
279{
280 if (m_bIsAuthenticated)
281 {
282 // Already authenticated
283 pMsg->SetVariable(VID_RCC, (g_dwFlags & AF_REQUIRE_AUTH) ? ERR_ALREADY_AUTHENTICATED : ERR_AUTH_NOT_REQUIRED);
284 }
285 else
286 {
287 char szSecret[MAX_SECRET_LENGTH];
d1d0b3be 288 BYTE hash[32];
7c205c0c
VK
289 WORD wAuthMethod;
290
291 wAuthMethod = pRequest->GetVariableShort(VID_AUTH_METHOD);
292 switch(wAuthMethod)
293 {
294 case AUTH_PLAINTEXT:
295 pRequest->GetVariableStr(VID_SHARED_SECRET, szSecret, MAX_SECRET_LENGTH);
296 if (!strcmp(szSecret, g_szSharedSecret))
297 {
298 m_bIsAuthenticated = TRUE;
299 pMsg->SetVariable(VID_RCC, ERR_SUCCESS);
300 }
301 else
302 {
303 WriteLog(MSG_AUTH_FAILED, EVENTLOG_WARNING_TYPE, "as", m_dwHostAddr, "PLAIN");
304 pMsg->SetVariable(VID_RCC, ERR_AUTH_FAILED);
305 }
306 break;
d1d0b3be
VK
307 case AUTH_MD5_HASH:
308 pRequest->GetVariableBinary(VID_SHARED_SECRET, (BYTE *)szSecret, MD5_DIGEST_SIZE);
309 CalculateMD5Hash((BYTE *)g_szSharedSecret, strlen(g_szSharedSecret), hash);
310 if (!memcmp(szSecret, hash, MD5_DIGEST_SIZE))
311 {
312 m_bIsAuthenticated = TRUE;
313 pMsg->SetVariable(VID_RCC, ERR_SUCCESS);
314 }
315 else
316 {
317 WriteLog(MSG_AUTH_FAILED, EVENTLOG_WARNING_TYPE, "as", m_dwHostAddr, "MD5");
318 pMsg->SetVariable(VID_RCC, ERR_AUTH_FAILED);
319 }
320 break;
321 case AUTH_SHA1_HASH:
322 pRequest->GetVariableBinary(VID_SHARED_SECRET, (BYTE *)szSecret, SHA1_DIGEST_SIZE);
323 CalculateSHA1Hash((BYTE *)g_szSharedSecret, strlen(g_szSharedSecret), hash);
324 if (!memcmp(szSecret, hash, SHA1_DIGEST_SIZE))
325 {
326 m_bIsAuthenticated = TRUE;
327 pMsg->SetVariable(VID_RCC, ERR_SUCCESS);
328 }
329 else
330 {
331 WriteLog(MSG_AUTH_FAILED, EVENTLOG_WARNING_TYPE, "as", m_dwHostAddr, "SHA1");
332 pMsg->SetVariable(VID_RCC, ERR_AUTH_FAILED);
333 }
334 break;
7c205c0c
VK
335 default:
336 pMsg->SetVariable(VID_RCC, ERR_NOT_IMPLEMENTED);
337 break;
338 }
339 }
340}
341
342
343//
344// Get parameter's value
345//
346
347void CommSession::GetParameter(CSCPMessage *pRequest, CSCPMessage *pMsg)
348{
349 char szParameter[MAX_PARAM_NAME], szValue[MAX_RESULT_LENGTH];
350 DWORD dwErrorCode;
351
352 pRequest->GetVariableStr(VID_PARAMETER, szParameter, MAX_PARAM_NAME);
353 dwErrorCode = GetParameterValue(szParameter, szValue);
354 pMsg->SetVariable(VID_RCC, dwErrorCode);
355 if (dwErrorCode == ERR_SUCCESS)
356 pMsg->SetVariable(VID_VALUE, szValue);
357}
901c96c7
VK
358
359
360//
361// Get list of values
362//
363
364void CommSession::GetList(CSCPMessage *pRequest, CSCPMessage *pMsg)
365{
366 char szParameter[MAX_PARAM_NAME];
367 DWORD i, dwErrorCode;
368 NETXMS_VALUES_LIST value;
369
370 pRequest->GetVariableStr(VID_PARAMETER, szParameter, MAX_PARAM_NAME);
371 value.dwNumStrings = 0;
372 value.ppStringList = NULL;
373
374 dwErrorCode = GetEnumValue(szParameter, &value);
375 pMsg->SetVariable(VID_RCC, dwErrorCode);
376 if (dwErrorCode == ERR_SUCCESS)
377 {
378 pMsg->SetVariable(VID_NUM_STRINGS, value.dwNumStrings);
379 for(i = 0; i < value.dwNumStrings; i++)
380 pMsg->SetVariable(VID_ENUM_VALUE_BASE + i, value.ppStringList[i]);
381 }
382
383 for(i = 0; i < value.dwNumStrings; i++)
9d72bde1
VK
384 safe_free(value.ppStringList[i]);
385 safe_free(value.ppStringList);
901c96c7 386}
3c774461
VK
387
388
389//
390// Perform action on request
391//
392
393void CommSession::Action(CSCPMessage *pRequest, CSCPMessage *pMsg)
394{
395 char szAction[MAX_PARAM_NAME];
396 NETXMS_VALUES_LIST args;
397 DWORD i, dwRetCode;
398
399 // Get action name and arguments
400 pRequest->GetVariableStr(VID_ACTION_NAME, szAction, MAX_PARAM_NAME);
401 args.dwNumStrings = pRequest->GetVariableLong(VID_NUM_ARGS);
9d72bde1 402 args.ppStringList = (char **)malloc(sizeof(char *) * args.dwNumStrings);
3c774461
VK
403 for(i = 0; i < args.dwNumStrings; i++)
404 args.ppStringList[i] = pRequest->GetVariableStr(VID_ACTION_ARG_BASE + i);
405
406 // Execute action
407 dwRetCode = ExecAction(szAction, &args);
408 pMsg->SetVariable(VID_RCC, dwRetCode);
409
410 // Cleanup
411 for(i = 0; i < args.dwNumStrings; i++)
9d72bde1
VK
412 safe_free(args.ppStringList[i]);
413 safe_free(args.ppStringList);
3c774461 414}