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 0 : MSGPACK_DEFINE_MAP(id, name, description, proto, scheme)
56 : };
57 :
58 : /// Request sent by the client over `svcdisc://query`.
59 : struct SvcDiscQuery
60 : {
61 : uint8_t v {MaxVersion};
62 : std::string type {MsgType::Query};
63 1214 : MSGPACK_DEFINE_MAP(v, type)
64 : };
65 :
66 : /// Successful response listing the services visible to the requesting peer.
67 : struct SvcDiscResponse
68 : {
69 : uint8_t v {MaxVersion};
70 : std::string type {MsgType::ServiceList};
71 : /// Long device id of the responder, so the requester can target the
72 : /// exact device when opening a tunnel without a separate lookup.
73 : std::string device;
74 : std::vector<SvcInfo> services;
75 2421 : MSGPACK_DEFINE_MAP(v, type, device, services)
76 : };
77 :
78 : /// Unsolicited push sent by the host when its service list changes.
79 : struct SvcDiscServiceUpdate
80 : {
81 : uint8_t v {MaxVersion};
82 : std::string type {MsgType::ServiceUpdate};
83 : std::string device;
84 : std::vector<SvcInfo> services;
85 0 : MSGPACK_DEFINE_MAP(v, type, device, services)
86 : };
87 :
88 : /// Application-level error response.
89 : struct SvcDiscError
90 : {
91 : uint8_t v {MaxVersion};
92 : std::string type {MsgType::Error};
93 : uint16_t code {0};
94 : std::string message;
95 0 : MSGPACK_DEFINE_MAP(v, type, code, message)
96 : };
97 :
98 : /// Sent when the client requested a higher protocol version than supported.
99 : struct SvcDiscVersionMismatch
100 : {
101 : uint8_t v {MaxVersion};
102 : std::string type {MsgType::VersionMismatch};
103 : uint8_t max_supported {MaxVersion};
104 0 : MSGPACK_DEFINE_MAP(v, type, max_supported)
105 : };
106 :
107 : /**
108 : * Try to read the `type` discriminator field from an opaque msgpack object
109 : * without committing to a specific message struct yet.
110 : * Returns the type string or an empty string if the field is missing.
111 : */
112 : inline std::string_view
113 2421 : peekType(const msgpack::object& obj)
114 : {
115 2421 : if (obj.type != msgpack::type::MAP)
116 0 : return {};
117 4842 : for (uint32_t i = 0; i < obj.via.map.size; ++i) {
118 4842 : const auto& kv = obj.via.map.ptr[i];
119 4842 : if (kv.key.type == msgpack::type::STR) {
120 4842 : std::string_view k(kv.key.via.str.ptr, kv.key.via.str.size);
121 4842 : if (k == "type"sv && kv.val.type == msgpack::type::STR)
122 2421 : return std::string_view(kv.val.via.str.ptr, kv.val.via.str.size);
123 : }
124 : }
125 0 : return {};
126 : }
127 :
128 : /**
129 : * Try to read the `v` (version) field from an opaque msgpack object.
130 : * Returns 0 if the field is missing or not a positive integer.
131 : */
132 : inline uint8_t
133 2421 : peekVersion(const msgpack::object& obj)
134 : {
135 2421 : if (obj.type != msgpack::type::MAP)
136 0 : return 0;
137 2421 : for (uint32_t i = 0; i < obj.via.map.size; ++i) {
138 2421 : const auto& kv = obj.via.map.ptr[i];
139 2421 : if (kv.key.type == msgpack::type::STR) {
140 2421 : std::string_view k(kv.key.via.str.ptr, kv.key.via.str.size);
141 2421 : if (k == "v"sv && kv.val.type == msgpack::type::POSITIVE_INTEGER) {
142 2421 : auto n = kv.val.via.u64;
143 2421 : return n > 255 ? 255 : static_cast<uint8_t>(n);
144 : }
145 : }
146 : }
147 0 : return 0;
148 : }
149 :
150 : } // namespace svc_protocol
151 : } // namespace jami
|