update XMPP

This commit is contained in:
AleaJactaEst 2023-12-18 21:25:25 +01:00
parent 0ab321c3e1
commit 981a05cb14
2 changed files with 362 additions and 37 deletions

View file

@ -28,14 +28,31 @@ enum StreamState {
} }
var stream_status = StreamState.END var stream_status = StreamState.END
@export var server_ip:String = "localhost": enum XMPPState {
set = set_server_ip, get = get_server_ip NONE, # Nothing is configured
TRY_CONNECT_SERVER, # Try to connect to server
CONNECTED_SERVER, # Confirmed we are connected to server
INTIALIZE, # Initialize first connection with server
INTIALIZED, # when initialize is finished
FEATURE_SERVER, # We received Feature (TLS ?)
START_TLS, # We start TLS
WAIT_TLS, # Wait TLS Done
MISSING_TLS, # Missing feature TLS -> wait 30min and retry
STARTED_TLS, # TLS connection is done
AUTHENTICATE_STEP_1, # Launch authenticate
AUTHENTICATE_STEP_2, # Launch authenticate
AUTHENTICATE_STEP_3, # Launch authenticate
AUTHENTICATED, # We finished authenticate
}
func set_server_ip(value:String): @export var server_xmpp_name:String = "localhost":
server_ip = value set = set_server_xmpp_name, get = get_server_xmpp_name
func set_server_xmpp_name(value:String):
server_xmpp_name = value
func get_server_ip() -> String: func get_server_xmpp_name() -> String:
return server_ip return server_xmpp_name
@export var port_number:int = 5222: @export var port_number:int = 5222:
set = set_port_number, get = get_port_number set = set_port_number, get = get_port_number
@ -79,28 +96,321 @@ func get_locale() -> String:
set(value): set(value):
xmppclient = value xmppclient = value
const MAX_WAIT_CONNECTING:float = 60.0
const MAX_WAIT_MISSING_TLS:float = 1800.0
var try_connect:int = 0 var try_connect:int = 0
var count_connecting_time:float = 0 var count_connecting_time:float = MAX_WAIT_CONNECTING
var partial_stanza:String = "" var partial_stanza:String = ""
var tcp_peer:StreamPeerTCP = StreamPeerTCP.new() var tcp_peer:StreamPeerTCP = StreamPeerTCP.new()
var ssl_peer:StreamPeerTLS = StreamPeerTLS.new() var ssl_peer:StreamPeerTLS = StreamPeerTLS.new()
var tgt_peer = null var tgt_peer = null
var status:StreamState = StreamState.END var status:StreamState = StreamState.END
var xmpp_state = XMPPState.NONE
var authentication_methods = []
func reinit_stream():
if ssl_peer != null:
ssl_peer.disconnect_from_stream()
else:
ssl_peer = StreamPeerTLS.new()
if tcp_peer != null:
tcp_peer.disconnect_from_host()
else:
tcp_peer = StreamPeerTCP.new()
tgt_peer = tcp_peer
start_stream()
stream_status = self.StreamState.START
func _init(): func _init() -> void:
var language = OS.get_locale() var language = OS.get_locale()
set_locale(language) set_locale(language)
tgt_peer = tcp_peer tgt_peer = tcp_peer
# reinit_stream()
func _process(delta):
#Global.msg_info("TCP:%d", [tcp_peer.get_status()]) func _process(delta) -> void:
print(server_ip, ":", port_number, " / get_status:" , tcp_peer.get_status(), " / count_connecting_time:", count_connecting_time, " / stream_status:", stream_status) print(server_xmpp_name, ":", port_number, " / get_status:" , tcp_peer.get_status(), " / count_connecting_time:", count_connecting_time, " / stream_status:", stream_status, " / xmpp_state:", XMPPState.keys()[xmpp_state])
# if tcp_peer.get_status() == StreamPeerTCP.STATUS_NONE and stream_status == self.StreamState.TLS: if xmppclient == false:
# #negotiate_tls() return
# print("negotiate_tls") # Wait MAX_WAIT_MISSING_TLS (s) if server can't negociate with TLS
# pass if xmpp_state == XMPPState.MISSING_TLS:
if (tgt_peer.get_status() == StreamPeerTCP.STATUS_CONNECTED) : if count_connecting_time >= MAX_WAIT_MISSING_TLS:
xmpp_state = XMPPState.NONE
else:
count_connecting_time += delta
elif xmpp_state == XMPPState.NONE:
if count_connecting_time >= MAX_WAIT_CONNECTING:
xmpp_state = XMPPState.TRY_CONNECT_SERVER
if ssl_peer.get_status() != StreamPeerTLS.STATUS_DISCONNECTED:
ssl_peer.disconnect_from_stream()
if tcp_peer.get_status() != StreamPeerTCP.STATUS_NONE:
tcp_peer.disconnect_from_host()
else:
count_connecting_time += delta
elif xmpp_state == XMPPState.TRY_CONNECT_SERVER:
if tcp_peer.get_status() == StreamPeerTCP.STATUS_CONNECTED:
xmpp_state = XMPPState.CONNECTED_SERVER
count_connecting_time = MAX_WAIT_CONNECTING
elif count_connecting_time >= MAX_WAIT_CONNECTING:
var res = tcp_peer.connect_to_host(server_xmpp_name, port_number)
if res == OK:
send_msg_debug.emit("Send connect_to_host : ok")
#stream_status = self.StreamState.END
#xmpp_state = XMPPState.CONNECTED_SERVER
count_connecting_time = 0
else:
send_msg_error.emit("Error to connect to XMPP server (return:%d)" % res)
xmpp_state = XMPPState.NONE
count_connecting_time = 0
else:
if (tcp_peer.has_method("poll")):
tcp_peer.poll()
count_connecting_time += delta
elif xmpp_state == XMPPState.CONNECTED_SERVER:
if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
xmpp_state = XMPPState.NONE
count_connecting_time = 0
return
if count_connecting_time >= MAX_WAIT_CONNECTING:
count_connecting_time = 0
send_msg_debug.emit("send_tcp_initialize_xmpp")
send_tcp_initialize_xmpp()
else:
count_connecting_time += delta
if (tcp_peer.has_method("poll")):
tcp_peer.poll()
if tcp_peer.get_available_bytes()>0:
var response = tcp_peer.get_string(tcp_peer.get_available_bytes())
send_msg_debug.emit("Stream: response: " + response)
response = remove_stream_header(response)
if not analyze_error(response):
if analyze_feature_starttls(response):
xmpp_state = XMPPState.START_TLS
else:
count_connecting_time = 0
xmpp_state = XMPPState.MISSING_TLS
elif xmpp_state == XMPPState.START_TLS:
if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
xmpp_state = XMPPState.NONE
count_connecting_time = 0
return
if count_connecting_time >= MAX_WAIT_CONNECTING:
count_connecting_time = 0
xmpp_state = XMPPState.NONE
else:
count_connecting_time += delta
if (tcp_peer.has_method("poll")):
tcp_peer.poll()
if tcp_peer.get_available_bytes()>0:
var response = tcp_peer.get_string(tcp_peer.get_available_bytes())
send_msg_debug.emit("Stream: response: " + response)
response = remove_stream_header(response)
if response.begins_with("<proceed"):
send_msg_debug.emit("Stream: gotten go ahead for ssl")
var options:TLSOptions = TLSOptions.client_unsafe()
var ssl_succes = ssl_peer.connect_to_stream(tcp_peer, "localhost", options)
if ssl_succes == OK:
send_msg_debug.emit("Stream: switched to SSL!")
xmpp_state = XMPPState.WAIT_TLS
else:
count_connecting_time = 0
xmpp_state = XMPPState.MISSING_TLS
else:
send_msg_error.emit(tr("TlS negotiation failed."))
count_connecting_time = 0
xmpp_state = XMPPState.MISSING_TLS
elif xmpp_state == XMPPState.WAIT_TLS:
if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
xmpp_state = XMPPState.NONE
count_connecting_time = 0
return
if ssl_peer.get_status() == StreamPeerTLS.STATUS_CONNECTED:
xmpp_state = XMPPState.STARTED_TLS
count_connecting_time = MAX_WAIT_CONNECTING
elif ssl_peer.get_status() == StreamPeerTLS.STATUS_HANDSHAKING:
ssl_peer.poll()
if count_connecting_time >= MAX_WAIT_CONNECTING:
count_connecting_time = 0
xmpp_state = XMPPState.MISSING_TLS
else:
count_connecting_time += delta
else:
count_connecting_time = 0
xmpp_state = XMPPState.MISSING_TLS
elif xmpp_state == XMPPState.STARTED_TLS:
if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
xmpp_state = XMPPState.NONE
count_connecting_time = 0
return
if ssl_peer.get_status() != StreamPeerTLS.STATUS_CONNECTED:
xmpp_state = XMPPState.NONE
count_connecting_time = 0
return
if count_connecting_time >= MAX_WAIT_CONNECTING:
count_connecting_time = 0
send_msg_debug.emit("send_ssl_initialize_xmpp")
send_ssl_initialize_xmpp()
else:
count_connecting_time += delta
if (ssl_peer.has_method("poll")):
ssl_peer.poll()
if ssl_peer.get_available_bytes()>0:
var response = ssl_peer.get_string(ssl_peer.get_available_bytes())
send_msg_debug.emit("Stream: response: " + response)
response = remove_stream_header(response)
if not analyze_error(response):
if analyze_feature_mechanisms(response):
if authentication_methods.size() > 0:
count_connecting_time = MAX_WAIT_CONNECTING
xmpp_state = XMPPState.AUTHENTICATE_STEP_1
#send_msg_debug.emit("AUTHENTICATE_STEP_1")
# elif response.begins_with("<success"):
# stream_status = self.StreamState.STANZA
if xmpp_state == XMPPState.AUTHENTICATE_STEP_1:
if tcp_peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
xmpp_state = XMPPState.NONE
count_connecting_time = 0
return
if count_connecting_time >= MAX_WAIT_CONNECTING:
count_connecting_time = 0
negotiate_ssl_sasl(authentication_methods)
else:
count_connecting_time += delta
if (ssl_peer.has_method("poll")):
ssl_peer.poll()
if ssl_peer.get_available_bytes()>0:
var response = ssl_peer.get_string(ssl_peer.get_available_bytes())
send_msg_debug.emit("Stream: response: " + response)
response = remove_stream_header(response)
if response.begins_with("<success"):
count_connecting_time = 0
xmpp_state = XMPPState.AUTHENTICATED
func analyze_error(response:String) -> bool:
if response.begins_with("<stream:error"):
var stream_error = XMPPStreamError.new()
stream_error.parse_from_xml(response)
var error_name = stream_error.error_name_for_enum(stream_error.error_type)
var error = tr('A stream error of type "%" occured, or in other words: % (Stream errors cannot be recovered from, the connection will be closed.'.format([error_name, stream_error.human_friendly_error_message()], "%)"))
error.emit(error)
return true
return false
func analyze_feature_starttls(response:String) -> bool:
if response.begins_with("<stream:features"):
var stream_features = XMPPStreamFeatures.new()
stream_features.parse_from_xml(response)
if stream_features.parsedDictionary.has("starttls"):
send_msg_debug.emit("Stream: sending request for ssl")
var request_tls = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
send_tcp_string(request_tls)
stream_status = self.StreamState.TLS
return true
return false
func analyze_feature_mechanisms(response:String) -> bool:
if response.begins_with("<stream:features"):
var stream_features = XMPPStreamFeatures.new()
stream_features.parse_from_xml(response)
if stream_features.parsedDictionary.has("mechanisms"):
print("response:", response)
authentication_methods = stream_features.parsedDictionary["mechanisms"]
if (authentication_methods.size() > 0):
var tmp:String = ""
var sep:String = ""
for item in authentication_methods:
tmp += sep + item
sep = ", "
send_msg_debug.emit("Stream: authentication methods: " + tmp)
#negotiate_ssl_sasl(authentication_methods)
#stream_status = self.StreamState.AUTHENTICATE
return true
#negotiate_ssl_sasl(authentication_methods)
return false
func send_tcp_initialize_xmpp() -> void:
""" Send the stream header.
Needs to be done several times over stream negotiation.
"""
var message:String = "<?xml version='1.0'?>" + \
"<stream:stream" + \
" xmlns=\"jabber:client\"" + \
" version=\"1.0\"" + \
" xmlns:stream=\"http://etherx.jabber.org/streams\"" + \
" to=\"" + server_xmpp_name + "\" " + \
" xml:lang=\"en\"" + \
" > "
# " from=\"" + account_name + "\" " + \
# + locale + "'" +
print(message)
send_tcp_string(message)
func send_ssl_initialize_xmpp() -> void:
""" Send the stream header.
Needs to be done several times over stream negotiation.
"""
var server_name = account_name.split("@")[-1]
var message:String = "<?xml version='1.0'?>" + \
"<stream:stream" + \
" xmlns=\"jabber:client\"" + \
" version=\"1.0\"" + \
" xmlns:stream=\"http://etherx.jabber.org/streams\"" + \
" from=\"" + account_name + "\" " + \
" to=\"" + server_name + "\" " + \
" xml:lang=\"en\"" + \
" > "
# " from=\"" + account_name + "\" " + \
# + locale + "'" +
print(message)
send_ssl_string(message)
func negotiate_ssl_sasl(authentication_methods : Array) -> bool:
if ( authentication_methods.has("PLAIN")):
send_msg_debug.emit("Stream: sending request for plain")
var msg:PackedByteArray = PackedByteArray()
msg.push_back(0)
var t = account_name.split("@")[0]
msg += t.to_ascii_buffer()
#msg += conv_string_to_PackedByteArray(account_name.split("@")[0])
msg.push_back(0)
msg += password.to_ascii_buffer()
var auth_account:String = Marshalls.raw_to_base64(msg)
var request_sasl:String = "<auth" + \
" xmlns='urn:ietf:params:xml:ns:xmpp-sasl'" + \
" mechanism='PLAIN'" + \
" >" + auth_account + "</auth>"
send_ssl_string(request_sasl)
return true
else:
send_msg_error.emit("Impossible to authenticate (unknown protocol)")
return false
#
#
#
func connect_to_server_xmpp() -> void:
var res = tcp_peer.connect_to_host(server_xmpp_name, port_number)
if res == OK:
count_connecting_time = 0
stream_status = self.StreamState.END
set_process(true)
tgt_peer = tcp_peer
func _process_old(delta) -> void:
print(server_xmpp_name, ":", port_number, " / get_status:" , tcp_peer.get_status(), " / count_connecting_time:", count_connecting_time, " / stream_status:", stream_status)
if tgt_peer.get_status() == StreamPeerTCP.STATUS_CONNECTED:
print("STATUS_CONNECTED:", delta) print("STATUS_CONNECTED:", delta)
if (tgt_peer.has_method("poll")): if (tgt_peer.has_method("poll")):
tgt_peer.poll() tgt_peer.poll()
@ -109,15 +419,15 @@ func _process(delta):
send_msg_debug.emit("Stream: response: " + response) send_msg_debug.emit("Stream: response: " + response)
if stream_status == self.StreamState.STANZA: if stream_status == self.StreamState.STANZA:
#collect_stanza(remove_stream_header(response)) #collect_stanza(remove_stream_header(response))
Global.msg_info("", [])
print("collect_stanza") print("collect_stanza")
else: else:
print("stream_process") print("stream_process")
stream_process(remove_stream_header(response)) stream_process(remove_stream_header(response))
# print(remove_stream_header(response)) # print(remove_stream_header(response))
elif stream_status == self.StreamState.END: elif stream_status == self.StreamState.END:
start_stream() reinit_stream()
stream_status = self.StreamState.START #start_stream()
#stream_status = self.StreamState.START
count_connecting_time = 0 count_connecting_time = 0
if tgt_peer.get_status() == StreamPeerTCP.STATUS_CONNECTING: if tgt_peer.get_status() == StreamPeerTCP.STATUS_CONNECTING:
print("STATUS_CONNECTING:", delta) print("STATUS_CONNECTING:", delta)
@ -132,6 +442,7 @@ func _process(delta):
set_process(false) # stop listening for packets set_process(false) # stop listening for packets
try_connect = 20 try_connect = 20
stream_status = self.StreamState.END stream_status = self.StreamState.END
reinit_stream()
if tgt_peer.get_status() == StreamPeerTCP.STATUS_NONE and xmppclient: if tgt_peer.get_status() == StreamPeerTCP.STATUS_NONE and xmppclient:
print("connect_to_server:", xmppclient) print("connect_to_server:", xmppclient)
connect_to_server() connect_to_server()
@ -144,7 +455,7 @@ func connect_to_server():
pass pass
if try_connect == 0 and tcp_peer.get_status() == StreamPeerTCP.STATUS_NONE: if try_connect == 0 and tcp_peer.get_status() == StreamPeerTCP.STATUS_NONE:
#print("-> ", server_ip, ":", port_number, "/" , tcp_peer.get_status()) #print("-> ", server_ip, ":", port_number, "/" , tcp_peer.get_status())
var res = tcp_peer.connect_to_host(server_ip, port_number) var res = tcp_peer.connect_to_host(server_xmpp_name, port_number)
if res == OK: if res == OK:
count_connecting_time = 0 count_connecting_time = 0
stream_status = self.StreamState.END stream_status = self.StreamState.END
@ -162,6 +473,18 @@ func send_string(stanza:String) ->void:
tgt_peer.put_data(stanza.to_utf8_buffer()) tgt_peer.put_data(stanza.to_utf8_buffer())
func send_tcp_string(stanza:String) ->void:
""" Send a string in the appropriate encoding. """
send_msg_debug.emit("Sending data: '%'".format([stanza], "%"))
tcp_peer.put_data(stanza.to_utf8_buffer())
func send_ssl_string(stanza:String) ->void:
""" Send a string in the appropriate encoding. """
send_msg_debug.emit("Sending data: '%'".format([stanza], "%"))
ssl_peer.put_data(stanza.to_utf8_buffer())
func conv_string_to_PackedByteArray(message:String) -> PackedByteArray: func conv_string_to_PackedByteArray(message:String) -> PackedByteArray:
return message.to_ascii_buffer() return message.to_ascii_buffer()
@ -277,7 +600,7 @@ func stream_process(response :String = "") -> void:
print("Mystery stream status: "+str(stream_status)) print("Mystery stream status: "+str(stream_status))
func negotiate_tls() -> void: func negotiate_tls() -> bool:
#var ssl_peer = StreamPeerTLS.new() #var ssl_peer = StreamPeerTLS.new()
# I am unsure how to validate the ssl certificate for an unknown host? # I am unsure how to validate the ssl certificate for an unknown host?
#var ssl_succes = FAILED #var ssl_succes = FAILED
@ -298,29 +621,31 @@ func negotiate_tls() -> void:
print("get_status:", ssl_peer.get_status()) print("get_status:", ssl_peer.get_status())
print("-----") print("-----")
#stream_status == StreamState.START #stream_status == StreamState.START
return true
else: else:
send_msg_error.emit("SSL failed, error %".format([ssl_succes], "%")) send_msg_error.emit("SSL failed, error %".format([ssl_succes], "%"))
return false
func negotiate_sasl(authentication_methods : Array) -> void: func negotiate_sasl(authentication_methods : Array) -> void:
if (!authentication_methods.has("PLAIN")): if ( authentication_methods.has("PLAIN")):
end_stream() send_msg_debug.emit("Stream: sending request for plain")
send_msg_debug.emit("Stream: sending request for plain") var msg:PackedByteArray = PackedByteArray()
var msg:PackedByteArray = PackedByteArray() msg.push_back(0)
msg.push_back(0) var t = account_name.split("@")[0]
var t = account_name.split("@")[0] msg += t.to_ascii_buffer()
msg += t.to_ascii_buffer() #msg += conv_string_to_PackedByteArray(account_name.split("@")[0])
#msg += conv_string_to_PackedByteArray(account_name.split("@")[0]) msg.push_back(0)
msg.push_back(0) msg += password.to_ascii_buffer()
msg += password.to_ascii_buffer() var auth_account:String = Marshalls.raw_to_base64(msg)
print("7:", msg) var request_sasl:String = "<auth" + \
var auth_account:String = Marshalls.raw_to_base64(msg)
var request_sasl:String = "<auth" + \
" xmlns='urn:ietf:params:xml:ns:xmpp-sasl'" + \ " xmlns='urn:ietf:params:xml:ns:xmpp-sasl'" + \
" mechanism='PLAIN'" + \ " mechanism='PLAIN'" + \
" >" + auth_account + "</auth>" " >" + auth_account + "</auth>"
send_string(request_sasl) send_string(request_sasl)
else:
send_msg_error.emit("Impossible to authenticate (unknown protocol)")
end_stream()
func end_stream() -> void: func end_stream() -> void:

View file

@ -35,7 +35,7 @@ func _ready():
Multi.update_my_position.connect(_on_update_me) Multi.update_my_position.connect(_on_update_me)
Multi.update_player_position.connect(_on_update_player) Multi.update_player_position.connect(_on_update_player)
Multi.remove_player.connect(_on_remove_player) Multi.remove_player.connect(_on_remove_player)
Stream.set_server_ip("localhost") Stream.set_server_xmpp_name("localhost")
Stream.set_port_number(5222) Stream.set_port_number(5222)
Stream.send_msg_debug.connect(show_stream_debug) Stream.send_msg_debug.connect(show_stream_debug)
Stream.send_msg_error.connect(show_stream_error) Stream.send_msg_error.connect(show_stream_error)