*********************** Transport & connections *********************** Connections =========== Environments ------------ The |ml| exists in two separate environments: * staging * production The staging environment allows to test multiple room integration. Note that the staging environment is sharing the user datastore with the production environment. All developers are sharing the same environments. Hopefully all staging application will use the staging instance and all production application will use the production instance. The idea is that all game will use the same instance in order for all players to share and chat together. Connecting to an Environment ---------------------------- The Client connects to the |ml| with a TLS (1.0+) TCP connection (preferably TLSv1.2 or better). The client doesn’t authenticate to the server with a certificate (it is an anonymous client), but the player authenticates herself to the server (see authentication). The client might choose to keep the connection open as much as possible, in this case it should send Ping requests so that the server can detect connection issues (in return the Client receives Ping responses, which also can help detect connection issues), and compute presence information for the given player. In the event a player is not connected and the |ml| needs to inform her of an important event, no push notification is intended, only live chat enabler. A connection is valid for a Player. .. _ml-transport-public-key-pinning: TLS Public Key Pinning ---------------------- To be noted that the production and staging |ml| present a self-signed certificate to the Client. Clients are supposed to validate the server certificate public key with a procedure called `Public Key Pinning `_. This is a counter-cheating measure that prevents `Man in the Middle attacks `_ where the attacker puts a proxy between the client and the server and is able to decrypt/modify the traffic because the client might not check the server is whom it pretends to be. To prevent this, the client **must** check that the server offered certificate public key is the correct one. It should also check that the ``CN`` part of the certificate ``DN`` is the one that it connected to (each developer have a different one). To check that the public key is correct, the client extracts the public key of the certificate presented by the server, and computes a SHA256 hash of it. The hash should match one of this checksum (valid for the staging environment, the production one will be given to the developer): Primary:: //TODO Backup:: //TODO If the presented public key checksum doesn't match any of those hash, the client *must* drop the connection and possibly inform the user that something went wrong. TLS Troubleshooting ------------------- The |ml| listens on the TCP port 2445. You can check that your own network allows to connect to our staging |ml| with the following command: .. code-block:: bash openssl s_client -host metalobby-staging.games.asmodee.net -port 2445 This should print the certificate chain and the various TLS parameters that were used. If you want to check your client code can connect to a TLS server, you can emulate the |ml| with the following command: .. code-block:: bash openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -nodes -days 3650 openssl s_server -no_ssl3 -accept 2445 -key key.pem -cert cert.pem Then point your client to this fake server to be able to troubleshoot the TLS connection. On the wire transport protocol ============================== Both the *client to server* and *server to client* communications use the same transport protocol. The |ml| transport protocol is a message based protocol, where client and server exchange messages. Most of the time, the message flow obey a request/response kind of protocol. Sometimes the server might send messages to a client on its own. The messages are described and serialized/deserialized on the wire with `Protocol Buffers (v3) `_ which is an efficient, cross-platform, maintainable system. .. _ml-transport-payload-size: Length Delimited Packets ------------------------ The messages are sent over the TLS connection as size-delimited fragments. .. packetdiag:: packetdiag { scale_direction = rtl colwidth = 32 node_height = 48 0-31: Payload size 32-63: Protobuf serialized payload [colheight = 3] } The size of the fragment is serialized in `RFC1700 network order `_ also called Big Endian, that is the most significant bits will be serialized first, and the last significant bit will be serialized last. For example, if the payload size is 498 bytes, the size will be serialized to the following byte string:: 00 00 01 f2 After this size, we'll find the serialized payload. .. _ml-transport-payload-encoding: Protobuf matriochkas -------------------- The :ref:`Protocol` defines several Protobuf messages, but those are not sent verbatim on the wire. They are encapsulated in two different more generic protobuf structures. .. _ml-transport-payload-no-extensions: Message ....... Each request is encapsulated into a `Protobuf oneof `_ field of ``Message`` .. code-block:: proto message Message { int32 request_number = 1; /// id of the underlying request oneof request { PingRequest ping_request = 777; /// 777 com.daysofwonder.metalobby.AsyncAuthRequest async_auth_request = 400; /// 400 [...] com.daysofwonder.metalobby.RoomCreatedRequest room_created_request = 2001; /// 2001 [...] } } Let's imagine we're serializing a ``Ping`` request which is defined as this: .. code-block:: proto message PingRequest { int64 timestamp = 1; } Then the ``request_number`` will be ``777``. When serializing, we'll get the following (shown as Protobuf decoded text): .. code-block:: proto { request_number: 777 ping_request { timestamp: 12343344456 } } The complete definition of the syntax is available on the `Protobuf proto3 page `_. .. note:: The wire format is compatible between Protobuf 2 and Protobuf 3. Any client serializing/deserializing the protocol with Protobuf 2.x can successfully connect to the server which uses Protobuf 3. Packet ...... Before being sent over the wire the ``Message`` needs to be encapsulated into a ``Packet`` structure: .. code-block:: proto message MetaPacket { int64 id = 1; com.daysofwonder.mm.MetaMessage payload = 3; } The ``id`` field is filled by the |client| with any arbitrary value. If this request allows a response from the server, the |ml| will do its best to put the same ``id`` in the response to allow the client to match a response to a sent request. Requests sent by the server without any prior request, or requests sent to another client in response of an incoming request will the ``id`` field set at 0 or unset.