]> git.stg.codes - stg.git/blob - projects/stargazer/plugins/configuration/sgconfig-ng/config_thread.cpp
Split SETTINGS to interface and implementation
[stg.git] / projects / stargazer / plugins / configuration / sgconfig-ng / config_thread.cpp
1 #include <expat.h>
2
3 #include <cstring>
4 #include <cerrno>
5 #include <cassert>
6 #include <iostream>
7
8 #include <boost/thread/mutex.hpp>
9
10 // TODO: Fix this shit!
11 #include "../../../admins.h"
12
13 #include "common.h"
14 #include "proto.h"
15 #include "config_thread.h"
16 #include "root_parser.h"
17
18 void DumpCrypto(const char * data, size_t size)
19 {
20     std::string dumpstr = "";
21     for (unsigned i = 0; i < size; ++i) {
22         std::string ch;
23         strprintf(&ch, "%x", *(data + i));
24         dumpstr += ch;
25     }
26     printfd(__FILE__, "Crypto dump: '%s'\n", dumpstr.c_str());
27 }
28
29 CONFIG_THREAD::CONFIG_THREAD(ADMINS * a, TARIFFS * t, USERS * u, const SETTINGS * s)
30     : sd(-1),
31       done(false),
32       state(ST_NOOP),
33       respCode(RESP::OK),
34       admins(a),
35       tariffs(t),
36       users(u),
37       settings(s)
38 {
39     /*printfd(__FILE__, "sizeof(REQ::HEADER) = %d\n", sizeof(REQ::HEADER));
40     printfd(__FILE__, "sizeof(REQ::CRYPTO_HEADER) = %d\n", sizeof(REQ::CRYPTO_HEADER));
41     printfd(__FILE__, "sizeof(RESP::HEADER) = %d\n", sizeof(RESP::HEADER));
42     printfd(__FILE__, "sizeof(RESP::CRYPTO_HEADER) = %d\n", sizeof(RESP::CRYPTO_HEADER));*/
43     assert(sizeof(REQ::HEADER) % 8 == 0);
44     assert(sizeof(REQ::CRYPTO_HEADER) % 8 == 0);
45     assert(sizeof(RESP::HEADER) % 8 == 0);
46     assert(sizeof(RESP::CRYPTO_HEADER) % 8 == 0);
47
48     iv = new unsigned char[8];
49     memset(iv, 0, 8);
50 }
51
52 CONFIG_THREAD::CONFIG_THREAD(const CONFIG_THREAD & rvalue)
53     : sd(rvalue.sd),
54       remoteAddr(rvalue.remoteAddr),
55       done(false),
56       state(ST_NOOP),
57       respCode(rvalue.respCode),
58       admins(rvalue.admins),
59       tariffs(rvalue.tariffs),
60       users(rvalue.users),
61       settings(rvalue.settings)
62 {
63     assert(!rvalue.done);
64     iv = new unsigned char[8];
65     memcpy(iv, rvalue.iv, 8);
66 }
67
68 CONFIG_THREAD & CONFIG_THREAD::operator=(const CONFIG_THREAD & rvalue)
69 {
70     assert(0 && "Never be here");
71     return *this;
72 }
73
74 CONFIG_THREAD::~CONFIG_THREAD()
75 {
76     //assert(done);
77     delete[] iv;
78 }
79
80 void CONFIG_THREAD::operator() ()
81 {
82     if (sd < 0) {
83         printfd(__FILE__, "CONFIG_THREAD::operator()() Invalid socket descriptor\n");
84         return;
85     }
86
87     if (ReadReq()) {
88         Process();
89     }
90
91     WriteResp();
92
93     close(sd);
94
95     {
96         boost::mutex::scoped_lock lock(mutex);
97         done = true;
98     }
99 }
100
101 bool CONFIG_THREAD::IsDone() const
102 {
103     boost::mutex::scoped_lock lock(mutex);
104     return done;
105 }
106
107 void CONFIG_THREAD::SetConnection(int sock, struct sockaddr_in sin)
108 {
109     sd = sock;
110     remoteAddr = sin;
111 }
112
113 bool CONFIG_THREAD::ReadBlock(void * dest, size_t & size, int timeout) const
114 {
115     unsigned readSize = 0;
116     char * ptr = static_cast<char *>(dest);
117     while (readSize < size) {
118         struct timeval tv;
119         tv.tv_sec = 0;
120         tv.tv_usec = timeout * 1000;
121
122         fd_set rfds;
123         FD_ZERO(&rfds);
124         FD_SET(sd, &rfds);
125
126         int res = select(sd + 1, &rfds, NULL, NULL, &tv);
127         /* Don't rely on the value of tv now! */
128
129         if (res < 0) {
130             printfd(__FILE__, "CONFIG_THREAD::ReadBlock() Select error: '%s'\n", strerror(errno));
131             return false;
132         }
133
134         if (res == 0) {
135             // Timeout
136             size = readSize;
137             return false;
138         }
139
140         res = read(sd, ptr + readSize, size - readSize);
141
142         if (res == 0) { // EOF
143             printfd(__FILE__, "CONFIG_THREAD::ReadBlock() EOF\n");
144             return false;
145         }
146
147         // Ignore 'Interrupted system call' errors
148         if (res < 0) {
149             if (errno != EINTR) {
150                 printfd(__FILE__, "CONFIG_THREAD::ReadBlock() Read error: '%s'\n", strerror(errno));
151                 return false;
152             } else {
153                 continue;
154             }
155         }
156
157         readSize += res;
158     }
159
160     return true;
161 }
162
163 bool CONFIG_THREAD::WriteBlock(const void * source, size_t & size, int timeout) const
164 {
165     const char * ptr = static_cast<const char *>(source);
166     unsigned writeSize = 0;
167     while (writeSize < size) {
168         struct timeval tv;
169         tv.tv_sec = 0;
170         tv.tv_usec = timeout * 1000;
171
172         fd_set wfds;
173         FD_ZERO(&wfds);
174         FD_SET(sd, &wfds);
175
176         int res = select(sd + 1, NULL, &wfds, NULL, &tv);
177         /* Don't rely on the value of tv now! */
178
179         if (res < 0) {
180             printfd(__FILE__, "CONFIG_THREAD::WriteBlock() Select error: '%s'\n", strerror(errno));
181             return false;
182         }
183
184         if (res == 0) {
185             // Timeout
186             size = writeSize;
187             return false;
188         }
189
190         res = write(sd, ptr + writeSize, size - writeSize);
191
192         // Ignore 'Interrupted system call' errors
193         if (res < 0 && errno != EINTR) {
194             printfd(__FILE__, "CONFIG_THREAD::WriteBlock() Write error: '%s'\n", strerror(errno));
195             return false;
196         }
197
198         writeSize += res;
199     }
200
201     return true;
202 }
203
204 bool CONFIG_THREAD::ReadReq()
205 {
206     struct REQ::HEADER reqHeader;
207
208     size_t size = sizeof(reqHeader);
209     if (!ReadBlock(&reqHeader, size, 5000)) {
210         state = ST_ERROR;
211         message = "No request header within 5 sec";
212         printfd(__FILE__, "CONFIG_THREAD::ReadReq() %s\n", message.c_str());
213         return false;
214     }
215
216     if (strncmp(reqHeader.magic, PROTO_MAGIC, sizeof(reqHeader.magic))) {
217         state = ST_ERROR;
218         respCode = RESP::INVALID_MAGIC;
219         message = "Invalid magic code in header";
220         printfd(__FILE__, "CONFIG_THREAD::ReadReq() %s\n", message.c_str());
221         return false;
222     }
223
224     uint32_t version = ntohl(reqHeader.version);
225     if (version > (2 << 8 | 0)) {
226         state = ST_ERROR;
227         respCode = RESP::UNSUPPORTED_VERSION;
228         message = "Unsupported version";
229         printfd(__FILE__, "CONFIG_THREAD::ReadReq() %s (wanted: %d, actual: %d)\n", message.c_str(), (2 << 8 | 0), version);
230         return false;
231     }
232
233     versionMinor = version & 0x0000FFFF;
234     versionMajor = (version >> 8) & 0x0000FFFF;
235
236     reqHeader.login[sizeof(reqHeader.login) - 1] = 0;
237
238     login = reqHeader.login;
239
240     if (!CheckLogin(login, password)) {
241         state = ST_ERROR;
242         respCode = RESP::INVALID_CREDENTIALS;
243         message = "Unknown login";
244         printfd(__FILE__, "CONFIG_THREAD::ReadReq() %s\n", message.c_str());
245         return false;
246     }
247
248     return ReceiveData();
249 }
250
251 bool CONFIG_THREAD::ReceiveData()
252 {
253     unsigned char buffer[sizeof(struct REQ::CRYPTO_HEADER)];
254     //unsigned char iv[] = "00000000";
255     size_t size = sizeof(struct REQ::CRYPTO_HEADER);
256
257     if (!ReadBlock(buffer, size, 5000)) {
258         state = ST_ERROR;
259         message = "No crypto header within 5 secs";
260         printfd(__FILE__, "CONFIG_THREAD::ReceiveData() %s\n", message.c_str());
261         return false;
262     }
263
264     BF_set_key(&key, password.length(), reinterpret_cast<const unsigned char *>(password.c_str()));
265
266     struct REQ::CRYPTO_HEADER reqCryptoHeader;
267
268     BF_cbc_encrypt(buffer, reinterpret_cast<unsigned char *>(&reqCryptoHeader), sizeof(struct REQ::CRYPTO_HEADER), &key, iv, BF_DECRYPT);
269
270     reqCryptoHeader.login[sizeof(reqCryptoHeader.login) - 1] = 0;
271
272     std::string cryptoLogin(reqCryptoHeader.login);
273
274     if (login != cryptoLogin) {
275         state = ST_ERROR;
276         respCode = RESP::INVALID_CREDENTIALS;
277         message = "Password is invalid";
278         printfd(__FILE__, "CONFIG_THREAD::ReceiveData() %s\n", message.c_str());
279         return false;
280     }
281
282     //assert(reqCryptoHeader.dataSize % 8 == 0);
283
284     char block[1496];
285     unsigned char cryptoBlock[1496];
286     size_t length = 0;
287     uint32_t dataSize = ntohl(reqCryptoHeader.dataSize);
288
289     while (length < dataSize) {
290         size_t delta = dataSize - length;
291         if (delta > sizeof(cryptoBlock)) {
292             delta = sizeof(cryptoBlock);
293         }
294         size_t bs = delta;
295         ReadBlock(cryptoBlock, bs, 5000);
296         if (bs != delta) {
297             state = ST_ERROR;
298             message = "No data within 5 secs";
299             printfd(__FILE__, "CONFIG_THREAD::ReceiveData() %s\n", message.c_str());
300             return false;
301         }
302
303         BF_cbc_encrypt(cryptoBlock, reinterpret_cast<unsigned char *>(block), bs, &key, iv, BF_DECRYPT);
304
305         xml.append(block, bs);
306
307         length += bs;
308     }
309
310     return true;
311 }
312
313 void CONFIG_THREAD::Process()
314 {
315     ROOT_PARSER parser(currAdmin, tariffs, users, settings);
316
317     XML_Parser p;
318
319     p= XML_ParserCreate(NULL);
320     XML_SetElementHandler(p, &TagBegin, &TagEnd);
321     XML_SetUserData(p, &parser);
322
323     if (!XML_Parse(p, xml.c_str(), xml.length(), true)) {
324         printfd(__FILE__, "CONFIG_THREAD::Process() Error: '%s' at line %d\n", XML_ErrorString(XML_GetErrorCode(p)), XML_GetCurrentLineNumber(p));
325         //MakeErrorXML();
326     }
327
328     XML_ParserFree(p);
329
330     xml = parser.GetResult();
331 }
332
333 void CONFIG_THREAD::WriteResp() const
334 {
335     RESP::HEADER respHeader;
336
337     strncpy(respHeader.magic, PROTO_MAGIC, sizeof(respHeader.magic));
338     respHeader.version = htonl(2 << 8 | 0);
339     respHeader.code = respCode;
340
341     RESP::CRYPTO_HEADER respCryptoHeader;
342     strncpy(respCryptoHeader.login, login.c_str(), sizeof(respCryptoHeader.login));
343     if (xml.size() % 8 == 0) {
344         respCryptoHeader.dataSize = htonl(xml.size());
345     } else {
346         respCryptoHeader.dataSize = htonl((xml.size() / 8 + 1) * 8);
347     }
348
349     size_t size = sizeof(respHeader);
350     if (!WriteBlock(&respHeader, size, 5000)) {
351         printfd(__FILE__, "CONFIG_THREAD::WriteResp() Failed to send answer header\n");
352         return;
353     }
354
355     if (state != ST_ERROR) {
356         unsigned char buffer[sizeof(respCryptoHeader)];
357         size = sizeof(respCryptoHeader);
358
359         BF_cbc_encrypt(reinterpret_cast<unsigned char *>(&respCryptoHeader), buffer, size, &key, iv, BF_ENCRYPT);
360
361         if (!WriteBlock(buffer, size, 5000)) {
362             printfd(__FILE__, "CONFIG_THREAD::WriteResp() Failed to send answer crypto-header\n");
363             return;
364         }
365
366         SendData();
367     }
368 }
369
370 void CONFIG_THREAD::SendData() const
371 {
372     size_t pos = 0;
373     std::string data(xml);
374     if (data.size() % 8) {
375         size_t delta = (data.size() / 8 + 1) * 8 - data.size();
376         data.append(delta, ' ');
377     }
378     while (pos < data.size()) {
379         unsigned char source[1496];
380         unsigned char buffer[1496];
381
382         size_t size;
383         if (data.size() - pos > sizeof(source)) {
384             memcpy(source, data.c_str() + pos, sizeof(source));
385             size = sizeof(source);
386         } else {
387             memset(source, 0, sizeof(source));
388             memcpy(source, data.c_str() + pos, data.size() - pos);
389             size = data.size() - pos;
390         }
391
392         BF_cbc_encrypt(source, buffer, size, &key, iv, BF_ENCRYPT);
393
394         if (!WriteBlock(buffer, size, 5000)) {
395             printfd(__FILE__, "CONFIG_THREAD::SendData() Failed to write data block\n");
396             return;
397         }
398
399         pos += size; // size?
400     }
401
402     return;
403 }
404
405 bool CONFIG_THREAD::CheckLogin(const std::string & login, std::string & password)
406 {
407     currAdmin = admins->FindAdmin(login);
408
409     if (currAdmin == NULL) {
410         printfd(__FILE__, "CONFIG_THREAD::CheckLogin() Admin '%s' not found\n", login.c_str());
411         return false;
412     }
413
414     password = currAdmin->GetPassword();
415
416     return true;
417 }
418
419 void CONFIG_THREAD::TagBegin(void * userData, const char * name, const char ** attr)
420 {
421     ROOT_PARSER * self = static_cast<ROOT_PARSER *>(userData);
422     self->StartTag(name, attr);
423 }
424
425 void CONFIG_THREAD::TagEnd(void * userData, const char * name)
426 {
427     ROOT_PARSER * self = static_cast<ROOT_PARSER *>(userData);
428     self->EndTag(name);
429 }