/// <summary> /// 블랙리스트, 밴IP 확인 /// </summary> /// <param name="client"></param> /// <returns></returns> private bool isBlacklistOrBanned(cltSession client) { //비밀번호 무한 대입 사용자 처리 if (Blacklist.ContainsKey(client.IPadress)) { #if DEBUG PrintLogs(string.Format("BlacklistIP: {0}", client.IPadress)); #endif Blacklist[client.IPadress] = DateTime.Now; client.Send(Packet.SayMessage(g_Config.Msg_Blacklist, 0, 0, client.Ver)); RemoveClient(client, true, false); return(true); } else { //banned ip, 점검중 체크 string sMsg = BanOrMaintainance(client.IPadress); if (!string.IsNullOrEmpty(sMsg)) { #if DEBUG PrintLogs("BanOrMaintainance: RemoveClient"); #endif client.Send(Packet.SayMessage(sMsg, 0, 0, client.Ver)); RemoveClient(client, true, true); return(true); } } return(false); }
/// <summary> /// Server selection packet processing. /// 서버 선택 패킷 /// </summary> /// <param name="client"></param> /// <param name="buffer"></param> private void SelectedServer(cltSession client, Byte[] buffer) { //22패킷(za 정보: ip, port, new uid) 넘겨주고 접속자 정보의 기존uid를 새 uid로 변경하기. short ServerID = buffer[10]; if (g_Config.ZoneAgentList.ContainsKey(ServerID)) { PrintLogs(string.Format("<CT>ServerID={0}", ServerID)); var User = g_Config.LoggedInList.First(x => x.Value.PCID == client.PCID); zaSession zAgent = g_Config.ZoneAgentList[ServerID]; //ZS용 UID발급 uint zs_PCID = g_Config.za_Uid.Uid; PrintPCID(g_Config.za_Uid.dspUid); //유저가 몇번 서버로 이동하는지 기록함. 응답 패킷에는 서버 번호가 없으므로 여기서 미리 변경 User.Value.ServerID = ServerID; //za에게 유저 정보 넘기기 zAgent.Send(Packet.PreparedAccount(zs_PCID, User.Key)); //클라에게 za정보 넘기기 client.Send(Packet.ZoneAgentInfo(client.PCID, zs_PCID, zAgent.IPadress, zAgent.Port, client.Ver)); //za에 유저가 정상 접속한다면 za에서 답패킷(1f,02,e3)이 날아옴 g_Config.inZoneAgent++; client.Step = "ZA"; } else { PrintLogs(string.Format("<CT>ServerID={0} but ZoneAgent is Down", ServerID)); } }
/// <summary> /// Message arrives from client. /// 클라이언트로부터 메시지 도착 /// </summary> /// <param name="asyncResult"></param> private void RequestReceived(IAsyncResult asyncResult) { cltSession client = (cltSession)asyncResult.AsyncState; try { if (client == null) { return; } int length = client.Stream.EndRead(asyncResult); if (length < 10) { client.TcpClient.Client.Disconnect(false); return; } ExecuteCommand(client, client.Buffer); client.Stream.BeginRead(client.Buffer, 0, client.Buffer.Length, new AsyncCallback(RequestReceived), client); } catch (IOException) { } catch (InvalidOperationException) { } catch (Exception ex) { WriteLogs(string.Format("LoginServer.RequestReceived:{0}{1}", Environment.NewLine, ex)); } }
/// <summary> /// Invalid request count processing. /// 대부분 소켓 연결은 자동으로 끊어지므로 특별한경우 아니면 disconnect처리 필요없음 /// </summary> /// <param name="client"></param> /// <param name="reUseSocket"></param> /// <param name="countFail"></param> private void RemoveClient(cltSession client, bool reUseSocket, bool countFail) { if (countFail) { Task.Factory.StartNew(() => CountFailedAttempts(client.IPadress)); } if (!reUseSocket) { client.TcpClient.Client.Disconnect(reUseSocket); } }
/// <summary> /// New client. /// 클라이언트 핸들러 /// </summary> /// <param name="asyncResult"></param> private void SessionHandler(IAsyncResult asyncResult) { try { TcpClient session = login_Agent.EndAcceptTcpClient(asyncResult); cltSession client = new cltSession(session); client.cltSessionClosed += SessionClosed; g_Config.ClientsList.Add(client); client.Stream.BeginRead(client.Buffer, 0, client.Buffer.Length, new AsyncCallback(RequestReceived), client); login_Agent.BeginAcceptTcpClient(SessionHandler, null); } catch (ObjectDisposedException) { } catch (Exception ex) { WriteLogs(string.Format("LoginServer.ZoneAgentHandler:{0}{1}", Environment.NewLine, ex)); } }
/// <summary> /// Client session termination event. /// 클라이언트 세션 종료 /// </summary> /// <param name="client"></param> private void SessionClosed(cltSession client) { if (g_Config.LoggedInList.Values.Any(x => x.PCID == client.PCID)) { var User = g_Config.LoggedInList.First(x => x.Value.PCID == client.PCID); g_Config.LoggedInList.Remove(User.Key); PrintLogs(string.Format("loginuser.destroy {0}", User.Key)); } #if DEBUG PrintLogs(string.Format("Auto.destroy: {0}@{1}:{2}", client.PCID, client.IPadress, client.Port)); #endif if (client.Step == "ZA") { g_Config.inZoneAgent--; } g_Config.ClientsList.Remove(client); client.Dispose(); }
/// <summary> /// 메시지 처리 /// </summary> /// <param name="buffers"></param> private void ExecuteCommand(cltSession client, Byte[] buffer) { IPEndPoint clientEp = (IPEndPoint)client.TcpClient.Client.RemoteEndPoint; client.IPadress = clientEp.Address.ToString(); client.Port = clientEp.Port; //클라이언트 버전에따라 패킷 자르고 아이피 검사(블랙리스트, 밴, 공사중...) if (CheckClientVersion(client, ref buffer) && !isBlacklistOrBanned(client)) { //새로운 요청이면 uid발급 if (client.PCID == 0) { client.PCID = la_PCID.Uid; PrintLogs(string.Format("NewClient: {0}:{1} {2}", client.IPadress, client.Port, client.PCID)); } MsgAnalysis(client, buffer); } }
/// <summary> /// Client version checking and packet trimming. /// 클라이언트 버전 확인및 패킷 자르기 /// </summary> /// <param name="client"></param> /// <param name="buffer"></param> private bool CheckClientVersion(cltSession client, ref Byte[] buffer) { if (client.Ver == ClientVer.undefined) { int length = BitConverter.ToInt32(buffer, 0); if ((ClientVer.v578 == (ClientVer)length && (g_Config.WorkMode == 0 || g_Config.WorkMode == 3)) || (ClientVer.v562 == (ClientVer)length && (g_Config.WorkMode == 0 || g_Config.WorkMode == 2))) { client.Ver = (ClientVer)length; Packet.TrimPacket(ref buffer, 0); } else if (ClientVer.v219 == (ClientVer)BitConverter.ToInt32(buffer, 0x0A) && (g_Config.WorkMode == 0 || g_Config.WorkMode == 1)) { client.Ver = ClientVer.v219; Packet.TrimPacket(ref buffer, 0x0A); int cVer = BitConverter.ToInt32(buffer, 0x34); //v219에는 클라 버전 체크 기능이 있음. 버전 확인 후 아래 패킷을 보내면 클라 강제 종료됨. if (g_Config.cLowVer > cVer || g_Config.cHighVer < cVer) { client.Send(Packet.InvalidClientVersion(client.Ver)); return(false); } } else { client.TcpClient.Client.Disconnect(false); return(false); } } else if (client.Ver == ClientVer.v219) { Packet.TrimPacket(ref buffer, 0x0A); } else { Packet.TrimPacket(ref buffer, 0); } return(true); }
/// <summary> /// 메시지 분석 /// </summary> /// <param name="buffer"></param> private void MsgAnalysis(cltSession client, Byte[] buffer) { if (buffer[9] == 0xE0 && buffer[8] == 0x01) { //로그인 요청 ReqUserLogin(client, buffer); } else if (buffer[9] == 0xE1 && buffer[8] == 0x01) { //서버 선택 패킷: 로그인 인정된 사용자의 패킷만 처리 if (g_Config.LoggedInList.Values.Any(x => x.PCID == client.PCID)) { SelectedServer(client, buffer); } else { client.TcpClient.Client.Disconnect(false); } } else { WriteNewPacket("CL->LA", buffer, client.IPadress, client.Ver); } }
/// <summary> /// Handling login packets. /// 로그인 요청 패킷 처리 /// </summary> /// <param name="client"></param> /// <param name="buffer"></param> private void ReqUserLogin(cltSession client, Byte[] buffer) { string str_id = Packet.Bytes2String(buffer, 0x0A); string str_pwd = Packet.Bytes2String(buffer, 0x1F); //check blank if (string.IsNullOrEmpty(str_id) || string.IsNullOrEmpty(str_pwd)) { client.Send(Packet.SayMessage(g_Config.Msg_Incorrect, client.PCID, 0, client.Ver)); RemoveClient(client, true, true); #if DEBUG PrintLogs(string.Format("BlankID/PWD: RemoveClient({0})", client.PCID)); #endif } else { //get db info by account AccountInfo AccInfo = Acc_DB.getAccountInfo(str_id); if (string.IsNullOrEmpty(AccInfo.id)) { client.Send(Packet.SayMessage(g_Config.Msg_Incorrect, client.PCID, 0, client.Ver)); RemoveClient(client, true, true); #if DEBUG PrintLogs(string.Format("Wrong ID: RemoveClient({0})", client.PCID)); #endif } else { //check pwd if (!Hasher.Verify(str_pwd, AccInfo.pwd, (HashType)g_Config.hashType)) { client.Send(Packet.SayMessage(g_Config.Msg_Incorrect, client.PCID, 0, client.Ver)); RemoveClient(client, true, true); #if DEBUG PrintLogs(string.Format("Wrong PWD: RemoveClient({0})", client.PCID)); #endif } else { //check status if (g_Config.RejectDB.ContainsKey(AccInfo.status)) { client.Send(Packet.SayMessage(g_Config.RejectDB[AccInfo.status], client.PCID, 3, client.Ver)); RemoveClient(client, true, false); #if DEBUG PrintLogs(string.Format("StatusReject: RemoveClient({0})", client.PCID)); #endif } else { #if DEBUG PrintLogs(string.Format("{0} ExpireDate: {1}", AccInfo.id, AccInfo.expDate)); #endif //check expire if (g_Config.isExpiration && DateTime.Now >= AccInfo.expDate) { client.Send(Packet.SayMessage(g_Config.Msg_Expired, client.PCID, 3, client.Ver)); RemoveClient(client, true, false); } else { //log-in succeed: check duplicate connections if (!g_Config.LoggedInList.ContainsKey(str_id)) { string msg = string.Empty; //아직 서버 선택전이므로 로그인 유저 정보의 Sid = -1로 설정함 g_Config.LoggedInList.Add(str_id, new LoginUser(client.PCID, -1)); if (g_Config.isExpiration) { TimeSpan ts = AccInfo.expDate - DateTime.Now; msg = string.Format(g_Config.Msg_ExpireInfo, ts.Days, ts.Hours, ts.Minutes); } client.Send(Packet.CreateSvrList(client.PCID, client.Ver, g_Config.ZoneAgentList, g_Config.ServerList, msg)); PrintLogs(string.Format("SuccLogin: {0}:{1} {2}", client.IPadress, client.Port, str_id)); #if DEBUG PrintLogs(string.Format("RegLoginUser: {0}", str_id)); #endif } else { //duplication act: in LA OR in ZA - uid가 ZaUID의 최소값(65535)이상이면 이미 za로 넘어간 유저 PrintLogs(string.Format("Duplicate login detected {0} {1}", g_Config.LoggedInList[str_id].PCID, str_id)); //za 유저인경우 za에게 디스커넥트 요청을 해야함: 완료시 30패킷을 되돌려받음 if (g_Config.LoggedInList[str_id].PCID > g_Config.za_Uid.MinValue) { zaSession zAgent = g_Config.ZoneAgentList[g_Config.LoggedInList[str_id].ServerID]; zAgent.Send(Packet.reqDisconnectAcc(g_Config.LoggedInList[str_id].PCID, str_id)); PrintLogs(string.Format("Request UserDrop {0} {1} to ZoneAgent", g_Config.LoggedInList[str_id].PCID, str_id)); } g_Config.LoggedInList.Remove(str_id); PrintLogs(string.Format("Previous LoggedInUser.destroy {0}", str_id)); client.Send(Packet.SayMessage(g_Config.Msg_Duplication, client.PCID, 2, client.Ver)); RemoveClient(client, true, false); } } } } } } }