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