1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "nel/misc/system_info.h"
21 #include "nel/net/callback_client.h"
23 #include "nel/net/login_cookie.h"
24 #include "nel/net/login_client.h"
26 #include "nel/net/udp_sock.h"
29 using namespace NLMISC
;
34 CLoginClient::TShardList
CLoginClient::ShardList
;
35 CCallbackClient
*CLoginClient::_LSCallbackClient
;
39 // CALLBACK FROM THE FS (Front-end Service)
42 // Callback for answer of the request shard
43 static bool ShardValidate
;
44 static string ShardValidateReason
;
45 static void cbShardValidate (CMessage
&msgin
, TSockId from
, CCallbackNetBase
&netbase
)
48 // S14: receive "SV" message from FES
51 msgin
.serial (ShardValidateReason
);
55 static TCallbackItem FESCallbackArray
[] =
57 { "SV", cbShardValidate
},
61 // CALLBACK FROM THE LS (Login Service)
64 // Callback for answer of the login password.
65 static bool VerifyLoginPassword
;
66 static string VerifyLoginPasswordReason
;
67 static void cbVerifyLoginPassword (CMessage
&msgin
, TSockId from
, CCallbackNetBase
&netbase
)
70 // S04: receive the "VLP" message from LS
73 msgin
.serial (VerifyLoginPasswordReason
);
74 if(VerifyLoginPasswordReason
.empty())
77 msgin
.serial (nbshard
);
79 CLoginClient::ShardList
.clear ();
80 VerifyLoginPasswordReason
.clear();
83 for (uint i
= 0; i
< nbshard
; i
++)
85 CLoginClient::CShardEntry se
;
86 msgin
.serial (se
.Name
, se
.NbPlayers
, se
.Id
);
87 CLoginClient::ShardList
.push_back (se
);
90 VerifyLoginPassword
= true;
93 // Callback for answer of the request shard
94 static bool ShardChooseShard
;
95 static string ShardChooseShardReason
;
96 static string ShardChooseShardAddr
;
97 static string ShardChooseShardCookie
;
98 static void cbShardChooseShard (CMessage
&msgin
, TSockId from
, CCallbackNetBase
&netbase
)
101 // S11: receive "SCS" message from LS
104 msgin
.serial (ShardChooseShardReason
);
106 if (ShardChooseShardReason
.empty())
108 msgin
.serial (ShardChooseShardCookie
);
109 msgin
.serial (ShardChooseShardAddr
);
111 ShardChooseShard
= true;
114 static TCallbackItem LSCallbackArray
[] =
116 { "VLP", cbVerifyLoginPassword
},
117 { "SCS", cbShardChooseShard
},
120 string
CLoginClient::authenticate(const string
&loginServiceAddr
, const ucstring
&login
, const string
&cpassword
, const string
&application
)
122 string result
= authenticateBegin(loginServiceAddr
, login
, cpassword
, application
);
123 if (!result
.empty()) return result
;
124 while (CLoginClient::authenticateUpdate(result
)) nlSleep(10);
128 string
CLoginClient::authenticateBegin(const string
&loginServiceAddr
, const ucstring
&login
, const string
&cpassword
, const string
&application
)
130 VerifyLoginPasswordReason
.clear();
131 VerifyLoginPassword
= false;
133 // S01: connect to the LS
136 if(_LSCallbackClient
== 0)
138 _LSCallbackClient
= new CCallbackClient();
139 _LSCallbackClient
->addCallbackArray(LSCallbackArray
, sizeof(LSCallbackArray
) / sizeof(LSCallbackArray
[0]));
142 string addr
= loginServiceAddr
;
143 if(addr
.find(":") == string::npos
)
145 if(_LSCallbackClient
->connected())
146 _LSCallbackClient
->disconnect();
147 _LSCallbackClient
->connect (CInetAddress(addr
));
149 catch (const ESocket
&e
)
151 delete _LSCallbackClient
;
152 _LSCallbackClient
= 0;
153 nlwarning("Connection refused to LS (addr:%s): %s", loginServiceAddr
.c_str(), e
.what());
154 return toString("Connection refused to LS (addr:%s): %s", loginServiceAddr
.c_str(), e
.what());
157 // S02: create and send the "VLP" message
158 CMessage
msgout("VLP");
159 msgout
.serial(const_cast<ucstring
&>(login
));
160 msgout
.serial(const_cast<string
&>(cpassword
));
161 msgout
.serial(const_cast<string
&>(application
));
162 _LSCallbackClient
->send(msgout
);
167 // returns true if it needs to be called again
168 // error not empty if something went wrong
169 bool CLoginClient::authenticateUpdate(string
&error
)
171 if (!_LSCallbackClient
)
173 error
= "CLoginClient::authenticateBegin() must be called first";
174 nlwarning("CLoginClient::authenticateUpdate(): %s", error
.c_str());
177 if (!_LSCallbackClient
->connected())
179 error
= "Disconnected from LS";
180 nlwarning("CLoginClient::authenticateUpdate(): %s", error
.c_str());
181 delete _LSCallbackClient
;
182 _LSCallbackClient
= 0;
185 _LSCallbackClient
->update();
186 if (VerifyLoginPassword
)
188 error
= VerifyLoginPasswordReason
;
191 nlwarning("CLoginClient::authenticateUpdate(): %s", error
.c_str());
192 _LSCallbackClient
->disconnect ();
193 delete _LSCallbackClient
;
194 _LSCallbackClient
= 0;
198 return true; // no news, try again
201 string
CLoginClient::connectToShard(CLoginCookie
&lc
, const std::string
&addr
, CCallbackClient
&cnx
)
203 nlassert (!cnx
.connected());
208 // S12: connect to the FES and send "SV" message to the FES
210 cnx
.connect (CInetAddress(addr
));
211 cnx
.addCallbackArray (FESCallbackArray
, sizeof(FESCallbackArray
)/sizeof(FESCallbackArray
[0]));
214 CMessage
msgout2 ("SV");
218 // wait the answer of the connection
219 ShardValidate
= false;
220 while (cnx
.connected() && !ShardValidate
)
226 // have we received the answer?
227 if (!ShardValidate
) return "FES disconnect me";
229 catch (const ESocket
&e
)
231 return string("FES refused the connection (") + e
.what () + ")";
234 return ShardValidateReason
;
237 string
CLoginClient::connectToShard (const std::string
&addr
, CUdpSock
&cnx
)
239 nlassert (!cnx
.connected());
244 // S12: connect to the FES. Note: In UDP mode, it's the user that have to send the cookie to the front end
246 // If a personal firewall such as ZoneAlarm is installed and permission not granted yet,
247 // the connect blocks until the user makes a choice.
248 // If the user denies the connection, the exception ESocket is thrown.
249 // Other firewalls such as Kerio make the send() fail instead.
251 cnx
.connect (CInetAddress(addr
));
253 catch (const ESocket
&e
)
255 return string("FES refused the connection (") + e
.what () + ")";
258 return ShardValidateReason
;
261 string
CLoginClient::connectToShard (const std::string
&addr
, CUdpSimSock
&cnx
)
263 nlassert (!cnx
.connected());
269 // S12: connect to the FES. Note: In UDP mode, it's the user that have to send the cookie to the front end
271 // See firewall comment in connectToShard(string,CUdpSock)
273 cnx
.connect (CInetAddress(addr
));
275 catch (const ESocket
&e
)
277 return string("FES refused the connection (") + e
.what () + ")";
280 return ShardValidateReason
;
283 string
CLoginClient::confirmConnection(sint32 shardId
)
285 nlassert(_LSCallbackClient
!= 0 && _LSCallbackClient
->connected());
288 // S05: create and send the "CS" message with the shardid choice to the LS
291 if (ShardList
.empty())
293 _LSCallbackClient
->disconnect();
294 return "No shard available";
297 CLoginClient::CShardEntry
*s
= getShard(shardId
);
300 _LSCallbackClient
->disconnect();
301 return "Invalid shard selected";
305 CMessage
msgout ("CS");
306 msgout
.serial (s
->Id
);
307 _LSCallbackClient
->send (msgout
);
310 ShardChooseShard
= false;
311 while (_LSCallbackClient
->connected() && !ShardChooseShard
)
313 _LSCallbackClient
->update ();
317 // have we received the answer?
318 if (!ShardChooseShard
)
320 delete _LSCallbackClient
;
321 _LSCallbackClient
= 0;
322 return "CLoginClientMtp::confirmConnection(): LS disconnects me";
326 _LSCallbackClient
->disconnect ();
327 delete _LSCallbackClient
;
328 _LSCallbackClient
= 0;
331 if (!ShardChooseShardReason
.empty())
333 return ShardChooseShardReason
;
336 // ok, we can try to connect to the good front end
338 nlinfo("addr: '%s' cookie: %s", ShardChooseShardAddr
.c_str(), ShardChooseShardCookie
.c_str());
343 string
CLoginClient::wantToConnectToShard(sint32 shardId
, string
&ip
, string
&cookie
)
345 string res
= confirmConnection(shardId
);
346 if (!res
.empty()) return res
;
348 ip
= ShardChooseShardAddr
;
349 cookie
= ShardChooseShardCookie
;
354 string
CLoginClient::selectShardBegin(sint32 shardId
)
356 nlassert(_LSCallbackClient
!= 0 && _LSCallbackClient
->connected());
358 ShardChooseShardReason
.clear();
359 ShardChooseShard
= false;
361 if (ShardList
.empty())
363 _LSCallbackClient
->disconnect();
364 delete _LSCallbackClient
;
365 _LSCallbackClient
= 0;
366 return "No shard available";
368 CLoginClient::CShardEntry
*s
= getShard(shardId
);
371 _LSCallbackClient
->disconnect();
372 delete _LSCallbackClient
;
373 _LSCallbackClient
= 0;
374 return "Invalid shard selected";
377 // S05: create and send the "CS" message with the shardid choice to the LS
378 CMessage
msgout ("CS");
379 msgout
.serial (s
->Id
);
380 _LSCallbackClient
->send (msgout
);
385 bool CLoginClient::selectShardUpdate(string
&error
, string
&ip
, string
&cookie
)
387 if (!_LSCallbackClient
)
389 error
= "CLoginClient::selectShardBegin() must be called first";
390 nlwarning("CLoginClient::selectShardUpdate(): %s", error
.c_str());
393 if (!_LSCallbackClient
->connected())
395 error
= "Disconnected from LS";
396 nlwarning("CLoginClient::selectShardUpdate(): %s", error
.c_str());
397 delete _LSCallbackClient
;
398 _LSCallbackClient
= 0;
401 _LSCallbackClient
->update();
402 if (ShardChooseShard
)
404 error
= ShardChooseShardReason
;
405 ip
= ShardChooseShardAddr
;
406 cookie
= ShardChooseShardCookie
;
407 if (!error
.empty()) nlwarning("CLoginClient::selectShardUpdate(): %s", error
.c_str());
408 else nlinfo("addr: '%s' cookie: %s", ShardChooseShardAddr
.c_str(), ShardChooseShardCookie
.c_str());
409 _LSCallbackClient
->disconnect ();
410 delete _LSCallbackClient
;
411 _LSCallbackClient
= 0;
417 CLoginClient::CShardEntry
*CLoginClient::getShard (sint32 shardId
)
419 for(TShardList::iterator it
=ShardList
.begin();it
!=ShardList
.end();it
++)
421 if((*it
).Id
== shardId
)