f5ed3920092af8ced945c941318982b53e3f6d8d
[public/netxms.git] / src / agent / core / session.cpp
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
26 //
27 // Externals
28 //
29
30 void UnregisterSession(DWORD dwIndex);
31
32
33 //
34 // Constants
35 //
36
37 #define RAW_MSG_SIZE 262144
38
39
40 //
41 // Client communication read thread
42 //
43
44 THREAD_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
61 THREAD_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
72 THREAD_RESULT THREAD_CALL CommSession::ProcessingThreadStarter(void *pArg)
73 {
74 ((CommSession *)pArg)->ProcessingThread();
75 return THREAD_OK;
76 }
77
78
79 //
80 // Client session class constructor
81 //
82
83 CommSession::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));
90 m_hWriteThread = INVALID_THREAD_HANDLE;
91 m_hProcessingThread = INVALID_THREAD_HANDLE;
92 m_dwHostAddr = dwHostAddr;
93 m_bIsAuthenticated = (g_dwFlags & AF_REQUIRE_AUTH) ? FALSE : TRUE;
94 }
95
96
97 //
98 // Destructor
99 //
100
101 CommSession::~CommSession()
102 {
103 shutdown(m_hSocket, 2);
104 closesocket(m_hSocket);
105 delete m_pSendQueue;
106 delete m_pMessageQueue;
107 safe_free(m_pMsgBuffer);
108 }
109
110
111 //
112 // Start all threads
113 //
114
115 void CommSession::Run(void)
116 {
117 m_hWriteThread = ThreadCreateEx(WriteThreadStarter, 0, this);
118 m_hProcessingThread = ThreadCreateEx(ProcessingThreadStarter, 0, this);
119 ThreadCreate(ReadThreadStarter, 0, this);
120 }
121
122
123 //
124 // Reading thread
125 //
126
127 void CommSession::ReadThread(void)
128 {
129 CSCP_MESSAGE *pRawMsg;
130 CSCPMessage *pMsg;
131 int iErr;
132 char szBuffer[32];
133 WORD wFlags;
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 {
151 DebugPrintf("Actual message size doesn't match wSize value (%d,%d)", iErr, ntohl(pRawMsg->dwSize));
152 continue; // Bad packet, wait for next
153 }
154
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 }
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
182 ThreadJoin(m_hWriteThread);
183 ThreadJoin(m_hProcessingThread);
184
185 DebugPrintf("Session with %s closed", IpToStr(m_dwHostAddr, szBuffer));
186 }
187
188
189 //
190 // Writing thread
191 //
192
193 void 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
204 DebugPrintf("Sending message %s", CSCPMessageCodeName(ntohs(pMsg->wCode), szBuffer));
205 if (send(m_hSocket, (const char *)pMsg, ntohl(pMsg->dwSize), 0) <= 0)
206 {
207 free(pMsg);
208 break;
209 }
210 free(pMsg);
211 }
212 }
213
214
215 //
216 // Message processing thread
217 //
218
219 void CommSession::ProcessingThread(void)
220 {
221 CSCPMessage *pMsg;
222 char szBuffer[128];
223 CSCPMessage msg;
224
225 while(1)
226 {
227 pMsg = (CSCPMessage *)m_pMessageQueue->GetOrBlock();
228 if (pMsg == INVALID_POINTER_VALUE) // Session termination indicator
229 break;
230 DebugPrintf("Received message %s", CSCPMessageCodeName(pMsg->GetCode(), szBuffer));
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:
252 GetList(pMsg, &msg);
253 break;
254 case CMD_KEEPALIVE:
255 msg.SetVariable(VID_RCC, ERR_SUCCESS);
256 break;
257 case CMD_ACTION:
258 Action(pMsg, &msg);
259 break;
260 default:
261 msg.SetVariable(VID_RCC, ERR_UNKNOWN_COMMAND);
262 break;
263 }
264 }
265 delete pMsg;
266
267 // Send responce
268 SendMessage(&msg);
269 msg.DeleteAllVariables();
270 }
271 }
272
273
274 //
275 // Authenticate peer
276 //
277
278 void 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];
288 BYTE hash[32];
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;
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;
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
347 void 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 }
358
359
360 //
361 // Get list of values
362 //
363
364 void 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++)
384 safe_free(value.ppStringList[i]);
385 safe_free(value.ppStringList);
386 }
387
388
389 //
390 // Perform action on request
391 //
392
393 void 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);
402 args.ppStringList = (char **)malloc(sizeof(char *) * args.dwNumStrings);
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++)
412 safe_free(args.ppStringList[i]);
413 safe_free(args.ppStringList);
414 }