1 /*****************************************************************************
2 * PokerTH - The open source texas holdem engine *
3 * Copyright (C) 2006-2012 Felix Hammer, Florian Thauer, Lothar May *
4 * *
5 * This program is free software: you can redistribute it and/or modify *
6 * it under the terms of the GNU Affero General Public License as *
7 * published by the Free Software Foundation, either version 3 of the *
8 * License, or (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU Affero General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU Affero General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17 * *
18 * *
19 * Additional permission under GNU AGPL version 3 section 7 *
20 * *
21 * If you modify this program, or any covered work, by linking or *
22 * combining it with the OpenSSL project's OpenSSL library (or a *
23 * modified version of that library), containing parts covered by the *
24 * terms of the OpenSSL or SSLeay licenses, the authors of PokerTH *
25 * (Felix Hammer, Florian Thauer, Lothar May) grant you additional *
26 * permission to convey the resulting work. *
27 * Corresponding Source for a non-source form of such a combination *
28 * shall include the source code for the parts of OpenSSL used as well *
29 * as that of the covered work. *
30 *****************************************************************************/
31
32 #include <net/sessiondata.h>
33 #include <net/asioreceivebuffer.h>
34 #include <net/webreceivebuffer.h>
35 #include <net/asiosendbuffer.h>
36 #include <net/websendbuffer.h>
37 #include <net/socket_msg.h>
38 #include <net/websocketdata.h>
39 #include <gsasl.h>
40
41 using namespace std;
42 using boost::asio::ip::tcp;
43
44 #ifdef BOOST_ASIO_HAS_STD_CHRONO
45 using namespace std::chrono;
46 #else
47 using namespace boost::chrono;
48 #endif
49
SessionData(boost::shared_ptr<boost::asio::ip::tcp::socket> sock,SessionId id,SessionDataCallback & cb,boost::asio::io_service & ioService)50 SessionData::SessionData(boost::shared_ptr<boost::asio::ip::tcp::socket> sock, SessionId id, SessionDataCallback &cb, boost::asio::io_service &ioService)
51 : m_socket(sock), m_id(id), m_state(SessionData::Init), m_readyFlag(false), m_wantsLobbyMsg(true),
52 m_activityTimeoutSec(0), m_activityWarningRemainingSec(0), m_initTimeoutTimer(ioService), m_globalTimeoutTimer(ioService),
53 m_activityTimeoutTimer(ioService), m_callback(cb), m_authSession(NULL), m_curAuthStep(0)
54 {
55 m_receiveBuffer.reset(new AsioReceiveBuffer);
56 m_sendBuffer.reset(new AsioSendBuffer);
57 }
58
SessionData(boost::shared_ptr<WebSocketData> webData,SessionId id,SessionDataCallback & cb,boost::asio::io_service & ioService,int)59 SessionData::SessionData(boost::shared_ptr<WebSocketData> webData, SessionId id, SessionDataCallback &cb, boost::asio::io_service &ioService, int /*filler*/)
60 : m_webData(webData), m_id(id), m_state(SessionData::Init), m_readyFlag(false), m_wantsLobbyMsg(true),
61 m_activityTimeoutSec(0), m_activityWarningRemainingSec(0), m_initTimeoutTimer(ioService), m_globalTimeoutTimer(ioService),
62 m_activityTimeoutTimer(ioService), m_callback(cb), m_authSession(NULL), m_curAuthStep(0)
63 {
64 m_receiveBuffer.reset(new WebReceiveBuffer);
65 m_sendBuffer.reset(new WebSendBuffer);
66 }
67
~SessionData()68 SessionData::~SessionData()
69 {
70 InternalClearAuthSession();
71 // Web Socket handle needs to be manually closed, asio socket is closed automatically.
72 CloseWebSocketHandle();
73 }
74
75 SessionId
GetId() const76 SessionData::GetId() const
77 {
78 // const value - no mutex needed.
79 return m_id;
80 }
81
82 boost::shared_ptr<ServerGame>
GetGame() const83 SessionData::GetGame() const
84 {
85 boost::mutex::scoped_lock lock(m_dataMutex);
86 return m_game.lock();
87 }
88
89 void
SetGame(boost::shared_ptr<ServerGame> game)90 SessionData::SetGame(boost::shared_ptr<ServerGame> game)
91 {
92 boost::mutex::scoped_lock lock(m_dataMutex);
93 m_game = game;
94 }
95
96 SessionData::State
GetState() const97 SessionData::GetState() const
98 {
99 boost::mutex::scoped_lock lock(m_dataMutex);
100 return m_state;
101 }
102
103 void
SetState(SessionData::State state)104 SessionData::SetState(SessionData::State state)
105 {
106 boost::mutex::scoped_lock lock(m_dataMutex);
107 m_state = state;
108 }
109
110 boost::shared_ptr<boost::asio::ip::tcp::socket>
GetAsioSocket()111 SessionData::GetAsioSocket()
112 {
113 return m_socket;
114 }
115
116 boost::shared_ptr<WebSocketData>
GetWebData()117 SessionData::GetWebData()
118 {
119 return m_webData;
120 }
121
122 bool
CreateServerAuthSession(Gsasl * context)123 SessionData::CreateServerAuthSession(Gsasl *context)
124 {
125 boost::mutex::scoped_lock lock(m_dataMutex);
126 InternalClearAuthSession();
127 int errorCode;
128 errorCode = gsasl_server_start(context, "SCRAM-SHA-1", &m_authSession);
129 return errorCode == GSASL_OK;
130 }
131
132 bool
CreateClientAuthSession(Gsasl * context,const string & userName,const string & password)133 SessionData::CreateClientAuthSession(Gsasl *context, const string &userName, const string &password)
134 {
135 bool retVal = false;
136 boost::mutex::scoped_lock lock(m_dataMutex);
137 InternalClearAuthSession();
138 int errorCode;
139 errorCode = gsasl_client_start(context, "SCRAM-SHA-1", &m_authSession);
140 if (errorCode == GSASL_OK) {
141 gsasl_property_set(m_authSession, GSASL_AUTHID, userName.c_str());
142 gsasl_property_set(m_authSession, GSASL_PASSWORD, password.c_str());
143
144 retVal = true;
145 }
146 return retVal;
147 }
148
149 bool
AuthStep(int stepNum,const std::string & inData)150 SessionData::AuthStep(int stepNum, const std::string &inData)
151 {
152 bool retVal = false;
153 boost::mutex::scoped_lock lock(m_dataMutex);
154 if (m_authSession && stepNum == m_curAuthStep + 1) {
155 m_curAuthStep = stepNum;
156 char *tmpOut;
157 size_t tmpOutSize;
158 int errorCode = gsasl_step(m_authSession, inData.c_str(), inData.length(), &tmpOut, &tmpOutSize);
159 if (errorCode == GSASL_NEEDS_MORE) {
160 m_nextGsaslMsg = string(tmpOut, tmpOutSize);
161 retVal = true;
162 } else if (errorCode == GSASL_OK && stepNum != 1) {
163 m_nextGsaslMsg = string(tmpOut, tmpOutSize);
164 retVal = true;
165 InternalClearAuthSession();
166 }
167 gsasl_free(tmpOut);
168 }
169 return retVal;
170 }
171
172 string
AuthGetUser() const173 SessionData::AuthGetUser() const
174 {
175 string retStr;
176 if (m_authSession) {
177 const char *tmpUser = gsasl_property_fast(m_authSession, GSASL_AUTHID);
178 if (tmpUser)
179 retStr = tmpUser;
180 }
181 return retStr;
182 }
183
184 void
AuthSetPassword(const std::string & password)185 SessionData::AuthSetPassword(const std::string &password)
186 {
187 if (m_authSession)
188 gsasl_property_set(m_authSession, GSASL_PASSWORD, password.c_str());
189 m_password = password;
190 }
191
192 string
AuthGetPassword() const193 SessionData::AuthGetPassword() const
194 {
195 return m_password;
196 }
197
198 string
AuthGetNextOutMsg() const199 SessionData::AuthGetNextOutMsg() const
200 {
201 return m_nextGsaslMsg;
202 }
203
204 int
AuthGetCurStepNum() const205 SessionData::AuthGetCurStepNum() const
206 {
207 return m_curAuthStep;
208 }
209
210 void
InternalClearAuthSession()211 SessionData::InternalClearAuthSession()
212 {
213 if (m_authSession) {
214 gsasl_finish(m_authSession);
215 m_authSession = NULL;
216 m_curAuthStep = 0;
217 }
218 }
219
220 void
TimerInitTimeout(const boost::system::error_code & ec)221 SessionData::TimerInitTimeout(const boost::system::error_code &ec)
222 {
223 if (!ec) {
224 if (GetState() == SessionData::Init) {
225 m_callback.SessionError(shared_from_this(), ERR_NET_SESSION_TIMED_OUT);
226 }
227 }
228 }
229
230 void
TimerSessionTimeout(const boost::system::error_code & ec)231 SessionData::TimerSessionTimeout(const boost::system::error_code &ec)
232 {
233 if (!ec) {
234 m_callback.SessionError(shared_from_this(), ERR_NET_SESSION_TIMED_OUT);
235 }
236 }
237
238 void
TimerActivityWarning(const boost::system::error_code & ec)239 SessionData::TimerActivityWarning(const boost::system::error_code &ec)
240 {
241 if (!ec) {
242 m_callback.SessionTimeoutWarning(shared_from_this(), m_activityWarningRemainingSec);
243
244 m_activityTimeoutTimer.expires_from_now(
245 seconds(m_activityWarningRemainingSec));
246 m_activityTimeoutTimer.async_wait(
247 boost::bind(
248 &SessionData::TimerSessionTimeout, shared_from_this(), boost::asio::placeholders::error));
249 }
250 }
251
252 void
SetReadyFlag()253 SessionData::SetReadyFlag()
254 {
255 boost::mutex::scoped_lock lock(m_dataMutex);
256 m_readyFlag = true;
257 }
258
259 void
ResetReadyFlag()260 SessionData::ResetReadyFlag()
261 {
262 boost::mutex::scoped_lock lock(m_dataMutex);
263 m_readyFlag = false;
264 }
265
266 bool
IsReady() const267 SessionData::IsReady() const
268 {
269 boost::mutex::scoped_lock lock(m_dataMutex);
270 return m_readyFlag;
271 }
272
273 void
SetWantsLobbyMsg()274 SessionData::SetWantsLobbyMsg()
275 {
276 boost::mutex::scoped_lock lock(m_dataMutex);
277 m_wantsLobbyMsg = true;
278 }
279
280 void
ResetWantsLobbyMsg()281 SessionData::ResetWantsLobbyMsg()
282 {
283 boost::mutex::scoped_lock lock(m_dataMutex);
284 m_wantsLobbyMsg = false;
285 }
286
287 bool
WantsLobbyMsg() const288 SessionData::WantsLobbyMsg() const
289 {
290 boost::mutex::scoped_lock lock(m_dataMutex);
291 return m_wantsLobbyMsg;
292 }
293
294 const std::string &
GetClientAddr() const295 SessionData::GetClientAddr() const
296 {
297 boost::mutex::scoped_lock lock(m_dataMutex);
298 return m_clientAddr;
299 }
300
301 void
SetClientAddr(const std::string & addr)302 SessionData::SetClientAddr(const std::string &addr)
303 {
304 boost::mutex::scoped_lock lock(m_dataMutex);
305 m_clientAddr = addr;
306 }
307
308 void
CloseSocketHandle()309 SessionData::CloseSocketHandle()
310 {
311 if (m_socket) {
312 boost::system::error_code ec;
313 m_socket->close(ec);
314 }
315 }
316
317 void
CloseWebSocketHandle()318 SessionData::CloseWebSocketHandle()
319 {
320 if (m_webData) {
321 #if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) // c++11
322 std::error_code std_ec;
323 m_webData->webSocketServer->close(m_webData->webHandle, websocketpp::close::status::normal, "PokerTH server closed the connection.", std_ec);
324 #else
325 boost::system::error_code ec;
326 m_webData->webSocketServer->close(m_webData->webHandle, websocketpp::close::status::normal, "PokerTH server closed the connection.", ec);
327 #endif
328 }
329 }
330
331 void
ResetActivityTimer()332 SessionData::ResetActivityTimer()
333 {
334 boost::mutex::scoped_lock lock(m_dataMutex);
335 m_activityTimeoutTimer.expires_from_now(
336 seconds(m_activityTimeoutSec - m_activityWarningRemainingSec));
337 m_activityTimeoutTimer.async_wait(
338 boost::bind(
339 &SessionData::TimerActivityWarning, shared_from_this(), boost::asio::placeholders::error));
340 }
341
342 void
StartTimerInitTimeout(unsigned timeoutSec)343 SessionData::StartTimerInitTimeout(unsigned timeoutSec)
344 {
345 boost::mutex::scoped_lock lock(m_dataMutex);
346 m_initTimeoutTimer.expires_from_now(
347 seconds(timeoutSec));
348 m_initTimeoutTimer.async_wait(
349 boost::bind(
350 &SessionData::TimerInitTimeout, shared_from_this(), boost::asio::placeholders::error));
351 }
352
353 void
StartTimerGlobalTimeout(unsigned timeoutSec)354 SessionData::StartTimerGlobalTimeout(unsigned timeoutSec)
355 {
356 boost::mutex::scoped_lock lock(m_dataMutex);
357 m_globalTimeoutTimer.expires_from_now(
358 seconds(timeoutSec));
359 m_globalTimeoutTimer.async_wait(
360 boost::bind(
361 &SessionData::TimerSessionTimeout, shared_from_this(), boost::asio::placeholders::error));
362 }
363
364 void
StartTimerActivityTimeout(unsigned timeoutSec,unsigned warningRemainingSec)365 SessionData::StartTimerActivityTimeout(unsigned timeoutSec, unsigned warningRemainingSec)
366 {
367 boost::mutex::scoped_lock lock(m_dataMutex);
368 m_activityTimeoutSec = timeoutSec;
369 m_activityWarningRemainingSec = warningRemainingSec;
370
371 m_activityTimeoutTimer.expires_from_now(
372 seconds(timeoutSec - warningRemainingSec));
373 m_activityTimeoutTimer.async_wait(
374 boost::bind(
375 &SessionData::TimerActivityWarning, shared_from_this(), boost::asio::placeholders::error));
376 }
377
378 void
CancelTimers()379 SessionData::CancelTimers()
380 {
381 boost::mutex::scoped_lock lock(m_dataMutex);
382 m_initTimeoutTimer.cancel();
383 m_globalTimeoutTimer.cancel();
384 m_activityTimeoutTimer.cancel();
385 }
386
387 void
SetPlayerData(boost::shared_ptr<PlayerData> player)388 SessionData::SetPlayerData(boost::shared_ptr<PlayerData> player)
389 {
390 boost::mutex::scoped_lock lock(m_dataMutex);
391 m_playerData = player;
392 }
393
394 boost::shared_ptr<PlayerData>
GetPlayerData()395 SessionData::GetPlayerData()
396 {
397 boost::mutex::scoped_lock lock(m_dataMutex);
398 return m_playerData;
399 }
400
401 string
GetRemoteIPAddressFromSocket() const402 SessionData::GetRemoteIPAddressFromSocket() const
403 {
404 boost::mutex::scoped_lock lock(m_dataMutex);
405 string ipAddress;
406 if (m_socket) {
407 boost::system::error_code errCode;
408 tcp::endpoint clientEndpoint = m_socket->remote_endpoint(errCode);
409 if (!errCode) {
410 ipAddress = clientEndpoint.address().to_string(errCode);
411 }
412 } else {
413 boost::system::error_code errCode;
414 server::connection_ptr con = m_webData->webSocketServer->get_con_from_hdl(m_webData->webHandle);
415 tcp::endpoint webClientEndpoint = con->get_raw_socket().remote_endpoint(errCode);
416 if (!errCode) {
417 ipAddress = webClientEndpoint.address().to_string(errCode);
418 }
419 }
420 return ipAddress;
421 }
422
423