Line data Source code
1 : /* SPDX-License-Identifier: GPL-3.0-or-later */
2 : /* Copyright 2026 Peter Csaszar */
3 :
4 : /**
5 : * @file app/dc_session.c
6 : * @brief Implementation of per-DC session bring-up.
7 : */
8 :
9 : #include "app/dc_session.h"
10 :
11 : #include "app/auth_flow.h"
12 : #include "app/dc_config.h"
13 : #include "app/session_store.h"
14 : #include "infrastructure/auth_transfer.h"
15 : #include "logger.h"
16 :
17 : #include <string.h>
18 :
19 30 : int dc_session_open(int dc_id, DcSession *out) {
20 30 : if (!out) return -1;
21 29 : memset(out, 0, sizeof(*out));
22 29 : out->dc_id = dc_id;
23 29 : transport_init(&out->transport);
24 29 : mtproto_session_init(&out->session);
25 :
26 : /* Fast path: reuse a previously persisted auth_key. A cached auth_key
27 : * on the server has whatever authorization state was bound to it at
28 : * last import, so we treat it as authorized here. A later
29 : * AUTH_KEY_UNREGISTERED would force a re-handshake. */
30 29 : if (session_store_load_dc(dc_id, &out->session) == 0) {
31 26 : const DcEndpoint *ep = dc_lookup(dc_id);
32 26 : if (ep && transport_connect(&out->transport, ep->host, ep->port) == 0) {
33 25 : out->transport.dc_id = dc_id;
34 25 : out->authorized = 1;
35 25 : logger_log(LOG_INFO, "dc_session: reused cached key on DC%d", dc_id);
36 25 : return 0;
37 : }
38 1 : logger_log(LOG_WARN,
39 : "dc_session: cached DC%d unusable, re-handshaking", dc_id);
40 1 : transport_close(&out->transport);
41 1 : mtproto_session_init(&out->session);
42 : }
43 :
44 : /* Slow path: fresh DH handshake. The new auth_key has no user binding
45 : * yet; the caller must run auth.importAuthorization before any
46 : * authorized RPC on @p out. */
47 4 : if (auth_flow_connect_dc(dc_id, &out->transport, &out->session) != 0) {
48 4 : logger_log(LOG_ERROR, "dc_session: handshake failed on DC%d", dc_id);
49 4 : return -1;
50 : }
51 0 : out->authorized = 0;
52 :
53 0 : if (session_store_save_dc(dc_id, &out->session) != 0) {
54 0 : logger_log(LOG_WARN,
55 : "dc_session: could not persist DC%d (non-fatal)", dc_id);
56 : }
57 0 : return 0;
58 : }
59 :
60 12 : int dc_session_ensure_authorized(DcSession *sess,
61 : const ApiConfig *cfg,
62 : MtProtoSession *home_s,
63 : Transport *home_t) {
64 12 : if (!sess || !cfg || !home_s || !home_t) return -1;
65 12 : if (sess->authorized) return 0;
66 :
67 2 : AuthExported tok = {0};
68 2 : RpcError err = {0};
69 2 : if (auth_transfer_export(cfg, home_s, home_t,
70 : sess->dc_id, &tok, &err) != 0) {
71 0 : logger_log(LOG_ERROR,
72 : "dc_session: export for DC%d failed (%d: %s)",
73 : sess->dc_id, err.error_code, err.error_msg);
74 0 : return -1;
75 : }
76 2 : RpcError ierr = {0};
77 2 : if (auth_transfer_import(cfg, &sess->session, &sess->transport,
78 : &tok, &ierr) != 0) {
79 0 : logger_log(LOG_ERROR,
80 : "dc_session: import on DC%d failed (%d: %s)",
81 : sess->dc_id, ierr.error_code, ierr.error_msg);
82 0 : return -1;
83 : }
84 2 : sess->authorized = 1;
85 :
86 : /* Persist the now-authorized auth_key. The server-side binding is
87 : * tied to the auth_key itself; on next run we skip the import. */
88 2 : (void)session_store_save_dc(sess->dc_id, &sess->session);
89 2 : return 0;
90 : }
91 :
92 26 : void dc_session_close(DcSession *s) {
93 26 : if (!s) return;
94 25 : transport_close(&s->transport);
95 : }
|