99 lines
2.3 KiB
Python
99 lines
2.3 KiB
Python
import time
|
|
import socket
|
|
|
|
from .app import app
|
|
from .util import get_addr_info
|
|
|
|
|
|
# Initial packet of type ORIGINAL, with no data.
|
|
# This should prompt the server to assign us a peer id.
|
|
# [0] u32 protocol_id (PROTOCOL_ID)
|
|
# [4] session_t sender_peer_id (PEER_ID_INEXISTENT)
|
|
# [6] u8 channel
|
|
# [7] u8 type (PACKET_TYPE_ORIGINAL)
|
|
PING_PACKET = b"\x4f\x45\x74\x03\x00\x00\x00\x01"
|
|
|
|
|
|
def get_ping_reply(data):
|
|
# [0] u32 protocol_id (PROTOCOL_ID)
|
|
# [4] session_t sender_peer_id
|
|
# [6] u8 channel
|
|
# [7] u8 type (PACKET_TYPE_RELIABLE)
|
|
# [8] u16 sequence number
|
|
# [10] u8 type (PACKET_TYPE_CONTROL)
|
|
# [11] u8 controltype (CONTROLTYPE_SET_PEER_ID)
|
|
# [12] session_t peer_id_new
|
|
peer_id = data[12:14]
|
|
|
|
# Send packet of type CONTROL, subtype DISCO,
|
|
# to cleanly close our server connection.
|
|
# [0] u32 protocol_id (PROTOCOL_ID)
|
|
# [4] session_t sender_peer_id
|
|
# [6] u8 channel
|
|
# [7] u8 type (PACKET_TYPE_CONTROL)
|
|
# [8] u8 controltype (CONTROLTYPE_DISCO)
|
|
return b"\x4f\x45\x74\x03" + peer_id + b"\x00\x00\x03"
|
|
|
|
|
|
def ping_server_addresses(address, port):
|
|
pings = []
|
|
addr_info = get_addr_info(address, port)
|
|
for record in addr_info:
|
|
ping = server_up(record)
|
|
if not ping:
|
|
app.logger.warning("Could not connect to %s:%d using resolved info %r.",
|
|
address, port, record)
|
|
return None
|
|
pings.append(ping)
|
|
return pings
|
|
|
|
|
|
def ping_server(sock):
|
|
sock.send(PING_PACKET)
|
|
|
|
# Receive reliable packet of type CONTROL, subtype SET_PEER_ID,
|
|
# with our assigned peer id as data.
|
|
start = time.time()
|
|
data = sock.recv(1024)
|
|
end = time.time()
|
|
|
|
if not data:
|
|
return None
|
|
|
|
sock.send(get_ping_reply(data))
|
|
return end - start
|
|
|
|
|
|
# Returns ping time in seconds (up) or None (down).
|
|
def server_up(info):
|
|
"""Pings a Minetest server to check if it is online.
|
|
"""
|
|
try:
|
|
sock = socket.socket(info[0], info[1], info[2])
|
|
sock.settimeout(2)
|
|
sock.connect(info[4])
|
|
except OSError:
|
|
return None
|
|
|
|
attempts = 0
|
|
pings = []
|
|
while len(pings) < 3 and attempts - len(pings) < 3:
|
|
attempts += 1
|
|
try:
|
|
ping = ping_server(sock)
|
|
if ping is not None:
|
|
pings.append(ping)
|
|
except socket.timeout:
|
|
pass
|
|
except ConnectionRefusedError:
|
|
return None
|
|
except OSError:
|
|
return None
|
|
|
|
sock.close()
|
|
|
|
if len(pings) != 0:
|
|
return min(pings)
|
|
|
|
return None
|