1 /*  $Id: ncbi_uv_nghttp2.cpp 632910 2021-06-09 19:11:25Z 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 "connect_misc_impl.hpp"
33 
34 #include <connect/impl/ncbi_uv_nghttp2.hpp>
35 #include <connect/ncbi_socket.hpp>
36 #include <connect/ncbi_tls.h>
37 
38 #include <corelib/ncbiapp.hpp>
39 #include <corelib/request_ctx.hpp>
40 
41 BEGIN_NCBI_SCOPE
42 
43 #define NCBI_UV_WRITE_TRACE(message)        _TRACE(message)
44 #define NCBI_UV_TCP_TRACE(message)          _TRACE(message)
45 #define NCBI_NGHTTP2_SESSION_TRACE(message) _TRACE(message)
46 #define NCBI_UVNGHTTP2_TLS_TRACE(message)   _TRACE(message)
47 #define NCBI_UVNGHTTP2_SESSION_TRACE(message) _TRACE(message)
48 
49 using namespace NCBI_XCONNECT;
50 
51 template <typename T, enable_if_t<is_signed<T>::value, T>>
operator ()(T e)52 const char* SUvNgHttp2_Error::SMbedTlsStr::operator()(T e)
53 {
54     mbedtls_strerror(static_cast<int>(e), data(), size());
55     return data();
56 }
57 
SUv_Write(void * user_data,size_t buf_size)58 SUv_Write::SUv_Write(void* user_data, size_t buf_size) :
59     m_UserData(user_data),
60     m_BufSize(buf_size)
61 {
62     NewBuffer();
63     NCBI_UV_WRITE_TRACE(this << " created");
64 }
65 
Write(uv_stream_t * handle,uv_write_cb cb)66 int SUv_Write::Write(uv_stream_t* handle, uv_write_cb cb)
67 {
68     _ASSERT(m_CurrentBuffer);
69     auto& request     = m_CurrentBuffer->request;
70     auto& data        = m_CurrentBuffer->data;
71     auto& in_progress = m_CurrentBuffer->in_progress;
72 
73     _ASSERT(!in_progress);
74 
75     if (data.empty()) {
76         NCBI_UV_WRITE_TRACE(this << " empty write");
77         return 0;
78     }
79 
80     uv_buf_t buf;
81     buf.base = data.data();
82     buf.len = static_cast<decltype(buf.len)>(data.size());
83 
84     auto try_rv = uv_try_write(handle, &buf, 1);
85 
86     // If immediately sent everything
87     if (try_rv == static_cast<int>(data.size())) {
88         NCBI_UV_WRITE_TRACE(this << '/' << &request << " try-wrote: " << try_rv);
89         data.clear();
90         return 0;
91 
92     // If sent partially
93     } else if (try_rv > 0) {
94         NCBI_UV_WRITE_TRACE(this << '/' << &request << " try-wrote partially: " << try_rv);
95         _ASSERT(try_rv < static_cast<int>(data.size()));
96         buf.base += try_rv;
97         buf.len -= try_rv;
98 
99     // If unexpected error
100     } else if (try_rv != UV_EAGAIN) {
101         NCBI_UV_WRITE_TRACE(this << '/' << &request << " try-write failed: " << SUvNgHttp2_Error::LibuvStr(try_rv));
102         return try_rv;
103     }
104 
105     auto rv = uv_write(&request, handle, &buf, 1, cb);
106 
107     if (rv < 0) {
108         NCBI_UV_WRITE_TRACE(this << '/' << &request << " pre-write failed: " << SUvNgHttp2_Error::LibuvStr(rv));
109         return rv;
110     }
111 
112     NCBI_UV_WRITE_TRACE(this << '/' << &request << " writing: " << data.size());
113     in_progress = true;
114 
115     // Looking for unused buffer
116     for (auto& buffer : m_Buffers) {
117         if (!buffer.in_progress) {
118             _ASSERT(buffer.data.empty());
119 
120             NCBI_UV_WRITE_TRACE(this << '/' << &buffer.request << " switching to");
121             m_CurrentBuffer = &buffer;
122             return 0;
123         }
124     }
125 
126     // Need more buffers
127     NewBuffer();
128     return 0;
129 }
130 
OnWrite(uv_write_t * req)131 void SUv_Write::OnWrite(uv_write_t* req)
132 {
133     for (auto& buffer : m_Buffers) {
134         if (&buffer.request == req) {
135             _ASSERT(buffer.data.size());
136             _ASSERT(buffer.in_progress);
137 
138             NCBI_UV_WRITE_TRACE(this << '/' << req << " wrote");
139             buffer.data.clear();
140             buffer.in_progress = false;
141             return;
142         }
143     }
144 
145     _TROUBLE;
146 }
147 
Reset()148 void SUv_Write::Reset()
149 {
150     NCBI_UV_WRITE_TRACE(this << " reset");
151 
152     for (auto& buffer : m_Buffers) {
153         buffer.data.clear();
154         buffer.in_progress = false;
155     }
156 }
157 
NewBuffer()158 void SUv_Write::NewBuffer()
159 {
160     m_Buffers.emplace_front();
161     m_CurrentBuffer = &m_Buffers.front();
162 
163     NCBI_UV_WRITE_TRACE(this << '/' << &m_CurrentBuffer->request << " new buffer");
164     m_CurrentBuffer->request.data = m_UserData;
165     m_CurrentBuffer->data.reserve(m_BufSize);
166 }
167 
168 
SUv_Connect(void * user_data,const SSocketAddress & address)169 SUv_Connect::SUv_Connect(void* user_data, const SSocketAddress& address)
170 {
171     m_Request.data = user_data;
172 
173     m_Address.sin_family = AF_INET;
174     m_Address.sin_addr.s_addr = address.host;
175     m_Address.sin_port = CSocketAPI::HostToNetShort(address.port);
176 #ifdef HAVE_SIN_LEN
177     m_Address.sin_len = sizeof(m_Address);
178 #endif
179 }
180 
operator ()(uv_tcp_t * handle,uv_connect_cb cb)181 int SUv_Connect::operator()(uv_tcp_t* handle, uv_connect_cb cb)
182 {
183     return uv_tcp_connect(&m_Request, handle, reinterpret_cast<sockaddr*>(&m_Address), cb);
184 }
185 
186 
SUv_Tcp(uv_loop_t * l,const SSocketAddress & address,size_t rd_buf_size,size_t wr_buf_size,TConnectCb connect_cb,TReadCb rcb,TWriteCb write_cb)187 SUv_Tcp::SUv_Tcp(uv_loop_t *l, const SSocketAddress& address, size_t rd_buf_size, size_t wr_buf_size,
188         TConnectCb connect_cb, TReadCb rcb, TWriteCb write_cb) :
189     SUv_Handle<uv_tcp_t>(s_OnClose),
190     m_Loop(l),
191     m_Connect(this, address),
192     m_Write(this, wr_buf_size),
193     m_ConnectCb(connect_cb),
194     m_ReadCb(rcb),
195     m_WriteCb(write_cb)
196 {
197     data = this;
198     m_ReadBuffer.reserve(rd_buf_size);
199 
200     NCBI_UV_TCP_TRACE(this << " created");
201 }
202 
Write()203 int SUv_Tcp::Write()
204 {
205     if (m_State == eClosed) {
206         auto rv = uv_tcp_init(m_Loop, this);
207 
208         if (rv < 0) {
209             NCBI_UV_TCP_TRACE(this << " init failed: " << SUvNgHttp2_Error::LibuvStr(rv));
210             return rv;
211         }
212 
213         rv = m_Connect(this, s_OnConnect);
214 
215         if (rv < 0) {
216             NCBI_UV_TCP_TRACE(this << " pre-connect failed: " << SUvNgHttp2_Error::LibuvStr(rv));
217             Close();
218             return rv;
219         }
220 
221         NCBI_UV_TCP_TRACE(this << " connecting");
222         m_State = eConnecting;
223     }
224 
225     if (m_State == eConnected) {
226         auto rv = m_Write.Write((uv_stream_t*)this, s_OnWrite);
227 
228         if (rv < 0) {
229             NCBI_UV_TCP_TRACE(this << "  pre-write failed: " << SUvNgHttp2_Error::LibuvStr(rv));
230             Close();
231             return rv;
232         }
233 
234         NCBI_UV_TCP_TRACE(this << " writing");
235     }
236 
237     return 0;
238 }
239 
Close()240 void SUv_Tcp::Close()
241 {
242     if (m_State == eConnected) {
243         auto rv = uv_read_stop(reinterpret_cast<uv_stream_t*>(this));
244 
245         if (rv < 0) {
246             NCBI_UV_TCP_TRACE(this << " read stop failed: " << SUvNgHttp2_Error::LibuvStr(rv));
247         } else {
248             NCBI_UV_TCP_TRACE(this << " read stopped");
249         }
250     }
251 
252     m_Write.Reset();
253 
254     if ((m_State != eClosing) && (m_State != eClosed)) {
255         NCBI_UV_TCP_TRACE(this << " closing");
256         m_State = eClosing;
257         SUv_Handle<uv_tcp_t>::Close();
258     } else {
259         NCBI_UV_TCP_TRACE(this << " already closing/closed");
260     }
261 }
262 
OnConnect(uv_connect_t *,int status)263 void SUv_Tcp::OnConnect(uv_connect_t*, int status)
264 {
265     if (status >= 0) {
266         status = uv_tcp_nodelay(this, 1);
267 
268         if (status >= 0) {
269             status = uv_read_start((uv_stream_t*)this, s_OnAlloc, s_OnRead);
270 
271             if (status >= 0) {
272                 NCBI_UV_TCP_TRACE(this << " connected");
273                 m_State = eConnected;
274                 m_ConnectCb(status);
275                 return;
276             } else {
277                 NCBI_UV_TCP_TRACE(this << " read start failed: " << SUvNgHttp2_Error::LibuvStr(status));
278             }
279         } else {
280             NCBI_UV_TCP_TRACE(this << " nodelay failed: " << SUvNgHttp2_Error::LibuvStr(status));
281         }
282     } else {
283         NCBI_UV_TCP_TRACE(this << " connect failed: " << SUvNgHttp2_Error::LibuvStr(status));
284     }
285 
286     Close();
287     m_ConnectCb(status);
288 }
289 
OnAlloc(uv_handle_t *,size_t suggested_size,uv_buf_t * buf)290 void SUv_Tcp::OnAlloc(uv_handle_t*, size_t suggested_size, uv_buf_t* buf)
291 {
292     m_ReadBuffer.resize(suggested_size);
293     buf->base = m_ReadBuffer.data();
294     buf->len = static_cast<decltype(buf->len)>(m_ReadBuffer.size());
295 }
296 
OnRead(uv_stream_t *,ssize_t nread,const uv_buf_t * buf)297 void SUv_Tcp::OnRead(uv_stream_t*, ssize_t nread, const uv_buf_t* buf)
298 {
299     if (nread < 0) {
300         NCBI_UV_TCP_TRACE(this << " read failed: " << SUvNgHttp2_Error::LibuvStr(nread));
301         Close();
302     } else {
303         NCBI_UV_TCP_TRACE(this << " read: " << nread);
304     }
305 
306     m_ReadCb(buf->base, nread);
307 }
308 
OnWrite(uv_write_t * req,int status)309 void SUv_Tcp::OnWrite(uv_write_t* req, int status)
310 {
311     if (status < 0) {
312         NCBI_UV_TCP_TRACE(this << '/' << req << " write failed: " << SUvNgHttp2_Error::LibuvStr(status));
313         Close();
314     } else {
315         NCBI_UV_TCP_TRACE(this << '/' << req << " wrote");
316         m_Write.OnWrite(req);
317     }
318 
319     m_WriteCb(status);
320 }
321 
OnClose(uv_handle_t *)322 void SUv_Tcp::OnClose(uv_handle_t*)
323 {
324     NCBI_UV_TCP_TRACE(this << " closed");
325     m_State = eClosed;
326 }
327 
328 struct SUvNgHttp2_UserAgentImpl : string
329 {
330     SUvNgHttp2_UserAgentImpl();
331 };
332 
SUvNgHttp2_UserAgentImpl()333 SUvNgHttp2_UserAgentImpl::SUvNgHttp2_UserAgentImpl()
334 {
335     if (auto app = CNcbiApplication::InstanceGuard()) {
336         const auto& full_version = app->GetFullVersion();
337         const auto& app_version = full_version.GetVersionInfo();
338         const auto pkg_version = full_version.GetPackageVersion();
339 
340         assign(app->GetProgramDisplayName());
341         append(1, '/');
342 
343         if (app_version.IsAny() && !pkg_version.IsAny()) {
344             append(1, 'p');
345             append(pkg_version.Print());
346         } else {
347             append(app_version.Print());
348         }
349     } else {
350         assign("UNKNOWN/UNKNOWN");
351     }
352 
353     append(" NcbiCxxToolkit/"
354 #if defined(NCBI_PRODUCTION_VER)
355         "P" NCBI_AS_STRING(NCBI_PRODUCTION_VER)
356 #elif defined(NCBI_SUBVERSION_REVISION)
357         "r" NCBI_AS_STRING(NCBI_SUBVERSION_REVISION)
358 #if defined(NCBI_DEVELOPMENT_VER)
359         ".D" NCBI_AS_STRING(NCBI_DEVELOPMENT_VER)
360 #elif defined(NCBI_SC_VERSION)
361         ".SC" NCBI_AS_STRING(NCBI_SC_VERSION)
362 #endif
363 #else
364         "UNKNOWN"
365 #endif
366         );
367 }
368 
Init()369 string SUvNgHttp2_UserAgent::Init()
370 {
371     return SUvNgHttp2_UserAgentImpl();
372 }
373 
SNgHttp2_Session(void * user_data,uint32_t max_streams,nghttp2_on_data_chunk_recv_callback on_data,nghttp2_on_stream_close_callback on_stream_close,nghttp2_on_header_callback on_header,nghttp2_error_callback on_error,nghttp2_on_frame_recv_callback on_frame_recv)374 SNgHttp2_Session::SNgHttp2_Session(void* user_data, uint32_t max_streams,
375         nghttp2_on_data_chunk_recv_callback on_data,
376         nghttp2_on_stream_close_callback    on_stream_close,
377         nghttp2_on_header_callback          on_header,
378         nghttp2_error_callback              on_error,
379         nghttp2_on_frame_recv_callback      on_frame_recv) :
380     m_UserData(user_data),
381     m_OnData(on_data),
382     m_OnStreamClose(on_stream_close),
383     m_OnHeader(on_header),
384     m_OnError(on_error),
385     m_OnFrameRecv(on_frame_recv),
386     m_MaxStreams(max_streams, max_streams)
387 {
388     NCBI_NGHTTP2_SESSION_TRACE(this << " created");
389 }
390 
Init()391 int SNgHttp2_Session::Init()
392 {
393     if (m_Session) return 0;
394 
395     nghttp2_session_callbacks* callbacks;
396     nghttp2_session_callbacks_new(&callbacks);
397 
398     nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, m_OnData);
399     nghttp2_session_callbacks_set_on_stream_close_callback(   callbacks, m_OnStreamClose);
400     nghttp2_session_callbacks_set_on_header_callback(         callbacks, m_OnHeader);
401     nghttp2_session_callbacks_set_error_callback(             callbacks, m_OnError);
402     if (m_OnFrameRecv) nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, m_OnFrameRecv);
403 
404     nghttp2_session_client_new(&m_Session, callbacks, m_UserData);
405     nghttp2_session_callbacks_del(callbacks);
406 
407     nghttp2_settings_entry iv[1] = {
408         {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, m_MaxStreams.second}
409     };
410 
411     /* client 24 bytes magic string will be sent by nghttp2 library */
412     if (auto rv = nghttp2_submit_settings(m_Session, NGHTTP2_FLAG_NONE, iv, sizeof(iv) / sizeof(iv[0]))) {
413         NCBI_NGHTTP2_SESSION_TRACE(this << " submit settings failed: " << SUvNgHttp2_Error::NgHttp2Str(rv));
414         return x_DelOnError(rv);
415     }
416 
417     NCBI_NGHTTP2_SESSION_TRACE(this << " initialized");
418     auto max_streams = nghttp2_session_get_remote_settings(m_Session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
419     m_MaxStreams.first = min(max_streams, m_MaxStreams.second);
420     return 0;
421 }
422 
Del()423 void SNgHttp2_Session::Del()
424 {
425     if (!m_Session) {
426         NCBI_NGHTTP2_SESSION_TRACE(this << " already terminated");
427         return;
428     }
429 
430     auto rv = nghttp2_session_terminate_session(m_Session, NGHTTP2_NO_ERROR);
431 
432     if (rv) {
433         NCBI_NGHTTP2_SESSION_TRACE(this << " terminate failed: " << SUvNgHttp2_Error::NgHttp2Str(rv));
434     } else {
435         NCBI_NGHTTP2_SESSION_TRACE(this << " terminated");
436     }
437 
438     x_DelOnError(-1);
439 }
440 
Submit(const nghttp2_nv * nva,size_t nvlen,nghttp2_data_provider * data_prd)441 int32_t SNgHttp2_Session::Submit(const nghttp2_nv *nva, size_t nvlen, nghttp2_data_provider* data_prd)
442 {
443     if (auto rv = Init()) return rv;
444 
445     auto rv = nghttp2_submit_request(m_Session, nullptr, nva, nvlen, data_prd, nullptr);
446 
447     if (rv < 0) {
448         NCBI_NGHTTP2_SESSION_TRACE(this << " submit failed: " << SUvNgHttp2_Error::NgHttp2Str(rv));
449     } else {
450         NCBI_NGHTTP2_SESSION_TRACE(this << " submitted");
451     }
452 
453     return x_DelOnError(rv);
454 }
455 
Resume(int32_t stream_id)456 int SNgHttp2_Session::Resume(int32_t stream_id)
457 {
458     if (auto rv = Init()) return rv;
459 
460     auto rv = nghttp2_session_resume_data(m_Session, stream_id);
461 
462     if (rv < 0) {
463         NCBI_NGHTTP2_SESSION_TRACE(this << " resume failed: " << SUvNgHttp2_Error::NgHttp2Str(rv));
464     } else {
465         NCBI_NGHTTP2_SESSION_TRACE(this << " resumed");
466     }
467 
468     return x_DelOnError(rv);
469 }
470 
Send(vector<char> & buffer)471 ssize_t SNgHttp2_Session::Send(vector<char>& buffer)
472 {
473     if (auto rv = Init()) return rv;
474 
475     _DEBUG_ARG(ssize_t total = 0);
476 
477     while (nghttp2_session_want_write(m_Session)) {
478         const uint8_t* data;
479         auto rv = nghttp2_session_mem_send(m_Session, &data);
480 
481         if (rv > 0) {
482             buffer.insert(buffer.end(), data, data + rv);
483             _DEBUG_CODE(total += rv;);
484 
485         } else if (rv < 0) {
486             NCBI_NGHTTP2_SESSION_TRACE(this << " send failed: " << SUvNgHttp2_Error::NgHttp2Str(rv));
487             return x_DelOnError(rv);
488 
489         } else {
490             NCBI_NGHTTP2_SESSION_TRACE(this << " sent: " << total);
491             return eOkay;
492         }
493     }
494 
495     if (nghttp2_session_want_read(m_Session) == 0) {
496         NCBI_NGHTTP2_SESSION_TRACE(this << " does not want to write and read");
497         x_DelOnError(-1);
498         return eWantsClose;
499     }
500 
501     NCBI_NGHTTP2_SESSION_TRACE(this << " does not want to write");
502     return eOkay;
503 }
504 
Recv(const uint8_t * buffer,size_t size)505 ssize_t SNgHttp2_Session::Recv(const uint8_t* buffer, size_t size)
506 {
507     if (auto rv = Init()) return rv;
508 
509     ssize_t total = 0;
510 
511     while (size > 0) {
512         auto rv = nghttp2_session_mem_recv(m_Session, buffer, size);
513 
514         if (rv > 0) {
515             buffer += rv;
516             size -= rv;
517             total += rv;
518 
519         } else  {
520             NCBI_NGHTTP2_SESSION_TRACE(this << " receive failed: " << SUvNgHttp2_Error::NgHttp2Str(rv));
521             return x_DelOnError(rv);
522         }
523     }
524 
525     NCBI_NGHTTP2_SESSION_TRACE(this << " received: " << total);
526     return total;
527 }
528 
529 struct SUvNgHttp2_TlsNoOp : SUvNgHttp2_Tls
530 {
SUvNgHttp2_TlsNoOpSUvNgHttp2_TlsNoOp531     SUvNgHttp2_TlsNoOp(TGetWriteBuf get_write_buf) : m_GetWriteBuf(get_write_buf) {}
532 
ReadSUvNgHttp2_TlsNoOp533     int Read(const char*& buf, ssize_t& nread) override
534     {
535         m_IncomingData = exchange(buf, buf + nread);
536         return static_cast<int>(exchange(nread, 0));
537     }
538 
WriteSUvNgHttp2_TlsNoOp539     int Write() override { return 0; }
CloseSUvNgHttp2_TlsNoOp540     int Close() override { return 0; }
541 
GetReadBufferSUvNgHttp2_TlsNoOp542     const char* GetReadBuffer() override { return m_IncomingData; }
GetWriteBufferSUvNgHttp2_TlsNoOp543     vector<char>& GetWriteBuffer() override { return m_GetWriteBuf(); }
544 
545 private:
546     const char* m_IncomingData = nullptr;
547     TGetWriteBuf m_GetWriteBuf;
548 };
549 
550 struct SUvNgHttp2_TlsImpl : SUvNgHttp2_Tls
551 {
552     SUvNgHttp2_TlsImpl(const SSocketAddress& address, size_t rd_buf_size, size_t wr_buf_size, TGetWriteBuf get_write_buf);
553     ~SUvNgHttp2_TlsImpl() override;
554 
555     int Read(const char*& buf, ssize_t& nread) override;
556     int Write() override;
557     int Close() override;
558 
GetReadBufferSUvNgHttp2_TlsImpl559     const char* GetReadBuffer() override { return m_ReadBuffer.data(); }
GetWriteBufferSUvNgHttp2_TlsImpl560     vector<char>& GetWriteBuffer() override { return m_WriteBuffer; }
561 
562 private:
563     // Provides scope-restricted access to incoming TLS data
564     struct SIncomingData : pair<const char**, ssize_t*>
565     {
operator ()SUvNgHttp2_TlsImpl::SIncomingData::SReset566         struct SReset { void operator()(first_type* p) const { *p = nullptr; } };
operator ()SUvNgHttp2_TlsImpl::SIncomingData567         auto operator()(first_type b, second_type l) { first = b; second = l; return unique_ptr<first_type, SReset>(&first); }
568     };
569 
570     SUvNgHttp2_TlsImpl(const SUvNgHttp2_TlsImpl&) = delete;
571     SUvNgHttp2_TlsImpl(SUvNgHttp2_TlsImpl&&) = delete;
572 
573     SUvNgHttp2_TlsImpl& operator=(const SUvNgHttp2_TlsImpl&) = delete;
574     SUvNgHttp2_TlsImpl& operator=(SUvNgHttp2_TlsImpl&&) = delete;
575 
576     int Init();
577     int GetReady();
578 
579     int OnRecv(unsigned char* buf, size_t len);
580     int OnSend(const unsigned char* buf, size_t len);
581 
GetThatSUvNgHttp2_TlsImpl582     static SUvNgHttp2_TlsImpl* GetThat(void* ctx)
583     {
584         _ASSERT(ctx);
585         return static_cast<SUvNgHttp2_TlsImpl*>(ctx);
586     }
587 
s_OnRecvSUvNgHttp2_TlsImpl588     static int s_OnRecv(void* ctx, unsigned char* buf, size_t len)
589     {
590         return GetThat(ctx)->OnRecv(buf, len);
591     }
592 
s_OnSendSUvNgHttp2_TlsImpl593     static int s_OnSend(void* ctx, const unsigned char* buf, size_t len)
594     {
595         return GetThat(ctx)->OnSend(buf, len);
596     }
597 
598     enum { eInitialized, eReady, eClosed } m_State = eInitialized;
599 
600     vector<char> m_ReadBuffer;
601     vector<char> m_WriteBuffer;
602     SIncomingData m_IncomingData;
603     TGetWriteBuf m_GetWriteBuf;
604 
605     mbedtls_ssl_context m_Ssl;
606     mbedtls_ssl_config m_Conf;
607     mbedtls_ctr_drbg_context m_CtrDrbg;
608     mbedtls_entropy_context m_Entropy;
609     array<const char*, 2> m_Protocols;
610 };
611 
s_WantReadOrWrite(int rv)612 bool s_WantReadOrWrite(int rv)
613 {
614     return (rv == MBEDTLS_ERR_SSL_WANT_READ) || (rv == MBEDTLS_ERR_SSL_WANT_WRITE);
615 }
616 
SUvNgHttp2_TlsImpl(const SSocketAddress & address,size_t rd_buf_size,size_t wr_buf_size,TGetWriteBuf get_write_buf)617 SUvNgHttp2_TlsImpl::SUvNgHttp2_TlsImpl(const SSocketAddress& address, size_t rd_buf_size, size_t wr_buf_size, TGetWriteBuf get_write_buf) :
618     m_ReadBuffer(rd_buf_size),
619     m_GetWriteBuf(get_write_buf),
620     m_Protocols({ "h2", nullptr })
621 {
622     NCBI_UVNGHTTP2_TLS_TRACE(this << " created");
623     m_WriteBuffer.reserve(wr_buf_size),
624 
625     SOCK_SetupSSLEx(NcbiSetupTls);
626     mbedtls_ssl_config_init(&m_Conf);
627 
628     auto c_rv = mbedtls_ssl_config_defaults(&m_Conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
629 
630     if (c_rv) {
631         NCBI_UVNGHTTP2_TLS_TRACE(this << " mbedtls_ssl_config_defaults: " << SUvNgHttp2_Error::MbedTlsStr(c_rv));
632         return;
633     }
634 
635     mbedtls_ssl_conf_authmode(&m_Conf, MBEDTLS_SSL_VERIFY_NONE);
636     mbedtls_entropy_init(&m_Entropy);
637     mbedtls_ctr_drbg_init(&m_CtrDrbg);
638 
639     auto d_rv = mbedtls_ctr_drbg_seed(&m_CtrDrbg, mbedtls_entropy_func, &m_Entropy, nullptr, 0);
640 
641     if (d_rv) {
642         NCBI_UVNGHTTP2_TLS_TRACE(this << " mbedtls_ctr_drbg_seed: " << SUvNgHttp2_Error::MbedTlsStr(d_rv));
643         return;
644     }
645 
646     mbedtls_ssl_conf_rng(&m_Conf, mbedtls_ctr_drbg_random, &m_CtrDrbg);
647     mbedtls_ssl_conf_alpn_protocols(&m_Conf, m_Protocols.data());
648     mbedtls_ssl_init(&m_Ssl);
649 
650     auto s_rv = mbedtls_ssl_setup(&m_Ssl, &m_Conf);
651 
652     if (s_rv) {
653         NCBI_UVNGHTTP2_TLS_TRACE(this << " mbedtls_ssl_setup: " << SUvNgHttp2_Error::MbedTlsStr(s_rv));
654         return;
655     }
656 
657     const auto host_name = address.GetHostName();
658     auto h_rv = mbedtls_ssl_set_hostname(&m_Ssl, host_name.c_str());
659 
660     if (h_rv) {
661         NCBI_UVNGHTTP2_TLS_TRACE(this << " mbedtls_ssl_set_hostname: " << SUvNgHttp2_Error::MbedTlsStr(h_rv));
662         return;
663     }
664 
665     mbedtls_ssl_set_bio(&m_Ssl, this, s_OnSend, s_OnRecv, nullptr);
666 }
667 
~SUvNgHttp2_TlsImpl()668 SUvNgHttp2_TlsImpl::~SUvNgHttp2_TlsImpl()
669 {
670     mbedtls_entropy_free(&m_Entropy);
671     mbedtls_ctr_drbg_free(&m_CtrDrbg);
672     mbedtls_ssl_config_free(&m_Conf);
673     mbedtls_ssl_free(&m_Ssl);
674 }
675 
Init()676 int SUvNgHttp2_TlsImpl::Init()
677 {
678     switch (m_State)
679     {
680         case eInitialized:
681             return GetReady();
682 
683         case eReady:
684             return 0;
685 
686         case eClosed:
687             break;
688     }
689 
690     m_WriteBuffer.clear();
691     auto rv = mbedtls_ssl_session_reset(&m_Ssl);
692 
693     if (rv < 0) {
694         NCBI_UVNGHTTP2_TLS_TRACE(this << " reset: " << SUvNgHttp2_Error::MbedTlsStr(rv));
695     } else {
696         NCBI_UVNGHTTP2_TLS_TRACE(this << " reset: " << rv);
697         m_State = eInitialized;
698     }
699 
700     return rv;
701 }
702 
GetReady()703 int SUvNgHttp2_TlsImpl::GetReady()
704 {
705     auto hs_rv = mbedtls_ssl_handshake(&m_Ssl);
706 
707     if (hs_rv < 0) {
708         NCBI_UVNGHTTP2_TLS_TRACE(this << " handshake: " << SUvNgHttp2_Error::MbedTlsStr(hs_rv));
709         return hs_rv;
710     }
711 
712     NCBI_UVNGHTTP2_TLS_TRACE(this << " handshake: " << hs_rv);
713 
714     auto v_rv = mbedtls_ssl_get_verify_result(&m_Ssl);
715 
716     if (v_rv) {
717         NCBI_UVNGHTTP2_TLS_TRACE(this << " verify: " << v_rv);
718     } else {
719         NCBI_UVNGHTTP2_TLS_TRACE(this << " verified");
720     }
721 
722     m_State = eReady;
723     return 0;
724 }
725 
Read(const char * & buf,ssize_t & nread)726 int SUvNgHttp2_TlsImpl::Read(const char*& buf, ssize_t& nread)
727 {
728     auto scope_guard = m_IncomingData(&buf, &nread);
729 
730     if (auto rv = Init()) return rv;
731 
732     auto rv = mbedtls_ssl_read(&m_Ssl, reinterpret_cast<unsigned char*>(m_ReadBuffer.data()), m_ReadBuffer.size());
733 
734     if (rv < 0) {
735         NCBI_UVNGHTTP2_TLS_TRACE(this << " read: " << SUvNgHttp2_Error::MbedTlsStr(rv));
736     } else {
737         NCBI_UVNGHTTP2_TLS_TRACE(this << " read: " << rv);
738     }
739 
740     return rv;
741 }
742 
Write()743 int SUvNgHttp2_TlsImpl::Write()
744 {
745     if (auto rv = Init()) return rv;
746 
747     auto buf = m_WriteBuffer.data();
748     auto size = m_WriteBuffer.size();
749 
750     while (size > 0) {
751         auto rv = mbedtls_ssl_write(&m_Ssl, (unsigned char*)buf, size);
752 
753         if (rv > 0) {
754             buf += rv;
755             size -= rv;
756 
757         } else if (rv < 0) {
758             NCBI_UVNGHTTP2_TLS_TRACE(this << " write: " << SUvNgHttp2_Error::MbedTlsStr(rv));
759             return rv;
760         }
761     }
762 
763     auto written = m_WriteBuffer.size() - size;
764     m_WriteBuffer.erase(m_WriteBuffer.begin(), m_WriteBuffer.begin() + written);
765     NCBI_UVNGHTTP2_TLS_TRACE(this << " write: " << written);
766     return static_cast<int>(written);
767 }
768 
Close()769 int SUvNgHttp2_TlsImpl::Close()
770 {
771     NCBI_UVNGHTTP2_TLS_TRACE(this << " close");
772 
773     switch (m_State)
774     {
775         case eInitialized:
776         case eClosed:      return 0;
777         case eReady:       break;
778     }
779 
780     m_State = eClosed;
781     return mbedtls_ssl_close_notify(&m_Ssl);
782 }
783 
OnRecv(unsigned char * buf,size_t len)784 int SUvNgHttp2_TlsImpl::OnRecv(unsigned char* buf, size_t len)
785 {
786     if (m_IncomingData.first && m_IncomingData.second) {
787         auto copied = min(len, static_cast<size_t>(*m_IncomingData.second));
788         NCBI_UVNGHTTP2_TLS_TRACE(this << " on receiving: " << copied);
789 
790         if (copied) {
791             memcpy(buf, *m_IncomingData.first, copied);
792             *m_IncomingData.first += copied;
793             *m_IncomingData.second -= copied;
794             return static_cast<int>(copied);
795         }
796     } else {
797         NCBI_UVNGHTTP2_TLS_TRACE(this << " on receiving");
798     }
799 
800     return MBEDTLS_ERR_SSL_WANT_READ;
801 }
802 
OnSend(const unsigned char * buf,size_t len)803 int SUvNgHttp2_TlsImpl::OnSend(const unsigned char* buf, size_t len)
804 {
805     NCBI_UVNGHTTP2_TLS_TRACE(this << " on sending: " << len);
806     auto& write_buf = m_GetWriteBuf();
807     write_buf.insert(write_buf.end(), buf, buf + len);
808     return static_cast<int>(len);
809 }
810 
Create(bool https,const SSocketAddress & address,size_t rd_buf_size,size_t wr_buf_size,TGetWriteBuf get_write_buf)811 SUvNgHttp2_Tls* SUvNgHttp2_Tls::Create(bool https, const SSocketAddress& address, size_t rd_buf_size, size_t wr_buf_size, TGetWriteBuf get_write_buf)
812 {
813     if (https) {
814         return new SUvNgHttp2_TlsImpl(address, rd_buf_size, wr_buf_size, get_write_buf);
815     }
816 
817     return new SUvNgHttp2_TlsNoOp(get_write_buf);
818 }
819 
Send()820 bool SUvNgHttp2_SessionBase::Send()
821 {
822     auto send_rv = m_Session.Send(m_Tls->GetWriteBuffer());
823 
824     if (send_rv < 0) {
825         Reset(SUvNgHttp2_Error::FromNgHttp2(send_rv, "on send"));
826 
827     } else if (send_rv == SNgHttp2_Session::eWantsClose) {
828         Reset("nghttp2 asked to drop connection");
829 
830     } else {
831         auto tls_rv = m_Tls->Write();
832 
833         if ((tls_rv < 0) && !s_WantReadOrWrite(tls_rv)) {
834             Reset(SUvNgHttp2_Error::FromMbedTls(tls_rv, "on write"));
835 
836         } else if (auto tcp_rv = m_Tcp.Write()) {
837             Reset(SUvNgHttp2_Error::FromLibuv(tcp_rv, "on write"));
838 
839         } else {
840             return true;
841         }
842     }
843 
844     return false;
845 }
846 
OnConnect(int status)847 void SUvNgHttp2_SessionBase::OnConnect(int status)
848 {
849     NCBI_UVNGHTTP2_SESSION_TRACE(this << " connected: " << status);
850 
851     if (status < 0) {
852         Reset(SUvNgHttp2_Error::FromLibuv(status, "on connecting"));
853     } else {
854         Send();
855     }
856 }
857 
OnWrite(int status)858 void SUvNgHttp2_SessionBase::OnWrite(int status)
859 {
860     NCBI_UVNGHTTP2_SESSION_TRACE(this << " wrote: " << status);
861 
862     if (status < 0) {
863         Reset(SUvNgHttp2_Error::FromLibuv(status, "on writing"));
864     }
865 }
866 
OnRead(const char * buf,ssize_t nread)867 void SUvNgHttp2_SessionBase::OnRead(const char* buf, ssize_t nread)
868 {
869     NCBI_UVNGHTTP2_SESSION_TRACE(this << " read: " << nread);
870 
871     if (nread < 0) {
872         Reset(SUvNgHttp2_Error::FromLibuv(nread, "on reading"));
873         return;
874     }
875 
876     while (nread > 0) {
877         auto read_rv = m_Tls->Read(buf, nread);
878 
879         if (read_rv == 0) {
880             m_Session.Del();
881             m_Tls->Close();
882             m_Tcp.Close();
883 
884         } else if (s_WantReadOrWrite(read_rv)) {
885             if (nread == 0) break;
886 
887             Reset("Some encrypted data was ignored");
888 
889         } else if (read_rv < 0) {
890             Reset(SUvNgHttp2_Error::FromMbedTls(read_rv, "on read"));
891 
892         } else {
893             auto recv_rv = m_Session.Recv((const uint8_t*)m_Tls->GetReadBuffer(), (size_t)read_rv);
894 
895             if (recv_rv < 0) {
896                 Reset(SUvNgHttp2_Error::FromNgHttp2(recv_rv, "on receive"));
897 
898             } else if (recv_rv != read_rv) {
899                 Reset("Processed size does not equal to received");
900 
901             } else {
902                 continue;
903             }
904         }
905 
906         return;
907     }
908 
909     Send();
910 }
911 
Reset(SUvNgHttp2_Error error)912 void SUvNgHttp2_SessionBase::Reset(SUvNgHttp2_Error error)
913 {
914     NCBI_UVNGHTTP2_SESSION_TRACE(this << " resetting with " << error);
915     m_Session.Del();
916     m_Tls->Close();
917     m_Tcp.Close();
918     OnReset(move(error));
919 }
920 
921 END_NCBI_SCOPE
922