]> git.stg.codes - stg.git/blob - projects/stargazer/plugins/other/rscript/rscript.cpp
README converted from KOI8 to UTF8
[stg.git] / projects / stargazer / plugins / other / rscript / rscript.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  *    Author : Maxim Mamontov <faust@stargazer.dp.ua>
20  */
21
22 /*
23  $Revision: 1.33 $
24  $Date: 2010/04/16 12:30:37 $
25  $Author: faust $
26 */
27
28 #include <sys/time.h>
29
30 #include <csignal>
31 #include <cassert>
32 #include <algorithm>
33
34 #include "rscript.h"
35 #include "common.h"
36 #include "ur_functor.h"
37 #include "send_functor.h"
38
39 extern volatile const time_t stgTime;
40
41 #define RS_MAX_ROUTERS  (100)
42
43 //-----------------------------------------------------------------------------
44 //-----------------------------------------------------------------------------
45 //-----------------------------------------------------------------------------
46 class RS_CREATOR
47 {
48 private:
49     REMOTE_SCRIPT * rs;
50
51 public:
52     RS_CREATOR()
53         : rs(new REMOTE_SCRIPT())
54         {
55         };
56     ~RS_CREATOR()
57         {
58         delete rs;
59         };
60
61     REMOTE_SCRIPT * GetPlugin()
62         {
63         return rs;
64         };
65 };
66 //-----------------------------------------------------------------------------
67 //-----------------------------------------------------------------------------
68 //-----------------------------------------------------------------------------
69 RS_CREATOR rsc;
70 //-----------------------------------------------------------------------------
71 //-----------------------------------------------------------------------------
72 //-----------------------------------------------------------------------------
73 BASE_PLUGIN * GetPlugin()
74 {
75 return rsc.GetPlugin();
76 }
77 //-----------------------------------------------------------------------------
78 //-----------------------------------------------------------------------------
79 //-----------------------------------------------------------------------------
80 RS_USER::RS_USER()
81     : lastSentTime(0),
82       shortPacketsCount(0)
83 {
84 }
85 //-----------------------------------------------------------------------------
86 RS_USER::RS_USER(const std::vector<uint32_t> & r, user_iter it)
87     : lastSentTime(0),
88       user(it),
89       routers(r),
90       shortPacketsCount(0)
91 {
92 }
93 //-----------------------------------------------------------------------------
94 RS_SETTINGS::RS_SETTINGS()
95     : sendPeriod(0),
96       port(0)
97 {
98 }
99 //-----------------------------------------------------------------------------
100 int RS_SETTINGS::ParseIntInRange(const string & str, int min, int max, int * val)
101 {
102 if (str2x(str.c_str(), *val))
103     {
104     errorStr = "Incorrect value \'" + str + "\'.";
105     return -1;
106     }
107 if (*val < min || *val > max)
108     {
109     errorStr = "Value \'" + str + "\' out of range.";
110     return -1;
111     }
112 return 0;
113 }
114 //-----------------------------------------------------------------------------
115 int RS_SETTINGS::ParseSettings(const MODULE_SETTINGS & s)
116 {
117 int p;
118 PARAM_VALUE pv;
119 vector<PARAM_VALUE>::const_iterator pvi;
120 netRouters.clear();
121 ///////////////////////////
122 pv.param = "Port";
123 pvi = find(s.moduleParams.begin(), s.moduleParams.end(), pv);
124 if (pvi == s.moduleParams.end())
125     {
126     errorStr = "Parameter \'Port\' not found.";
127     printfd(__FILE__, "Parameter 'Port' not found\n");
128     return -1;
129     }
130 if (ParseIntInRange(pvi->value[0], 2, 65535, &p))
131     {
132     errorStr = "Cannot parse parameter \'Port\': " + errorStr;
133     printfd(__FILE__, "Cannot parse parameter 'Port'\n");
134     return -1;
135     }
136 port = p;
137 ///////////////////////////
138 pv.param = "SendPeriod";
139 pvi = find(s.moduleParams.begin(), s.moduleParams.end(), pv);
140 if (pvi == s.moduleParams.end())
141     {
142     errorStr = "Parameter \'SendPeriod\' not found.";
143     printfd(__FILE__, "Parameter 'SendPeriod' not found\n");
144     return -1;
145     }
146
147 if (ParseIntInRange(pvi->value[0], 5, 600, &sendPeriod))
148     {
149     errorStr = "Cannot parse parameter \'SendPeriod\': " + errorStr;
150     printfd(__FILE__, "Cannot parse parameter 'SendPeriod'\n");
151     return -1;
152     }
153 ///////////////////////////
154 pv.param = "UserParams";
155 pvi = find(s.moduleParams.begin(), s.moduleParams.end(), pv);
156 if (pvi == s.moduleParams.end())
157     {
158     errorStr = "Parameter \'UserParams\' not found.";
159     printfd(__FILE__, "Parameter 'UserParams' not found\n");
160     return -1;
161     }
162 userParams = pvi->value;
163 ///////////////////////////
164 pv.param = "Password";
165 pvi = find(s.moduleParams.begin(), s.moduleParams.end(), pv);
166 if (pvi == s.moduleParams.end())
167     {
168     errorStr = "Parameter \'Password\' not found.";
169     printfd(__FILE__, "Parameter 'Password' not found\n");
170     return -1;
171     }
172 password = pvi->value[0];
173 ///////////////////////////
174 pv.param = "SubnetFile";
175 pvi = find(s.moduleParams.begin(), s.moduleParams.end(), pv);
176 if (pvi == s.moduleParams.end())
177     {
178     errorStr = "Parameter \'SubnetFile\' not found.";
179     printfd(__FILE__, "Parameter 'SubnetFile' not found\n");
180     return -1;
181     }
182 subnetFile = pvi->value[0];
183
184 NRMapParser nrMapParser;
185
186 if (nrMapParser.ReadFile(subnetFile))
187     {
188     errorStr = nrMapParser.GetErrorStr();
189     return -1;
190     }
191
192 netRouters = nrMapParser.GetMap();
193
194 if (netRouters.empty())
195     {
196     errorStr = "Parameter(s) \'Subnet*\' not found.";
197     printfd(__FILE__, "Parameter(s) 'Subnet*' not found\n");
198     return -1;
199     }
200
201 return 0;
202 }
203 //-----------------------------------------------------------------------------
204 //-----------------------------------------------------------------------------
205 //-----------------------------------------------------------------------------
206 REMOTE_SCRIPT::REMOTE_SCRIPT()
207     : sendPeriod(15),
208       halfPeriod(8),
209       nonstop(false),
210       isRunning(false),
211       users(NULL),
212       sock(0),
213       onAddUserNotifier(*this),
214       onDelUserNotifier(*this)
215 {
216 pthread_mutex_init(&mutex, NULL);
217 }
218 //-----------------------------------------------------------------------------
219 REMOTE_SCRIPT::~REMOTE_SCRIPT()
220 {
221 pthread_mutex_destroy(&mutex);
222 }
223 //-----------------------------------------------------------------------------
224 void * REMOTE_SCRIPT::Run(void * d)
225 {
226 REMOTE_SCRIPT * rs = static_cast<REMOTE_SCRIPT *>(d);
227
228 rs->isRunning = true;
229
230 while (rs->nonstop)
231     {
232     rs->PeriodicSend();
233     sleep(2);
234     }
235
236 rs->isRunning = false;
237 return NULL;
238 }
239 //-----------------------------------------------------------------------------
240 int REMOTE_SCRIPT::ParseSettings()
241 {
242 int ret = rsSettings.ParseSettings(settings);
243 if (ret)
244     errorStr = rsSettings.GetStrError();
245
246 sendPeriod = rsSettings.GetSendPeriod();
247 halfPeriod = sendPeriod / 2;
248
249 return ret;
250 }
251 //-----------------------------------------------------------------------------
252 int REMOTE_SCRIPT::Start()
253 {
254 netRouters = rsSettings.GetSubnetsMap();
255
256 InitEncrypt(&ctx, rsSettings.GetPassword());
257
258 //onAddUserNotifier.SetRemoteScript(this);
259 //onDelUserNotifier.SetRemoteScript(this);
260
261 users->AddNotifierUserAdd(&onAddUserNotifier);
262 users->AddNotifierUserDel(&onDelUserNotifier);
263
264 nonstop = true;
265
266 if (GetUsers())
267     {
268     return -1;
269     }
270
271 if (PrepareNet())
272     {
273     return -1;
274     }
275
276 if (!isRunning)
277     {
278     if (pthread_create(&thread, NULL, Run, this))
279         {
280         errorStr = "Cannot create thread.";
281         printfd(__FILE__, "Cannot create thread\n");
282         return -1;
283         }
284     }
285
286 errorStr = "";
287 return 0;
288 }
289 //-----------------------------------------------------------------------------
290 int REMOTE_SCRIPT::Stop()
291 {
292 if (!IsRunning())
293     return 0;
294
295 nonstop = false;
296
297 std::for_each(
298         authorizedUsers.begin(),
299         authorizedUsers.end(),
300         DisconnectUser(*this)
301         );
302
303 FinalizeNet();
304
305 if (isRunning)
306     {
307     //5 seconds to thread stops itself
308     for (int i = 0; i < 25 && isRunning; i++)
309         {
310         usleep(200000);
311         }
312
313     //after 5 seconds waiting thread still running. now killing it
314     if (isRunning)
315         {
316         if (pthread_kill(thread, SIGINT))
317             {
318             errorStr = "Cannot kill thread.";
319             printfd(__FILE__, "Cannot kill thread\n");
320             return -1;
321             }
322         printfd(__FILE__, "REMOTE_SCRIPT killed Run\n");
323         }
324     }
325
326 users->DelNotifierUserDel(&onDelUserNotifier);
327 users->DelNotifierUserAdd(&onAddUserNotifier);
328
329 return 0;
330 }
331 //-----------------------------------------------------------------------------
332 int REMOTE_SCRIPT::Reload()
333 {
334 NRMapParser nrMapParser;
335
336 if (nrMapParser.ReadFile(rsSettings.GetMapFileName()))
337     {
338     errorStr = nrMapParser.GetErrorStr();
339     return -1;
340     }
341
342     {
343     STG_LOCKER lock(&mutex, __FILE__, __LINE__);
344
345     printfd(__FILE__, "REMOTE_SCRIPT::Reload()\n");
346
347     netRouters = nrMapParser.GetMap();
348     }
349
350 std::for_each(authorizedUsers.begin(),
351               authorizedUsers.end(),
352               UpdateRouter(*this));
353
354 return 0;
355 }
356 //-----------------------------------------------------------------------------
357 bool REMOTE_SCRIPT::PrepareNet()
358 {
359 sock = socket(AF_INET, SOCK_DGRAM, 0);
360
361 if (sock < 0)
362     {
363     errorStr = "Cannot create socket.";
364     printfd(__FILE__, "Cannot create socket\n");
365     return true;
366     }
367
368 return false;
369 }
370 //-----------------------------------------------------------------------------
371 bool REMOTE_SCRIPT::FinalizeNet()
372 {
373 close(sock);
374 return false;
375 }
376 //-----------------------------------------------------------------------------
377 void REMOTE_SCRIPT::PeriodicSend()
378 {
379 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
380
381 map<uint32_t, RS_USER>::iterator it(authorizedUsers.begin());
382 while (it != authorizedUsers.end())
383     {
384     if (difftime(stgTime, it->second.lastSentTime) - (rand() % halfPeriod) > sendPeriod)
385     //if (stgTime - it->second.lastSentTime > sendPeriod)
386         {
387         Send(it->first, it->second);
388         }
389     ++it;
390     }
391 }
392 //-----------------------------------------------------------------------------
393 bool REMOTE_SCRIPT::PreparePacket(char * buf, size_t bufSize, uint32_t ip, RS_USER & rsu, bool forceDisconnect) const
394 {
395 RS_PACKET_HEADER packetHead;
396
397 memset(packetHead.padding, 0, sizeof(packetHead.padding));
398 strcpy((char*)packetHead.magic, RS_ID);
399 packetHead.protoVer[0] = '0';
400 packetHead.protoVer[1] = '2';
401 if (forceDisconnect)
402     {
403     packetHead.packetType = RS_DISCONNECT_PACKET;
404     }
405 else
406     {
407     if (rsu.shortPacketsCount % MAX_SHORT_PCKT == 0)
408         {
409         //SendLong
410         packetHead.packetType = rsu.user->IsInetable() ? RS_CONNECT_PACKET : RS_DISCONNECT_PACKET;
411         }
412     else
413         {
414         //SendShort
415         packetHead.packetType = rsu.user->IsInetable() ? RS_ALIVE_PACKET : RS_DISCONNECT_PACKET;
416         }
417     }
418 rsu.shortPacketsCount++;
419 rsu.lastSentTime = stgTime;
420
421 packetHead.ip = htonl(ip);
422 packetHead.id = htonl(rsu.user->GetID());
423 strncpy((char*)packetHead.login, rsu.user->GetLogin().c_str(), RS_LOGIN_LEN);
424 packetHead.login[RS_LOGIN_LEN - 1] = 0;
425
426 memcpy(buf, &packetHead, sizeof(packetHead));
427
428 if (packetHead.packetType == RS_ALIVE_PACKET)
429     {
430     return false;
431     }
432
433 RS_PACKET_TAIL packetTail;
434
435 memset(packetTail.padding, 0, sizeof(packetTail.padding));
436 strcpy((char*)packetTail.magic, RS_ID);
437 vector<string>::const_iterator it;
438 std::string params;
439 for(it = rsSettings.GetUserParams().begin();
440     it != rsSettings.GetUserParams().end();
441     ++it)
442     {
443     std::string parameter(GetUserParam(rsu.user, *it));
444     if (params.length() + parameter.length() > RS_PARAMS_LEN - 1)
445         break;
446     params += parameter + " ";
447     }
448 strncpy((char *)packetTail.params, params.c_str(), RS_PARAMS_LEN);
449 packetTail.params[RS_PARAMS_LEN - 1] = 0;
450
451 assert(sizeof(packetHead) + sizeof(packetTail) <= bufSize && "Insufficient buffer space");
452
453 Encrypt(&ctx, buf + sizeof(packetHead), (char *)&packetTail, sizeof(packetTail) / 8);
454
455 return false;
456 }
457 //-----------------------------------------------------------------------------
458 bool REMOTE_SCRIPT::Send(uint32_t ip, RS_USER & rsu, bool forceDisconnect) const
459 {
460 char buffer[RS_MAX_PACKET_LEN];
461
462 memset(buffer, 0, sizeof(buffer));
463
464 if (PreparePacket(buffer, sizeof(buffer), ip, rsu, forceDisconnect))
465     {
466     printfd(__FILE__, "REMOTE_SCRIPT::Send() - Invalid packet length!\n");
467     return true;
468     }
469
470 std::for_each(
471         rsu.routers.begin(),
472         rsu.routers.end(),
473         PacketSender(sock, buffer, sizeof(buffer), htons(rsSettings.GetPort()))
474         );
475
476 return false;
477 }
478 //-----------------------------------------------------------------------------
479 bool REMOTE_SCRIPT::SendDirect(uint32_t ip, RS_USER & rsu, uint32_t routerIP, bool forceDisconnect) const
480 {
481 char buffer[RS_MAX_PACKET_LEN];
482
483 if (PreparePacket(buffer, sizeof(buffer), ip, rsu, forceDisconnect))
484     {
485     printfd(__FILE__, "REMOTE_SCRIPT::SendDirect() - Invalid packet length!\n");
486     return true;
487     }
488
489 struct sockaddr_in sendAddr;
490
491 sendAddr.sin_family = AF_INET;
492 sendAddr.sin_port = htons(rsSettings.GetPort());
493 sendAddr.sin_addr.s_addr = routerIP;
494
495 int res = sendto(sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&sendAddr, sizeof(sendAddr));
496
497 return (res != sizeof(buffer));
498 }
499 //-----------------------------------------------------------------------------
500 bool REMOTE_SCRIPT::GetUsers()
501 {
502 user_iter u;
503
504 int h = users->OpenSearch();
505 if (!h)
506     {
507     errorStr = "users->OpenSearch() error.";
508     printfd(__FILE__, "OpenSearch() error\n");
509     return true;
510     }
511
512 while (!users->SearchNext(h, &u))
513     {
514     SetUserNotifier(u);
515     }
516
517 users->CloseSearch(h);
518 return false;
519 }
520 //-----------------------------------------------------------------------------
521 void REMOTE_SCRIPT::ChangedIP(user_iter u, uint32_t oldIP, uint32_t newIP)
522 {
523 /*
524  * When ip changes process looks like:
525  * old => 0, 0 => new
526  *
527  */
528 if (newIP)
529     {
530     RS_USER rsu(IP2Routers(newIP), u);
531     Send(newIP, rsu);
532
533     STG_LOCKER lock(&mutex, __FILE__, __LINE__);
534     authorizedUsers[newIP] = rsu;
535     }
536 else
537     {
538     STG_LOCKER lock(&mutex, __FILE__, __LINE__);
539     const map<uint32_t, RS_USER>::iterator it(
540             authorizedUsers.find(oldIP)
541             );
542     if (it != authorizedUsers.end())
543         {
544         Send(oldIP, it->second, true);
545         authorizedUsers.erase(it);
546         }
547     }
548 }
549 //-----------------------------------------------------------------------------
550 std::vector<uint32_t> REMOTE_SCRIPT::IP2Routers(uint32_t ip)
551 {
552 STG_LOCKER lock(&mutex, __FILE__, __LINE__);
553 for (size_t i = 0; i < netRouters.size(); ++i)
554     {
555     if ((ip & netRouters[i].subnetMask) == (netRouters[i].subnetIP & netRouters[i].subnetMask))
556         {
557         return netRouters[i].routers;
558         }
559     }
560 return std::vector<uint32_t>();
561 }
562 //-----------------------------------------------------------------------------
563 string REMOTE_SCRIPT::GetUserParam(user_iter u, const string & paramName) const
564 {
565 string value = "";
566 if (strcasecmp(paramName.c_str(), "cash") == 0)
567     strprintf(&value, "%f", u->property.cash.Get());
568 else
569 if (strcasecmp(paramName.c_str(), "freeMb") == 0)
570     strprintf(&value, "%f", u->property.freeMb.Get());
571 else
572 if (strcasecmp(paramName.c_str(), "passive") == 0)
573     strprintf(&value, "%d", u->property.passive.Get());
574 else
575 if (strcasecmp(paramName.c_str(), "disabled") == 0)
576     strprintf(&value, "%d", u->property.disabled.Get());
577 else
578 if (strcasecmp(paramName.c_str(), "alwaysOnline") == 0)
579     strprintf(&value, "%d", u->property.alwaysOnline.Get());
580 else
581 if (strcasecmp(paramName.c_str(), "tariffName") == 0 ||
582     strcasecmp(paramName.c_str(), "tariff") == 0)
583     value = "\"" + u->property.tariffName.Get() + "\"";
584 else
585 if (strcasecmp(paramName.c_str(), "nextTariff") == 0)
586     value = "\"" + u->property.nextTariff.Get() + "\"";
587 else
588 if (strcasecmp(paramName.c_str(), "address") == 0)
589     value = "\"" + u->property.address.Get() + "\"";
590 else
591 if (strcasecmp(paramName.c_str(), "note") == 0)
592     value = "\"" + u->property.note.Get() + "\"";
593 else
594 if (strcasecmp(paramName.c_str(), "group") == 0)
595     value = "\"" + u->property.group.Get() + "\"";
596 else
597 if (strcasecmp(paramName.c_str(), "email") == 0)
598     value = "\"" + u->property.email.Get() + "\"";
599 else
600 if (strcasecmp(paramName.c_str(), "realName") == 0)
601     value = "\"" + u->property.realName.Get() + "\"";
602 else
603 if (strcasecmp(paramName.c_str(), "credit") == 0)
604     strprintf(&value, "%f", u->property.credit.Get());
605 else
606 if (strcasecmp(paramName.c_str(), "userdata0") == 0)
607     value = "\"" + u->property.userdata0.Get() + "\"";
608 else
609 if (strcasecmp(paramName.c_str(), "userdata1") == 0)
610     value = "\"" + u->property.userdata1.Get() + "\"";
611 else
612 if (strcasecmp(paramName.c_str(), "userdata2") == 0)
613     value = "\"" + u->property.userdata2.Get() + "\"";
614 else
615 if (strcasecmp(paramName.c_str(), "userdata3") == 0)
616     value = "\"" + u->property.userdata3.Get() + "\"";
617 else
618 if (strcasecmp(paramName.c_str(), "userdata4") == 0)
619     value = "\"" + u->property.userdata4.Get() + "\"";
620 else
621 if (strcasecmp(paramName.c_str(), "userdata5") == 0)
622     value = "\"" + u->property.userdata5.Get() + "\"";
623 else
624 if (strcasecmp(paramName.c_str(), "userdata6") == 0)
625     value = "\"" + u->property.userdata6.Get() + "\"";
626 else
627 if (strcasecmp(paramName.c_str(), "userdata7") == 0)
628     value = "\"" + u->property.userdata7.Get() + "\"";
629 else
630 if (strcasecmp(paramName.c_str(), "userdata8") == 0)
631     value = "\"" + u->property.userdata8.Get() + "\"";
632 else
633 if (strcasecmp(paramName.c_str(), "userdata9") == 0)
634     value = "\"" + u->property.userdata9.Get() + "\"";
635 else
636 if (strcasecmp(paramName.c_str(), "enabledDirs") == 0)
637     value = u->GetEnabledDirs();
638 else
639     printfd(__FILE__, "Unknown value name: %s\n", paramName.c_str());
640 return value;
641 }
642 //-----------------------------------------------------------------------------
643 void REMOTE_SCRIPT::SetUserNotifier(user_iter u)
644 {
645 RS_CHG_AFTER_NOTIFIER<uint32_t> afterChgIPNotifier(*this, u);
646
647 afterChgIPNotifierList.push_front(afterChgIPNotifier);
648
649 u->AddCurrIPAfterNotifier(&(*afterChgIPNotifierList.begin()));
650 }
651 //-----------------------------------------------------------------------------
652 void REMOTE_SCRIPT::UnSetUserNotifier(user_iter u)
653 {
654 list<RS_CHG_AFTER_NOTIFIER<uint32_t> >::iterator  ipAIter;
655 std::list<list<RS_CHG_AFTER_NOTIFIER<uint32_t> >::iterator> toErase;
656
657 for (ipAIter = afterChgIPNotifierList.begin(); ipAIter != afterChgIPNotifierList.end(); ++ipAIter)
658     {
659     if (ipAIter->GetUser() == u)
660         {
661         u->DelCurrIPAfterNotifier(&(*ipAIter));
662         toErase.push_back(ipAIter);
663         }
664     }
665
666 std::list<list<RS_CHG_AFTER_NOTIFIER<uint32_t> >::iterator>::iterator eIter;
667
668 for (eIter = toErase.begin(); eIter != toErase.end(); ++eIter)
669     {
670     afterChgIPNotifierList.erase(*eIter);
671     }
672 }
673 //-----------------------------------------------------------------------------
674 template <typename varParamType>
675 void RS_CHG_AFTER_NOTIFIER<varParamType>::Notify(const varParamType & oldValue, const varParamType & newValue)
676 {
677 rs.ChangedIP(user, oldValue, newValue);
678 }
679 //-----------------------------------------------------------------------------
680 void REMOTE_SCRIPT::InitEncrypt(BLOWFISH_CTX * ctx, const string & password) const
681 {
682 unsigned char keyL[PASSWD_LEN];  // Пароль для шифровки
683 memset(keyL, 0, PASSWD_LEN);
684 strncpy((char *)keyL, password.c_str(), PASSWD_LEN);
685 Blowfish_Init(ctx, keyL, PASSWD_LEN);
686 }
687 //-----------------------------------------------------------------------------
688 void REMOTE_SCRIPT::Encrypt(BLOWFISH_CTX * ctx, char * dst, const char * src, size_t len8) const
689 {
690 if (dst != src)
691     memcpy(dst, src, len8 * 8);
692 for (size_t i = 0; i < len8; ++i)
693     Blowfish_Encrypt(ctx, (uint32_t *)(dst + i * 8), (uint32_t *)(dst + i * 8 + 4));
694 }
695 //-----------------------------------------------------------------------------