1 /* $Id: connect_misc.cpp 622184 2020-12-21 18:30:13Z ivanov $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Authors: Rafael Sadyrov
27 *
28 */
29
30 #include <ncbi_pch.hpp>
31
32 #include <array>
33
34 #include "connect_misc_impl.hpp"
35
36 #include "ncbi_servicep.h"
37
38 #include <connect/impl/connect_misc.hpp>
39
40 #include <connect/ncbi_core_cxx.hpp>
41 #include <connect/ncbi_socket.hpp>
42
43 #include <corelib/ncbi_system.hpp>
44
45 BEGIN_NCBI_SCOPE
46
47 // This class also initializes CONNECT library in its constructor (via CConnIniter base)
48 struct SSocketAddressImpl : CConnIniter
49 {
50 // Do not make static (see above)
GetHostSSocketAddressImpl51 unsigned GetHost(const string& name) const
52 {
53 return CSocketAPI::gethostbyname(name, eOn);
54 }
55
GetNameSSocketAddressImpl56 const string& GetName(unsigned host)
57 {
58 auto& name = m_Data[host];
59
60 // Name was not looked up yet or host changed
61 if (name.empty()) {
62 name = CSocketAPI::gethostbyaddr(host, eOn);
63
64 if (name.empty()) {
65 name = CSocketAPI::ntoa(host);
66 }
67 }
68
69 return name;
70 }
71
GetInstanceSSocketAddressImpl72 static SSocketAddressImpl& GetInstance()
73 {
74 thread_local static SSocketAddressImpl impl;
75 return impl;
76 }
77
78 private:
79 map<unsigned, string> m_Data;
80 };
81
SHost(const string & h)82 SSocketAddress::SHost::SHost(const string& h) :
83 host(SSocketAddressImpl::GetInstance().GetHost(h))
84 {
85 }
86
GetHostName() const87 string SSocketAddress::GetHostName() const
88 {
89 return SSocketAddressImpl::GetInstance().GetName(host);
90 }
91
Parse(const string & address)92 SSocketAddress SSocketAddress::Parse(const string& address)
93 {
94 string host, port;
95
96 if (NStr::SplitInTwo(address, ":", host, port)) {
97 return { host, port };
98 }
99
100 return { 0, 0 };
101 }
102
operator ==(const SSocketAddress & lhs,const SSocketAddress & rhs)103 bool operator==(const SSocketAddress& lhs, const SSocketAddress& rhs)
104 {
105 return lhs.host == rhs.host && lhs.port == rhs.port;
106 }
107
operator <(const SSocketAddress & lhs,const SSocketAddress & rhs)108 bool operator< (const SSocketAddress& lhs, const SSocketAddress& rhs)
109 {
110 if (lhs.host != rhs.host) return lhs.host < rhs.host;
111
112 return lhs.port < rhs.port;
113 }
114
115 // This class also initializes CONNECT library in its constructor (via CConnIniter base)
116 struct SServiceDiscoveryImpl : CConnIniter
117 {
118 // Do not make static (see above)
GetSingleServerSServiceDiscoveryImpl119 shared_ptr<void> GetSingleServer(const string& service_name) const
120 {
121 if (auto address = SSocketAddress::Parse(service_name)) {
122 CServiceDiscovery::TServer server(move(address), 1.0);
123 return make_shared<CServiceDiscovery::TServers>(1, move(server));
124 }
125
126 return {};
127 }
128 };
129
CServiceDiscovery(const string & service_name)130 CServiceDiscovery::CServiceDiscovery(const string& service_name) :
131 m_ServiceName(service_name),
132 m_Data(SServiceDiscoveryImpl().GetSingleServer(m_ServiceName)),
133 m_IsSingleServer(m_Data)
134 {
135 }
136
operator ()()137 CServiceDiscovery::TServers CServiceDiscovery::operator()()
138 {
139 // Single server "discovery"
140 if (m_IsSingleServer) {
141 _ASSERT(m_Data);
142 return *static_pointer_cast<TServers>(m_Data);
143 }
144
145 const TSERV_Type types = fSERV_Standalone | fSERV_IncludeStandby;
146 return DiscoverImpl(m_ServiceName, types, m_Data, {}, 0, 0);
147 }
148
DiscoverImpl(const string & service_name,unsigned types,shared_ptr<void> & net_info,pair<string,const char * > lbsm_affinity,int try_count,unsigned long retry_delay)149 CServiceDiscovery::TServers CServiceDiscovery::DiscoverImpl(const string& service_name, unsigned types,
150 shared_ptr<void>& net_info, pair<string, const char*> lbsm_affinity,
151 int try_count, unsigned long retry_delay)
152 {
153 TServers rv;
154
155 // Query the Load Balancer.
156 for (;;) {
157 if (!net_info) {
158 net_info.reset(ConnNetInfo_Create(service_name.c_str()), ConnNetInfo_Destroy);
159 }
160
161 if (auto it = make_c_unique(SERV_OpenP(service_name.c_str(), types, SERV_LOCALHOST, 0, 0.0,
162 static_cast<const SConnNetInfo*>(net_info.get()), NULL, 0, 0 /*false*/,
163 lbsm_affinity.first.c_str(), lbsm_affinity.second), SERV_Close)) {
164
165 while (auto info = SERV_GetNextInfoEx(it.get(), 0)) {
166 if (info->time > 0 && info->time != NCBI_TIME_INFINITE && info->rate != 0.0) {
167 rv.emplace_back(SSocketAddress(info->host, info->port), info->rate);
168 }
169 }
170
171 break;
172 }
173
174 // FIXME Retry logic can be removed as soon as LBSMD with
175 // packet compression is installed universally.
176 if (--try_count < 0) {
177 break;
178 }
179
180 ERR_POST("Could not find LB service name '" << service_name << "', will retry after delay");
181 SleepMilliSec(retry_delay);
182 }
183
184 return rv;
185 }
186
Parse(istream & is)187 CLogLatencies::TResult CLogLatencies::Parse(istream& is)
188 {
189 using namespace chrono;
190
191 enum : size_t { eTime = 1, eMicroseconds, eServer };
192 enum : size_t { fNothing = 0, fStart, fStop, eBoth };
193 array<const char*, eBoth> prefixes{ " ", "Start -> ", "Stop -> " };
194
195 array<char, 1024> line;
196 cmatch m;
197
198 using TServer = pair<size_t, array<system_clock::time_point, eBoth>>;
199 unordered_map<string, TServer> servers;
200 TServer* current = nullptr;
201 TResult latencies;
202
203 for (;;) {
204 if (!is.getline(line.data(), line.size())) {
205 // If it was a partial read (the line was too long)
206 if (is.fail() && is.gcount() == (streamsize) line.size() - 1) {
207 is.clear();
208 } else {
209 break;
210 }
211 }
212
213 size_t matched = fNothing;
214
215 if (regex_match(line.data(), m, m_Start)) {
216 matched = fStart;
217 current = &servers[m[eServer].str()];
218
219 } else if (regex_match(line.data(), m, m_Stop)) {
220 matched = fStop;
221
222 // If there is a server specified
223 if (m.size() > eServer) {
224 current = &servers[m[eServer].str()];
225 }
226 }
227
228 if (matched && current) {
229 tm tm = {};
230 stringstream ss(m[eTime].str());
231 ss >> get_time(&tm, "%Y-%m-%dT%H:%M:%S %b %d");
232 auto tp = system_clock::from_time_t(mktime(&tm)) + microseconds(stoull(m[eMicroseconds].str()));
233 current->first |= matched;
234 current->second[matched] = tp;
235 }
236
237 if (m_Debug) {
238 cerr << prefixes[matched] << line.data() << endl;
239 }
240 }
241
242 for (const auto& server : servers) {
243 const auto& server_name = server.first;
244 const auto& server_data = server.second;
245
246 if (server_data.first == eBoth) {
247 const auto& start = server_data.second[fStart];
248 const auto& stop = server_data.second[fStop];
249 latencies.emplace(server_name, duration_cast<microseconds>(stop - start));
250 }
251 }
252
253 return latencies;
254 }
255
~CLogLatencyReport()256 CLogLatencyReport::~CLogLatencyReport()
257 {
258 // It has not been started, nothing to report
259 if (!m_CerrBuf) {
260 return;
261 }
262
263 cerr.rdbuf(m_CerrBuf);
264 m_CerrOutput.seekg(0);
265 const auto latencies = Parse(m_CerrOutput);
266
267 for (const auto& server : latencies) {
268 const auto& server_name = server.first;
269 const auto& server_latency = server.second;
270 cerr << "server=" << server_name << "&latency=" << server_latency.count() << endl;
271 }
272
273 cerr.rdbuf(nullptr);
274 }
275
Start()276 void CLogLatencyReport::Start()
277 {
278 _ASSERT(!m_CerrBuf);
279 m_CerrBuf = cerr.rdbuf(m_CerrOutput.rdbuf());
280 GetDiagContext().SetOldPostFormat(false);
281 }
282
283 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ctr_drbg_free);
284 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ctr_drbg_init);
285 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ctr_drbg_random);
286 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ctr_drbg_seed);
287 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_entropy_free);
288 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_entropy_func);
289 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_entropy_init);
290 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_close_notify);
291 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_conf_alpn_protocols);
292 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_conf_authmode);
293 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_conf_rng);
294 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_config_defaults);
295 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_config_free);
296 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_config_init);
297 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_free);
298 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_get_verify_result);
299 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_handshake);
300 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_init);
301 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_read);
302 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_session_reset);
303 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_set_bio);
304 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_set_hostname);
305 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_setup);
306 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_ssl_write);
307 NCBI_EXPORT_FUNC_DEFINE(XCONNECT, mbedtls_strerror);
308
309 END_NCBI_SCOPE
310