Newer
Older
*
* This file is part of L2J Server.
*
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.loginserver;
import static com.l2jserver.loginserver.config.Configuration.server;
import static com.l2jserver.loginserver.network.loginserverpackets.LoginServerFail.REASON_IP_BANNED;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.l2jserver.commons.dao.ServerNameDAO;
import com.l2jserver.commons.network.BaseSendablePacket;
import com.l2jserver.commons.security.crypt.NewCrypt;
import com.l2jserver.commons.util.Util;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import com.l2jserver.loginserver.GameServerTable.GameServerInfo;
import com.l2jserver.loginserver.network.L2JGameServerPacketHandler;
import com.l2jserver.loginserver.network.L2JGameServerPacketHandler.GameServerState;
import com.l2jserver.loginserver.network.loginserverpackets.ChangePasswordResponse;
import com.l2jserver.loginserver.network.loginserverpackets.InitLS;
import com.l2jserver.loginserver.network.loginserverpackets.KickPlayer;
import com.l2jserver.loginserver.network.loginserverpackets.LoginServerFail;
import com.l2jserver.loginserver.network.loginserverpackets.RequestCharacters;
/**
* Game Server thread.
* @author -Wooden-
* @author KenM
* @version 2.6.1.0
*/
public class GameServerThread extends Thread {
private static final Logger LOG = LoggerFactory.getLogger(GameServerThread.class);
private final Socket _connection;
private InputStream _in;
private OutputStream _out;
private final RSAPublicKey _publicKey;
private final RSAPrivateKey _privateKey;
private NewCrypt _blowfish;
private GameServerState _loginConnectionState = GameServerState.CONNECTED;
private final String _connectionIp;
private GameServerInfo _gsi;
/** Authed Clients on a GameServer */
private final Set<String> _accountsOnGameServer = ConcurrentHashMap.newKeySet();
private String _connectionIPAddress;
@Override
public void run() {
_connectionIPAddress = _connection.getInetAddress().getHostAddress();
if (GameServerThread.isBannedGameServerIP(_connectionIPAddress)) {
LOG.warn("IP Address {} is on banned IP list.", _connectionIPAddress);
forceClose(REASON_IP_BANNED);
return;
}
try {
sendPacket(new InitLS(_publicKey.getModulus().toByteArray()));
int lengthHi;
int lengthLo;
int length;
boolean checksumOk;
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
for (;;) {
lengthLo = _in.read();
lengthHi = _in.read();
length = (lengthHi * 256) + lengthLo;
if ((lengthHi < 0) || _connection.isClosed()) {
LOG.warn("Login terminated the connection!");
break;
}
byte[] data = new byte[length - 2];
int receivedBytes = 0;
int newBytes = 0;
int left = length - 2;
while ((newBytes != -1) && (receivedBytes < (length - 2))) {
newBytes = _in.read(data, receivedBytes, left);
receivedBytes = receivedBytes + newBytes;
left -= newBytes;
}
if (receivedBytes != (length - 2)) {
LOG.warn("Incomplete Packet is sent to the server, closing connection. (LS)");
break;
}
// decrypt if we have a key
_blowfish.decrypt(data, 0, data.length);
checksumOk = NewCrypt.verifyChecksum(data);
if (!checksumOk) {
LOG.warn("Incorrect packet checksum, closing connection. (LS)");
return;
}
LOG.warn("[C]" + System.lineSeparator() + Util.printData(data));
}
L2JGameServerPacketHandler.handlePacket(data, this);
}
} catch (IOException ex) {
final var serverName = (getServerId() != -1 ? "[" + getServerId() + "] " + ServerNameDAO.getServer(getServerId()) : "(" + _connectionIPAddress + ")");
LOG.warn("Game Server {} lost connection!", serverName);
broadcastToTelnet("Game Server " + serverName + " lost connection!");
} finally {
if (isAuthed()) {
_gsi.setDown();
LOG.info("Server {}[{}] is now disconnected.", ServerNameDAO.getServer(getServerId()), getServerId());
}
LoginServer.getInstance().getGameServerListener().removeGameServer(this);
LoginServer.getInstance().getGameServerListener().removeFloodProtection(_connectionIp);
}
}
public boolean hasAccountOnGameServer(String account) {
return _accountsOnGameServer.contains(account);
}
public int getPlayerCount() {
return _accountsOnGameServer.size();
}
/**
* Attaches a GameServerInfo to this Thread<br>
* <ul>
* <li>Updates the GameServerInfo values based on GameServerAuth packet</li>
* <li><b>Sets the GameServerInfo as Authed</b></li>
* </ul>
* @param gsi The GameServerInfo to be attached.
* @param port the port
* @param hosts the hosts
* @param maxPlayers the maximum amount of players
*/
public void attachGameServerInfo(GameServerInfo gsi, int port, String[] hosts, int maxPlayers) {
setGameServerInfo(gsi);
gsi.setGameServerThread(this);
gsi.setPort(port);
setGameHosts(hosts);
gsi.setMaxPlayers(maxPlayers);
gsi.setAuthed(true);
}
public void forceClose(int reason) {
sendPacket(new LoginServerFail(reason));
try {
_connection.close();
} catch (IOException ex) {
LOG.debug("Failed disconnecting banned server, server already disconnected.");
}
}
public static boolean isBannedGameServerIP(String ipAddress) {
return false;
}
public GameServerThread(Socket con) {
_connection = con;
_connectionIp = con.getInetAddress().getHostAddress();
try {
_in = _connection.getInputStream();
_out = new BufferedOutputStream(_connection.getOutputStream());
} catch (IOException ex) {
LOG.warn("There has been an error creating a connection!", ex);
}
KeyPair pair = GameServerTable.getInstance().getKeyPair();
_privateKey = (RSAPrivateKey) pair.getPrivate();
_publicKey = (RSAPublicKey) pair.getPublic();
_blowfish = new NewCrypt("_;v.]05-31!|+-%xT!^[$\00");
setName(getClass().getSimpleName() + "-" + threadId() + "@" + _connectionIp);
start();
}
public void sendPacket(BaseSendablePacket sl) {
try {
byte[] data = sl.getContent();
NewCrypt.appendChecksum(data);
LOG.info("[S] {}:{}{}", sl.getClass().getSimpleName(), System.lineSeparator(), Util.printData(data));
}
_blowfish.crypt(data, 0, data.length);
int len = data.length + 2;
synchronized (_out) {
_out.write(len & 0xff);
_out.write((len >> 8) & 0xff);
_out.write(data);
_out.flush();
}
} catch (IOException ex) {
LOG.error("There has been an error while sending packet {}!", sl.getClass().getSimpleName(), ex);
}
}
public void broadcastToTelnet(String msg) {
if (LoginServer.getInstance().getStatusServer() != null) {
LoginServer.getInstance().getStatusServer().sendMessageToTelnets(msg);
}
}
public void kickPlayer(String account) {
sendPacket(new KickPlayer(account));
}
public void requestCharacters(String account) {
sendPacket(new RequestCharacters(account));
}
public void ChangePasswordResponse(byte successful, String characterName, String msgToSend) {
sendPacket(new ChangePasswordResponse(successful, characterName, msgToSend));
}
public void setGameHosts(String[] hosts) {
LOG.info("Updated game server {}[{}] IPs.", ServerNameDAO.getServer(getServerId()), getServerId());
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
_gsi.clearServerAddresses();
for (int i = 0; i < hosts.length; i += 2) {
try {
_gsi.addServerAddress(hosts[i], hosts[i + 1]);
} catch (Exception ex) {
LOG.warn("There has been an error resolving host name {}!", hosts[i], ex);
}
}
for (String s : _gsi.getServerAddresses()) {
LOG.info(s);
}
}
public boolean isAuthed() {
if (getGameServerInfo() == null) {
return false;
}
return getGameServerInfo().isAuthed();
}
public void setGameServerInfo(GameServerInfo gsi) {
_gsi = gsi;
}
public GameServerInfo getGameServerInfo() {
return _gsi;
}
public String getConnectionIpAddress() {
return _connectionIPAddress;
}
public int getServerId() {
if (getGameServerInfo() != null) {
return getGameServerInfo().getId();
}
return -1;
}
public RSAPrivateKey getPrivateKey() {
return _privateKey;
}
public void SetBlowFish(NewCrypt blowfish) {
_blowfish = blowfish;
}
public void addAccountOnGameServer(String account) {
_accountsOnGameServer.add(account);
}
public void removeAccountOnGameServer(String account) {
_accountsOnGameServer.remove(account);
}
public GameServerState getLoginConnectionState() {
return _loginConnectionState;
}
public void setLoginConnectionState(GameServerState state) {
_loginConnectionState = state;
}
}