Skip to content

Commit 7830fb9

Browse files
committed
Fixed race condition with CM_MAC_ADDRESS
LS could send account info before the game client would send `CM_MAC_ADDRESS`, resulting in invalid checks (added in 1e0080f) due to missing data. Refactored everything to request account info only after receiving `CM_MAC_ADDRESS`. Also removed pointless todo in CM_RECONNECT_AUTH, as this packet's validStates already take care of it.
1 parent 07c1c93 commit 7830fb9

File tree

5 files changed

+30
-48
lines changed

5 files changed

+30
-48
lines changed

game-server/src/com/aionemu/gameserver/network/aion/AionConnection.java

+2-6
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,9 @@ protected final void onDisconnect() {
242242
return;
243243
}
244244

245-
String msg = "";
246-
Account account = getAccount();
247-
if (account != null) {
248-
msg += " " + account;
249-
LoginServer.getInstance().aionClientDisconnected(account.getId());
250-
}
245+
LoginServer.getInstance().onDisconnect(this);
251246

247+
String msg = getAccount() == null ? "" : " " + getAccount();
252248
Player player = getActivePlayer();
253249
if (player != null) {
254250
msg += " " + player + " (client crash or connection loss)";

game-server/src/com/aionemu/gameserver/network/aion/clientpackets/CM_L2AUTH_LOGIN_CHECK.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,6 @@ protected void readImpl() {
5959

6060
@Override
6161
protected void runImpl() {
62-
LoginServer.getInstance().requestAuthenticationOfClient(accountId, getConnection(), loginOk, playOk1, playOk2);
62+
LoginServer.getInstance().registerLoginRequest(accountId, getConnection(), loginOk, playOk1, playOk2);
6363
}
6464
}

game-server/src/com/aionemu/gameserver/network/aion/clientpackets/CM_MAC_ADDRESS.java

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.aionemu.gameserver.network.aion.AionClientPacket;
88
import com.aionemu.gameserver.network.aion.AionConnection;
99
import com.aionemu.gameserver.network.aion.AionConnection.State;
10+
import com.aionemu.gameserver.network.loginserver.LoginServer;
1011

1112
/**
1213
* In this packet client is sending connection and system information (IPs, MAC Address and HDD serial).
@@ -37,6 +38,7 @@ protected void runImpl() {
3738
AionConnection con = getConnection();
3839
con.setMacAddress(macAddress);
3940
con.setHddSerial(hddSerial);
41+
LoginServer.getInstance().authenticateClient(con);
4042
}
4143

4244
private static String fixHddSerial(String hddSerial) {

game-server/src/com/aionemu/gameserver/network/aion/clientpackets/CM_RECONNECT_AUTH.java

-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ protected void readImpl() {
2525
@Override
2626
protected void runImpl() {
2727
AionConnection client = getConnection();
28-
// TODO! check if may reconnect
2928
LoginServer.getInstance().requestAuthReconnection(client.getAccount().getId(), client);
3029
}
3130
}

game-server/src/com/aionemu/gameserver/network/loginserver/LoginServer.java

+25-40
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,8 @@ public class LoginServer {
4141

4242
private static final Logger log = LoggerFactory.getLogger(LoginServer.class);
4343

44-
/**
45-
* Map<accountId,Connection> for waiting request. This request is send to LoginServer and GameServer is waiting for response.
46-
*/
47-
private Map<Integer, AionConnection> loginRequests = new ConcurrentHashMap<>();
48-
49-
/**
50-
* Map<accountId,Connection> for all logged in accounts.
51-
*/
52-
private Map<Integer, AionConnection> loggedInAccounts = new ConcurrentHashMap<>();
44+
private final Map<Integer, LoginRequest> loginRequests = new ConcurrentHashMap<>();
45+
private final Map<Integer, AionConnection> loggedInAccounts = new ConcurrentHashMap<>();
5346
private LoginServerConnection lsCon = null;
5447
private NioServer nioServer = null;
5548
private int gameServerCount = 1;
@@ -98,8 +91,8 @@ public void disconnect() {
9891
lsCon.close();
9992
lsCon = null;
10093
}
101-
for (AionConnection client : loginRequests.values())
102-
client.close(/* closePacket */); // TODO! some error packet!
94+
for (LoginRequest loginRequest : loginRequests.values())
95+
loginRequest.connection.close();
10396
loginRequests.clear();
10497
}
10598

@@ -119,13 +112,13 @@ public boolean isUp() {
119112
/**
120113
* Notify that client is disconnected - we must clear waiting request to LoginServer if any to prevent leaks. Also notify LoginServer that this
121114
* account is no longer on GameServer side.
122-
*
123-
* @param accountId
124115
*/
125-
public void aionClientDisconnected(int accountId) {
126-
loginRequests.remove(accountId);
127-
loggedInAccounts.remove(accountId);
128-
sendPacket(new SM_ACCOUNT_DISCONNECTED(accountId));
116+
public void onDisconnect(AionConnection connection) {
117+
loginRequests.values().removeIf(r -> r.connection == connection);
118+
if (connection.getAccount() != null) {
119+
loggedInAccounts.remove(connection.getAccount().getId());
120+
sendPacket(new SM_ACCOUNT_DISCONNECTED(connection.getAccount().getId()));
121+
}
129122
}
130123

131124
public void setGameServerCount(int gameServerCount) {
@@ -136,18 +129,16 @@ public int getGameServerCount() {
136129
return gameServerCount;
137130
}
138131

132+
public void registerLoginRequest(int accountId, AionConnection client, int loginOk, int playOk1, int playOk2) {
133+
loginRequests.putIfAbsent(accountId, new LoginRequest(client, new SM_ACCOUNT_AUTH(accountId, loginOk, playOk1, playOk2)));
134+
}
135+
139136
/**
140137
* Starts authentication procedure of this client - LoginServer will send response with information about account name if authentication is ok.
141-
*
142-
* @param accountId
143-
* @param client
144-
* @param loginOk
145-
* @param playOk1
146-
* @param playOk2
147138
*/
148-
public void requestAuthenticationOfClient(int accountId, AionConnection client, int loginOk, int playOk1, int playOk2) {
149-
if (isUp() && loginRequests.putIfAbsent(accountId, client) == null)
150-
lsCon.sendPacket(new SM_ACCOUNT_AUTH(accountId, loginOk, playOk1, playOk2));
139+
public void authenticateClient(AionConnection client) {
140+
if (isUp())
141+
loginRequests.values().stream().filter(r -> r.connection == client).findAny().ifPresent(r -> lsCon.sendPacket(r.lsAuthResponse));
151142
else
152143
client.close(new SM_L2AUTH_LOGIN_CHECK(false, null)); // disconnect this client since authentication will not happen
153144
}
@@ -157,10 +148,11 @@ public void requestAuthenticationOfClient(int accountId, AionConnection client,
157148
*/
158149
public void accountAuthenticationResponse(int accountId, String accountName, boolean result, long creationDate, AccountTime accountTime,
159150
byte accessLevel, byte membership, long toll, String allowedHddSerial) {
160-
AionConnection client = loginRequests.remove(accountId);
161-
if (client == null)
151+
LoginRequest loginRequest = loginRequests.remove(accountId);
152+
if (loginRequest == null)
162153
return;
163154

155+
AionConnection client = loginRequest.connection;
164156
if (!result || !validateMacAndHddSerial(client, allowedHddSerial)) {
165157
client.close(new SM_L2AUTH_LOGIN_CHECK(false, accountName)); // LS sends no accName when result is false
166158
sendPacket(new SM_ACCOUNT_DISCONNECTED(accountId)); // disconnect manually from login server because account isn't attached to connection yet
@@ -181,10 +173,7 @@ public void accountAuthenticationResponse(int accountId, String accountName, boo
181173
}
182174

183175
private boolean validateMacAndHddSerial(AionConnection client, String allowedHddSerial) {
184-
if (client.getMacAddress() == null || client.getHddSerial() == null) {
185-
log.warn(client + " did not send CM_MAC_ADDRESS during login (modified client or hack)");
186-
return false;
187-
} else if (!client.getMacAddress().matches("^([0-9A-F]{2}-){5}[0-9A-F]{2}$")) {
176+
if (!client.getMacAddress().matches("^([0-9A-F]{2}-){5}[0-9A-F]{2}$")) {
188177
log.warn(client + " sent an invalid MAC address (modified client or hack): " + client.getMacAddress());
189178
return false;
190179
} else if (BannedMacManager.getInstance().isBanned(client.getMacAddress())) {
@@ -214,12 +203,9 @@ private void kickOnlineCharacters(Account account) {
214203

215204
/**
216205
* Starts reconnection to LoginServer procedure. LoginServer in response will send reconnection key.
217-
*
218-
* @param accountId
219-
* @param client
220206
*/
221207
public void requestAuthReconnection(int accountId, AionConnection client) {
222-
if (isUp() && loggedInAccounts.containsKey(accountId) && loginRequests.putIfAbsent(accountId, client) == null)
208+
if (isUp() && client.equals(loggedInAccounts.get(accountId)))
223209
lsCon.sendPacket(new SM_ACCOUNT_RECONNECT_KEY(client.getAccount().getId()));
224210
else
225211
client.close(/* closePacket */);
@@ -228,12 +214,9 @@ public void requestAuthReconnection(int accountId, AionConnection client) {
228214
/**
229215
* This method is called by CM_ACCOUNT_RECONNECT_KEY LoginServer packets to give GameServer reconnection key for client that was requesting
230216
* reconnection.
231-
*
232-
* @param accountId
233-
* @param reconnectKey
234217
*/
235218
public void authReconnectionResponse(int accountId, int reconnectKey) {
236-
AionConnection client = loginRequests.remove(accountId);
219+
AionConnection client = loggedInAccounts.get(accountId);
237220
if (client == null)
238221
return;
239222
client.close(new SM_RECONNECT_KEY(reconnectKey));
@@ -287,4 +270,6 @@ private static class SingletonHolder {
287270

288271
protected static final LoginServer instance = new LoginServer();
289272
}
273+
274+
private record LoginRequest(AionConnection connection, SM_ACCOUNT_AUTH lsAuthResponse) {}
290275
}

0 commit comments

Comments
 (0)