public async Task<LoginCheckerResponse> Login(Login login, string ip)
        {
            await semaphore.WaitAsync();
            try
            {
                var userID = login.UserID;
                var lobbyVersion = login.LobbyVersion;

                using (var db = new ZkDataContext())
                {
                    if (!VerifyIp(ip)) return new LoginCheckerResponse(LoginResponse.Code.Banned, "Too many conneciton attempts");

                    var acc = db.Accounts.Include(x => x.Clan).Include(x => x.Faction).FirstOrDefault(x => x.Name == login.Name);
                    if (acc == null)
                    {
                        LogIpFailure(ip);
                        return new LoginCheckerResponse(LoginResponse.Code.InvalidName, "Invalid user name");
                    }
                    if (!acc.VerifyPassword(login.PasswordHash))
                    {
                        LogIpFailure(ip);
                        return new LoginCheckerResponse(LoginResponse.Code.InvalidPassword, "Invalid password");
                    }

                    var ret = new LoginCheckerResponse(LoginResponse.Code.Ok, null);
                    var user = ret.User;

                    acc.Country = ResolveCountry(ip);
                    if ((acc.Country == null) || string.IsNullOrEmpty(acc.Country)) acc.Country = "unknown";
                    acc.LobbyVersion = lobbyVersion;
                    acc.LastLogin = DateTime.UtcNow;

                    user.LobbyVersion = login.LobbyVersion;
                    user.IpAddress = ip;
                    UpdateUserFromAccount(user, acc);
                    LogIP(db, acc, ip);
                    LogUserID(db, acc, userID);

                    db.SaveChanges();

                    ret.LoginResponse.SessionToken = Guid.NewGuid().ToString(); // create session token

                    var banPenalty = Punishment.GetActivePunishment(acc.AccountID, ip, userID, x => x.BanLobby);

                    if (banPenalty != null)
                        return
                            BlockLogin(
                                $"Banned until {banPenalty.BanExpires} (match to {banPenalty.AccountByAccountID.Name}), reason: {banPenalty.Reason}",
                                acc,
                                ip,
                                userID);

                    if (!acc.HasVpnException && GlobalConst.VpnCheckEnabled) if (HasVpn(ip, acc, db)) return BlockLogin("Connection using proxy or VPN is not allowed! (You can ask for exception)", acc, ip, userID);

                    return ret;
                }
            }
            finally
            {
                semaphore.Release();
            }
        }
        public LoginResponse Login(User user, Login login, Client client)
        {
            string ip = client.RemoteEndpointIP;
            long userID = login.UserID;
            string lobbyVersion = login.LobbyVersion;

            using (var db = new ZkDataContext()) {
                Account acc = db.Accounts.Include(x => x.Clan).Include(x => x.Faction).FirstOrDefault(x => x.Name == login.Name);
                if (acc == null) return new LoginResponse { ResultCode = LoginResponse.Code.InvalidName };
                if (!acc.VerifyPassword(login.PasswordHash)) return new LoginResponse { ResultCode = LoginResponse.Code.InvalidPassword };
                if (state.Clients.ContainsKey(login.Name)) return new LoginResponse { ResultCode = LoginResponse.Code.AlreadyConnected };
                
                acc.Country = ResolveCountry(ip);
                if (acc.Country == null || String.IsNullOrEmpty(acc.Country)) acc.Country = "unknown";
                acc.LobbyVersion = lobbyVersion;
                acc.LastLogin = DateTime.UtcNow;

                user.ClientType = login.ClientType;
                user.LobbyVersion = login.LobbyVersion;
                UpdateUserFromAccount(user, acc);

                LogIP(db, acc, ip);

                LogUserID(db, acc, userID);

                db.SaveChanges();


                var banMute = Punishment.GetActivePunishment(acc.AccountID, ip, userID, x => x.BanMute, db);
                if (banMute != null) user.BanMute = true;
                

                Punishment banPenalty = Punishment.GetActivePunishment(acc.AccountID, ip, userID, x => x.BanLobby, db);

                if (banPenalty != null) {
                    return
                        BlockLogin(
                            string.Format("Banned until {0} (match to {1}), reason: {2}", banPenalty.BanExpires, banPenalty.AccountByAccountID.Name,
                                banPenalty.Reason), acc, ip, userID);
                }

                Account accAnteep = db.Accounts.FirstOrDefault(x => x.AccountID == 4490);
                if (accAnteep != null) {
                    if (accAnteep.AccountUserIDs.Any(y => y.UserID == userID)) {
                        Talk(String.Format("Suspected Anteep smurf: {0} (ID match {1}) {2}", acc.Name, userID,
                            string.Format("{1}/Users/Detail/{0}", acc.AccountID, GlobalConst.BaseSiteUrl)));
                    }

                    if (userID > 0 && userID < 1000) {
                        Talk(String.Format("Suspected Anteep smurf: {0} (too short userID {1}) {2}", acc.Name, userID,
                            string.Format("{1}/Users/Detail/{0}", acc.AccountID, GlobalConst.BaseSiteUrl)));
                    }

                    if (accAnteep.AccountIPs.Any(y => y.IP == ip)) {
                        Talk(String.Format("Suspected Anteep smurf: {0} (IP match {1}) {2}", acc.Name, ip,
                            string.Format("{1}/Users/Detail/{0}", acc.AccountID, GlobalConst.BaseSiteUrl)));
                    }
                }

                if (!acc.HasVpnException && GlobalConst.VpnCheckEnabled) {
                    // check user IP against http://dnsbl.tornevall.org
                    // does not catch all smurfs
                    // mostly false positives, do not use
                    string reversedIP = string.Join(".", ip.Split('.').Reverse().ToArray());
                    try {
                        IPAddress[] resolved = Dns.GetHostEntry(string.Format("{0}.dnsbl.tornevall.org", reversedIP)).AddressList;
                        if (resolved.Length > 0) {
                            Talk(String.Format("User {0} {3} has IP {1} on dnsbl.tornevall.org ({2} result/s)", acc.Name, ip, resolved.Length,
                                string.Format("{1}/Users/Detail/{0}", acc.AccountID, GlobalConst.BaseSiteUrl)));
                        }
                    } catch (SocketException sockEx) {
                        // not in database, do nothing
                    }
                }

                try {
                    if (!acc.HasVpnException) {
                        for (int i = 0; i <= 1; i++) {
                            var whois = new Whois();
                            Dictionary<string, string> data = whois.QueryByIp(ip, i == 1);

                            if (!data.ContainsKey("netname")) data["netname"] = "UNKNOWN NETNAME";
                            if (!data.ContainsKey("org-name")) data["org-name"] = "UNKNOWN ORG";
                            if (!data.ContainsKey("abuse-mailbox")) data["abuse-mailbox"] = "no mailbox";
                            if (!data.ContainsKey("notify")) data["notify"] = "no notify address";
                            if (!data.ContainsKey("role")) data["role"] = "UNKNOWN ROLE";
                            if (!data.ContainsKey("descr")) data["descr"] = "no description";
                            if (!data.ContainsKey("remarks")) data["remarks"] = "no remarks";

                            List<string> blockedCompanies = db.BlockedCompanies.Select(x => x.CompanyName.ToLower()).ToList();
                            List<string> blockedHosts = db.BlockedHosts.Select(x => x.HostName).ToList();
                            /*if (acc.Country == "MY")
                        {
                            client.Say(SayPlace.User, "KingRaptor", String.Format("USER {0}\nnetname: {1}\norgname: {2}\ndescr: {3}\nabuse-mailbox: {4}",
                                acc.Name, data["netname"], data["org-name"], data["descr"], data["abuse-mailbox"]), false);
                        }*/

                            bool blockedHost = blockedHosts.Any(x => data["abuse-mailbox"].Contains(x)) ||
                                               (blockedHosts.Any(x => data["notify"].Contains(x)));

                            foreach (string company in blockedCompanies) {
                                if (data["netname"].ToLower().Contains(company) || data["org-name"].ToLower().Contains(company) ||
                                    data["descr"].ToLower().Contains(company) || data["role"].ToLower().Contains(company) ||
                                    data["remarks"].ToLower().Contains(company)) {
                                    blockedHost = true;
                                    break;
                                }
                            }

                            string hostname = Dns.GetHostEntry(ip).HostName;
                            if (blockedHosts.Any(hostname.Contains)) blockedHost = true;

                            if (blockedHost) return BlockLogin("Connection using proxy or VPN is not allowed! (You can ask for exception)", acc, ip, userID);
                        }
                    }
                } catch (SocketException sockEx) {} catch (Exception ex) {
                    Trace.TraceError("VPN check error: {0}", ex);
                }

                if (state.Clients.TryAdd(login.Name, client)) return new LoginResponse { ResultCode = LoginResponse.Code.Ok };
                else return new LoginResponse() {ResultCode = LoginResponse.Code.AlreadyConnected};
            }
        }