Line data Source code
1 : /*
2 : * Copyright (C) 2004-2026 Savoir-faire Linux Inc.
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 : */
17 : #pragma once
18 :
19 : #include <cstdint>
20 : #include <string>
21 : #include <string_view>
22 : #include <vector>
23 : #include <msgpack.hpp>
24 :
25 : namespace jami {
26 : namespace svc_protocol {
27 :
28 : using namespace std::literals;
29 :
30 : /// Maximum protocol version implemented.
31 : constexpr uint8_t MaxVersion = 1;
32 :
33 : /// Discovery message type discriminators.
34 : namespace MsgType {
35 : constexpr std::string_view Query = "query";
36 : constexpr std::string_view ServiceList = "service_list";
37 : constexpr std::string_view ServiceUpdate = "service_update";
38 : constexpr std::string_view Error = "error";
39 : constexpr std::string_view VersionMismatch = "version_mismatch";
40 : } // namespace MsgType
41 :
42 : /// Channel name prefix used for tunnels: "svc://<service-uuid>".
43 : constexpr std::string_view TunnelChannelPrefix = "svc://";
44 : /// Channel name used for discovery: "svcdisc://query".
45 : constexpr std::string_view DiscoveryChannelName = "svcdisc://query";
46 :
47 : /// Single service descriptor exposed in a service_list response.
48 : struct SvcInfo
49 : {
50 : std::string id; ///< RFC 4122 v4 UUID
51 : std::string name;
52 : std::string description;
53 : std::string proto; ///< "tcp" in v1
54 : std::string scheme; ///< Optional URI scheme hint (e.g. "http", "https"); empty means raw TCP
55 : uint16_t preferred_port {0}; ///< Suggested local listener port on the peer side (0 = any)
56 38 : MSGPACK_DEFINE_MAP(id, name, description, proto, scheme, preferred_port)
57 : };
58 :
59 : /// Request sent by the client over `svcdisc://query`.
60 : struct SvcDiscQuery
61 : {
62 : uint8_t v {MaxVersion};
63 : std::string type {MsgType::Query};
64 1162 : MSGPACK_DEFINE_MAP(v, type)
65 : };
66 :
67 : /// Successful response listing the services visible to the requesting peer.
68 : struct SvcDiscResponse
69 : {
70 : uint8_t v {MaxVersion};
71 : std::string type {MsgType::ServiceList};
72 : /// Long device id of the responder, so the requester can target the
73 : /// exact device when opening a tunnel without a separate lookup.
74 : std::string device;
75 : std::vector<SvcInfo> services;
76 2316 : MSGPACK_DEFINE_MAP(v, type, device, services)
77 : };
78 :
79 : /// Unsolicited push sent by the host when its service list changes.
80 : struct SvcDiscServiceUpdate
81 : {
82 : uint8_t v {MaxVersion};
83 : std::string type {MsgType::ServiceUpdate};
84 : std::string device;
85 : std::vector<SvcInfo> services;
86 31 : MSGPACK_DEFINE_MAP(v, type, device, services)
87 : };
88 :
89 : /// Application-level error response.
90 : struct SvcDiscError
91 : {
92 : uint8_t v {MaxVersion};
93 : std::string type {MsgType::Error};
94 : uint16_t code {0};
95 : std::string message;
96 1 : MSGPACK_DEFINE_MAP(v, type, code, message)
97 : };
98 :
99 : /// Sent when the client requested a higher protocol version than supported.
100 : struct SvcDiscVersionMismatch
101 : {
102 : uint8_t v {MaxVersion};
103 : std::string type {MsgType::VersionMismatch};
104 : uint8_t max_supported {MaxVersion};
105 2 : MSGPACK_DEFINE_MAP(v, type, max_supported)
106 : };
107 :
108 : /**
109 : * Try to read the `type` discriminator field from an opaque msgpack object
110 : * without committing to a specific message struct yet.
111 : * Returns the type string or an empty string if the field is missing.
112 : */
113 : inline std::string_view
114 2329 : peekType(const msgpack::object& obj)
115 : {
116 2329 : if (obj.type != msgpack::type::MAP)
117 0 : return {};
118 4658 : for (uint32_t i = 0; i < obj.via.map.size; ++i) {
119 4658 : const auto& kv = obj.via.map.ptr[i];
120 4658 : if (kv.key.type == msgpack::type::STR) {
121 4658 : std::string_view k(kv.key.via.str.ptr, kv.key.via.str.size);
122 4658 : if (k == "type"sv && kv.val.type == msgpack::type::STR)
123 2329 : return std::string_view(kv.val.via.str.ptr, kv.val.via.str.size);
124 : }
125 : }
126 0 : return {};
127 : }
128 :
129 : /**
130 : * Try to read the `v` (version) field from an opaque msgpack object.
131 : * Returns 0 if the field is missing or not a positive integer.
132 : */
133 : inline uint8_t
134 2326 : peekVersion(const msgpack::object& obj)
135 : {
136 2326 : if (obj.type != msgpack::type::MAP)
137 0 : return 0;
138 2326 : for (uint32_t i = 0; i < obj.via.map.size; ++i) {
139 2326 : const auto& kv = obj.via.map.ptr[i];
140 2326 : if (kv.key.type == msgpack::type::STR) {
141 2326 : std::string_view k(kv.key.via.str.ptr, kv.key.via.str.size);
142 2326 : if (k == "v"sv && kv.val.type == msgpack::type::POSITIVE_INTEGER) {
143 2326 : auto n = kv.val.via.u64;
144 2326 : return n > 255 ? 255 : static_cast<uint8_t>(n);
145 : }
146 : }
147 : }
148 0 : return 0;
149 : }
150 :
151 : } // namespace svc_protocol
152 : } // namespace jami
|