prepare xmpp & dtls

This commit is contained in:
AleaJactaEst 2023-11-05 15:54:21 +01:00
parent d0508567f4
commit 51d7d0c252
14 changed files with 817 additions and 56 deletions

1
.gitignore vendored
View file

@ -2,3 +2,4 @@
server/target/ server/target/
Godot_* Godot_*
.current_version .current_version
.cert/

View file

@ -52,7 +52,8 @@ When you're ready to make this README your own, just edit this file and use the
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
## Name ## Name
Choose a self-explaining name for your project. Project just to test network functionnality (validated communication between client & server, validated xmpp)
And test client 3d
## Description ## Description
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
@ -64,10 +65,20 @@ On some READMEs, you may see small images that convey metadata, such as whether
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
## Installation ## Installation
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
### Create certicifate
./create-certificate.sh
## Usage ## Usage
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
### Launch server
./start-bazar-server.sh
### Launch XMPP server
./start-bazar-fake-xmpp-server.sh
### Launch client
./start-bazar-client.sh
## Support ## Support
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
@ -83,10 +94,10 @@ For people who want to make changes to your project, it's helpful to have some d
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
## Authors and acknowledgment ## Authors and acknowledgment
Show your appreciation to those who have contributed to the project. AleaJactaEst
## License ## License
For open source projects, say how it is licensed. GNU GPL v3
## Project status ## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,56 @@
extends Window
signal send_server_config(alea_ip, alea_port, xmpp_ip, xmpp_port)
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass
func show_window():
self._on_text_changed("")
self.show()
func _on_button_pressed():
send_server_config.emit(
$VBoxContainer/TabContainer/Alea/Address.get_text(),
int($VBoxContainer/TabContainer/Alea/Port.get_text()),
$VBoxContainer/TabContainer/XMPP/Address.get_text(),
int($VBoxContainer/TabContainer/XMPP/Port.get_text())
)
self.hide()
func _on_text_changed(_text):
var listen_port:int
if len($VBoxContainer/TabContainer/Alea/Address.get_text()) == 0:
$VBoxContainer/Button.set_disabled(true)
return
if len($VBoxContainer/TabContainer/Alea/Port.get_text()) == 0:
$VBoxContainer/Button.set_disabled(true)
return
if len($VBoxContainer/TabContainer/XMPP/Address.get_text()) == 0:
$VBoxContainer/Button.set_disabled(true)
return
if len($VBoxContainer/TabContainer/XMPP/Port.get_text()) == 0:
$VBoxContainer/Button.set_disabled(true)
return
listen_port = int($VBoxContainer/TabContainer/Alea/Port.get_text())
if listen_port <= 0 || listen_port >= 65536 :
$VBoxContainer/Button.set_disabled(true)
return
listen_port = int($VBoxContainer/TabContainer/XMPP/Port.get_text())
if listen_port <= 0 || listen_port >= 65536 :
$VBoxContainer/Button.set_disabled(true)
return
$VBoxContainer/Button.set_disabled(false)
func _on_close_requested():
self.hide()

View file

@ -3,8 +3,10 @@ extends Window
var focus_ok:bool = false var focus_ok:bool = false
var last_event = null var last_event = null
@export var listen_ip:String = "" signal show_preference_server()
@export var listen_port:int = 33333
#@export var listen_ip:String = ""
#@export var listen_port:int = 33333
# Called when the node enters the scene tree for the first time. # Called when the node enters the scene tree for the first time.
func _ready(): func _ready():
@ -17,33 +19,28 @@ func _process(_delta):
func _on_button_pressed(): func _on_button_pressed():
if $VBoxContainer/TextEdit.get_text() != "": if $VBoxContainer/Account.get_text() != "":
self.hide() self.hide()
#get_parent().get_node("CharacterBody3D").set_enable_event(true) #get_parent().get_node("CharacterBody3D").set_enable_event(true)
#get_parent().connect_enet($VBoxContainer/TextEdit.get_text(), listen_ip, listen_port) #get_parent().connect_enet($VBoxContainer/TextEdit.get_text(), listen_ip, listen_port)
Multi.set_username($VBoxContainer/TextEdit.get_text()) Multi.set_username($VBoxContainer/Account.get_text())
#Multi.connect_server() #Multi.connect_server()
func check_testedit(): func check_testedit():
if len($VBoxContainer/TextEdit.get_text()) == 0: if len($VBoxContainer/Account.get_text()) == 0:
$VBoxContainer/Button.set_disabled(true) $VBoxContainer/Button.set_disabled(true)
return return
if len($VBoxContainer/Address.get_text()) == 0: if len($VBoxContainer/Password.get_text()) == 0:
$VBoxContainer/Button.set_disabled(true)
return
listen_ip = $VBoxContainer/Address.get_text()
listen_port = int($VBoxContainer/Port.get_text())
if listen_port <= 0 || listen_port >= 65536 :
$VBoxContainer/Button.set_disabled(true) $VBoxContainer/Button.set_disabled(true)
return return
$VBoxContainer/Button.set_disabled(false) $VBoxContainer/Button.set_disabled(false)
Multi.set_ip(listen_ip)
Multi.set_port(listen_port)
func _on_text_edit_text_changed(_value:String): func _on_text_edit_text_changed(_value:String):
check_testedit() check_testedit()
func _on_password_text_changed(_value:String):
check_testedit()
func _on_focus_entered(): func _on_focus_entered():
focus_ok = true focus_ok = true
@ -74,3 +71,8 @@ func open_windows():
# return # return
# if (event is InputEventKey) && (self.type_event == 1): # if (event is InputEventKey) && (self.type_event == 1):
# last_event = event # last_event = event
func _on_preference_pressed():
show_preference_server.emit()

View file

@ -22,27 +22,27 @@ 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("127.0.0.1") $Window.show_preference_server.connect(_on_preference_server)
Stream.server_ip = "127.0.0.1" $PreferenceServer.send_server_config.connect(_on_edit_preference_server)
print(Stream.get_server_ip()) $PreferenceServer._on_button_pressed()
func _on_connexion_updated(new_state): func _on_connexion_updated(new_state):
if new_state == Multi.Connexion.NONE: if new_state == Multi.Connexion.NONE:
self.get_node("CharacterBody3D").set_enable_event(false) self.get_node("CharacterBody3D").set_enable_event(false)
$Panel/State.set_text("Not Connected") $StateAlea/VBoxContainer/State.set_text("Alea: Not Connected")
$Panel.show() $StateAlea.show()
if new_state == Multi.Connexion.ACCOUNT_REFUSED: if new_state == Multi.Connexion.ACCOUNT_REFUSED:
self.get_node("CharacterBody3D").set_enable_event(false) self.get_node("CharacterBody3D").set_enable_event(false)
$Panel/State.set_text("Account Refused") $StateAlea/VBoxContainer/State.set_text("Alea: Account Refused")
$Panel.show() $StateAlea.show()
$Window.show() $Window.show()
elif new_state == Multi.Connexion.CONNECTING: elif new_state == Multi.Connexion.CONNECTING:
self.get_node("CharacterBody3D").set_enable_event(false) self.get_node("CharacterBody3D").set_enable_event(false)
$Panel/State.set_text("Connecting") $StateAlea/VBoxContainer/State.set_text("Alea: Connecting")
$Panel.show() $StateAlea.show()
else: else:
$Panel.hide() $StateAlea.hide()
func _on_update_me(pos:Vector3): func _on_update_me(pos:Vector3):
@ -93,3 +93,14 @@ func _on_remove_player(id:int):
func set_player_position(pos: Vector3): func set_player_position(pos: Vector3):
self.get_node("CharacterBody3D").set_global_position(pos) self.get_node("CharacterBody3D").set_global_position(pos)
func _on_preference_server():
$PreferenceServer.show_window()
func _on_edit_preference_server(alea_ip, alea_port, xmpp_ip, xmpp_port):
print(alea_ip, ', ', alea_port, ', ', xmpp_ip, ', ', xmpp_port)
Multi.set_ip(alea_ip)
Multi.set_port(alea_port)
Stream.set_server_ip(xmpp_ip)
Stream.set_port_number(xmpp_port)

View file

@ -1,9 +1,11 @@
[gd_scene load_steps=11 format=3 uid="uid://bemavktwweaog"] [gd_scene load_steps=13 format=3 uid="uid://bemavktwweaog"]
[ext_resource type="Shader" path="res://scenes/main.gdshader" id="1_caff6"] [ext_resource type="Shader" path="res://scenes/main.gdshader" id="1_caff6"]
[ext_resource type="PackedScene" uid="uid://cg5uqqd4ibdem" path="res://scenes/player.tscn" id="1_nc7b3"] [ext_resource type="PackedScene" uid="uid://cg5uqqd4ibdem" path="res://scenes/player.tscn" id="1_nc7b3"]
[ext_resource type="Script" path="res://scenes/main.gd" id="1_ts8of"] [ext_resource type="Script" path="res://scenes/main.gd" id="1_ts8of"]
[ext_resource type="Script" path="res://scenes/Window.gd" id="3_uwnj8"] [ext_resource type="Script" path="res://scenes/Window.gd" id="3_uwnj8"]
[ext_resource type="Texture2D" uid="uid://cc7b2q50tp3yn" path="res://assets/preference.png" id="5_c5esr"]
[ext_resource type="Script" path="res://scenes/PreferenceServer.gd" id="6_54nnr"]
[sub_resource type="BoxShape3D" id="BoxShape3D_5dcgs"] [sub_resource type="BoxShape3D" id="BoxShape3D_5dcgs"]
size = Vector3(1000, 1, 1000) size = Vector3(1000, 1, 1000)
@ -66,49 +68,136 @@ anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
[node name="Label" type="Label" parent="Window/VBoxContainer"] [node name="HBoxContainer" type="HBoxContainer" parent="Window/VBoxContainer"]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="Window/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 1
text = "Login" text = "Login"
horizontal_alignment = 1 horizontal_alignment = 1
vertical_alignment = 1
[node name="Address" type="LineEdit" parent="Window/VBoxContainer"] [node name="Preference" type="Button" parent="Window/VBoxContainer/HBoxContainer"]
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 size_flags_horizontal = 8
text = "127.0.0.1" icon = ExtResource("5_c5esr")
placeholder_text = "Address" flat = true
[node name="Port" type="LineEdit" parent="Window/VBoxContainer"] [node name="Account" type="LineEdit" parent="Window/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
text = "33333"
placeholder_text = "Address"
[node name="TextEdit" type="LineEdit" parent="Window/VBoxContainer"]
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
placeholder_text = "Account" placeholder_text = "Account"
[node name="Password" type="LineEdit" parent="Window/VBoxContainer"]
layout_mode = 2
placeholder_text = "Password"
[node name="Button" type="Button" parent="Window/VBoxContainer"] [node name="Button" type="Button" parent="Window/VBoxContainer"]
layout_mode = 2 layout_mode = 2
disabled = true disabled = true
text = "OK" text = "OK"
[node name="Panel" type="Panel" parent="."] [node name="StateAlea" type="Panel" parent="."]
offset_right = 199.0 offset_right = 199.0
offset_bottom = 40.0 offset_bottom = 36.0
size_flags_horizontal = 3 size_flags_horizontal = 3
size_flags_vertical = 3 size_flags_vertical = 3
[node name="State" type="Label" parent="Panel"] [node name="VBoxContainer" type="VBoxContainer" parent="StateAlea"]
layout_mode = 1 layout_mode = 1
anchors_preset = -1 anchors_preset = 15
anchor_right = 0.994 anchor_right = 1.0
anchor_bottom = 0.75 anchor_bottom = 1.0
offset_right = -0.0279999
offset_bottom = 10.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
text = "Not connected"
[node name="State" type="Label" parent="StateAlea/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
text = "ALEA: Not connected"
horizontal_alignment = 1
vertical_alignment = 1
[node name="PreferenceServer" type="Window" parent="."]
title = "Preference Server"
initial_position = 2
size = Vector2i(300, 300)
visible = false
exclusive = true
always_on_top = true
script = ExtResource("6_54nnr")
[node name="VBoxContainer" type="VBoxContainer" parent="PreferenceServer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="TabContainer" type="TabContainer" parent="PreferenceServer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="Alea" type="VBoxContainer" parent="PreferenceServer/VBoxContainer/TabContainer"]
layout_mode = 2
[node name="Address" type="LineEdit" parent="PreferenceServer/VBoxContainer/TabContainer/Alea"]
layout_mode = 2
size_flags_vertical = 3
text = "127.0.0.1"
placeholder_text = "Address"
[node name="Port" type="LineEdit" parent="PreferenceServer/VBoxContainer/TabContainer/Alea"]
layout_mode = 2
size_flags_vertical = 3
text = "33333"
placeholder_text = "Address"
[node name="XMPP" type="VBoxContainer" parent="PreferenceServer/VBoxContainer/TabContainer"]
visible = false
layout_mode = 2
[node name="Address" type="LineEdit" parent="PreferenceServer/VBoxContainer/TabContainer/XMPP"]
layout_mode = 2
size_flags_vertical = 3
text = "127.0.0.1"
placeholder_text = "Address"
[node name="Port" type="LineEdit" parent="PreferenceServer/VBoxContainer/TabContainer/XMPP"]
layout_mode = 2
size_flags_vertical = 3
text = "5222"
placeholder_text = "Address"
[node name="Button" type="Button" parent="PreferenceServer/VBoxContainer"]
layout_mode = 2
disabled = true
text = "Done"
[node name="StateXmpp" type="Panel" parent="."]
anchors_preset = 1
anchor_left = 1.0
anchor_right = 1.0
offset_left = -199.0
offset_bottom = 36.0
grow_horizontal = 0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="VBoxContainer" type="VBoxContainer" parent="StateXmpp"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="State" type="Label" parent="StateXmpp/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
text = "XMPP: Not connected"
horizontal_alignment = 1 horizontal_alignment = 1
vertical_alignment = 1 vertical_alignment = 1
@ -116,7 +205,13 @@ vertical_alignment = 1
[connection signal="focus_exited" from="Window" to="Window" method="_on_focus_exited"] [connection signal="focus_exited" from="Window" to="Window" method="_on_focus_exited"]
[connection signal="mouse_entered" from="Window" to="Window" method="_on_mouse_entered"] [connection signal="mouse_entered" from="Window" to="Window" method="_on_mouse_entered"]
[connection signal="mouse_exited" from="Window" to="Window" method="_on_mouse_exited"] [connection signal="mouse_exited" from="Window" to="Window" method="_on_mouse_exited"]
[connection signal="text_changed" from="Window/VBoxContainer/Address" to="Window" method="_on_text_edit_text_changed"] [connection signal="pressed" from="Window/VBoxContainer/HBoxContainer/Preference" to="Window" method="_on_preference_pressed"]
[connection signal="text_changed" from="Window/VBoxContainer/Port" to="Window" method="_on_text_edit_text_changed"] [connection signal="text_changed" from="Window/VBoxContainer/Account" to="Window" method="_on_text_edit_text_changed"]
[connection signal="text_changed" from="Window/VBoxContainer/TextEdit" to="Window" method="_on_text_edit_text_changed"] [connection signal="text_changed" from="Window/VBoxContainer/Password" to="Window" method="_on_password_text_changed"]
[connection signal="pressed" from="Window/VBoxContainer/Button" to="Window" method="_on_button_pressed"] [connection signal="pressed" from="Window/VBoxContainer/Button" to="Window" method="_on_button_pressed"]
[connection signal="close_requested" from="PreferenceServer" to="PreferenceServer" method="_on_close_requested"]
[connection signal="text_changed" from="PreferenceServer/VBoxContainer/TabContainer/Alea/Address" to="PreferenceServer" method="_on_text_changed"]
[connection signal="text_changed" from="PreferenceServer/VBoxContainer/TabContainer/Alea/Port" to="PreferenceServer" method="_on_text_changed"]
[connection signal="text_changed" from="PreferenceServer/VBoxContainer/TabContainer/XMPP/Address" to="PreferenceServer" method="_on_text_changed"]
[connection signal="text_changed" from="PreferenceServer/VBoxContainer/TabContainer/XMPP/Port" to="PreferenceServer" method="_on_text_changed"]
[connection signal="pressed" from="PreferenceServer/VBoxContainer/Button" to="PreferenceServer" method="_on_button_pressed"]

View file

@ -73,6 +73,7 @@ func _init():
set_locale(language) set_locale(language)
func _process(delta): func _process(delta):
print("TCP:", tcp_peer.get_status())
if (tcp_peer.get_status() == StreamPeerTCP.STATUS_CONNECTED) : if (tcp_peer.get_status() == StreamPeerTCP.STATUS_CONNECTED) :
if (tcp_peer.has_method("poll")): if (tcp_peer.has_method("poll")):
tcp_peer.poll() tcp_peer.poll()
@ -92,6 +93,48 @@ func _process(delta):
set_process(false) # stop listening for packets set_process(false) # stop listening for packets
###
# Connect to the server ip and port, and start checking whether there's stream
# info yet.
###
func connect_to_server(server:String, port:int):
if tcp_peer.get_status() == StreamPeerTCP.STATUS_CONNECTED:
pass
tcp_peer.connect_to_host(server, port)
count_connecting_time = 0
set_process(true)
###
# Send a string in the appropriate encoding.
###
func send_string(stanza:String):
debug.emit("Sending data: [color=blue]%[/color]".format([stanza], "%"))
tcp_peer.put_data(stanza.to_utf8_buffer())
###
# End the stream.
###
func end_stream():
debug.emit("Stream: Ending stream")
send_string("</stream>")
if tcp_peer.has_method("disconnect_from_stream"):
tcp_peer.disconnect_from_stream()
else:
tcp_peer.disconnect_from_host()
set_process(false)
stream_status = StreamState.END
func connect_stream():
if (tcp_peer.get_status() == StreamPeerTCP.STATUS_CONNECTED):
end_stream()
tcp_peer = StreamPeerTCP.new()
stream_status = StreamState.START
stream_process()
func remove_stream_header(text :String) -> String: func remove_stream_header(text :String) -> String:
var index = 0 var index = 0
if text.begins_with("<?") : if text.begins_with("<?") :
@ -113,5 +156,119 @@ func collect_stanza(text : String):
pass pass
###
# Try to switch to an SSL based connection.
###
func negotiate_tls():
var ssl_peer = StreamPeerTLS.new()
# I am unsure how to validate the ssl certificate for an unknown host?
var ssl_succes = FAILED
#var ssl_succes = ssl_peer.connect_to_stream(tcp_peer)
print(ssl_succes)
if ssl_succes == OK:
debug.emit("Stream: switched to SSL!")
tcp_peer = ssl_peer
start_stream()
else:
error.emit("SSL failed, error %".format([ssl_succes], "%"))
###
# Try to authenticate using SASL. We can currently only do plain.
# For SCRAM based methods, we need HMAC at the very least.
###
func stream_process(response :String = ""): func stream_process(response :String = ""):
if (!response.length() == 0 and stream_status != self.StreamState.STANZA):
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: % \n\nStream errors cannot be recovered from, the connection will be closed.'.format([error_name, stream_error.human_friendly_error_message()], "%"))
error.emit(error)
elif response.begins_with("<stream:features"):
var stream_features = XMPPStreamFeatures.new()
stream_features.parse_from_xml(response)
if stream_features.parsedDictionary.has("starttls"):
debug.emit("Stream: sending request for ssl")
var request_tls = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
send_string(request_tls)
stream_status = self.StreamState.TLS
elif stream_features.parsedDictionary.has("mechanisms"):
var authentication_methods = stream_features.parsedDictionary["mechanisms"]
if (!authentication_methods.empty()):
debug.emit("Stream: authentication methods: " + authentication_methods.join(", "))
negotiate_sasl(authentication_methods)
stream_status = self.StreamState.AUTHENTICATE
else:
if stream_status == StreamState.TLS:
if response.begins_with("<proceed"):
debug.emit("Stream: gotten go ahead for ssl")
negotiate_tls()
else:
error.emit(tr("Tls negotiation failed."))
elif stream_status == StreamState.AUTHENTICATE:
if response.begins_with("<success"):
stream_status = self.StreamState.STANZA
# start_stream()
return
else:
var parser = XMLParser.new()
# parser.open_buffer(response.to_utf8())
var failure_text = []
while parser.read() == OK:
if parser.get_node_type() == XMLParser.NODE_TEXT:
failure_text.append(parser.get_node_data())
print(failure_text)
# failure_text = tr("Authentication failed! %".format([PoolStringArray(failure_text).join(" ")], "%"))
error.emit(failure_text)
elif stream_status == self.StreamState.START:
connect_to_server(server_ip, port_number)
start_stream()
pass pass
else:
print("Mystery stream status: "+str(stream_status))
####
# Send the stream header.
# Needs to be done several times over stream negotiation.
####
func start_stream():
var server_name = account_name.split("@")[-1]
var streamstart = []
var xmlVersion= "<?xml version='1.0'?>\n"
streamstart.append("<stream:stream")
streamstart.append("xmlns='jabber:client'")
streamstart.append("xmlns:stream='http://etherx.jabber.org/streams'")
streamstart.append("from='%'".format([account_name], "%"))
streamstart.append("to='%'".format([server_name], "%"))
streamstart.append("version='1.0'")
streamstart.append("xml:lang='%'".format([locale], "%"))
streamstart.append(">")
streamstart = streamstart.join(" ")
send_string(str(xmlVersion + streamstart))
###
# Try to switch to an SSL based connection.
###
func negotiate_sasl(authentication_methods : Array):
if (!authentication_methods.has("PLAIN")):
end_stream()
debug.emit("Stream: sending request for plain")
var auth_account = "\u0000"+account_name.split("@")[0]
auth_account += "\u0000"
auth_account += password
auth_account = Marshalls.utf8_to_base64(auth_account)
var request_sasl = []
request_sasl.append("<auth")
request_sasl.append("xmlns='urn:ietf:params:xml:ns:xmpp-sasl'")
request_sasl.append("mechanism='PLAIN'")
request_sasl.append(">%</auth>".format([auth_account], "%"))
send_string(request_sasl.join(" "))

View file

@ -0,0 +1,149 @@
###
# This file is part of Godot XMPP Client
# SPDX-FileCopyrightText: 2020 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
#
# SPDX-License-Identifier: MIT
###
extends Resource
class_name XMPPStreamError
###
# This is a parser for stream errors, that keeps a list of human friendly error-explainations.
# Stream errors, unlike stanza errors, will always close the stream.
###
enum StreamErrorTypes{
bad_format,
bad_namespace_prefix,
conflict,
connection_timeout,
host_gone,
host_unknown,
improper_addressing,
internal_server_error,
invalid_from,
invalid_namespace,
invalid_xml,
not_authorized,
not_well_formed,
policy_violation,
remote_connection_failed,
reset,
resource_constraint,
restricted_xml,
see_other_host,
system_shutdown,
undefined_condition,
unsupported_encoding,
unsupported_feature,
unsupported_stanza_type,
unsupported_version
}
var humane_error_messages = {
StreamErrorTypes.bad_format:
tr("The sent message cannot be processed."),
StreamErrorTypes.bad_namespace_prefix:
tr("The namespace cannot be recognized"),
StreamErrorTypes.conflict:
tr("This connection conflicts with another connection coming from the same address, which makes it impossible to differentiate between the connections."),
StreamErrorTypes.connection_timeout:
tr("This client took too long to respond, and thus the server assumed the device had lost internet connection."),
StreamErrorTypes.host_gone:
tr("The address of the server has changed."),
StreamErrorTypes.host_unknown:
tr("The server does not know the address."),
StreamErrorTypes.improper_addressing:
tr("The address is missing from the sent message."),
StreamErrorTypes.internal_server_error:
tr("The server is experiencing issues, try again later."),
StreamErrorTypes.invalid_from:
tr("This connection is not allowed to sign it's messages with the given address."),
StreamErrorTypes.invalid_namespace:
tr("The message was formatted with an incorrect namespace."),
StreamErrorTypes.invalid_xml:
tr("The message was formatted with invalid xml."),
StreamErrorTypes.not_authorized:
tr("The client is not allowed to sent these messages because it is not authorized to do so."),
StreamErrorTypes.not_well_formed:
tr("The message was formatted with incorrectly formed xml."),
StreamErrorTypes.policy_violation:
tr("The server determined the message violated server policy in some technical manner."),
StreamErrorTypes.remote_connection_failed:
tr("The server is unable to deliver the message because it cannot connect to the other server."),
StreamErrorTypes.reset:
tr("The server closed the connection because it deemed to connection needed to be reset for either feature or security purposes."),
StreamErrorTypes.resource_constraint:
tr("The server is too busy to handle this stream."),
StreamErrorTypes.restricted_xml:
tr("The message contained restricted xml (such as comments, processing instructions, dtd subset, or an xml entity reference)"),
StreamErrorTypes.see_other_host:
tr("The server is redirecting the client to a different host."),
StreamErrorTypes.system_shutdown:
tr("The server is being shut down."),
StreamErrorTypes.undefined_condition:
tr("A special case error has occured. See included xml stream."),
StreamErrorTypes.unsupported_encoding:
tr("The message was encoded with an encoding other than UTF-8, or contained extra bytes that the server is not expecting (as possible with godot's tls functions)."),
StreamErrorTypes.unsupported_feature:
tr("The required stream features are not supported."),
StreamErrorTypes.unsupported_stanza_type:
tr("The sent message is of a stanza type that the server does not recognize."),
StreamErrorTypes.unsupported_version:
tr("This server does not support XMPP version 1.0")
}
var error_type = StreamErrorTypes.undefined_condition
var extra_data = ""
func parse_from_xml(xml:String):
var parser = XMLParser.new()
parser.open_buffer(xml.to_utf8_buffer())
parse_with_parser(parser)
func parse_with_parser(parser:XMLParser):
# Read the error element
if parser.read() == OK and parser.get_node_name() == "stream:error":
if parser.is_empty():
error_type = 0
# Read the error inside...
if parser.read() == OK:
var error_name = parser.get_node_name().replace("-", "_")
error_type = StreamErrorTypes[error_name]
if !parser.is_empty() and parser.read() == OK:
extra_data = parser.get_node_data()
return OK
func stanza() -> String:
var stanza = []
stanza.append("<stream:error>")
var error_name = error_name_for_enum(error_type)
if !error_type.empty():
stanza.append("<"+error_name)
stanza.append("xmlns='urn:ietf:params:xml:ns:xmpp-streams'")
if extra_data.empty():
stanza.append("/>")
else:
stanza.append(">"+extra_data+"</"+error_name+">")
stanza.append("</stream:error>")
return stanza.join(" ")
func error_name_for_enum(value :int):
var error_name = ""
for i in StreamErrorTypes.keys():
if StreamErrorTypes[i] == value:
error_name = str(i).replace("_", "-")
return error_name
###
# Human friendly error messages for the error dialogue.
###
func human_friendly_error_message():
return humane_error_messages[error_type]

View file

@ -0,0 +1,93 @@
###
# This file is part of Godot XMPP Client
# SPDX-FileCopyrightText: 2020 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
#
# SPDX-License-Identifier: MIT
###
extends Resource
class_name XMPPStreamFeatures
###
# Parser for stream features.
##
@export var xml_string:String = "":
set = parse_from_xml
@export var parsedDictionary:Dictionary = {}
enum FeatureReq{
NO_FEATURE,
NO_REQ,
OPTIONAL,
REQUIRED
}
###
# Remove the entry as it's done.
###
func feature_done(feature:String):
parsedDictionary.erase(feature)
func parse_from_xml(xml:String):
var parser = XMLParser.new()
parser.open_buffer(xml.to_utf8_buffer())
parser.read()
parse_children(parser, parser.get_node_name())
func parse_children(parser:XMLParser, _parent_name:String):
parsedDictionary = {}
while (parser.read() == OK):
if (parser.get_node_type()== XMLParser.NODE_ELEMENT):
var node_name = parser.get_node_name()
if !parser.is_empty():
match(node_name):
"mechanisms":
parsedDictionary[node_name] = parse_option_list(parser, node_name, "mechanism")
"compression":
parsedDictionary[node_name] = parse_option_list(parser, node_name, "method")
_:
parsedDictionary[node_name] = parse_requirement(parser, node_name)
else:
parsedDictionary[node_name] = FeatureReq.NO_REQ
# else:
# print("Unhandled xml node type: "+str(parser.get_node_type()))
func parse_requirement(parser:XMLParser, parent_name:String):
var required = self.FeatureReq.NO_REQ
while (parser.read() == OK):
if (parser.get_node_type()== XMLParser.NODE_ELEMENT):
var node_name = parser.get_node_name()
if parser.is_empty():
if node_name == "required":
return self.FeatureReq.REQUIRED
elif node_name == "optional":
return self.FeatureReq.OPTIONAL
elif(parser.get_node_type()== XMLParser.NODE_ELEMENT_END):
var node_name = parser.get_node_name()
if node_name == parent_name:
return required
func parse_option_list(parser:XMLParser, parent_name:String, option_name:String):
var options = []
while (parser.read() == OK):
var _result
if (parser.get_node_type()== XMLParser.NODE_ELEMENT):
if parser.get_node_name() == option_name:
_result =parser.read()
if (parser.get_node_type()== XMLParser.NODE_TEXT):
options.append(parser.get_node_data())
_result = parser.read()
elif(parser.get_node_type()== XMLParser.NODE_ELEMENT_END):
if parser.get_node_name() == parent_name:
return options

155
create-certificate.sh Executable file
View file

@ -0,0 +1,155 @@
#!/bin/bash
#
# Script to create certificate
#
# Copyright (C) 2023 AleaJactaEst
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# Example :
# ./create-certificate.sh
declare DEBUG=0
declare VERBOSE=0
declare HELP=0
declare WORKDIR="$(dirname $(readlink -f $0))"
declare OPENSSLBIN="openssl"
declare CERTDIR="$WORKDIR/.cert"
function msg_debug()
{
if [ $DEBUG -ne 0 ]
then
echo "### DEBUG : $*" >&2
fi
}
function msg_info()
{
echo "--- INFO : $*" >&2
}
function msg_error()
{
echo "*** ERROR : $*" >&2
}
function byebye()
{
local CODE=$?
if [ $CODE -ne 0 ]
then
msg_error "return code:$CODE"
else
msg_info "End"
fi
exit $CODE
}
while getopts hdvs:c: flag
do
case "${flag}" in
h) HELP=1;;
d) DEBUG=1;;
v) VERBOSE=1;;
s) OPENSSLBIN=${OPTARG};;
c) CERTDIR=${OPTARG};;
*) HELP=1;;
esac
done
if [[ $HELP -ne 0 ]]
then
cat << EOF
$(basename $0) [Option] : Donwload Launch Godot
Option:
-h : Show help
-d : Show debug message
-v : Show verbose message
-s <path> : localization openssl
-c <path> : directory where certificate are created
EOF
exit 1
fi
trap byebye EXIT
msg_info "Start"
msg_debug "WORKDIR:$WORKDIR"
mkdir -p $CERTDIR
msg_info "Clean old file"
rm -f $CERTDIR/ca-cert.pem $CERTDIR/ca-db-index.attr $CERTDIR/ca-db-index.attr.old $CERTDIR/ca-db-index.old $CERTDIR/ca-db-serial $CERTDIR/ca-db-serial.old $CERTDIR/ca-key.pem $CERTDIR/client-key.pem $CERTDIR/client.csr $CERTDIR/server-key.pem $CERTDIR/server.csr $CERTDIR/server-cert.pem $CERTDIR/client-cert.pem $CERTDIR/01.pem $CERTDIR/02.pem
rm -f $CERTDIR/ca-db-index $CERTDIR/ca.conf
ls -l $CERTDIR
msg_info "Restart index certificate"
touch $CERTDIR/ca-db-index
echo 01 > $CERTDIR/ca-db-serial
ls -l $CERTDIR
msg_info "Create CA config"
cat << EOF > $CERTDIR/ca.conf
[ ca ]
default_ca = ca_default
[ ca_default ]
dir = $CERTDIR/
certs = \$dir
new_certs_dir = \$dir
database = $CERTDIR/ca-db-index
serial = $CERTDIR/ca-db-serial
RANDFILE = $CERTDIR/ca-db-rand
certificate = $CERTDIR/ca-cert.pem
private_key = $CERTDIR/ca-key.pem
default_days = 365
default_crl_days = 365
default_md = sha256
preserve = no
policy = generic_policy
[ generic_policy ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
EOF
msg_info "Certificate Authority"
$OPENSSLBIN req -nodes -x509 -newkey rsa:2048 -days 365 -keyout $CERTDIR/ca-key.pem -out $CERTDIR/ca-cert.pem -subj "/C=EU/ST=France/L=Paris/O=Khaganat/OU=FR/CN=khanagat.org" || exit 2
msg_info "Server Certificate"
$OPENSSLBIN req -nodes -new -newkey rsa:2048 -keyout $CERTDIR/server-key.pem -out $CERTDIR/server.csr -subj "/C=EU/ST=France/L=Paris/O=Khaganat/OU=FR/CN=server.khanagat.org" || exit 2
msg_info "Sign Server Certificate"
$OPENSSLBIN ca -config $CERTDIR/ca.conf -days 365 -in $CERTDIR/server.csr -out $CERTDIR/server-cert.pem -batch || exit 2
msg_info "Client Certificate"
$OPENSSLBIN req -nodes -new -newkey rsa:2048 -keyout $CERTDIR/client-key.pem -out $CERTDIR/client.csr -subj "/C=EU/ST=France/L=Paris/O=Khaganat/OU=FR/CN=client.khanagat.org" || exit 2
msg_info "Sign Client Certificate"
$OPENSSLBIN ca -config $CERTDIR/ca.conf -days 365 -in $CERTDIR/client.csr -out $CERTDIR/client-cert.pem -batch || exit 2
msg_info "Publish certificate"
#cp $CERTDIR/client-cert.pem src/certs/
#cp $CERTDIR/client-key.pem src/certs/
#cp $CERTDIR/server-cert.pem src/certs/
#cp $CERTDIR/server-key.pem src/certs/

View file

@ -668,6 +668,14 @@ struct Cli {
/// Loop to force update /// Loop to force update
#[arg(short, long, default_value_t = 10)] #[arg(short, long, default_value_t = 10)]
forceupdate: u8, forceupdate: u8,
/// Server Key
#[arg(short = 'k', long, default_value = "server-key.pem")]
serverkey: String,
/// Server Certificate
#[arg(short = 'm', long, default_value = "server-cert.pem")]
servercert: String,
} }
/* /*

16
start-bazar-fake-xmpp-server.sh Executable file
View file

@ -0,0 +1,16 @@
#!/bin/bash
declare WORKDIR="$(dirname $(readlink -f $0))"
declare OPENFIREDIR="$WORKDIR/.xmpp"
echo "WORKDIR:$WORKDIR"
echo "OPENFIREDIR:$OPENFIREDIR"
docker pull sameersbn/openfire:3.10.3-19
mkdir -p $OPENFIREDIR
docker run --name openfire \
--publish 9090:9090 --publish 5222:5222 --publish 7777:7777 \
--volume $OPENFIREDIR:/var/lib/openfire \
sameersbn/openfire:3.10.3-19
docker start openfire

View file

@ -26,6 +26,7 @@ declare BUILD=0
declare WORKDIR="$(dirname $(readlink -f $0))" declare WORKDIR="$(dirname $(readlink -f $0))"
declare RUSTDIR="$WORKDIR/.rust" declare RUSTDIR="$WORKDIR/.rust"
declare SERVERDIR="$WORKDIR/server" declare SERVERDIR="$WORKDIR/server"
declare CERTDIR="$WORKDIR/.cert"
declare OPTIONS="" declare OPTIONS=""
declare LISTEN="" declare LISTEN=""
declare PORT="" declare PORT=""
@ -53,7 +54,7 @@ function byebye()
local CODE=$? local CODE=$?
if [ $CODE -ne 0 ] if [ $CODE -ne 0 ]
then then
msg_error "return code:$code" msg_error "return code:$CODE"
else else
msg_info "End" msg_info "End"
fi fi
@ -95,6 +96,12 @@ trap byebye EXIT
msg_info "Start" msg_info "Start"
msg_debug "WORKDIR:$WORKDIR" msg_debug "WORKDIR:$WORKDIR"
if [[ (! -f $CERTDIR/server-cert.pem) || (! -f $CERTDIR/server-key.pem) ]]
then
msg_error "Missing certificate"
exit 2
fi
if [ $DEBUG -ne 0 ] if [ $DEBUG -ne 0 ]
then then
OPTIONS="$OPTIONS -d" OPTIONS="$OPTIONS -d"
@ -140,6 +147,6 @@ then
fi fi
#echo "OPTIONS:$OPTIONS" #echo "OPTIONS:$OPTIONS"
$SERVERDIR/target/debug/server $OPTIONS $SERVERDIR/target/debug/server $OPTIONS --serverkey $CERTDIR/server-key.pem --servercert $CERTDIR/server-cert.pem
# END (call function byebye) # END (call function byebye)