package cc.blynk.server.hardware.handlers.hardware.auth; import cc.blynk.server.Holder; import cc.blynk.server.core.BlockingIOProcessor; import cc.blynk.server.core.dao.TokenValue; import cc.blynk.server.core.model.DashBoard; import cc.blynk.server.core.model.auth.Session; import cc.blynk.server.core.model.auth.User; import cc.blynk.server.core.model.device.Device; import cc.blynk.server.core.protocol.handlers.DefaultExceptionHandler; import cc.blynk.server.core.protocol.model.messages.appllication.LoginMessage; import cc.blynk.server.core.session.HardwareStateHolder; import cc.blynk.server.handlers.DefaultReregisterHandler; import cc.blynk.server.handlers.common.HardwareNotLoggedHandler; import cc.blynk.server.hardware.handlers.hardware.HardwareHandler; import cc.blynk.server.redis.RedisClient; import cc.blynk.utils.IPUtils; import cc.blynk.utils.StringUtils; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import static cc.blynk.server.core.protocol.enums.Command.*; import static cc.blynk.server.core.protocol.enums.Response.INVALID_TOKEN; import static cc.blynk.utils.BlynkByteBufUtil.*; /** * Handler responsible for managing hardware and apps login messages. * Initializes netty channel with a state tied with user. * * The Blynk Project. * Created by Dmitriy Dumanskiy. * Created on 2/1/2015. * */ @ChannelHandler.Sharable public class HardwareLoginHandler extends SimpleChannelInboundHandler<LoginMessage> implements DefaultReregisterHandler, DefaultExceptionHandler { private static final Logger log = LogManager.getLogger(DefaultExceptionHandler.class); private static final int HARDWARE_PIN_MODE_MSG_ID = 1; private final Holder holder; private final RedisClient redisClient; private final BlockingIOProcessor blockingIOProcessor; private final String listenPort; public HardwareLoginHandler(Holder holder, int listenPort) { this.holder = holder; this.redisClient = holder.redisClient; this.blockingIOProcessor = holder.blockingIOProcessor; this.listenPort = String.valueOf(listenPort); } private static void completeLogin(Channel channel, Session session, User user, DashBoard dash, int deviceId, int msgId) { log.debug("completeLogin. {}", channel); session.addHardChannel(channel); channel.write(ok(msgId)); final String body = dash.buildPMMessage(deviceId); if (dash.isActive && body.length() > 2) { channel.write(makeASCIIStringMessage(HARDWARE, HARDWARE_PIN_MODE_MSG_ID, body)); } channel.flush(); session.sendToApps(HARDWARE_CONNECTED, msgId, dash.id, deviceId); Device device = dash.getDeviceById(deviceId); if (device != null) { log.trace("Connected device id {}, dash id {}", deviceId, dash.id); device.connected(); device.lastLoggedIP = IPUtils.getIp(channel); } log.info("{} hardware joined.", user.email); } @Override protected void channelRead0(ChannelHandlerContext ctx, LoginMessage message) throws Exception { final String token = message.body.trim(); final TokenValue tokenValue = holder.tokenManager.getUserByToken(token); //no user on current server, trying to find server that user belongs to. if (tokenValue == null) { //checkUserOnOtherServer(ctx, token, message.id); log.debug("HardwareLogic token is invalid. Token '{}', '{}'", token, ctx.channel().remoteAddress()); ctx.writeAndFlush(makeResponse(message.id, INVALID_TOKEN), ctx.voidPromise()); return; } final User user = tokenValue.user; final int dashId = tokenValue.dashId; final int deviceId = tokenValue.deviceId; DashBoard dash = user.profile.getDashById(dashId); if (dash == null) { log.warn("User : {} requested token {} for non-existing {} dash id.", user.email, token, dashId); ctx.writeAndFlush(makeResponse(message.id, INVALID_TOKEN), ctx.voidPromise()); return; } ctx.pipeline().remove(this); ctx.pipeline().remove(HardwareNotLoggedHandler.class); HardwareStateHolder hardwareStateHolder = new HardwareStateHolder(dashId, deviceId, user, token); ctx.pipeline().addLast("HHArdwareHandler", new HardwareHandler(holder, hardwareStateHolder)); Session session = holder.sessionDao.getOrCreateSessionByUser(hardwareStateHolder.userKey, ctx.channel().eventLoop()); if (session.initialEventLoop != ctx.channel().eventLoop()) { log.debug("Re registering hard channel. {}", ctx.channel()); reRegisterChannel(ctx, session, channelFuture -> completeLogin(channelFuture.channel(), session, user, dash, deviceId, message.id)); } else { completeLogin(ctx.channel(), session, user, dash, deviceId, message.id); } } private void checkUserOnOtherServer(ChannelHandlerContext ctx, String token, int msgId) { blockingIOProcessor.executeDB(() -> { String server = redisClient.getServerByToken(token); // no server found, that's means token is wrong. if (server == null || server.equals(holder.host)) { log.debug("HardwareLogic token is invalid. Token '{}', '{}'", token, ctx.channel().remoteAddress()); ctx.writeAndFlush(makeResponse(msgId, INVALID_TOKEN), ctx.voidPromise()); } else { log.info("Redirecting token '{}', '{}' to {}", token, ctx.channel().remoteAddress(), server); ctx.writeAndFlush(makeASCIIStringMessage(CONNECT_REDIRECT, msgId, server + StringUtils.BODY_SEPARATOR + listenPort), ctx.voidPromise()); } }); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { handleGeneralException(ctx, cause); } }