]> git.stg.codes - stg.git/blob - stglibs/srvconf.lib/netunit.cpp
Minor fix.
[stg.git] / stglibs / srvconf.lib / netunit.cpp
1 /*
2  *    This program is free software; you can redistribute it and/or modify
3  *    it under the terms of the GNU General Public License as published by
4  *    the Free Software Foundation; either version 2 of the License, or
5  *    (at your option) any later version.
6  *
7  *    This program is distributed in the hope that it will be useful,
8  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *    GNU General Public License for more details.
11  *
12  *    You should have received a copy of the GNU General Public License
13  *    along with this program; if not, write to the Free Software
14  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
15  */
16
17 /*
18  *    Author : Boris Mikhailenko <stg34@stargazer.dp.ua>
19  */
20
21 #include "netunit.h"
22
23 #include "stg/servconf_types.h"
24 #include "stg/common.h"
25 #include "stg/blowfish.h"
26
27 #include <algorithm> // std::min
28
29 #include <cstdio>
30 #include <cerrno>
31 #include <cstring>
32
33 #include <netdb.h>
34 #include <arpa/inet.h>
35 #include <unistd.h>
36
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40
41 using namespace STG;
42
43 namespace
44 {
45
46 const std::string::size_type MAX_XML_CHUNK_LENGTH = 2048;
47
48 }
49
50 //---------------------------------------------------------------------------
51
52 #define SEND_DATA_ERROR             "Send data error!"
53 #define RECV_DATA_ANSWER_ERROR      "Recv data answer error!"
54 #define UNKNOWN_ERROR               "Unknown error!"
55 #define CONNECT_FAILED              "Connect failed!"
56 #define BIND_FAILED                 "Bind failed!"
57 #define INCORRECT_LOGIN             "Incorrect login!"
58 #define INCORRECT_HEADER            "Incorrect header!"
59 #define SEND_LOGIN_ERROR            "Send login error!"
60 #define RECV_LOGIN_ANSWER_ERROR     "Recv login answer error!"
61 #define CREATE_SOCKET_ERROR         "Create socket failed!"
62 #define WSASTARTUP_FAILED           "WSAStartup failed!"
63 #define SEND_HEADER_ERROR           "Send header error!"
64 #define RECV_HEADER_ANSWER_ERROR    "Recv header answer error!"
65
66 //---------------------------------------------------------------------------
67 NETTRANSACT::NETTRANSACT(const std::string & s, uint16_t p,
68                          const std::string & l, const std::string & pwd)
69     : server(s),
70       port(p),
71       login(l),
72       password(pwd),
73       outerSocket(-1)
74 {
75 }
76 //---------------------------------------------------------------------------
77 NETTRANSACT::NETTRANSACT(const std::string & s, uint16_t p,
78                          const std::string & la, uint16_t lp,
79                          const std::string & l, const std::string & pwd)
80     : server(s),
81       port(p),
82       localAddress(la),
83       localPort(lp),
84       login(l),
85       password(pwd),
86       outerSocket(-1)
87 {
88 }
89 //---------------------------------------------------------------------------
90 int NETTRANSACT::Connect()
91 {
92 outerSocket = socket(PF_INET, SOCK_STREAM, 0);
93 if (outerSocket < 0)
94     {
95     errorMsg = CREATE_SOCKET_ERROR;
96     return st_conn_fail;
97     }
98
99 if (!localAddress.empty())
100     {
101     if (localPort == 0)
102         localPort = port;
103
104     unsigned long ip = inet_addr(localAddress.c_str());
105
106     if (ip == INADDR_NONE)
107         {
108         struct hostent * phe = gethostbyname(localAddress.c_str());
109         if (phe == NULL)
110             {
111             errorMsg = "DNS error.\nCan not reslove " + localAddress;
112             return st_dns_err;
113             }
114
115         struct hostent he;
116         memcpy(&he, phe, sizeof(he));
117         ip = *((long *)he.h_addr_list[0]);
118         }
119
120     struct sockaddr_in localAddr;
121     memset(&localAddr, 0, sizeof(localAddr));
122     localAddr.sin_family = AF_INET;
123     localAddr.sin_port = htons(localPort);
124     localAddr.sin_addr.s_addr = ip;
125
126     if (bind(outerSocket, (struct sockaddr *)&localAddr, sizeof(localAddr)) < 0)
127         {
128         errorMsg = BIND_FAILED;
129         close(outerSocket);
130         return st_conn_fail;
131         }
132     }
133
134 struct sockaddr_in outerAddr;
135 memset(&outerAddr, 0, sizeof(outerAddr));
136
137 unsigned long ip = inet_addr(server.c_str());
138
139 if (ip == INADDR_NONE)
140     {
141     struct hostent * phe = gethostbyname(server.c_str());
142     if (phe == NULL)
143         {
144         errorMsg = "DNS error.\nCan not reslove " + server;
145         return st_dns_err;
146         }
147
148     struct hostent he;
149     memcpy(&he, phe, sizeof(he));
150     ip = *((long *)he.h_addr_list[0]);
151     }
152
153 outerAddr.sin_family = AF_INET;
154 outerAddr.sin_port = htons(port);
155 outerAddr.sin_addr.s_addr = ip;
156
157 if (connect(outerSocket, (struct sockaddr *)&outerAddr, sizeof(outerAddr)) < 0)
158     {
159     errorMsg = CONNECT_FAILED;
160     close(outerSocket);
161     return st_conn_fail;
162     }
163
164 return st_ok;
165 }
166 //---------------------------------------------------------------------------
167 void NETTRANSACT::Disconnect()
168 {
169 close(outerSocket);
170 }
171 //---------------------------------------------------------------------------
172 int NETTRANSACT::Transact(const std::string & request, CALLBACK callback, void * data)
173 {
174 int ret;
175 if ((ret = TxHeader()) != st_ok)
176     {
177     Disconnect();
178     return ret;
179     }
180
181 if ((ret = RxHeaderAnswer()) != st_ok)
182     {
183     Disconnect();
184     return ret;
185     }
186
187 if ((ret = TxLogin()) != st_ok)
188     {
189     Disconnect();
190     return ret;
191     }
192
193 if ((ret = RxLoginAnswer()) != st_ok)
194     {
195     Disconnect();
196     return ret;
197     }
198
199 if ((ret = TxLoginS()) != st_ok)
200     {
201     Disconnect();
202     return ret;
203     }
204
205 if ((ret = RxLoginSAnswer()) != st_ok)
206     {
207     Disconnect();
208     return ret;
209     }
210
211 if ((ret = TxData(request)) != st_ok)
212     {
213     Disconnect();
214     return ret;
215     }
216
217 if ((ret = RxDataAnswer(callback, data)) != st_ok)
218     {
219     Disconnect();
220     return ret;
221     }
222
223 return st_ok;
224 }
225 //---------------------------------------------------------------------------
226 int NETTRANSACT::TxHeader()
227 {
228 if (send(outerSocket, STG_HEADER, strlen(STG_HEADER), 0) <= 0)
229     {
230     errorMsg = SEND_HEADER_ERROR;
231     return st_send_fail;
232     }
233
234 return st_ok;
235 }
236 //---------------------------------------------------------------------------
237 int NETTRANSACT::RxHeaderAnswer()
238 {
239 char buffer[sizeof(STG_HEADER) + 1];
240
241 if (recv(outerSocket, buffer, strlen(OK_HEADER), 0) <= 0)
242     {
243     printf("Receive header answer error: '%s'\n", strerror(errno));
244     errorMsg = RECV_HEADER_ANSWER_ERROR;
245     return st_recv_fail;
246     }
247
248 if (strncmp(OK_HEADER, buffer, strlen(OK_HEADER)) == 0)
249     {
250     return st_ok;
251     }
252 else
253     {
254     if (strncmp(ERR_HEADER, buffer, strlen(ERR_HEADER)) == 0)
255         {
256         errorMsg = INCORRECT_HEADER;
257         return st_header_err;
258         }
259     else
260         {
261         errorMsg = UNKNOWN_ERROR;
262         return st_unknown_err;
263         }
264     }
265 }
266 //---------------------------------------------------------------------------
267 int NETTRANSACT::TxLogin()
268 {
269 char loginZ[ADM_LOGIN_LEN];
270 memset(loginZ, 0, ADM_LOGIN_LEN);
271 strncpy(loginZ, login.c_str(), ADM_LOGIN_LEN);
272
273 if (send(outerSocket, loginZ, ADM_LOGIN_LEN, 0) <= 0)
274     {
275     errorMsg = SEND_LOGIN_ERROR;
276     return st_send_fail;
277     }
278
279 return st_ok;
280 }
281 //---------------------------------------------------------------------------
282 int NETTRANSACT::RxLoginAnswer()
283 {
284 char buffer[sizeof(OK_LOGIN) + 1];
285
286 if (recv(outerSocket, buffer, strlen(OK_LOGIN), 0) <= 0)
287     {
288     printf("Receive login answer error: '%s'\n", strerror(errno));
289     errorMsg = RECV_LOGIN_ANSWER_ERROR;
290     return st_recv_fail;
291     }
292
293 if (strncmp(OK_LOGIN, buffer, strlen(OK_LOGIN)) == 0)
294     {
295     return st_ok;
296     }
297 else
298     {
299     if (strncmp(ERR_LOGIN, buffer, strlen(ERR_LOGIN)) == 0)
300         {
301         errorMsg = INCORRECT_LOGIN;
302         return st_login_err;
303         }
304     else
305         {
306         errorMsg = UNKNOWN_ERROR;
307         return st_unknown_err;
308         }
309     }
310 }
311 //---------------------------------------------------------------------------
312 int NETTRANSACT::TxLoginS()
313 {
314 char loginZ[ADM_LOGIN_LEN];
315 memset(loginZ, 0, ADM_LOGIN_LEN);
316 strncpy(loginZ, login.c_str(), ADM_LOGIN_LEN);
317
318 BLOWFISH_CTX ctx;
319 EnDecodeInit(password.c_str(), PASSWD_LEN, &ctx);
320
321 for (int j = 0; j < ADM_LOGIN_LEN / ENC_MSG_LEN; j++)
322     {
323     char ct[ENC_MSG_LEN];
324     EncodeString(ct, loginZ + j * ENC_MSG_LEN, &ctx);
325     if (send(outerSocket, ct, ENC_MSG_LEN, 0) <= 0)
326         {
327         errorMsg = SEND_LOGIN_ERROR;
328         return st_send_fail;
329         }
330     }
331
332 return st_ok;
333 }
334 //---------------------------------------------------------------------------
335 int NETTRANSACT::RxLoginSAnswer()
336 {
337 char buffer[sizeof(OK_LOGINS) + 1];
338
339 if (recv(outerSocket, buffer, strlen(OK_LOGINS), 0) <= 0)
340     {
341     printf("Receive secret login answer error: '%s'\n", strerror(errno));
342     errorMsg = RECV_LOGIN_ANSWER_ERROR;
343     return st_recv_fail;
344     }
345
346 if (strncmp(OK_LOGINS, buffer, strlen(OK_LOGINS)) == 0)
347     {
348     return st_ok;
349     }
350 else
351     {
352     if (strncmp(ERR_LOGINS, buffer, strlen(ERR_LOGINS)) == 0)
353         {
354         errorMsg = INCORRECT_LOGIN;
355         return st_logins_err;
356         }
357     else
358         {
359         errorMsg = UNKNOWN_ERROR;
360         return st_unknown_err;
361         }
362     }
363 }
364 //---------------------------------------------------------------------------
365 int NETTRANSACT::TxData(const std::string & text)
366 {
367 BLOWFISH_CTX ctx;
368 EnDecodeInit(password.c_str(), PASSWD_LEN, &ctx);
369
370 size_t pos = 0;
371 while (pos < text.size())
372     {
373     char textZ[ENC_MSG_LEN];
374     if (text.size() - pos < ENC_MSG_LEN)
375         memset(textZ, 0, ENC_MSG_LEN);
376     strncpy(textZ, text.c_str() + pos, std::min(ENC_MSG_LEN, (int)(text.size() - pos)));
377     char ct[ENC_MSG_LEN];
378     EncodeString(ct, textZ, &ctx);
379     if (send(outerSocket, ct, ENC_MSG_LEN, 0) <= 0)
380         {
381         errorMsg = SEND_DATA_ERROR;
382         return st_send_fail;
383         }
384     pos += ENC_MSG_LEN;
385     }
386
387 return st_ok;
388 }
389 //---------------------------------------------------------------------------
390 int NETTRANSACT::RxDataAnswer(CALLBACK callback, void * data)
391 {
392 BLOWFISH_CTX ctx;
393 EnDecodeInit(password.c_str(), PASSWD_LEN, &ctx);
394
395 std::string chunk;
396 while (true)
397     {
398     char bufferS[ENC_MSG_LEN];
399     size_t toRead = ENC_MSG_LEN;
400     while (toRead > 0)
401         {
402         int ret = recv(outerSocket, &bufferS[ENC_MSG_LEN - toRead], toRead, 0);
403         if (ret <= 0)
404             {
405             printf("Receive data error: '%s'\n", strerror(errno));
406             close(outerSocket);
407             errorMsg = RECV_DATA_ANSWER_ERROR;
408             return st_recv_fail;
409             }
410         toRead -= ret;
411         }
412
413     char buffer[ENC_MSG_LEN];
414     DecodeString(buffer, bufferS, &ctx);
415
416     bool final = false;
417     size_t pos = 0;
418     for (; pos < ENC_MSG_LEN && buffer[pos] != 0; pos++) ;
419     if (pos < ENC_MSG_LEN && buffer[pos] == 0)
420         final = true;
421
422     if (pos > 0)
423         chunk.append(&buffer[0], &buffer[pos]);
424
425     if (chunk.length() > MAX_XML_CHUNK_LENGTH || final)
426         {
427         if (callback)
428             if (!callback(chunk, final, data))
429                 return st_xml_parse_error;
430         chunk.clear();
431         }
432
433     if (final)
434         return st_ok;
435     }
436 }