fix login issue*
This commit is contained in:
@@ -14,7 +14,8 @@ defmodule Odinsea.Login.Handler do
|
||||
require Logger
|
||||
|
||||
alias Odinsea.Net.Packet.{In, Out}
|
||||
alias Odinsea.Net.Cipher.LoginCrypto
|
||||
alias Odinsea.Net.Cipher.{ClientCrypto, LoginCrypto}
|
||||
alias Odinsea.Net.PacketLogger
|
||||
alias Odinsea.Login.Packets
|
||||
alias Odinsea.Constants.Server
|
||||
alias Odinsea.Database.Context
|
||||
@@ -79,19 +80,26 @@ defmodule Odinsea.Login.Handler do
|
||||
Logger.info("Login attempt: username=#{username} from #{state.ip}")
|
||||
|
||||
# Check if IP/MAC is banned
|
||||
features = Application.get_env(:odinsea, :features, [])
|
||||
skip_maccheck = Keyword.get(features, :skip_maccheck, false)
|
||||
|
||||
ip_banned = Context.ip_banned?(state.ip)
|
||||
mac_banned = Context.mac_banned?(state.mac)
|
||||
mac_banned = if skip_maccheck or state.macs == [] do
|
||||
false
|
||||
else
|
||||
Enum.any?(state.macs, &Context.mac_banned?/1)
|
||||
end
|
||||
|
||||
if (ip_banned || mac_banned) do
|
||||
Logger.warning("Banned IP/MAC attempted login: ip=#{state.ip}, mac=#{state.mac}")
|
||||
|
||||
Logger.warning("Banned IP/MAC attempted login: ip=#{state.ip}, macs=#{inspect(state.macs)}")
|
||||
|
||||
# If MAC banned, also ban the IP for enforcement
|
||||
if mac_banned do
|
||||
Context.ban_ip_address(state.ip, "Enforcing account ban, account #{username}", false, 4)
|
||||
end
|
||||
|
||||
|
||||
response = Packets.get_login_failed(3)
|
||||
send_packet(state, response)
|
||||
state = send_packet(state, response)
|
||||
{:ok, state}
|
||||
else
|
||||
# Authenticate with database
|
||||
@@ -106,7 +114,7 @@ defmodule Odinsea.Login.Handler do
|
||||
format_timestamp(temp_ban_info.expires),
|
||||
temp_ban_info.reason || ""
|
||||
)
|
||||
send_packet(state, response)
|
||||
state = send_packet(state, response)
|
||||
{:ok, state}
|
||||
else
|
||||
# Check if already logged in - kick other session
|
||||
@@ -132,6 +140,8 @@ defmodule Odinsea.Login.Handler do
|
||||
account_info.second_password
|
||||
)
|
||||
|
||||
state = send_packet(state, response)
|
||||
|
||||
new_state =
|
||||
state
|
||||
|> Map.put(:logged_in, true)
|
||||
@@ -142,8 +152,8 @@ defmodule Odinsea.Login.Handler do
|
||||
|> Map.put(:second_password, account_info.second_password)
|
||||
|> Map.put(:login_attempts, 0)
|
||||
|
||||
send_packet(state, response)
|
||||
{:ok, new_state}
|
||||
# Send world info immediately after auth success (Java: LoginWorker.registerClient)
|
||||
on_world_info_request(new_state)
|
||||
end
|
||||
|
||||
{:error, :invalid_credentials} ->
|
||||
@@ -156,7 +166,7 @@ defmodule Odinsea.Login.Handler do
|
||||
else
|
||||
# Send login failed (reason 4 = incorrect password)
|
||||
response = Packets.get_login_failed(4)
|
||||
send_packet(state, response)
|
||||
state = send_packet(state, response)
|
||||
|
||||
new_state = Map.put(state, :login_attempts, login_attempts)
|
||||
{:ok, new_state}
|
||||
@@ -165,7 +175,7 @@ defmodule Odinsea.Login.Handler do
|
||||
{:error, :account_not_found} ->
|
||||
# Send login failed (reason 5 = not registered ID)
|
||||
response = Packets.get_login_failed(5)
|
||||
send_packet(state, response)
|
||||
state = send_packet(state, response)
|
||||
{:ok, state}
|
||||
|
||||
{:error, :already_logged_in} ->
|
||||
@@ -176,12 +186,12 @@ defmodule Odinsea.Login.Handler do
|
||||
|
||||
# Send login failed (reason 7 = already logged in) but client can retry
|
||||
response = Packets.get_login_failed(7)
|
||||
send_packet(state, response)
|
||||
state = send_packet(state, response)
|
||||
{:ok, state}
|
||||
|
||||
{:error, :banned} ->
|
||||
response = Packets.get_perm_ban(0)
|
||||
send_packet(state, response)
|
||||
state = send_packet(state, response)
|
||||
{:ok, state}
|
||||
end
|
||||
end
|
||||
@@ -209,19 +219,19 @@ defmodule Odinsea.Login.Handler do
|
||||
channel_load
|
||||
)
|
||||
|
||||
send_packet(state, server_list)
|
||||
state = send_packet(state, server_list)
|
||||
|
||||
# Send end of server list
|
||||
end_list = Packets.get_end_of_server_list()
|
||||
send_packet(state, end_list)
|
||||
state = send_packet(state, end_list)
|
||||
|
||||
# Send latest connected world
|
||||
latest_world = Packets.get_latest_connected_world(0)
|
||||
send_packet(state, latest_world)
|
||||
state = send_packet(state, latest_world)
|
||||
|
||||
# Send recommended world message
|
||||
recommend = Packets.get_recommend_world_message(0, "Join now!")
|
||||
send_packet(state, recommend)
|
||||
state = send_packet(state, recommend)
|
||||
|
||||
{:ok, state}
|
||||
end
|
||||
@@ -246,7 +256,7 @@ defmodule Odinsea.Login.Handler do
|
||||
end
|
||||
|
||||
response = Packets.get_server_status(status)
|
||||
send_packet(state, response)
|
||||
state = send_packet(state, response)
|
||||
|
||||
{:ok, state}
|
||||
end
|
||||
@@ -277,7 +287,7 @@ defmodule Odinsea.Login.Handler do
|
||||
if world_id != 0 do
|
||||
Logger.warning("Invalid world ID: #{world_id}")
|
||||
response = Packets.get_login_failed(10)
|
||||
send_packet(state, response)
|
||||
state = send_packet(state, response)
|
||||
{:ok, state}
|
||||
else
|
||||
# TODO: Check if channel is available
|
||||
@@ -299,7 +309,7 @@ defmodule Odinsea.Login.Handler do
|
||||
3 # character slots
|
||||
)
|
||||
|
||||
send_packet(state, response)
|
||||
state = send_packet(state, response)
|
||||
|
||||
new_state =
|
||||
state
|
||||
@@ -332,7 +342,7 @@ defmodule Odinsea.Login.Handler do
|
||||
name_used = check_name_used(char_name, state)
|
||||
|
||||
response = Packets.get_char_name_response(char_name, name_used)
|
||||
send_packet(state, response)
|
||||
state = send_packet(state, response)
|
||||
|
||||
{:ok, state}
|
||||
end
|
||||
@@ -381,7 +391,7 @@ defmodule Odinsea.Login.Handler do
|
||||
# Validate name is not forbidden and doesn't exist
|
||||
if check_name_used(name, state) do
|
||||
response = Packets.get_add_new_char_entry(nil, false)
|
||||
send_packet(state, response)
|
||||
state = send_packet(state, response)
|
||||
{:ok, state}
|
||||
else
|
||||
# TODO: Validate appearance items are eligible for gender/job type
|
||||
@@ -420,8 +430,8 @@ defmodule Odinsea.Login.Handler do
|
||||
# Reload character with full data
|
||||
char_data = Context.load_character(character.id)
|
||||
response = Packets.get_add_new_char_entry(char_data, true)
|
||||
send_packet(state, response)
|
||||
|
||||
state = send_packet(state, response)
|
||||
|
||||
# Add character ID to state's character list
|
||||
new_char_ids = [character.id | Map.get(state, :character_ids, [])]
|
||||
new_state = Map.put(state, :character_ids, new_char_ids)
|
||||
@@ -430,7 +440,7 @@ defmodule Odinsea.Login.Handler do
|
||||
{:error, changeset} ->
|
||||
Logger.error("Failed to create character: #{inspect(changeset.errors)}")
|
||||
response = Packets.get_add_new_char_entry(nil, false)
|
||||
send_packet(state, response)
|
||||
state = send_packet(state, response)
|
||||
{:ok, state}
|
||||
end
|
||||
end
|
||||
@@ -511,8 +521,8 @@ defmodule Odinsea.Login.Handler do
|
||||
end
|
||||
|
||||
response = Packets.get_delete_char_response(character_id, result)
|
||||
send_packet(state, response)
|
||||
|
||||
state = send_packet(state, response)
|
||||
|
||||
# Update state if successful
|
||||
new_state =
|
||||
if result == 0 do
|
||||
@@ -559,7 +569,7 @@ defmodule Odinsea.Login.Handler do
|
||||
# Validate second password length
|
||||
if String.length(new_spw) < 6 || String.length(new_spw) > 16 do
|
||||
response = Packets.get_second_pw_error(0x14)
|
||||
send_packet(state, response)
|
||||
state = send_packet(state, response)
|
||||
{:ok, state}
|
||||
else
|
||||
# Update second password
|
||||
@@ -605,9 +615,9 @@ defmodule Odinsea.Login.Handler do
|
||||
|
||||
# Send migration command
|
||||
response = Packets.get_server_ip(false, channel_ip, channel_port, character_id)
|
||||
send_packet(state, response)
|
||||
|
||||
new_state =
|
||||
state = send_packet(state, response)
|
||||
|
||||
new_state =
|
||||
state
|
||||
|> Map.put(:character_id, character_id)
|
||||
|> Map.put(:migration_token, migration_token)
|
||||
@@ -643,7 +653,7 @@ defmodule Odinsea.Login.Handler do
|
||||
else
|
||||
# Failure - send error
|
||||
response = Packets.get_second_pw_error(15) # Incorrect SPW
|
||||
send_packet(state, response)
|
||||
state = send_packet(state, response)
|
||||
{:ok, state}
|
||||
end
|
||||
end
|
||||
@@ -662,14 +672,12 @@ defmodule Odinsea.Login.Handler do
|
||||
# TODO: Send damage cap packet if custom client
|
||||
|
||||
# Send login background
|
||||
background = Application.get_env(:odinsea, :login_background, "MapLogin")
|
||||
bg_response = Packets.get_login_background(background)
|
||||
send_packet(state, bg_response)
|
||||
bg_response = Packets.get_login_background(Server.maplogin_default())
|
||||
state = send_packet(state, bg_response)
|
||||
|
||||
# Send RSA public key
|
||||
pub_key = Application.get_env(:odinsea, :rsa_public_key, "")
|
||||
key_response = Packets.get_rsa_key(pub_key)
|
||||
send_packet(state, key_response)
|
||||
key_response = Packets.get_rsa_key(Server.pub_key())
|
||||
state = send_packet(state, key_response)
|
||||
|
||||
{:ok, state}
|
||||
end
|
||||
@@ -678,27 +686,40 @@ defmodule Odinsea.Login.Handler do
|
||||
# Helper Functions
|
||||
# ==================================================================================================
|
||||
|
||||
defp send_packet(%{socket: socket} = state, packet_data) do
|
||||
# Add header (2 bytes: packet length)
|
||||
packet_length = byte_size(packet_data)
|
||||
header = <<packet_length::little-size(16)>>
|
||||
full_packet = header <> packet_data
|
||||
defp send_packet(%{socket: socket, crypto: crypto} = state, packet_data) do
|
||||
# Flatten iodata to binary for pattern matching
|
||||
packet_data = IO.iodata_to_binary(packet_data)
|
||||
|
||||
# Extract opcode from packet data (first 2 bytes)
|
||||
<<opcode::little-16, rest::binary>> = packet_data
|
||||
|
||||
# Log the packet
|
||||
context = %{
|
||||
ip: state.ip,
|
||||
server_type: :login
|
||||
}
|
||||
PacketLogger.log_server_packet(opcode, rest, context)
|
||||
|
||||
# Encrypt the data (Shanda then AES) and morph send IV
|
||||
{updated_crypto, encrypted, header} = ClientCrypto.encrypt(crypto, packet_data)
|
||||
|
||||
# Send encrypted packet with 4-byte crypto header
|
||||
full_packet = header <> encrypted
|
||||
|
||||
case :gen_tcp.send(socket, full_packet) do
|
||||
:ok ->
|
||||
Logger.debug("Sent packet: #{packet_length} bytes")
|
||||
:ok
|
||||
%{state | crypto: updated_crypto}
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.error("Failed to send packet: #{inspect(reason)}")
|
||||
{:error, reason}
|
||||
state
|
||||
end
|
||||
end
|
||||
|
||||
defp send_packet(_state, _packet_data) do
|
||||
defp send_packet(state, _packet_data) do
|
||||
# Socket not available in state
|
||||
Logger.error("Cannot send packet: socket not in state")
|
||||
:error
|
||||
state
|
||||
end
|
||||
|
||||
defp authenticate_user(username, password, _state) do
|
||||
|
||||
Reference in New Issue
Block a user