Handle hash collision in handshake; properly prune old servers from browser.

This commit is contained in:
wan-may 2023-10-01 20:16:10 -03:00
parent 788b7a11f5
commit 2ca4a77b33
7 changed files with 44 additions and 24 deletions

View File

@ -58,8 +58,8 @@ function game.update( dt )
if t > 0.1 then if t > 0.1 then
t = 0 t = 0
tick = tick + 1 tick = tick + 1
server.newPacket() server.newPacket( tick )
assert( server.send( packet.get() ) ) assert( server.send() )
end end
end end
@ -69,10 +69,7 @@ function game.newGame( )
end end
function game.disconnect( ) function game.disconnect( )
server.newPacket() return scene.mainmenu( server.disconnect( tick ) )
server.disconnect()
server.send( packet.get() )
return scene.mainmenu( server.disconnect() )
end end
function game.onLoad( params ) function game.onLoad( params )

View File

@ -6,7 +6,6 @@ local udp = {}
local packet = assert( require 'shared.packet' ) local packet = assert( require 'shared.packet' )
local hash = assert( require 'shared.hash' ) local hash = assert( require 'shared.hash' )
local token
local cxn = assert( socket.udp() ) local cxn = assert( socket.udp() )
local mscxn = assert( socket.udp() ) local mscxn = assert( socket.udp() )
cxn:settimeout( 0 ) cxn:settimeout( 0 )
@ -32,19 +31,20 @@ function udp.requestServerList()
end end
function udp.newPacket( tick ) function udp.newPacket( tick )
packet.get() if udp.token then packet.connected{ token = udp.token, tick = tick or 0 } end
if token then packet.connected{ token = token, tick = tick or 0 } end
end end
function udp.setToken( token ) function udp.setToken( token )
token = token print( "Setting server token:", token )
udp.token = token
end end
function udp.answerChallenge( svNonce ) function udp.answerChallenge( svNonce )
local clNonce = hash.rand() local clNonce = hash.rand()
packet.get() packet.get()
packet.clChimo{ nonce = clNonce, hash = hash.hash( clNonce, svNonce ) } packet.clChimo{ nonce = clNonce, hash = hash.hash( clNonce, svNonce ) }
return cxn:send( packet.get() ) print( "Received authentication nonce. Reply:", clNonce, svNonce )
return udp.send()
end end
function udp.isValid( ip, port ) function udp.isValid( ip, port )
@ -57,17 +57,22 @@ end
function udp.connect( ip, port ) function udp.connect( ip, port )
assert( cxn:setpeername( ip, port ) ) assert( cxn:setpeername( ip, port ) )
print( "Connection request to:", ip, port ) print( "Connection request to:", ip, port )
return udp.send( packet.get( packet.clientInfo{ username = config.plName } ) ) return udp.send( packet.clientInfo{ username = config.plName })
end end
function udp.disconnect( ) function udp.disconnect( tick )
udp.send( packet.get( packet.disconnect{ reason = 0 })) for i = 1, 10 do
udp.newPacket( tick )
packet.disconnect()
udp.send()
end
udp.setToken()
cxn = assert( socket.udp() ) cxn = assert( socket.udp() )
cxn:settimeout( 0 ) cxn:settimeout( 0 )
end end
function udp.send( s ) function udp.send()
return cxn:send( s ) return cxn:send( packet.get() )
end end
return udp return udp

View File

@ -144,7 +144,7 @@ end
local metaServerHandlers = setmetatable( local metaServerHandlers = setmetatable(
{ {
default = function() end, default = function() end,
heartbeat = serverList.clear, connected = serverList.clear,
serverInfo = serverList.add, serverInfo = serverList.add,
}, },
{__index = function( t ) return t.default end }) {__index = function( t ) return t.default end })

View File

@ -56,6 +56,8 @@ local handlers = setmetatable({
local t = socket.gettime() local t = socket.gettime()
clients[ip].time = t clients[ip].time = t
packet.connected{ token = 5, tick = tick }
for svIP, server in pairs( servers ) do for svIP, server in pairs( servers ) do
print( "", svIP, packet.getString( server.info.svname )) print( "", svIP, packet.getString( server.info.svname ))

View File

@ -9,6 +9,8 @@ assert( mscxn:setpeername( shared.metaserver.ip, shared.metaserver.port ), "Coul
local udp local udp
local io = assert( io ) local io = assert( io )
local CLIENTTIMEOUT = 10
local svInfo = packet.serverInfo{ version = 13, local svInfo = packet.serverInfo{ version = 13,
players = 0, players = 0,
capacity = 255, capacity = 255,
@ -41,6 +43,7 @@ local handlers = setmetatable({
end end
client.tick = msg.tick client.tick = msg.tick
client.time = socket.gettime()
server.currentClient = client server.currentClient = client
end, end,
@ -58,11 +61,17 @@ local handlers = setmetatable({
--Client responds to handshake challenge. --Client responds to handshake challenge.
clChimo = function( clChimo, ip, port ) clChimo = function( clChimo, ip, port )
print( "Received handshake response." )
--No active challenge, don't send anything.
local key = ip..":"..port local key = ip..":"..port
if not connecting[ key ] then if not connecting[ key ] then
print( "Old connection attempt from:", key ) print( "Old connection attempt from:", key )
return true return true
end end
--Compute session token.
local remoteHash = clChimo.hash local remoteHash = clChimo.hash
local clNonce = clChimo.nonce local clNonce = clChimo.nonce
local svNonce = connecting[key].nonce local svNonce = connecting[key].nonce
@ -72,10 +81,14 @@ local handlers = setmetatable({
return true return true
end end
--Hash collision.
while clients[token] do token = token + 1 end
--Successful handshake. --Successful handshake.
print( "Client connected:", ip, port, token ) print( "Client connected:", token, port, ip )
clients[ token ] = connecting[ key ] clients[ token ] = connecting[ key ]
clients[ token ].token = token clients[ token ].id = token
clients[ token ].time = socket.gettime()
packet.connected{ token = token, tick = server.tick } packet.connected{ token = token, tick = server.tick }
return udp:sendto( packet.get(), ip, port ) return udp:sendto( packet.get(), ip, port )
end, end,
@ -126,11 +139,9 @@ end
--Incoming packet. --Incoming packet.
function server.Parse( msg, ip, port ) function server.Parse( msg, ip, port )
if (not msg) or (#msg < 1) then return end if (not msg) or (#msg < 1) then return end
print( "Parsing:", ip, port, #msg, msg )
local msgs, types = packet.deserialise( msg ) local msgs, types = packet.deserialise( msg )
if msgs then if msgs then
for i = 1, #msgs do for i = 1, #msgs do
print( "Received: ", types[i], ip, port )
if handlers[ types[i] ]( msgs[i], ip, port ) then break end if handlers[ types[i] ]( msgs[i], ip, port ) then break end
end end
server.currentClient = false server.currentClient = false
@ -164,14 +175,14 @@ end
function server.Advance() function server.Advance()
server.tick = server.tick + 1 server.tick = server.tick + 1
server.time = socket.gettime()
if server.tick % 100 == 0 then if server.tick % 100 == 0 then
for id, client in pairs( clients ) do for id, client in pairs( clients ) do
packet.get() packet.get()
print( "updating client:", id )
packet.connected{ token = id, tick = server.tick } packet.connected{ token = id, tick = server.tick }
udp:sendto( packet.get(), client.ip, client.port ) udp:sendto( packet.get(), client.ip, client.port )
if server.tick - client.tick > 1000 then if server.time - client.time > CLIENTTIMEOUT then
print( "dropping client:", id ) print( "dropping client:", id )
clients[id] = nil clients[id] = nil
end end

View File

@ -1,6 +1,6 @@
local math = math local math = math
local bit = assert( require 'bit' ) local bit = assert( require 'bit' )
local max = math.huge local max = 65536
math.randomseed( 4 ) math.randomseed( 4 )
--hash of a pair of 32-bit numbers --hash of a pair of 32-bit numbers
return { return {

View File

@ -109,6 +109,11 @@ newStruct{
"char reason", "char reason",
} }
newStruct{
name = "debugLatency",
"uint32_t lastTick",
}
local readBuffer = buffer.new( 1024 ) local readBuffer = buffer.new( 1024 )
function packet.deserialise( str ) function packet.deserialise( str )
readBuffer:set( str ) readBuffer:set( str )