condTimedWait fixed
[public/netxms.git] / src / libnxcl / comm.cpp
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 // for TCP_NODELAY
27 //#include "netinet/tcp.h"
28
29
30 //
31 // Network receiver thread
32 //
33
34 THREAD_RESULT THREAD_CALL NetReceiver(NXCL_Session *pSession)
35 {
36 CSCPMessage *pMsg;
37 CSCP_MESSAGE *pRawMsg;
38 CSCP_BUFFER *pMsgBuffer;
39 int iErr;
40 BOOL bMsgNotNeeded;
41 TCHAR szBuffer[128];
42
43 // Initialize raw message receiving function
44 pMsgBuffer = (CSCP_BUFFER *)malloc(sizeof(CSCP_BUFFER));
45 RecvCSCPMessage(0, NULL, pMsgBuffer, 0);
46
47 // Allocate space for raw message
48 pRawMsg = (CSCP_MESSAGE *)malloc(pSession->m_dwReceiverBufferSize);
49
50 // Message receiving loop
51 while(1)
52 {
53 // Receive raw message
54 if ((iErr = RecvCSCPMessage(pSession->m_hSocket, pRawMsg,
55 pMsgBuffer, pSession->m_dwReceiverBufferSize)) <= 0)
56 break;
57
58 // Check if we get too large message
59 if (iErr == 1)
60 {
61 DebugPrintf(_T("Received too large message %s (%ld bytes)"),
62 CSCPMessageCodeName(ntohs(pRawMsg->wCode), szBuffer),
63 ntohl(pRawMsg->dwSize));
64 continue;
65 }
66
67 // Check that actual received packet size is equal to encoded in packet
68 if ((int)ntohl(pRawMsg->dwSize) != iErr)
69 {
70 DebugPrintf(_T("RecvMsg: Bad packet length [dwSize=%d ActualSize=%d]"), ntohl(pRawMsg->dwSize), iErr);
71 continue; // Bad packet, wait for next
72 }
73
74 // Create message object from raw message
75 if (IsBinaryMsg(pRawMsg))
76 {
77 // Convert numeric fields to host byte order
78 pRawMsg->wCode = ntohs(pRawMsg->wCode);
79 pRawMsg->wFlags = ntohs(pRawMsg->wFlags);
80 pRawMsg->dwSize = ntohl(pRawMsg->dwSize);
81 pRawMsg->dwId = ntohl(pRawMsg->dwId);
82 pRawMsg->dwNumVars = ntohl(pRawMsg->dwNumVars);
83
84 DebugPrintf(_T("RecvRawMsg(\"%s\", id:%ld)"), CSCPMessageCodeName(pRawMsg->wCode, szBuffer), pRawMsg->dwId);
85
86 // Process message
87 switch(pRawMsg->wCode)
88 {
89 case CMD_EVENT:
90 ProcessEvent(pSession, NULL, pRawMsg);
91 break;
92 default: // Put unknown raw messages into the wait queue
93 pSession->m_msgWaitQueue.Put((CSCP_MESSAGE *)nx_memdup(pRawMsg, pRawMsg->dwSize));
94 break;
95 }
96 }
97 else
98 {
99 pMsg = new CSCPMessage(pRawMsg);
100 bMsgNotNeeded = TRUE;
101 DebugPrintf(_T("RecvMsg(\"%s\", id:%ld)"), CSCPMessageCodeName(pMsg->GetCode(), szBuffer), pMsg->GetId());
102
103 // Process message
104 switch(pMsg->GetCode())
105 {
106 case CMD_KEEPALIVE: // Keepalive message, ignore it
107 break;
108 case CMD_OBJECT: // Object information
109 case CMD_OBJECT_UPDATE:
110 case CMD_OBJECT_LIST_END:
111 pSession->ProcessObjectUpdate(pMsg);
112 break;
113 case CMD_EVENT_LIST_END:
114 ProcessEvent(pSession, pMsg, NULL);
115 break;
116 case CMD_EVENT_DB_RECORD:
117 ProcessEventDBRecord(pSession, pMsg);
118 break;
119 case CMD_USER_DATA:
120 case CMD_GROUP_DATA:
121 case CMD_USER_DB_EOF:
122 pSession->ProcessUserDBRecord(pMsg);
123 break;
124 case CMD_USER_DB_UPDATE:
125 pSession->ProcessUserDBUpdate(pMsg);
126 break;
127 case CMD_NODE_DCI:
128 case CMD_NODE_DCI_LIST_END:
129 pSession->ProcessDCI(pMsg);
130 break;
131 case CMD_ALARM_UPDATE:
132 ProcessAlarmUpdate(pSession, pMsg);
133 break;
134 case CMD_ACTION_DB_UPDATE:
135 ProcessActionUpdate(pSession, pMsg);
136 break;
137 case CMD_NOTIFY:
138 pSession->CallEventHandler(NXC_EVENT_NOTIFICATION,
139 pMsg->GetVariableLong(VID_NOTIFICATION_CODE),
140 (void *)pMsg->GetVariableLong(VID_NOTIFICATION_DATA));
141 break;
142 default:
143 pSession->m_msgWaitQueue.Put(pMsg);
144 bMsgNotNeeded = FALSE;
145 break;
146 }
147 if (bMsgNotNeeded)
148 delete pMsg;
149 }
150 }
151
152 pSession->CompleteSync(RCC_COMM_FAILURE); // Abort active sync operation
153 DebugPrintf(_T("Network receiver thread stopped"));
154 free(pRawMsg);
155 free(pMsgBuffer);
156
157 // Close socket
158 shutdown(pSession->m_hSocket, SHUT_WR);
159 {
160 char cTmp;
161 while(recv(pSession->m_hSocket, &cTmp, 1, 0) > 0);
162 }
163 shutdown(pSession->m_hSocket, SHUT_RD);
164 closesocket(pSession->m_hSocket);
165 return THREAD_OK;
166 }
167
168
169 //
170 // Connect to server
171 //
172
173 DWORD LIBNXCL_EXPORTABLE NXCConnect(TCHAR *pszServer, TCHAR *pszLogin,
174 TCHAR *pszPassword, NXC_SESSION *phSession)
175 {
176 struct sockaddr_in servAddr;
177 CSCPMessage msg, *pResp;
178 BYTE szPasswordHash[SHA1_DIGEST_SIZE];
179 DWORD dwRetCode = RCC_COMM_FAILURE;
180 SOCKET hSocket;
181 THREAD hThread;
182 char *pServer;
183 #ifdef UNICODE
184 char szMHost[64];
185
186 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
187 pszServer, -1, szMHost, sizeof(szMHost), NULL, NULL);
188 pServer = szMHost;
189 #else
190 pServer = pszServer;
191 #endif
192
193 // Prepare address structure
194 memset(&servAddr, 0, sizeof(struct sockaddr_in));
195 servAddr.sin_family = AF_INET;
196 servAddr.sin_port = htons((WORD)SERVER_LISTEN_PORT);
197
198 servAddr.sin_addr.s_addr = inet_addr(pServer);
199
200 if (servAddr.sin_addr.s_addr == INADDR_NONE)
201 {
202 struct hostent *hs;
203
204 hs = gethostbyname(pServer);
205 if (hs != NULL)
206 memcpy(&servAddr.sin_addr, hs->h_addr, hs->h_length);
207 }
208
209 if (servAddr.sin_addr.s_addr != INADDR_NONE)
210 {
211 // Create socket
212 if ((hSocket = socket(AF_INET, SOCK_STREAM, 0)) != -1)
213 {
214 // enable TCP_NODELAY
215 //int nVal = 1;
216 //setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, &nVal, sizeof(nVal));
217
218 // Connect to target
219 if (connect(hSocket, (struct sockaddr *)&servAddr, sizeof(struct sockaddr_in)) == 0)
220 {
221 NXCL_Session *pSession;
222
223 // Create new session and start receiver thread
224 pSession = new NXCL_Session;
225 pSession->Attach(hSocket);
226 hThread = ThreadCreateEx((THREAD_RESULT (THREAD_CALL *)(void *))NetReceiver, 0, pSession);
227 if (hThread != INVALID_THREAD_HANDLE)
228 pSession->SetRecvThread(hThread);
229
230 // Query server information
231 msg.SetId(pSession->CreateRqId());
232 msg.SetCode(CMD_GET_SERVER_INFO);
233 if (pSession->SendMsg(&msg))
234 {
235 // Receive responce message
236 pResp = pSession->WaitForMessage(CMD_REQUEST_COMPLETED, msg.GetId());
237 if (pResp != NULL)
238 {
239 dwRetCode = pResp->GetVariableLong(VID_RCC);
240 if (dwRetCode == RCC_SUCCESS)
241 {
242 TCHAR szServerVersion[64];
243
244 pResp->GetVariableStr(VID_SERVER_VERSION, szServerVersion, 64);
245 if (_tcsncmp(szServerVersion, NETXMS_VERSION_STRING, 64))
246 dwRetCode = RCC_VERSION_MISMATCH;
247 }
248 delete pResp;
249
250 if (dwRetCode == RCC_SUCCESS)
251 {
252 // Do login if we are requested to do so
253 // Login is not needed for web sessions
254 if (pszLogin != NULL)
255 {
256 // Prepare login message
257 msg.DeleteAllVariables();
258 msg.SetId(pSession->CreateRqId());
259 msg.SetCode(CMD_LOGIN);
260 msg.SetVariable(VID_LOGIN_NAME, pszLogin);
261 CalculateSHA1Hash((BYTE *)pszPassword, _tcslen(pszPassword), szPasswordHash);
262 msg.SetVariable(VID_PASSWORD, szPasswordHash, SHA1_DIGEST_SIZE);
263
264 if (pSession->SendMsg(&msg))
265 {
266 // Receive responce message
267 pResp = pSession->WaitForMessage(CMD_LOGIN_RESP, msg.GetId());
268 if (pResp != NULL)
269 {
270 dwRetCode = pResp->GetVariableLong(VID_RCC);
271 delete pResp;
272 }
273 else
274 {
275 // Connection is broken or timed out
276 dwRetCode = RCC_TIMEOUT;
277 }
278 }
279 }
280 }
281 }
282 else
283 {
284 // Connection is broken or timed out
285 dwRetCode = RCC_TIMEOUT;
286 }
287 }
288
289 if (dwRetCode == RCC_SUCCESS)
290 {
291 *phSession = pSession;
292 }
293 else
294 {
295 delete pSession;
296 }
297 }
298 else // connect() failed
299 {
300 closesocket(hSocket);
301 }
302 }
303 }
304
305 if (dwRetCode != RCC_SUCCESS)
306 *phSession = NULL;
307
308 return dwRetCode;
309 }
310
311
312 //
313 // Disconnect from server
314 //
315
316 void LIBNXCL_EXPORTABLE NXCDisconnect(NXC_SESSION hSession)
317 {
318 if (hSession != NULL)
319 {
320 // ((NXCL_Session *)hSession)->Disconnect();
321 delete ((NXCL_Session *)hSession);
322 }
323 }