diff --git a/MSVC/build/game.vcxproj b/MSVC/build/game.vcxproj
index 0f0319a9c8..7902dfa359 100644
--- a/MSVC/build/game.vcxproj
+++ b/MSVC/build/game.vcxproj
@@ -164,6 +164,7 @@
+
@@ -184,6 +185,7 @@
+
diff --git a/MSVC/build/game.vcxproj.filters b/MSVC/build/game.vcxproj.filters
index 0b0ce9f449..ca2e99da21 100644
--- a/MSVC/build/game.vcxproj.filters
+++ b/MSVC/build/game.vcxproj.filters
@@ -61,6 +61,9 @@
Source Files
+
+ Source Files
+
@@ -114,6 +117,9 @@
Header Files
+
+ Header Files
+
diff --git a/Xcode/BZFlag.xcodeproj/project.pbxproj b/Xcode/BZFlag.xcodeproj/project.pbxproj
index 62493fc74d..7331f32c58 100644
--- a/Xcode/BZFlag.xcodeproj/project.pbxproj
+++ b/Xcode/BZFlag.xcodeproj/project.pbxproj
@@ -529,6 +529,7 @@
C38E766527F9062B00EC312C /* CustomZoneSample.bzw in Copy Files (1 item) */ = {isa = PBXBuildFile; fileRef = C353B1E81AE2489900C5AED5 /* CustomZoneSample.bzw */; };
C39F69371E3D3E8100132C55 /* customPollTypeSample.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = C333EFC91E2B9F0800B4B182 /* customPollTypeSample.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
C3DE232E21E2FBDD0081B64E /* libplugin_utils.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0394E895167B2513007F4035 /* libplugin_utils.a */; };
+ D21A35892BFAA6BD001A28EB /* ServerAuth.cxx in Sources */ = {isa = PBXBuildFile; fileRef = D21A35882BFAA6BD001A28EB /* ServerAuth.cxx */; };
D2B721C92BFAA226007126EE /* mathRoutine.cxx in Sources */ = {isa = PBXBuildFile; fileRef = D2B721C82BFAA225007126EE /* mathRoutine.cxx */; };
DF28982B16B4F5AE006C78AC /* ShotManager.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DF28982916B4F5AE006C78AC /* ShotManager.cxx */; };
/* End PBXBuildFile section */
@@ -1814,6 +1815,8 @@
C353B1E01AE23C8100C5AED5 /* CustomZoneSample.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CustomZoneSample.cpp; sourceTree = ""; };
C353B1E81AE2489900C5AED5 /* CustomZoneSample.bzw */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CustomZoneSample.bzw; sourceTree = ""; };
C353B1E91AE2490D00C5AED5 /* flagStay.bzw */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = flagStay.bzw; sourceTree = ""; };
+ D21A35872BFAA6AE001A28EB /* ServerAuth.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ServerAuth.h; sourceTree = ""; };
+ D21A35882BFAA6BD001A28EB /* ServerAuth.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ServerAuth.cxx; sourceTree = ""; };
D2B721C72BFAA1E1007126EE /* mathRoutine.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mathRoutine.h; sourceTree = ""; };
D2B721C82BFAA225007126EE /* mathRoutine.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mathRoutine.cxx; sourceTree = ""; };
DF28982916B4F5AE006C78AC /* ShotManager.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShotManager.cxx; sourceTree = ""; };
@@ -2347,6 +2350,7 @@
0305D5D9166C9DAE00557FC4 /* SceneDatabase.h */,
0305D5DA166C9DAE00557FC4 /* SceneNode.h */,
0305D5DB166C9DAE00557FC4 /* SceneRenderer.h */,
+ D21A35872BFAA6AE001A28EB /* ServerAuth.h */,
0305D5DC166C9DAE00557FC4 /* ServerItem.h */,
0305D5DD166C9DAE00557FC4 /* ServerList.h */,
0305D5DE166C9DAE00557FC4 /* ServerListCache.h */,
@@ -2858,6 +2862,7 @@
0355447B166C846F008806E9 /* PlayerInfo.cxx */,
0355447C166C846F008806E9 /* Ray.cxx */,
0355447D166C846F008806E9 /* README */,
+ D21A35882BFAA6BD001A28EB /* ServerAuth.cxx */,
0355447E166C846F008806E9 /* ServerItem.cxx */,
0355447F166C846F008806E9 /* ServerList.cxx */,
03554480166C846F008806E9 /* ServerListCache.cxx */,
@@ -5070,6 +5075,7 @@
0357A80E1670B2090056C938 /* PhysicsDriver.cxx in Sources */,
0357A80F1670B2090056C938 /* PlayerInfo.cxx in Sources */,
0357A8101670B2090056C938 /* Ray.cxx in Sources */,
+ D21A35892BFAA6BD001A28EB /* ServerAuth.cxx in Sources */,
0357A8111670B2090056C938 /* ServerItem.cxx in Sources */,
0357A8121670B2090056C938 /* ServerList.cxx in Sources */,
0357A8131670B2090056C938 /* ServerListCache.cxx in Sources */,
diff --git a/include/Makefile.am b/include/Makefile.am
index 0730ef41e7..3ad03bd874 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -102,6 +102,7 @@ noinst_HEADERS = \
SceneDatabase.h \
SceneNode.h \
SceneRenderer.h \
+ ServerAuth.h \
ServerItem.h \
ServerList.h \
ServerListCache.h \
diff --git a/include/ServerAuth.h b/include/ServerAuth.h
new file mode 100644
index 0000000000..3309657f59
--- /dev/null
+++ b/include/ServerAuth.h
@@ -0,0 +1,38 @@
+/* bzflag
+ * Copyright (c) 1993-2024 Tim Riker
+ *
+ * This package is free software; you can redistribute it and/or
+ * modify it under the terms of the license found in the file
+ * named COPYING that should have accompanied this file.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef BZF_SERVERAUTH_H
+#define BZF_SERVERAUTH_H
+
+#include "common.h"
+
+/* system interface headers */
+#include
+
+/* common interface headers */
+#include "StartupInfo.h"
+#include "cURLManager.h"
+
+class ServerAuth : cURLManager
+{
+public:
+ ServerAuth();
+ virtual ~ServerAuth();
+
+ void requestToken(StartupInfo *info);
+ void finalization(char *data, unsigned int length, bool good);
+
+private:
+ StartupInfo *startupInfo;
+};
+
+#endif // BZF_SERVERAUTH_H
diff --git a/include/cURLManager.h b/include/cURLManager.h
index 34da946d58..059d282645 100644
--- a/include/cURLManager.h
+++ b/include/cURLManager.h
@@ -46,7 +46,6 @@ class cURLManager
void setPostMode(std::string postData);
void setRequestFileTime(bool request);
void setURL(const std::string &url);
- void setURLwithNonce(const std::string &url);
void setProgressFunction(curl_xferinfo_callback func, const void* data);
void setTimeCondition(timeCondition condition, time_t &t);
void setInterface(const std::string &interfaceIP);
diff --git a/src/bzadmin/BZAdminClient.cxx b/src/bzadmin/BZAdminClient.cxx
index fd1c4198c9..aee74f8c8b 100644
--- a/src/bzadmin/BZAdminClient.cxx
+++ b/src/bzadmin/BZAdminClient.cxx
@@ -30,6 +30,7 @@
#include "TextUtils.h"
#include "version.h"
#include "Team.h"
+#include "ServerAuth.h"
#include "ServerList.h"
#include "ErrorHandler.h"
#include "cURLManager.h"
@@ -73,10 +74,25 @@ BZAdminClient::BZAdminClient(BZAdminUI* bzInterface)
std::cout << std::endl;
return;
}
- if ((startupInfo.token[0] == '\0') && (startupInfo.password[0] != '\0'))
+
+ // If a password was provided, get a token
+ if (startupInfo.password[0] != '\0')
{
- // won't really output anything, just gets token
- outputServerList();
+ ServerAuth* serverAuth = new ServerAuth;
+ serverAuth->requestToken(&startupInfo);
+ // wait no more than 10 seconds for a token
+ for (int j = 0; j < 40; j++)
+ {
+ cURLManager::perform();
+ if (startupInfo.token[0] != '\0') break;
+ TimeKeeper::sleep(0.25f);
+ }
+ delete serverAuth;
+
+ // don't let the bad token specifier slip through to the server,
+ // just erase it
+ if (strcmp(startupInfo.token, "badtoken") == 0)
+ startupInfo.token[0] = '\0';
}
sLink.sendEnter(TankPlayer, myTeam, startupInfo.callsign, "bzadmin", startupInfo.token);
if (sLink.getState() != ServerLink::Okay)
diff --git a/src/bzflag/playing.cxx b/src/bzflag/playing.cxx
index cb1c8bf5a6..a7b0291a5d 100644
--- a/src/bzflag/playing.cxx
+++ b/src/bzflag/playing.cxx
@@ -52,7 +52,7 @@
#include "PhysicsDriver.h"
#include "PlatformFactory.h"
#include "QuadWallSceneNode.h"
-#include "ServerList.h"
+#include "ServerAuth.h"
#include "SphereSceneNode.h"
#include "TankGeometryMgr.h"
#include "Team.h"
@@ -7010,26 +7010,28 @@ static void playingLoop()
// if already connected to a game then first sign off
if (myTank) leaveGame();
- // get token if we need to (have a password but no token)
- if ((startupInfo.token[0] == '\0')
- && (startupInfo.password[0] != '\0'))
+ // Erase any existing token
+ startupInfo.token[0] = '\0';
+
+ // get token if we have a password
+ if (startupInfo.password[0] != '\0')
{
- ServerList* serverList = new ServerList;
- serverList->startServerPings(&startupInfo);
+ ServerAuth* serverAuth = new ServerAuth;
+ serverAuth->requestToken(&startupInfo);
// wait no more than 10 seconds for a token
for (int j = 0; j < 40; j++)
{
- serverList->checkEchos(getStartupInfo());
cURLManager::perform();
if (startupInfo.token[0] != '\0') break;
TimeKeeper::sleep(0.25f);
}
- delete serverList;
+ delete serverAuth;
+
+ // don't let the bad token specifier slip through to the server,
+ // just erase it
+ if (strcmp(startupInfo.token, "badtoken") == 0)
+ startupInfo.token[0] = '\0';
}
- // don't let the bad token specifier slip through to the server,
- // just erase it
- if (strcmp(startupInfo.token, "badtoken") == 0)
- startupInfo.token[0] = '\0';
ares->queryHost(startupInfo.serverName);
waitingDNS = true;
diff --git a/src/bzfs/ListServerConnection.cxx b/src/bzfs/ListServerConnection.cxx
index 4f9b5a13f0..d15fa7889b 100644
--- a/src/bzfs/ListServerConnection.cxx
+++ b/src/bzfs/ListServerConnection.cxx
@@ -43,7 +43,7 @@ ListServerLink::ListServerLink(std::string listServerURL,
std::string bzfsUserAgent = "bzfs ";
bzfsUserAgent += getAppVersion();
- setURLwithNonce(listServerURL);
+ setURL(listServerURL);
setUserAgent(bzfsUserAgent);
setTimeout(10);
diff --git a/src/common/cURLManager.cxx b/src/common/cURLManager.cxx
index 3b0818c503..5155f8070c 100644
--- a/src/common/cURLManager.cxx
+++ b/src/common/cURLManager.cxx
@@ -186,14 +186,6 @@ void cURLManager::setURL(const std::string &url)
logDebugMessage(1,"CURLOPT_URL error %d : %s\n", result, errorBuffer);
}
-void cURLManager::setURLwithNonce(const std::string &url)
-{
- // only the default list server is known to support the nonce parameter
- const std::string nonce = (strcasecmp(url.c_str(), DefaultListServerURL) == 0) ? TextUtils::format("?nocache=%lu",
- time(0)) : "";
- setURL(url + nonce);
-}
-
void cURLManager::setProgressFunction(curl_xferinfo_callback func, const void* data)
{
CURLcode result;
diff --git a/src/game/Makefile.am b/src/game/Makefile.am
index 7ddefe71e1..1d10ac8e74 100644
--- a/src/game/Makefile.am
+++ b/src/game/Makefile.am
@@ -26,6 +26,7 @@ libGame_la_SOURCES = \
PlayerInfo.cxx \
Ray.cxx \
ServerItem.cxx \
+ ServerAuth.cxx \
ServerList.cxx \
ServerListCache.cxx \
StartupInfo.cxx \
diff --git a/src/game/ServerAuth.cxx b/src/game/ServerAuth.cxx
new file mode 100644
index 0000000000..fc4d393505
--- /dev/null
+++ b/src/game/ServerAuth.cxx
@@ -0,0 +1,111 @@
+/* bzflag
+ * Copyright (c) 1993-2024 Tim Riker
+ *
+ * This package is free software; you can redistribute it and/or
+ * modify it under the terms of the license found in the file
+ * named COPYING that should have accompanied this file.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* interface header */
+#include "ServerAuth.h"
+
+/* system headers */
+#include
+
+/* common implementation headers */
+#include "TextUtils.h"
+#include "ErrorHandler.h"
+
+ServerAuth::ServerAuth()
+{
+}
+
+ServerAuth::~ServerAuth()
+{
+}
+
+void ServerAuth::requestToken(StartupInfo *info)
+{
+ startupInfo = info;
+
+ std::string url = info->listServerURL;
+
+ std::string msg = "action=GETTOKEN&callsign=";
+ msg += TextUtils::url_encode(info->callsign);
+ msg += "&password=";
+ msg += TextUtils::url_encode(info->password);
+ if (info->serverName[0] != '\0')
+ {
+ msg += "&nameport=";
+ msg += info->serverName;
+ msg += ':';
+ msg += std::to_string(info->serverPort);
+ }
+ setPostMode(msg);
+ setURL(url);
+ addHandle();
+}
+
+void ServerAuth::finalization(char *_data, unsigned int length, bool good)
+{
+ if (good)
+ {
+ char *base = (char *)_data;
+ char *endS = base + length;
+ const char tokenIdentifier[] = "TOKEN: ";
+ const char noTokenIdentifier[] = "NOTOK: ";
+ const char errorIdentifier[] = "ERROR: ";
+ const char noticeIdentifier[] = "NOTICE: ";
+
+ while (base < endS)
+ {
+ // find next newline
+ char* scan = base;
+ while (scan < endS && *scan != '\n')
+ scan++;
+
+ // if no newline then no more complete replies
+ if (scan >= endS)
+ break;
+ *scan++ = '\0';
+
+ // look for TOKEN: and save token if found also look for NOTOK:
+ // and record "badtoken" into the token string and print an
+ // error
+ if (strncmp(base, tokenIdentifier, strlen(tokenIdentifier)) == 0)
+ {
+ strncpy(startupInfo->token, (char *)(base + strlen(tokenIdentifier)),
+ TokenLen - 1);
+ startupInfo->token[TokenLen - 1] = '\0';
+#ifdef DEBUG
+ std::vector args;
+ args.push_back(startupInfo->token);
+ printError("got token: {1}", &args);
+#endif
+ }
+ else if (!strncmp(base, noTokenIdentifier,
+ strlen(noTokenIdentifier)))
+ {
+ printError("ERROR: did not get token:");
+ printError(base);
+ strcpy(startupInfo->token, "badtoken\0");
+ }
+ else if (!strncmp(base, errorIdentifier, strlen(errorIdentifier)))
+ {
+ printError(base);
+ strcpy(startupInfo->token, "badtoken\0");
+ }
+ else if (!strncmp(base, noticeIdentifier, strlen(noticeIdentifier)))
+ printError(base);
+
+ // next reply
+ base = scan;
+ }
+ }
+ else
+ strcpy(startupInfo->token, "badtoken\0");
+}
diff --git a/src/game/ServerList.cxx b/src/game/ServerList.cxx
index 0abb76bc51..15e63ae2ea 100644
--- a/src/game/ServerList.cxx
+++ b/src/game/ServerList.cxx
@@ -64,9 +64,6 @@ void ServerList::readServerList()
{
char *base = (char *)theData;
char *endS = base + theLen;
- const char tokenIdentifier[] = "TOKEN: ";
- const char noTokenIdentifier[] = "NOTOK: ";
- const char errorIdentifier[] = "ERROR: ";
const char noticeIdentifier[] = "NOTICE: ";
// walks entire reply including HTTP headers
while (base < endS)
@@ -81,38 +78,7 @@ void ServerList::readServerList()
break;
*scan++ = '\0';
- // look for TOKEN: and save token if found also look for NOTOK:
- // and record "badtoken" into the token string and print an
- // error
- if (strncmp(base, tokenIdentifier, strlen(tokenIdentifier)) == 0)
- {
- strncpy(startupInfo->token, (char *)(base + strlen(tokenIdentifier)),
- TokenLen - 1);
- startupInfo->token[TokenLen - 1] = '\0';
-#ifdef DEBUG
- printError("got token:");
- printError(startupInfo->token);
-#endif
- base = scan;
- continue;
- }
- else if (!strncmp(base, noTokenIdentifier,
- strlen(noTokenIdentifier)))
- {
- printError("ERROR: did not get token:");
- printError(base);
- strcpy(startupInfo->token, "badtoken\0");
- base = scan;
- continue;
- }
- else if (!strncmp(base, errorIdentifier, strlen(errorIdentifier)))
- {
- printError(base);
- strcpy(startupInfo->token, "badtoken\0");
- base = scan;
- continue;
- }
- else if (!strncmp(base, noticeIdentifier, strlen(noticeIdentifier)))
+ if (!strncmp(base, noticeIdentifier, strlen(noticeIdentifier)))
{
printError(base);
base = scan;
@@ -306,19 +272,19 @@ void ServerList::checkEchos(StartupInfo *info)
std::string msg = "action=LIST&version=";
msg += getServerVersion();
- msg += "&callsign=";
- msg += TextUtils::url_encode(info->callsign);
- msg += "&password=";
- msg += TextUtils::url_encode(info->password);
- if (info->serverName[0] != '\0')
+ // Send callsign/password only if the password is set
+ if (info->password[0] != '\0')
{
- msg += "&nameport=";
- msg += info->serverName;
- msg += ':';
- msg += std::to_string(info->serverPort);
+ msg += "&callsign=";
+ msg += TextUtils::url_encode(info->callsign);
+ msg += "&password=";
+ msg += TextUtils::url_encode(info->password);
+ // Since tokens are now tied to a game server host/port instead of the player IP, we will skip generating
+ // a token during the LIST operation.
+ msg += "&skiptoken=1";
}
setPostMode(msg);
- setURLwithNonce(url);
+ setURL(url);
addHandle();
// do phase 1 only if we found a valid list server url