Add documentation for new DTLS features.

This commit is contained in:
Fabio Alessandrelli 2020-02-15 21:34:00 +01:00
parent 7d1a290af2
commit 9eea2cf9d6
5 changed files with 298 additions and 0 deletions

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="DTLSServer" inherits="Reference" version="4.0">
<brief_description>
Helper class to implement a DTLS server.
</brief_description>
<description>
This class is used to store the state of a DTLS server. Upon [method setup] it converts connected [PacketPeerUDP] to [PacketPeerDTLS] accepting them via [method take_connection] as DTLS clients. Under the hood, this class is used to store the DTLS state and cookies of the server. The reason of why the state and cookies are needed is outside of the scope of this documentation.
Below a small example of how to use it:
[codeblock]
# server.gd
extends Node
var dtls := DTLSServer.new()
var server := UDPServer.new()
var peers = []
func _ready():
server.listen(4242)
var key = load("key.key") # Your private key.
var cert = load("cert.crt") # Your X509 certificate.
dtls.setup(key, cert)
func _process(delta):
while server.is_connection_available():
var peer : PacketPeerUDP = server.take_connection()
var dtls_peer : PacketPeerDTLS = dtls.take_connection(peer)
if dtls_peer.get_status() != PacketPeerDTLS.STATUS_HANDSHAKING:
continue # It is normal that 50% of the connections fails due to cookie exchange.
print("Peer connected!")
peers.append(dtls_peer)
for p in peers:
p.poll() # Must poll to update the state.
if p.get_status() == PacketPeerDTLS.STATUS_CONNECTED:
while p.get_available_packet_count() > 0:
print("Received message from client: %s" % p.get_packet().get_string_from_utf8())
p.put_packet("Hello DTLS client".to_utf8())
[/codeblock]
[codeblock]
# client.gd
extends Node
var dtls := PacketPeerDTLS.new()
var udp := PacketPeerUDP.new()
var connected = false
func _ready():
udp.connect_to_host("127.0.0.1", 4242)
dtls.connect_to_peer(udp, false) # Use true in production for certificate validation!
func _process(delta):
dtls.poll()
if dtls.get_status() == PacketPeerDTLS.STATUS_CONNECTED:
if !connected:
# Try to contact server
dtls.put_packet("The answer is... 42!".to_utf8())
while dtls.get_available_packet_count() > 0:
print("Connected: %s" % dtls.get_packet().get_string_from_utf8())
connected = true
[/codeblock]
</description>
<tutorials>
</tutorials>
<methods>
<method name="setup">
<return type="int" enum="Error">
</return>
<argument index="0" name="key" type="CryptoKey">
</argument>
<argument index="1" name="certificate" type="X509Certificate">
</argument>
<argument index="2" name="ca_chain" type="X509Certificate">
</argument>
<description>
Setup the DTLS server to use the given [code]private_key[/code] and provide the given [code]certificate[/code] to clients. You can pass the optional [code]chain[/code] parameter to provide additional CA chain information along with the certificate.
</description>
</method>
<method name="take_connection">
<return type="PacketPeerDTLS">
</return>
<argument index="0" name="udp_peer" type="PacketPeerUDP">
</argument>
<description>
Try to initiate the DTLS handshake with the given [code]udp_peer[/code] which must be already connected (see [method PacketPeerUDP.connect_to_host]).
[b]Note[/b]: You must check that the state of the return PacketPeerUDP is [constant PacketPeerDTLS.STATUS_HANDSHAKING], as it is normal that 50% of the new connections will be invalid due to cookie exchange.
</description>
</method>
</methods>
<constants>
</constants>
</class>

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="PacketPeerDTLS" inherits="PacketPeer" version="4.0">
<brief_description>
DTLS packet peer.
</brief_description>
<description>
This class represents a DTLS peer connection. It can be used to connect to a DTLS server, and is returned by [method DTLSServer.take_connection].
</description>
<tutorials>
</tutorials>
<methods>
<method name="connect_to_peer">
<return type="int" enum="Error">
</return>
<argument index="0" name="packet_peer" type="PacketPeerUDP">
</argument>
<argument index="1" name="validate_certs" type="bool" default="false">
</argument>
<argument index="2" name="for_hostname" type="String" default="&quot;&quot;">
</argument>
<argument index="3" name="valid_certificate" type="X509Certificate" default="null">
</argument>
<description>
Connects a [code]peer[/code] beginning the DTLS handshake using the underlying [PacketPeerUDP] which must be connected (see [method PacketPeerUDP.connect_to_host]). If [code]validate_certs[/code] is [code]true[/code], [PacketPeerDTLS] will validate that the certificate presented by the remote peer and match it with the [code]for_hostname[/code] argument. You can specify a custom [X509Certificate] to use for validation via the [code]valid_certificate[/code] argument.
</description>
</method>
<method name="disconnect_from_peer">
<return type="void">
</return>
<description>
Disconnects this peer, terminating the DTLS session.
</description>
</method>
<method name="get_status" qualifiers="const">
<return type="int" enum="PacketPeerDTLS.Status">
</return>
<description>
Returns the status of the connection. See [enum Status] for values.
</description>
</method>
<method name="poll">
<return type="void">
</return>
<description>
Poll the connection to check for incoming packets. Call this frequently to update the status and keep the connection working.
</description>
</method>
</methods>
<constants>
<constant name="STATUS_DISCONNECTED" value="0" enum="Status">
A status representing a [PacketPeerDTLS] that is disconnected.
</constant>
<constant name="STATUS_HANDSHAKING" value="1" enum="Status">
A status representing a [PacketPeerDTLS] that is currently performing the handshake with a remote peer.
</constant>
<constant name="STATUS_CONNECTED" value="2" enum="Status">
A status representing a [PacketPeerDTLS] that is connected to a remote peer.
</constant>
<constant name="STATUS_ERROR" value="3" enum="Status">
A status representing a [PacketPeerDTLS] in a generic error state.
</constant>
<constant name="STATUS_ERROR_HOSTNAME_MISMATCH" value="4" enum="Status">
An error status that shows a mismatch in the DTLS certificate domain presented by the host and the domain requested for validation.
</constant>
</constants>
</class>

View File

@ -16,6 +16,18 @@
Closes the UDP socket the [PacketPeerUDP] is currently listening on. Closes the UDP socket the [PacketPeerUDP] is currently listening on.
</description> </description>
</method> </method>
<method name="connect_to_host">
<return type="int" enum="Error">
</return>
<argument index="0" name="host" type="String">
</argument>
<argument index="1" name="port" type="int">
</argument>
<description>
Calling this method connects this UDP peer to the given [code]host[/code]/[code]port[/code] pair. UDP is in reality connectionless, so this option only means that incoming packets from different addresses are automatically discarded, and that outgoing packets are always sent to the connected address (future calls to [method set_dest_address] are not allowed). This method does not send any data to the remote peer, to do that, use [method PacketPeer.put_var] or [method PacketPeer.put_packet] as usual. See also [UDPServer].
Note: Connecting to the remote peer does not help to protect from malicious attacks like IP spoofing, etc. Think about using an encryption technique like SSL or DTLS if you feel like your application is transfering sensitive information.
</description>
</method>
<method name="get_packet_ip" qualifiers="const"> <method name="get_packet_ip" qualifiers="const">
<return type="String"> <return type="String">
</return> </return>
@ -30,6 +42,13 @@
Returns the port of the remote peer that sent the last packet(that was received with [method PacketPeer.get_packet] or [method PacketPeer.get_var]). Returns the port of the remote peer that sent the last packet(that was received with [method PacketPeer.get_packet] or [method PacketPeer.get_var]).
</description> </description>
</method> </method>
<method name="is_connected_to_host" qualifiers="const">
<return type="bool">
</return>
<description>
Returns [code]true[/code] if the UDP socket is open and has been connected to a remote address. See [method connect_to_host].
</description>
</method>
<method name="is_listening" qualifiers="const"> <method name="is_listening" qualifiers="const">
<return type="bool"> <return type="bool">
</return> </return>

98
doc/classes/UDPServer.xml Normal file
View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="UDPServer" inherits="Reference" version="4.0">
<brief_description>
Helper class to implement a UDP server.
</brief_description>
<description>
A simple server that opens a UDP socket and returns connected [PacketPeerUDP] upon receiving new packets. See also [method PacketPeerUDP.connect_to_host].
Below a small example of how it can be used:
[codeblock]
# server.gd
extends Node
var server := UDPServer.new()
var peers = []
func _ready():
server.listen(4242)
func _process(delta):
if server.is_connection_available():
var peer : PacketPeerUDP = server.take_connection()
var pkt = peer.get_packet()
print("Accepted peer: %s:%s" % [peer.get_packet_ip(), peer.get_packet_port()])
print("Received data: %s" % [pkt.get_string_from_utf8()])
# Reply so it knows we received the message.
peer.put_packet(pkt)
# Keep a reference so we can keep contacting the remote peer.
peers.append(peer)
for i in range(0, peers.size()):
pass # Do something with the connected peers.
[/codeblock]
[codeblock]
# client.gd
extends Node
var udp := PacketPeerUDP.new()
var connected = false
func _ready():
udp.connect_to_host("127.0.0.1", 4242)
func _process(delta):
if !connected:
# Try to contact server
udp.put_packet("The answer is... 42!".to_utf8())
if udp.get_available_packet_count() > 0:
print("Connected: %s" % udp.get_packet().get_string_from_utf8())
connected = true
[/codeblock]
</description>
<tutorials>
</tutorials>
<methods>
<method name="is_connection_available" qualifiers="const">
<return type="bool">
</return>
<description>
Returns [code]true[/code] if a packet with a new address/port combination is received on the socket.
</description>
</method>
<method name="is_listening" qualifiers="const">
<return type="bool">
</return>
<description>
Returns [code]true[/code] if the socket is open and listening on a port.
</description>
</method>
<method name="listen">
<return type="int" enum="Error">
</return>
<argument index="0" name="port" type="int">
</argument>
<argument index="1" name="bind_address" type="String" default="&quot;*&quot;">
</argument>
<description>
Starts the server by opening a UDP socket listening on the given port. You can optionally specify a [code]bind_address[/code] to only listen for packets sent to that address. See also [method PacketPeerUDP.listen].
</description>
</method>
<method name="stop">
<return type="void">
</return>
<description>
Stops the server, closing the UDP socket if open. Will not disconnect any connected [PacketPeerUDP].
</description>
</method>
<method name="take_connection">
<return type="PacketPeerUDP">
</return>
<description>
Returns a [PacketPeerUDP] connected to the address/port combination of the first packet in queue. Will return [code]null[/code] if no packet is in queue. See also [method PacketPeerUDP.connect_to_host].
</description>
</method>
</methods>
<constants>
</constants>
</class>

View File

@ -104,6 +104,24 @@
The IP used when creating a server. This is set to the wildcard [code]"*"[/code] by default, which binds to all available interfaces. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]"192.168.1.1"[/code]. The IP used when creating a server. This is set to the wildcard [code]"*"[/code] by default, which binds to all available interfaces. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]"192.168.1.1"[/code].
</description> </description>
</method> </method>
<method name="set_dtls_certificate">
<return type="void">
</return>
<argument index="0" name="certificate" type="X509Certificate">
</argument>
<description>
Configure the [X509Certificate] to use when [member use_dtls] is [code]true[/code]. For servers, you must also setup the [CryptoKey] via [method set_dtls_key].
</description>
</method>
<method name="set_dtls_key">
<return type="void">
</return>
<argument index="0" name="key" type="CryptoKey">
</argument>
<description>
Configure the [CryptoKey] to use when [member use_dtls] is [code]true[/code]. Remember to also call [method set_dtls_certificate] to setup your [X509Certificate].
</description>
</method>
</methods> </methods>
<members> <members>
<member name="always_ordered" type="bool" setter="set_always_ordered" getter="is_always_ordered" default="false"> <member name="always_ordered" type="bool" setter="set_always_ordered" getter="is_always_ordered" default="false">
@ -115,6 +133,9 @@
<member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="NetworkedMultiplayerENet.CompressionMode" default="0"> <member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="NetworkedMultiplayerENet.CompressionMode" default="0">
The compression method used for network packets. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all. The compression method used for network packets. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all.
</member> </member>
<member name="dtls_verify" type="bool" setter="set_dtls_verify_enabled" getter="is_dtls_verify_enabled" default="true">
Enable or disable certiticate verification when [member use_dtls] [code]true[/code].
</member>
<member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" /> <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" />
<member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true"> <member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true">
Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server. Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server.
@ -123,6 +144,10 @@
Set the default channel to be used to transfer data. By default, this value is [code]-1[/code] which means that ENet will only use 2 channels, one for reliable and one for unreliable packets. Channel [code]0[/code] is reserved, and cannot be used. Setting this member to any value between [code]0[/code] and [member channel_count] (excluded) will force ENet to use that channel for sending data. Set the default channel to be used to transfer data. By default, this value is [code]-1[/code] which means that ENet will only use 2 channels, one for reliable and one for unreliable packets. Channel [code]0[/code] is reserved, and cannot be used. Setting this member to any value between [code]0[/code] and [member channel_count] (excluded) will force ENet to use that channel for sending data.
</member> </member>
<member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="NetworkedMultiplayerPeer.TransferMode" default="2" /> <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="NetworkedMultiplayerPeer.TransferMode" default="2" />
<member name="use_dtls" type="bool" setter="set_dtls_enabled" getter="is_dtls_enabled" default="false">
When enabled, the client or server created by this peer, will use [PacketPeerDTLS] instead of raw UDP sockets for communicating with the remote peer. This will make the communication encrypted with DTLS at the cost of higher resource usage and potentially larger packet size.
Note: When creating a DTLS server, make sure you setup the key/certificate pair via [method set_dtls_key] and [method set_dtls_certificate]. For DTLS clients, have a look at the [member dtls_verify] option, and configure the certificate accordingly via [method set_dtls_certificate].
</member>
</members> </members>
<constants> <constants>
<constant name="COMPRESS_NONE" value="0" enum="CompressionMode"> <constant name="COMPRESS_NONE" value="0" enum="CompressionMode">