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