source: chevmsgr/trunk/msgsrv.cpp@ 31

Last change on this file since 31 was 31, checked in by cheese, 8 years ago

키 ㅋ

File size: 14.1 KB
Line 
1
2#include "cf/network.h"
3#include "cf/task.h"
4#include "cf/codec.h"
5#include "cf/file.h"
6#include "crypto.h"
7
8#include "msg.hpp"
9
10#include <map>
11#include <stdlib.h>
12#include <time.h>
13
14#include "sqlite3.h"
15
16// --------------------------------------------------------------
17
18std::map<std::string, SecureSocket> gOnlineUsers;
19std::map<std::string, std::vector<std::string> > gSessionMap;
20
21// --------------------------------------------------------------
22
23int cb_getFriendList(void * userArg, int argc, char ** argv, char ** colName)
24{
25 std::vector<std::string> * friendList = (std::vector<std::string> *) userArg;
26
27 friendList->push_back(argv[1]);
28
29 return 0;
30}
31
32int cb_getSMS(void * userArg, int argc, char ** argv, char ** colName)
33{
34 std::string * sms = (std::string *) userArg;
35
36 for (int i = 0; i < argc; i++)
37 {
38 if (!stricmp(colName[i], "sms"))
39 {
40 *sms = argv[i];
41 break;
42 }
43 }
44
45 return 0;
46}
47
48int cb_exist(void * userArg, int argc, char ** argv, char ** colName)
49{
50 // È£ÃâµÇ¸é Àִ°Å
51 bool * result = (bool *)userArg;
52
53 *result = true;
54
55 return 0;
56}
57
58
59//===============================================================
60
61// --------------------------------------------------------------
62class DBManager
63{
64private:
65 sqlite3 * db;
66
67public:
68 DBManager()
69 throw (cf::exception)
70 : db(NULL)
71 {
72 Init();
73 }
74
75 void Init()
76 throw (cf::exception)
77 {
78 int result = 0;
79 bool isExist = false;
80
81 isExist = cf::file("account.db").exists();
82
83 result = sqlite3_open("account.db", &db);
84 if (result)
85 fprintf(stderr, "sqlite_open failed\n");
86
87 if (!isExist)
88 setup();
89 }
90
91 void setup()
92 throw (cf::exception)
93 {
94 std::string sqlCreateAccountTable;
95 std::string sqlCreateAuthTable;
96 std::string sqlCreateFriendsTable;
97 // declaration
98
99 // query
100 sqlCreateAccountTable =
101 "CREATE TABLE T_ACCOUNT"
102 "("
103 " id TEXT PRIMARY KEY,"
104 " pw TEXT"
105 ")";
106 sqlCreateAuthTable =
107 "CREATE TABLE T_AUTH"
108 "("
109 " id TEXT NOT NULL,"
110 " ip TEXT NOT NULL,"
111 " sms TEXT NOT NULL"
112 ")";
113 sqlCreateFriendsTable =
114 "CREATE TABLE T_FRIENDS"
115 "("
116 " id TEXT NOT NULL,"
117 " friend TEXT NOT NULL"
118 ")";
119
120 exec(sqlCreateAccountTable, NULL, NULL);
121 exec(sqlCreateAuthTable, NULL, NULL);
122 exec(sqlCreateFriendsTable, NULL, NULL);
123 }
124
125 void exec(std::string & query, sqlite3_callback cb, void * userArg)
126 throw(cf::exception)
127 {
128 int result = 0;
129 char * errMsg = NULL;
130
131 result = sqlite3_exec(db, query.c_str(), cb, userArg, &errMsg);
132 LOG("[SQL] " + query);
133
134 if (result != SQLITE_OK)
135 THROW_EXCEPTION("[DBERROR][" << result << "] " << errMsg);
136 }
137
138 bool login(const std::string & id, const std::string & pw)
139 throw (cf::exception)
140 {
141 try
142 {
143 bool result = false;
144 std::string query = "select * from T_ACCOUNT where id ='" + id + "' and pw = '" + pw + "'";
145
146 this->exec(query, cb_exist, &result);
147
148 return result;
149 }
150 catch (cf::exception & e)
151 {
152 FORWARD_EXCEPTION(e);
153 }
154 }
155
156 bool join(const std::string & id, const std::string & pw, const std::string & sms, const std::string & ip)
157 throw (cf::exception)
158 {
159 try
160 {
161 // account
162 bool isExistAccount = false;
163 std::string existAccount = "select * from T_ACCOUNT where id = '" + id + "'";
164 std::string insertAccount = "insert into T_ACCOUNT(id, pw) values('" + id + "', '" + pw + "')";
165
166 // auth
167 bool isExistAuth = false;
168 std::string existAuth = "select * from T_AUTH where id = '" + id + "' and sms = '" + sms + "' and ip = '" + ip + "'";
169 std::string insertAuth = "insert into T_AUTH(id, sms, ip) values( '" + id + "', '" + sms + "' , '" + ip + "')";
170
171 this->exec(existAccount, cb_exist, &isExistAccount);
172
173 if (!isExistAccount)
174 {
175 this->exec(insertAccount, NULL, NULL);
176 this->exec(insertAuth, NULL, NULL);
177 }
178 else
179 {
180 this->exec(existAuth, cb_exist, &isExistAuth);
181 if (!isExistAuth)
182 this->exec(insertAuth, NULL, NULL);
183 }
184 }
185 catch (cf::exception & e)
186 {
187 FORWARD_EXCEPTION(e);
188 }
189
190 return true;
191 }
192
193 std::vector<std::string> getFriendList(std::string & id)
194 throw(cf::exception)
195 {
196 int result = 0;
197 std::vector<std::string> friendList;
198
199 try
200 {
201 std::string query = "select * from T_FRIENDS where id = '" + id + "'";
202
203 this->exec(query, cb_getFriendList, &friendList);
204
205 return friendList;
206 }
207 catch (cf::exception & e)
208 {
209 FORWARD_EXCEPTION(e);
210 }
211 }
212
213 std::string getSMS(std::string & id, std::string & ip)
214 {
215 try
216 {
217 std::string sms;
218 std::string query = "select * from T_AUTH where id = '" + id + "' and ip = '" + ip + "'";
219
220 this->exec(query, cb_getSMS, &sms);
221 if (sms.length() == 0)
222 THROW_EXCEPTION("sms was not found for '" + id + "' from '" + ip + "'");
223
224 return sms;
225 }
226 catch (cf::exception & e)
227 {
228 FORWARD_EXCEPTION(e);
229 }
230 }
231
232 bool addFriend(std::string & id, std::string & friendID)
233 {
234 try
235 {
236 std::string query = "insert into T_FRIENDS values('" + id + "', '" + friendID + "')";
237
238 this->exec(query, NULL, NULL);
239
240 return true;
241 }
242 catch (cf::exception & e)
243 {
244 FORWARD_EXCEPTION(e);
245 return false;
246 }
247 }
248};
249DBManager dbmgr;
250
251class Runner
252{
253public:
254 cf::network::tcp * mSock;
255 cf::task::thread * mThread;
256
257 Runner(cf::network::tcp & sock, int (*worker)(void *))
258 : mSock(NULL)
259 , mThread(NULL)
260 {
261 mSock = new(std::nothrow) cf::network::tcp(sock.detach());
262 mThread = new(std::nothrow) cf::task::thread(worker);
263 if (!mSock || !mThread)
264 {
265 dispose();
266 THROW_EXCEPTION("cannot allocate thread arguments");
267 }
268
269 mThread->start(this);
270 }
271
272 ~Runner()
273 {
274 dispose();
275 }
276
277 void dispose()
278 {
279 if (mSock) delete mSock;
280 if (mThread) delete mThread;
281 }
282};
283
284static bool isOnline(const std::string & id)
285{
286 return gOnlineUsers.find(id) != gOnlineUsers.end();
287}
288
289static void logout(const std::string & id)
290{
291 gOnlineUsers[id].close();
292 gOnlineUsers.erase(id);
293 LOG(STR(id << " was logged out"));
294}
295
296static unsigned int generateSeed()
297{
298 unsigned int ret = 0;
299 int t = (int)time(NULL);
300
301 cf::bin b;
302 b.resize(sizeof(int));
303
304 b.set((cf::uint8_t*)&t, sizeof(int));
305 cf::bin s = crypto().sha256(b);
306
307 memcpy(&ret, s.buffer(), sizeof(unsigned int));
308
309 return ret;
310}
311
312static std::string generateSMSCode()
313{
314 return "545454";
315 char random[8] = {0x00,};
316 sprintf(random, "%06d", generateSeed() % 1000000);
317
318 return random;
319}
320
321static std::string httpSMS(const Protocol::Message & parser)
322{
323 std::string phone = parser.get<std::string>(ProtocolType::PHONE);
324
325#define CRLF "\r\n"
326 std::string code = generateSMSCode();
327 std::string url = "unsigned.kr";
328 std::string uri = "/~cheese/sms/req.php?to=" + phone + "&code=" + code;
329 std::string http =
330 "GET " + uri + " HTTP/1.1" CRLF
331 "Host: " + url + CRLF
332 "Accept: text/plain" CRLF
333 CRLF;
334
335 cf::network::tcp smsSock;
336 cf::network::host smsServer(url, 80);
337
338 smsSock.connect(smsServer);
339 smsSock.send(http);
340 smsSock.receive();
341 smsSock.close();
342
343 return code;
344}
345
346static Protocol::Message getSecret(const Protocol::Message & parser, const std::string & sms)
347{
348 std::string secret = parser.get<std::string>(ProtocolType::SECRET);
349 std::string random = parser.get<std::string>(ProtocolType::RANDOM);
350
351 crypto crypt;
352 crypt.setKey(cf::bin(sms + DELIMITER + random));
353
354 std::string auth = crypt.decrypt(cf::codec::hex::getInstance()->decode(secret)).toString();
355
356 if (auth[0] != '{')
357 THROW_EXCEPTION("invalid secret data (check cipher and its key)");
358
359 for (size_t iter = 0; iter < auth.length(); iter++)
360 {
361 if (auth[iter] < ' ' || '~' < auth[iter])
362 THROW_EXCEPTION("invalid secret data (check cipher and its key)");
363 }
364
365 Protocol::Message authParser;
366
367 authParser.parse(auth);
368
369 return authParser;
370}
371
372static bool join(const Protocol::Message & parser, const std::string & sms, const std::string & address)
373 throw (cf::exception)
374{
375 Protocol::Message & authParser = getSecret(parser, sms);
376
377 if (sms != authParser.get<std::string>(ProtocolType::SMS))
378 THROW_EXCEPTION("SMS is not same");
379
380 std::string id = authParser.get<std::string>(ProtocolType::ID);
381 std::string pw = authParser.get<std::string>(ProtocolType::PW);
382 std::string rcvdSMS = authParser.get<std::string>(ProtocolType::SMS);
383
384 if (sms != rcvdSMS)
385 THROW_EXCEPTION("invalid sms code");
386
387 return dbmgr.join(id, pw, sms, address);
388}
389
390static bool login(const Protocol::Message & parser, cf::network::tcp & sock, std::string & ip, std::string sms)
391 throw (cf::exception)
392{
393 Protocol::Message & authParser = getSecret(parser, sms);
394
395 std::string id = authParser.get<std::string>(ProtocolType::ID);
396 std::string pw = authParser.get<std::string>(ProtocolType::PW);
397
398 bool result = dbmgr.login(id, pw);
399 if (result)
400 gOnlineUsers[id] = SecureSocket(&sock, ip, sms);
401
402 return result;
403}
404
405static bool chat(const Protocol::Message & message)
406{
407 bool result = false;
408 Protocol::Message parser = message;
409 std::string sessid = parser.get<std::string>(ProtocolType::SESSION_ID);
410 std::vector<std::string> idList = gSessionMap[sessid];
411 std::string sender = parser.get<std::string>(ProtocolType::ID);
412 parser.mObject[ProtocolType::TYPE] = ProtocolType::LISTEN;
413 std::string serialized = parser.serialize();
414
415 for (size_t iter = 0; iter < idList.size(); iter++)
416 {
417 std::string id = idList[iter];
418
419 if (sender != id && isOnline(id))
420 gOnlineUsers[id].send(serialized);
421
422 result = true;
423 }
424
425 return result;
426}
427
428static std::vector<SFriend> getFriendList(const Protocol::Message & parser)
429 throw (cf::exception)
430{
431 std::string id = parser.get<std::string>(ProtocolType::ID);
432 std::vector<std::string> fl = dbmgr.getFriendList(id);
433 std::vector<SFriend> fv;
434
435 for (size_t iter = 0; iter < fl.size(); iter++)
436 {
437 SFriend s;
438 s.id = fl[iter];
439 s.name = fl[iter]; // username?
440 fv.push_back(s);
441 }
442
443 return fv;
444}
445
446static std::string createSessionID(std::string & idList)
447{
448 cf::bin sessid;
449
450 sessid = crypto().sha256(cf::bin(idList));
451
452 return cf::codec::hex::getInstance()->encode(sessid);
453}
454
455static bool openSession(const Protocol::Message & message)
456{
457 const static cf::bin delim(DELIMITER);
458
459 Protocol::Message parser = message;
460 bool result = true;
461 std::string sessid;
462 std::vector<std::string> idList = parser.getList<std::string>(ProtocolType::ID_LIST);
463 std::string concat = joinStrings(idList);
464
465 sessid = createSessionID(concat);
466
467 parser.mObject[ProtocolType::SESSION_ID] = sessid;
468
469 cf::bin allKeys;
470
471 for (size_t iter = 0; iter < idList.size(); iter++)
472 {
473 std::string id = idList[iter];
474 if (isOnline(id))
475 {
476 SecureSocket & secsock = gOnlineUsers[id];
477 std::string ip = secsock.getTcpSocket().local().address();
478 std::string sms = dbmgr.getSMS(id, ip);
479
480 cf::bin seed = sms + DELIMITER + ip;
481 cf::bin key = crypto().sha256(seed);
482
483 allKeys += key;
484 }
485 }
486
487 allKeys += generateRandom();
488 std::string hashedKey = cf::codec::hex::getInstance()->encode(crypto().sha256(allKeys));
489 parser.mObject[ProtocolType::SESSION_KEY] = hashedKey;
490
491 std::string serialized = parser.serialize();
492
493 for (size_t iter = 0; iter < idList.size(); iter++)
494 {
495 std::string id = idList[iter];
496 if (isOnline(id))
497 gOnlineUsers[id].send(serialized);
498 }
499
500 gSessionMap[sessid] = idList;
501
502 return result;
503}
504
505static bool addFriend(const Protocol::Message & message)
506{
507 Protocol::Message parser = message;
508 std::string result = "";
509 std::string id = parser.get<std::string>(ProtocolType::ID);
510 std::string friendID = parser.get<std::string>(ProtocolType::FRIEND_ID);
511
512 return dbmgr.addFriend(id, friendID);
513}
514
515static std::string authenticator(cf::network::tcp & sock)
516{
517 Protocol::Message parser;
518
519 try
520 {
521 std::string sms;
522 std::string id;
523 bool loggedIn = false;
524 bool result = false;
525
526 while (true)
527 {
528 result = false;
529 std::string message = sock.receive().toString();
530 if (message[0] != '{')
531 THROW_EXCEPTION("unknown message");
532
533 parser.parse(message);
534
535 LOG(message);
536
537 if (parser.type() == ProtocolType::SMS)
538 {
539 sms = httpSMS(parser);
540 result = true;
541 }
542 else if (parser.type() == ProtocolType::JOIN)
543 {
544 if (!join(parser, sms, sock.peer().address()))
545 THROW_EXCEPTION("user(" << parser.get<std::string>(ProtocolType::ID) << ") cannot join");
546 result = true;
547 }
548 else if (parser.type() == ProtocolType::LOGIN)
549 {
550 id = parser.get<std::string>(ProtocolType::ID);
551 std::string ip = sock.peer().address();
552 sms = dbmgr.getSMS(id, ip);
553
554 // ½ÇÆÐÇÏ¸é ¾Õ¿¡¼­ exceptionÀ¸·Î ´Ù 󸮵Ê
555 // ¿©±ä ¼º°øÇÒ ¶§¿¡¸¸ ¿È
556 if (login(parser, sock, ip, sms))
557 {
558 loggedIn = true;
559 result = true;
560 }
561 }
562
563 // success
564 sock.send(Protocol::Response().result(parser.type(), result));
565 if (loggedIn)
566 return id;
567 }
568 }
569 catch (cf::exception & e)
570 {
571 sock.send(Protocol::Response().result(parser.type(), false));
572 FORWARD_EXCEPTION(e);
573 }
574}
575
576static int deliverer(void * arg)
577{
578 Runner * runner = reinterpret_cast<Runner *>(arg);
579 cf::network::tcp * sock = runner->mSock;
580
581 std::string id;
582
583 try
584 {
585 id = authenticator(*sock);
586 SecureSocket & secsock = gOnlineUsers[id];
587
588 while (true)
589 {
590 bool result = true;
591 Protocol::Response response;
592 std::string message = secsock.receive();
593 Protocol::Message parser;
594 parser.parse(message);
595
596 LOG(message);
597
598 if (parser.type() == ProtocolType::TELL)
599 {
600 result = chat(parser);
601 }
602 else if (parser.type() == ProtocolType::OPEN_SESSION)
603 {
604 result = openSession(parser);
605 }
606 else if (parser.type() == ProtocolType::ADD_FRIEND)
607 {
608 result = addFriend(parser);
609 }
610 else if (parser.type() == ProtocolType::GET_FRIEND_LIST)
611 {
612 try
613 {
614 std::vector<SFriend> fl = getFriendList(parser);
615 secsock.send(response.friendList(fl));
616 } catch (cf::exception & e)
617 {
618 LOG(e.stackTrace());
619 result = false;
620 }
621 }
622
623 secsock.send(response.result(parser.type(), result));
624 }
625 }
626 catch (cf::exception & e)
627 {
628 LOG(e.stackTrace());
629 }
630
631 logout(id);
632 delete runner;
633 return 0;
634}
635
636static int server(unsigned short port)
637{
638 cf::network::tcp sock;
639
640 try
641 {
642 sock.bind(port);
643 sock.listen();
644 }
645 catch(cf::exception & e)
646 {
647 LOG(e.what());
648 return -1;
649 }
650
651 while (true)
652 {
653 try
654 {
655 cf::network::tcp client = sock.accept();
656 Runner * runner = new(std::nothrow) Runner(client, deliverer);
657 if (!runner)
658 THROW_EXCEPTION("cannot create thread argument");
659 }
660 catch(cf::exception & e)
661 {
662 LOG(e.what());
663 }
664 }
665
666 return 0;
667}
668
669int main(int argc, char ** argv)
670{
671 if (argc != 2)
672 {
673 std::cerr << "-_-^" << std::endl;
674 return -1;
675 }
676
677 return server((unsigned short)atoi(argv[1]));
678}
Note: See TracBrowser for help on using the repository browser.