package com.cwa.msg;
import com.cwa.gamecore.dispatcher.DefaultActionDispatcher;
import com.cwa.gamecore.message.GameMessage;
import com.cwa.gamecore.message.GameRequest;
import com.cwa.gamecore.message.GameResponse;
import com.cwa.gamecore.session.CatchResponseGameSession;
import com.cwa.gamecore.session.GameSession;
import com.cwa.msg.batch.BatchRequest;
import com.cwa.msg.batch.BatchResponse;
import com.cwa.msg.compression.GzipRequest;
import com.cwa.msg.encryption.CipherRequest;
import com.cwa.msg.mac.MacRequest;
import com.cwa.msg.sign.SignResponseGameSession;
import java.util.Arrays;
import java.util.List;
import org.apache.log4j.Logger;
public class SmartActionDispatcher extends DefaultActionDispatcher {
private static final Logger logger = Logger.getLogger(SmartActionDispatcher.class);
private SecretProvider secretProvider;
// 注意:safeCommands和unsafeCommands不应该同时配置
private List<Integer> safeCommands;
private List<Integer> unsafeCommands;
// 需要RSA签名的响应消息
private List<Integer> responsesNeedToSign;
public SmartActionDispatcher() {
}
public void setSecretProvider(SecretProvider secretProvider) {
this.secretProvider = secretProvider;
}
public SecretProvider getSecretProvider() {
return secretProvider;
}
/*
* 设置安全消息列表。
* 如果配置了该列表的话,列表中的消息可以被安全执行。
*/
public void setSafeCommands(List<Integer> safeCommands) {
this.safeCommands = safeCommands;
}
public List<Integer> getSafeCommands() {
return safeCommands;
}
/*
* 不安全消息列表。
* 如果配置了该列表的话,只有列表中的消息需要受到保护。
*/
public void setUnsafeCommands(List<Integer> unsafeCommands) {
this.unsafeCommands = unsafeCommands;
}
public List<Integer> getUnsafeCommands() {
return unsafeCommands;
}
/**
* 设置需要RSA签名的响应消息列表。
*/
public void setResponsesNeedToSign(List<Integer> responsesNeedToSign) {
this.responsesNeedToSign = responsesNeedToSign;
}
public List<Integer> getResponsesNeedToSign() {
return responsesNeedToSign;
}
@Override
public void dispatchAction(GameSession session, GameRequest req) {
switch (req.getCommandId()) {
case GameMessage.CMD_RSA_ACTION: {
// 加密消息
handleCipherReq(session, (CipherRequest) req);
return;
}
case GameMessage.CMD_MD5_ACTION:
case GameMessage.CMD_SHA256_ACTION: {
// 消息带有MAC
handleMacReq(session, (MacRequest) req);
return;
}
case GameMessage.CMD_GZIP_ACTION: {
// GZIP消息
handleGzipReq(session, (GzipRequest) req);
return;
}
case GameMessage.CMD_BATCH_ACTION: {
// 批量消息
handleBatchReq(session, (BatchRequest) req);
return;
}
}
if (isSafeMessage(req)) {
// 安全消息
handleSafeReq(session, req);
return;
}
// 不允许直接执行的消息!
throw new RuntimeException("Unsafe message: " + req);
}
private void handleCipherReq(GameSession session, CipherRequest req) {
if (logger.isDebugEnabled()) {
logger.debug("handle cipher message:" + req);
}
req.decrypt();
execReq(session, req.getPlainRequest());
}
private void handleMacReq(GameSession session, MacRequest req) {
if (logger.isDebugEnabled()) {
logger.debug("handle message with MAC:" + req);
}
checkMac(session, req);
execReq(session, req.getWrappedRequest());
}
// 检查MAC
private void checkMac(GameSession session, MacRequest req) {
byte[] secretData = secretProvider.getSecret(session, req.getWrappedRequest());
if (!req.checkMac(secretData)) {
// MAC不匹配
if (logger.isDebugEnabled()) {
logger.debug("MAC not match!");
logger.debug("message MAC:" + Arrays.toString(req.getMsgMac()));
logger.debug("calculated MAC:" + Arrays.toString(req.getMsgMac2()));
}
throw new RuntimeException("Message has been modified!");
}
}
private void handleGzipReq(GameSession session, GzipRequest req) {
if (logger.isDebugEnabled()) {
logger.debug("handle GZIP message:" + req);
}
dispatchAction(session, req.getWrappedRequest());
}
private void handleBatchReq(GameSession session, BatchRequest req) {
if (logger.isDebugEnabled()) {
logger.debug("handle batch message:" + req);
}
GameRequest[] subReqs = req.getRequests();
GameResponse[] subResps = new GameResponse[subReqs.length];
CatchResponseGameSession subSession = new CatchResponseGameSession();
for (int i = 0; i < subReqs.length; i++) {
GameRequest subReq = subReqs[i];
dispatchAction(subSession, subReq);
GameResponse subResp = subSession.getResponse();
subResps[i] = subResp;
}
BatchResponse resp = new BatchResponse(subResps);
session.write(resp);
}
// 是否消息可以被直接处理
private boolean isSafeMessage(GameRequest req) {
if (safeCommands != null && safeCommands.contains(req.getCommandId())) {
return true;
}
if (unsafeCommands != null && !unsafeCommands.contains(req.getCommandId())) {
return true;
}
return false;
}
// 安全消息,直接处理
private void handleSafeReq(GameSession session, GameRequest req) {
if (logger.isDebugEnabled()) {
logger.debug("handle safe message:" + req);
}
execReq(session, req);
}
private void execReq(GameSession session, GameRequest req) {
if (needToSignResponse(req)) {
super.dispatchAction(new SignResponseGameSession(session), req);
} else {
super.dispatchAction(session, req);
}
}
// 是否需要对响应消息RSA签名
private boolean needToSignResponse(GameRequest req) {
return responsesNeedToSign != null
&& responsesNeedToSign.contains(req.getCommandId());
}
}