]> git.stg.codes - stg.git/blob - stglibs/srvconf.lib/netunit.cpp
Added Makefile for tests for Darwin platform.
[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 #include "stg/bfstream.h"
27
28 #include <algorithm> // std::min
29
30 #include <cerrno>
31 #include <cstring>
32 #include <cassert>
33
34 #include <netdb.h>
35 #include <arpa/inet.h>
36 #include <unistd.h>
37
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41
42 using namespace STG;
43
44 namespace
45 {
46
47 struct ReadState
48 {
49     bool final;
50     NETTRANSACT::CALLBACK callback;
51     void * callbackData;
52     NETTRANSACT * nt;
53 };
54
55 }
56
57 //---------------------------------------------------------------------------
58
59 #define SEND_DATA_ERROR             "Send data error!"
60 #define RECV_DATA_ANSWER_ERROR      "Recv data answer error!"
61 #define UNKNOWN_ERROR               "Unknown error!"
62 #define CONNECT_FAILED              "Connect failed!"
63 #define BIND_FAILED                 "Bind failed!"
64 #define INCORRECT_LOGIN             "Incorrect login!"
65 #define INCORRECT_HEADER            "Incorrect header!"
66 #define SEND_LOGIN_ERROR            "Send login error!"
67 #define RECV_LOGIN_ANSWER_ERROR     "Recv login answer error!"
68 #define CREATE_SOCKET_ERROR         "Create socket failed!"
69 #define WSASTARTUP_FAILED           "WSAStartup failed!"
70 #define SEND_HEADER_ERROR           "Send header error!"
71 #define RECV_HEADER_ANSWER_ERROR    "Recv header answer error!"
72
73 //---------------------------------------------------------------------------
74 NETTRANSACT::NETTRANSACT(const std::string & s, uint16_t p,
75                          const std::string & l, const std::string & pwd)
76     : server(s),
77       port(p),
78       localPort(0),
79       login(l),
80       password(pwd),
81       sock(-1)
82 {
83 }
84 //---------------------------------------------------------------------------
85 NETTRANSACT::NETTRANSACT(const std::string & s, uint16_t p,
86                          const std::string & la, uint16_t lp,
87                          const std::string & l, const std::string & pwd)
88     : server(s),
89       port(p),
90       localAddress(la),
91       localPort(lp),
92       login(l),
93       password(pwd),
94       sock(-1)
95 {
96 }
97 //---------------------------------------------------------------------------
98 NETTRANSACT::~NETTRANSACT()
99 {
100 Disconnect();
101 }
102 //---------------------------------------------------------------------------
103 int NETTRANSACT::Connect()
104 {
105 sock = socket(PF_INET, SOCK_STREAM, 0);
106 if (sock < 0)
107     {
108     errorMsg = CREATE_SOCKET_ERROR;
109     return st_conn_fail;
110     }
111
112 if (!localAddress.empty())
113     {
114     if (localPort == 0)
115         localPort = port;
116
117     unsigned long ip = inet_addr(localAddress.c_str());
118
119     if (ip == INADDR_NONE)
120         {
121         struct hostent * phe = gethostbyname(localAddress.c_str());
122         if (phe == NULL)
123             {
124             errorMsg = "Can not reslove '" + localAddress + "'";
125             return st_dns_err;
126             }
127
128         struct hostent he;
129         memcpy(&he, phe, sizeof(he));
130         ip = *((long *)he.h_addr_list[0]);
131         }
132
133     struct sockaddr_in localAddr;
134     memset(&localAddr, 0, sizeof(localAddr));
135     localAddr.sin_family = AF_INET;
136     localAddr.sin_port = htons(localPort);
137     localAddr.sin_addr.s_addr = ip;
138
139     if (bind(sock, (struct sockaddr *)&localAddr, sizeof(localAddr)) < 0)
140         {
141         errorMsg = BIND_FAILED;
142         return st_conn_fail;
143         }
144     }
145
146 struct sockaddr_in outerAddr;
147 memset(&outerAddr, 0, sizeof(outerAddr));
148
149 unsigned long ip = inet_addr(server.c_str());
150
151 if (ip == INADDR_NONE)
152     {
153     struct hostent * phe = gethostbyname(server.c_str());
154     if (phe == NULL)
155         {
156         errorMsg = "Can not reslove '" + server + "'";
157         return st_dns_err;
158         }
159
160     struct hostent he;
161     memcpy(&he, phe, sizeof(he));
162     ip = *((long *)he.h_addr_list[0]);
163     }
164
165 outerAddr.sin_family = AF_INET;
166 outerAddr.sin_port = htons(port);
167 outerAddr.sin_addr.s_addr = ip;
168
169 if (connect(sock, (struct sockaddr *)&outerAddr, sizeof(outerAddr)) < 0)
170     {
171     errorMsg = CONNECT_FAILED;
172     return st_conn_fail;
173     }
174
175 return st_ok;
176 }
177 //---------------------------------------------------------------------------
178 void NETTRANSACT::Disconnect()
179 {
180 if (sock != -1)
181     {
182     shutdown(sock, SHUT_RDWR);
183     close(sock);
184     sock = -1;
185     }
186 }
187 //---------------------------------------------------------------------------
188 int NETTRANSACT::Transact(const std::string & request, CALLBACK callback, void * data)
189 {
190 int ret;
191 if ((ret = TxHeader()) != st_ok)
192     return ret;
193
194 if ((ret = RxHeaderAnswer()) != st_ok)
195     return ret;
196
197 if ((ret = TxLogin()) != st_ok)
198     return ret;
199
200 if ((ret = RxLoginAnswer()) != st_ok)
201     return ret;
202
203 if ((ret = TxLoginS()) != st_ok)
204     return ret;
205
206 if ((ret = RxLoginSAnswer()) != st_ok)
207     return ret;
208
209 if ((ret = TxData(request)) != st_ok)
210     return ret;
211
212 if ((ret = RxDataAnswer(callback, data)) != st_ok)
213     return ret;
214
215 return st_ok;
216 }
217 //---------------------------------------------------------------------------
218 int NETTRANSACT::TxHeader()
219 {
220 if (!WriteAll(sock, STG_HEADER, strlen(STG_HEADER)))
221     {
222     errorMsg = SEND_HEADER_ERROR;
223     return st_send_fail;
224     }
225
226 return st_ok;
227 }
228 //---------------------------------------------------------------------------
229 int NETTRANSACT::RxHeaderAnswer()
230 {
231 char buffer[sizeof(STG_HEADER) + 1];
232
233 if (!ReadAll(sock, buffer, strlen(OK_HEADER)))
234     {
235     errorMsg = RECV_HEADER_ANSWER_ERROR;
236     return st_recv_fail;
237     }
238
239 if (strncmp(OK_HEADER, buffer, strlen(OK_HEADER)) == 0)
240     return st_ok;
241
242 if (strncmp(ERR_HEADER, buffer, strlen(ERR_HEADER)) == 0)
243     {
244     errorMsg = INCORRECT_HEADER;
245     return st_header_err;
246     }
247 else
248     {
249     errorMsg = UNKNOWN_ERROR;
250     return st_unknown_err;
251     }
252 }
253 //---------------------------------------------------------------------------
254 int NETTRANSACT::TxLogin()
255 {
256 char loginZ[ADM_LOGIN_LEN + 1];
257 memset(loginZ, 0, ADM_LOGIN_LEN + 1);
258 strncpy(loginZ, login.c_str(), ADM_LOGIN_LEN);
259
260 if (!WriteAll(sock, loginZ, ADM_LOGIN_LEN))
261     {
262     errorMsg = SEND_LOGIN_ERROR;
263     return st_send_fail;
264     }
265
266 return st_ok;
267 }
268 //---------------------------------------------------------------------------
269 int NETTRANSACT::RxLoginAnswer()
270 {
271 char buffer[sizeof(OK_LOGIN) + 1];
272
273 if (!ReadAll(sock, buffer, strlen(OK_LOGIN)))
274     {
275     errorMsg = RECV_LOGIN_ANSWER_ERROR;
276     return st_recv_fail;
277     }
278
279 if (strncmp(OK_LOGIN, buffer, strlen(OK_LOGIN)) == 0)
280     return st_ok;
281
282 if (strncmp(ERR_LOGIN, buffer, strlen(ERR_LOGIN)) == 0)
283     {
284     errorMsg = INCORRECT_LOGIN;
285     return st_login_err;
286     }
287 else
288     {
289     errorMsg = UNKNOWN_ERROR;
290     return st_unknown_err;
291     }
292 }
293 //---------------------------------------------------------------------------
294 int NETTRANSACT::TxLoginS()
295 {
296 char loginZ[ADM_LOGIN_LEN + 1];
297 memset(loginZ, 0, ADM_LOGIN_LEN + 1);
298
299 BLOWFISH_CTX ctx;
300 InitContext(password.c_str(), PASSWD_LEN, &ctx);
301 EncryptString(loginZ, login.c_str(), std::min<size_t>(login.length() + 1, ADM_LOGIN_LEN), &ctx);
302 if (!WriteAll(sock, loginZ, ADM_LOGIN_LEN))
303     {
304     errorMsg = SEND_LOGIN_ERROR;
305     return st_send_fail;
306     }
307
308 return st_ok;
309 }
310 //---------------------------------------------------------------------------
311 int NETTRANSACT::RxLoginSAnswer()
312 {
313 char buffer[sizeof(OK_LOGINS) + 1];
314
315 if (!ReadAll(sock, buffer, strlen(OK_LOGINS)))
316     {
317     errorMsg = RECV_LOGIN_ANSWER_ERROR;
318     return st_recv_fail;
319     }
320
321 if (strncmp(OK_LOGINS, buffer, strlen(OK_LOGINS)) == 0)
322     return st_ok;
323
324 if (strncmp(ERR_LOGINS, buffer, strlen(ERR_LOGINS)) == 0)
325     {
326     errorMsg = INCORRECT_LOGIN;
327     return st_logins_err;
328     }
329 else
330     {
331     errorMsg = UNKNOWN_ERROR;
332     return st_unknown_err;
333     }
334 }
335 //---------------------------------------------------------------------------
336 int NETTRANSACT::TxData(const std::string & text)
337 {
338 STG::ENCRYPT_STREAM stream(password, TxCrypto, this);
339 stream.Put(text.c_str(), text.length() + 1, true);
340 if (!stream.IsOk())
341     {
342     errorMsg = SEND_DATA_ERROR;
343     return st_send_fail;
344     }
345
346 return st_ok;
347 }
348 //---------------------------------------------------------------------------
349 int NETTRANSACT::RxDataAnswer(CALLBACK callback, void * data)
350 {
351 ReadState state = {false, callback, data, this};
352 STG::DECRYPT_STREAM stream(password, RxCrypto, &state);
353 while (!state.final)
354     {
355     char buffer[1024];
356     ssize_t res = read(sock, buffer, sizeof(buffer));
357     if (res < 0)
358         {
359         errorMsg = RECV_DATA_ANSWER_ERROR;
360         return st_recv_fail;
361         }
362     stream.Put(buffer, res, res == 0);
363     if (!stream.IsOk())
364         return st_xml_parse_error;
365     }
366
367 return st_ok;
368 }
369 //---------------------------------------------------------------------------
370 bool NETTRANSACT::TxCrypto(const void * block, size_t size, void * data)
371 {
372 assert(data != NULL);
373 NETTRANSACT & nt = *static_cast<NETTRANSACT *>(data);
374 if (!WriteAll(nt.sock, block, size))
375     return false;
376 return true;
377 }
378 //---------------------------------------------------------------------------
379 bool NETTRANSACT::RxCrypto(const void * block, size_t size, void * data)
380 {
381 assert(data != NULL);
382 ReadState & state = *static_cast<ReadState *>(data);
383
384 const char * buffer = static_cast<const char *>(block);
385 for (size_t pos = 0; pos < size; ++pos)
386     if (buffer[pos] == 0)
387         {
388         state.final = true;
389         size = pos; // Adjust string size
390         }
391
392 if (state.callback)
393     if (!state.callback(std::string(buffer, size), state.final, state.callbackData))
394         return false;
395
396 return true;
397 }