*********************** Transport & connections *********************** Connections =========== Environments ------------ The |ss| exists in two separate environments: * staging * production The staging environment allows to test a game. Note that the staging environment is sharing the user datastore with the production environment, which means that game history, rankings, etc are common to both environments. All developers are sharing the same staging environment, but every developer have their own production environment (created by request to the |dow| administrators). Before a client can connect to a staging or production environment, the developer must have created its game identifier in the |dow| Studio Manager, and contacted the |dow| Administrators to obtain the connection information to the staging environment. Before the developer's game enters in beta stage, the developer needs to contact the |dow| Administrators again to obtain a production environment. The developer will then receive the correct hostname and public key pinning parameters. This procedure will soon be automated through the |dow| Studio Manager. Connecting to an Environment ---------------------------- The Client connects to the Scalable Server 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 |ss| needs to inform her of an important event (like an invitation, or if it's this player's turn in a game), the |ss| will send a cross-platform push notification to all devices that this player connected with, provided the developer gives the necessary push certificates to the |dow| Administrators. A connection is valid for a Player and a given :term:`Game`. .. _transport-public-key-pinning: TLS Public Key Pinning ---------------------- To be noted that the production and staging |ss| 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:: 25:5c:20:0e:ae:6c:55:e1:a7:a9:f8:eb:7b:93:3c:ad:3b:98:18:7c:75:28:cc:10:5d:42:48:9e:55:ca:a3:cc Backup:: 1d:34:65:b4:70:9d:29:e8:22:53:a8:a2:7c:fc:5a:00:1e:9e:9b:72:3a:a8:02:00:4f:f8:6c:4e:d2:c0:4e:d8 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 |ss| listens on the TCP port 2445. You can check that your own network allows to connect to our staging |ss| with the following command: .. code-block:: bash openssl s_client -host staging-async-games.daysofwonder.com -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 |ss| 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 |ss| 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. .. _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. .. _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. .. _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.async.AsyncAuthRequest async_auth_request = 400; /// 400 com.daysofwonder.async.GameCreatedRequest game_created_request = 404; /// 404 [...] } } 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 Packet { int64 id = 1; com.daysofwonder.mm.Message payload = 3; } The ``id`` field is filled by the |client| with any arbitrary value. If this request allows a response from the server, the |ss| 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.