package games.strategy.net.nio;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import games.strategy.net.ILoginValidator;
import games.strategy.net.MessageHeader;
import games.strategy.net.Node;
import games.strategy.net.ServerMessenger;
public class ServerQuarantineConversation extends QuarantineConversation {
/**
* Communication sequence
* 1) server reads client name
* 2) server sends challenge (or null if no challenge is to be made)
* 3) server reads response (or null if no challenge)
* 4) server send null then client name and node info on success, or an error message if there is an error
* 5) if the client reads an error message, the client sends an acknowledgment (we need to make sur the client gets
* the message before
* closing the socket).
*/
private static final Logger s_logger = Logger.getLogger(ServerQuarantineConversation.class.getName());
private enum STEP {
READ_NAME, READ_MAC, CHALLENGE, ACK_ERROR
}
private final ILoginValidator m_validator;
private final SocketChannel m_channel;
private final NIOSocket m_socket;
private STEP m_step = STEP.READ_NAME;
private String m_remoteName;
private String m_remoteMac;
private Map<String, String> challenge;
private final ServerMessenger m_serverMessenger;
public ServerQuarantineConversation(final ILoginValidator validator, final SocketChannel channel,
final NIOSocket socket, final ServerMessenger serverMessenger) {
m_validator = validator;
m_socket = socket;
m_channel = channel;
m_serverMessenger = serverMessenger;
}
public String getRemoteName() {
return m_remoteName;
}
public String getRemoteMac() {
return m_remoteMac;
}
@Override
@SuppressWarnings("unchecked")
public ACTION message(final Object o) {
try {
switch (m_step) {
case READ_NAME:
// read name, send challent
m_remoteName = (String) o;
if (s_logger.isLoggable(Level.FINER)) {
s_logger.log(Level.FINER, "read name:" + m_remoteName);
}
m_step = STEP.READ_MAC;
return ACTION.NONE;
case READ_MAC:
// read name, send challent
m_remoteMac = (String) o;
if (s_logger.isLoggable(Level.FINER)) {
s_logger.log(Level.FINER, "read mac:" + m_remoteMac);
}
if (m_validator != null) {
challenge = m_validator.getChallengeProperties(m_remoteName, m_channel.socket().getRemoteSocketAddress());
}
if (s_logger.isLoggable(Level.FINER)) {
s_logger.log(Level.FINER, "writing challenge:" + challenge);
}
send((Serializable) challenge);
m_step = STEP.CHALLENGE;
return ACTION.NONE;
case CHALLENGE:
final Map<String, String> response = (Map<String, String>) o;
if (s_logger.isLoggable(Level.FINER)) {
s_logger.log(Level.FINER, "read challenge response:" + response);
}
if (m_validator != null) {
final String error = m_validator.verifyConnection(challenge, response, m_remoteName, m_remoteMac,
m_channel.socket().getRemoteSocketAddress());
if (s_logger.isLoggable(Level.FINER)) {
s_logger.log(Level.FINER, "error:" + error);
}
send(error);
if (error != null) {
m_step = STEP.ACK_ERROR;
return ACTION.NONE;
}
} else {
send(null);
}
// get a unique name
m_remoteName = m_serverMessenger.getUniqueName(m_remoteName);
if (s_logger.isLoggable(Level.FINER)) {
s_logger.log(Level.FINER, "Sending name:" + m_remoteName);
}
// send the node its name and our name
send(new String[] {m_remoteName, m_serverMessenger.getLocalNode().getName()});
// send the node its and our address as we see it
send(new InetSocketAddress[] {(InetSocketAddress) m_channel.socket().getRemoteSocketAddress(),
m_serverMessenger.getLocalNode().getSocketAddress()});
// Login succeeded, so notify the ServerMessenger about the login with the name, mac, etc.
m_serverMessenger.NotifyPlayerLogin(m_remoteName, m_channel.socket().getInetAddress().getHostAddress(),
m_remoteMac);
// We are good
return ACTION.UNQUARANTINE;
case ACK_ERROR:
return ACTION.TERMINATE;
default:
throw new IllegalStateException("Invalid state");
}
} catch (final Throwable t) {
s_logger.log(Level.SEVERE, "Error with connection", t);
return ACTION.TERMINATE;
}
}
private void send(final Serializable object) {
// this messenger is quarantined, so to and from dont matter
final MessageHeader header = new MessageHeader(Node.NULL_NODE, Node.NULL_NODE, object);
m_socket.send(m_channel, header);
}
@Override
public void close() {}
}