- Initial work on Web UI support
[public/netxms.git] / src / libnxcl / comm.cpp
CommitLineData
05d9bba3
VK
1/*
2** NetXMS - Network Management System
3** Client Library
4** Copyright (C) 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: comm.cpp
21**
22**/
23
24#include "libnxcl.h"
25
26
27//
55198bf4 28// Unique message ID
05d9bba3
VK
29//
30
55198bf4 31DWORD g_dwMsgId;
05d9bba3
VK
32
33
1dd9669b 34//
55198bf4 35// Static data
1dd9669b
VK
36//
37
55198bf4
VK
38static SOCKET m_hSocket = -1;
39static MsgWaitQueue m_msgWaitQueue;
edbaa8c0 40static DWORD m_dwReceiverBufferSize = 4194304; // 4MB
1dd9669b
VK
41
42
05d9bba3
VK
43//
44// Send raw message
45//
46
47static BOOL SendRawMsg(CSCP_MESSAGE *pMsg)
48{
74dccebe 49 return send(m_hSocket, (char *)pMsg, ntohl(pMsg->dwSize), 0) == (int)ntohl(pMsg->dwSize);
05d9bba3
VK
50}
51
52
53//
54// Send message
55//
56
57BOOL SendMsg(CSCPMessage *pMsg)
58{
59 CSCP_MESSAGE *pRawMsg;
60 BOOL bResult;
b40371d4 61 TCHAR szBuffer[128];
05d9bba3 62
b40371d4 63 DebugPrintf(_T("SendMsg(\"%s\"), id:%ld)"), CSCPMessageCodeName(pMsg->GetCode(), szBuffer), pMsg->GetId());
05d9bba3
VK
64 pRawMsg = pMsg->CreateMessage();
65 bResult = SendRawMsg(pRawMsg);
9d72bde1 66 free(pRawMsg);
05d9bba3
VK
67 return bResult;
68}
69
70
71//
72// Network receiver thread
73//
74
ccdbbb52 75static THREAD_RESULT THREAD_CALL NetReceiver(void *pArg)
05d9bba3
VK
76{
77 CSCPMessage *pMsg;
78 CSCP_MESSAGE *pRawMsg;
79 CSCP_BUFFER *pMsgBuffer;
80 int iErr;
d8d6e1a4 81 BOOL bMsgNotNeeded;
b40371d4 82 TCHAR szBuffer[128];
05d9bba3
VK
83
84 // Initialize raw message receiving function
9d72bde1 85 pMsgBuffer = (CSCP_BUFFER *)malloc(sizeof(CSCP_BUFFER));
edbaa8c0 86 RecvCSCPMessage(0, NULL, pMsgBuffer, 0);
05d9bba3
VK
87
88 // Allocate space for raw message
9d72bde1 89 pRawMsg = (CSCP_MESSAGE *)malloc(m_dwReceiverBufferSize);
05d9bba3
VK
90
91 // Message receiving loop
92 while(1)
93 {
94 // Receive raw message
edbaa8c0 95 if ((iErr = RecvCSCPMessage(m_hSocket, pRawMsg, pMsgBuffer, m_dwReceiverBufferSize)) <= 0)
05d9bba3
VK
96 break;
97
edbaa8c0
VK
98 // Check if we get too large message
99 if (iErr == 1)
100 {
b40371d4 101 DebugPrintf(_T("Received too large message %s (%ld bytes)"),
edbaa8c0
VK
102 CSCPMessageCodeName(ntohs(pRawMsg->wCode), szBuffer),
103 ntohl(pRawMsg->dwSize));
104 continue;
105 }
106
05d9bba3 107 // Check that actual received packet size is equal to encoded in packet
74dccebe 108 if ((int)ntohl(pRawMsg->dwSize) != iErr)
05d9bba3 109 {
b40371d4 110 DebugPrintf(_T("RecvMsg: Bad packet length [dwSize=%d ActualSize=%d]"), ntohl(pRawMsg->dwSize), iErr);
05d9bba3
VK
111 continue; // Bad packet, wait for next
112 }
113
114 // Create message object from raw message
f8e075ce
VK
115 if (IsBinaryMsg(pRawMsg))
116 {
117 // Convert numeric fields to host byte order
74dccebe
VK
118 pRawMsg->wCode = ntohs(pRawMsg->wCode);
119 pRawMsg->wFlags = ntohs(pRawMsg->wFlags);
120 pRawMsg->dwSize = ntohl(pRawMsg->dwSize);
f8e075ce 121 pRawMsg->dwId = ntohl(pRawMsg->dwId);
74dccebe 122 pRawMsg->dwNumVars = ntohl(pRawMsg->dwNumVars);
f8e075ce 123
b40371d4 124 DebugPrintf(_T("RecvRawMsg(\"%s\", id:%ld)"), CSCPMessageCodeName(pRawMsg->wCode, szBuffer), pRawMsg->dwId);
1dd9669b 125
f8e075ce
VK
126 // Process message
127 switch(pRawMsg->wCode)
128 {
129 case CMD_EVENT:
130 ProcessEvent(NULL, pRawMsg);
f8e075ce 131 break;
74dccebe 132 default: // Put unknown raw messages into the wait queue
edbaa8c0 133 m_msgWaitQueue.Put((CSCP_MESSAGE *)nx_memdup(pRawMsg, pRawMsg->dwSize));
f8e075ce
VK
134 break;
135 }
136 }
137 else
05d9bba3 138 {
f8e075ce 139 pMsg = new CSCPMessage(pRawMsg);
d8d6e1a4 140 bMsgNotNeeded = TRUE;
b40371d4 141 DebugPrintf(_T("RecvMsg(\"%s\", id:%ld)"), CSCPMessageCodeName(pMsg->GetCode(), szBuffer), pMsg->GetId());
f8e075ce
VK
142
143 // Process message
144 switch(pMsg->GetCode())
145 {
146 case CMD_KEEPALIVE: // Keepalive message, ignore it
f8e075ce
VK
147 break;
148 case CMD_OBJECT: // Object information
824615ef 149 case CMD_OBJECT_UPDATE:
f8e075ce
VK
150 case CMD_OBJECT_LIST_END:
151 ProcessObjectUpdate(pMsg);
f8e075ce
VK
152 break;
153 case CMD_EVENT_LIST_END:
154 ProcessEvent(pMsg, NULL);
f8e075ce 155 break;
f160ff09 156 case CMD_EVENT_DB_RECORD:
f160ff09
VK
157 ProcessEventDBRecord(pMsg);
158 break;
bb68f467
VK
159 case CMD_USER_DATA:
160 case CMD_GROUP_DATA:
161 case CMD_USER_DB_EOF:
162 ProcessUserDBRecord(pMsg);
163 break;
0779b7d3
VK
164 case CMD_USER_DB_UPDATE:
165 ProcessUserDBUpdate(pMsg);
166 break;
7ec6cca8
VK
167 case CMD_NODE_DCI:
168 case CMD_NODE_DCI_LIST_END:
169 ProcessDCI(pMsg);
170 break;
eb484131
VK
171 case CMD_ALARM_UPDATE:
172 ProcessAlarmUpdate(pMsg);
173 break;
5bfcf934
VK
174 case CMD_ACTION_DB_UPDATE:
175 ProcessActionUpdate(pMsg);
176 break;
3421c063
VK
177 case CMD_NOTIFY:
178 CallEventHandler(NXC_EVENT_NOTIFICATION,
4de204a3
VK
179 pMsg->GetVariableLong(VID_NOTIFICATION_CODE),
180 (void *)pMsg->GetVariableLong(VID_NOTIFICATION_DATA));
3421c063 181 break;
f8e075ce
VK
182 default:
183 m_msgWaitQueue.Put(pMsg);
d8d6e1a4 184 bMsgNotNeeded = FALSE;
f8e075ce
VK
185 break;
186 }
d8d6e1a4
VK
187 if (bMsgNotNeeded)
188 delete pMsg;
05d9bba3
VK
189 }
190 }
191
39d7a7ed 192 CompleteSync(RCC_COMM_FAILURE); // Abort active sync operation
b40371d4 193 DebugPrintf(_T("Network receiver thread stopped"));
05d9bba3 194 ChangeState(STATE_DISCONNECTED);
9d72bde1
VK
195 free(pRawMsg);
196 free(pMsgBuffer);
8f7e2482
VK
197
198 // Close socket
199 shutdown(m_hSocket, SHUT_WR);
b40371d4
AK
200 {
201 char cTmp;
202 while(recv(m_hSocket, &cTmp, 1, 0) > 0);
203 }
8f7e2482
VK
204 shutdown(m_hSocket, SHUT_RD);
205 closesocket(m_hSocket);
ccdbbb52 206 return THREAD_OK;
05d9bba3
VK
207}
208
209
210//
211// Connect to server
212//
213
b40371d4 214DWORD LIBNXCL_EXPORTABLE NXCConnect(TCHAR *szServer, TCHAR *szLogin, TCHAR *szPassword)
05d9bba3
VK
215{
216 struct sockaddr_in servAddr;
217 CSCPMessage msg, *pResp;
99eee1db 218 BYTE szPasswordHash[SHA1_DIGEST_SIZE];
3a32dee8 219 DWORD dwRetCode = RCC_COMM_FAILURE;
b40371d4
AK
220 char *pServer;
221#ifdef UNICODE
222 char szMHost[64];
223
224
225 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
226 szServer, -1, szMHost, sizeof(szMHost), NULL, NULL);
227 pServer = szMHost;
228#else
229 pServer = szServer;
230#endif
05d9bba3 231
55198bf4 232 if (g_dwState == STATE_DISCONNECTED)
05d9bba3 233 {
55198bf4 234 ChangeState(STATE_CONNECTING);
05d9bba3 235
55198bf4
VK
236 // Reset unique message ID
237 g_dwMsgId = 1;
05d9bba3 238
55198bf4
VK
239 // Prepare address structure
240 memset(&servAddr, 0, sizeof(struct sockaddr_in));
241 servAddr.sin_family = AF_INET;
242 servAddr.sin_port = htons((WORD)SERVER_LISTEN_PORT);
b40371d4
AK
243
244 servAddr.sin_addr.s_addr = inet_addr(pServer);
245
55198bf4
VK
246 if (servAddr.sin_addr.s_addr == INADDR_NONE)
247 {
248 struct hostent *hs;
05d9bba3 249
b40371d4 250 hs = gethostbyname(pServer);
55198bf4
VK
251 if (hs != NULL)
252 memcpy(&servAddr.sin_addr, hs->h_addr, hs->h_length);
253 }
05d9bba3 254
55198bf4
VK
255 if (servAddr.sin_addr.s_addr != INADDR_NONE)
256 {
257 // Create socket
258 if ((m_hSocket = socket(AF_INET, SOCK_STREAM, 0)) != -1)
259 {
260 // Connect to target
261 if (connect(m_hSocket, (struct sockaddr *)&servAddr, sizeof(struct sockaddr_in)) == 0)
262 {
263 // Start receiver thread
264 ThreadCreate(NetReceiver, 0, NULL);
55198bf4 265
693119d6
VK
266 // Query server information
267 msg.SetId(g_dwMsgId++);
268 msg.SetCode(CMD_GET_SERVER_INFO);
55198bf4
VK
269 if (SendMsg(&msg))
270 {
271 // Receive responce message
693119d6 272 pResp = m_msgWaitQueue.WaitForMessage(CMD_REQUEST_COMPLETED, msg.GetId(), g_dwCommandTimeout);
3a32dee8 273 if (pResp != NULL)
55198bf4 274 {
3a32dee8 275 dwRetCode = pResp->GetVariableLong(VID_RCC);
693119d6
VK
276 if (dwRetCode == RCC_SUCCESS)
277 {
278 TCHAR szServerVersion[64];
279
280 pResp->GetVariableStr(VID_SERVER_VERSION, szServerVersion, 64);
281 if (_tcsncmp(szServerVersion, NETXMS_VERSION_STRING, 64))
282 dwRetCode = RCC_VERSION_MISMATCH;
283 }
55198bf4 284 delete pResp;
693119d6
VK
285
286 if (dwRetCode == RCC_SUCCESS)
287 {
288 // Prepare login message
289 msg.DeleteAllVariables();
290 msg.SetId(g_dwMsgId++);
291 msg.SetCode(CMD_LOGIN);
292 msg.SetVariable(VID_LOGIN_NAME, szLogin);
293 CalculateSHA1Hash((BYTE *)szPassword, _tcslen(szPassword), szPasswordHash);
294 msg.SetVariable(VID_PASSWORD, szPasswordHash, SHA1_DIGEST_SIZE);
295
296 if (SendMsg(&msg))
297 {
298 // Receive responce message
299 pResp = m_msgWaitQueue.WaitForMessage(CMD_LOGIN_RESP, msg.GetId(), g_dwCommandTimeout);
300 if (pResp != NULL)
301 {
302 dwRetCode = pResp->GetVariableLong(VID_RCC);
303 delete pResp;
304 }
305 else
306 {
307 // Connection is broken or timed out
308 dwRetCode = RCC_TIMEOUT;
309 }
310 }
311 }
55198bf4 312 }
3a32dee8
VK
313 else
314 {
315 // Connection is broken or timed out
316 dwRetCode = RCC_TIMEOUT;
317 }
55198bf4
VK
318 }
319
3a32dee8
VK
320 if (dwRetCode != RCC_SUCCESS)
321 shutdown(m_hSocket, 2);
322 }
323
324 if (dwRetCode != RCC_SUCCESS)
325 {
326 closesocket(m_hSocket);
327 m_hSocket = -1;
55198bf4 328 }
55198bf4
VK
329 }
330 }
3a32dee8 331 CallEventHandler(NXC_EVENT_LOGIN_RESULT, dwRetCode, NULL);
39d7a7ed 332 ChangeState(dwRetCode == RCC_SUCCESS ? STATE_CONNECTED : STATE_DISCONNECTED);
3a32dee8
VK
333 }
334 else
335 {
336 dwRetCode = RCC_OUT_OF_STATE_REQUEST;
05d9bba3
VK
337 }
338
3a32dee8 339 return dwRetCode;
05d9bba3
VK
340}
341
342
05d9bba3
VK
343//
344// Disconnect from server
345//
346
8aeffdee 347void LIBNXCL_EXPORTABLE NXCDisconnect(void)
05d9bba3
VK
348{
349 // Close socket
8f7e2482 350 shutdown(m_hSocket, SHUT_RDWR);
05d9bba3
VK
351 closesocket(m_hSocket);
352
353 // Clear message wait queue
354 m_msgWaitQueue.Clear();
22c38feb
VK
355
356 // Cleanup
357 DestroyAllObjects();
05d9bba3 358}
d8d6e1a4
VK
359
360
361//
362// Wait for specific message
363//
364
74dccebe 365CSCPMessage *WaitForMessage(WORD wCode, DWORD dwId, DWORD dwTimeOut)
d8d6e1a4 366{
74dccebe
VK
367 return m_msgWaitQueue.WaitForMessage(wCode, dwId, dwTimeOut);
368}
369
370
371//
372// Wait for specific raw message
373//
374
375CSCP_MESSAGE *WaitForRawMessage(WORD wCode, DWORD dwId, DWORD dwTimeOut)
376{
377 return m_msgWaitQueue.WaitForRawMessage(wCode, dwId, dwTimeOut);
d8d6e1a4 378}
9ed4eaff
VK
379
380
381//
382// Wait for request completion notification and extract result code
383// from recived message
384//
385
386DWORD WaitForRCC(DWORD dwRqId)
387{
388 CSCPMessage *pResponce;
389 DWORD dwRetCode;
390
dbe67493 391 pResponce = WaitForMessage(CMD_REQUEST_COMPLETED, dwRqId, g_dwCommandTimeout);
9ed4eaff
VK
392 if (pResponce != NULL)
393 {
394 dwRetCode = pResponce->GetVariableLong(VID_RCC);
395 delete pResponce;
396 }
397 else
398 {
399 dwRetCode = RCC_TIMEOUT;
400 }
401 return dwRetCode;
22c38feb 402}