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