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 // Load test program for PokerTH
33
34 #include <third_party/asn1/PokerTHMessage.h>
35 #include <boost/program_options.hpp>
36 #include <boost/asio.hpp>
37 #include <boost/thread.hpp>
38 #include <gsasl.h>
39
40 #include <iostream>
41 #include <sstream>
42
43 #define STL_STRING_FROM_OCTET_STRING(_a) (string((const char *)(_a).buf, (_a).size))
44
45 using namespace std;
46 using boost::asio::ip::tcp;
47 namespace po = boost::program_options;
48
49
50 #define BUF_SIZE 1024
51
52 struct NetSession {
NetSessionNetSession53 NetSession(boost::asio::io_service &ioService) : recBufPos(0), authSession(NULL), socket(ioService) {}
54 boost::array<char, BUF_SIZE> recBuf;
55 size_t recBufPos;
56 boost::array<char, BUF_SIZE> sendBuf;
57 Gsasl_session *authSession;
58 tcp::socket socket;
59 string name;
60 };
61
62 static int
net_packet_print_to_string(const void * buffer,size_t size,void * packetStr)63 net_packet_print_to_string(const void *buffer, size_t size, void *packetStr)
64 {
65 string *tmpString = (string *)packetStr;
66 *tmpString += string((const char *)buffer, size);
67 return 0;
68 }
69
70 /*string packetString;
71 xer_encode(&asn_DEF_PokerTHMessage, msg, XER_F_BASIC, &net_packet_print_to_string, &packetString);
72 cout << packetString << endl;*/
73
74 PokerTHMessage_t *
receiveMessage(NetSession * session)75 receiveMessage(NetSession *session)
76 {
77 PokerTHMessage_t *msg = NULL;
78 do {
79 asn_dec_rval_t retVal = ber_decode(0, &asn_DEF_PokerTHMessage, (void **)&msg, session->recBuf.data(), session->recBufPos);
80 if(retVal.code == RC_OK && msg != NULL) {
81 if (retVal.consumed < session->recBufPos) {
82 session->recBufPos -= retVal.consumed;
83 memmove(session->recBuf.c_array(), session->recBuf.c_array() + retVal.consumed, session->recBufPos);
84 } else {
85 session->recBufPos = 0;
86 }
87 if (asn_check_constraints(&asn_DEF_PokerTHMessage, msg, NULL, NULL) != 0) {
88 cerr << "Invalid packet received:" << endl;
89 string packetString;
90 xer_encode(&asn_DEF_PokerTHMessage, msg, XER_F_BASIC, &net_packet_print_to_string, &packetString);
91 cout << packetString << endl;
92 }
93 } else {
94 // Free the partially decoded message (if applicable).
95 ASN_STRUCT_FREE(asn_DEF_PokerTHMessage, msg);
96 msg = NULL;
97 session->recBufPos += session->socket.receive(boost::asio::buffer(session->recBuf.c_array() + session->recBufPos, BUF_SIZE - session->recBufPos));
98 }
99 } while (msg == NULL);
100 return msg;
101 }
102
103 bool
sendMessage(NetSession * session,PokerTHMessage_t * msg)104 sendMessage(NetSession *session, PokerTHMessage_t *msg)
105 {
106 bool retVal = false;
107 if (msg) {
108 asn_enc_rval_t e = der_encode_to_buffer(&asn_DEF_PokerTHMessage, msg, session->sendBuf.data(), BUF_SIZE);
109 if (e.encoded != -1) {
110 session->socket.send(boost::asio::buffer(session->sendBuf.data(), e.encoded));
111 retVal = true;
112 }
113 ASN_STRUCT_FREE(asn_DEF_PokerTHMessage, msg);
114 }
115 return retVal;
116 }
117
118 int
main(int argc,char * argv[])119 main(int argc, char *argv[])
120 {
121 try {
122 // Check command line options.
123 po::options_description desc("Allowed options");
124 desc.add_options()
125 ("help,h", "produce help message")
126 ("server,s", po::value<string>(), "PokerTH server name")
127 ("port,P", po::value<string>(), "PokerTH server port")
128 ("numGames,n", po::value<unsigned>(), "Number of games to open")
129 ("firstId,f", po::value<int>(), "First id of username testx")
130 ;
131
132 po::variables_map vm;
133 po::store(po::parse_command_line(argc, argv, desc), vm);
134 po::notify(vm);
135
136 if (vm.count("help")) {
137 cout << desc << endl;
138 return 1;
139 }
140 if (!vm.count("server") || !vm.count("port") || !vm.count("numGames") || !vm.count("firstId")) {
141 cout << "Missing option!" << endl << desc << endl;
142 return 1;
143 }
144
145 string server(vm["server"].as<string>());
146 string port = vm["port"].as<string>();
147 unsigned numGames = vm["numGames"].as<unsigned>();
148 int firstId = vm["firstId"].as<int>();
149
150 // Initialise gsasl.
151 Gsasl *authContext;
152 int res = gsasl_init(&authContext);
153 if (res != GSASL_OK) {
154 cout << "gsasl init failed" << endl;
155 return 1;
156 }
157
158 if (!gsasl_client_support_p(authContext, "SCRAM-SHA-1")) {
159 gsasl_done(authContext);
160 cout << "This version of gsasl does not support SCRAM-SHA-1" << endl;
161 return 1;
162 }
163
164 // Connect to the PokerTH server.
165 boost::asio::io_service ioService;
166 tcp::resolver resolver(ioService);
167 tcp::resolver::query query(server, port);
168 tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
169 tcp::resolver::iterator end;
170 boost::system::error_code error = boost::asio::error::host_not_found;
171 tcp::resolver::iterator curEndpoint;
172 tcp::socket tmpSocket(ioService);
173 while (error && endpoint_iterator != end) {
174 curEndpoint = endpoint_iterator++;
175 tmpSocket.connect(*curEndpoint, error);
176 tmpSocket.close();
177 }
178
179 if (error) {
180 cout << "Connect failed" << endl;
181 return 1;
182 }
183
184 PokerTHMessage_t *msg = NULL;
185 int errorCode;
186 char *tmpOut;
187 size_t tmpOutSize;
188 string nextGsaslMsg;
189 NetSession **sessionArray = new NetSession *[numGames * 10];
190 unsigned *gameId = new unsigned[numGames];
191 const int LoginsPerLoop = 50;
192 for (int t = 0; t < (numGames * 10) / LoginsPerLoop + 1; t++) {
193 int startNum = t * LoginsPerLoop;
194 int endNum = (t + 1) * LoginsPerLoop;
195 if (endNum > numGames * 10) {
196 endNum = numGames * 10;
197 }
198 for (int i = startNum; i < endNum; i++) {
199 sessionArray[i] = new NetSession(ioService);
200 NetSession *session = sessionArray[i];
201
202 session->socket.connect(*curEndpoint, error);
203
204 // Receive server information
205 msg = receiveMessage(session);
206 if (!msg || msg->present != PokerTHMessage_PR_announceMessage) {
207 cout << "Announce failed" << endl;
208 return 1;
209 }
210 ASN_STRUCT_FREE(asn_DEF_PokerTHMessage, msg);
211
212 // Send init
213 msg = (PokerTHMessage_t *)calloc(1, sizeof(PokerTHMessage_t));
214 msg->present = PokerTHMessage_PR_initMessage;
215 InitMessage_t *netInit = &msg->choice.initMessage;
216 netInit->requestedVersion.major = 2;
217 netInit->requestedVersion.minor = 0;
218 errorCode = gsasl_client_start(authContext, "SCRAM-SHA-1", &session->authSession);
219 if (errorCode != GSASL_OK) {
220 cout << "Auth start error." << endl;
221 return 1;
222 }
223 ostringstream param;
224 param << "test" << i + firstId;
225 cout << "User " << param.str() << " logging in." << endl;
226 session->name = param.str();
227 gsasl_property_set(session->authSession, GSASL_AUTHID, session->name.c_str());
228 gsasl_property_set(session->authSession, GSASL_PASSWORD, session->name.c_str());
229
230 netInit->login.present = login_PR_authenticatedLogin;
231 AuthenticatedLogin_t *authLogin = &netInit->login.choice.authenticatedLogin;
232
233 errorCode = gsasl_step(session->authSession, NULL, 0, &tmpOut, &tmpOutSize);
234 if (errorCode == GSASL_NEEDS_MORE) {
235 nextGsaslMsg = string(tmpOut, tmpOutSize);
236 } else {
237 cout << "gsasl step 1 failed" << endl;
238 return 1;
239 }
240 gsasl_free(tmpOut);
241
242 OCTET_STRING_fromBuf(&authLogin->clientUserData,
243 nextGsaslMsg.c_str(),
244 nextGsaslMsg.length());
245 if (!sendMessage(session, msg)) {
246 cout << "Init auth request failed" << endl;
247 return 1;
248 }
249 }
250
251 for (int i = startNum; i < endNum; i++) {
252 NetSession *session = sessionArray[i];
253 msg = receiveMessage(session);
254 if (!msg || msg->present != PokerTHMessage_PR_authMessage) {
255 cout << "Auth request failed" << endl;
256 return 1;
257 }
258
259 AuthMessage_t *netAuth = &msg->choice.authMessage;
260 AuthServerChallenge_t *netChallenge = &netAuth->choice.authServerChallenge;
261 string challengeStr = STL_STRING_FROM_OCTET_STRING(netChallenge->serverChallenge);
262 errorCode = gsasl_step(session->authSession, challengeStr.c_str(), challengeStr.size(), &tmpOut, &tmpOutSize);
263 if (errorCode == GSASL_NEEDS_MORE) {
264 nextGsaslMsg = string(tmpOut, tmpOutSize);
265 } else {
266 cout << "gsasl step 2 failed" << endl;
267 return 1;
268 }
269 gsasl_free(tmpOut);
270 ASN_STRUCT_FREE(asn_DEF_PokerTHMessage, msg);
271 msg = (PokerTHMessage_t *)calloc(1, sizeof(PokerTHMessage_t));
272 msg->present = PokerTHMessage_PR_authMessage;
273 AuthMessage_t *outAuth = &msg->choice.authMessage;
274 outAuth->present = AuthMessage_PR_authClientResponse;
275 AuthClientResponse_t *outResponse = &outAuth->choice.authClientResponse;
276
277 OCTET_STRING_fromBuf(&outResponse->clientResponse,
278 nextGsaslMsg.c_str(),
279 nextGsaslMsg.length());
280 if (!sendMessage(session, msg)) {
281 cout << "Init auth response failed" << endl;
282 return 1;
283 }
284 }
285
286 for (int i = startNum; i < endNum; i++) {
287 NetSession *session = sessionArray[i];
288 msg = receiveMessage(session);
289 if (!msg || msg->present != PokerTHMessage_PR_authMessage) {
290 cout << "Auth response failed" << endl;
291 return 1;
292 }
293 ASN_STRUCT_FREE(asn_DEF_PokerTHMessage, msg);
294
295 // Receive init ack
296 msg = receiveMessage(session);
297 if (!msg || msg->present != PokerTHMessage_PR_initAckMessage) {
298 cout << "Init ack failed" << endl;
299 return 1;
300 }
301 ASN_STRUCT_FREE(asn_DEF_PokerTHMessage, msg);
302
303 for (int j = i; j >= 0; j--) {
304 size_t bytes_readable = sessionArray[j]->socket.available();
305 while (bytes_readable > 0) {
306 msg = receiveMessage(sessionArray[j]);
307 ASN_STRUCT_FREE(asn_DEF_PokerTHMessage, msg);
308 bytes_readable = sessionArray[j]->socket.available();
309 }
310 }
311 }
312 }
313
314 for (int g = 0; g < numGames; g++) {
315 NetSession *session = sessionArray[g * 10];
316 // Send create game
317 cout << "Player " << session->name << " creating game " << g+1 << endl;
318 msg = (PokerTHMessage_t *)calloc(1, sizeof(PokerTHMessage_t));
319 msg->present = PokerTHMessage_PR_joinGameRequestMessage;
320 JoinGameRequestMessage_t *netJoinGame = &msg->choice.joinGameRequestMessage;
321 string tmpGamePassword("blah123");
322 netJoinGame->password = OCTET_STRING_new_fromBuf(
323 &asn_DEF_UTF8String,
324 tmpGamePassword.c_str(),
325 tmpGamePassword.length());
326 netJoinGame->joinGameAction.present = joinGameAction_PR_joinNewGame;
327 JoinNewGame_t *joinNew = &netJoinGame->joinGameAction.choice.joinNewGame;
328 string tmpGameName("_loadtest_do_not_join_" + session->name);
329 joinNew->gameInfo.netGameType = netGameType_normalGame;
330 joinNew->gameInfo.maxNumPlayers = 10;
331 joinNew->gameInfo.raiseIntervalMode.present = raiseIntervalMode_PR_raiseEveryHands;
332 joinNew->gameInfo.raiseIntervalMode.choice.raiseEveryHands = 1;
333 joinNew->gameInfo.endRaiseMode = endRaiseMode_keepLastBlind;
334 joinNew->gameInfo.proposedGuiSpeed = 5;
335 joinNew->gameInfo.delayBetweenHands = 5;
336 joinNew->gameInfo.playerActionTimeout = 5;
337 joinNew->gameInfo.endRaiseSmallBlindValue = 0;
338 joinNew->gameInfo.firstSmallBlind = 200;
339 joinNew->gameInfo.startMoney = 10000;
340 OCTET_STRING_fromBuf(&joinNew->gameInfo.gameName,
341 tmpGameName.c_str(),
342 tmpGameName.length());
343 if (!sendMessage(session, msg)) {
344 cout << "Create game failed" << endl;
345 return 1;
346 }
347 msg = NULL;
348 // Receive join game ack
349 do {
350 ASN_STRUCT_FREE(asn_DEF_PokerTHMessage, msg);
351 msg = receiveMessage(session);
352 if (!msg) {
353 cout << "Receive in lobby failed" << endl;
354 return 1;
355 }
356 if (msg->present == PokerTHMessage_PR_errorMessage) {
357 cout << "Received error" << endl;
358 return 1;
359 }
360 } while (msg->present != PokerTHMessage_PR_joinGameReplyMessage);
361 if (msg->choice.joinGameReplyMessage.joinGameResult.present != joinGameResult_PR_joinGameAck) {
362 cout << "Join game ack failed" << endl;
363 return 1;
364 }
365 gameId[g] = msg->choice.joinGameReplyMessage.gameId;
366 ASN_STRUCT_FREE(asn_DEF_PokerTHMessage, msg);
367 }
368
369 for (int t = 0; t < (numGames * 10) / LoginsPerLoop + 1; t++) {
370 int startNum = t * LoginsPerLoop;
371 int endNum = (t + 1) * LoginsPerLoop;
372 if (endNum > numGames * 10) {
373 endNum = numGames * 10;
374 }
375 for (int i = startNum; i < endNum; i++) {
376 if (i % 10 == 0) {
377 continue;
378 }
379 NetSession *session = sessionArray[i];
380
381 cout << "Player " << session->name << " joining game " << (i / 10)+1 << endl;
382 msg = (PokerTHMessage_t *)calloc(1, sizeof(PokerTHMessage_t));
383 msg->present = PokerTHMessage_PR_joinGameRequestMessage;
384 JoinGameRequestMessage_t *netJoinGame = &msg->choice.joinGameRequestMessage;
385 string tmpGamePassword("blah123");
386 netJoinGame->password = OCTET_STRING_new_fromBuf(
387 &asn_DEF_UTF8String,
388 tmpGamePassword.c_str(),
389 tmpGamePassword.length());
390 netJoinGame->joinGameAction.present = joinGameAction_PR_joinExistingGame;
391 JoinExistingGame_t *joinExisting = &netJoinGame->joinGameAction.choice.joinExistingGame;
392 joinExisting->gameId = gameId[i / 10];
393 if (!sendMessage(session, msg)) {
394 cout << "Join game failed" << endl;
395 return 1;
396 }
397 msg = NULL;
398 }
399 for (int i = startNum; i < endNum; i++) {
400 if (i % 10 == 0) {
401 continue;
402 }
403 NetSession *session = sessionArray[i];
404 // Receive join game ack
405 do {
406 ASN_STRUCT_FREE(asn_DEF_PokerTHMessage, msg);
407 msg = receiveMessage(session);
408 if (!msg) {
409 cout << "Receive in lobby failed" << endl;
410 return 1;
411 }
412 if (msg->present == PokerTHMessage_PR_errorMessage) {
413 cout << "Received error" << endl;
414 return 1;
415 }
416 } while (msg->present != PokerTHMessage_PR_joinGameReplyMessage);
417 if (msg->choice.joinGameReplyMessage.joinGameResult.present != joinGameResult_PR_joinGameAck) {
418 cout << "Join game ack failed" << endl;
419 return 1;
420 }
421 ASN_STRUCT_FREE(asn_DEF_PokerTHMessage, msg);
422 msg = NULL;
423 }
424 }
425 bool terminated = false;
426 while (!terminated) {
427 for (int i = 0; i < numGames * 10; i++) {
428 NetSession *session = sessionArray[i];
429
430 size_t bytes_readable = session->socket.available();
431 while (bytes_readable > 0) {
432 msg = receiveMessage(session);
433 if (msg->present == PokerTHMessage_PR_endOfGameMessage) {
434 cout << "One game was ended." << endl;
435 terminated = true;
436 }
437 ASN_STRUCT_FREE(asn_DEF_PokerTHMessage, msg);
438 bytes_readable = session->socket.available();
439 }
440 }
441 boost::this_thread::sleep(boost::posix_time::milliseconds(100));
442 }
443 gsasl_done(authContext);
444
445 } catch (const exception &e) {
446 cout << "Exception caught " << e.what() << endl;
447 return 1;
448 }
449
450 return 0;
451 }
452
453